diff rust/hg-core/src/dirstate_tree/status.rs @ 49585:18282cf18aa2

branching: merge stable into default
author Rapha?l Gom?s <rgomes@octobus.net>
date Mon, 14 Nov 2022 10:59:09 +0100
parents da48f170d203 363923bd51cd
children c7fb9b74e753
line wrap: on
line diff
--- a/rust/hg-core/src/dirstate_tree/status.rs	Mon Nov 07 09:25:20 2022 +0100
+++ b/rust/hg-core/src/dirstate_tree/status.rs	Mon Nov 14 10:59:09 2022 +0100
@@ -10,6 +10,7 @@
 use crate::matchers::get_ignore_function;
 use crate::matchers::Matcher;
 use crate::utils::files::get_bytes_from_os_string;
+use crate::utils::files::get_bytes_from_path;
 use crate::utils::files::get_path_from_bytes;
 use crate::utils::hg_path::HgPath;
 use crate::BadMatch;
@@ -67,7 +68,7 @@
                     let (ignore_fn, warnings) = get_ignore_function(
                         ignore_files,
                         &root_dir,
-                        &mut |_pattern_bytes| {},
+                        &mut |_source, _pattern_bytes| {},
                     )?;
                     (ignore_fn, warnings, None)
                 }
@@ -76,7 +77,24 @@
                     let (ignore_fn, warnings) = get_ignore_function(
                         ignore_files,
                         &root_dir,
-                        &mut |pattern_bytes| hasher.update(pattern_bytes),
+                        &mut |source, pattern_bytes| {
+                            // If inside the repo, use the relative version to
+                            // make it deterministic inside tests.
+                            // The performance hit should be negligible.
+                            let source = source
+                                .strip_prefix(&root_dir)
+                                .unwrap_or(source);
+                            let source = get_bytes_from_path(source);
+
+                            let mut subhasher = Sha1::new();
+                            subhasher.update(pattern_bytes);
+                            let patterns_hash = subhasher.finalize();
+
+                            hasher.update(source);
+                            hasher.update(b" ");
+                            hasher.update(patterns_hash);
+                            hasher.update(b"\n");
+                        },
                     )?;
                     let new_hash = *hasher.finalize().as_ref();
                     let changed = new_hash != dmap.ignore_patterns_hash;
@@ -122,8 +140,8 @@
         ignore_fn,
         outcome: Mutex::new(outcome),
         ignore_patterns_have_changed: patterns_changed,
-        new_cachable_directories: Default::default(),
-        outated_cached_directories: Default::default(),
+        new_cacheable_directories: Default::default(),
+        outdated_cached_directories: Default::default(),
         filesystem_time_at_status_start,
     };
     let is_at_repo_root = true;
@@ -147,12 +165,12 @@
         is_at_repo_root,
     )?;
     let mut outcome = common.outcome.into_inner().unwrap();
-    let new_cachable = common.new_cachable_directories.into_inner().unwrap();
-    let outdated = common.outated_cached_directories.into_inner().unwrap();
+    let new_cacheable = common.new_cacheable_directories.into_inner().unwrap();
+    let outdated = common.outdated_cached_directories.into_inner().unwrap();
 
     outcome.dirty = common.ignore_patterns_have_changed == Some(true)
         || !outdated.is_empty()
-        || (!new_cachable.is_empty()
+        || (!new_cacheable.is_empty()
             && dmap.dirstate_version == DirstateVersion::V2);
 
     // Remove outdated mtimes before adding new mtimes, in case a given
@@ -160,7 +178,7 @@
     for path in &outdated {
         dmap.clear_cached_mtime(path)?;
     }
-    for (path, mtime) in &new_cachable {
+    for (path, mtime) in &new_cacheable {
         dmap.set_cached_mtime(path, *mtime)?;
     }
 
@@ -175,9 +193,11 @@
     matcher: &'a (dyn Matcher + Sync),
     ignore_fn: IgnoreFnType<'a>,
     outcome: Mutex<DirstateStatus<'on_disk>>,
-    new_cachable_directories:
+    /// New timestamps of directories to be used for caching their readdirs
+    new_cacheable_directories:
         Mutex<Vec<(Cow<'on_disk, HgPath>, TruncatedTimestamp)>>,
-    outated_cached_directories: Mutex<Vec<Cow<'on_disk, HgPath>>>,
+    /// Used to invalidate the readdir cache of directories
+    outdated_cached_directories: Mutex<Vec<Cow<'on_disk, HgPath>>>,
 
     /// Whether ignore files like `.hgignore` have changed since the previous
     /// time a `status()` call wrote their hash to the dirstate. `None` means
@@ -305,17 +325,18 @@
     fn check_for_outdated_directory_cache(
         &self,
         dirstate_node: &NodeRef<'tree, 'on_disk>,
-    ) -> Result<(), DirstateV2ParseError> {
+    ) -> Result<bool, DirstateV2ParseError> {
         if self.ignore_patterns_have_changed == Some(true)
             && dirstate_node.cached_directory_mtime()?.is_some()
         {
-            self.outated_cached_directories.lock().unwrap().push(
+            self.outdated_cached_directories.lock().unwrap().push(
                 dirstate_node
                     .full_path_borrowed(self.dmap.on_disk)?
                     .detach_from_tree(),
-            )
+            );
+            return Ok(true);
         }
-        Ok(())
+        Ok(false)
     }
 
     /// If this returns true, we can get accurate results by only using
@@ -487,7 +508,8 @@
         dirstate_node: NodeRef<'tree, 'on_disk>,
         has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>,
     ) -> Result<(), DirstateV2ParseError> {
-        self.check_for_outdated_directory_cache(&dirstate_node)?;
+        let outdated_dircache =
+            self.check_for_outdated_directory_cache(&dirstate_node)?;
         let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?;
         let file_or_symlink = fs_entry.is_file() || fs_entry.is_symlink();
         if !file_or_symlink {
@@ -522,6 +544,7 @@
                 children_all_have_dirstate_node_or_are_ignored,
                 fs_entry,
                 dirstate_node,
+                outdated_dircache,
             )?
         } else {
             if file_or_symlink && self.matcher.matches(&hg_path) {
@@ -561,11 +584,17 @@
         Ok(())
     }
 
+    /// Save directory mtime if applicable.
+    ///
+    /// `outdated_directory_cache` is `true` if we've just invalidated the
+    /// cache for this directory in `check_for_outdated_directory_cache`,
+    /// which forces the update.
     fn maybe_save_directory_mtime(
         &self,
         children_all_have_dirstate_node_or_are_ignored: bool,
         directory_entry: &DirEntry,
         dirstate_node: NodeRef<'tree, 'on_disk>,
+        outdated_directory_cache: bool,
     ) -> Result<(), DirstateV2ParseError> {
         if !children_all_have_dirstate_node_or_are_ignored {
             return Ok(());
@@ -635,17 +664,18 @@
         // We deem this scenario (unlike the previous one) to be
         // unlikely enough in practice.
 
-        let is_up_to_date =
-            if let Some(cached) = dirstate_node.cached_directory_mtime()? {
-                cached.likely_equal(directory_mtime)
-            } else {
-                false
-            };
+        let is_up_to_date = if let Some(cached) =
+            dirstate_node.cached_directory_mtime()?
+        {
+            !outdated_directory_cache && cached.likely_equal(directory_mtime)
+        } else {
+            false
+        };
         if !is_up_to_date {
             let hg_path = dirstate_node
                 .full_path_borrowed(self.dmap.on_disk)?
                 .detach_from_tree();
-            self.new_cachable_directories
+            self.new_cacheable_directories
                 .lock()
                 .unwrap()
                 .push((hg_path, directory_mtime))