comparison rust/hg-core/src/dirstate_tree/status.rs @ 47409:0ef8231e413f

dirstate-v2: Store a hash of ignore patterns (.hgignore) Later, this help extend `read_dir` caching to directories that contain ignored files (but no unknown files). Such cache must be invalidated when ignore patterns change since a formerly-ignored file might become unknown. This helps the default configuration of `hg status` where unknown files must be listed, but ignored files are not. Differential Revision: https://phab.mercurial-scm.org/D10836
author Simon Sapin <simon.sapin@octobus.net>
date Wed, 02 Jun 2021 11:25:18 +0200
parents 04d1f17f49e7
children c657beacdf2e
comparison
equal deleted inserted replaced
47408:7954ee2d7cf7 47409:0ef8231e413f
19 use crate::PatternFileWarning; 19 use crate::PatternFileWarning;
20 use crate::StatusError; 20 use crate::StatusError;
21 use crate::StatusOptions; 21 use crate::StatusOptions;
22 use micro_timer::timed; 22 use micro_timer::timed;
23 use rayon::prelude::*; 23 use rayon::prelude::*;
24 use sha1::{Digest, Sha1};
24 use std::borrow::Cow; 25 use std::borrow::Cow;
25 use std::io; 26 use std::io;
26 use std::path::Path; 27 use std::path::Path;
27 use std::path::PathBuf; 28 use std::path::PathBuf;
28 use std::sync::Mutex; 29 use std::sync::Mutex;
43 matcher: &(dyn Matcher + Sync), 44 matcher: &(dyn Matcher + Sync),
44 root_dir: PathBuf, 45 root_dir: PathBuf,
45 ignore_files: Vec<PathBuf>, 46 ignore_files: Vec<PathBuf>,
46 options: StatusOptions, 47 options: StatusOptions,
47 ) -> Result<(DirstateStatus<'on_disk>, Vec<PatternFileWarning>), StatusError> { 48 ) -> Result<(DirstateStatus<'on_disk>, Vec<PatternFileWarning>), StatusError> {
48 let (ignore_fn, warnings): (IgnoreFnType, _) = 49 let (ignore_fn, warnings, patterns_changed): (IgnoreFnType, _, _) =
49 if options.list_ignored || options.list_unknown { 50 if options.list_ignored || options.list_unknown {
50 get_ignore_function(ignore_files, &root_dir)? 51 let mut hasher = Sha1::new();
52 let (ignore_fn, warnings) = get_ignore_function(
53 ignore_files,
54 &root_dir,
55 &mut |pattern_bytes| hasher.update(pattern_bytes),
56 )?;
57 let new_hash = *hasher.finalize().as_ref();
58 let changed = new_hash != dmap.ignore_patterns_hash;
59 dmap.ignore_patterns_hash = new_hash;
60 (ignore_fn, warnings, Some(changed))
51 } else { 61 } else {
52 (Box::new(|&_| true), vec![]) 62 (Box::new(|&_| true), vec![], None)
53 }; 63 };
54 64
55 let common = StatusCommon { 65 let common = StatusCommon {
56 dmap, 66 dmap,
57 options, 67 options,
58 matcher, 68 matcher,
59 ignore_fn, 69 ignore_fn,
60 outcome: Default::default(), 70 outcome: Default::default(),
61 cached_directory_mtimes_to_add: Default::default(), 71 ignore_patterns_have_changed: patterns_changed,
72 new_cachable_directories: Default::default(),
62 filesystem_time_at_status_start: filesystem_now(&root_dir).ok(), 73 filesystem_time_at_status_start: filesystem_now(&root_dir).ok(),
63 }; 74 };
64 let is_at_repo_root = true; 75 let is_at_repo_root = true;
65 let hg_path = &BorrowedPath::OnDisk(HgPath::new("")); 76 let hg_path = &BorrowedPath::OnDisk(HgPath::new(""));
66 let has_ignored_ancestor = false; 77 let has_ignored_ancestor = false;
77 root_dir_metadata, 88 root_dir_metadata,
78 root_cached_mtime, 89 root_cached_mtime,
79 is_at_repo_root, 90 is_at_repo_root,
80 )?; 91 )?;
81 let mut outcome = common.outcome.into_inner().unwrap(); 92 let mut outcome = common.outcome.into_inner().unwrap();
82 let to_add = common.cached_directory_mtimes_to_add.into_inner().unwrap(); 93 let new_cachable = common.new_cachable_directories.into_inner().unwrap();
83 outcome.dirty = !to_add.is_empty(); 94
84 for (path, mtime) in &to_add { 95 outcome.dirty = common.ignore_patterns_have_changed == Some(true)
96 || !new_cachable.is_empty();
97
98 for (path, mtime) in &new_cachable {
85 let node = DirstateMap::get_or_insert_node( 99 let node = DirstateMap::get_or_insert_node(
86 dmap.on_disk, 100 dmap.on_disk,
87 &mut dmap.root, 101 &mut dmap.root,
88 path, 102 path,
89 WithBasename::to_cow_owned, 103 WithBasename::to_cow_owned,
94 NodeData::CachedDirectory { .. } | NodeData::None => { 108 NodeData::CachedDirectory { .. } | NodeData::None => {
95 node.data = NodeData::CachedDirectory { mtime: *mtime } 109 node.data = NodeData::CachedDirectory { mtime: *mtime }
96 } 110 }
97 } 111 }
98 } 112 }
113
99 Ok((outcome, warnings)) 114 Ok((outcome, warnings))
100 } 115 }
101 116
102 /// Bag of random things needed by various parts of the algorithm. Reduces the 117 /// Bag of random things needed by various parts of the algorithm. Reduces the
103 /// number of parameters passed to functions. 118 /// number of parameters passed to functions.
105 dmap: &'tree DirstateMap<'on_disk>, 120 dmap: &'tree DirstateMap<'on_disk>,
106 options: StatusOptions, 121 options: StatusOptions,
107 matcher: &'a (dyn Matcher + Sync), 122 matcher: &'a (dyn Matcher + Sync),
108 ignore_fn: IgnoreFnType<'a>, 123 ignore_fn: IgnoreFnType<'a>,
109 outcome: Mutex<DirstateStatus<'on_disk>>, 124 outcome: Mutex<DirstateStatus<'on_disk>>,
110 cached_directory_mtimes_to_add: 125 new_cachable_directories: Mutex<Vec<(Cow<'on_disk, HgPath>, Timestamp)>>,
111 Mutex<Vec<(Cow<'on_disk, HgPath>, Timestamp)>>, 126
127 /// Whether ignore files like `.hgignore` have changed since the previous
128 /// time a `status()` call wrote their hash to the dirstate. `None` means
129 /// we don’t know as this run doesn’t list either ignored or uknown files
130 /// and therefore isn’t reading `.hgignore`.
131 ignore_patterns_have_changed: Option<bool>,
112 132
113 /// The current time at the start of the `status()` algorithm, as measured 133 /// The current time at the start of the `status()` algorithm, as measured
114 /// and possibly truncated by the filesystem. 134 /// and possibly truncated by the filesystem.
115 filesystem_time_at_status_start: Option<SystemTime>, 135 filesystem_time_at_status_start: Option<SystemTime>,
116 } 136 }
420 let cached = dirstate_node.cached_directory_mtime(); 440 let cached = dirstate_node.cached_directory_mtime();
421 if cached != Some(&timestamp) { 441 if cached != Some(&timestamp) {
422 let hg_path = dirstate_node 442 let hg_path = dirstate_node
423 .full_path_borrowed(self.dmap.on_disk)? 443 .full_path_borrowed(self.dmap.on_disk)?
424 .detach_from_tree(); 444 .detach_from_tree();
425 self.cached_directory_mtimes_to_add 445 self.new_cachable_directories
426 .lock() 446 .lock()
427 .unwrap() 447 .unwrap()
428 .push((hg_path, timestamp)) 448 .push((hg_path, timestamp))
429 } 449 }
430 } 450 }