D7058: rust-dirstate-status: add first Rust implementation of `dirstate.status`

Alphare (Raphaël Gomès) phabricator at mercurial-scm.org
Mon Oct 14 06:23:53 EDT 2019


Alphare updated this revision to Diff 17133.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D7058?vs=17132&id=17133

CHANGES SINCE LAST ACTION
  https://phab.mercurial-scm.org/D7058/new/

REVISION DETAIL
  https://phab.mercurial-scm.org/D7058

AFFECTED FILES
  rust/Cargo.lock
  rust/hg-core/Cargo.toml
  rust/hg-core/src/dirstate.rs
  rust/hg-core/src/dirstate/status.rs
  rust/hg-core/src/lib.rs
  rust/hg-core/src/utils/files.rs

CHANGE DETAILS

diff --git a/rust/hg-core/src/utils/files.rs b/rust/hg-core/src/utils/files.rs
--- a/rust/hg-core/src/utils/files.rs
+++ b/rust/hg-core/src/utils/files.rs
@@ -12,6 +12,7 @@
 use crate::utils::hg_path::{HgPath, HgPathBuf};
 use std::iter::FusedIterator;
 
+use std::fs::{read_link, Metadata};
 use std::path::Path;
 
 pub fn get_path_from_bytes(bytes: &[u8]) -> &Path {
@@ -79,6 +80,57 @@
     path.to_ascii_lowercase()
 }
 
+/// Get name in the case stored in the filesystem
+/// The name should be relative to root, and be normcase-ed for efficiency.
+///
+/// Note that this function is unnecessary, and should not be
+//  called, for case-sensitive filesystems (simply because it's expensive).
+/// The root should be normcase-ed, too.
+pub fn filesystem_path<P: AsRef<HgPath>>(root: &HgPath, path: P) -> HgPathBuf {
+    // TODO path for case-insensitive filesystems, for now this is transparent
+    root.to_owned().join(path.as_ref())
+}
+
+/// Returns `true` if path refers to an existing path.
+/// Returns `true` for broken symbolic links.
+/// Equivalent to `exists()` on platforms lacking `lstat`.
+pub fn lexists<P: AsRef<Path>>(path: P) -> bool {
+    if !path.as_ref().exists() {
+        return read_link(path).is_ok();
+    }
+    true
+}
+
+#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
+pub struct HgMetadata {
+    pub st_dev: u64,
+    pub st_mode: u32,
+    pub st_nlink: u64,
+    pub st_size: u64,
+    pub st_mtime: i64,
+    pub st_ctime: i64,
+}
+
+impl HgMetadata {
+    #[cfg(unix)]
+    pub fn from_metadata(metadata: Metadata) -> Self {
+        use std::os::linux::fs::MetadataExt;
+        Self {
+            st_dev: metadata.st_dev(),
+            st_mode: metadata.st_mode(),
+            st_nlink: metadata.st_nlink(),
+            st_size: metadata.st_size(),
+            st_mtime: metadata.st_mtime(),
+            st_ctime: metadata.st_ctime(),
+        }
+    }
+    #[cfg(not(unix))]
+    pub fn from_metdata(metadata: Metadata) -> Self {
+        // TODO support other platforms
+        unimplemented!()
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/rust/hg-core/src/lib.rs b/rust/hg-core/src/lib.rs
--- a/rust/hg-core/src/lib.rs
+++ b/rust/hg-core/src/lib.rs
@@ -12,6 +12,7 @@
     dirs_multiset::{DirsMultiset, DirsMultisetIter},
     dirstate_map::DirstateMap,
     parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
+    status::status,
     CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
     StateMap, StateMapIter,
 };
diff --git a/rust/hg-core/src/dirstate/status.rs b/rust/hg-core/src/dirstate/status.rs
new file mode 100644
--- /dev/null
+++ b/rust/hg-core/src/dirstate/status.rs
@@ -0,0 +1,254 @@
+// status.rs
+//
+// Copyright 2019 Raphaël Gomès <rgomes at 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.
+
+//! Rust implementation of dirstate.status (dirstate.py).
+//! It is currently missing a lot of functionality compared to the Python one
+//! and will only be triggered in narrow cases.
+
+use crate::utils::files::HgMetadata;
+use crate::utils::hg_path::{hg_path_to_path_buf, HgPathBuf};
+use crate::{DirstateEntry, DirstateMap, EntryState};
+use rayon::prelude::*;
+use std::collections::HashMap;
+use std::fs::Metadata;
+use std::path::Path;
+
+/// Get stat data about the files explicitly specified by match.
+/// TODO subrepos
+fn walk_explicit<P: AsRef<Path> + Sync>(
+    files: Vec<HgPathBuf>,
+    dmap: &mut DirstateMap,
+    root_dir: P,
+) -> std::io::Result<HashMap<HgPathBuf, Option<HgMetadata>>> {
+    let mut results = HashMap::new();
+
+    // A tuple of the normalized filename and the `Result` of the call to
+    // `symlink_metadata` for separate handling.
+    type WalkTuple<'a> = (&'a HgPathBuf, std::io::Result<Metadata>);
+
+    let stats_res: std::io::Result<Vec<WalkTuple>> = files
+        .par_iter()
+        .map(|filename| {
+            // TODO normalization
+            let normalized = filename;
+
+            let target_filename =
+                root_dir.as_ref().join(hg_path_to_path_buf(normalized)?);
+
+            Ok((normalized, target_filename.symlink_metadata()))
+        })
+        .collect();
+
+    for res in stats_res? {
+        match res {
+            (normalized, Ok(stat)) => {
+                if stat.is_file() {
+                    results.insert(
+                        normalized.to_owned(),
+                        Some(HgMetadata::from_metadata(stat)),
+                    );
+                } else {
+                    if dmap.contains_key(normalized) {
+                        results.insert(normalized.to_owned(), None);
+                    }
+                }
+            }
+            (normalized, Err(_)) => {
+                if dmap.contains_key(normalized) {
+                    results.insert(normalized.to_owned(), None);
+                }
+            }
+        };
+    }
+
+    Ok(results)
+}
+
+// Stat all entries in the `DirstateMap` and return their new metadata.
+pub fn stat_dmap_entries<P: AsRef<Path> + Sync>(
+    dmap: &DirstateMap,
+    results: &HashMap<HgPathBuf, Option<HgMetadata>>,
+    root_dir: P,
+) -> std::io::Result<Vec<(HgPathBuf, Option<HgMetadata>)>> {
+    dmap.par_iter()
+        .filter_map(
+            // Getting file metadata is costly, so we don't do it if the
+            // file is already present in the results, hence `filter_map`
+            |(filename, _)| -> Option<
+                std::io::Result<(HgPathBuf, Option<HgMetadata>)>
+            > {
+                if results.contains_key(filename) {
+                    return None;
+                }
+                let meta = match hg_path_to_path_buf(filename) {
+                    Ok(p) => root_dir.as_ref().join(p).symlink_metadata(),
+                    Err(e) => return Some(Err(e.into())),
+                };
+
+                Some(match meta {
+                    Ok(ref m)
+                        if !(m.file_type().is_file()
+                            || m.file_type().is_symlink()) =>
+                    {
+                        Ok((filename.to_owned(), None))
+                    }
+                    Ok(m) => Ok((
+                        filename.to_owned(),
+                        Some(HgMetadata::from_metadata(m)),
+                    )),
+                    Err(ref e)
+                        if e.kind() == std::io::ErrorKind::NotFound
+                            || e.raw_os_error() == Some(20) =>
+                    {
+                        // Rust does not yet have an `ErrorKind` for
+                        // `NotADirectory` (errno 20)
+                        Ok((filename.to_owned(), None))
+                    }
+                    Err(e) => Err(e),
+                })
+            },
+        )
+        .collect()
+}
+
+pub struct StatusResult {
+    pub modified: Vec<HgPathBuf>,
+    pub added: Vec<HgPathBuf>,
+    pub removed: Vec<HgPathBuf>,
+    pub deleted: Vec<HgPathBuf>,
+    pub clean: Vec<HgPathBuf>,
+    // TODO ignored
+    // TODO unknown
+}
+
+fn build_response(
+    dmap: &DirstateMap,
+    list_clean: bool,
+    last_normal_time: i64,
+    check_exec: bool,
+    results: HashMap<HgPathBuf, Option<HgMetadata>>,
+) -> ((Vec<HgPathBuf>, StatusResult)) {
+    let mut lookup = vec![];
+    let mut modified = vec![];
+    let mut added = vec![];
+    let mut removed = vec![];
+    let mut deleted = vec![];
+    let mut clean = vec![];
+
+    for (filename, metadata_option) in results.into_iter() {
+        let DirstateEntry {
+            state,
+            mode,
+            mtime,
+            size,
+        } = match dmap.get(&filename) {
+            None => {
+                continue;
+            }
+            Some(e) => *e,
+        };
+
+        match metadata_option {
+            None if match state {
+                EntryState::Normal
+                | EntryState::Merged
+                | EntryState::Added => true,
+                _ => false,
+            } =>
+            {
+                deleted.push(filename);
+            }
+            None => match state {
+                EntryState::Removed => removed.push(filename),
+                _ => {}
+            },
+            Some(HgMetadata {
+                st_mode,
+                st_size,
+                st_mtime,
+                ..
+            }) => {
+                match state {
+                    EntryState::Normal => {
+                        // Dates and times that are outside the 31-bit signed
+                        // range are compared modulo 2^31. This should prevent
+                        // it from behaving badly with very large files or
+                        // corrupt dates while still having a high probability
+                        // of detecting changes. (issue2608)
+                        let range_mask = 0x7fffffff;
+
+                        let size_changed = (size != st_size as i32)
+                            && size != (st_size as i32 & range_mask);
+                        let mode_changed = (mode ^ st_mode as i32) & 0o100
+                            != 0o000
+                            && check_exec;
+                        if size >= 0
+                            && (size_changed || mode_changed)
+                            || size == -2  // other parent
+                            || dmap.copy_map.contains_key(&filename)
+                        {
+                            modified.push(filename);
+                        } else if mtime != st_mtime as i32
+                            && mtime != (st_mtime as i32 & range_mask)
+                        {
+                            lookup.push(filename);
+                        } else if st_mtime == last_normal_time {
+                            // the file may have just been marked as normal and
+                            // it may have changed in the same second without
+                            // changing its size. This can happen if we quickly
+                            // do multiple commits. Force lookup, so we don't
+                            // miss such a racy file change.
+                            lookup.push(filename);
+                        } else if list_clean {
+                            clean.push(filename);
+                        }
+                    }
+                    EntryState::Merged => modified.push(filename),
+                    EntryState::Added => added.push(filename),
+                    EntryState::Removed => removed.push(filename),
+                    EntryState::Unknown => {}
+                }
+            }
+        }
+    }
+
+    (
+        lookup,
+        StatusResult {
+            modified,
+            added,
+            removed,
+            deleted,
+            clean,
+        },
+    )
+}
+
+pub fn status<P: AsRef<Path>>(
+    mut dmap: &mut DirstateMap,
+    root_dir: P,
+    files: Vec<HgPathBuf>,
+    list_clean: bool,
+    last_normal_time: i64,
+    check_exec: bool,
+) -> std::io::Result<(Vec<HgPathBuf>, StatusResult)>
+where
+    P: Sync,
+{
+    let mut results =
+        walk_explicit(files, &mut dmap, root_dir.as_ref().to_owned())?;
+
+    results.extend(stat_dmap_entries(&dmap, &results, root_dir)?);
+
+    Ok(build_response(
+        &dmap,
+        list_clean,
+        last_normal_time,
+        check_exec,
+        results,
+    ))
+}
diff --git a/rust/hg-core/src/dirstate.rs b/rust/hg-core/src/dirstate.rs
--- a/rust/hg-core/src/dirstate.rs
+++ b/rust/hg-core/src/dirstate.rs
@@ -13,6 +13,7 @@
 pub mod dirs_multiset;
 pub mod dirstate_map;
 pub mod parsers;
+pub mod status;
 
 #[derive(Debug, PartialEq, Clone)]
 pub struct DirstateParents {
diff --git a/rust/hg-core/Cargo.toml b/rust/hg-core/Cargo.toml
--- a/rust/hg-core/Cargo.toml
+++ b/rust/hg-core/Cargo.toml
@@ -15,3 +15,4 @@
 rand = "> 0.6.4"
 rand_pcg = "> 0.1.0"
 regex = "^1.1"
+rayon = "1.2.0"
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -9,6 +9,14 @@
 ]
 
 [[package]]
+name = "arrayvec"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "autocfg"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -24,6 +32,11 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "cfg-if"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "cloudabi"
 version = "0.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -43,6 +56,50 @@
 ]
 
 [[package]]
+name = "crossbeam-deque"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "either"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "fuchsia-cprng"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -56,6 +113,7 @@
  "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -94,11 +152,32 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "memoffset"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "nodrop"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "num-traits"
 version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "num_cpus"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "python27-sys"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -213,6 +292,28 @@
 ]
 
 [[package]]
+name = "rayon"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "rdrand"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -249,6 +350,11 @@
 ]
 
 [[package]]
+name = "scopeguard"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "semver"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -300,16 +406,26 @@
 
 [metadata]
 "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
+"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba"
 "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
 "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
 "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
+"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
 "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
 "checksum cpython 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b489034e723e7f5109fecd19b719e664f89ef925be785885252469e9822fa940"
+"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71"
+"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9"
+"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b"
+"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
+"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b"
 "checksum fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f7f8eb465745ea9b02e2704612a9946a59fa40572086c6fd49d6ddcf30bf31"
 "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14"
 "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
 "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
+"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f"
+"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
 "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
+"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273"
 "checksum python27-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56114c37d4dca82526d74009df7782a28c871ac9d36b19d4cb9e67672258527e"
 "checksum python3-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "61e4aac43f833fd637e429506cb2ac9d7df672c4b68f2eaaa163649b7fdc0444"
 "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
@@ -322,10 +438,13 @@
 "checksum rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b7c690732391ae0abafced5015ffb53656abfaec61b342290e5eb56b286a679d"
 "checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05"
 "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
+"checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123"
+"checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b"
 "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
 "checksum regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37e7cbbd370869ce2e8dff25c7018702d10b21a20ef7135316f8daecd6c25b7f"
 "checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
 "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
 "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
 "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"



To: Alphare, #hg-reviewers
Cc: martinvonz, durin42, kevincox, mercurial-devel


More information about the Mercurial-devel mailing list