comparison rust/hg-core/src/dirstate_tree/status.rs @ 47479:c657beacdf2e

dirstate-v2: Drop cached read_dir results after .hgignore changes Soon we?ll want the status algorithm to be able to skip `std::fs::read_dir` in more cases, notabling when listing unknown files but not ignored files. When ignore patterns change (which we detect by their hash, added to the dirstate-v2 format in a previous changeset), a formerly-ignored file could become unknown without changing its parent directory?s modification time. Therefore we remove any directory mtime from the dirstate, effictively invalidating the existing caches. Differential Revision: https://phab.mercurial-scm.org/D10907
author Simon Sapin <simon.sapin@octobus.net>
date Fri, 04 Jun 2021 15:26:38 +0200
parents 0ef8231e413f
children 94e38822d395
comparison
equal deleted inserted replaced
47478:5045ba2a3afd 47479:c657beacdf2e
4 use crate::dirstate_tree::dirstate_map::DirstateMap; 4 use crate::dirstate_tree::dirstate_map::DirstateMap;
5 use crate::dirstate_tree::dirstate_map::NodeData; 5 use crate::dirstate_tree::dirstate_map::NodeData;
6 use crate::dirstate_tree::dirstate_map::NodeRef; 6 use crate::dirstate_tree::dirstate_map::NodeRef;
7 use crate::dirstate_tree::on_disk::DirstateV2ParseError; 7 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
8 use crate::dirstate_tree::on_disk::Timestamp; 8 use crate::dirstate_tree::on_disk::Timestamp;
9 use crate::dirstate_tree::path_with_basename::WithBasename;
10 use crate::matchers::get_ignore_function; 9 use crate::matchers::get_ignore_function;
11 use crate::matchers::Matcher; 10 use crate::matchers::Matcher;
12 use crate::utils::files::get_bytes_from_os_string; 11 use crate::utils::files::get_bytes_from_os_string;
13 use crate::utils::files::get_path_from_bytes; 12 use crate::utils::files::get_path_from_bytes;
14 use crate::utils::hg_path::HgPath; 13 use crate::utils::hg_path::HgPath;
68 matcher, 67 matcher,
69 ignore_fn, 68 ignore_fn,
70 outcome: Default::default(), 69 outcome: Default::default(),
71 ignore_patterns_have_changed: patterns_changed, 70 ignore_patterns_have_changed: patterns_changed,
72 new_cachable_directories: Default::default(), 71 new_cachable_directories: Default::default(),
72 outated_cached_directories: Default::default(),
73 filesystem_time_at_status_start: filesystem_now(&root_dir).ok(), 73 filesystem_time_at_status_start: filesystem_now(&root_dir).ok(),
74 }; 74 };
75 let is_at_repo_root = true; 75 let is_at_repo_root = true;
76 let hg_path = &BorrowedPath::OnDisk(HgPath::new("")); 76 let hg_path = &BorrowedPath::OnDisk(HgPath::new(""));
77 let has_ignored_ancestor = false; 77 let has_ignored_ancestor = false;
89 root_cached_mtime, 89 root_cached_mtime,
90 is_at_repo_root, 90 is_at_repo_root,
91 )?; 91 )?;
92 let mut outcome = common.outcome.into_inner().unwrap(); 92 let mut outcome = common.outcome.into_inner().unwrap();
93 let new_cachable = common.new_cachable_directories.into_inner().unwrap(); 93 let new_cachable = common.new_cachable_directories.into_inner().unwrap();
94 let outdated = common.outated_cached_directories.into_inner().unwrap();
94 95
95 outcome.dirty = common.ignore_patterns_have_changed == Some(true) 96 outcome.dirty = common.ignore_patterns_have_changed == Some(true)
97 || !outdated.is_empty()
96 || !new_cachable.is_empty(); 98 || !new_cachable.is_empty();
97 99
100 // Remove outdated mtimes before adding new mtimes, in case a given
101 // directory is both
102 for path in &outdated {
103 let node = dmap.get_or_insert(path)?;
104 if let NodeData::CachedDirectory { .. } = &node.data {
105 node.data = NodeData::None
106 }
107 }
98 for (path, mtime) in &new_cachable { 108 for (path, mtime) in &new_cachable {
99 let node = DirstateMap::get_or_insert_node( 109 let node = dmap.get_or_insert(path)?;
100 dmap.on_disk,
101 &mut dmap.root,
102 path,
103 WithBasename::to_cow_owned,
104 |_| {},
105 )?;
106 match &node.data { 110 match &node.data {
107 NodeData::Entry(_) => {} // Don’t overwrite an entry 111 NodeData::Entry(_) => {} // Don’t overwrite an entry
108 NodeData::CachedDirectory { .. } | NodeData::None => { 112 NodeData::CachedDirectory { .. } | NodeData::None => {
109 node.data = NodeData::CachedDirectory { mtime: *mtime } 113 node.data = NodeData::CachedDirectory { mtime: *mtime }
110 } 114 }
121 options: StatusOptions, 125 options: StatusOptions,
122 matcher: &'a (dyn Matcher + Sync), 126 matcher: &'a (dyn Matcher + Sync),
123 ignore_fn: IgnoreFnType<'a>, 127 ignore_fn: IgnoreFnType<'a>,
124 outcome: Mutex<DirstateStatus<'on_disk>>, 128 outcome: Mutex<DirstateStatus<'on_disk>>,
125 new_cachable_directories: Mutex<Vec<(Cow<'on_disk, HgPath>, Timestamp)>>, 129 new_cachable_directories: Mutex<Vec<(Cow<'on_disk, HgPath>, Timestamp)>>,
130 outated_cached_directories: Mutex<Vec<Cow<'on_disk, HgPath>>>,
126 131
127 /// Whether ignore files like `.hgignore` have changed since the previous 132 /// Whether ignore files like `.hgignore` have changed since the previous
128 /// time a `status()` call wrote their hash to the dirstate. `None` means 133 /// 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 134 /// we don’t know as this run doesn’t list either ignored or uknown files
130 /// and therefore isn’t reading `.hgignore`. 135 /// and therefore isn’t reading `.hgignore`.
151 self.outcome 156 self.outcome
152 .lock() 157 .lock()
153 .unwrap() 158 .unwrap()
154 .bad 159 .bad
155 .push((hg_path.to_owned().into(), BadMatch::OsError(errno))) 160 .push((hg_path.to_owned().into(), BadMatch::OsError(errno)))
161 }
162
163 fn check_for_outdated_directory_cache(
164 &self,
165 dirstate_node: &NodeRef<'tree, 'on_disk>,
166 ) -> Result<(), DirstateV2ParseError> {
167 if self.ignore_patterns_have_changed == Some(true)
168 && dirstate_node.cached_directory_mtime().is_some()
169 {
170 self.outated_cached_directories.lock().unwrap().push(
171 dirstate_node
172 .full_path_borrowed(self.dmap.on_disk)?
173 .detach_from_tree(),
174 )
175 }
176 Ok(())
156 } 177 }
157 178
158 /// If this returns true, we can get accurate results by only using 179 /// If this returns true, we can get accurate results by only using
159 /// `symlink_metadata` for child nodes that exist in the dirstate and don’t 180 /// `symlink_metadata` for child nodes that exist in the dirstate and don’t
160 /// need to call `read_dir`. 181 /// need to call `read_dir`.
302 fs_path: &Path, 323 fs_path: &Path,
303 fs_metadata: &std::fs::Metadata, 324 fs_metadata: &std::fs::Metadata,
304 dirstate_node: NodeRef<'tree, 'on_disk>, 325 dirstate_node: NodeRef<'tree, 'on_disk>,
305 has_ignored_ancestor: bool, 326 has_ignored_ancestor: bool,
306 ) -> Result<(), DirstateV2ParseError> { 327 ) -> Result<(), DirstateV2ParseError> {
328 self.check_for_outdated_directory_cache(&dirstate_node)?;
307 let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?; 329 let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?;
308 let file_type = fs_metadata.file_type(); 330 let file_type = fs_metadata.file_type();
309 let file_or_symlink = file_type.is_file() || file_type.is_symlink(); 331 let file_or_symlink = file_type.is_file() || file_type.is_symlink();
310 if !file_or_symlink { 332 if !file_or_symlink {
311 // If we previously had a file here, it was removed (with 333 // If we previously had a file here, it was removed (with
519 /// A node in the dirstate tree has no corresponding filesystem entry 541 /// A node in the dirstate tree has no corresponding filesystem entry
520 fn traverse_dirstate_only( 542 fn traverse_dirstate_only(
521 &self, 543 &self,
522 dirstate_node: NodeRef<'tree, 'on_disk>, 544 dirstate_node: NodeRef<'tree, 'on_disk>,
523 ) -> Result<(), DirstateV2ParseError> { 545 ) -> Result<(), DirstateV2ParseError> {
546 self.check_for_outdated_directory_cache(&dirstate_node)?;
524 self.mark_removed_or_deleted_if_file( 547 self.mark_removed_or_deleted_if_file(
525 &dirstate_node.full_path_borrowed(self.dmap.on_disk)?, 548 &dirstate_node.full_path_borrowed(self.dmap.on_disk)?,
526 dirstate_node.state()?, 549 dirstate_node.state()?,
527 ); 550 );
528 dirstate_node 551 dirstate_node