Show More
@@ -98,13 +98,16 b' pub(super) struct Node {' | |||||
98 | /// and must cause a different modification time (unless the system |
|
98 | /// and must cause a different modification time (unless the system | |
99 | /// clock jumps back and we get unlucky, which is not impossible but |
|
99 | /// clock jumps back and we get unlucky, which is not impossible but | |
100 | /// but deemed unlikely enough). |
|
100 | /// but deemed unlikely enough). | |
101 | /// - The directory did not contain any child entry that did not have a |
|
101 | /// - All direct children of this directory (as returned by | |
102 |
/// corresponding dirstate node |
|
102 | /// `std::fs::read_dir`) either have a corresponding dirstate node, or | |
|
103 | /// are ignored by ignore patterns whose hash is in | |||
|
104 | /// `Header::ignore_patterns_hash`. | |||
103 | /// |
|
105 | /// | |
104 | /// This means that if `std::fs::symlink_metadata` later reports the |
|
106 | /// This means that if `std::fs::symlink_metadata` later reports the | |
105 | /// same modification time, we don’t need to call `std::fs::read_dir` |
|
107 | /// same modification time and ignored patterns haven’t changed, a run | |
106 | /// again for this directory and can iterate child dirstate nodes |
|
108 | /// of status that is not listing ignored files can skip calling | |
107 | /// instead. |
|
109 | /// `std::fs::read_dir` again for this directory, iterate child | |
|
110 | /// dirstate nodes instead. | |||
108 | state: u8, |
|
111 | state: u8, | |
109 | data: Entry, |
|
112 | data: Entry, | |
110 | } |
|
113 | } |
@@ -190,18 +190,21 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
190 | // This happens for example with `hg status -mard`. |
|
190 | // This happens for example with `hg status -mard`. | |
191 | return true; |
|
191 | return true; | |
192 | } |
|
192 | } | |
193 | if let Some(cached_mtime) = cached_directory_mtime { |
|
193 | if !self.options.list_ignored | |
194 | // The dirstate contains a cached mtime for this directory, set by |
|
194 | && self.ignore_patterns_have_changed == Some(false) | |
195 | // a previous run of the `status` algorithm which found this |
|
195 | { | |
196 | // directory eligible for `read_dir` caching. |
|
196 | if let Some(cached_mtime) = cached_directory_mtime { | |
197 | if let Some(meta) = directory_metadata { |
|
197 | // The dirstate contains a cached mtime for this directory, set | |
198 | if let Ok(current_mtime) = meta.modified() { |
|
198 | // by a previous run of the `status` algorithm which found this | |
199 | if current_mtime == cached_mtime.into() { |
|
199 | // directory eligible for `read_dir` caching. | |
200 | // The mtime of that directory has not changed since |
|
200 | if let Some(meta) = directory_metadata { | |
201 | // then, which means that the |
|
201 | if let Ok(current_mtime) = meta.modified() { | |
202 | // results of `read_dir` should also |
|
202 | if current_mtime == cached_mtime.into() { | |
203 |
|
|
203 | // The mtime of that directory has not changed | |
204 | return true; |
|
204 | // since then, which means that the results of | |
|
205 | // `read_dir` should also be unchanged. | |||
|
206 | return true; | |||
|
207 | } | |||
205 | } |
|
208 | } | |
206 | } |
|
209 | } | |
207 | } |
|
210 | } | |
@@ -209,8 +212,8 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
209 | false |
|
212 | false | |
210 | } |
|
213 | } | |
211 |
|
214 | |||
212 |
/// Returns whether the filesystem directory |
|
215 | /// Returns whether all child entries of the filesystem directory have a | |
213 |
/// |
|
216 | /// corresponding dirstate node or are ignored. | |
214 | fn traverse_fs_directory_and_dirstate( |
|
217 | fn traverse_fs_directory_and_dirstate( | |
215 | &self, |
|
218 | &self, | |
216 | has_ignored_ancestor: bool, |
|
219 | has_ignored_ancestor: bool, | |
@@ -248,11 +251,10 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
248 | }) |
|
251 | }) | |
249 | .collect::<Result<_, _>>()?; |
|
252 | .collect::<Result<_, _>>()?; | |
250 |
|
253 | |||
251 | // Conservatively don’t let the caller assume that there aren’t |
|
254 | // We don’t know, so conservatively say this isn’t the case | |
252 | // any, since we don’t know. |
|
255 | let children_all_have_dirstate_node_or_are_ignored = false; | |
253 | let directory_has_any_fs_only_entry = true; |
|
|||
254 |
|
256 | |||
255 | return Ok(directory_has_any_fs_only_entry); |
|
257 | return Ok(children_all_have_dirstate_node_or_are_ignored); | |
256 | } |
|
258 | } | |
257 |
|
259 | |||
258 | let mut fs_entries = if let Ok(entries) = self.read_dir( |
|
260 | let mut fs_entries = if let Ok(entries) = self.read_dir( | |
@@ -295,27 +297,32 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
295 | .par_bridge() |
|
297 | .par_bridge() | |
296 | .map(|pair| { |
|
298 | .map(|pair| { | |
297 | use itertools::EitherOrBoth::*; |
|
299 | use itertools::EitherOrBoth::*; | |
298 | let is_fs_only = pair.is_right(); |
|
300 | let has_dirstate_node_or_is_ignored; | |
299 | match pair { |
|
301 | match pair { | |
300 |
Both(dirstate_node, fs_entry) => |
|
302 | Both(dirstate_node, fs_entry) => { | |
301 | .traverse_fs_and_dirstate( |
|
303 | self.traverse_fs_and_dirstate( | |
302 | &fs_entry.full_path, |
|
304 | &fs_entry.full_path, | |
303 | &fs_entry.metadata, |
|
305 | &fs_entry.metadata, | |
304 | dirstate_node, |
|
306 | dirstate_node, | |
305 | has_ignored_ancestor, |
|
307 | has_ignored_ancestor, | |
306 |
)? |
|
308 | )?; | |
|
309 | has_dirstate_node_or_is_ignored = true | |||
|
310 | } | |||
307 | Left(dirstate_node) => { |
|
311 | Left(dirstate_node) => { | |
308 | self.traverse_dirstate_only(dirstate_node)? |
|
312 | self.traverse_dirstate_only(dirstate_node)?; | |
|
313 | has_dirstate_node_or_is_ignored = true; | |||
309 | } |
|
314 | } | |
310 |
Right(fs_entry) => |
|
315 | Right(fs_entry) => { | |
311 | has_ignored_ancestor, |
|
316 | has_dirstate_node_or_is_ignored = self.traverse_fs_only( | |
312 |
|
|
317 | has_ignored_ancestor, | |
313 |
|
|
318 | directory_hg_path, | |
314 |
|
|
319 | fs_entry, | |
|
320 | ) | |||
|
321 | } | |||
315 | } |
|
322 | } | |
316 | Ok(is_fs_only) |
|
323 | Ok(has_dirstate_node_or_is_ignored) | |
317 | }) |
|
324 | }) | |
318 |
.try_reduce(|| |
|
325 | .try_reduce(|| true, |a, b| Ok(a && b)) | |
319 | } |
|
326 | } | |
320 |
|
327 | |||
321 | fn traverse_fs_and_dirstate( |
|
328 | fn traverse_fs_and_dirstate( | |
@@ -348,7 +355,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
348 | } |
|
355 | } | |
349 | let is_ignored = has_ignored_ancestor || (self.ignore_fn)(hg_path); |
|
356 | let is_ignored = has_ignored_ancestor || (self.ignore_fn)(hg_path); | |
350 | let is_at_repo_root = false; |
|
357 | let is_at_repo_root = false; | |
351 | let directory_has_any_fs_only_entry = self |
|
358 | let children_all_have_dirstate_node_or_are_ignored = self | |
352 | .traverse_fs_directory_and_dirstate( |
|
359 | .traverse_fs_directory_and_dirstate( | |
353 | is_ignored, |
|
360 | is_ignored, | |
354 | dirstate_node.children(self.dmap.on_disk)?, |
|
361 | dirstate_node.children(self.dmap.on_disk)?, | |
@@ -359,7 +366,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
359 | is_at_repo_root, |
|
366 | is_at_repo_root, | |
360 | )?; |
|
367 | )?; | |
361 | self.maybe_save_directory_mtime( |
|
368 | self.maybe_save_directory_mtime( | |
362 | directory_has_any_fs_only_entry, |
|
369 | children_all_have_dirstate_node_or_are_ignored, | |
363 | fs_metadata, |
|
370 | fs_metadata, | |
364 | dirstate_node, |
|
371 | dirstate_node, | |
365 | )? |
|
372 | )? | |
@@ -394,7 +401,10 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
394 | } else { |
|
401 | } else { | |
395 | // `node.entry.is_none()` indicates a "directory" |
|
402 | // `node.entry.is_none()` indicates a "directory" | |
396 | // node, but the filesystem has a file |
|
403 | // node, but the filesystem has a file | |
397 |
self.mark_unknown_or_ignored( |
|
404 | self.mark_unknown_or_ignored( | |
|
405 | has_ignored_ancestor, | |||
|
406 | hg_path, | |||
|
407 | ); | |||
398 | } |
|
408 | } | |
399 | } |
|
409 | } | |
400 |
|
410 | |||
@@ -408,11 +418,11 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
408 |
|
418 | |||
409 | fn maybe_save_directory_mtime( |
|
419 | fn maybe_save_directory_mtime( | |
410 | &self, |
|
420 | &self, | |
411 | directory_has_any_fs_only_entry: bool, |
|
421 | children_all_have_dirstate_node_or_are_ignored: bool, | |
412 | directory_metadata: &std::fs::Metadata, |
|
422 | directory_metadata: &std::fs::Metadata, | |
413 | dirstate_node: NodeRef<'tree, 'on_disk>, |
|
423 | dirstate_node: NodeRef<'tree, 'on_disk>, | |
414 | ) -> Result<(), DirstateV2ParseError> { |
|
424 | ) -> Result<(), DirstateV2ParseError> { | |
415 | if !directory_has_any_fs_only_entry { |
|
425 | if children_all_have_dirstate_node_or_are_ignored { | |
416 | // All filesystem directory entries from `read_dir` have a |
|
426 | // All filesystem directory entries from `read_dir` have a | |
417 | // corresponding node in the dirstate, so we can reconstitute the |
|
427 | // corresponding node in the dirstate, so we can reconstitute the | |
418 | // names of those entries without calling `read_dir` again. |
|
428 | // names of those entries without calling `read_dir` again. | |
@@ -584,12 +594,14 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
584 | } |
|
594 | } | |
585 |
|
595 | |||
586 | /// Something in the filesystem has no corresponding dirstate node |
|
596 | /// Something in the filesystem has no corresponding dirstate node | |
|
597 | /// | |||
|
598 | /// Returns whether that path is ignored | |||
587 | fn traverse_fs_only( |
|
599 | fn traverse_fs_only( | |
588 | &self, |
|
600 | &self, | |
589 | has_ignored_ancestor: bool, |
|
601 | has_ignored_ancestor: bool, | |
590 | directory_hg_path: &HgPath, |
|
602 | directory_hg_path: &HgPath, | |
591 | fs_entry: &DirEntry, |
|
603 | fs_entry: &DirEntry, | |
592 | ) { |
|
604 | ) -> bool { | |
593 | let hg_path = directory_hg_path.join(&fs_entry.base_name); |
|
605 | let hg_path = directory_hg_path.join(&fs_entry.base_name); | |
594 | let file_type = fs_entry.metadata.file_type(); |
|
606 | let file_type = fs_entry.metadata.file_type(); | |
595 | let file_or_symlink = file_type.is_file() || file_type.is_symlink(); |
|
607 | let file_or_symlink = file_type.is_file() || file_type.is_symlink(); | |
@@ -616,26 +628,43 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
616 | is_ignored, |
|
628 | is_ignored, | |
617 | &hg_path, |
|
629 | &hg_path, | |
618 | child_fs_entry, |
|
630 | child_fs_entry, | |
619 | ) |
|
631 | ); | |
620 | }) |
|
632 | }) | |
621 | } |
|
633 | } | |
622 | } |
|
634 | } | |
623 | if self.options.collect_traversed_dirs { |
|
635 | if self.options.collect_traversed_dirs { | |
624 | self.outcome.lock().unwrap().traversed.push(hg_path.into()) |
|
636 | self.outcome.lock().unwrap().traversed.push(hg_path.into()) | |
625 | } |
|
637 | } | |
626 | } else if file_or_symlink && self.matcher.matches(&hg_path) { |
|
638 | is_ignored | |
627 | self.mark_unknown_or_ignored( |
|
639 | } else { | |
628 | has_ignored_ancestor, |
|
640 | if file_or_symlink { | |
629 |
|
|
641 | if self.matcher.matches(&hg_path) { | |
630 | ) |
|
642 | self.mark_unknown_or_ignored( | |
|
643 | has_ignored_ancestor, | |||
|
644 | &BorrowedPath::InMemory(&hg_path), | |||
|
645 | ) | |||
|
646 | } else { | |||
|
647 | // We haven’t computed whether this path is ignored. It | |||
|
648 | // might not be, and a future run of status might have a | |||
|
649 | // different matcher that matches it. So treat it as not | |||
|
650 | // ignored. That is, inhibit readdir caching of the parent | |||
|
651 | // directory. | |||
|
652 | false | |||
|
653 | } | |||
|
654 | } else { | |||
|
655 | // This is neither a directory, a plain file, or a symlink. | |||
|
656 | // Treat it like an ignored file. | |||
|
657 | true | |||
|
658 | } | |||
631 | } |
|
659 | } | |
632 | } |
|
660 | } | |
633 |
|
661 | |||
|
662 | /// Returns whether that path is ignored | |||
634 | fn mark_unknown_or_ignored( |
|
663 | fn mark_unknown_or_ignored( | |
635 | &self, |
|
664 | &self, | |
636 | has_ignored_ancestor: bool, |
|
665 | has_ignored_ancestor: bool, | |
637 | hg_path: &BorrowedPath<'_, 'on_disk>, |
|
666 | hg_path: &BorrowedPath<'_, 'on_disk>, | |
638 | ) { |
|
667 | ) -> bool { | |
639 | let is_ignored = has_ignored_ancestor || (self.ignore_fn)(&hg_path); |
|
668 | let is_ignored = has_ignored_ancestor || (self.ignore_fn)(&hg_path); | |
640 | if is_ignored { |
|
669 | if is_ignored { | |
641 | if self.options.list_ignored { |
|
670 | if self.options.list_ignored { | |
@@ -654,6 +683,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
654 | .push(hg_path.detach_from_tree()) |
|
683 | .push(hg_path.detach_from_tree()) | |
655 | } |
|
684 | } | |
656 | } |
|
685 | } | |
|
686 | is_ignored | |||
657 | } |
|
687 | } | |
658 | } |
|
688 | } | |
659 |
|
689 |
General Comments 0
You need to be logged in to leave comments.
Login now