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 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; To: marmoute, #hg-reviewers Cc: mercurial-patches, mercurial-devel _______________________________________________ Mercurial-devel mailing list Mercurial-devel@mercurial-scm.org https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel