diff --git a/rust/hg-core/src/dirstate/status.rs b/rust/hg-core/src/dirstate/status.rs --- a/rust/hg-core/src/dirstate/status.rs +++ b/rust/hg-core/src/dirstate/status.rs @@ -13,6 +13,7 @@ use crate::{ dirstate::SIZE_FROM_OTHER_PARENT, filepatterns::PatternFileWarning, matchers::{get_ignore_function, Matcher, VisitChildrenSet}, + operations::Operation, utils::{ files::{find_dirs, HgMetadata}, hg_path::{ @@ -101,7 +102,7 @@ type IgnoreFnType<'a> = Box /// We have a good mix of owned (from directory traversal) and borrowed (from /// the dirstate/explicit) paths, this comes up a lot. -type HgPathCow<'a> = Cow<'a, HgPath>; +pub type HgPathCow<'a> = Cow<'a, HgPath>; /// A path with its computed ``Dispatch`` information type DispatchedPath<'a> = (HgPathCow<'a>, Dispatch); @@ -294,9 +295,9 @@ impl ToString for StatusError { /// and how, compared to the revision we're based on pub struct Status<'a, M: Matcher + Sync> { dmap: &'a DirstateMap, - matcher: &'a M, + pub(crate) matcher: &'a M, root_dir: PathBuf, - options: StatusOptions, + pub(crate) options: StatusOptions, ignore_fn: IgnoreFnType<'a>, } @@ -708,7 +709,7 @@ where /// This takes a mutable reference to the results to account for the /// `extend` in timings #[timed] - fn handle_unknowns( + pub fn handle_unknowns( &self, results: &mut Vec>, ) -> IoResult<()> { @@ -787,7 +788,7 @@ where /// This takes a mutable reference to the results to account for the /// `extend` in timings #[timed] - fn extend_from_dmap(&self, results: &mut Vec>) { + pub fn extend_from_dmap(&self, results: &mut Vec>) { results.par_extend(self.dmap.par_iter().flat_map( move |(filename, entry)| { let filename: &HgPath = filename; @@ -837,7 +838,7 @@ where } #[timed] -fn build_response<'a>( +pub fn build_response<'a>( results: impl IntoIterator>, traversed: Vec, ) -> (Vec>, DirstateStatus<'a>) { @@ -899,56 +900,8 @@ pub fn status<'a>( (Vec>, DirstateStatus<'a>), Vec, )> { - let (traversed_sender, traversed_receiver) = - crossbeam::channel::unbounded(); - let (st, warnings) = + let (status, warnings) = Status::new(dmap, matcher, root_dir, ignore_files, options)?; - // Step 1: check the files explicitly mentioned by the user - let (work, mut results) = st.walk_explicit(traversed_sender.clone()); - - if !work.is_empty() { - // Hashmaps are quite a bit slower to build than vecs, so only build it - // if needed. - let old_results = results.iter().cloned().collect(); - - // Step 2: recursively check the working directory for changes if - // needed - for (dir, dispatch) in work { - match dispatch { - Dispatch::Directory { was_file } => { - if was_file { - results.push((dir.to_owned(), Dispatch::Removed)); - } - if options.list_ignored - || options.list_unknown && !st.dir_ignore(&dir) - { - st.traverse( - &dir, - &old_results, - &mut results, - traversed_sender.clone(), - )?; - } - } - _ => unreachable!("There can only be directories in `work`"), - } - } - } - - if !matcher.is_exact() { - if options.list_unknown { - st.handle_unknowns(&mut results)?; - } else { - // TODO this is incorrect, see issue6335 - // This requires a fix in both Python and Rust that can happen - // with other pending changes to `status`. - st.extend_from_dmap(&mut results); - } - } - - drop(traversed_sender); - let traversed = traversed_receiver.into_iter().collect(); - - Ok((build_response(results, traversed), warnings)) + Ok((status.run()?, warnings)) } diff --git a/rust/hg-core/src/operations/dirstate_status.rs b/rust/hg-core/src/operations/dirstate_status.rs new file mode 100644 --- /dev/null +++ b/rust/hg-core/src/operations/dirstate_status.rs @@ -0,0 +1,76 @@ +// dirstate_status.rs +// +// Copyright 2019, Raphaël Gomès +// +// This software may be used and distributed according to the terms of the +// GNU General Public License version 2 or any later version. + +use crate::dirstate::status::{build_response, Dispatch, HgPathCow, Status}; +use crate::matchers::Matcher; +use crate::operations::Operation; +use crate::{DirstateStatus, StatusError}; + +/// A tuple of the paths that need to be checked in the filelog because it's +/// ambiguous whether they've changed, and the rest of the already dispatched +/// files. +pub type LookupAndStatus<'a> = (Vec>, DirstateStatus<'a>); + +impl<'a, M: Matcher + Sync> Operation> for Status<'a, M> { + type Error = StatusError; + + fn run(&self) -> Result, Self::Error> { + let (traversed_sender, traversed_receiver) = + crossbeam::channel::unbounded(); + + // Step 1: check the files explicitly mentioned by the user + let (work, mut results) = self.walk_explicit(traversed_sender.clone()); + + if !work.is_empty() { + // Hashmaps are quite a bit slower to build than vecs, so only + // build it if needed. + let old_results = results.iter().cloned().collect(); + + // Step 2: recursively check the working directory for changes if + // needed + for (dir, dispatch) in work { + match dispatch { + Dispatch::Directory { was_file } => { + if was_file { + results.push((dir.to_owned(), Dispatch::Removed)); + } + if self.options.list_ignored + || self.options.list_unknown + && !self.dir_ignore(&dir) + { + self.traverse( + &dir, + &old_results, + &mut results, + traversed_sender.clone(), + )?; + } + } + _ => { + unreachable!("There can only be directories in `work`") + } + } + } + } + + if !self.matcher.is_exact() { + if self.options.list_unknown { + self.handle_unknowns(&mut results)?; + } else { + // TODO this is incorrect, see issue6335 + // This requires a fix in both Python and Rust that can happen + // with other pending changes to `status`. + self.extend_from_dmap(&mut results); + } + } + + drop(traversed_sender); + let traversed = traversed_receiver.into_iter().collect(); + + Ok(build_response(results, traversed)) + } +} diff --git a/rust/hg-core/src/operations/mod.rs b/rust/hg-core/src/operations/mod.rs --- a/rust/hg-core/src/operations/mod.rs +++ b/rust/hg-core/src/operations/mod.rs @@ -1,3 +1,4 @@ +mod dirstate_status; mod find_root; pub use find_root::{FindRoot, FindRootError, FindRootErrorKind};