Patchwork D11842: dirstate-item: add a "second_ambiguous` flag in the mtime tuple

login
register
mail settings
Submitter phabricator
Date Dec. 2, 2021, 7:05 p.m.
Message ID <differential-rev-PHID-DREV-rwav4b7jinsn4ctjadk7-req@mercurial-scm.org>
Download mbox | patch
Permalink /patch/50172/
State Superseded
Headers show

Comments

phabricator - Dec. 2, 2021, 7:05 p.m.
marmoute created this revision.
Herald added a reviewer: hg-reviewers.
Herald added a subscriber: mercurial-patches.

REVISION SUMMARY
  This will be used to support the `mtime-second-ambiguous` flag from dirstate
  v2. See format documentation for details.
  
  For now, we only make it possible to store the information, no other logic have
  been added.

REPOSITORY
  rHG Mercurial

BRANCH
  default

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

AFFECTED FILES
  mercurial/cext/parsers.c
  mercurial/dirstateutils/timestamp.py
  mercurial/pure/parsers.py
  rust/hg-core/src/dirstate/entry.rs
  rust/hg-core/src/dirstate_tree/on_disk.rs
  rust/hg-cpython/src/dirstate/item.rs
  tests/fakedirstatewritetime.py

CHANGE DETAILS




To: marmoute, #hg-reviewers
Cc: mercurial-patches, mercurial-devel

Patch

diff --git a/tests/fakedirstatewritetime.py b/tests/fakedirstatewritetime.py
--- a/tests/fakedirstatewritetime.py
+++ b/tests/fakedirstatewritetime.py
@@ -55,7 +55,7 @@ 
     # parsing 'fakenow' in YYYYmmddHHMM format makes comparison between
     # 'fakenow' value and 'touch -t YYYYmmddHHMM' argument easy
     fakenow = dateutil.parsedate(fakenow, [b'%Y%m%d%H%M'])[0]
-    fakenow = timestamp.timestamp((fakenow, 0))
+    fakenow = timestamp.timestamp((fakenow, 0, False))
 
     if has_rust_dirstate:
         # The Rust implementation does not use public parse/pack dirstate
diff --git a/rust/hg-cpython/src/dirstate/item.rs b/rust/hg-cpython/src/dirstate/item.rs
--- a/rust/hg-cpython/src/dirstate/item.rs
+++ b/rust/hg-cpython/src/dirstate/item.rs
@@ -23,7 +23,7 @@ 
         p2_info: bool = false,
         has_meaningful_data: bool = true,
         has_meaningful_mtime: bool = true,
-        parentfiledata: Option<(u32, u32, Option<(u32, u32)>)> = None,
+        parentfiledata: Option<(u32, u32, Option<(u32, u32, bool)>)> = None,
         fallback_exec: Option<bool> = None,
         fallback_symlink: Option<bool> = None,
 
@@ -194,7 +194,8 @@ 
         Ok(mtime)
     }
 
-    def mtime_likely_equal_to(&self, other: (u32, u32)) -> PyResult<bool> {
+    def mtime_likely_equal_to(&self, other: (u32, u32, bool))
+        -> PyResult<bool> {
         if let Some(mtime) = self.entry(py).get().truncated_mtime() {
             Ok(mtime.likely_equal(timestamp(py, other)?))
         } else {
@@ -227,7 +228,7 @@ 
         &self,
         mode: u32,
         size: u32,
-        mtime: (u32, u32),
+        mtime: (u32, u32, bool),
     ) -> PyResult<PyNone> {
         let mtime = timestamp(py, mtime)?;
         self.update(py, |entry| entry.set_clean(mode, size, mtime));
@@ -272,12 +273,13 @@ 
 
 pub(crate) fn timestamp(
     py: Python<'_>,
-    (s, ns): (u32, u32),
+    (s, ns, second_ambiguous): (u32, u32, bool),
 ) -> PyResult<TruncatedTimestamp> {
-    TruncatedTimestamp::from_already_truncated(s, ns).map_err(|_| {
-        PyErr::new::<exc::ValueError, _>(
-            py,
-            "expected mtime truncated to 31 bits",
-        )
-    })
+    TruncatedTimestamp::from_already_truncated(s, ns, second_ambiguous)
+        .map_err(|_| {
+            PyErr::new::<exc::ValueError, _>(
+                py,
+                "expected mtime truncated to 31 bits",
+            )
+        })
 }
diff --git a/rust/hg-core/src/dirstate_tree/on_disk.rs b/rust/hg-core/src/dirstate_tree/on_disk.rs
--- a/rust/hg-core/src/dirstate_tree/on_disk.rs
+++ b/rust/hg-core/src/dirstate_tree/on_disk.rs
@@ -773,6 +773,7 @@ 
         Self::from_already_truncated(
             timestamp.truncated_seconds.get(),
             timestamp.nanoseconds.get(),
+            false,
         )
     }
 }
diff --git a/rust/hg-core/src/dirstate/entry.rs b/rust/hg-core/src/dirstate/entry.rs
--- a/rust/hg-core/src/dirstate/entry.rs
+++ b/rust/hg-core/src/dirstate/entry.rs
@@ -43,6 +43,7 @@ 
     truncated_seconds: u32,
     /// Always in the `0 .. 1_000_000_000` range.
     nanoseconds: u32,
+    second_ambiguous: bool,
 }
 
 impl TruncatedTimestamp {
@@ -50,11 +51,16 @@ 
     /// and truncate the seconds components to its lower 31 bits.
     ///
     /// Panics if the nanoseconds components is not in the expected range.
-    pub fn new_truncate(seconds: i64, nanoseconds: u32) -> Self {
+    pub fn new_truncate(
+        seconds: i64,
+        nanoseconds: u32,
+        second_ambiguous: bool,
+    ) -> Self {
         assert!(nanoseconds < NSEC_PER_SEC);
         Self {
             truncated_seconds: seconds as u32 & RANGE_MASK_31BIT,
             nanoseconds,
+            second_ambiguous,
         }
     }
 
@@ -63,6 +69,7 @@ 
     pub fn from_already_truncated(
         truncated_seconds: u32,
         nanoseconds: u32,
+        second_ambiguous: bool,
     ) -> Result<Self, DirstateV2ParseError> {
         if truncated_seconds & !RANGE_MASK_31BIT == 0
             && nanoseconds < NSEC_PER_SEC
@@ -70,6 +77,7 @@ 
             Ok(Self {
                 truncated_seconds,
                 nanoseconds,
+                second_ambiguous,
             })
         } else {
             Err(DirstateV2ParseError)
@@ -83,7 +91,7 @@ 
             let seconds = metadata.mtime();
             // i64 -> u32 with value always in the `0 .. NSEC_PER_SEC` range
             let nanoseconds = metadata.mtime_nsec().try_into().unwrap();
-            Ok(Self::new_truncate(seconds, nanoseconds))
+            Ok(Self::new_truncate(seconds, nanoseconds, false))
         }
         #[cfg(not(unix))]
         {
@@ -168,7 +176,7 @@ 
                 }
             }
         };
-        Self::new_truncate(seconds, nanoseconds)
+        Self::new_truncate(seconds, nanoseconds, false)
     }
 }
 
@@ -258,9 +266,10 @@ 
                     let mode = u32::try_from(mode).unwrap();
                     let size = u32::try_from(size).unwrap();
                     let mtime = u32::try_from(mtime).unwrap();
-                    let mtime =
-                        TruncatedTimestamp::from_already_truncated(mtime, 0)
-                            .unwrap();
+                    let mtime = TruncatedTimestamp::from_already_truncated(
+                        mtime, 0, false,
+                    )
+                    .unwrap();
                     Self {
                         flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED,
                         mode_size: Some((mode, size)),
diff --git a/mercurial/pure/parsers.py b/mercurial/pure/parsers.py
--- a/mercurial/pure/parsers.py
+++ b/mercurial/pure/parsers.py
@@ -104,6 +104,7 @@ 
     _mtime_ns = attr.ib()
     _fallback_exec = attr.ib()
     _fallback_symlink = attr.ib()
+    _mtime_second_ambiguous = attr.ib()
 
     def __init__(
         self,
@@ -127,6 +128,7 @@ 
         self._size = None
         self._mtime_s = None
         self._mtime_ns = None
+        self._mtime_second_ambiguous = False
         if parentfiledata is None:
             has_meaningful_mtime = False
             has_meaningful_data = False
@@ -136,7 +138,11 @@ 
             self._mode = parentfiledata[0]
             self._size = parentfiledata[1]
         if has_meaningful_mtime:
-            self._mtime_s, self._mtime_ns = parentfiledata[2]
+            (
+                self._mtime_s,
+                self._mtime_ns,
+                self._mtime_second_ambiguous,
+            ) = parentfiledata[2]
 
     @classmethod
     def from_v2_data(cls, flags, size, mtime_s, mtime_ns):
@@ -179,7 +185,7 @@ 
             p2_info=bool(flags & DIRSTATE_V2_P2_INFO),
             has_meaningful_data=has_mode_size,
             has_meaningful_mtime=has_meaningful_mtime,
-            parentfiledata=(mode, size, (mtime_s, mtime_ns)),
+            parentfiledata=(mode, size, (mtime_s, mtime_ns, False)),
             fallback_exec=fallback_exec,
             fallback_symlink=fallback_symlink,
         )
@@ -216,13 +222,13 @@ 
                     wc_tracked=True,
                     p1_tracked=True,
                     has_meaningful_mtime=False,
-                    parentfiledata=(mode, size, (42, 0)),
+                    parentfiledata=(mode, size, (42, 0, False)),
                 )
             else:
                 return cls(
                     wc_tracked=True,
                     p1_tracked=True,
-                    parentfiledata=(mode, size, (mtime, 0)),
+                    parentfiledata=(mode, size, (mtime, 0, False)),
                 )
         else:
             raise RuntimeError(b'unknown state: %s' % state)
@@ -248,7 +254,7 @@ 
         self._p1_tracked = True
         self._mode = mode
         self._size = size
-        self._mtime_s, self._mtime_ns = mtime
+        self._mtime_s, self._mtime_ns, self._mtime_second_ambiguous = mtime
 
     def set_tracked(self):
         """mark a file as tracked in the working copy
@@ -303,7 +309,7 @@ 
         if self_sec is None:
             return False
         self_ns = self._mtime_ns
-        other_sec, other_ns = other_mtime
+        other_sec, other_ns, second_ambiguous = other_mtime
         return self_sec == other_sec and (
             self_ns == other_ns or self_ns == 0 or other_ns == 0
         )
diff --git a/mercurial/dirstateutils/timestamp.py b/mercurial/dirstateutils/timestamp.py
--- a/mercurial/dirstateutils/timestamp.py
+++ b/mercurial/dirstateutils/timestamp.py
@@ -31,8 +31,8 @@ 
     """
 
     def __new__(cls, value):
-        truncated_seconds, subsec_nanos = value
-        value = (truncated_seconds & rangemask, subsec_nanos)
+        truncated_seconds, subsec_nanos, second_ambiguous = value
+        value = (truncated_seconds & rangemask, subsec_nanos, second_ambiguous)
         return super(timestamp, cls).__new__(cls, value)
 
     def __eq__(self, other):
@@ -89,7 +89,7 @@ 
         secs = nanos // billion
         subsec_nanos = nanos % billion
 
-    return timestamp((secs, subsec_nanos))
+    return timestamp((secs, subsec_nanos, False))
 
 
 def reliable_mtime_of(stat_result, present_mtime):
diff --git a/mercurial/cext/parsers.c b/mercurial/cext/parsers.c
--- a/mercurial/cext/parsers.c
+++ b/mercurial/cext/parsers.c
@@ -61,6 +61,7 @@ 
 	int p2_info;
 	int has_meaningful_data;
 	int has_meaningful_mtime;
+	int mtime_second_ambiguous;
 	int mode;
 	int size;
 	int mtime_s;
@@ -79,6 +80,7 @@ 
 	p2_info = 0;
 	has_meaningful_mtime = 1;
 	has_meaningful_data = 1;
+	mtime_second_ambiguous = 0;
 	parentfiledata = Py_None;
 	fallback_exec = Py_None;
 	fallback_symlink = Py_None;
@@ -124,8 +126,8 @@ 
 			return NULL;
 		}
 		if (mtime != Py_None) {
-			if (!PyArg_ParseTuple(mtime, "ii", &mtime_s,
-			                      &mtime_ns)) {
+			if (!PyArg_ParseTuple(mtime, "iii", &mtime_s, &mtime_ns,
+			                      &mtime_second_ambiguous)) {
 				return NULL;
 			}
 		} else {
@@ -139,6 +141,9 @@ 
 		t->flags |= dirstate_flag_has_meaningful_data;
 		t->mode = mode;
 		t->size = size;
+		if (mtime_second_ambiguous) {
+			t->flags |= dirstate_flag_mtime_second_ambiguous;
+		}
 	} else {
 		t->mode = 0;
 		t->size = 0;
@@ -325,7 +330,9 @@ 
 {
 	int other_s;
 	int other_ns;
-	if (!PyArg_ParseTuple(other, "ii", &other_s, &other_ns)) {
+	int other_second_ambiguous;
+	if (!PyArg_ParseTuple(other, "iii", &other_s, &other_ns,
+	                      &other_second_ambiguous)) {
 		return NULL;
 	}
 	if ((self->flags & dirstate_flag_has_mtime) &&
@@ -468,15 +475,17 @@ 
 static PyObject *dirstate_item_set_clean(dirstateItemObject *self,
                                          PyObject *args)
 {
-	int size, mode, mtime_s, mtime_ns;
+	int size, mode, mtime_s, mtime_ns, mtime_second_ambiguous;
 	PyObject *mtime;
 	mtime_s = 0;
 	mtime_ns = 0;
+	mtime_second_ambiguous = 0;
 	if (!PyArg_ParseTuple(args, "iiO", &mode, &size, &mtime)) {
 		return NULL;
 	}
 	if (mtime != Py_None) {
-		if (!PyArg_ParseTuple(mtime, "ii", &mtime_s, &mtime_ns)) {
+		if (!PyArg_ParseTuple(mtime, "iii", &mtime_s, &mtime_ns,
+		                      &mtime_second_ambiguous)) {
 			return NULL;
 		}
 	} else {
@@ -485,6 +494,9 @@ 
 	self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked |
 	              dirstate_flag_has_meaningful_data |
 	              dirstate_flag_has_mtime;
+	if (mtime_second_ambiguous) {
+		self->flags |= dirstate_flag_mtime_second_ambiguous;
+	}
 	self->mode = mode;
 	self->size = size;
 	self->mtime_s = mtime_s;