comparison rust/hg-core/src/dirstate_tree/status.rs @ 49585:18282cf18aa2

branching: merge stable into default
author Rapha?l Gom?s <rgomes@octobus.net>
date Mon, 14 Nov 2022 10:59:09 +0100
parents da48f170d203 363923bd51cd
children c7fb9b74e753
comparison
equal deleted inserted replaced
49577:53e4f44ba0e8 49585:18282cf18aa2
8 use crate::dirstate_tree::dirstate_map::NodeRef; 8 use crate::dirstate_tree::dirstate_map::NodeRef;
9 use crate::dirstate_tree::on_disk::DirstateV2ParseError; 9 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
10 use crate::matchers::get_ignore_function; 10 use crate::matchers::get_ignore_function;
11 use crate::matchers::Matcher; 11 use crate::matchers::Matcher;
12 use crate::utils::files::get_bytes_from_os_string; 12 use crate::utils::files::get_bytes_from_os_string;
13 use crate::utils::files::get_bytes_from_path;
13 use crate::utils::files::get_path_from_bytes; 14 use crate::utils::files::get_path_from_bytes;
14 use crate::utils::hg_path::HgPath; 15 use crate::utils::hg_path::HgPath;
15 use crate::BadMatch; 16 use crate::BadMatch;
16 use crate::DirstateStatus; 17 use crate::DirstateStatus;
17 use crate::HgPathCow; 18 use crate::HgPathCow;
65 let (ignore_fn, warnings, changed) = match dmap.dirstate_version { 66 let (ignore_fn, warnings, changed) = match dmap.dirstate_version {
66 DirstateVersion::V1 => { 67 DirstateVersion::V1 => {
67 let (ignore_fn, warnings) = get_ignore_function( 68 let (ignore_fn, warnings) = get_ignore_function(
68 ignore_files, 69 ignore_files,
69 &root_dir, 70 &root_dir,
70 &mut |_pattern_bytes| {}, 71 &mut |_source, _pattern_bytes| {},
71 )?; 72 )?;
72 (ignore_fn, warnings, None) 73 (ignore_fn, warnings, None)
73 } 74 }
74 DirstateVersion::V2 => { 75 DirstateVersion::V2 => {
75 let mut hasher = Sha1::new(); 76 let mut hasher = Sha1::new();
76 let (ignore_fn, warnings) = get_ignore_function( 77 let (ignore_fn, warnings) = get_ignore_function(
77 ignore_files, 78 ignore_files,
78 &root_dir, 79 &root_dir,
79 &mut |pattern_bytes| hasher.update(pattern_bytes), 80 &mut |source, pattern_bytes| {
81 // If inside the repo, use the relative version to
82 // make it deterministic inside tests.
83 // The performance hit should be negligible.
84 let source = source
85 .strip_prefix(&root_dir)
86 .unwrap_or(source);
87 let source = get_bytes_from_path(source);
88
89 let mut subhasher = Sha1::new();
90 subhasher.update(pattern_bytes);
91 let patterns_hash = subhasher.finalize();
92
93 hasher.update(source);
94 hasher.update(b" ");
95 hasher.update(patterns_hash);
96 hasher.update(b"\n");
97 },
80 )?; 98 )?;
81 let new_hash = *hasher.finalize().as_ref(); 99 let new_hash = *hasher.finalize().as_ref();
82 let changed = new_hash != dmap.ignore_patterns_hash; 100 let changed = new_hash != dmap.ignore_patterns_hash;
83 dmap.ignore_patterns_hash = new_hash; 101 dmap.ignore_patterns_hash = new_hash;
84 (ignore_fn, warnings, Some(changed)) 102 (ignore_fn, warnings, Some(changed))
120 options, 138 options,
121 matcher, 139 matcher,
122 ignore_fn, 140 ignore_fn,
123 outcome: Mutex::new(outcome), 141 outcome: Mutex::new(outcome),
124 ignore_patterns_have_changed: patterns_changed, 142 ignore_patterns_have_changed: patterns_changed,
125 new_cachable_directories: Default::default(), 143 new_cacheable_directories: Default::default(),
126 outated_cached_directories: Default::default(), 144 outdated_cached_directories: Default::default(),
127 filesystem_time_at_status_start, 145 filesystem_time_at_status_start,
128 }; 146 };
129 let is_at_repo_root = true; 147 let is_at_repo_root = true;
130 let hg_path = &BorrowedPath::OnDisk(HgPath::new("")); 148 let hg_path = &BorrowedPath::OnDisk(HgPath::new(""));
131 let has_ignored_ancestor = HasIgnoredAncestor::create(None, hg_path); 149 let has_ignored_ancestor = HasIgnoredAncestor::create(None, hg_path);
145 }, 163 },
146 root_cached_mtime, 164 root_cached_mtime,
147 is_at_repo_root, 165 is_at_repo_root,
148 )?; 166 )?;
149 let mut outcome = common.outcome.into_inner().unwrap(); 167 let mut outcome = common.outcome.into_inner().unwrap();
150 let new_cachable = common.new_cachable_directories.into_inner().unwrap(); 168 let new_cacheable = common.new_cacheable_directories.into_inner().unwrap();
151 let outdated = common.outated_cached_directories.into_inner().unwrap(); 169 let outdated = common.outdated_cached_directories.into_inner().unwrap();
152 170
153 outcome.dirty = common.ignore_patterns_have_changed == Some(true) 171 outcome.dirty = common.ignore_patterns_have_changed == Some(true)
154 || !outdated.is_empty() 172 || !outdated.is_empty()
155 || (!new_cachable.is_empty() 173 || (!new_cacheable.is_empty()
156 && dmap.dirstate_version == DirstateVersion::V2); 174 && dmap.dirstate_version == DirstateVersion::V2);
157 175
158 // Remove outdated mtimes before adding new mtimes, in case a given 176 // Remove outdated mtimes before adding new mtimes, in case a given
159 // directory is both 177 // directory is both
160 for path in &outdated { 178 for path in &outdated {
161 dmap.clear_cached_mtime(path)?; 179 dmap.clear_cached_mtime(path)?;
162 } 180 }
163 for (path, mtime) in &new_cachable { 181 for (path, mtime) in &new_cacheable {
164 dmap.set_cached_mtime(path, *mtime)?; 182 dmap.set_cached_mtime(path, *mtime)?;
165 } 183 }
166 184
167 Ok((outcome, warnings)) 185 Ok((outcome, warnings))
168 } 186 }
173 dmap: &'tree DirstateMap<'on_disk>, 191 dmap: &'tree DirstateMap<'on_disk>,
174 options: StatusOptions, 192 options: StatusOptions,
175 matcher: &'a (dyn Matcher + Sync), 193 matcher: &'a (dyn Matcher + Sync),
176 ignore_fn: IgnoreFnType<'a>, 194 ignore_fn: IgnoreFnType<'a>,
177 outcome: Mutex<DirstateStatus<'on_disk>>, 195 outcome: Mutex<DirstateStatus<'on_disk>>,
178 new_cachable_directories: 196 /// New timestamps of directories to be used for caching their readdirs
197 new_cacheable_directories:
179 Mutex<Vec<(Cow<'on_disk, HgPath>, TruncatedTimestamp)>>, 198 Mutex<Vec<(Cow<'on_disk, HgPath>, TruncatedTimestamp)>>,
180 outated_cached_directories: Mutex<Vec<Cow<'on_disk, HgPath>>>, 199 /// Used to invalidate the readdir cache of directories
200 outdated_cached_directories: Mutex<Vec<Cow<'on_disk, HgPath>>>,
181 201
182 /// Whether ignore files like `.hgignore` have changed since the previous 202 /// Whether ignore files like `.hgignore` have changed since the previous
183 /// time a `status()` call wrote their hash to the dirstate. `None` means 203 /// time a `status()` call wrote their hash to the dirstate. `None` means
184 /// we don’t know as this run doesn’t list either ignored or uknown files 204 /// we don’t know as this run doesn’t list either ignored or uknown files
185 /// and therefore isn’t reading `.hgignore`. 205 /// and therefore isn’t reading `.hgignore`.
303 } 323 }
304 324
305 fn check_for_outdated_directory_cache( 325 fn check_for_outdated_directory_cache(
306 &self, 326 &self,
307 dirstate_node: &NodeRef<'tree, 'on_disk>, 327 dirstate_node: &NodeRef<'tree, 'on_disk>,
308 ) -> Result<(), DirstateV2ParseError> { 328 ) -> Result<bool, DirstateV2ParseError> {
309 if self.ignore_patterns_have_changed == Some(true) 329 if self.ignore_patterns_have_changed == Some(true)
310 && dirstate_node.cached_directory_mtime()?.is_some() 330 && dirstate_node.cached_directory_mtime()?.is_some()
311 { 331 {
312 self.outated_cached_directories.lock().unwrap().push( 332 self.outdated_cached_directories.lock().unwrap().push(
313 dirstate_node 333 dirstate_node
314 .full_path_borrowed(self.dmap.on_disk)? 334 .full_path_borrowed(self.dmap.on_disk)?
315 .detach_from_tree(), 335 .detach_from_tree(),
316 ) 336 );
317 } 337 return Ok(true);
318 Ok(()) 338 }
339 Ok(false)
319 } 340 }
320 341
321 /// If this returns true, we can get accurate results by only using 342 /// If this returns true, we can get accurate results by only using
322 /// `symlink_metadata` for child nodes that exist in the dirstate and don’t 343 /// `symlink_metadata` for child nodes that exist in the dirstate and don’t
323 /// need to call `read_dir`. 344 /// need to call `read_dir`.
485 &self, 506 &self,
486 fs_entry: &DirEntry, 507 fs_entry: &DirEntry,
487 dirstate_node: NodeRef<'tree, 'on_disk>, 508 dirstate_node: NodeRef<'tree, 'on_disk>,
488 has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>, 509 has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>,
489 ) -> Result<(), DirstateV2ParseError> { 510 ) -> Result<(), DirstateV2ParseError> {
490 self.check_for_outdated_directory_cache(&dirstate_node)?; 511 let outdated_dircache =
512 self.check_for_outdated_directory_cache(&dirstate_node)?;
491 let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?; 513 let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?;
492 let file_or_symlink = fs_entry.is_file() || fs_entry.is_symlink(); 514 let file_or_symlink = fs_entry.is_file() || fs_entry.is_symlink();
493 if !file_or_symlink { 515 if !file_or_symlink {
494 // If we previously had a file here, it was removed (with 516 // If we previously had a file here, it was removed (with
495 // `hg rm` or similar) or deleted before it could be 517 // `hg rm` or similar) or deleted before it could be
520 )?; 542 )?;
521 self.maybe_save_directory_mtime( 543 self.maybe_save_directory_mtime(
522 children_all_have_dirstate_node_or_are_ignored, 544 children_all_have_dirstate_node_or_are_ignored,
523 fs_entry, 545 fs_entry,
524 dirstate_node, 546 dirstate_node,
547 outdated_dircache,
525 )? 548 )?
526 } else { 549 } else {
527 if file_or_symlink && self.matcher.matches(&hg_path) { 550 if file_or_symlink && self.matcher.matches(&hg_path) {
528 if let Some(entry) = dirstate_node.entry()? { 551 if let Some(entry) = dirstate_node.entry()? {
529 if !entry.any_tracked() { 552 if !entry.any_tracked() {
559 } 582 }
560 } 583 }
561 Ok(()) 584 Ok(())
562 } 585 }
563 586
587 /// Save directory mtime if applicable.
588 ///
589 /// `outdated_directory_cache` is `true` if we've just invalidated the
590 /// cache for this directory in `check_for_outdated_directory_cache`,
591 /// which forces the update.
564 fn maybe_save_directory_mtime( 592 fn maybe_save_directory_mtime(
565 &self, 593 &self,
566 children_all_have_dirstate_node_or_are_ignored: bool, 594 children_all_have_dirstate_node_or_are_ignored: bool,
567 directory_entry: &DirEntry, 595 directory_entry: &DirEntry,
568 dirstate_node: NodeRef<'tree, 'on_disk>, 596 dirstate_node: NodeRef<'tree, 'on_disk>,
597 outdated_directory_cache: bool,
569 ) -> Result<(), DirstateV2ParseError> { 598 ) -> Result<(), DirstateV2ParseError> {
570 if !children_all_have_dirstate_node_or_are_ignored { 599 if !children_all_have_dirstate_node_or_are_ignored {
571 return Ok(()); 600 return Ok(());
572 } 601 }
573 // All filesystem directory entries from `read_dir` have a 602 // All filesystem directory entries from `read_dir` have a
633 // the wrong tick. 662 // the wrong tick.
634 // 663 //
635 // We deem this scenario (unlike the previous one) to be 664 // We deem this scenario (unlike the previous one) to be
636 // unlikely enough in practice. 665 // unlikely enough in practice.
637 666
638 let is_up_to_date = 667 let is_up_to_date = if let Some(cached) =
639 if let Some(cached) = dirstate_node.cached_directory_mtime()? { 668 dirstate_node.cached_directory_mtime()?
640 cached.likely_equal(directory_mtime) 669 {
641 } else { 670 !outdated_directory_cache && cached.likely_equal(directory_mtime)
642 false 671 } else {
643 }; 672 false
673 };
644 if !is_up_to_date { 674 if !is_up_to_date {
645 let hg_path = dirstate_node 675 let hg_path = dirstate_node
646 .full_path_borrowed(self.dmap.on_disk)? 676 .full_path_borrowed(self.dmap.on_disk)?
647 .detach_from_tree(); 677 .detach_from_tree();
648 self.new_cachable_directories 678 self.new_cacheable_directories
649 .lock() 679 .lock()
650 .unwrap() 680 .unwrap()
651 .push((hg_path, directory_mtime)) 681 .push((hg_path, directory_mtime))
652 } 682 }
653 Ok(()) 683 Ok(())