rust/hg-core/src/dirstate_tree/status.rs
changeset 47409 0ef8231e413f
parent 47350 04d1f17f49e7
child 47474 c657beacdf2e
--- a/rust/hg-core/src/dirstate_tree/status.rs	Mon Jun 07 17:29:32 2021 +0530
+++ b/rust/hg-core/src/dirstate_tree/status.rs	Wed Jun 02 11:25:18 2021 +0200
@@ -21,6 +21,7 @@
 use crate::StatusOptions;
 use micro_timer::timed;
 use rayon::prelude::*;
+use sha1::{Digest, Sha1};
 use std::borrow::Cow;
 use std::io;
 use std::path::Path;
@@ -45,11 +46,20 @@
     ignore_files: Vec<PathBuf>,
     options: StatusOptions,
 ) -> Result<(DirstateStatus<'on_disk>, Vec<PatternFileWarning>), StatusError> {
-    let (ignore_fn, warnings): (IgnoreFnType, _) =
+    let (ignore_fn, warnings, patterns_changed): (IgnoreFnType, _, _) =
         if options.list_ignored || options.list_unknown {
-            get_ignore_function(ignore_files, &root_dir)?
+            let mut hasher = Sha1::new();
+            let (ignore_fn, warnings) = get_ignore_function(
+                ignore_files,
+                &root_dir,
+                &mut |pattern_bytes| hasher.update(pattern_bytes),
+            )?;
+            let new_hash = *hasher.finalize().as_ref();
+            let changed = new_hash != dmap.ignore_patterns_hash;
+            dmap.ignore_patterns_hash = new_hash;
+            (ignore_fn, warnings, Some(changed))
         } else {
-            (Box::new(|&_| true), vec![])
+            (Box::new(|&_| true), vec![], None)
         };
 
     let common = StatusCommon {
@@ -58,7 +68,8 @@
         matcher,
         ignore_fn,
         outcome: Default::default(),
-        cached_directory_mtimes_to_add: Default::default(),
+        ignore_patterns_have_changed: patterns_changed,
+        new_cachable_directories: Default::default(),
         filesystem_time_at_status_start: filesystem_now(&root_dir).ok(),
     };
     let is_at_repo_root = true;
@@ -79,9 +90,12 @@
         is_at_repo_root,
     )?;
     let mut outcome = common.outcome.into_inner().unwrap();
-    let to_add = common.cached_directory_mtimes_to_add.into_inner().unwrap();
-    outcome.dirty = !to_add.is_empty();
-    for (path, mtime) in &to_add {
+    let new_cachable = common.new_cachable_directories.into_inner().unwrap();
+
+    outcome.dirty = common.ignore_patterns_have_changed == Some(true)
+        || !new_cachable.is_empty();
+
+    for (path, mtime) in &new_cachable {
         let node = DirstateMap::get_or_insert_node(
             dmap.on_disk,
             &mut dmap.root,
@@ -96,6 +110,7 @@
             }
         }
     }
+
     Ok((outcome, warnings))
 }
 
@@ -107,8 +122,13 @@
     matcher: &'a (dyn Matcher + Sync),
     ignore_fn: IgnoreFnType<'a>,
     outcome: Mutex<DirstateStatus<'on_disk>>,
-    cached_directory_mtimes_to_add:
-        Mutex<Vec<(Cow<'on_disk, HgPath>, Timestamp)>>,
+    new_cachable_directories: Mutex<Vec<(Cow<'on_disk, HgPath>, Timestamp)>>,
+
+    /// Whether ignore files like `.hgignore` have changed since the previous
+    /// time a `status()` call wrote their hash to the dirstate. `None` means
+    /// we don’t know as this run doesn’t list either ignored or uknown files
+    /// and therefore isn’t reading `.hgignore`.
+    ignore_patterns_have_changed: Option<bool>,
 
     /// The current time at the start of the `status()` algorithm, as measured
     /// and possibly truncated by the filesystem.
@@ -422,7 +442,7 @@
                         let hg_path = dirstate_node
                             .full_path_borrowed(self.dmap.on_disk)?
                             .detach_from_tree();
-                        self.cached_directory_mtimes_to_add
+                        self.new_cachable_directories
                             .lock()
                             .unwrap()
                             .push((hg_path, timestamp))