Show More
@@ -0,0 +1,76 b'' | |||||
|
1 | // dirstate_status.rs | |||
|
2 | // | |||
|
3 | // Copyright 2019, Raphaël Gomès <rgomes@octobus.net> | |||
|
4 | // | |||
|
5 | // This software may be used and distributed according to the terms of the | |||
|
6 | // GNU General Public License version 2 or any later version. | |||
|
7 | ||||
|
8 | use crate::dirstate::status::{build_response, Dispatch, HgPathCow, Status}; | |||
|
9 | use crate::matchers::Matcher; | |||
|
10 | use crate::operations::Operation; | |||
|
11 | use crate::{DirstateStatus, StatusError}; | |||
|
12 | ||||
|
13 | /// A tuple of the paths that need to be checked in the filelog because it's | |||
|
14 | /// ambiguous whether they've changed, and the rest of the already dispatched | |||
|
15 | /// files. | |||
|
16 | pub type LookupAndStatus<'a> = (Vec<HgPathCow<'a>>, DirstateStatus<'a>); | |||
|
17 | ||||
|
18 | impl<'a, M: Matcher + Sync> Operation<LookupAndStatus<'a>> for Status<'a, M> { | |||
|
19 | type Error = StatusError; | |||
|
20 | ||||
|
21 | fn run(&self) -> Result<LookupAndStatus<'a>, Self::Error> { | |||
|
22 | let (traversed_sender, traversed_receiver) = | |||
|
23 | crossbeam::channel::unbounded(); | |||
|
24 | ||||
|
25 | // Step 1: check the files explicitly mentioned by the user | |||
|
26 | let (work, mut results) = self.walk_explicit(traversed_sender.clone()); | |||
|
27 | ||||
|
28 | if !work.is_empty() { | |||
|
29 | // Hashmaps are quite a bit slower to build than vecs, so only | |||
|
30 | // build it if needed. | |||
|
31 | let old_results = results.iter().cloned().collect(); | |||
|
32 | ||||
|
33 | // Step 2: recursively check the working directory for changes if | |||
|
34 | // needed | |||
|
35 | for (dir, dispatch) in work { | |||
|
36 | match dispatch { | |||
|
37 | Dispatch::Directory { was_file } => { | |||
|
38 | if was_file { | |||
|
39 | results.push((dir.to_owned(), Dispatch::Removed)); | |||
|
40 | } | |||
|
41 | if self.options.list_ignored | |||
|
42 | || self.options.list_unknown | |||
|
43 | && !self.dir_ignore(&dir) | |||
|
44 | { | |||
|
45 | self.traverse( | |||
|
46 | &dir, | |||
|
47 | &old_results, | |||
|
48 | &mut results, | |||
|
49 | traversed_sender.clone(), | |||
|
50 | )?; | |||
|
51 | } | |||
|
52 | } | |||
|
53 | _ => { | |||
|
54 | unreachable!("There can only be directories in `work`") | |||
|
55 | } | |||
|
56 | } | |||
|
57 | } | |||
|
58 | } | |||
|
59 | ||||
|
60 | if !self.matcher.is_exact() { | |||
|
61 | if self.options.list_unknown { | |||
|
62 | self.handle_unknowns(&mut results)?; | |||
|
63 | } else { | |||
|
64 | // TODO this is incorrect, see issue6335 | |||
|
65 | // This requires a fix in both Python and Rust that can happen | |||
|
66 | // with other pending changes to `status`. | |||
|
67 | self.extend_from_dmap(&mut results); | |||
|
68 | } | |||
|
69 | } | |||
|
70 | ||||
|
71 | drop(traversed_sender); | |||
|
72 | let traversed = traversed_receiver.into_iter().collect(); | |||
|
73 | ||||
|
74 | Ok(build_response(results, traversed)) | |||
|
75 | } | |||
|
76 | } |
@@ -13,6 +13,7 b' use crate::{' | |||||
13 | dirstate::SIZE_FROM_OTHER_PARENT, |
|
13 | dirstate::SIZE_FROM_OTHER_PARENT, | |
14 | filepatterns::PatternFileWarning, |
|
14 | filepatterns::PatternFileWarning, | |
15 | matchers::{get_ignore_function, Matcher, VisitChildrenSet}, |
|
15 | matchers::{get_ignore_function, Matcher, VisitChildrenSet}, | |
|
16 | operations::Operation, | |||
16 | utils::{ |
|
17 | utils::{ | |
17 | files::{find_dirs, HgMetadata}, |
|
18 | files::{find_dirs, HgMetadata}, | |
18 | hg_path::{ |
|
19 | hg_path::{ | |
@@ -101,7 +102,7 b" type IgnoreFnType<'a> = Box<dyn for<'r> " | |||||
101 |
|
102 | |||
102 | /// We have a good mix of owned (from directory traversal) and borrowed (from |
|
103 | /// We have a good mix of owned (from directory traversal) and borrowed (from | |
103 | /// the dirstate/explicit) paths, this comes up a lot. |
|
104 | /// the dirstate/explicit) paths, this comes up a lot. | |
104 | type HgPathCow<'a> = Cow<'a, HgPath>; |
|
105 | pub type HgPathCow<'a> = Cow<'a, HgPath>; | |
105 |
|
106 | |||
106 | /// A path with its computed ``Dispatch`` information |
|
107 | /// A path with its computed ``Dispatch`` information | |
107 | type DispatchedPath<'a> = (HgPathCow<'a>, Dispatch); |
|
108 | type DispatchedPath<'a> = (HgPathCow<'a>, Dispatch); | |
@@ -294,9 +295,9 b' impl ToString for StatusError {' | |||||
294 | /// and how, compared to the revision we're based on |
|
295 | /// and how, compared to the revision we're based on | |
295 | pub struct Status<'a, M: Matcher + Sync> { |
|
296 | pub struct Status<'a, M: Matcher + Sync> { | |
296 | dmap: &'a DirstateMap, |
|
297 | dmap: &'a DirstateMap, | |
297 | matcher: &'a M, |
|
298 | pub(crate) matcher: &'a M, | |
298 | root_dir: PathBuf, |
|
299 | root_dir: PathBuf, | |
299 | options: StatusOptions, |
|
300 | pub(crate) options: StatusOptions, | |
300 | ignore_fn: IgnoreFnType<'a>, |
|
301 | ignore_fn: IgnoreFnType<'a>, | |
301 | } |
|
302 | } | |
302 |
|
303 | |||
@@ -708,7 +709,7 b' where' | |||||
708 | /// This takes a mutable reference to the results to account for the |
|
709 | /// This takes a mutable reference to the results to account for the | |
709 | /// `extend` in timings |
|
710 | /// `extend` in timings | |
710 | #[timed] |
|
711 | #[timed] | |
711 | fn handle_unknowns( |
|
712 | pub fn handle_unknowns( | |
712 | &self, |
|
713 | &self, | |
713 | results: &mut Vec<DispatchedPath<'a>>, |
|
714 | results: &mut Vec<DispatchedPath<'a>>, | |
714 | ) -> IoResult<()> { |
|
715 | ) -> IoResult<()> { | |
@@ -787,7 +788,7 b' where' | |||||
787 | /// This takes a mutable reference to the results to account for the |
|
788 | /// This takes a mutable reference to the results to account for the | |
788 | /// `extend` in timings |
|
789 | /// `extend` in timings | |
789 | #[timed] |
|
790 | #[timed] | |
790 | fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) { |
|
791 | pub fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) { | |
791 | results.par_extend(self.dmap.par_iter().flat_map( |
|
792 | results.par_extend(self.dmap.par_iter().flat_map( | |
792 | move |(filename, entry)| { |
|
793 | move |(filename, entry)| { | |
793 | let filename: &HgPath = filename; |
|
794 | let filename: &HgPath = filename; | |
@@ -837,7 +838,7 b' where' | |||||
837 | } |
|
838 | } | |
838 |
|
839 | |||
839 | #[timed] |
|
840 | #[timed] | |
840 | fn build_response<'a>( |
|
841 | pub fn build_response<'a>( | |
841 | results: impl IntoIterator<Item = DispatchedPath<'a>>, |
|
842 | results: impl IntoIterator<Item = DispatchedPath<'a>>, | |
842 | traversed: Vec<HgPathBuf>, |
|
843 | traversed: Vec<HgPathBuf>, | |
843 | ) -> (Vec<HgPathCow<'a>>, DirstateStatus<'a>) { |
|
844 | ) -> (Vec<HgPathCow<'a>>, DirstateStatus<'a>) { | |
@@ -899,56 +900,8 b" pub fn status<'a>(" | |||||
899 | (Vec<HgPathCow<'a>>, DirstateStatus<'a>), |
|
900 | (Vec<HgPathCow<'a>>, DirstateStatus<'a>), | |
900 | Vec<PatternFileWarning>, |
|
901 | Vec<PatternFileWarning>, | |
901 | )> { |
|
902 | )> { | |
902 | let (traversed_sender, traversed_receiver) = |
|
903 | let (status, warnings) = | |
903 | crossbeam::channel::unbounded(); |
|
|||
904 | let (st, warnings) = |
|
|||
905 | Status::new(dmap, matcher, root_dir, ignore_files, options)?; |
|
904 | Status::new(dmap, matcher, root_dir, ignore_files, options)?; | |
906 |
|
905 | |||
907 | // Step 1: check the files explicitly mentioned by the user |
|
906 | Ok((status.run()?, warnings)) | |
908 | let (work, mut results) = st.walk_explicit(traversed_sender.clone()); |
|
|||
909 |
|
||||
910 | if !work.is_empty() { |
|
|||
911 | // Hashmaps are quite a bit slower to build than vecs, so only build it |
|
|||
912 | // if needed. |
|
|||
913 | let old_results = results.iter().cloned().collect(); |
|
|||
914 |
|
||||
915 | // Step 2: recursively check the working directory for changes if |
|
|||
916 | // needed |
|
|||
917 | for (dir, dispatch) in work { |
|
|||
918 | match dispatch { |
|
|||
919 | Dispatch::Directory { was_file } => { |
|
|||
920 | if was_file { |
|
|||
921 | results.push((dir.to_owned(), Dispatch::Removed)); |
|
|||
922 | } |
|
907 | } | |
923 | if options.list_ignored |
|
|||
924 | || options.list_unknown && !st.dir_ignore(&dir) |
|
|||
925 | { |
|
|||
926 | st.traverse( |
|
|||
927 | &dir, |
|
|||
928 | &old_results, |
|
|||
929 | &mut results, |
|
|||
930 | traversed_sender.clone(), |
|
|||
931 | )?; |
|
|||
932 | } |
|
|||
933 | } |
|
|||
934 | _ => unreachable!("There can only be directories in `work`"), |
|
|||
935 | } |
|
|||
936 | } |
|
|||
937 | } |
|
|||
938 |
|
||||
939 | if !matcher.is_exact() { |
|
|||
940 | if options.list_unknown { |
|
|||
941 | st.handle_unknowns(&mut results)?; |
|
|||
942 | } else { |
|
|||
943 | // TODO this is incorrect, see issue6335 |
|
|||
944 | // This requires a fix in both Python and Rust that can happen |
|
|||
945 | // with other pending changes to `status`. |
|
|||
946 | st.extend_from_dmap(&mut results); |
|
|||
947 | } |
|
|||
948 | } |
|
|||
949 |
|
||||
950 | drop(traversed_sender); |
|
|||
951 | let traversed = traversed_receiver.into_iter().collect(); |
|
|||
952 |
|
||||
953 | Ok((build_response(results, traversed), warnings)) |
|
|||
954 | } |
|
General Comments 0
You need to be logged in to leave comments.
Login now