Read about our upcoming Code of Conduct on this issue

Commit dee5e60b authored by Pierre-Yves David's avatar Pierre-Yves David 🐙
Browse files

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
parent 8d585aa9becf
......@@ -61,6 +61,7 @@ static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
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 @@ static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
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 @@ static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
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 @@ static PyObject *dirstate_item_new(PyTypeObject *subtype, PyObject *args,
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 @@ static PyObject *dirstate_item_mtime_likely_equal_to(dirstateItemObject *self,
{
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_possibly_dirty(dirstateItemObject *self)
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 @@ static PyObject *dirstate_item_set_clean(dirstateItemObject *self,
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;
......
......@@ -31,8 +31,8 @@ class timestamp(tuple):
"""
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 @@ def mtime_of(stat_result):
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):
......
......@@ -104,6 +104,7 @@ class DirstateItem(object):
_mtime_ns = attr.ib()
_fallback_exec = attr.ib()
_fallback_symlink = attr.ib()
_mtime_second_ambiguous = attr.ib()
def __init__(
self,
......@@ -127,6 +128,7 @@ class DirstateItem(object):
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 @@ class DirstateItem(object):
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 @@ class DirstateItem(object):
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 @@ class DirstateItem(object):
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 @@ class DirstateItem(object):
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 @@ class DirstateItem(object):
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
)
......
......@@ -43,6 +43,7 @@ pub struct TruncatedTimestamp {
truncated_seconds: u32,
/// Always in the `0 .. 1_000_000_000` range.
nanoseconds: u32,
second_ambiguous: bool,
}
impl TruncatedTimestamp {
......@@ -50,11 +51,16 @@ impl TruncatedTimestamp {
/// 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 @@ impl TruncatedTimestamp {
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 @@ impl TruncatedTimestamp {
Ok(Self {
truncated_seconds,
nanoseconds,
second_ambiguous,
})
} else {
Err(DirstateV2ParseError)
......@@ -83,7 +91,7 @@ impl TruncatedTimestamp {
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 @@ impl From<SystemTime> for TruncatedTimestamp {
}
}
};
Self::new_truncate(seconds, nanoseconds)
Self::new_truncate(seconds, nanoseconds, false)
}
}
......@@ -258,9 +266,10 @@ impl DirstateEntry {
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)),
......
......@@ -773,6 +773,7 @@ impl TryFrom<PackedTruncatedTimestamp> for TruncatedTimestamp {
Self::from_already_truncated(
timestamp.truncated_seconds.get(),
timestamp.nanoseconds.get(),
false,
)
}
}
......
......@@ -23,7 +23,7 @@ py_class!(pub class DirstateItem |py| {
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 @@ py_class!(pub class DirstateItem |py| {
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 @@ py_class!(pub class DirstateItem |py| {
&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 @@ impl DirstateItem {
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",
)
})
}
......@@ -55,7 +55,7 @@ def fakewrite(ui, func):
# 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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment