dirstate-v2: Add --dirs to debugdirstate command
authorSimon Sapin <simon.sapin@octobus.net>
Mon, 31 May 2021 19:54:41 +0200
changeset 47351 3b9914b28133
parent 47350 04d1f17f49e7
child 47352 9d58e54b5966
dirstate-v2: Add --dirs to debugdirstate command `hg debugdirstate --dirs` also shows information stored in the dirstate (for `read_dir` caching) about directories. Differential Revision: https://phab.mercurial-scm.org/D10828
mercurial/debugcommands.py
mercurial/dirstate.py
rust/hg-core/src/dirstate/parsers.rs
rust/hg-core/src/dirstate_tree/dirstate_map.rs
rust/hg-core/src/dirstate_tree/dispatch.rs
rust/hg-core/src/dirstate_tree/on_disk.rs
rust/hg-cpython/src/dirstate/dirstate_map.rs
rust/hg-cpython/src/dirstate/dispatch.rs
rust/hg-cpython/src/parsers.rs
tests/test-completion.t
tests/test-status.t
--- a/mercurial/debugcommands.py	Mon May 31 18:35:44 2021 +0200
+++ b/mercurial/debugcommands.py	Mon May 31 19:54:41 2021 +0200
@@ -941,6 +941,7 @@
         ),
         (b'', b'dates', True, _(b'display the saved mtime')),
         (b'', b'datesort', None, _(b'sort by saved mtime')),
+        (b'', b'dirs', False, _(b'display directories')),
     ],
     _(b'[OPTION]...'),
 )
@@ -956,7 +957,11 @@
         keyfunc = lambda x: (x[1][3], x[0])  # sort by mtime, then by filename
     else:
         keyfunc = None  # sort by filename
-    for file_, ent in sorted(pycompat.iteritems(repo.dirstate), key=keyfunc):
+    entries = list(pycompat.iteritems(repo.dirstate))
+    if opts['dirs']:
+        entries.extend(repo.dirstate.directories())
+    entries.sort(key=keyfunc)
+    for file_, ent in entries:
         if ent[3] == -1:
             timestr = b'unset               '
         elif nodates:
--- a/mercurial/dirstate.py	Mon May 31 18:35:44 2021 +0200
+++ b/mercurial/dirstate.py	Mon May 31 19:54:41 2021 +0200
@@ -315,6 +315,9 @@
 
     iteritems = items
 
+    def directories(self):
+        return self._map.directories()
+
     def parents(self):
         return [self._validate(p) for p in self._pl]
 
@@ -1479,6 +1482,10 @@
         self._map
         return self.copymap
 
+    def directories(self):
+        # Rust / dirstate-v2 only
+        return []
+
     def clear(self):
         self._map.clear()
         self.copymap.clear()
@@ -1806,6 +1813,9 @@
         def copymap(self):
             return self._rustmap.copymap()
 
+        def directories(self):
+            return self._rustmap.directories()
+
         def preload(self):
             self._rustmap
 
--- a/rust/hg-core/src/dirstate/parsers.rs	Mon May 31 18:35:44 2021 +0200
+++ b/rust/hg-core/src/dirstate/parsers.rs	Mon May 31 19:54:41 2021 +0200
@@ -129,7 +129,7 @@
 }
 
 /// Seconds since the Unix epoch
-pub struct Timestamp(pub u64);
+pub struct Timestamp(pub i64);
 
 impl DirstateEntry {
     pub fn mtime_is_ambiguous(&self, now: i32) -> bool {
--- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs	Mon May 31 18:35:44 2021 +0200
+++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs	Mon May 31 19:54:41 2021 +0200
@@ -319,7 +319,7 @@
 
     pub(super) fn cached_directory_mtime(
         &self,
-    ) -> Option<&on_disk::Timestamp> {
+    ) -> Option<&'tree on_disk::Timestamp> {
         match self {
             NodeRef::InMemory(_path, node) => match &node.data {
                 NodeData::CachedDirectory { mtime } => Some(mtime),
@@ -1068,4 +1068,28 @@
             })
         }))
     }
+
+    fn iter_directories(
+        &self,
+    ) -> Box<
+        dyn Iterator<
+                Item = Result<
+                    (&HgPath, Option<Timestamp>),
+                    DirstateV2ParseError,
+                >,
+            > + Send
+            + '_,
+    > {
+        Box::new(filter_map_results(self.iter_nodes(), move |node| {
+            Ok(if node.state()?.is_none() {
+                Some((
+                    node.full_path(self.on_disk)?,
+                    node.cached_directory_mtime()
+                        .map(|mtime| Timestamp(mtime.seconds())),
+                ))
+            } else {
+                None
+            })
+        }))
+    }
 }
--- a/rust/hg-core/src/dirstate_tree/dispatch.rs	Mon May 31 18:35:44 2021 +0200
+++ b/rust/hg-core/src/dirstate_tree/dispatch.rs	Mon May 31 19:54:41 2021 +0200
@@ -143,6 +143,18 @@
     ) -> Result<Option<DirstateEntry>, DirstateV2ParseError>;
 
     fn iter(&self) -> StateMapIter<'_>;
+
+    fn iter_directories(
+        &self,
+    ) -> Box<
+        dyn Iterator<
+                Item = Result<
+                    (&HgPath, Option<Timestamp>),
+                    DirstateV2ParseError,
+                >,
+            > + Send
+            + '_,
+    >;
 }
 
 impl DirstateMapMethods for DirstateMap {
@@ -350,4 +362,18 @@
     fn iter(&self) -> StateMapIter<'_> {
         Box::new((&**self).iter().map(|(key, value)| Ok((&**key, *value))))
     }
+
+    fn iter_directories(
+        &self,
+    ) -> Box<
+        dyn Iterator<
+                Item = Result<
+                    (&HgPath, Option<Timestamp>),
+                    DirstateV2ParseError,
+                >,
+            > + Send
+            + '_,
+    > {
+        Box::new(std::iter::empty())
+    }
 }
--- a/rust/hg-core/src/dirstate_tree/on_disk.rs	Mon May 31 18:35:44 2021 +0200
+++ b/rust/hg-core/src/dirstate_tree/on_disk.rs	Mon May 31 19:54:41 2021 +0200
@@ -352,6 +352,12 @@
     }
 }
 
+impl Timestamp {
+    pub fn seconds(&self) -> i64 {
+        self.seconds.get()
+    }
+}
+
 impl From<SystemTime> for Timestamp {
     fn from(system_time: SystemTime) -> Self {
         let (secs, nanos) = match system_time.duration_since(UNIX_EPOCH) {
--- a/rust/hg-cpython/src/dirstate/dirstate_map.rs	Mon May 31 18:35:44 2021 +0200
+++ b/rust/hg-cpython/src/dirstate/dirstate_map.rs	Mon May 31 19:54:41 2021 +0200
@@ -535,6 +535,18 @@
         )
     }
 
+    def directories(&self) -> PyResult<PyList> {
+        let dirs = PyList::new(py, &[]);
+        for item in self.inner(py).borrow().iter_directories() {
+            let (path, mtime) = item.map_err(|e| v2_error(py, e))?;
+            let path = PyBytes::new(py, path.as_bytes());
+            let mtime = mtime.map(|t| t.0).unwrap_or(-1);
+            let tuple = (path, (b'd', 0, 0, mtime));
+            dirs.append(py, tuple.to_py_object(py).into_object())
+        }
+        Ok(dirs)
+    }
+
 });
 
 impl DirstateMap {
--- a/rust/hg-cpython/src/dirstate/dispatch.rs	Mon May 31 18:35:44 2021 +0200
+++ b/rust/hg-cpython/src/dirstate/dispatch.rs	Mon May 31 19:54:41 2021 +0200
@@ -206,4 +206,18 @@
     fn iter(&self) -> StateMapIter<'_> {
         self.get().iter()
     }
+
+    fn iter_directories(
+        &self,
+    ) -> Box<
+        dyn Iterator<
+                Item = Result<
+                    (&HgPath, Option<Timestamp>),
+                    DirstateV2ParseError,
+                >,
+            > + Send
+            + '_,
+    > {
+        self.get().iter_directories()
+    }
 }
--- a/rust/hg-cpython/src/parsers.rs	Mon May 31 18:35:44 2021 +0200
+++ b/rust/hg-cpython/src/parsers.rs	Mon May 31 19:54:41 2021 +0200
@@ -98,7 +98,7 @@
             p1: p1.try_into().unwrap(),
             p2: p2.try_into().unwrap(),
         },
-        Timestamp(now.as_object().extract::<u64>(py)?),
+        Timestamp(now.as_object().extract::<i64>(py)?),
     ) {
         Ok(packed) => {
             for (filename, entry) in dirstate_map.iter() {
--- a/tests/test-completion.t	Mon May 31 18:35:44 2021 +0200
+++ b/tests/test-completion.t	Mon May 31 19:54:41 2021 +0200
@@ -282,7 +282,7 @@
   debugdata: changelog, manifest, dir
   debugdate: extended
   debugdeltachain: changelog, manifest, dir, template
-  debugdirstate: nodates, dates, datesort
+  debugdirstate: nodates, dates, datesort, dirs
   debugdiscovery: old, nonheads, rev, seed, local-as-revs, remote-as-revs, ssh, remotecmd, insecure, template
   debugdownload: output
   debugextensions: template
--- a/tests/test-status.t	Mon May 31 18:35:44 2021 +0200
+++ b/tests/test-status.t	Mon May 31 19:54:41 2021 +0200
@@ -915,3 +915,46 @@
   I A.hs
   I B.hs
   I ignored-folder/ctest.hs
+
+#if dirstate-v2
+
+Check read_dir caching
+
+  $ cd ..
+  $ hg init repo8
+  $ cd repo8
+  $ mkdir subdir
+  $ touch subdir/a subdir/b
+  $ hg ci -Aqm '#0'
+
+The cached mtime is initially unset
+
+  $ hg debugdirstate --dirs --no-dates | grep '^d'
+  d   0          0 unset               subdir
+
+It is still not set when there are unknown files
+
+  $ touch subdir/unknown
+  $ hg status
+  ? subdir/unknown
+  $ hg debugdirstate --dirs --no-dates | grep '^d'
+  d   0          0 unset               subdir
+
+Now the directory is eligible for caching, so its mtime is save in the dirstate
+
+  $ rm subdir/unknown
+  $ hg status
+  $ hg debugdirstate --dirs --no-dates | grep '^d'
+  d   0          0 set                 subdir
+
+This time the command should be ever so slightly faster since it does not need `read_dir("subdir")`
+
+  $ hg status
+
+Creating a new file changes the directory’s mtime, invalidating the cache
+
+  $ touch subdir/unknown
+  $ hg status
+  ? subdir/unknown
+
+#endif