Mercurial > hg
changeset 45113:98817e5daca7
hg-core: define a `dirstate_status` `Operation`
This is 3/3 in a series of patches to improve dirstate status' code.
Following in the footsteps of a46e36b82461, we move the main status
functionality to an `Operation`. This will most likely be subject to change in
the future (what function signature, what parameters, etc., but we will see
when `rhg` gets `hg status` support.
Differential Revision: https://phab.mercurial-scm.org/D8663
author | Raphaël Gomès <rgomes@octobus.net> |
---|---|
date | Wed, 24 Jun 2020 17:53:44 +0200 |
parents | 470d306e616c |
children | e2d17974a869 |
files | rust/hg-core/src/dirstate/status.rs rust/hg-core/src/operations/dirstate_status.rs rust/hg-core/src/operations/mod.rs |
diffstat | 3 files changed, 86 insertions(+), 56 deletions(-) [+] |
line wrap: on
line diff
--- a/rust/hg-core/src/dirstate/status.rs Wed Jun 24 17:20:39 2020 +0200 +++ b/rust/hg-core/src/dirstate/status.rs Wed Jun 24 17:53:44 2020 +0200 @@ -13,6 +13,7 @@ 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 @@ /// 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 @@ /// 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 @@ /// 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<DispatchedPath<'a>>, ) -> IoResult<()> { @@ -787,7 +788,7 @@ /// This takes a mutable reference to the results to account for the /// `extend` in timings #[timed] - fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) { + pub fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) { results.par_extend(self.dmap.par_iter().flat_map( move |(filename, entry)| { let filename: &HgPath = filename; @@ -837,7 +838,7 @@ } #[timed] -fn build_response<'a>( +pub fn build_response<'a>( results: impl IntoIterator<Item = DispatchedPath<'a>>, traversed: Vec<HgPathBuf>, ) -> (Vec<HgPathCow<'a>>, DirstateStatus<'a>) { @@ -899,56 +900,8 @@ (Vec<HgPathCow<'a>>, DirstateStatus<'a>), Vec<PatternFileWarning>, )> { - 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)) }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/hg-core/src/operations/dirstate_status.rs Wed Jun 24 17:53:44 2020 +0200 @@ -0,0 +1,76 @@ +// dirstate_status.rs +// +// Copyright 2019, Raphaël Gomès <rgomes@octobus.net> +// +// 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<HgPathCow<'a>>, DirstateStatus<'a>); + +impl<'a, M: Matcher + Sync> Operation<LookupAndStatus<'a>> for Status<'a, M> { + type Error = StatusError; + + fn run(&self) -> Result<LookupAndStatus<'a>, 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)) + } +}