63 (Box::new(|&_| true), vec![], None) |
63 (Box::new(|&_| true), vec![], None) |
64 }; |
64 }; |
65 |
65 |
66 let filesystem_time_at_status_start = |
66 let filesystem_time_at_status_start = |
67 filesystem_now(&root_dir).ok().map(TruncatedTimestamp::from); |
67 filesystem_now(&root_dir).ok().map(TruncatedTimestamp::from); |
|
68 |
|
69 // If the repository is under the current directory, prefer using a |
|
70 // relative path, so the kernel needs to traverse fewer directory in every |
|
71 // call to `read_dir` or `symlink_metadata`. |
|
72 // This is effective in the common case where the current directory is the |
|
73 // repository root. |
|
74 |
|
75 // TODO: Better yet would be to use libc functions like `openat` and |
|
76 // `fstatat` to remove such repeated traversals entirely, but the standard |
|
77 // library does not provide APIs based on those. |
|
78 // Maybe with a crate like https://crates.io/crates/openat instead? |
|
79 let root_dir = if let Some(relative) = std::env::current_dir() |
|
80 .ok() |
|
81 .and_then(|cwd| root_dir.strip_prefix(cwd).ok()) |
|
82 { |
|
83 relative |
|
84 } else { |
|
85 &root_dir |
|
86 }; |
|
87 |
68 let outcome = DirstateStatus { |
88 let outcome = DirstateStatus { |
69 filesystem_time_at_status_start, |
89 filesystem_time_at_status_start, |
70 ..Default::default() |
90 ..Default::default() |
71 }; |
91 }; |
72 let common = StatusCommon { |
92 let common = StatusCommon { |
750 /// |
770 /// |
751 /// * At the repository root, ignore that sub-directory |
771 /// * At the repository root, ignore that sub-directory |
752 /// * Elsewhere, we’re listing the content of a sub-repo. Return an empty |
772 /// * Elsewhere, we’re listing the content of a sub-repo. Return an empty |
753 /// list instead. |
773 /// list instead. |
754 fn read_dir(path: &Path, is_at_repo_root: bool) -> io::Result<Vec<Self>> { |
774 fn read_dir(path: &Path, is_at_repo_root: bool) -> io::Result<Vec<Self>> { |
|
775 // `read_dir` returns a "not found" error for the empty path |
|
776 let at_cwd = path == Path::new(""); |
|
777 let read_dir_path = if at_cwd { Path::new(".") } else { path }; |
755 let mut results = Vec::new(); |
778 let mut results = Vec::new(); |
756 for entry in path.read_dir()? { |
779 for entry in read_dir_path.read_dir()? { |
757 let entry = entry?; |
780 let entry = entry?; |
758 let metadata = entry.metadata()?; |
781 let metadata = entry.metadata()?; |
759 let name = get_bytes_from_os_string(entry.file_name()); |
782 let file_name = entry.file_name(); |
760 // FIXME don't do this when cached |
783 // FIXME don't do this when cached |
761 if name == b".hg" { |
784 if file_name == ".hg" { |
762 if is_at_repo_root { |
785 if is_at_repo_root { |
763 // Skip the repo’s own .hg (might be a symlink) |
786 // Skip the repo’s own .hg (might be a symlink) |
764 continue; |
787 continue; |
765 } else if metadata.is_dir() { |
788 } else if metadata.is_dir() { |
766 // A .hg sub-directory at another location means a subrepo, |
789 // A .hg sub-directory at another location means a subrepo, |
767 // skip it entirely. |
790 // skip it entirely. |
768 return Ok(Vec::new()); |
791 return Ok(Vec::new()); |
769 } |
792 } |
770 } |
793 } |
|
794 let full_path = if at_cwd { |
|
795 file_name.clone().into() |
|
796 } else { |
|
797 entry.path() |
|
798 }; |
|
799 let base_name = get_bytes_from_os_string(file_name).into(); |
771 results.push(DirEntry { |
800 results.push(DirEntry { |
772 base_name: name.into(), |
801 base_name, |
773 full_path: entry.path(), |
802 full_path, |
774 metadata, |
803 metadata, |
775 }) |
804 }) |
776 } |
805 } |
777 Ok(results) |
806 Ok(results) |
778 } |
807 } |