diff rust/hg-core/src/dirstate/status.rs @ 44597:e62052d0f377

rust-status: only involve ignore mechanism when needed This prevents unnecessary fallbacks to Python, improving performance for `hg update` for instance. On Mozilla-Central a noop update goes from 1.6s down to 700ms. Differential Revision: https://phab.mercurial-scm.org/D8315
author Rapha?l Gom?s <rgomes@octobus.net>
date Fri, 20 Mar 2020 15:21:34 +0100
parents ece43c79333e
children d8b703b8bf70
line wrap: on
line diff
--- a/rust/hg-core/src/dirstate/status.rs	Thu Mar 26 00:07:12 2020 +0900
+++ b/rust/hg-core/src/dirstate/status.rs	Fri Mar 20 15:21:34 2020 +0100
@@ -33,7 +33,7 @@
     fs::{read_dir, DirEntry},
     io::ErrorKind,
     ops::Deref,
-    path::Path,
+    path::{Path, PathBuf},
 };
 
 /// Wrong type of file from a `BadMatch`
@@ -94,6 +94,9 @@
 }
 
 type IoResult<T> = std::io::Result<T>;
+/// `Box<dyn Trait>` is syntactic sugar for `Box<dyn Trait, 'static>`, so add
+/// an explicit lifetime here to not fight `'static` bounds "out of nowhere".
+type IgnoreFnType<'a> = Box<dyn for<'r> Fn(&'r HgPath) -> bool + Sync + 'a>;
 
 /// Dates and times that are outside the 31-bit signed range are compared
 /// modulo 2^31. This should prevent hg from behaving badly with very large
@@ -312,8 +315,8 @@
     root_dir: impl AsRef<Path> + Sync + Send + Copy + 'a,
     dmap: &'a DirstateMap,
     old_results: &'a FastHashMap<Cow<HgPath>, Dispatch>,
-    ignore_fn: &'a (impl for<'r> Fn(&'r HgPath) -> bool + Sync),
-    dir_ignore_fn: &'a (impl for<'r> Fn(&'r HgPath) -> bool + Sync),
+    ignore_fn: &'a IgnoreFnType,
+    dir_ignore_fn: &'a IgnoreFnType,
     options: StatusOptions,
     filename: HgPathBuf,
     dir_entry: DirEntry,
@@ -393,8 +396,8 @@
     root_dir: impl AsRef<Path> + Sync + Send + Copy + 'a,
     dmap: &'a DirstateMap,
     old_results: &'a FastHashMap<Cow<HgPath>, Dispatch>,
-    ignore_fn: &'a (impl for<'r> Fn(&'r HgPath) -> bool + Sync),
-    dir_ignore_fn: &'a (impl for<'r> Fn(&'r HgPath) -> bool + Sync),
+    ignore_fn: &'a IgnoreFnType,
+    dir_ignore_fn: &'a IgnoreFnType,
     options: StatusOptions,
     entry_option: Option<&'a DirstateEntry>,
     directory: HgPathBuf,
@@ -439,8 +442,8 @@
     dmap: &'a DirstateMap,
     directory: impl AsRef<HgPath>,
     old_results: &FastHashMap<Cow<'a, HgPath>, Dispatch>,
-    ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
-    dir_ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
+    ignore_fn: &IgnoreFnType,
+    dir_ignore_fn: &IgnoreFnType,
     options: StatusOptions,
 ) -> IoResult<()> {
     let directory = directory.as_ref();
@@ -522,8 +525,8 @@
     dmap: &'a DirstateMap,
     path: impl AsRef<HgPath>,
     old_results: &FastHashMap<Cow<'a, HgPath>, Dispatch>,
-    ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
-    dir_ignore_fn: &(impl for<'r> Fn(&'r HgPath) -> bool + Sync),
+    ignore_fn: &IgnoreFnType,
+    dir_ignore_fn: &IgnoreFnType,
     options: StatusOptions,
     results: &mut Vec<(Cow<'a, HgPath>, Dispatch)>,
 ) -> IoResult<()> {
@@ -805,26 +808,39 @@
     dmap: &'a DirstateMap,
     matcher: &'b (impl Matcher + Sync),
     root_dir: impl AsRef<Path> + Sync + Send + Copy + 'c,
-    ignore_files: &[impl AsRef<Path> + 'c],
+    ignore_files: Vec<PathBuf>,
     options: StatusOptions,
 ) -> StatusResult<(
     (Vec<Cow<'c, HgPath>>, DirstateStatus<'c>),
     Vec<PatternFileWarning>,
 )> {
-    let (ignore_fn, warnings) = get_ignore_function(&ignore_files, root_dir)?;
+    // Needs to outlive `dir_ignore_fn` since it's captured.
+    let mut ignore_fn: IgnoreFnType;
+
+    // Only involve real ignore mechanism if we're listing unknowns or ignored.
+    let (dir_ignore_fn, warnings): (IgnoreFnType, _) = if options.list_ignored
+        || options.list_unknown
+    {
+        let (ignore, warnings) = get_ignore_function(ignore_files, root_dir)?;
 
-    // Is the path or one of its ancestors ignored?
-    let dir_ignore_fn = |dir: &_| {
-        if ignore_fn(dir) {
-            true
-        } else {
-            for p in find_dirs(dir) {
-                if ignore_fn(p) {
-                    return true;
+        ignore_fn = ignore;
+        let dir_ignore_fn = Box::new(|dir: &_| {
+            // Is the path or one of its ancestors ignored?
+            if ignore_fn(dir) {
+                true
+            } else {
+                for p in find_dirs(dir) {
+                    if ignore_fn(p) {
+                        return true;
+                    }
                 }
+                false
             }
-            false
-        }
+        });
+        (dir_ignore_fn, warnings)
+    } else {
+        ignore_fn = Box::new(|&_| true);
+        (Box::new(|&_| true), vec![])
     };
 
     let files = matcher.file_set();