18 use crate::HgPathCow; |
18 use crate::HgPathCow; |
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 once_cell::sync::OnceCell; |
23 use rayon::prelude::*; |
24 use rayon::prelude::*; |
24 use sha1::{Digest, Sha1}; |
25 use sha1::{Digest, Sha1}; |
25 use std::borrow::Cow; |
26 use std::borrow::Cow; |
26 use std::io; |
27 use std::io; |
27 use std::path::Path; |
28 use std::path::Path; |
124 outated_cached_directories: Default::default(), |
125 outated_cached_directories: Default::default(), |
125 filesystem_time_at_status_start, |
126 filesystem_time_at_status_start, |
126 }; |
127 }; |
127 let is_at_repo_root = true; |
128 let is_at_repo_root = true; |
128 let hg_path = &BorrowedPath::OnDisk(HgPath::new("")); |
129 let hg_path = &BorrowedPath::OnDisk(HgPath::new("")); |
129 let has_ignored_ancestor = false; |
130 let has_ignored_ancestor = HasIgnoredAncestor::create(None, hg_path); |
130 let root_cached_mtime = None; |
131 let root_cached_mtime = None; |
131 let root_dir_metadata = None; |
132 let root_dir_metadata = None; |
132 // If the path we have for the repository root is a symlink, do follow it. |
133 // If the path we have for the repository root is a symlink, do follow it. |
133 // (As opposed to symlinks within the working directory which are not |
134 // (As opposed to symlinks within the working directory which are not |
134 // followed, using `std::fs::symlink_metadata`.) |
135 // followed, using `std::fs::symlink_metadata`.) |
135 common.traverse_fs_directory_and_dirstate( |
136 common.traverse_fs_directory_and_dirstate( |
136 has_ignored_ancestor, |
137 &has_ignored_ancestor, |
137 dmap.root.as_ref(), |
138 dmap.root.as_ref(), |
138 hg_path, |
139 hg_path, |
139 &root_dir, |
140 &root_dir, |
140 root_dir_metadata, |
141 root_dir_metadata, |
141 root_cached_mtime, |
142 root_cached_mtime, |
192 Deleted, |
193 Deleted, |
193 Clean, |
194 Clean, |
194 Ignored, |
195 Ignored, |
195 Unknown, |
196 Unknown, |
196 Unsure, |
197 Unsure, |
|
198 } |
|
199 |
|
200 /// Lazy computation of whether a given path has a hgignored |
|
201 /// ancestor. |
|
202 struct HasIgnoredAncestor<'a> { |
|
203 /// `path` and `parent` constitute the inputs to the computation, |
|
204 /// `cache` stores the outcome. |
|
205 path: &'a HgPath, |
|
206 parent: Option<&'a HasIgnoredAncestor<'a>>, |
|
207 cache: OnceCell<bool>, |
|
208 } |
|
209 |
|
210 impl<'a> HasIgnoredAncestor<'a> { |
|
211 fn create( |
|
212 parent: Option<&'a HasIgnoredAncestor<'a>>, |
|
213 path: &'a HgPath, |
|
214 ) -> HasIgnoredAncestor<'a> { |
|
215 Self { |
|
216 path, |
|
217 parent, |
|
218 cache: OnceCell::new(), |
|
219 } |
|
220 } |
|
221 |
|
222 fn force<'b>(&self, ignore_fn: &IgnoreFnType<'b>) -> bool { |
|
223 match self.parent { |
|
224 None => false, |
|
225 Some(parent) => { |
|
226 *(parent.cache.get_or_init(|| { |
|
227 parent.force(ignore_fn) || ignore_fn(&self.path) |
|
228 })) |
|
229 } |
|
230 } |
|
231 } |
197 } |
232 } |
198 |
233 |
199 impl<'a, 'tree, 'on_disk> StatusCommon<'a, 'tree, 'on_disk> { |
234 impl<'a, 'tree, 'on_disk> StatusCommon<'a, 'tree, 'on_disk> { |
200 fn push_outcome( |
235 fn push_outcome( |
201 &self, |
236 &self, |
316 false |
351 false |
317 } |
352 } |
318 |
353 |
319 /// Returns whether all child entries of the filesystem directory have a |
354 /// Returns whether all child entries of the filesystem directory have a |
320 /// corresponding dirstate node or are ignored. |
355 /// corresponding dirstate node or are ignored. |
321 fn traverse_fs_directory_and_dirstate( |
356 fn traverse_fs_directory_and_dirstate<'ancestor>( |
322 &self, |
357 &self, |
323 has_ignored_ancestor: bool, |
358 has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>, |
324 dirstate_nodes: ChildNodesRef<'tree, 'on_disk>, |
359 dirstate_nodes: ChildNodesRef<'tree, 'on_disk>, |
325 directory_hg_path: &BorrowedPath<'tree, 'on_disk>, |
360 directory_hg_path: &BorrowedPath<'tree, 'on_disk>, |
326 directory_fs_path: &Path, |
361 directory_fs_path: &Path, |
327 directory_metadata: Option<&std::fs::Metadata>, |
362 directory_metadata: Option<&std::fs::Metadata>, |
328 cached_directory_mtime: Option<TruncatedTimestamp>, |
363 cached_directory_mtime: Option<TruncatedTimestamp>, |
416 self.traverse_dirstate_only(dirstate_node)?; |
451 self.traverse_dirstate_only(dirstate_node)?; |
417 has_dirstate_node_or_is_ignored = true; |
452 has_dirstate_node_or_is_ignored = true; |
418 } |
453 } |
419 Right(fs_entry) => { |
454 Right(fs_entry) => { |
420 has_dirstate_node_or_is_ignored = self.traverse_fs_only( |
455 has_dirstate_node_or_is_ignored = self.traverse_fs_only( |
421 has_ignored_ancestor, |
456 has_ignored_ancestor.force(&self.ignore_fn), |
422 directory_hg_path, |
457 directory_hg_path, |
423 fs_entry, |
458 fs_entry, |
424 ) |
459 ) |
425 } |
460 } |
426 } |
461 } |
427 Ok(has_dirstate_node_or_is_ignored) |
462 Ok(has_dirstate_node_or_is_ignored) |
428 }) |
463 }) |
429 .try_reduce(|| true, |a, b| Ok(a && b)) |
464 .try_reduce(|| true, |a, b| Ok(a && b)) |
430 } |
465 } |
431 |
466 |
432 fn traverse_fs_and_dirstate( |
467 fn traverse_fs_and_dirstate<'ancestor>( |
433 &self, |
468 &self, |
434 fs_path: &Path, |
469 fs_path: &Path, |
435 fs_metadata: &std::fs::Metadata, |
470 fs_metadata: &std::fs::Metadata, |
436 dirstate_node: NodeRef<'tree, 'on_disk>, |
471 dirstate_node: NodeRef<'tree, 'on_disk>, |
437 has_ignored_ancestor: bool, |
472 has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>, |
438 ) -> Result<(), DirstateV2ParseError> { |
473 ) -> Result<(), DirstateV2ParseError> { |
439 self.check_for_outdated_directory_cache(&dirstate_node)?; |
474 self.check_for_outdated_directory_cache(&dirstate_node)?; |
440 let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?; |
475 let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?; |
441 let file_type = fs_metadata.file_type(); |
476 let file_type = fs_metadata.file_type(); |
442 let file_or_symlink = file_type.is_file() || file_type.is_symlink(); |
477 let file_or_symlink = file_type.is_file() || file_type.is_symlink(); |
452 .lock() |
487 .lock() |
453 .unwrap() |
488 .unwrap() |
454 .traversed |
489 .traversed |
455 .push(hg_path.detach_from_tree()) |
490 .push(hg_path.detach_from_tree()) |
456 } |
491 } |
457 let is_ignored = has_ignored_ancestor || (self.ignore_fn)(hg_path); |
492 let is_ignored = HasIgnoredAncestor::create( |
|
493 Some(&has_ignored_ancestor), |
|
494 hg_path, |
|
495 ); |
458 let is_at_repo_root = false; |
496 let is_at_repo_root = false; |
459 let children_all_have_dirstate_node_or_are_ignored = self |
497 let children_all_have_dirstate_node_or_are_ignored = self |
460 .traverse_fs_directory_and_dirstate( |
498 .traverse_fs_directory_and_dirstate( |
461 is_ignored, |
499 &is_ignored, |
462 dirstate_node.children(self.dmap.on_disk)?, |
500 dirstate_node.children(self.dmap.on_disk)?, |
463 hg_path, |
501 hg_path, |
464 fs_path, |
502 fs_path, |
465 Some(fs_metadata), |
503 Some(fs_metadata), |
466 dirstate_node.cached_directory_mtime()?, |
504 dirstate_node.cached_directory_mtime()?, |
470 children_all_have_dirstate_node_or_are_ignored, |
508 children_all_have_dirstate_node_or_are_ignored, |
471 fs_metadata, |
509 fs_metadata, |
472 dirstate_node, |
510 dirstate_node, |
473 )? |
511 )? |
474 } else { |
512 } else { |
475 if file_or_symlink && self.matcher.matches(hg_path) { |
513 if file_or_symlink && self.matcher.matches(&hg_path) { |
476 if let Some(entry) = dirstate_node.entry()? { |
514 if let Some(entry) = dirstate_node.entry()? { |
477 if !entry.any_tracked() { |
515 if !entry.any_tracked() { |
478 // Forward-compat if we start tracking unknown/ignored |
516 // Forward-compat if we start tracking unknown/ignored |
479 // files for caching reasons |
517 // files for caching reasons |
480 self.mark_unknown_or_ignored( |
518 self.mark_unknown_or_ignored( |
481 has_ignored_ancestor, |
519 has_ignored_ancestor.force(&self.ignore_fn), |
482 hg_path, |
520 &hg_path, |
483 ); |
521 ); |
484 } |
522 } |
485 if entry.added() { |
523 if entry.added() { |
486 self.push_outcome(Outcome::Added, &dirstate_node)?; |
524 self.push_outcome(Outcome::Added, &dirstate_node)?; |
487 } else if entry.removed() { |
525 } else if entry.removed() { |