Show More
@@ -14,7 +14,6 b' use crate::utils::files::get_path_from_b' | |||||
14 | use crate::utils::hg_path::HgPath; |
|
14 | use crate::utils::hg_path::HgPath; | |
15 | use crate::BadMatch; |
|
15 | use crate::BadMatch; | |
16 | use crate::DirstateStatus; |
|
16 | use crate::DirstateStatus; | |
17 | use crate::HgPathBuf; |
|
|||
18 | use crate::HgPathCow; |
|
17 | use crate::HgPathCow; | |
19 | use crate::PatternFileWarning; |
|
18 | use crate::PatternFileWarning; | |
20 | use crate::StatusError; |
|
19 | use crate::StatusError; | |
@@ -24,6 +23,8 b' use once_cell::sync::OnceCell;' | |||||
24 | use rayon::prelude::*; |
|
23 | use rayon::prelude::*; | |
25 | use sha1::{Digest, Sha1}; |
|
24 | use sha1::{Digest, Sha1}; | |
26 | use std::borrow::Cow; |
|
25 | use std::borrow::Cow; | |
|
26 | use std::convert::TryFrom; | |||
|
27 | use std::convert::TryInto; | |||
27 | use std::io; |
|
28 | use std::io; | |
28 | use std::path::Path; |
|
29 | use std::path::Path; | |
29 | use std::path::PathBuf; |
|
30 | use std::path::PathBuf; | |
@@ -129,7 +130,6 b" pub fn status<'dirstate>(" | |||||
129 | let hg_path = &BorrowedPath::OnDisk(HgPath::new("")); |
|
130 | let hg_path = &BorrowedPath::OnDisk(HgPath::new("")); | |
130 | let has_ignored_ancestor = HasIgnoredAncestor::create(None, hg_path); |
|
131 | let has_ignored_ancestor = HasIgnoredAncestor::create(None, hg_path); | |
131 | let root_cached_mtime = None; |
|
132 | let root_cached_mtime = None; | |
132 | let root_dir_metadata = None; |
|
|||
133 | // 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. | |
134 | // (As opposed to symlinks within the working directory which are not |
|
134 | // (As opposed to symlinks within the working directory which are not | |
135 | // followed, using `std::fs::symlink_metadata`.) |
|
135 | // followed, using `std::fs::symlink_metadata`.) | |
@@ -137,8 +137,12 b" pub fn status<'dirstate>(" | |||||
137 | &has_ignored_ancestor, |
|
137 | &has_ignored_ancestor, | |
138 | dmap.root.as_ref(), |
|
138 | dmap.root.as_ref(), | |
139 | hg_path, |
|
139 | hg_path, | |
140 | &root_dir, |
|
140 | &DirEntry { | |
141 | root_dir_metadata, |
|
141 | hg_path: Cow::Borrowed(HgPath::new(b"")), | |
|
142 | fs_path: Cow::Borrowed(&root_dir), | |||
|
143 | symlink_metadata: None, | |||
|
144 | file_type: FakeFileType::Directory, | |||
|
145 | }, | |||
142 | root_cached_mtime, |
|
146 | root_cached_mtime, | |
143 | is_at_repo_root, |
|
147 | is_at_repo_root, | |
144 | )?; |
|
148 | )?; | |
@@ -319,7 +323,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
319 | /// need to call `read_dir`. |
|
323 | /// need to call `read_dir`. | |
320 | fn can_skip_fs_readdir( |
|
324 | fn can_skip_fs_readdir( | |
321 | &self, |
|
325 | &self, | |
322 | directory_metadata: Option<&std::fs::Metadata>, |
|
326 | directory_entry: &DirEntry, | |
323 | cached_directory_mtime: Option<TruncatedTimestamp>, |
|
327 | cached_directory_mtime: Option<TruncatedTimestamp>, | |
324 | ) -> bool { |
|
328 | ) -> bool { | |
325 | if !self.options.list_unknown && !self.options.list_ignored { |
|
329 | if !self.options.list_unknown && !self.options.list_ignored { | |
@@ -335,9 +339,9 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
335 | // The dirstate contains a cached mtime for this directory, set |
|
339 | // The dirstate contains a cached mtime for this directory, set | |
336 | // by a previous run of the `status` algorithm which found this |
|
340 | // by a previous run of the `status` algorithm which found this | |
337 | // directory eligible for `read_dir` caching. |
|
341 | // directory eligible for `read_dir` caching. | |
338 |
if let |
|
342 | if let Ok(meta) = directory_entry.symlink_metadata() { | |
339 | if cached_mtime |
|
343 | if cached_mtime | |
340 | .likely_equal_to_mtime_of(meta) |
|
344 | .likely_equal_to_mtime_of(&meta) | |
341 | .unwrap_or(false) |
|
345 | .unwrap_or(false) | |
342 | { |
|
346 | { | |
343 | // The mtime of that directory has not changed |
|
347 | // The mtime of that directory has not changed | |
@@ -358,26 +362,40 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
358 | has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>, |
|
362 | has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>, | |
359 | dirstate_nodes: ChildNodesRef<'tree, 'on_disk>, |
|
363 | dirstate_nodes: ChildNodesRef<'tree, 'on_disk>, | |
360 | directory_hg_path: &BorrowedPath<'tree, 'on_disk>, |
|
364 | directory_hg_path: &BorrowedPath<'tree, 'on_disk>, | |
361 |
directory_ |
|
365 | directory_entry: &DirEntry, | |
362 | directory_metadata: Option<&std::fs::Metadata>, |
|
|||
363 | cached_directory_mtime: Option<TruncatedTimestamp>, |
|
366 | cached_directory_mtime: Option<TruncatedTimestamp>, | |
364 | is_at_repo_root: bool, |
|
367 | is_at_repo_root: bool, | |
365 | ) -> Result<bool, DirstateV2ParseError> { |
|
368 | ) -> Result<bool, DirstateV2ParseError> { | |
366 |
if self.can_skip_fs_readdir(directory_ |
|
369 | if self.can_skip_fs_readdir(directory_entry, cached_directory_mtime) { | |
367 | { |
|
|||
368 | dirstate_nodes |
|
370 | dirstate_nodes | |
369 | .par_iter() |
|
371 | .par_iter() | |
370 | .map(|dirstate_node| { |
|
372 | .map(|dirstate_node| { | |
371 |
let fs_path = directory_ |
|
373 | let fs_path = &directory_entry.fs_path; | |
|
374 | let fs_path = fs_path.join(get_path_from_bytes( | |||
372 | dirstate_node.base_name(self.dmap.on_disk)?.as_bytes(), |
|
375 | dirstate_node.base_name(self.dmap.on_disk)?.as_bytes(), | |
373 | )); |
|
376 | )); | |
374 | match std::fs::symlink_metadata(&fs_path) { |
|
377 | match std::fs::symlink_metadata(&fs_path) { | |
375 |
Ok(fs_metadata) => |
|
378 | Ok(fs_metadata) => { | |
376 |
|
|
379 | let file_type = | |
377 |
|
|
380 | match fs_metadata.file_type().try_into() { | |
378 |
|
|
381 | Ok(file_type) => file_type, | |
379 |
|
|
382 | Err(_) => return Ok(()), | |
380 |
|
|
383 | }; | |
|
384 | let entry = DirEntry { | |||
|
385 | hg_path: Cow::Borrowed( | |||
|
386 | dirstate_node | |||
|
387 | .full_path(&self.dmap.on_disk)?, | |||
|
388 | ), | |||
|
389 | fs_path: Cow::Borrowed(&fs_path), | |||
|
390 | symlink_metadata: Some(fs_metadata), | |||
|
391 | file_type, | |||
|
392 | }; | |||
|
393 | self.traverse_fs_and_dirstate( | |||
|
394 | &entry, | |||
|
395 | dirstate_node, | |||
|
396 | has_ignored_ancestor, | |||
|
397 | ) | |||
|
398 | } | |||
381 | Err(e) if e.kind() == std::io::ErrorKind::NotFound => { |
|
399 | Err(e) if e.kind() == std::io::ErrorKind::NotFound => { | |
382 | self.traverse_dirstate_only(dirstate_node) |
|
400 | self.traverse_dirstate_only(dirstate_node) | |
383 | } |
|
401 | } | |
@@ -398,7 +416,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
398 |
|
416 | |||
399 | let mut fs_entries = if let Ok(entries) = self.read_dir( |
|
417 | let mut fs_entries = if let Ok(entries) = self.read_dir( | |
400 | directory_hg_path, |
|
418 | directory_hg_path, | |
401 | directory_fs_path, |
|
419 | &directory_entry.fs_path, | |
402 | is_at_repo_root, |
|
420 | is_at_repo_root, | |
403 | ) { |
|
421 | ) { | |
404 | entries |
|
422 | entries | |
@@ -440,8 +458,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
440 | match pair { |
|
458 | match pair { | |
441 | Both(dirstate_node, fs_entry) => { |
|
459 | Both(dirstate_node, fs_entry) => { | |
442 | self.traverse_fs_and_dirstate( |
|
460 | self.traverse_fs_and_dirstate( | |
443 |
&fs_entry |
|
461 | &fs_entry, | |
444 | &fs_entry.metadata, |
|
|||
445 | dirstate_node, |
|
462 | dirstate_node, | |
446 | has_ignored_ancestor, |
|
463 | has_ignored_ancestor, | |
447 | )?; |
|
464 | )?; | |
@@ -466,22 +483,20 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
466 |
|
483 | |||
467 | fn traverse_fs_and_dirstate<'ancestor>( |
|
484 | fn traverse_fs_and_dirstate<'ancestor>( | |
468 | &self, |
|
485 | &self, | |
469 |
fs_ |
|
486 | fs_entry: &DirEntry, | |
470 | fs_metadata: &std::fs::Metadata, |
|
|||
471 | dirstate_node: NodeRef<'tree, 'on_disk>, |
|
487 | dirstate_node: NodeRef<'tree, 'on_disk>, | |
472 | has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>, |
|
488 | has_ignored_ancestor: &'ancestor HasIgnoredAncestor<'ancestor>, | |
473 | ) -> Result<(), DirstateV2ParseError> { |
|
489 | ) -> Result<(), DirstateV2ParseError> { | |
474 | self.check_for_outdated_directory_cache(&dirstate_node)?; |
|
490 | self.check_for_outdated_directory_cache(&dirstate_node)?; | |
475 | let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?; |
|
491 | let hg_path = &dirstate_node.full_path_borrowed(self.dmap.on_disk)?; | |
476 |
let file_ |
|
492 | let file_or_symlink = fs_entry.is_file() || fs_entry.is_symlink(); | |
477 | let file_or_symlink = file_type.is_file() || file_type.is_symlink(); |
|
|||
478 | if !file_or_symlink { |
|
493 | if !file_or_symlink { | |
479 | // If we previously had a file here, it was removed (with |
|
494 | // If we previously had a file here, it was removed (with | |
480 | // `hg rm` or similar) or deleted before it could be |
|
495 | // `hg rm` or similar) or deleted before it could be | |
481 | // replaced by a directory or something else. |
|
496 | // replaced by a directory or something else. | |
482 | self.mark_removed_or_deleted_if_file(&dirstate_node)?; |
|
497 | self.mark_removed_or_deleted_if_file(&dirstate_node)?; | |
483 | } |
|
498 | } | |
484 |
if f |
|
499 | if fs_entry.is_dir() { | |
485 | if self.options.collect_traversed_dirs { |
|
500 | if self.options.collect_traversed_dirs { | |
486 | self.outcome |
|
501 | self.outcome | |
487 | .lock() |
|
502 | .lock() | |
@@ -499,14 +514,13 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
499 | &is_ignored, |
|
514 | &is_ignored, | |
500 | dirstate_node.children(self.dmap.on_disk)?, |
|
515 | dirstate_node.children(self.dmap.on_disk)?, | |
501 | hg_path, |
|
516 | hg_path, | |
502 |
fs_ |
|
517 | fs_entry, | |
503 | Some(fs_metadata), |
|
|||
504 | dirstate_node.cached_directory_mtime()?, |
|
518 | dirstate_node.cached_directory_mtime()?, | |
505 | is_at_repo_root, |
|
519 | is_at_repo_root, | |
506 | )?; |
|
520 | )?; | |
507 | self.maybe_save_directory_mtime( |
|
521 | self.maybe_save_directory_mtime( | |
508 | children_all_have_dirstate_node_or_are_ignored, |
|
522 | children_all_have_dirstate_node_or_are_ignored, | |
509 |
fs_ |
|
523 | fs_entry, | |
510 | dirstate_node, |
|
524 | dirstate_node, | |
511 | )? |
|
525 | )? | |
512 | } else { |
|
526 | } else { | |
@@ -527,7 +541,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
527 | } else if entry.modified() { |
|
541 | } else if entry.modified() { | |
528 | self.push_outcome(Outcome::Modified, &dirstate_node)?; |
|
542 | self.push_outcome(Outcome::Modified, &dirstate_node)?; | |
529 | } else { |
|
543 | } else { | |
530 |
self.handle_normal_file(&dirstate_node, fs_ |
|
544 | self.handle_normal_file(&dirstate_node, fs_entry)?; | |
531 | } |
|
545 | } | |
532 | } else { |
|
546 | } else { | |
533 | // `node.entry.is_none()` indicates a "directory" |
|
547 | // `node.entry.is_none()` indicates a "directory" | |
@@ -550,7 +564,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
550 | fn maybe_save_directory_mtime( |
|
564 | fn maybe_save_directory_mtime( | |
551 | &self, |
|
565 | &self, | |
552 | children_all_have_dirstate_node_or_are_ignored: bool, |
|
566 | children_all_have_dirstate_node_or_are_ignored: bool, | |
553 |
directory_ |
|
567 | directory_entry: &DirEntry, | |
554 | dirstate_node: NodeRef<'tree, 'on_disk>, |
|
568 | dirstate_node: NodeRef<'tree, 'on_disk>, | |
555 | ) -> Result<(), DirstateV2ParseError> { |
|
569 | ) -> Result<(), DirstateV2ParseError> { | |
556 | if !children_all_have_dirstate_node_or_are_ignored { |
|
570 | if !children_all_have_dirstate_node_or_are_ignored { | |
@@ -576,11 +590,13 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
576 | // resolution based on the filesystem (for example ext3 |
|
590 | // resolution based on the filesystem (for example ext3 | |
577 | // only stores integer seconds), kernel (see |
|
591 | // only stores integer seconds), kernel (see | |
578 | // https://stackoverflow.com/a/14393315/1162888), etc. |
|
592 | // https://stackoverflow.com/a/14393315/1162888), etc. | |
|
593 | let metadata = match directory_entry.symlink_metadata() { | |||
|
594 | Ok(meta) => meta, | |||
|
595 | Err(_) => return Ok(()), | |||
|
596 | }; | |||
579 | let directory_mtime = if let Ok(option) = |
|
597 | let directory_mtime = if let Ok(option) = | |
580 | TruncatedTimestamp::for_reliable_mtime_of( |
|
598 | TruncatedTimestamp::for_reliable_mtime_of(&metadata, status_start) | |
581 | directory_metadata, |
|
599 | { | |
582 | status_start, |
|
|||
583 | ) { |
|
|||
584 | if let Some(directory_mtime) = option { |
|
600 | if let Some(directory_mtime) = option { | |
585 | directory_mtime |
|
601 | directory_mtime | |
586 | } else { |
|
602 | } else { | |
@@ -641,18 +657,23 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
641 | fn handle_normal_file( |
|
657 | fn handle_normal_file( | |
642 | &self, |
|
658 | &self, | |
643 | dirstate_node: &NodeRef<'tree, 'on_disk>, |
|
659 | dirstate_node: &NodeRef<'tree, 'on_disk>, | |
644 | fs_metadata: &std::fs::Metadata, |
|
660 | fs_entry: &DirEntry, | |
645 | ) -> Result<(), DirstateV2ParseError> { |
|
661 | ) -> Result<(), DirstateV2ParseError> { | |
646 | // Keep the low 31 bits |
|
662 | // Keep the low 31 bits | |
647 | fn truncate_u64(value: u64) -> i32 { |
|
663 | fn truncate_u64(value: u64) -> i32 { | |
648 | (value & 0x7FFF_FFFF) as i32 |
|
664 | (value & 0x7FFF_FFFF) as i32 | |
649 | } |
|
665 | } | |
650 |
|
666 | |||
|
667 | let fs_metadata = match fs_entry.symlink_metadata() { | |||
|
668 | Ok(meta) => meta, | |||
|
669 | Err(_) => return Ok(()), | |||
|
670 | }; | |||
|
671 | ||||
651 | let entry = dirstate_node |
|
672 | let entry = dirstate_node | |
652 | .entry()? |
|
673 | .entry()? | |
653 | .expect("handle_normal_file called with entry-less node"); |
|
674 | .expect("handle_normal_file called with entry-less node"); | |
654 | let mode_changed = |
|
675 | let mode_changed = | |
655 | || self.options.check_exec && entry.mode_changed(fs_metadata); |
|
676 | || self.options.check_exec && entry.mode_changed(&fs_metadata); | |
656 | let size = entry.size(); |
|
677 | let size = entry.size(); | |
657 | let size_changed = size != truncate_u64(fs_metadata.len()); |
|
678 | let size_changed = size != truncate_u64(fs_metadata.len()); | |
658 | if size >= 0 && size_changed && fs_metadata.file_type().is_symlink() { |
|
679 | if size >= 0 && size_changed && fs_metadata.file_type().is_symlink() { | |
@@ -667,7 +688,7 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
667 | } else { |
|
688 | } else { | |
668 | let mtime_looks_clean; |
|
689 | let mtime_looks_clean; | |
669 | if let Some(dirstate_mtime) = entry.truncated_mtime() { |
|
690 | if let Some(dirstate_mtime) = entry.truncated_mtime() { | |
670 | let fs_mtime = TruncatedTimestamp::for_mtime_of(fs_metadata) |
|
691 | let fs_mtime = TruncatedTimestamp::for_mtime_of(&fs_metadata) | |
671 | .expect("OS/libc does not support mtime?"); |
|
692 | .expect("OS/libc does not support mtime?"); | |
672 | // There might be a change in the future if for example the |
|
693 | // There might be a change in the future if for example the | |
673 | // internal clock become off while process run, but this is a |
|
694 | // internal clock become off while process run, but this is a | |
@@ -738,9 +759,8 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
738 | fs_entry: &DirEntry, |
|
759 | fs_entry: &DirEntry, | |
739 | ) -> bool { |
|
760 | ) -> bool { | |
740 | let hg_path = directory_hg_path.join(&fs_entry.hg_path); |
|
761 | let hg_path = directory_hg_path.join(&fs_entry.hg_path); | |
741 |
let file_ |
|
762 | let file_or_symlink = fs_entry.is_file() || fs_entry.is_symlink(); | |
742 | let file_or_symlink = file_type.is_file() || file_type.is_symlink(); |
|
763 | if fs_entry.is_dir() { | |
743 | if file_type.is_dir() { |
|
|||
744 | let is_ignored = |
|
764 | let is_ignored = | |
745 | has_ignored_ancestor || (self.ignore_fn)(&hg_path); |
|
765 | has_ignored_ancestor || (self.ignore_fn)(&hg_path); | |
746 | let traverse_children = if is_ignored { |
|
766 | let traverse_children = if is_ignored { | |
@@ -753,11 +773,9 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
753 | }; |
|
773 | }; | |
754 | if traverse_children { |
|
774 | if traverse_children { | |
755 | let is_at_repo_root = false; |
|
775 | let is_at_repo_root = false; | |
756 |
if let Ok(children_fs_entries) = |
|
776 | if let Ok(children_fs_entries) = | |
757 | &hg_path, |
|
777 | self.read_dir(&hg_path, &fs_entry.fs_path, is_at_repo_root) | |
758 | &fs_entry.fs_path, |
|
778 | { | |
759 | is_at_repo_root, |
|
|||
760 | ) { |
|
|||
761 | children_fs_entries.par_iter().for_each(|child_fs_entry| { |
|
779 | children_fs_entries.par_iter().for_each(|child_fs_entry| { | |
762 | self.traverse_fs_only( |
|
780 | self.traverse_fs_only( | |
763 | is_ignored, |
|
781 | is_ignored, | |
@@ -820,17 +838,46 b" impl<'a, 'tree, 'on_disk> StatusCommon<'" | |||||
820 | } |
|
838 | } | |
821 | } |
|
839 | } | |
822 |
|
840 | |||
823 | struct DirEntry { |
|
841 | /// Since [`std::fs::FileType`] cannot be built directly, we emulate what we | |
824 | /// Path as stored in the dirstate |
|
842 | /// care about. | |
825 | hg_path: HgPathBuf, |
|
843 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | |
826 | /// Filesystem path |
|
844 | enum FakeFileType { | |
827 | fs_path: PathBuf, |
|
845 | File, | |
828 | metadata: std::fs::Metadata, |
|
846 | Directory, | |
|
847 | Symlink, | |||
829 | } |
|
848 | } | |
830 |
|
849 | |||
831 | impl DirEntry { |
|
850 | impl TryFrom<std::fs::FileType> for FakeFileType { | |
832 | /// Returns **unsorted** entries in the given directory, with name and |
|
851 | type Error = (); | |
833 | /// metadata. |
|
852 | ||
|
853 | fn try_from(f: std::fs::FileType) -> Result<Self, Self::Error> { | |||
|
854 | if f.is_dir() { | |||
|
855 | Ok(Self::Directory) | |||
|
856 | } else if f.is_file() { | |||
|
857 | Ok(Self::File) | |||
|
858 | } else if f.is_symlink() { | |||
|
859 | Ok(Self::Symlink) | |||
|
860 | } else { | |||
|
861 | // Things like FIFO etc. | |||
|
862 | Err(()) | |||
|
863 | } | |||
|
864 | } | |||
|
865 | } | |||
|
866 | ||||
|
867 | struct DirEntry<'a> { | |||
|
868 | /// Path as stored in the dirstate, or just the filename for optimization. | |||
|
869 | hg_path: HgPathCow<'a>, | |||
|
870 | /// Filesystem path | |||
|
871 | fs_path: Cow<'a, Path>, | |||
|
872 | /// Lazily computed | |||
|
873 | symlink_metadata: Option<std::fs::Metadata>, | |||
|
874 | /// Already computed for ergonomics. | |||
|
875 | file_type: FakeFileType, | |||
|
876 | } | |||
|
877 | ||||
|
878 | impl<'a> DirEntry<'a> { | |||
|
879 | /// Returns **unsorted** entries in the given directory, with name, | |||
|
880 | /// metadata and file type. | |||
834 | /// |
|
881 | /// | |
835 | /// If a `.hg` sub-directory is encountered: |
|
882 | /// If a `.hg` sub-directory is encountered: | |
836 | /// |
|
883 | /// | |
@@ -844,7 +891,7 b' impl DirEntry {' | |||||
844 | let mut results = Vec::new(); |
|
891 | let mut results = Vec::new(); | |
845 | for entry in read_dir_path.read_dir()? { |
|
892 | for entry in read_dir_path.read_dir()? { | |
846 | let entry = entry?; |
|
893 | let entry = entry?; | |
847 |
let |
|
894 | let file_type = match entry.file_type() { | |
848 | Ok(v) => v, |
|
895 | Ok(v) => v, | |
849 | Err(e) => { |
|
896 | Err(e) => { | |
850 | // race with file deletion? |
|
897 | // race with file deletion? | |
@@ -861,7 +908,7 b' impl DirEntry {' | |||||
861 | if is_at_repo_root { |
|
908 | if is_at_repo_root { | |
862 | // Skip the repo’s own .hg (might be a symlink) |
|
909 | // Skip the repo’s own .hg (might be a symlink) | |
863 | continue; |
|
910 | continue; | |
864 |
} else if |
|
911 | } else if file_type.is_dir() { | |
865 | // A .hg sub-directory at another location means a subrepo, |
|
912 | // A .hg sub-directory at another location means a subrepo, | |
866 | // skip it entirely. |
|
913 | // skip it entirely. | |
867 | return Ok(Vec::new()); |
|
914 | return Ok(Vec::new()); | |
@@ -872,15 +919,40 b' impl DirEntry {' | |||||
872 | } else { |
|
919 | } else { | |
873 | entry.path() |
|
920 | entry.path() | |
874 | }; |
|
921 | }; | |
875 | let base_name = get_bytes_from_os_string(file_name).into(); |
|
922 | let filename = | |
|
923 | Cow::Owned(get_bytes_from_os_string(file_name).into()); | |||
|
924 | let file_type = match FakeFileType::try_from(file_type) { | |||
|
925 | Ok(file_type) => file_type, | |||
|
926 | Err(_) => continue, | |||
|
927 | }; | |||
876 | results.push(DirEntry { |
|
928 | results.push(DirEntry { | |
877 |
hg_path: |
|
929 | hg_path: filename, | |
878 | fs_path: full_path, |
|
930 | fs_path: Cow::Owned(full_path.to_path_buf()), | |
879 | metadata, |
|
931 | symlink_metadata: None, | |
|
932 | file_type, | |||
880 | }) |
|
933 | }) | |
881 | } |
|
934 | } | |
882 | Ok(results) |
|
935 | Ok(results) | |
883 | } |
|
936 | } | |
|
937 | ||||
|
938 | fn symlink_metadata(&self) -> Result<std::fs::Metadata, std::io::Error> { | |||
|
939 | match &self.symlink_metadata { | |||
|
940 | Some(meta) => Ok(meta.clone()), | |||
|
941 | None => std::fs::symlink_metadata(&self.fs_path), | |||
|
942 | } | |||
|
943 | } | |||
|
944 | ||||
|
945 | fn is_dir(&self) -> bool { | |||
|
946 | self.file_type == FakeFileType::Directory | |||
|
947 | } | |||
|
948 | ||||
|
949 | fn is_file(&self) -> bool { | |||
|
950 | self.file_type == FakeFileType::File | |||
|
951 | } | |||
|
952 | ||||
|
953 | fn is_symlink(&self) -> bool { | |||
|
954 | self.file_type == FakeFileType::Symlink | |||
|
955 | } | |||
884 | } |
|
956 | } | |
885 |
|
957 | |||
886 | /// Return the `mtime` of a temporary file newly-created in the `.hg` directory |
|
958 | /// Return the `mtime` of a temporary file newly-created in the `.hg` directory |
General Comments 0
You need to be logged in to leave comments.
Login now