dirstate-item: add a "second_ambiguous` flag in the mtime tuple
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.
Differential Revision: https://phab.mercurial-scm.org/D11842
--- a/mercurial/cext/parsers.c Tue Nov 23 19:27:17 2021 +0100
+++ b/mercurial/cext/parsers.c Wed Nov 24 04:40:00 2021 +0100
@@ -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;
--- a/mercurial/dirstateutils/timestamp.py Tue Nov 23 19:27:17 2021 +0100
+++ b/mercurial/dirstateutils/timestamp.py Wed Nov 24 04:40:00 2021 +0100
@@ -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):
--- a/mercurial/pure/parsers.py Tue Nov 23 19:27:17 2021 +0100
+++ b/mercurial/pure/parsers.py Wed Nov 24 04:40:00 2021 +0100
@@ -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
)
--- a/rust/hg-core/src/dirstate/entry.rs Tue Nov 23 19:27:17 2021 +0100
+++ b/rust/hg-core/src/dirstate/entry.rs Wed Nov 24 04:40:00 2021 +0100
@@ -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)),
--- a/rust/hg-core/src/dirstate_tree/on_disk.rs Tue Nov 23 19:27:17 2021 +0100
+++ b/rust/hg-core/src/dirstate_tree/on_disk.rs Wed Nov 24 04:40:00 2021 +0100
@@ -773,6 +773,7 @@
Self::from_already_truncated(
timestamp.truncated_seconds.get(),
timestamp.nanoseconds.get(),
+ false,
)
}
}
--- a/rust/hg-cpython/src/dirstate/item.rs Tue Nov 23 19:27:17 2021 +0100
+++ b/rust/hg-cpython/src/dirstate/item.rs Wed Nov 24 04:40:00 2021 +0100
@@ -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",
+ )
+ })
}
--- a/tests/fakedirstatewritetime.py Tue Nov 23 19:27:17 2021 +0100
+++ b/tests/fakedirstatewritetime.py Wed Nov 24 04:40:00 2021 +0100
@@ -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