dirstate-v2: actually use sub-second mtime precision
authorSimon Sapin <simon.sapin@octobus.net>
Wed, 13 Oct 2021 15:58:14 +0200
changeset 48263 83d0bd45b662
parent 48262 68bb472aee9c
child 48264 bb240915f69f
dirstate-v2: actually use sub-second mtime precision Instead of zero, set the nanoseconds field to its correct value whenever possible and preserve it across serialization+parsing. Differential Revision: https://phab.mercurial-scm.org/D11702
mercurial/dirstateutils/timestamp.py
mercurial/dirstateutils/v2.py
rust/hg-core/src/dirstate/entry.rs
rust/hg-core/src/dirstate_tree/on_disk.rs
rust/hg-core/src/dirstate_tree/status.rs
--- a/mercurial/dirstateutils/timestamp.py	Thu Oct 14 13:54:39 2021 +0200
+++ b/mercurial/dirstateutils/timestamp.py	Wed Oct 13 15:58:14 2021 +0200
@@ -66,12 +66,22 @@
     Takes an `os.stat_result`-like object and returns a `timestamp` object
     for its modification time.
     """
-    # https://docs.python.org/2/library/os.html#os.stat_float_times
-    # "For compatibility with older Python versions,
-    #  accessing stat_result as a tuple always returns integers."
-    secs = stat_result[stat.ST_MTIME]
+    try:
+        # TODO: add this attribute to `osutil.stat` objects,
+        # see `mercurial/cext/osutil.c`.
+        #
+        # This attribute is also not available on Python 2.
+        nanos = stat_result.st_mtime_ns
+    except AttributeError:
+        # https://docs.python.org/2/library/os.html#os.stat_float_times
+        # "For compatibility with older Python versions,
+        #  accessing stat_result as a tuple always returns integers."
+        secs = stat_result[stat.ST_MTIME]
 
-    # For now
-    subsec_nanos = 0
+        subsec_nanos = 0
+    else:
+        billion = int(1e9)
+        secs = nanos // billion
+        subsec_nanos = nanos % billion
 
     return timestamp((secs, subsec_nanos))
--- a/mercurial/dirstateutils/v2.py	Thu Oct 14 13:54:39 2021 +0200
+++ b/mercurial/dirstateutils/v2.py	Wed Oct 13 15:58:14 2021 +0200
@@ -101,15 +101,12 @@
             flags,
             size,
             mtime_s,
-            _mtime_ns,
+            mtime_ns,
         ) = NODE.unpack(node_bytes)
 
         # Parse child nodes of this node recursively
         parse_nodes(map, copy_map, data, children_start, children_count)
 
-        # Don’t yet use sub-second precision if it exists in the file,
-        # since other parts of the code still set it to zero.
-        mtime_ns = 0
         item = parsers.DirstateItem.from_v2_data(flags, size, mtime_s, mtime_ns)
         if not item.any_tracked:
             continue
--- a/rust/hg-core/src/dirstate/entry.rs	Thu Oct 14 13:54:39 2021 +0200
+++ b/rust/hg-core/src/dirstate/entry.rs	Wed Oct 13 15:58:14 2021 +0200
@@ -91,11 +91,6 @@
         }
     }
 
-    pub fn to_integer_second(mut self) -> Self {
-        self.nanoseconds = 0;
-        self
-    }
-
     /// The lower 31 bits of the number of seconds since the epoch.
     pub fn truncated_seconds(&self) -> u32 {
         self.truncated_seconds
--- a/rust/hg-core/src/dirstate_tree/on_disk.rs	Thu Oct 14 13:54:39 2021 +0200
+++ b/rust/hg-core/src/dirstate_tree/on_disk.rs	Wed Oct 13 15:58:14 2021 +0200
@@ -376,15 +376,7 @@
             // MTIME_SECOND_AMBIGUOUS requires. So we ignore the mtime
             && !self.flags().contains(Flags::MTIME_SECOND_AMBIGUOUS)
         {
-            // TODO: replace this by `self.mtime.try_into()?` to use
-            // sub-second precision from the file.
-            // We don’t do this yet because other parts of the code
-            // always set it to zero.
-            let mtime = TruncatedTimestamp::from_already_truncated(
-                self.mtime.truncated_seconds.get(),
-                0,
-            )?;
-            Some(mtime)
+            Some(self.mtime.try_into()?)
         } else {
             None
         };
--- a/rust/hg-core/src/dirstate_tree/status.rs	Thu Oct 14 13:54:39 2021 +0200
+++ b/rust/hg-core/src/dirstate_tree/status.rs	Wed Oct 13 15:58:14 2021 +0200
@@ -531,9 +531,7 @@
             let mtime_looks_clean;
             if let Some(dirstate_mtime) = entry.truncated_mtime() {
                 let fs_mtime = TruncatedTimestamp::for_mtime_of(fs_metadata)
-                    .expect("OS/libc does not support mtime?")
-                    // For now don’t use sub-second precision for file mtimes
-                    .to_integer_second();
+                    .expect("OS/libc does not support mtime?");
                 mtime_looks_clean = fs_mtime.likely_equal(dirstate_mtime)
                     && !fs_mtime.likely_equal(self.options.last_normal_time)
             } else {