Patchwork D11516: dirstate: Remove the flat Rust DirstateMap implementation

login
register
mail settings
Submitter phabricator
Date Sept. 29, 2021, 2:58 p.m.
Message ID <differential-rev-PHID-DREV-tmqnnuc56buusb5qwqje-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/49841/
State Superseded
Headers show

Comments

Patch

diff --git a/tests/test-symlinks.t b/tests/test-symlinks.t
--- a/tests/test-symlinks.t
+++ b/tests/test-symlinks.t
@@ -1,12 +1,6 @@ 
 #require symlink
 
-#testcases dirstate-v1 dirstate-v1-tree dirstate-v2
-
-#if dirstate-v1-tree
-#require rust
-  $ echo '[experimental]' >> $HGRCPATH
-  $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH
-#endif
+#testcases dirstate-v1 dirstate-v2
 
 #if dirstate-v2
 #require rust
diff --git a/tests/test-status.t b/tests/test-status.t
--- a/tests/test-status.t
+++ b/tests/test-status.t
@@ -1,4 +1,4 @@ 
-#testcases dirstate-v1 dirstate-v1-tree dirstate-v2
+#testcases dirstate-v1 dirstate-v2
 
 #if no-rust
   $ hg init repo0 --config format.exp-dirstate-v2=1
@@ -6,12 +6,6 @@ 
   [255]
 #endif
 
-#if dirstate-v1-tree
-#require rust
-  $ echo '[experimental]' >> $HGRCPATH
-  $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH
-#endif
-
 #if dirstate-v2
 #require rust
   $ echo '[format]' >> $HGRCPATH
diff --git a/tests/test-purge.t b/tests/test-purge.t
--- a/tests/test-purge.t
+++ b/tests/test-purge.t
@@ -1,10 +1,4 @@ 
-#testcases dirstate-v1 dirstate-v1-tree dirstate-v2
-
-#if dirstate-v1-tree
-#require rust
-  $ echo '[experimental]' >> $HGRCPATH
-  $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH
-#endif
+#testcases dirstate-v1 dirstate-v2
 
 #if dirstate-v2
 #require rust
diff --git a/tests/test-permissions.t b/tests/test-permissions.t
--- a/tests/test-permissions.t
+++ b/tests/test-permissions.t
@@ -1,12 +1,6 @@ 
 #require unix-permissions no-root reporevlogstore
 
-#testcases dirstate-v1 dirstate-v1-tree dirstate-v2
-
-#if dirstate-v1-tree
-#require rust
-  $ echo '[experimental]' >> $HGRCPATH
-  $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH
-#endif
+#testcases dirstate-v1 dirstate-v2
 
 #if dirstate-v2
 #require rust
diff --git a/tests/test-hgignore.t b/tests/test-hgignore.t
--- a/tests/test-hgignore.t
+++ b/tests/test-hgignore.t
@@ -1,10 +1,4 @@ 
-#testcases dirstate-v1 dirstate-v1-tree dirstate-v2
-
-#if dirstate-v1-tree
-#require rust
-  $ echo '[experimental]' >> $HGRCPATH
-  $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH
-#endif
+#testcases dirstate-v1 dirstate-v2
 
 #if dirstate-v2
 #require rust
diff --git a/tests/test-dirstate.t b/tests/test-dirstate.t
--- a/tests/test-dirstate.t
+++ b/tests/test-dirstate.t
@@ -1,10 +1,4 @@ 
-#testcases dirstate-v1 dirstate-v1-tree dirstate-v2
-
-#if dirstate-v1-tree
-#require rust
-  $ echo '[experimental]' >> $HGRCPATH
-  $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH
-#endif
+#testcases dirstate-v1 dirstate-v2
 
 #if dirstate-v2
 #require rust
diff --git a/tests/test-dirstate-race2.t b/tests/test-dirstate-race2.t
--- a/tests/test-dirstate-race2.t
+++ b/tests/test-dirstate-race2.t
@@ -1,10 +1,4 @@ 
-#testcases dirstate-v1 dirstate-v1-tree dirstate-v2
-
-#if dirstate-v1-tree
-#require rust
-  $ echo '[experimental]' >> $HGRCPATH
-  $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH
-#endif
+#testcases dirstate-v1 dirstate-v2
 
 #if dirstate-v2
 #require rust
diff --git a/tests/test-dirstate-race.t b/tests/test-dirstate-race.t
--- a/tests/test-dirstate-race.t
+++ b/tests/test-dirstate-race.t
@@ -1,10 +1,4 @@ 
-#testcases dirstate-v1 dirstate-v1-tree dirstate-v2
-
-#if dirstate-v1-tree
-#require rust
-  $ echo '[experimental]' >> $HGRCPATH
-  $ echo 'dirstate-tree.in-memory=1' >> $HGRCPATH
-#endif
+#testcases dirstate-v1 dirstate-v2
 
 #if dirstate-v2
 #require rust
diff --git a/rust/hg-cpython/src/dirstate/dirstate_map.rs b/rust/hg-cpython/src/dirstate/dirstate_map.rs
--- a/rust/hg-cpython/src/dirstate/dirstate_map.rs
+++ b/rust/hg-cpython/src/dirstate/dirstate_map.rs
@@ -23,6 +23,7 @@ 
 };
 use hg::{
     dirstate::parsers::Timestamp,
+    dirstate::StateMapIter,
     dirstate_tree::dirstate_map::DirstateMap as TreeDirstateMap,
     dirstate_tree::dispatch::DirstateMapMethods,
     dirstate_tree::on_disk::DirstateV2ParseError,
@@ -30,8 +31,7 @@ 
     revlog::Node,
     utils::files::normalize_case,
     utils::hg_path::{HgPath, HgPathBuf},
-    DirstateEntry, DirstateError, DirstateMap as RustDirstateMap,
-    DirstateParents, EntryState, StateMapIter,
+    DirstateEntry, DirstateError, DirstateParents, EntryState,
 };
 
 // TODO
@@ -52,25 +52,16 @@ 
     /// Returns a `(dirstate_map, parents)` tuple
     @staticmethod
     def new_v1(
-        use_dirstate_tree: bool,
         on_disk: PyBytes,
     ) -> PyResult<PyObject> {
-        let (inner, parents) = if use_dirstate_tree {
-            let on_disk = PyBytesDeref::new(py, on_disk);
-            let mut map = OwningDirstateMap::new_empty(on_disk);
-            let (on_disk, map_placeholder) = map.get_mut_pair();
+        let on_disk = PyBytesDeref::new(py, on_disk);
+        let mut map = OwningDirstateMap::new_empty(on_disk);
+        let (on_disk, map_placeholder) = map.get_mut_pair();
 
-            let (actual_map, parents) = TreeDirstateMap::new_v1(on_disk)
-                .map_err(|e| dirstate_error(py, e))?;
-            *map_placeholder = actual_map;
-            (Box::new(map) as _, parents)
-        } else {
-            let bytes = on_disk.data(py);
-            let mut map = RustDirstateMap::default();
-            let parents = map.read(bytes).map_err(|e| dirstate_error(py, e))?;
-            (Box::new(map) as _, parents)
-        };
-        let map = Self::create_instance(py, inner)?;
+        let (actual_map, parents) = TreeDirstateMap::new_v1(on_disk)
+            .map_err(|e| dirstate_error(py, e))?;
+        *map_placeholder = actual_map;
+        let map = Self::create_instance(py, Box::new(map))?;
         let parents = parents.map(|p| {
             let p1 = PyBytes::new(py, p.p1.as_bytes());
             let p2 = PyBytes::new(py, p.p2.as_bytes());
diff --git a/rust/hg-cpython/src/dirstate/copymap.rs b/rust/hg-cpython/src/dirstate/copymap.rs
--- a/rust/hg-cpython/src/dirstate/copymap.rs
+++ b/rust/hg-cpython/src/dirstate/copymap.rs
@@ -15,9 +15,9 @@ 
 
 use crate::dirstate::dirstate_map::v2_error;
 use crate::dirstate::dirstate_map::DirstateMap;
+use hg::dirstate::CopyMapIter;
 use hg::dirstate_tree::on_disk::DirstateV2ParseError;
 use hg::utils::hg_path::HgPath;
-use hg::CopyMapIter;
 
 py_class!(pub class CopyMap |py| {
     data dirstate_map: DirstateMap;
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
@@ -4,7 +4,6 @@ 
 
 mod cat;
 mod debugdata;
-mod dirstate_status;
 mod list_tracked_files;
 pub use cat::{cat, CatOutput};
 pub use debugdata::{debug_data, DebugDataKind};
diff --git a/rust/hg-core/src/operations/dirstate_status.rs b/rust/hg-core/src/operations/dirstate_status.rs
deleted file mode 100644
--- a/rust/hg-core/src/operations/dirstate_status.rs
+++ /dev/null
@@ -1,71 +0,0 @@ 
-// 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, Status};
-use crate::matchers::Matcher;
-use crate::{DirstateStatus, StatusError};
-
-impl<'a, M: ?Sized + Matcher + Sync> Status<'a, M> {
-    pub(crate) fn run(&self) -> Result<DirstateStatus<'a>, StatusError> {
-        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()
-            .map(std::borrow::Cow::Owned)
-            .collect();
-
-        Ok(build_response(results, traversed))
-    }
-}
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
@@ -16,14 +16,11 @@ 
 pub mod testing; // unconditionally built, for use from integration tests
 pub use dirstate::{
     dirs_multiset::{DirsMultiset, DirsMultisetIter},
-    dirstate_map::DirstateMap,
-    parsers::{pack_dirstate, parse_dirstate, PARENT_SIZE},
     status::{
-        status, BadMatch, BadType, DirstateStatus, HgPathCow, StatusError,
+        BadMatch, BadType, DirstateStatus, HgPathCow, StatusError,
         StatusOptions,
     },
-    CopyMap, CopyMapIter, DirstateEntry, DirstateParents, EntryState,
-    StateMap, StateMapIter,
+    DirstateEntry, DirstateParents, EntryState,
 };
 pub mod copy_tracing;
 mod filepatterns;
diff --git a/rust/hg-core/src/dirstate_tree/owning_dispatch.rs b/rust/hg-core/src/dirstate_tree/owning_dispatch.rs
--- a/rust/hg-core/src/dirstate_tree/owning_dispatch.rs
+++ b/rust/hg-core/src/dirstate_tree/owning_dispatch.rs
@@ -1,16 +1,16 @@ 
 use crate::dirstate::parsers::Timestamp;
+use crate::dirstate::CopyMapIter;
+use crate::dirstate::StateMapIter;
 use crate::dirstate_tree::dispatch::DirstateMapMethods;
 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
 use crate::dirstate_tree::owning::OwningDirstateMap;
 use crate::matchers::Matcher;
 use crate::utils::hg_path::{HgPath, HgPathBuf};
-use crate::CopyMapIter;
 use crate::DirstateEntry;
 use crate::DirstateError;
 use crate::DirstateParents;
 use crate::DirstateStatus;
 use crate::PatternFileWarning;
-use crate::StateMapIter;
 use crate::StatusError;
 use crate::StatusOptions;
 use std::path::PathBuf;
diff --git a/rust/hg-core/src/dirstate_tree/dispatch.rs b/rust/hg-core/src/dirstate_tree/dispatch.rs
--- a/rust/hg-core/src/dirstate_tree/dispatch.rs
+++ b/rust/hg-core/src/dirstate_tree/dispatch.rs
@@ -1,17 +1,16 @@ 
 use std::path::PathBuf;
 
 use crate::dirstate::parsers::Timestamp;
+use crate::dirstate::CopyMapIter;
+use crate::dirstate::StateMapIter;
 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
 use crate::matchers::Matcher;
 use crate::utils::hg_path::{HgPath, HgPathBuf};
-use crate::CopyMapIter;
 use crate::DirstateEntry;
 use crate::DirstateError;
-use crate::DirstateMap;
 use crate::DirstateParents;
 use crate::DirstateStatus;
 use crate::PatternFileWarning;
-use crate::StateMapIter;
 use crate::StatusError;
 use crate::StatusOptions;
 
@@ -212,190 +211,3 @@ 
             + '_,
     >;
 }
-
-impl DirstateMapMethods for DirstateMap {
-    fn clear(&mut self) {
-        self.clear()
-    }
-
-    /// Used to set a value directory.
-    ///
-    /// XXX Is temporary during a refactor of V1 dirstate and will disappear
-    /// shortly.
-    fn set_entry(
-        &mut self,
-        filename: &HgPath,
-        entry: DirstateEntry,
-    ) -> Result<(), DirstateV2ParseError> {
-        self.set_entry(&filename, entry);
-        Ok(())
-    }
-
-    fn add_file(
-        &mut self,
-        filename: &HgPath,
-        entry: DirstateEntry,
-    ) -> Result<(), DirstateError> {
-        self.add_file(filename, entry)
-    }
-
-    fn remove_file(
-        &mut self,
-        filename: &HgPath,
-        in_merge: bool,
-    ) -> Result<(), DirstateError> {
-        self.remove_file(filename, in_merge)
-    }
-
-    fn drop_entry_and_copy_source(
-        &mut self,
-        filename: &HgPath,
-    ) -> Result<(), DirstateError> {
-        self.drop_entry_and_copy_source(filename)
-    }
-
-    fn has_tracked_dir(
-        &mut self,
-        directory: &HgPath,
-    ) -> Result<bool, DirstateError> {
-        self.has_tracked_dir(directory)
-    }
-
-    fn has_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateError> {
-        self.has_dir(directory)
-    }
-
-    fn pack_v1(
-        &mut self,
-        parents: DirstateParents,
-        now: Timestamp,
-    ) -> Result<Vec<u8>, DirstateError> {
-        Ok(self.pack(parents, now)?)
-    }
-
-    fn pack_v2(
-        &mut self,
-        _now: Timestamp,
-        _can_append: bool,
-    ) -> Result<(Vec<u8>, Vec<u8>, bool), DirstateError> {
-        panic!(
-            "should have used dirstate_tree::DirstateMap to use the v2 format"
-        )
-    }
-
-    fn status<'a>(
-        &'a mut self,
-        matcher: &'a (dyn Matcher + Sync),
-        root_dir: PathBuf,
-        ignore_files: Vec<PathBuf>,
-        options: StatusOptions,
-    ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>
-    {
-        crate::status(self, matcher, root_dir, ignore_files, options)
-    }
-
-    fn copy_map_len(&self) -> usize {
-        self.copy_map.len()
-    }
-
-    fn copy_map_iter(&self) -> CopyMapIter<'_> {
-        Box::new(
-            self.copy_map
-                .iter()
-                .map(|(key, value)| Ok((&**key, &**value))),
-        )
-    }
-
-    fn copy_map_contains_key(
-        &self,
-        key: &HgPath,
-    ) -> Result<bool, DirstateV2ParseError> {
-        Ok(self.copy_map.contains_key(key))
-    }
-
-    fn copy_map_get(
-        &self,
-        key: &HgPath,
-    ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
-        Ok(self.copy_map.get(key).map(|p| &**p))
-    }
-
-    fn copy_map_remove(
-        &mut self,
-        key: &HgPath,
-    ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
-        Ok(self.copy_map.remove(key))
-    }
-
-    fn copy_map_insert(
-        &mut self,
-        key: HgPathBuf,
-        value: HgPathBuf,
-    ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
-        Ok(self.copy_map.insert(key, value))
-    }
-
-    fn len(&self) -> usize {
-        (&**self).len()
-    }
-
-    fn contains_key(
-        &self,
-        key: &HgPath,
-    ) -> Result<bool, DirstateV2ParseError> {
-        Ok((&**self).contains_key(key))
-    }
-
-    fn get(
-        &self,
-        key: &HgPath,
-    ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
-        Ok((&**self).get(key).cloned())
-    }
-
-    fn iter(&self) -> StateMapIter<'_> {
-        Box::new((&**self).iter().map(|(key, value)| Ok((&**key, *value))))
-    }
-
-    fn iter_tracked_dirs(
-        &mut self,
-    ) -> Result<
-        Box<
-            dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>>
-                + Send
-                + '_,
-        >,
-        DirstateError,
-    > {
-        self.set_all_dirs()?;
-        Ok(Box::new(
-            self.all_dirs
-                .as_ref()
-                .unwrap()
-                .iter()
-                .map(|path| Ok(&**path)),
-        ))
-    }
-
-    fn debug_iter(
-        &self,
-        all: bool,
-    ) -> Box<
-        dyn Iterator<
-                Item = Result<
-                    (&HgPath, (u8, i32, i32, i32)),
-                    DirstateV2ParseError,
-                >,
-            > + Send
-            + '_,
-    > {
-        // Not used for the flat (not tree-based) DirstateMap
-        let _ = all;
-
-        Box::new(
-            (&**self)
-                .iter()
-                .map(|(path, entry)| Ok((&**path, entry.debug_tuple()))),
-        )
-    }
-}
diff --git a/rust/hg-core/src/dirstate_tree/dirstate_map.rs b/rust/hg-core/src/dirstate_tree/dirstate_map.rs
--- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs
+++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs
@@ -11,11 +11,12 @@ 
 use crate::dirstate::parsers::packed_entry_size;
 use crate::dirstate::parsers::parse_dirstate_entries;
 use crate::dirstate::parsers::Timestamp;
+use crate::dirstate::CopyMapIter;
+use crate::dirstate::StateMapIter;
 use crate::dirstate::SIZE_FROM_OTHER_PARENT;
 use crate::dirstate::SIZE_NON_NORMAL;
 use crate::matchers::Matcher;
 use crate::utils::hg_path::{HgPath, HgPathBuf};
-use crate::CopyMapIter;
 use crate::DirstateEntry;
 use crate::DirstateError;
 use crate::DirstateParents;
@@ -23,7 +24,6 @@ 
 use crate::EntryState;
 use crate::FastHashMap;
 use crate::PatternFileWarning;
-use crate::StateMapIter;
 use crate::StatusError;
 use crate::StatusOptions;
 
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
@@ -10,33 +10,13 @@ 
 //! and will only be triggered in narrow cases.
 
 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
-use crate::utils::path_auditor::PathAuditor;
+
 use crate::{
-    dirstate::SIZE_FROM_OTHER_PARENT,
-    filepatterns::PatternFileWarning,
-    matchers::{get_ignore_function, Matcher, VisitChildrenSet},
-    utils::{
-        files::{find_dirs, HgMetadata},
-        hg_path::{
-            hg_path_to_path_buf, os_string_to_hg_path_buf, HgPath, HgPathBuf,
-            HgPathError,
-        },
-    },
-    CopyMap, DirstateEntry, DirstateMap, EntryState, FastHashMap,
+    utils::hg_path::{HgPath, HgPathError},
     PatternError,
 };
-use lazy_static::lazy_static;
-use micro_timer::timed;
-use rayon::prelude::*;
-use std::{
-    borrow::Cow,
-    collections::HashSet,
-    fmt,
-    fs::{read_dir, DirEntry},
-    io::ErrorKind,
-    ops::Deref,
-    path::{Path, PathBuf},
-};
+
+use std::{borrow::Cow, fmt};
 
 /// Wrong type of file from a `BadMatch`
 /// Note: a lot of those don't exist on all platforms.
@@ -70,32 +50,6 @@ 
     BadType(BadType),
 }
 
-/// Enum used to dispatch new status entries into the right collections.
-/// Is similar to `crate::EntryState`, but represents the transient state of
-/// entries during the lifetime of a command.
-#[derive(Debug, Copy, Clone)]
-pub enum Dispatch {
-    Unsure,
-    Modified,
-    Added,
-    Removed,
-    Deleted,
-    Clean,
-    Unknown,
-    Ignored,
-    /// Empty dispatch, the file is not worth listing
-    None,
-    /// Was explicitly matched but cannot be found/accessed
-    Bad(BadMatch),
-    Directory {
-        /// True if the directory used to be a file in the dmap so we can say
-        /// that it's been removed.
-        was_file: bool,
-    },
-}
-
-type IoResult<T> = std::io::Result<T>;
-
 /// `Box<dyn Trait>` is syntactic sugar for `Box<dyn Trait + 'static>`, so add
 /// an explicit lifetime here to not fight `'static` bounds "out of nowhere".
 pub type IgnoreFnType<'a> =
@@ -105,135 +59,6 @@ 
 /// the dirstate/explicit) paths, this comes up a lot.
 pub type HgPathCow<'a> = Cow<'a, HgPath>;
 
-/// A path with its computed ``Dispatch`` information
-type DispatchedPath<'a> = (HgPathCow<'a>, Dispatch);
-
-/// The conversion from `HgPath` to a real fs path failed.
-/// `22` is the error code for "Invalid argument"
-const INVALID_PATH_DISPATCH: Dispatch = Dispatch::Bad(BadMatch::OsError(22));
-
-/// Dates and times that are outside the 31-bit signed range are compared
-/// modulo 2^31. This should prevent hg from behaving badly with very large
-/// files or corrupt dates while still having a high probability of detecting
-/// changes. (issue2608)
-/// TODO I haven't found a way of having `b` be `Into<i32>`, since `From<u64>`
-/// is not defined for `i32`, and there is no `As` trait. This forces the
-/// caller to cast `b` as `i32`.
-fn mod_compare(a: i32, b: i32) -> bool {
-    a & i32::max_value() != b & i32::max_value()
-}
-
-/// Return a sorted list containing information about the entries
-/// in the directory.
-///
-/// * `skip_dot_hg` - Return an empty vec if `path` contains a `.hg` directory
-fn list_directory(
-    path: impl AsRef<Path>,
-    skip_dot_hg: bool,
-) -> std::io::Result<Vec<(HgPathBuf, DirEntry)>> {
-    let mut results = vec![];
-    let entries = read_dir(path.as_ref())?;
-
-    for entry in entries {
-        let entry = entry?;
-        let filename = os_string_to_hg_path_buf(entry.file_name())?;
-        let file_type = entry.file_type()?;
-        if skip_dot_hg && filename.as_bytes() == b".hg" && file_type.is_dir() {
-            return Ok(vec![]);
-        } else {
-            results.push((filename, entry))
-        }
-    }
-
-    results.sort_unstable_by_key(|e| e.0.clone());
-    Ok(results)
-}
-
-/// The file corresponding to the dirstate entry was found on the filesystem.
-fn dispatch_found(
-    filename: impl AsRef<HgPath>,
-    entry: DirstateEntry,
-    metadata: HgMetadata,
-    copy_map: &CopyMap,
-    options: StatusOptions,
-) -> Dispatch {
-    match entry.state() {
-        EntryState::Normal => {
-            let mode = entry.mode();
-            let size = entry.size();
-            let mtime = entry.mtime();
-
-            let HgMetadata {
-                st_mode,
-                st_size,
-                st_mtime,
-                ..
-            } = metadata;
-
-            let size_changed = mod_compare(size, st_size as i32);
-            let mode_changed =
-                (mode ^ st_mode as i32) & 0o100 != 0o000 && options.check_exec;
-            let metadata_changed = size >= 0 && (size_changed || mode_changed);
-            let other_parent = size == SIZE_FROM_OTHER_PARENT;
-
-            if metadata_changed
-                || other_parent
-                || copy_map.contains_key(filename.as_ref())
-            {
-                if metadata.is_symlink() && size_changed {
-                    // issue6456: Size returned may be longer due to encryption
-                    // on EXT-4 fscrypt. TODO maybe only do it on EXT4?
-                    Dispatch::Unsure
-                } else {
-                    Dispatch::Modified
-                }
-            } else if mod_compare(mtime, st_mtime as i32)
-                || st_mtime == options.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.
-                Dispatch::Unsure
-            } else if options.list_clean {
-                Dispatch::Clean
-            } else {
-                Dispatch::None
-            }
-        }
-        EntryState::Merged => Dispatch::Modified,
-        EntryState::Added => Dispatch::Added,
-        EntryState::Removed => Dispatch::Removed,
-    }
-}
-
-/// The file corresponding to this Dirstate entry is missing.
-fn dispatch_missing(state: EntryState) -> Dispatch {
-    match state {
-        // File was removed from the filesystem during commands
-        EntryState::Normal | EntryState::Merged | EntryState::Added => {
-            Dispatch::Deleted
-        }
-        // File was removed, everything is normal
-        EntryState::Removed => Dispatch::Removed,
-    }
-}
-
-fn dispatch_os_error(e: &std::io::Error) -> Dispatch {
-    Dispatch::Bad(BadMatch::OsError(
-        e.raw_os_error().expect("expected real OS error"),
-    ))
-}
-
-lazy_static! {
-    static ref DEFAULT_WORK: HashSet<&'static HgPath> = {
-        let mut h = HashSet::new();
-        h.insert(HgPath::new(b""));
-        h
-    };
-}
-
 #[derive(Debug, Copy, Clone)]
 pub struct StatusOptions {
     /// Remember the most recent modification timeslot for status, to make
@@ -319,626 +144,3 @@ 
         }
     }
 }
-
-/// Gives information about which files are changed in the working directory
-/// and how, compared to the revision we're based on
-pub struct Status<'a, M: ?Sized + Matcher + Sync> {
-    dmap: &'a DirstateMap,
-    pub(crate) matcher: &'a M,
-    root_dir: PathBuf,
-    pub(crate) options: StatusOptions,
-    ignore_fn: IgnoreFnType<'a>,
-}
-
-impl<'a, M> Status<'a, M>
-where
-    M: ?Sized + Matcher + Sync,
-{
-    pub fn new(
-        dmap: &'a DirstateMap,
-        matcher: &'a M,
-        root_dir: PathBuf,
-        ignore_files: Vec<PathBuf>,
-        options: StatusOptions,
-    ) -> StatusResult<(Self, Vec<PatternFileWarning>)> {
-        // Needs to outlive `dir_ignore_fn` since it's captured.
-
-        let (ignore_fn, warnings): (IgnoreFnType, _) =
-            if options.list_ignored || options.list_unknown {
-                get_ignore_function(ignore_files, &root_dir, &mut |_| {})?
-            } else {
-                (Box::new(|&_| true), vec![])
-            };
-
-        Ok((
-            Self {
-                dmap,
-                matcher,
-                root_dir,
-                options,
-                ignore_fn,
-            },
-            warnings,
-        ))
-    }
-
-    /// Is the path ignored?
-    pub fn is_ignored(&self, path: impl AsRef<HgPath>) -> bool {
-        (self.ignore_fn)(path.as_ref())
-    }
-
-    /// Is the path or one of its ancestors ignored?
-    pub fn dir_ignore(&self, dir: impl AsRef<HgPath>) -> bool {
-        // Only involve ignore mechanism if we're listing unknowns or ignored.
-        if self.options.list_ignored || self.options.list_unknown {
-            if self.is_ignored(&dir) {
-                true
-            } else {
-                for p in find_dirs(dir.as_ref()) {
-                    if self.is_ignored(p) {
-                        return true;
-                    }
-                }
-                false
-            }
-        } else {
-            true
-        }
-    }
-
-    /// Get stat data about the files explicitly specified by the matcher.
-    /// Returns a tuple of the directories that need to be traversed and the
-    /// files with their corresponding `Dispatch`.
-    /// TODO subrepos
-    #[timed]
-    pub fn walk_explicit(
-        &self,
-        traversed_sender: crossbeam_channel::Sender<HgPathBuf>,
-    ) -> (Vec<DispatchedPath<'a>>, Vec<DispatchedPath<'a>>) {
-        self.matcher
-            .file_set()
-            .unwrap_or(&DEFAULT_WORK)
-            .par_iter()
-            .flat_map(|&filename| -> Option<_> {
-                // TODO normalization
-                let normalized = filename;
-
-                let buf = match hg_path_to_path_buf(normalized) {
-                    Ok(x) => x,
-                    Err(_) => {
-                        return Some((
-                            Cow::Borrowed(normalized),
-                            INVALID_PATH_DISPATCH,
-                        ))
-                    }
-                };
-                let target = self.root_dir.join(buf);
-                let st = target.symlink_metadata();
-                let in_dmap = self.dmap.get(normalized);
-                match st {
-                    Ok(meta) => {
-                        let file_type = meta.file_type();
-                        return if file_type.is_file() || file_type.is_symlink()
-                        {
-                            if let Some(entry) = in_dmap {
-                                return Some((
-                                    Cow::Borrowed(normalized),
-                                    dispatch_found(
-                                        &normalized,
-                                        *entry,
-                                        HgMetadata::from_metadata(meta),
-                                        &self.dmap.copy_map,
-                                        self.options,
-                                    ),
-                                ));
-                            }
-                            Some((
-                                Cow::Borrowed(normalized),
-                                Dispatch::Unknown,
-                            ))
-                        } else if file_type.is_dir() {
-                            if self.options.collect_traversed_dirs {
-                                traversed_sender
-                                    .send(normalized.to_owned())
-                                    .expect("receiver should outlive sender");
-                            }
-                            Some((
-                                Cow::Borrowed(normalized),
-                                Dispatch::Directory {
-                                    was_file: in_dmap.is_some(),
-                                },
-                            ))
-                        } else {
-                            Some((
-                                Cow::Borrowed(normalized),
-                                Dispatch::Bad(BadMatch::BadType(
-                                    // TODO do more than unknown
-                                    // Support for all `BadType` variant
-                                    // varies greatly between platforms.
-                                    // So far, no tests check the type and
-                                    // this should be good enough for most
-                                    // users.
-                                    BadType::Unknown,
-                                )),
-                            ))
-                        };
-                    }
-                    Err(_) => {
-                        if let Some(entry) = in_dmap {
-                            return Some((
-                                Cow::Borrowed(normalized),
-                                dispatch_missing(entry.state()),
-                            ));
-                        }
-                    }
-                };
-                None
-            })
-            .partition(|(_, dispatch)| match dispatch {
-                Dispatch::Directory { .. } => true,
-                _ => false,
-            })
-    }
-
-    /// Walk the working directory recursively to look for changes compared to
-    /// the current `DirstateMap`.
-    ///
-    /// This takes a mutable reference to the results to account for the
-    /// `extend` in timings
-    #[timed]
-    pub fn traverse(
-        &self,
-        path: impl AsRef<HgPath>,
-        old_results: &FastHashMap<HgPathCow<'a>, Dispatch>,
-        results: &mut Vec<DispatchedPath<'a>>,
-        traversed_sender: crossbeam_channel::Sender<HgPathBuf>,
-    ) {
-        // The traversal is done in parallel, so use a channel to gather
-        // entries. `crossbeam_channel::Sender` is `Sync`, while `mpsc::Sender`
-        // is not.
-        let (files_transmitter, files_receiver) =
-            crossbeam_channel::unbounded();
-
-        self.traverse_dir(
-            &files_transmitter,
-            path,
-            &old_results,
-            traversed_sender,
-        );
-
-        // Disconnect the channel so the receiver stops waiting
-        drop(files_transmitter);
-
-        let new_results = files_receiver
-            .into_iter()
-            .par_bridge()
-            .map(|(f, d)| (Cow::Owned(f), d));
-
-        results.par_extend(new_results);
-    }
-
-    /// Dispatch a single entry (file, folder, symlink...) found during
-    /// `traverse`. If the entry is a folder that needs to be traversed, it
-    /// will be handled in a separate thread.
-    fn handle_traversed_entry<'b>(
-        &'a self,
-        scope: &rayon::Scope<'b>,
-        files_sender: &'b crossbeam_channel::Sender<(HgPathBuf, Dispatch)>,
-        old_results: &'a FastHashMap<Cow<HgPath>, Dispatch>,
-        filename: HgPathBuf,
-        dir_entry: DirEntry,
-        traversed_sender: crossbeam_channel::Sender<HgPathBuf>,
-    ) -> IoResult<()>
-    where
-        'a: 'b,
-    {
-        let file_type = dir_entry.file_type()?;
-        let entry_option = self.dmap.get(&filename);
-
-        if filename.as_bytes() == b".hg" {
-            // Could be a directory or a symlink
-            return Ok(());
-        }
-
-        if file_type.is_dir() {
-            self.handle_traversed_dir(
-                scope,
-                files_sender,
-                old_results,
-                entry_option,
-                filename,
-                traversed_sender,
-            );
-        } else if file_type.is_file() || file_type.is_symlink() {
-            if let Some(entry) = entry_option {
-                if self.matcher.matches_everything()
-                    || self.matcher.matches(&filename)
-                {
-                    let metadata = dir_entry.metadata()?;
-                    files_sender
-                        .send((
-                            filename.to_owned(),
-                            dispatch_found(
-                                &filename,
-                                *entry,
-                                HgMetadata::from_metadata(metadata),
-                                &self.dmap.copy_map,
-                                self.options,
-                            ),
-                        ))
-                        .unwrap();
-                }
-            } else if (self.matcher.matches_everything()
-                || self.matcher.matches(&filename))
-                && !self.is_ignored(&filename)
-            {
-                if (self.options.list_ignored
-                    || self.matcher.exact_match(&filename))
-                    && self.dir_ignore(&filename)
-                {
-                    if self.options.list_ignored {
-                        files_sender
-                            .send((filename.to_owned(), Dispatch::Ignored))
-                            .unwrap();
-                    }
-                } else if self.options.list_unknown {
-                    files_sender
-                        .send((filename.to_owned(), Dispatch::Unknown))
-                        .unwrap();
-                }
-            } else if self.is_ignored(&filename) && self.options.list_ignored {
-                if self.matcher.matches(&filename) {
-                    files_sender
-                        .send((filename.to_owned(), Dispatch::Ignored))
-                        .unwrap();
-                }
-            }
-        } else if let Some(entry) = entry_option {
-            // Used to be a file or a folder, now something else.
-            if self.matcher.matches_everything()
-                || self.matcher.matches(&filename)
-            {
-                files_sender
-                    .send((
-                        filename.to_owned(),
-                        dispatch_missing(entry.state()),
-                    ))
-                    .unwrap();
-            }
-        }
-
-        Ok(())
-    }
-
-    /// A directory was found in the filesystem and needs to be traversed
-    fn handle_traversed_dir<'b>(
-        &'a self,
-        scope: &rayon::Scope<'b>,
-        files_sender: &'b crossbeam_channel::Sender<(HgPathBuf, Dispatch)>,
-        old_results: &'a FastHashMap<Cow<HgPath>, Dispatch>,
-        entry_option: Option<&'a DirstateEntry>,
-        directory: HgPathBuf,
-        traversed_sender: crossbeam_channel::Sender<HgPathBuf>,
-    ) where
-        'a: 'b,
-    {
-        scope.spawn(move |_| {
-            // Nested `if` until `rust-lang/rust#53668` is stable
-            if let Some(entry) = entry_option {
-                // Used to be a file, is now a folder
-                if self.matcher.matches_everything()
-                    || self.matcher.matches(&directory)
-                {
-                    files_sender
-                        .send((
-                            directory.to_owned(),
-                            dispatch_missing(entry.state()),
-                        ))
-                        .unwrap();
-                }
-            }
-            // Do we need to traverse it?
-            if !self.is_ignored(&directory) || self.options.list_ignored {
-                self.traverse_dir(
-                    files_sender,
-                    directory,
-                    &old_results,
-                    traversed_sender,
-                )
-            }
-        });
-    }
-
-    /// Decides whether the directory needs to be listed, and if so handles the
-    /// entries in a separate thread.
-    fn traverse_dir(
-        &self,
-        files_sender: &crossbeam_channel::Sender<(HgPathBuf, Dispatch)>,
-        directory: impl AsRef<HgPath>,
-        old_results: &FastHashMap<Cow<HgPath>, Dispatch>,
-        traversed_sender: crossbeam_channel::Sender<HgPathBuf>,
-    ) {
-        let directory = directory.as_ref();
-
-        if self.options.collect_traversed_dirs {
-            traversed_sender
-                .send(directory.to_owned())
-                .expect("receiver should outlive sender");
-        }
-
-        let visit_entries = match self.matcher.visit_children_set(directory) {
-            VisitChildrenSet::Empty => return,
-            VisitChildrenSet::This | VisitChildrenSet::Recursive => None,
-            VisitChildrenSet::Set(set) => Some(set),
-        };
-        let buf = match hg_path_to_path_buf(directory) {
-            Ok(b) => b,
-            Err(_) => {
-                files_sender
-                    .send((directory.to_owned(), INVALID_PATH_DISPATCH))
-                    .expect("receiver should outlive sender");
-                return;
-            }
-        };
-        let dir_path = self.root_dir.join(buf);
-
-        let skip_dot_hg = !directory.as_bytes().is_empty();
-        let entries = match list_directory(dir_path, skip_dot_hg) {
-            Err(e) => {
-                files_sender
-                    .send((directory.to_owned(), dispatch_os_error(&e)))
-                    .expect("receiver should outlive sender");
-                return;
-            }
-            Ok(entries) => entries,
-        };
-
-        rayon::scope(|scope| {
-            for (filename, dir_entry) in entries {
-                if let Some(ref set) = visit_entries {
-                    if !set.contains(filename.deref()) {
-                        continue;
-                    }
-                }
-                // TODO normalize
-                let filename = if directory.is_empty() {
-                    filename.to_owned()
-                } else {
-                    directory.join(&filename)
-                };
-
-                if !old_results.contains_key(filename.deref()) {
-                    match self.handle_traversed_entry(
-                        scope,
-                        files_sender,
-                        old_results,
-                        filename,
-                        dir_entry,
-                        traversed_sender.clone(),
-                    ) {
-                        Err(e) => {
-                            files_sender
-                                .send((
-                                    directory.to_owned(),
-                                    dispatch_os_error(&e),
-                                ))
-                                .expect("receiver should outlive sender");
-                        }
-                        Ok(_) => {}
-                    }
-                }
-            }
-        })
-    }
-
-    /// Add the files in the dirstate to the results.
-    ///
-    /// This takes a mutable reference to the results to account for the
-    /// `extend` in timings
-    #[timed]
-    pub fn extend_from_dmap(&self, results: &mut Vec<DispatchedPath<'a>>) {
-        results.par_extend(
-            self.dmap
-                .par_iter()
-                .filter(|(path, _)| self.matcher.matches(path))
-                .map(move |(filename, entry)| {
-                    let filename: &HgPath = filename;
-                    let filename_as_path = match hg_path_to_path_buf(filename)
-                    {
-                        Ok(f) => f,
-                        Err(_) => {
-                            return (
-                                Cow::Borrowed(filename),
-                                INVALID_PATH_DISPATCH,
-                            )
-                        }
-                    };
-                    let meta = self
-                        .root_dir
-                        .join(filename_as_path)
-                        .symlink_metadata();
-                    match meta {
-                        Ok(m)
-                            if !(m.file_type().is_file()
-                                || m.file_type().is_symlink()) =>
-                        {
-                            (
-                                Cow::Borrowed(filename),
-                                dispatch_missing(entry.state()),
-                            )
-                        }
-                        Ok(m) => (
-                            Cow::Borrowed(filename),
-                            dispatch_found(
-                                filename,
-                                *entry,
-                                HgMetadata::from_metadata(m),
-                                &self.dmap.copy_map,
-                                self.options,
-                            ),
-                        ),
-                        Err(e)
-                            if e.kind() == ErrorKind::NotFound
-                                || e.raw_os_error() == Some(20) =>
-                        {
-                            // Rust does not yet have an `ErrorKind` for
-                            // `NotADirectory` (errno 20)
-                            // It happens if the dirstate contains `foo/bar`
-                            // and foo is not a
-                            // directory
-                            (
-                                Cow::Borrowed(filename),
-                                dispatch_missing(entry.state()),
-                            )
-                        }
-                        Err(e) => {
-                            (Cow::Borrowed(filename), dispatch_os_error(&e))
-                        }
-                    }
-                }),
-        );
-    }
-
-    /// Checks all files that are in the dirstate but were not found during the
-    /// working directory traversal. This means that the rest must
-    /// be either ignored, under a symlink or under a new nested repo.
-    ///
-    /// This takes a mutable reference to the results to account for the
-    /// `extend` in timings
-    #[timed]
-    pub fn handle_unknowns(&self, results: &mut Vec<DispatchedPath<'a>>) {
-        let to_visit: Vec<(&HgPath, &DirstateEntry)> =
-            if results.is_empty() && self.matcher.matches_everything() {
-                self.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();
-                self.dmap
-                    .iter()
-                    .filter_map(move |(f, e)| {
-                        if !old_results.contains_key(f.deref())
-                            && self.matcher.matches(f)
-                        {
-                            Some((f.deref(), e))
-                        } else {
-                            None
-                        }
-                    })
-                    .collect()
-            };
-
-        let path_auditor = PathAuditor::new(&self.root_dir);
-
-        let new_results = to_visit.into_par_iter().filter_map(
-            |(filename, entry)| -> Option<_> {
-                // 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(_) => {
-                            return Some((
-                                Cow::Owned(filename.to_owned()),
-                                INVALID_PATH_DISPATCH,
-                            ));
-                        }
-                    };
-                    Some((
-                        Cow::Owned(filename.to_owned()),
-                        match self.root_dir.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,
-                                    &self.dmap.copy_map,
-                                    self.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((
-                        Cow::Owned(filename.to_owned()),
-                        dispatch_missing(entry.state()),
-                    ))
-                }
-            },
-        );
-
-        results.par_extend(new_results);
-    }
-}
-
-#[timed]
-pub fn build_response<'a>(
-    results: impl IntoIterator<Item = DispatchedPath<'a>>,
-    traversed: Vec<HgPathCow<'a>>,
-) -> DirstateStatus<'a> {
-    let mut unsure = vec![];
-    let mut modified = vec![];
-    let mut added = vec![];
-    let mut removed = vec![];
-    let mut deleted = vec![];
-    let mut clean = vec![];
-    let mut ignored = vec![];
-    let mut unknown = vec![];
-    let mut bad = vec![];
-
-    for (filename, dispatch) in results.into_iter() {
-        match dispatch {
-            Dispatch::Unknown => unknown.push(filename),
-            Dispatch::Unsure => unsure.push(filename),
-            Dispatch::Modified => modified.push(filename),
-            Dispatch::Added => added.push(filename),
-            Dispatch::Removed => removed.push(filename),
-            Dispatch::Deleted => deleted.push(filename),
-            Dispatch::Clean => clean.push(filename),
-            Dispatch::Ignored => ignored.push(filename),
-            Dispatch::None => {}
-            Dispatch::Bad(reason) => bad.push((filename, reason)),
-            Dispatch::Directory { .. } => {}
-        }
-    }
-
-    DirstateStatus {
-        modified,
-        added,
-        removed,
-        deleted,
-        clean,
-        ignored,
-        unknown,
-        bad,
-        unsure,
-        traversed,
-        dirty: false,
-    }
-}
-
-/// Get the status of files in the working directory.
-///
-/// This is the current entry-point for `hg-core` and is realistically unusable
-/// outside of a Python context because its arguments need to provide a lot of
-/// information that will not be necessary in the future.
-#[timed]
-pub fn status<'a>(
-    dmap: &'a DirstateMap,
-    matcher: &'a (dyn Matcher + Sync),
-    root_dir: PathBuf,
-    ignore_files: Vec<PathBuf>,
-    options: StatusOptions,
-) -> StatusResult<(DirstateStatus<'a>, Vec<PatternFileWarning>)> {
-    let (status, warnings) =
-        Status::new(dmap, matcher, root_dir, ignore_files, options)?;
-
-    Ok((status.run()?, warnings))
-}
diff --git a/rust/hg-core/src/dirstate/parsers.rs b/rust/hg-core/src/dirstate/parsers.rs
--- a/rust/hg-core/src/dirstate/parsers.rs
+++ b/rust/hg-core/src/dirstate/parsers.rs
@@ -5,14 +5,11 @@ 
 
 use crate::errors::HgError;
 use crate::utils::hg_path::HgPath;
-use crate::{
-    dirstate::{CopyMap, EntryState, StateMap},
-    DirstateEntry, DirstateParents,
-};
+use crate::{dirstate::EntryState, DirstateEntry, DirstateParents};
 use byteorder::{BigEndian, WriteBytesExt};
 use bytes_cast::{unaligned, BytesCast};
 use micro_timer::timed;
-use std::convert::{TryFrom, TryInto};
+use std::convert::TryFrom;
 
 /// Parents are stored in the dirstate as byte hashes.
 pub const PARENT_SIZE: usize = 20;
@@ -141,328 +138,3 @@ 
 
 /// Seconds since the Unix epoch
 pub struct Timestamp(pub i64);
-
-pub fn pack_dirstate(
-    state_map: &mut StateMap,
-    copy_map: &CopyMap,
-    parents: DirstateParents,
-    now: Timestamp,
-) -> Result<Vec<u8>, HgError> {
-    // TODO move away from i32 before 2038.
-    let now: i32 = now.0.try_into().expect("time overflow");
-
-    let expected_size: usize = state_map
-        .iter()
-        .map(|(filename, _)| {
-            packed_entry_size(filename, copy_map.get(filename).map(|p| &**p))
-        })
-        .sum();
-    let expected_size = expected_size + PARENT_SIZE * 2;
-
-    let mut packed = Vec::with_capacity(expected_size);
-
-    packed.extend(parents.p1.as_bytes());
-    packed.extend(parents.p2.as_bytes());
-
-    for (filename, entry) in state_map.iter_mut() {
-        entry.clear_ambiguous_mtime(now);
-        pack_entry(
-            filename,
-            entry,
-            copy_map.get(filename).map(|p| &**p),
-            &mut packed,
-        )
-    }
-
-    if packed.len() != expected_size {
-        return Err(HgError::CorruptedRepository(format!(
-            "bad dirstate size: {} != {}",
-            expected_size,
-            packed.len()
-        )));
-    }
-
-    Ok(packed)
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::{utils::hg_path::HgPathBuf, FastHashMap};
-    use pretty_assertions::assert_eq;
-
-    #[test]
-    fn test_pack_dirstate_empty() {
-        let mut state_map = StateMap::default();
-        let copymap = FastHashMap::default();
-        let parents = DirstateParents {
-            p1: b"12345678910111213141".into(),
-            p2: b"00000000000000000000".into(),
-        };
-        let now = Timestamp(15000000);
-        let expected = b"1234567891011121314100000000000000000000".to_vec();
-
-        assert_eq!(
-            expected,
-            pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
-        );
-
-        assert!(state_map.is_empty())
-    }
-    #[test]
-    fn test_pack_dirstate_one_entry() {
-        let expected_state_map: StateMap = [(
-            HgPathBuf::from_bytes(b"f1"),
-            DirstateEntry::from_v1_data(
-                EntryState::Normal,
-                0o644,
-                0,
-                791231220,
-            ),
-        )]
-        .iter()
-        .cloned()
-        .collect();
-        let mut state_map = expected_state_map.clone();
-
-        let copymap = FastHashMap::default();
-        let parents = DirstateParents {
-            p1: b"12345678910111213141".into(),
-            p2: b"00000000000000000000".into(),
-        };
-        let now = Timestamp(15000000);
-        let expected = [
-            49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
-            51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
-            48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
-            41, 58, 244, 0, 0, 0, 2, 102, 49,
-        ]
-        .to_vec();
-
-        assert_eq!(
-            expected,
-            pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
-        );
-
-        assert_eq!(expected_state_map, state_map);
-    }
-    #[test]
-    fn test_pack_dirstate_one_entry_with_copy() {
-        let expected_state_map: StateMap = [(
-            HgPathBuf::from_bytes(b"f1"),
-            DirstateEntry::from_v1_data(
-                EntryState::Normal,
-                0o644,
-                0,
-                791231220,
-            ),
-        )]
-        .iter()
-        .cloned()
-        .collect();
-        let mut state_map = expected_state_map.clone();
-        let mut copymap = FastHashMap::default();
-        copymap.insert(
-            HgPathBuf::from_bytes(b"f1"),
-            HgPathBuf::from_bytes(b"copyname"),
-        );
-        let parents = DirstateParents {
-            p1: b"12345678910111213141".into(),
-            p2: b"00000000000000000000".into(),
-        };
-        let now = Timestamp(15000000);
-        let expected = [
-            49, 50, 51, 52, 53, 54, 55, 56, 57, 49, 48, 49, 49, 49, 50, 49,
-            51, 49, 52, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
-            48, 48, 48, 48, 48, 48, 48, 48, 110, 0, 0, 1, 164, 0, 0, 0, 0, 47,
-            41, 58, 244, 0, 0, 0, 11, 102, 49, 0, 99, 111, 112, 121, 110, 97,
-            109, 101,
-        ]
-        .to_vec();
-
-        assert_eq!(
-            expected,
-            pack_dirstate(&mut state_map, &copymap, parents, now).unwrap()
-        );
-        assert_eq!(expected_state_map, state_map);
-    }
-
-    #[test]
-    fn test_parse_pack_one_entry_with_copy() {
-        let mut state_map: StateMap = [(
-            HgPathBuf::from_bytes(b"f1"),
-            DirstateEntry::from_v1_data(
-                EntryState::Normal,
-                0o644,
-                0,
-                791231220,
-            ),
-        )]
-        .iter()
-        .cloned()
-        .collect();
-        let mut copymap = FastHashMap::default();
-        copymap.insert(
-            HgPathBuf::from_bytes(b"f1"),
-            HgPathBuf::from_bytes(b"copyname"),
-        );
-        let parents = DirstateParents {
-            p1: b"12345678910111213141".into(),
-            p2: b"00000000000000000000".into(),
-        };
-        let now = Timestamp(15000000);
-        let result =
-            pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
-                .unwrap();
-
-        let (new_parents, entries, copies) =
-            parse_dirstate(result.as_slice()).unwrap();
-        let new_state_map: StateMap = entries
-            .into_iter()
-            .map(|(path, entry)| (path.to_owned(), entry))
-            .collect();
-        let new_copy_map: CopyMap = copies
-            .into_iter()
-            .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
-            .collect();
-
-        assert_eq!(
-            (&parents, state_map, copymap),
-            (new_parents, new_state_map, new_copy_map)
-        )
-    }
-
-    #[test]
-    fn test_parse_pack_multiple_entries_with_copy() {
-        let mut state_map: StateMap = [
-            (
-                HgPathBuf::from_bytes(b"f1"),
-                DirstateEntry::from_v1_data(
-                    EntryState::Normal,
-                    0o644,
-                    0,
-                    791231220,
-                ),
-            ),
-            (
-                HgPathBuf::from_bytes(b"f2"),
-                DirstateEntry::from_v1_data(
-                    EntryState::Merged,
-                    0o777,
-                    1000,
-                    791231220,
-                ),
-            ),
-            (
-                HgPathBuf::from_bytes(b"f3"),
-                DirstateEntry::from_v1_data(
-                    EntryState::Removed,
-                    0o644,
-                    234553,
-                    791231220,
-                ),
-            ),
-            (
-                HgPathBuf::from_bytes(b"f4\xF6"),
-                DirstateEntry::from_v1_data(EntryState::Added, 0o644, -1, -1),
-            ),
-        ]
-        .iter()
-        .cloned()
-        .collect();
-        let mut copymap = FastHashMap::default();
-        copymap.insert(
-            HgPathBuf::from_bytes(b"f1"),
-            HgPathBuf::from_bytes(b"copyname"),
-        );
-        copymap.insert(
-            HgPathBuf::from_bytes(b"f4\xF6"),
-            HgPathBuf::from_bytes(b"copyname2"),
-        );
-        let parents = DirstateParents {
-            p1: b"12345678910111213141".into(),
-            p2: b"00000000000000000000".into(),
-        };
-        let now = Timestamp(15000000);
-        let result =
-            pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
-                .unwrap();
-
-        let (new_parents, entries, copies) =
-            parse_dirstate(result.as_slice()).unwrap();
-        let new_state_map: StateMap = entries
-            .into_iter()
-            .map(|(path, entry)| (path.to_owned(), entry))
-            .collect();
-        let new_copy_map: CopyMap = copies
-            .into_iter()
-            .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
-            .collect();
-
-        assert_eq!(
-            (&parents, state_map, copymap),
-            (new_parents, new_state_map, new_copy_map)
-        )
-    }
-
-    #[test]
-    /// https://www.mercurial-scm.org/repo/hg/rev/af3f26b6bba4
-    fn test_parse_pack_one_entry_with_copy_and_time_conflict() {
-        let mut state_map: StateMap = [(
-            HgPathBuf::from_bytes(b"f1"),
-            DirstateEntry::from_v1_data(
-                EntryState::Normal,
-                0o644,
-                0,
-                15000000,
-            ),
-        )]
-        .iter()
-        .cloned()
-        .collect();
-        let mut copymap = FastHashMap::default();
-        copymap.insert(
-            HgPathBuf::from_bytes(b"f1"),
-            HgPathBuf::from_bytes(b"copyname"),
-        );
-        let parents = DirstateParents {
-            p1: b"12345678910111213141".into(),
-            p2: b"00000000000000000000".into(),
-        };
-        let now = Timestamp(15000000);
-        let result =
-            pack_dirstate(&mut state_map, &copymap, parents.clone(), now)
-                .unwrap();
-
-        let (new_parents, entries, copies) =
-            parse_dirstate(result.as_slice()).unwrap();
-        let new_state_map: StateMap = entries
-            .into_iter()
-            .map(|(path, entry)| (path.to_owned(), entry))
-            .collect();
-        let new_copy_map: CopyMap = copies
-            .into_iter()
-            .map(|(path, copy)| (path.to_owned(), copy.to_owned()))
-            .collect();
-
-        assert_eq!(
-            (
-                &parents,
-                [(
-                    HgPathBuf::from_bytes(b"f1"),
-                    DirstateEntry::from_v1_data(
-                        EntryState::Normal,
-                        0o644,
-                        0,
-                        -1
-                    )
-                )]
-                .iter()
-                .cloned()
-                .collect::<StateMap>(),
-                copymap,
-            ),
-            (new_parents, new_state_map, new_copy_map)
-        )
-    }
-}
diff --git a/rust/hg-core/src/dirstate/dirstate_map.rs b/rust/hg-core/src/dirstate/dirstate_map.rs
deleted file mode 100644
--- a/rust/hg-core/src/dirstate/dirstate_map.rs
+++ /dev/null
@@ -1,263 +0,0 @@ 
-// dirstate_map.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::parsers::Timestamp;
-use crate::errors::HgError;
-use crate::{
-    dirstate::EntryState,
-    dirstate::SIZE_FROM_OTHER_PARENT,
-    dirstate::SIZE_NON_NORMAL,
-    pack_dirstate, parse_dirstate,
-    utils::hg_path::{HgPath, HgPathBuf},
-    CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateParents,
-    StateMap,
-};
-use micro_timer::timed;
-use std::iter::FromIterator;
-use std::ops::Deref;
-
-#[derive(Default)]
-pub struct DirstateMap {
-    state_map: StateMap,
-    pub copy_map: CopyMap,
-    pub dirs: Option<DirsMultiset>,
-    pub all_dirs: Option<DirsMultiset>,
-}
-
-/// Should only really be used in python interface code, for clarity
-impl Deref for DirstateMap {
-    type Target = StateMap;
-
-    fn deref(&self) -> &Self::Target {
-        &self.state_map
-    }
-}
-
-impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
-    fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
-        iter: I,
-    ) -> Self {
-        Self {
-            state_map: iter.into_iter().collect(),
-            ..Self::default()
-        }
-    }
-}
-
-impl DirstateMap {
-    pub fn new() -> Self {
-        Self::default()
-    }
-
-    pub fn clear(&mut self) {
-        self.state_map = StateMap::default();
-        self.copy_map.clear();
-    }
-
-    pub fn set_entry(&mut self, filename: &HgPath, entry: DirstateEntry) {
-        self.state_map.insert(filename.to_owned(), entry);
-    }
-
-    /// Add a tracked file to the dirstate
-    pub fn add_file(
-        &mut self,
-        filename: &HgPath,
-        entry: DirstateEntry,
-    ) -> Result<(), DirstateError> {
-        let old_state = self.get(filename).map(|e| e.state());
-        if old_state.is_none() || old_state == Some(EntryState::Removed) {
-            if let Some(ref mut dirs) = self.dirs {
-                dirs.add_path(filename)?;
-            }
-        }
-        if old_state.is_none() {
-            if let Some(ref mut all_dirs) = self.all_dirs {
-                all_dirs.add_path(filename)?;
-            }
-        }
-        self.state_map.insert(filename.to_owned(), entry.to_owned());
-        Ok(())
-    }
-
-    /// Mark a file as removed in the dirstate.
-    ///
-    /// The `size` parameter is used to store sentinel values that indicate
-    /// the file's previous state.  In the future, we should refactor this
-    /// to be more explicit about what that state is.
-    pub fn remove_file(
-        &mut self,
-        filename: &HgPath,
-        in_merge: bool,
-    ) -> Result<(), DirstateError> {
-        let old_entry_opt = self.get(filename);
-        let old_state = old_entry_opt.map(|e| e.state());
-        let mut size = 0;
-        if in_merge {
-            // XXX we should not be able to have 'm' state and 'FROM_P2' if not
-            // during a merge. So I (marmoute) am not sure we need the
-            // conditionnal at all. Adding double checking this with assert
-            // would be nice.
-            if let Some(old_entry) = old_entry_opt {
-                // backup the previous state
-                if old_entry.state() == EntryState::Merged {
-                    size = SIZE_NON_NORMAL;
-                } else if old_entry.state() == EntryState::Normal
-                    && old_entry.size() == SIZE_FROM_OTHER_PARENT
-                {
-                    // other parent
-                    size = SIZE_FROM_OTHER_PARENT;
-                }
-            }
-        }
-        if old_state.is_some() && old_state != Some(EntryState::Removed) {
-            if let Some(ref mut dirs) = self.dirs {
-                dirs.delete_path(filename)?;
-            }
-        }
-        if old_state.is_none() {
-            if let Some(ref mut all_dirs) = self.all_dirs {
-                all_dirs.add_path(filename)?;
-            }
-        }
-        if size == 0 {
-            self.copy_map.remove(filename);
-        }
-
-        self.state_map
-            .insert(filename.to_owned(), DirstateEntry::new_removed(size));
-        Ok(())
-    }
-
-    /// Remove a file from the dirstate.
-    /// Returns `true` if the file was previously recorded.
-    pub fn drop_entry_and_copy_source(
-        &mut self,
-        filename: &HgPath,
-    ) -> Result<(), DirstateError> {
-        let old_state = self.get(filename).map(|e| e.state());
-        let exists = self.state_map.remove(filename).is_some();
-
-        if exists {
-            if old_state != Some(EntryState::Removed) {
-                if let Some(ref mut dirs) = self.dirs {
-                    dirs.delete_path(filename)?;
-                }
-            }
-            if let Some(ref mut all_dirs) = self.all_dirs {
-                all_dirs.delete_path(filename)?;
-            }
-        }
-        self.copy_map.remove(filename);
-
-        Ok(())
-    }
-
-    /// Both of these setters and their uses appear to be the simplest way to
-    /// emulate a Python lazy property, but it is ugly and unidiomatic.
-    /// TODO One day, rewriting this struct using the typestate might be a
-    /// good idea.
-    pub fn set_all_dirs(&mut self) -> Result<(), DirstateError> {
-        if self.all_dirs.is_none() {
-            self.all_dirs = Some(DirsMultiset::from_dirstate(
-                self.state_map.iter().map(|(k, v)| Ok((k, *v))),
-                false,
-            )?);
-        }
-        Ok(())
-    }
-
-    pub fn set_dirs(&mut self) -> Result<(), DirstateError> {
-        if self.dirs.is_none() {
-            self.dirs = Some(DirsMultiset::from_dirstate(
-                self.state_map.iter().map(|(k, v)| Ok((k, *v))),
-                true,
-            )?);
-        }
-        Ok(())
-    }
-
-    pub fn has_tracked_dir(
-        &mut self,
-        directory: &HgPath,
-    ) -> Result<bool, DirstateError> {
-        self.set_dirs()?;
-        Ok(self.dirs.as_ref().unwrap().contains(directory))
-    }
-
-    pub fn has_dir(
-        &mut self,
-        directory: &HgPath,
-    ) -> Result<bool, DirstateError> {
-        self.set_all_dirs()?;
-        Ok(self.all_dirs.as_ref().unwrap().contains(directory))
-    }
-
-    #[timed]
-    pub fn read(
-        &mut self,
-        file_contents: &[u8],
-    ) -> Result<Option<DirstateParents>, DirstateError> {
-        if file_contents.is_empty() {
-            return Ok(None);
-        }
-
-        let (parents, entries, copies) = parse_dirstate(file_contents)?;
-        self.state_map.extend(
-            entries
-                .into_iter()
-                .map(|(path, entry)| (path.to_owned(), entry)),
-        );
-        self.copy_map.extend(
-            copies
-                .into_iter()
-                .map(|(path, copy)| (path.to_owned(), copy.to_owned())),
-        );
-        Ok(Some(parents.clone()))
-    }
-
-    pub fn pack(
-        &mut self,
-        parents: DirstateParents,
-        now: Timestamp,
-    ) -> Result<Vec<u8>, HgError> {
-        pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_dirs_multiset() {
-        let mut map = DirstateMap::new();
-        assert!(map.dirs.is_none());
-        assert!(map.all_dirs.is_none());
-
-        assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
-        assert!(map.all_dirs.is_some());
-        assert!(map.dirs.is_none());
-
-        assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
-        assert!(map.dirs.is_some());
-    }
-
-    #[test]
-    fn test_add_file() {
-        let mut map = DirstateMap::new();
-
-        assert_eq!(0, map.len());
-
-        map.add_file(
-            HgPath::new(b"meh"),
-            DirstateEntry::from_v1_data(EntryState::Normal, 1337, 1337, 1337),
-        )
-        .unwrap();
-
-        assert_eq!(1, map.len());
-    }
-}
diff --git a/rust/hg-core/src/dirstate/dirs_multiset.rs b/rust/hg-core/src/dirstate/dirs_multiset.rs
--- a/rust/hg-core/src/dirstate/dirs_multiset.rs
+++ b/rust/hg-core/src/dirstate/dirs_multiset.rs
@@ -216,7 +216,6 @@ 
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::StateMap;
 
     #[test]
     fn test_delete_path_path_not_found() {
@@ -341,8 +340,8 @@ 
         };
         assert_eq!(expected, new);
 
-        let new = DirsMultiset::from_dirstate(
-            StateMap::default().into_iter().map(Ok),
+        let new = DirsMultiset::from_dirstate::<_, HgPathBuf>(
+            std::iter::empty(),
             false,
         )
         .unwrap();
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
@@ -8,12 +8,10 @@ 
 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
 use crate::revlog::node::NULL_NODE;
 use crate::revlog::Node;
-use crate::utils::hg_path::{HgPath, HgPathBuf};
-use crate::FastHashMap;
+use crate::utils::hg_path::HgPath;
 use bytes_cast::BytesCast;
 
 pub mod dirs_multiset;
-pub mod dirstate_map;
 pub mod entry;
 pub mod parsers;
 pub mod status;
@@ -34,7 +32,6 @@ 
     };
 }
 
-pub type StateMap = FastHashMap<HgPathBuf, DirstateEntry>;
 pub type StateMapIter<'a> = Box<
     dyn Iterator<
             Item = Result<(&'a HgPath, DirstateEntry), DirstateV2ParseError>,
@@ -42,7 +39,6 @@ 
         + 'a,
 >;
 
-pub type CopyMap = FastHashMap<HgPathBuf, HgPathBuf>;
 pub type CopyMapIter<'a> = Box<
     dyn Iterator<Item = Result<(&'a HgPath, &'a HgPath), DirstateV2ParseError>>
         + Send
diff --git a/mercurial/upgrade_utils/engine.py b/mercurial/upgrade_utils/engine.py
--- a/mercurial/upgrade_utils/engine.py
+++ b/mercurial/upgrade_utils/engine.py
@@ -638,7 +638,6 @@ 
         )
 
     assert srcrepo.dirstate._use_dirstate_v2 == (old == b'v2')
-    srcrepo.dirstate._map._use_dirstate_tree = True
     srcrepo.dirstate._map.preload()
     srcrepo.dirstate._use_dirstate_v2 = new == b'v2'
     srcrepo.dirstate._map._use_dirstate_v2 = srcrepo.dirstate._use_dirstate_v2
diff --git a/mercurial/dirstatemap.py b/mercurial/dirstatemap.py
--- a/mercurial/dirstatemap.py
+++ b/mercurial/dirstatemap.py
@@ -493,12 +493,6 @@ 
             # for consistent view between _pl() and _read() invocations
             self._pendingmode = None
 
-            self._use_dirstate_tree = self._ui.configbool(
-                b"experimental",
-                b"dirstate-tree.in-memory",
-                False,
-            )
-
         def addfile(
             self,
             f,
@@ -818,7 +812,7 @@ 
                 parents = self.docket.parents
             else:
                 self._rustmap, parents = rustmod.DirstateMap.new_v1(
-                    self._use_dirstate_tree, self._readdirstatefile()
+                    self._readdirstatefile()
                 )
 
             if parents and not self._dirtyparents:
diff --git a/mercurial/configitems.py b/mercurial/configitems.py
--- a/mercurial/configitems.py
+++ b/mercurial/configitems.py
@@ -964,11 +964,6 @@ 
 )
 coreconfigitem(
     b'experimental',
-    b'dirstate-tree.in-memory',
-    default=False,
-)
-coreconfigitem(
-    b'experimental',
     b'editortmpinhg',
     default=False,
 )