Show More
@@ -468,6 +468,7 b' dependencies = [' | |||
|
468 | 468 | "log", |
|
469 | 469 | "memmap2", |
|
470 | 470 | "micro-timer", |
|
471 | "once_cell", | |
|
471 | 472 | "ouroboros", |
|
472 | 473 | "pretty_assertions", |
|
473 | 474 | "rand 0.8.5", |
@@ -687,6 +688,12 b' dependencies = [' | |||
|
687 | 688 | ] |
|
688 | 689 | |
|
689 | 690 | [[package]] |
|
691 | name = "once_cell" | |
|
692 | version = "1.14.0" | |
|
693 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
|
694 | checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" | |
|
695 | ||
|
696 | [[package]] | |
|
690 | 697 | name = "opaque-debug" |
|
691 | 698 | version = "0.3.0" |
|
692 | 699 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -35,6 +35,9 b' log = "0.4.8"' | |||
|
35 | 35 | memmap2 = { version = "0.5.3", features = ["stable_deref_trait"] } |
|
36 | 36 | zstd = "0.5.3" |
|
37 | 37 | format-bytes = "0.3.0" |
|
38 | # once_cell 1.15 uses edition 2021, while the heptapod CI | |
|
39 | # uses an old version of Cargo that doesn't support it. | |
|
40 | once_cell = "=1.14.0" | |
|
38 | 41 | |
|
39 | 42 | # We don't use the `miniz-oxide` backend to not change rhg benchmarks and until |
|
40 | 43 | # we have a clearer view of which backend is the fastest. |
@@ -20,6 +20,7 b' use crate::PatternFileWarning;' | |||
|
20 | 20 | use crate::StatusError; |
|
21 | 21 | use crate::StatusOptions; |
|
22 | 22 | use micro_timer::timed; |
|
23 | use once_cell::sync::OnceCell; | |
|
23 | 24 | use rayon::prelude::*; |
|
24 | 25 | use sha1::{Digest, Sha1}; |
|
25 | 26 | use std::borrow::Cow; |
@@ -126,14 +127,14 b" pub fn status<'dirstate>(" | |||
|
126 | 127 | }; |
|
127 | 128 | let is_at_repo_root = true; |
|
128 | 129 | let hg_path = &BorrowedPath::OnDisk(HgPath::new("")); |
|
129 |
let has_ignored_ancestor = |
|
|
130 | let has_ignored_ancestor = HasIgnoredAncestor::create(None, hg_path); | |
|
130 | 131 | let root_cached_mtime = None; |
|
131 | 132 | let root_dir_metadata = None; |
|
132 | 133 | // If the path we have for the repository root is a symlink, do follow it. |
|
133 | 134 | // (As opposed to symlinks within the working directory which are not |
|
134 | 135 | // followed, using `std::fs::symlink_metadata`.) |
|
135 | 136 | common.traverse_fs_directory_and_dirstate( |
|
136 | has_ignored_ancestor, | |
|
137 | &has_ignored_ancestor, | |
|
137 | 138 | dmap.root.as_ref(), |
|
138 | 139 | hg_path, |
|
139 | 140 | &root_dir, |
@@ -196,6 +197,40 b' enum Outcome {' | |||
|
196 | 197 | Unsure, |
|
197 | 198 | } |
|
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 | } | |
|
232 | } | |
|
233 | ||
|
199 | 234 | impl<'a, 'tree, 'on_disk> StatusCommon<'a, 'tree, 'on_disk> { |
|
200 | 235 | fn push_outcome( |
|
201 | 236 | &self, |
@@ -318,9 +353,9 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
318 | 353 | |
|
319 | 354 | /// Returns whether all child entries of the filesystem directory have a |
|
320 | 355 | /// corresponding dirstate node or are ignored. |
|
321 | fn traverse_fs_directory_and_dirstate( | |
|
356 | fn traverse_fs_directory_and_dirstate<'ancestor>( | |
|
322 | 357 | &self, |
|
323 |
has_ignored_ancestor: |
|
|
358 | has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>, | |
|
324 | 359 | dirstate_nodes: ChildNodesRef<'tree, 'on_disk>, |
|
325 | 360 | directory_hg_path: &BorrowedPath<'tree, 'on_disk>, |
|
326 | 361 | directory_fs_path: &Path, |
@@ -418,7 +453,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
418 | 453 | } |
|
419 | 454 | Right(fs_entry) => { |
|
420 | 455 | has_dirstate_node_or_is_ignored = self.traverse_fs_only( |
|
421 | has_ignored_ancestor, | |
|
456 | has_ignored_ancestor.force(&self.ignore_fn), | |
|
422 | 457 | directory_hg_path, |
|
423 | 458 | fs_entry, |
|
424 | 459 | ) |
@@ -429,12 +464,12 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
429 | 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 | 468 | &self, |
|
434 | 469 | fs_path: &Path, |
|
435 | 470 | fs_metadata: &std::fs::Metadata, |
|
436 | 471 | dirstate_node: NodeRef<'tree, 'on_disk>, |
|
437 |
has_ignored_ancestor: |
|
|
472 | has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>, | |
|
438 | 473 | ) -> Result<(), DirstateV2ParseError> { |
|
439 | 474 | self.check_for_outdated_directory_cache(&dirstate_node)?; |
|
440 | 475 | let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?; |
@@ -454,11 +489,14 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
454 | 489 | .traversed |
|
455 | 490 | .push(hg_path.detach_from_tree()) |
|
456 | 491 | } |
|
457 |
let is_ignored = |
|
|
492 | let is_ignored = HasIgnoredAncestor::create( | |
|
493 | Some(&has_ignored_ancestor), | |
|
494 | hg_path, | |
|
495 | ); | |
|
458 | 496 | let is_at_repo_root = false; |
|
459 | 497 | let children_all_have_dirstate_node_or_are_ignored = self |
|
460 | 498 | .traverse_fs_directory_and_dirstate( |
|
461 | is_ignored, | |
|
499 | &is_ignored, | |
|
462 | 500 | dirstate_node.children(self.dmap.on_disk)?, |
|
463 | 501 | hg_path, |
|
464 | 502 | fs_path, |
@@ -472,14 +510,14 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
472 | 510 | dirstate_node, |
|
473 | 511 | )? |
|
474 | 512 | } else { |
|
475 | if file_or_symlink && self.matcher.matches(hg_path) { | |
|
513 | if file_or_symlink && self.matcher.matches(&hg_path) { | |
|
476 | 514 | if let Some(entry) = dirstate_node.entry()? { |
|
477 | 515 | if !entry.any_tracked() { |
|
478 | 516 | // Forward-compat if we start tracking unknown/ignored |
|
479 | 517 | // files for caching reasons |
|
480 | 518 | self.mark_unknown_or_ignored( |
|
481 | has_ignored_ancestor, | |
|
482 | hg_path, | |
|
519 | has_ignored_ancestor.force(&self.ignore_fn), | |
|
520 | &hg_path, | |
|
483 | 521 | ); |
|
484 | 522 | } |
|
485 | 523 | if entry.added() { |
@@ -495,7 +533,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||
|
495 | 533 | // `node.entry.is_none()` indicates a "directory" |
|
496 | 534 | // node, but the filesystem has a file |
|
497 | 535 | self.mark_unknown_or_ignored( |
|
498 | has_ignored_ancestor, | |
|
536 | has_ignored_ancestor.force(&self.ignore_fn), | |
|
499 | 537 | hg_path, |
|
500 | 538 | ); |
|
501 | 539 | } |
General Comments 0
You need to be logged in to leave comments.
Login now