Show More
@@ -69,7 +69,7 b' pub enum BadMatch {' | |||||
69 | BadType(BadType), |
|
69 | BadType(BadType), | |
70 | } |
|
70 | } | |
71 |
|
71 | |||
72 |
/// |
|
72 | /// Enum used to dispatch new status entries into the right collections. | |
73 | /// Is similar to `crate::EntryState`, but represents the transient state of |
|
73 | /// Is similar to `crate::EntryState`, but represents the transient state of | |
74 | /// entries during the lifetime of a command. |
|
74 | /// entries during the lifetime of a command. | |
75 | #[derive(Debug, Copy, Clone)] |
|
75 | #[derive(Debug, Copy, Clone)] | |
@@ -94,10 +94,18 b' pub enum Dispatch {' | |||||
94 | } |
|
94 | } | |
95 |
|
95 | |||
96 | type IoResult<T> = std::io::Result<T>; |
|
96 | type IoResult<T> = std::io::Result<T>; | |
|
97 | ||||
97 | /// `Box<dyn Trait>` is syntactic sugar for `Box<dyn Trait, 'static>`, so add |
|
98 | /// `Box<dyn Trait>` is syntactic sugar for `Box<dyn Trait, 'static>`, so add | |
98 | /// an explicit lifetime here to not fight `'static` bounds "out of nowhere". |
|
99 | /// an explicit lifetime here to not fight `'static` bounds "out of nowhere". | |
99 | type IgnoreFnType<'a> = Box<dyn for<'r> Fn(&'r HgPath) -> bool + Sync + 'a>; |
|
100 | type IgnoreFnType<'a> = Box<dyn for<'r> Fn(&'r HgPath) -> bool + Sync + 'a>; | |
100 |
|
101 | |||
|
102 | /// We have a good mix of owned (from directory traversal) and borrowed (from | |||
|
103 | /// the dirstate/explicit) paths, this comes up a lot. | |||
|
104 | type HgPathCow<'a> = Cow<'a, HgPath>; | |||
|
105 | ||||
|
106 | /// A path with its computed ``Dispatch`` information | |||
|
107 | type DispatchedPath<'a> = (HgPathCow<'a>, Dispatch); | |||
|
108 | ||||
101 | /// Dates and times that are outside the 31-bit signed range are compared |
|
109 | /// Dates and times that are outside the 31-bit signed range are compared | |
102 | /// modulo 2^31. This should prevent hg from behaving badly with very large |
|
110 | /// modulo 2^31. This should prevent hg from behaving badly with very large | |
103 | /// files or corrupt dates while still having a high probability of detecting |
|
111 | /// files or corrupt dates while still having a high probability of detecting | |
@@ -232,22 +240,25 b' pub struct StatusOptions {' | |||||
232 |
|
240 | |||
233 | #[derive(Debug)] |
|
241 | #[derive(Debug)] | |
234 | pub struct DirstateStatus<'a> { |
|
242 | pub struct DirstateStatus<'a> { | |
235 |
pub modified: Vec<Cow<'a |
|
243 | pub modified: Vec<HgPathCow<'a>>, | |
236 |
pub added: Vec<Cow<'a |
|
244 | pub added: Vec<HgPathCow<'a>>, | |
237 |
pub removed: Vec<Cow<'a |
|
245 | pub removed: Vec<HgPathCow<'a>>, | |
238 |
pub deleted: Vec<Cow<'a |
|
246 | pub deleted: Vec<HgPathCow<'a>>, | |
239 |
pub clean: Vec<Cow<'a |
|
247 | pub clean: Vec<HgPathCow<'a>>, | |
240 |
pub ignored: Vec<Cow<'a |
|
248 | pub ignored: Vec<HgPathCow<'a>>, | |
241 |
pub unknown: Vec<Cow<'a |
|
249 | pub unknown: Vec<HgPathCow<'a>>, | |
242 |
pub bad: Vec<(Cow<'a |
|
250 | pub bad: Vec<(HgPathCow<'a>, BadMatch)>, | |
243 | /// Only filled if `collect_traversed_dirs` is `true` |
|
251 | /// Only filled if `collect_traversed_dirs` is `true` | |
244 | pub traversed: Vec<HgPathBuf>, |
|
252 | pub traversed: Vec<HgPathBuf>, | |
245 | } |
|
253 | } | |
246 |
|
254 | |||
247 | #[derive(Debug)] |
|
255 | #[derive(Debug)] | |
248 | pub enum StatusError { |
|
256 | pub enum StatusError { | |
|
257 | /// Generic IO error | |||
249 | IO(std::io::Error), |
|
258 | IO(std::io::Error), | |
|
259 | /// An invalid path that cannot be represented in Mercurial was found | |||
250 | Path(HgPathError), |
|
260 | Path(HgPathError), | |
|
261 | /// An invalid "ignore" pattern was found | |||
251 | Pattern(PatternError), |
|
262 | Pattern(PatternError), | |
252 | } |
|
263 | } | |
253 |
|
264 | |||
@@ -279,6 +290,8 b' impl ToString for StatusError {' | |||||
279 | } |
|
290 | } | |
280 | } |
|
291 | } | |
281 |
|
292 | |||
|
293 | /// Gives information about which files are changed in the working directory | |||
|
294 | /// and how, compared to the revision we're based on | |||
282 | pub struct Status<'a, M: Matcher + Sync> { |
|
295 | pub struct Status<'a, M: Matcher + Sync> { | |
283 | dmap: &'a DirstateMap, |
|
296 | dmap: &'a DirstateMap, | |
284 | matcher: &'a M, |
|
297 | matcher: &'a M, | |
@@ -319,6 +332,7 b' where' | |||||
319 | )) |
|
332 | )) | |
320 | } |
|
333 | } | |
321 |
|
334 | |||
|
335 | /// Is the path ignored? | |||
322 | pub fn is_ignored(&self, path: impl AsRef<HgPath>) -> bool { |
|
336 | pub fn is_ignored(&self, path: impl AsRef<HgPath>) -> bool { | |
323 | (self.ignore_fn)(path.as_ref()) |
|
337 | (self.ignore_fn)(path.as_ref()) | |
324 | } |
|
338 | } | |
@@ -342,16 +356,15 b' where' | |||||
342 | } |
|
356 | } | |
343 | } |
|
357 | } | |
344 |
|
358 | |||
345 | /// Get stat data about the files explicitly specified by match. |
|
359 | /// Get stat data about the files explicitly specified by the matcher. | |
|
360 | /// Returns a tuple of the directories that need to be traversed and the | |||
|
361 | /// files with their corresponding `Dispatch`. | |||
346 | /// TODO subrepos |
|
362 | /// TODO subrepos | |
347 | #[timed] |
|
363 | #[timed] | |
348 | pub fn walk_explicit( |
|
364 | pub fn walk_explicit( | |
349 | &self, |
|
365 | &self, | |
350 | traversed_sender: crossbeam::Sender<HgPathBuf>, |
|
366 | traversed_sender: crossbeam::Sender<HgPathBuf>, | |
351 | ) -> ( |
|
367 | ) -> (Vec<DispatchedPath<'a>>, Vec<DispatchedPath<'a>>) { | |
352 | Vec<(Cow<'a, HgPath>, Dispatch)>, |
|
|||
353 | Vec<(Cow<'a, HgPath>, Dispatch)>, |
|
|||
354 | ) { |
|
|||
355 | self.matcher |
|
368 | self.matcher | |
356 | .file_set() |
|
369 | .file_set() | |
357 | .unwrap_or(&DEFAULT_WORK) |
|
370 | .unwrap_or(&DEFAULT_WORK) | |
@@ -443,8 +456,8 b' where' | |||||
443 | pub fn traverse( |
|
456 | pub fn traverse( | |
444 | &self, |
|
457 | &self, | |
445 | path: impl AsRef<HgPath>, |
|
458 | path: impl AsRef<HgPath>, | |
446 |
old_results: &FastHashMap<Cow<'a |
|
459 | old_results: &FastHashMap<HgPathCow<'a>, Dispatch>, | |
447 |
results: &mut Vec< |
|
460 | results: &mut Vec<DispatchedPath<'a>>, | |
448 | traversed_sender: crossbeam::Sender<HgPathBuf>, |
|
461 | traversed_sender: crossbeam::Sender<HgPathBuf>, | |
449 | ) -> IoResult<()> { |
|
462 | ) -> IoResult<()> { | |
450 | // The traversal is done in parallel, so use a channel to gather |
|
463 | // The traversal is done in parallel, so use a channel to gather | |
@@ -637,23 +650,25 b' where' | |||||
637 |
|
650 | |||
638 | let skip_dot_hg = !directory.as_bytes().is_empty(); |
|
651 | let skip_dot_hg = !directory.as_bytes().is_empty(); | |
639 | let entries = match list_directory(dir_path, skip_dot_hg) { |
|
652 | let entries = match list_directory(dir_path, skip_dot_hg) { | |
640 |
Err(e) => |
|
653 | Err(e) => { | |
641 | ErrorKind::NotFound | ErrorKind::PermissionDenied => { |
|
654 | return match e.kind() { | |
642 | files_sender |
|
655 | ErrorKind::NotFound | ErrorKind::PermissionDenied => { | |
643 |
|
|
656 | files_sender | |
644 |
|
|
657 | .send(Ok(( | |
645 | Dispatch::Bad(BadMatch::OsError( |
|
658 | directory.to_owned(), | |
646 | // Unwrapping here is OK because the error |
|
659 | Dispatch::Bad(BadMatch::OsError( | |
647 |
|
|
660 | // Unwrapping here is OK because the error | |
648 |
|
|
661 | // always is a | |
649 |
|
|
662 | // real os error | |
650 | )), |
|
663 | e.raw_os_error().unwrap(), | |
651 |
)) |
|
664 | )), | |
652 |
|
|
665 | ))) | |
653 | return Ok(()); |
|
666 | .expect("receiver should outlive sender"); | |
654 |
|
|
667 | Ok(()) | |
655 |
|
|
668 | } | |
656 | }, |
|
669 | _ => Err(e), | |
|
670 | }; | |||
|
671 | } | |||
657 | Ok(entries) => entries, |
|
672 | Ok(entries) => entries, | |
658 | }; |
|
673 | }; | |
659 |
|
674 | |||
@@ -686,12 +701,16 b' where' | |||||
686 | }) |
|
701 | }) | |
687 | } |
|
702 | } | |
688 |
|
703 | |||
|
704 | /// Checks all files that are in the dirstate but were not found during the | |||
|
705 | /// working directory traversal. This means that the rest must | |||
|
706 | /// be either ignored, under a symlink or under a new nested repo. | |||
|
707 | /// | |||
689 | /// This takes a mutable reference to the results to account for the |
|
708 | /// This takes a mutable reference to the results to account for the | |
690 | /// `extend` in timings |
|
709 | /// `extend` in timings | |
691 | #[timed] |
|
710 | #[timed] | |
692 | fn handle_unknowns( |
|
711 | fn handle_unknowns( | |
693 | &self, |
|
712 | &self, | |
694 |
results: &mut Vec< |
|
713 | results: &mut Vec<DispatchedPath<'a>>, | |
695 | ) -> IoResult<()> { |
|
714 | ) -> IoResult<()> { | |
696 | let to_visit: Vec<(&HgPath, &DirstateEntry)> = |
|
715 | let to_visit: Vec<(&HgPath, &DirstateEntry)> = | |
697 | if results.is_empty() && self.matcher.matches_everything() { |
|
716 | if results.is_empty() && self.matcher.matches_everything() { | |
@@ -714,9 +733,6 b' where' | |||||
714 | .collect() |
|
733 | .collect() | |
715 | }; |
|
734 | }; | |
716 |
|
735 | |||
717 | // We walked all dirs under the roots that weren't ignored, and |
|
|||
718 | // everything that matched was stat'ed and is already in results. |
|
|||
719 | // The rest must thus be ignored or under a symlink. |
|
|||
720 | let path_auditor = PathAuditor::new(&self.root_dir); |
|
736 | let path_auditor = PathAuditor::new(&self.root_dir); | |
721 |
|
737 | |||
722 | // TODO don't collect. Find a way of replicating the behavior of |
|
738 | // TODO don't collect. Find a way of replicating the behavior of | |
@@ -766,13 +782,12 b' where' | |||||
766 | Ok(()) |
|
782 | Ok(()) | |
767 | } |
|
783 | } | |
768 |
|
784 | |||
|
785 | /// Add the files in the dirstate to the results. | |||
|
786 | /// | |||
769 | /// This takes a mutable reference to the results to account for the |
|
787 | /// This takes a mutable reference to the results to account for the | |
770 | /// `extend` in timings |
|
788 | /// `extend` in timings | |
771 | #[timed] |
|
789 | #[timed] | |
772 | fn extend_from_dmap( |
|
790 | fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) { | |
773 | &self, |
|
|||
774 | results: &mut Vec<(Cow<'a, HgPath>, Dispatch)>, |
|
|||
775 | ) { |
|
|||
776 | results.par_extend(self.dmap.par_iter().flat_map( |
|
791 | results.par_extend(self.dmap.par_iter().flat_map( | |
777 | move |(filename, entry)| { |
|
792 | move |(filename, entry)| { | |
778 | let filename: &HgPath = filename; |
|
793 | let filename: &HgPath = filename; | |
@@ -823,9 +838,9 b' where' | |||||
823 |
|
838 | |||
824 | #[timed] |
|
839 | #[timed] | |
825 | fn build_response<'a>( |
|
840 | fn build_response<'a>( | |
826 |
results: impl IntoIterator<Item = |
|
841 | results: impl IntoIterator<Item = DispatchedPath<'a>>, | |
827 | traversed: Vec<HgPathBuf>, |
|
842 | traversed: Vec<HgPathBuf>, | |
828 |
) -> (Vec<Cow<'a |
|
843 | ) -> (Vec<HgPathCow<'a>>, DirstateStatus<'a>) { | |
829 | let mut lookup = vec![]; |
|
844 | let mut lookup = vec![]; | |
830 | let mut modified = vec![]; |
|
845 | let mut modified = vec![]; | |
831 | let mut added = vec![]; |
|
846 | let mut added = vec![]; | |
@@ -881,7 +896,7 b" pub fn status<'a>(" | |||||
881 | ignore_files: Vec<PathBuf>, |
|
896 | ignore_files: Vec<PathBuf>, | |
882 | options: StatusOptions, |
|
897 | options: StatusOptions, | |
883 | ) -> StatusResult<( |
|
898 | ) -> StatusResult<( | |
884 |
(Vec<Cow<'a |
|
899 | (Vec<HgPathCow<'a>>, DirstateStatus<'a>), | |
885 | Vec<PatternFileWarning>, |
|
900 | Vec<PatternFileWarning>, | |
886 | )> { |
|
901 | )> { | |
887 | let (traversed_sender, traversed_receiver) = |
|
902 | let (traversed_sender, traversed_receiver) = | |
@@ -922,16 +937,12 b" pub fn status<'a>(" | |||||
922 | } |
|
937 | } | |
923 |
|
938 | |||
924 | if !matcher.is_exact() { |
|
939 | if !matcher.is_exact() { | |
925 | // Step 3: Check the remaining files from the dmap. |
|
|||
926 | // If a dmap file is not in results yet, it was either |
|
|||
927 | // a) not matched b) ignored, c) missing, or d) under a |
|
|||
928 | // symlink directory. |
|
|||
929 |
|
||||
930 | if options.list_unknown { |
|
940 | if options.list_unknown { | |
931 | st.handle_unknowns(&mut results)?; |
|
941 | st.handle_unknowns(&mut results)?; | |
932 | } else { |
|
942 | } else { | |
933 | // We may not have walked the full directory tree above, so stat |
|
943 | // TODO this is incorrect, see issue6335 | |
934 | // and check everything we missed. |
|
944 | // This requires a fix in both Python and Rust that can happen | |
|
945 | // with other pending changes to `status`. | |||
935 | st.extend_from_dmap(&mut results); |
|
946 | st.extend_from_dmap(&mut results); | |
936 | } |
|
947 | } | |
937 | } |
|
948 | } |
General Comments 0
You need to be logged in to leave comments.
Login now