Mercurial > public > mercurial-scm > hg-stable
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 |