rust-status: refactor handling of unknown files
authorRaphaël Gomès <rgomes@octobus.net>
Fri, 06 Mar 2020 17:48:30 +0100
changeset 44537 5f6a504dc0bd
parent 44536 f8a9922a02cb
child 44538 b8ba46c97cdd
rust-status: refactor handling of unknown files Differential Revision: https://phab.mercurial-scm.org/D8249
rust/hg-core/src/dirstate/status.rs
--- a/rust/hg-core/src/dirstate/status.rs	Wed Feb 19 11:14:30 2020 +0100
+++ b/rust/hg-core/src/dirstate/status.rs	Fri Mar 06 17:48:30 2020 +0100
@@ -597,6 +597,85 @@
     }
 }
 
+/// This takes a mutable reference to the results to account for the `extend`
+/// in timings
+fn handle_unknowns<'a>(
+    dmap: &'a DirstateMap,
+    matcher: &(impl Matcher + Sync),
+    root_dir: impl AsRef<Path> + Sync + Send + Copy,
+    options: StatusOptions,
+    results: &mut Vec<(Cow<'a, HgPath>, Dispatch)>,
+) -> IoResult<()> {
+    let to_visit: Vec<(&HgPath, &DirstateEntry)> = if results.is_empty()
+        && matcher.matches_everything()
+    {
+        dmap.iter().map(|(f, e)| (f.deref(), e)).collect()
+    } else {
+        // Only convert to a hashmap if needed.
+        let old_results: FastHashMap<_, _> = results.iter().cloned().collect();
+        dmap.iter()
+            .filter_map(move |(f, e)| {
+                if !old_results.contains_key(f.deref()) && matcher.matches(f) {
+                    Some((f.deref(), e))
+                } else {
+                    None
+                }
+            })
+            .collect()
+    };
+
+    // We walked all dirs under the roots that weren't ignored, and
+    // everything that matched was stat'ed and is already in results.
+    // The rest must thus be ignored or under a symlink.
+    let path_auditor = PathAuditor::new(root_dir);
+
+    // TODO don't collect. Find a way of replicating the behavior of
+    // `itertools::process_results`, but for `rayon::ParallelIterator`
+    let new_results: IoResult<Vec<_>> = to_visit
+        .into_par_iter()
+        .filter_map(|(filename, entry)| -> Option<IoResult<_>> {
+            // Report ignored items in the dmap as long as they are not
+            // under a symlink directory.
+            if path_auditor.check(filename) {
+                // TODO normalize for case-insensitive filesystems
+                let buf = match hg_path_to_path_buf(filename) {
+                    Ok(x) => x,
+                    Err(e) => return Some(Err(e.into())),
+                };
+                Some(Ok((
+                    Cow::Borrowed(filename),
+                    match root_dir.as_ref().join(&buf).symlink_metadata() {
+                        // File was just ignored, no links, and exists
+                        Ok(meta) => {
+                            let metadata = HgMetadata::from_metadata(meta);
+                            dispatch_found(
+                                filename,
+                                *entry,
+                                metadata,
+                                &dmap.copy_map,
+                                options,
+                            )
+                        }
+                        // File doesn't exist
+                        Err(_) => dispatch_missing(entry.state),
+                    },
+                )))
+            } else {
+                // It's either missing or under a symlink directory which
+                // we, in this case, report as missing.
+                Some(Ok((
+                    Cow::Borrowed(filename),
+                    dispatch_missing(entry.state),
+                )))
+            }
+        })
+        .collect();
+
+    results.par_extend(new_results?);
+
+    Ok(())
+}
+
 /// Get the status of files in the working directory.
 ///
 /// This is the current entry-point for `hg-core` and is realistically unusable
@@ -683,64 +762,7 @@
         // symlink directory.
 
         if options.list_unknown {
-            let to_visit: Box<dyn Iterator<Item = (&HgPath, &DirstateEntry)>> =
-                if results.is_empty() && matcher.matches_everything() {
-                    Box::new(dmap.iter().map(|(f, e)| (f.deref(), e)))
-                } else {
-                    // Only convert to a hashmap if needed.
-                    let old_results: FastHashMap<_, _> =
-                        results.iter().cloned().collect();
-                    Box::new(dmap.iter().filter_map(move |(f, e)| {
-                        if !old_results.contains_key(f.deref())
-                            && matcher.matches(f)
-                        {
-                            Some((f.deref(), e))
-                        } else {
-                            None
-                        }
-                    }))
-                };
-            let mut to_visit: Vec<_> = to_visit.collect();
-            to_visit.sort_by(|a, b| a.0.cmp(&b.0));
-
-            // We walked all dirs under the roots that weren't ignored, and
-            // everything that matched was stat'ed and is already in results.
-            // The rest must thus be ignored or under a symlink.
-            let path_auditor = PathAuditor::new(root_dir);
-
-            for (ref filename, entry) in to_visit {
-                // Report ignored items in the dmap as long as they are not
-                // under a symlink directory.
-                if path_auditor.check(filename) {
-                    // TODO normalize for case-insensitive filesystems
-                    let buf = hg_path_to_path_buf(filename)?;
-                    results.push((
-                        Cow::Borrowed(filename),
-                        match root_dir.as_ref().join(&buf).symlink_metadata() {
-                            // File was just ignored, no links, and exists
-                            Ok(meta) => {
-                                let metadata = HgMetadata::from_metadata(meta);
-                                dispatch_found(
-                                    filename,
-                                    *entry,
-                                    metadata,
-                                    &dmap.copy_map,
-                                    options,
-                                )
-                            }
-                            // File doesn't exist
-                            Err(_) => dispatch_missing(entry.state),
-                        },
-                    ));
-                } else {
-                    // It's either missing or under a symlink directory which
-                    // we, in this case, report as missing.
-                    results.push((
-                        Cow::Borrowed(filename),
-                        dispatch_missing(entry.state),
-                    ));
-                }
-            }
+            handle_unknowns(dmap, matcher, root_dir, options, &mut results)?;
         } else {
             // We may not have walked the full directory tree above, so stat
             // and check everything we missed.