diff --git a/mercurial/cext/parsers.c b/mercurial/cext/parsers.c --- a/mercurial/cext/parsers.c +++ b/mercurial/cext/parsers.c @@ -65,21 +65,100 @@ /* We do all the initialization here and not a tp_init function because * dirstate_item is immutable. */ dirstateItemObject *t; - char state; - int size, mode, mtime; - if (!PyArg_ParseTuple(args, "ciii", &state, &mode, &size, &mtime)) { + int wc_tracked; + int p1_tracked; + int p2_tracked; + int merged; + int clean_p1; + int clean_p2; + int possibly_dirty; + PyObject *parentfiledata; + static char *keywords_name[] = { + "wc_tracked", "p1_tracked", "p2_tracked", + "merged", "clean_p1", "clean_p2", + "possibly_dirty", "parentfiledata", NULL, + }; + wc_tracked = 0; + p1_tracked = 0; + p2_tracked = 0; + merged = 0; + clean_p1 = 0; + clean_p2 = 0; + possibly_dirty = 0; + parentfiledata = Py_None; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiiiiiiO", keywords_name, + &wc_tracked, &p1_tracked, &p2_tracked, + &merged, &clean_p1, &clean_p2, + &possibly_dirty, &parentfiledata + + )) { return NULL; } - + if (merged && (clean_p1 || clean_p2)) { + PyErr_SetString(PyExc_RuntimeError, + "`merged` argument incompatible with " + "`clean_p1`/`clean_p2`"); + return NULL; + } t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1); if (!t) { return NULL; } - t->state = state; - t->mode = mode; - t->size = size; - t->mtime = mtime; - + t->state = 'r'; + t->mode = 0; + t->size = dirstate_v1_nonnormal; + t->mtime = ambiguous_time; + if (!(p1_tracked || p2_tracked || wc_tracked)) { + /* Nothing special to do, file is untracked */ + } else if (merged) { + t->state = 'm'; + t->size = dirstate_v1_from_p2; + t->mtime = ambiguous_time; + } else if (!(p1_tracked || p2_tracked) && wc_tracked) { + t->state = 'a'; + t->size = dirstate_v1_nonnormal; + t->mtime = ambiguous_time; + } else if ((p1_tracked || p2_tracked) && !wc_tracked) { + t->state = 'r'; + t->size = 0; + t->mtime = 0; + } else if (clean_p2 && wc_tracked) { + t->state = 'n'; + t->size = dirstate_v1_from_p2; + t->mtime = ambiguous_time; + } else if (!p1_tracked && p2_tracked && wc_tracked) { + t->state = 'n'; + t->size = dirstate_v1_from_p2; + t->mtime = ambiguous_time; + } else if (possibly_dirty) { + t->state = 'n'; + t->size = dirstate_v1_nonnormal; + t->mtime = ambiguous_time; + } else if (wc_tracked) { + /* this is a "normal" file */ + if (parentfiledata == Py_None) { + PyErr_SetString( + PyExc_RuntimeError, + "failed to pass parentfiledata for a normal file"); + return NULL; + } + if (!PyTuple_CheckExact(parentfiledata)) { + PyErr_SetString( + PyExc_TypeError, + "parentfiledata should be a Tuple or None"); + return NULL; + } + t->state = 'n'; + t->mode = + (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 0)); + t->size = + (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 1)); + t->mtime = + (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 2)); + } else { + PyErr_SetString(PyExc_RuntimeError, "unreachable"); + return NULL; + } return (PyObject *)t; } diff --git a/mercurial/pure/parsers.py b/mercurial/pure/parsers.py --- a/mercurial/pure/parsers.py +++ b/mercurial/pure/parsers.py @@ -61,11 +61,62 @@ _size = attr.ib() _mtime = attr.ib() - def __init__(self, state, mode, size, mtime): - self._state = state - self._mode = mode - self._size = size - self._mtime = mtime + def __init__( + self, + wc_tracked=False, + p1_tracked=False, + p2_tracked=False, + merged=False, + clean_p1=False, + clean_p2=False, + possibly_dirty=False, + parentfiledata=None, + ): + if merged and (clean_p1 or clean_p2): + msg = b'`merged` argument incompatible with `clean_p1`/`clean_p2`' + raise error.ProgrammingError(msg) + + self._state = None + self._mode = 0 + self._size = NONNORMAL + self._mtime = AMBIGUOUS_TIME + if not (p1_tracked or p2_tracked or wc_tracked): + pass # the object has no state to record + elif merged: + self._state = b'm' + self._size = FROM_P2 + self._mtime = AMBIGUOUS_TIME + elif not (p1_tracked or p2_tracked) and wc_tracked: + self._state = b'a' + self._size = NONNORMAL + self._mtime = AMBIGUOUS_TIME + elif (p1_tracked or p2_tracked) and not wc_tracked: + self._state = b'r' + self._size = 0 + self._mtime = 0 + elif clean_p2 and wc_tracked: + self._state = b'n' + self._size = FROM_P2 + self._mtime = AMBIGUOUS_TIME + elif not p1_tracked and p2_tracked and wc_tracked: + self._state = b'n' + self._size = FROM_P2 + self._mtime = AMBIGUOUS_TIME + elif possibly_dirty: + self._state = b'n' + self._size = NONNORMAL + self._mtime = AMBIGUOUS_TIME + elif wc_tracked: + # this is a "normal" file + if parentfiledata is None: + msg = b'failed to pass parentfiledata for a normal file' + raise error.ProgrammingError(msg) + self._state = b'n' + self._mode = parentfiledata[0] + self._size = parentfiledata[1] + self._mtime = parentfiledata[2] + else: + assert False, 'unreachable' @classmethod def from_v1_data(cls, state, mode, size, mtime): @@ -74,12 +125,12 @@ Since the dirstate-v1 format is frozen, the signature of this function is not expected to change, unlike the __init__ one. """ - return cls( - state=state, - mode=mode, - size=size, - mtime=mtime, - ) + instance = cls() + instance._state = state + instance._mode = mode + instance._size = size + instance._mtime = mtime + return instance def set_possibly_dirty(self): """Mark a file as "possibly dirty"