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
--- 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();
--- a/rust/hg-core/src/matchers.rs Thu Mar 26 00:07:12 2020 +0900
+++ b/rust/hg-core/src/matchers.rs Fri Mar 20 15:21:34 2020 +0100
@@ -24,12 +24,12 @@
PatternSyntax,
};
-use micro_timer::timed;
+use std::borrow::ToOwned;
use std::collections::HashSet;
use std::fmt::{Display, Error, Formatter};
use std::iter::FromIterator;
use std::ops::Deref;
-use std::path::Path;
+use std::path::{Path, PathBuf};
#[derive(Debug, PartialEq)]
pub enum VisitChildrenSet<'a> {
@@ -507,7 +507,8 @@
let mut prefixes = vec![];
for SubInclude { prefix, root, path } in subincludes.into_iter() {
- let (match_fn, warnings) = get_ignore_function(&[path], root)?;
+ let (match_fn, warnings) =
+ get_ignore_function(vec![path.to_path_buf()], root)?;
all_warnings.extend(warnings);
prefixes.push(prefix.to_owned());
submatchers.insert(prefix.to_owned(), match_fn);
@@ -578,12 +579,11 @@
/// Parses all "ignore" files with their recursive includes and returns a
/// function that checks whether a given file (in the general sense) should be
/// ignored.
-#[timed]
pub fn get_ignore_function<'a>(
- all_pattern_files: &[impl AsRef<Path>],
+ all_pattern_files: Vec<PathBuf>,
root_dir: impl AsRef<Path>,
) -> PatternResult<(
- impl for<'r> Fn(&'r HgPath) -> bool + Sync,
+ Box<dyn for<'r> Fn(&'r HgPath) -> bool + Sync + 'a>,
Vec<PatternFileWarning>,
)> {
let mut all_patterns = vec![];
@@ -593,12 +593,15 @@
let (patterns, warnings) =
get_patterns_from_file(pattern_file, &root_dir)?;
- all_patterns.extend(patterns);
+ all_patterns.extend(patterns.to_owned());
all_warnings.extend(warnings);
}
let (matcher, warnings) = IncludeMatcher::new(all_patterns, root_dir)?;
all_warnings.extend(warnings);
- Ok((move |path: &HgPath| matcher.matches(path), all_warnings))
+ Ok((
+ Box::new(move |path: &HgPath| matcher.matches(path)),
+ all_warnings,
+ ))
}
impl<'a> IncludeMatcher<'a> {
--- a/rust/hg-cpython/src/dirstate/status.rs Thu Mar 26 00:07:12 2020 +0900
+++ b/rust/hg-cpython/src/dirstate/status.rs Fri Mar 20 15:21:34 2020 +0100
@@ -127,7 +127,7 @@
&dmap,
&matcher,
&root_dir,
- &ignore_files,
+ ignore_files,
StatusOptions {
check_exec,
last_normal_time,
@@ -163,7 +163,7 @@
&dmap,
&matcher,
&root_dir,
- &ignore_files,
+ ignore_files,
StatusOptions {
check_exec,
last_normal_time,
@@ -217,7 +217,7 @@
&dmap,
&matcher,
&root_dir,
- &ignore_files,
+ ignore_files,
StatusOptions {
check_exec,
last_normal_time,