diff --git a/mercurial/cext/parsers.c b/mercurial/cext/parsers.c --- a/mercurial/cext/parsers.c +++ b/mercurial/cext/parsers.c @@ -52,37 +52,32 @@ dirstateItemObject *t; int wc_tracked; int p1_tracked; - int p2_tracked; - int merged; - int clean_p1; - int clean_p2; - int possibly_dirty; + int p2_info; + int has_meaningful_data; + int has_meaningful_mtime; + int mode; + int size; + int mtime; PyObject *parentfiledata; static char *keywords_name[] = { - "wc_tracked", "p1_tracked", "p2_tracked", - "merged", "clean_p1", "clean_p2", - "possibly_dirty", "parentfiledata", NULL, + "wc_tracked", + "p1_tracked", + "p2_info", + "has_meaningful_data", + "has_meaningful_mtime", + "parentfiledata", + NULL, }; wc_tracked = 0; p1_tracked = 0; - p2_tracked = 0; - merged = 0; - clean_p1 = 0; - clean_p2 = 0; - possibly_dirty = 0; + p2_info = 0; + has_meaningful_mtime = 1; + has_meaningful_data = 1; 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`"); + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "|iiiiiO", keywords_name, &wc_tracked, &p1_tracked, + &p2_info, &has_meaningful_data, &has_meaningful_mtime, + &parentfiledata)) { return NULL; } t = (dirstateItemObject *)subtype->tp_alloc(subtype, 1); @@ -97,24 +92,10 @@ if (p1_tracked) { t->flags |= dirstate_flag_p1_tracked; } - if (p2_tracked) { - t->flags |= dirstate_flag_p2_tracked; - } - if (possibly_dirty) { - t->flags |= dirstate_flag_possibly_dirty; - } - if (merged) { - t->flags |= dirstate_flag_merged; + if (p2_info) { + t->flags |= dirstate_flag_p2_info; } - if (clean_p1) { - t->flags |= dirstate_flag_clean_p1; - } - if (clean_p2) { - t->flags |= dirstate_flag_clean_p2; - } - t->mode = 0; - t->size = dirstate_v1_nonnormal; - t->mtime = ambiguous_time; + if (parentfiledata != Py_None) { if (!PyTuple_CheckExact(parentfiledata)) { PyErr_SetString( @@ -122,12 +103,26 @@ "parentfiledata should be a Tuple or None"); return NULL; } - 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)); + mode = (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 0)); + size = (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 1)); + mtime = (int)PyLong_AsLong(PyTuple_GetItem(parentfiledata, 2)); + } else { + has_meaningful_data = 0; + has_meaningful_mtime = 0; + } + if (has_meaningful_data) { + t->flags |= dirstate_flag_has_meaningful_data; + t->mode = mode; + t->size = size; + } else { + t->mode = 0; + t->size = 0; + } + if (has_meaningful_mtime) { + t->flags |= dirstate_flag_has_meaningful_mtime; + t->mtime = mtime; + } else { + t->mtime = 0; } return (PyObject *)t; } @@ -142,12 +137,20 @@ return (self->flags & dirstate_flag_wc_tracked); } +static inline bool dirstate_item_c_any_tracked(dirstateItemObject *self) +{ + const unsigned char mask = dirstate_flag_wc_tracked | + dirstate_flag_p1_tracked | + dirstate_flag_p2_info; + return (self->flags & mask); +} + static inline bool dirstate_item_c_added(dirstateItemObject *self) { - unsigned char mask = + const unsigned char mask = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked | - dirstate_flag_p2_tracked); - unsigned char target = dirstate_flag_wc_tracked; + dirstate_flag_p2_info); + const unsigned char target = dirstate_flag_wc_tracked; return (self->flags & mask) == target; } @@ -157,21 +160,21 @@ return false; } return (self->flags & - (dirstate_flag_p1_tracked | dirstate_flag_p2_tracked)); + (dirstate_flag_p1_tracked | dirstate_flag_p2_info)); } static inline bool dirstate_item_c_merged(dirstateItemObject *self) { return ((self->flags & dirstate_flag_wc_tracked) && - (self->flags & dirstate_flag_merged)); + (self->flags & dirstate_flag_p1_tracked) && + (self->flags & dirstate_flag_p2_info)); } static inline bool dirstate_item_c_from_p2(dirstateItemObject *self) { - if (!dirstate_item_c_tracked(self)) { - return false; - } - return (self->flags & dirstate_flag_clean_p2); + return ((self->flags & dirstate_flag_wc_tracked) && + !(self->flags & dirstate_flag_p1_tracked) && + (self->flags & dirstate_flag_p2_info)); } static inline char dirstate_item_c_v1_state(dirstateItemObject *self) @@ -189,29 +192,32 @@ static inline int dirstate_item_c_v1_mode(dirstateItemObject *self) { - return self->mode; + if (self->flags & dirstate_flag_has_meaningful_data) { + return self->mode; + } else { + return 0; + } } static inline int dirstate_item_c_v1_size(dirstateItemObject *self) { - if (dirstate_item_c_removed(self) && - (self->flags & dirstate_flag_merged)) { - return dirstate_v1_nonnormal; - } else if (dirstate_item_c_removed(self) && - (self->flags & dirstate_flag_clean_p2)) { - return dirstate_v1_from_p2; + if (!(self->flags & dirstate_flag_wc_tracked) && + (self->flags & dirstate_flag_p2_info)) { + if (self->flags & dirstate_flag_p1_tracked) { + return dirstate_v1_nonnormal; + } else { + return dirstate_v1_from_p2; + } } else if (dirstate_item_c_removed(self)) { return 0; - } else if (dirstate_item_c_merged(self)) { + } else if (self->flags & dirstate_flag_p2_info) { return dirstate_v1_from_p2; } else if (dirstate_item_c_added(self)) { return dirstate_v1_nonnormal; - } else if (dirstate_item_c_from_p2(self)) { - return dirstate_v1_from_p2; - } else if (self->flags & dirstate_flag_possibly_dirty) { - return self->size; /* NON NORMAL ? */ + } else if (self->flags & dirstate_flag_has_meaningful_data) { + return self->size; } else { - return self->size; + return dirstate_v1_nonnormal; } } @@ -219,13 +225,10 @@ { if (dirstate_item_c_removed(self)) { return 0; - } else if (self->flags & dirstate_flag_possibly_dirty) { - return ambiguous_time; - } else if (dirstate_item_c_merged(self)) { - return ambiguous_time; - } else if (dirstate_item_c_added(self)) { - return ambiguous_time; - } else if (dirstate_item_c_from_p2(self)) { + } else if (!(self->flags & dirstate_flag_has_meaningful_mtime) || + !(self->flags & dirstate_flag_p1_tracked) || + !(self->flags & dirstate_flag_wc_tracked) || + (self->flags & dirstate_flag_p2_info)) { return ambiguous_time; } else { return self->mtime; @@ -278,58 +281,43 @@ if (!t) { return NULL; } + t->flags = 0; + t->mode = 0; + t->size = 0; + t->mtime = 0; if (state == 'm') { - t->flags = - (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked | - dirstate_flag_p2_tracked | dirstate_flag_merged); - t->mode = 0; - t->size = dirstate_v1_from_p2; - t->mtime = ambiguous_time; + t->flags = (dirstate_flag_wc_tracked | + dirstate_flag_p1_tracked | dirstate_flag_p2_info); } else if (state == 'a') { t->flags = dirstate_flag_wc_tracked; - t->mode = 0; - t->size = dirstate_v1_nonnormal; - t->mtime = ambiguous_time; } else if (state == 'r') { - t->mode = 0; - t->size = 0; - t->mtime = 0; if (size == dirstate_v1_nonnormal) { t->flags = - (dirstate_flag_p1_tracked | - dirstate_flag_p2_tracked | dirstate_flag_merged); + dirstate_flag_p1_tracked | dirstate_flag_p2_info; } else if (size == dirstate_v1_from_p2) { - t->flags = - (dirstate_flag_p2_tracked | dirstate_flag_clean_p2); + t->flags = dirstate_flag_p2_info; } else { t->flags = dirstate_flag_p1_tracked; } } else if (state == 'n') { if (size == dirstate_v1_from_p2) { t->flags = - (dirstate_flag_wc_tracked | - dirstate_flag_p2_tracked | dirstate_flag_clean_p2); - t->mode = 0; - t->size = dirstate_v1_from_p2; - t->mtime = ambiguous_time; + dirstate_flag_wc_tracked | dirstate_flag_p2_info; } else if (size == dirstate_v1_nonnormal) { - t->flags = (dirstate_flag_wc_tracked | - dirstate_flag_p1_tracked | - dirstate_flag_possibly_dirty); - t->mode = 0; - t->size = dirstate_v1_nonnormal; - t->mtime = ambiguous_time; + t->flags = + dirstate_flag_wc_tracked | dirstate_flag_p1_tracked; } else if (mtime == ambiguous_time) { t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked | - dirstate_flag_possibly_dirty); + dirstate_flag_has_meaningful_data); t->mode = mode; t->size = size; - t->mtime = 0; } else { t->flags = (dirstate_flag_wc_tracked | - dirstate_flag_p1_tracked); + dirstate_flag_p1_tracked | + dirstate_flag_has_meaningful_data | + dirstate_flag_has_meaningful_mtime); t->mode = mode; t->size = size; t->mtime = mtime; @@ -371,8 +359,8 @@ } t->flags = dirstate_flag_wc_tracked; t->mode = 0; - t->size = dirstate_v1_nonnormal; - t->mtime = ambiguous_time; + t->size = 0; + t->mtime = 0; return (PyObject *)t; }; @@ -387,10 +375,10 @@ return NULL; } t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked | - dirstate_flag_p2_tracked | dirstate_flag_merged); + dirstate_flag_p2_info); t->mode = 0; - t->size = dirstate_v1_from_p2; - t->mtime = ambiguous_time; + t->size = 0; + t->mtime = 0; return (PyObject *)t; }; @@ -406,11 +394,10 @@ if (!t) { return NULL; } - t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p2_tracked | - dirstate_flag_clean_p2); + t->flags = dirstate_flag_wc_tracked | dirstate_flag_p2_info; t->mode = 0; - t->size = dirstate_v1_from_p2; - t->mtime = ambiguous_time; + t->size = 0; + t->mtime = 0; return (PyObject *)t; }; @@ -426,11 +413,10 @@ if (!t) { return NULL; } - t->flags = (dirstate_flag_wc_tracked | dirstate_flag_p1_tracked | - dirstate_flag_possibly_dirty); + t->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked; t->mode = 0; - t->size = dirstate_v1_nonnormal; - t->mtime = ambiguous_time; + t->size = 0; + t->mtime = 0; return (PyObject *)t; }; @@ -462,7 +448,7 @@ to make sure it is correct. */ static PyObject *dirstate_item_set_possibly_dirty(dirstateItemObject *self) { - self->flags |= dirstate_flag_possibly_dirty; + self->flags &= ~dirstate_flag_has_meaningful_mtime; Py_RETURN_NONE; } @@ -474,7 +460,9 @@ if (!PyArg_ParseTuple(args, "iii", &mode, &size, &mtime)) { return NULL; } - self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked; + self->flags = dirstate_flag_wc_tracked | dirstate_flag_p1_tracked | + dirstate_flag_has_meaningful_data | + dirstate_flag_has_meaningful_mtime; self->mode = mode; self->size = size; self->mtime = mtime; @@ -484,11 +472,7 @@ static PyObject *dirstate_item_set_tracked(dirstateItemObject *self) { self->flags |= dirstate_flag_wc_tracked; - self->flags |= dirstate_flag_possibly_dirty; - /* size = None on the python size turn into size = NON_NORMAL when - * accessed. So the next line is currently required, but a some future - * clean up would be welcome. */ - self->size = dirstate_v1_nonnormal; + self->flags &= ~dirstate_flag_has_meaningful_mtime; Py_RETURN_NONE; } @@ -503,22 +487,13 @@ static PyObject *dirstate_item_drop_merge_data(dirstateItemObject *self) { - if (dirstate_item_c_merged(self) || dirstate_item_c_from_p2(self)) { - if (dirstate_item_c_merged(self)) { - self->flags |= dirstate_flag_p1_tracked; - } else { - self->flags &= ~dirstate_flag_p1_tracked; - } - self->flags &= - ~(dirstate_flag_merged | dirstate_flag_clean_p1 | - dirstate_flag_clean_p2 | dirstate_flag_p2_tracked); - self->flags |= dirstate_flag_possibly_dirty; + if (self->flags & dirstate_flag_p2_info) { + self->flags &= ~(dirstate_flag_p2_info | + dirstate_flag_has_meaningful_data | + dirstate_flag_has_meaningful_mtime); self->mode = 0; self->mtime = 0; - /* size = None on the python size turn into size = NON_NORMAL - * when accessed. So the next line is currently required, but a - * some future clean up would be welcome. */ - self->size = dirstate_v1_nonnormal; + self->size = 0; } Py_RETURN_NONE; } @@ -624,11 +599,9 @@ { if (!(self->flags & dirstate_flag_wc_tracked)) { Py_RETURN_FALSE; - } else if (dirstate_item_c_added(self)) { + } else if (!(self->flags & dirstate_flag_p1_tracked)) { Py_RETURN_FALSE; - } else if (self->flags & dirstate_flag_merged) { - Py_RETURN_FALSE; - } else if (self->flags & dirstate_flag_clean_p2) { + } else if (self->flags & dirstate_flag_p2_info) { Py_RETURN_FALSE; } else { Py_RETURN_TRUE; @@ -637,10 +610,7 @@ static PyObject *dirstate_item_get_any_tracked(dirstateItemObject *self) { - unsigned char mask = dirstate_flag_wc_tracked | - dirstate_flag_p1_tracked | - dirstate_flag_p2_tracked; - if ((self->flags & mask) != 0) { + if (dirstate_item_c_any_tracked(self)) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; diff --git a/mercurial/cext/util.h b/mercurial/cext/util.h --- a/mercurial/cext/util.h +++ b/mercurial/cext/util.h @@ -33,11 +33,9 @@ static const unsigned char dirstate_flag_wc_tracked = 1; static const unsigned char dirstate_flag_p1_tracked = 1 << 1; -static const unsigned char dirstate_flag_p2_tracked = 1 << 2; -static const unsigned char dirstate_flag_possibly_dirty = 1 << 3; -static const unsigned char dirstate_flag_merged = 1 << 4; -static const unsigned char dirstate_flag_clean_p1 = 1 << 5; -static const unsigned char dirstate_flag_clean_p2 = 1 << 6; +static const unsigned char dirstate_flag_p2_info = 1 << 2; +static const unsigned char dirstate_flag_has_meaningful_data = 1 << 3; +static const unsigned char dirstate_flag_has_meaningful_mtime = 1 << 4; extern PyTypeObject dirstateItemType; #define dirstate_tuple_check(op) (Py_TYPE(op) == &dirstateItemType) diff --git a/mercurial/dirstatemap.py b/mercurial/dirstatemap.py --- a/mercurial/dirstatemap.py +++ b/mercurial/dirstatemap.py @@ -137,14 +137,7 @@ if entry is None: self._dirs_incr(filename) entry = DirstateItem( - p1_tracked=False, - p2_tracked=False, wc_tracked=True, - merged=False, - clean_p1=False, - clean_p2=False, - possibly_dirty=False, - parentfiledata=None, ) self._insert_entry(filename, entry) @@ -208,37 +201,20 @@ self._drop_entry(filename) self._dirs_decr(filename, old_entry=old_entry) return - elif merged: - pass - elif not (p1_tracked or p2_tracked) and wc_tracked: - pass # file is added, nothing special to adjust - elif (p1_tracked or p2_tracked) and not wc_tracked: - pass - elif clean_p2 and wc_tracked: - pass - elif not p1_tracked and p2_tracked and wc_tracked: - clean_p2 = True - elif possibly_dirty: - pass - elif wc_tracked: - # this is a "normal" file - if parentfiledata is None: - msg = b'failed to pass parentfiledata for a normal file: %s' - msg %= filename - raise error.ProgrammingError(msg) - else: - assert False, 'unreachable' + + p2_info = merged or clean_p2 + if merged: + assert p1_tracked + + has_meaningful_mtime = not possibly_dirty old_entry = self._map.get(filename) self._dirs_incr(filename, old_entry) entry = DirstateItem( wc_tracked=wc_tracked, p1_tracked=p1_tracked, - p2_tracked=p2_tracked, - merged=merged, - clean_p1=clean_p1, - clean_p2=clean_p2, - possibly_dirty=possibly_dirty, + p2_info=p2_info, + has_meaningful_mtime=has_meaningful_mtime, parentfiledata=parentfiledata, ) self._insert_entry(filename, entry) diff --git a/mercurial/pure/parsers.py b/mercurial/pure/parsers.py --- a/mercurial/pure/parsers.py +++ b/mercurial/pure/parsers.py @@ -53,36 +53,32 @@ # about file tracking - wc_tracked: is the file tracked by the working copy - p1_tracked: is the file tracked in working copy first parent - - p2_tracked: is the file tracked in working copy second parent - - # about what possible merge action related to this file - - clean_p1: merge picked the file content from p1 - - clean_p2: merge picked the file content from p2 - - merged: file gather changes from both side. + - p2_info: the file has been involved in some merge operation. Either + because it was actually merged, or because the p2 version was + ahead, or because some renamed moved it there. In either case + `hg status` will want it displayed as modified. # about the file state expected from p1 manifest: - mode: the file mode in p1 - size: the file size in p1 + These value can be set to None, which mean we don't have a meaningful value + to compare with. Either because we don't really care about them as there + `status` is known without having to look at the disk or because we don't + know these right now and a full comparison will be needed to find out if + the file is clean. + # about the file state on disk last time we saw it: - mtime: the last known clean mtime for the file. - The last three item (mode, size and mtime) can be None if no meaningful (or - trusted) value exists. - + This value can be set to None if no cachable state exist. Either because we + do not care (see previous section) or because we could not cache something + yet. """ _wc_tracked = attr.ib() _p1_tracked = attr.ib() - _p2_tracked = attr.ib() - # the three item above should probably be combined - # - # However it is unclear if they properly cover some of the most advanced - # merge case. So we should probably wait on this to be settled. - _merged = attr.ib() - _clean_p1 = attr.ib() - _clean_p2 = attr.ib() - _possibly_dirty = attr.ib() + _p2_info = attr.ib() _mode = attr.ib() _size = attr.ib() _mtime = attr.ib() @@ -91,32 +87,25 @@ self, wc_tracked=False, p1_tracked=False, - p2_tracked=False, - merged=False, - clean_p1=False, - clean_p2=False, - possibly_dirty=False, + p2_info=False, + has_meaningful_data=True, + has_meaningful_mtime=True, parentfiledata=None, ): - if merged and (clean_p1 or clean_p2): - msg = b'`merged` argument incompatible with `clean_p1`/`clean_p2`' - raise error.ProgrammingError(msg) - - assert not (merged and not p1_tracked) self._wc_tracked = wc_tracked self._p1_tracked = p1_tracked - self._p2_tracked = p2_tracked - self._merged = merged - self._clean_p1 = clean_p1 - self._clean_p2 = clean_p2 - self._possibly_dirty = possibly_dirty + self._p2_info = p2_info + + self._mode = None + self._size = None + self._mtime = None if parentfiledata is None: - self._mode = None - self._size = None - self._mtime = None - else: + has_meaningful_mtime = False + has_meaningful_data = False + if has_meaningful_data: self._mode = parentfiledata[0] self._size = parentfiledata[1] + if has_meaningful_mtime: self._mtime = parentfiledata[2] @classmethod @@ -125,11 +114,7 @@ Should eventually be removed """ - instance = cls() - instance._wc_tracked = True - instance._p1_tracked = False - instance._p2_tracked = False - return instance + return cls(wc_tracked=True) @classmethod def new_merged(cls): @@ -137,12 +122,7 @@ Should eventually be removed """ - instance = cls() - instance._wc_tracked = True - instance._p1_tracked = True # might not be True because of rename ? - instance._p2_tracked = True # might not be True because of rename ? - instance._merged = True - return instance + return cls(wc_tracked=True, p1_tracked=True, p2_info=True) @classmethod def new_from_p2(cls): @@ -150,12 +130,7 @@ Should eventually be removed """ - instance = cls() - instance._wc_tracked = True - instance._p1_tracked = False # might actually be True - instance._p2_tracked = True - instance._clean_p2 = True - return instance + return cls(wc_tracked=True, p2_info=True) @classmethod def new_possibly_dirty(cls): @@ -163,11 +138,7 @@ Should eventually be removed """ - instance = cls() - instance._wc_tracked = True - instance._p1_tracked = True - instance._possibly_dirty = True - return instance + return cls(wc_tracked=True, p1_tracked=True) @classmethod def new_normal(cls, mode, size, mtime): @@ -177,13 +148,11 @@ """ assert size != FROM_P2 assert size != NONNORMAL - instance = cls() - instance._wc_tracked = True - instance._p1_tracked = True - instance._mode = mode - instance._size = size - instance._mtime = mtime - return instance + return cls( + wc_tracked=True, + p1_tracked=True, + parentfiledata=(mode, size, mtime), + ) @classmethod def from_v1_data(cls, state, mode, size, mtime): @@ -197,25 +166,16 @@ elif state == b'a': return cls.new_added() elif state == b'r': - instance = cls() - instance._wc_tracked = False if size == NONNORMAL: - instance._merged = True - instance._p1_tracked = ( - True # might not be True because of rename ? - ) - instance._p2_tracked = ( - True # might not be True because of rename ? - ) + p1_tracked = True + p2_info = True elif size == FROM_P2: - instance._clean_p2 = True - instance._p1_tracked = ( - False # We actually don't know (file history) - ) - instance._p2_tracked = True + p1_tracked = False + p2_info = True else: - instance._p1_tracked = True - return instance + p1_tracked = True + p2_info = False + return cls(p1_tracked=p1_tracked, p2_info=p2_info) elif state == b'n': if size == FROM_P2: return cls.new_from_p2() @@ -224,7 +184,6 @@ elif mtime == AMBIGUOUS_TIME: instance = cls.new_normal(mode, size, 42) instance._mtime = None - instance._possibly_dirty = True return instance else: return cls.new_normal(mode, size, mtime) @@ -237,7 +196,7 @@ This means the next status call will have to actually check its content to make sure it is correct. """ - self._possibly_dirty = True + self._mtime = None def set_clean(self, mode, size, mtime): """mark a file as "clean" cancelling potential "possibly dirty call" @@ -249,10 +208,6 @@ """ self._wc_tracked = True self._p1_tracked = True - self._p2_tracked = False # this might be wrong - self._merged = False - self._clean_p2 = False - self._possibly_dirty = False self._mode = mode self._size = size self._mtime = mtime @@ -263,11 +218,11 @@ This will ultimately be called by command like `hg add`. """ self._wc_tracked = True - # `set_tracked` is replacing various `normallookup` call. So we set - # "possibly dirty" to stay on the safe side. + # `set_tracked` is replacing various `normallookup` call. So we mark + # the files as needing lookup # # Consider dropping this in the future in favor of something less broad. - self._possibly_dirty = True + self._mtime = None def set_untracked(self): """mark a file as untracked in the working copy @@ -284,18 +239,11 @@ This is to be call by the dirstatemap code when the second parent is dropped """ - if not (self.merged or self.from_p2): - return - self._p1_tracked = self.merged # why is this not already properly set ? - - self._merged = False - self._clean_p1 = False - self._clean_p2 = False - self._p2_tracked = False - self._possibly_dirty = True - self._mode = None - self._size = None - self._mtime = None + if self._p2_info: + self._p2_info = False + self._mode = None + self._size = None + self._mtime = None @property def mode(self): @@ -334,23 +282,21 @@ @property def any_tracked(self): """True is the file is tracked anywhere (wc or parents)""" - return self._wc_tracked or self._p1_tracked or self._p2_tracked + return self._wc_tracked or self._p1_tracked or self._p2_info @property def added(self): """True if the file has been added""" - return self._wc_tracked and not (self._p1_tracked or self._p2_tracked) + return self._wc_tracked and not (self._p1_tracked or self._p2_info) @property def maybe_clean(self): """True if the file has a chance to be in the "clean" state""" if not self._wc_tracked: return False - elif self.added: + elif not self._p1_tracked: return False - elif self._merged: - return False - elif self._clean_p2: + elif self._p2_info: return False return True @@ -360,7 +306,7 @@ Should only be set if a merge is in progress in the dirstate """ - return self._wc_tracked and self._merged + return self._wc_tracked and self._p1_tracked and self._p2_info @property def from_p2(self): @@ -370,18 +316,16 @@ Should only be set if a merge is in progress in the dirstate """ - if not self._wc_tracked: - return False - return self._clean_p2 + return self._wc_tracked and (not self._p1_tracked) and self._p2_info @property def removed(self): """True if the file has been removed""" - return not self._wc_tracked and (self._p1_tracked or self._p2_tracked) + return not self._wc_tracked and (self._p1_tracked or self._p2_info) def v1_state(self): """return a "state" suitable for v1 serialization""" - if not (self._p1_tracked or self._p2_tracked or self._wc_tracked): + if not self.any_tracked: # the object has no state to record, this is -currently- # unsupported raise RuntimeError('untracked item') @@ -404,9 +348,9 @@ # the object has no state to record, this is -currently- # unsupported raise RuntimeError('untracked item') - elif self.removed and self._merged: + elif self.removed and self._p1_tracked and self._p2_info: return NONNORMAL - elif self.removed and self._clean_p2: + elif self.removed and self._p2_info: return FROM_P2 elif self.removed: return 0 @@ -416,8 +360,8 @@ return NONNORMAL elif self.from_p2: return FROM_P2 - elif self._possibly_dirty: - return self._size if self._size is not None else NONNORMAL + elif self._size is None: + return NONNORMAL else: return self._size @@ -429,16 +373,14 @@ raise RuntimeError('untracked item') elif self.removed: return 0 - elif self._possibly_dirty: - return AMBIGUOUS_TIME - elif self.merged: + elif self._mtime is None: return AMBIGUOUS_TIME - elif self.added: + elif self._p2_info: return AMBIGUOUS_TIME - elif self.from_p2: + elif not self._p1_tracked: return AMBIGUOUS_TIME else: - return self._mtime if self._mtime is not None else 0 + return self._mtime def need_delay(self, now): """True if the stored mtime would be ambiguous with the current time""" 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 @@ -16,21 +16,15 @@ #[derive(Debug, PartialEq, Copy, Clone)] pub struct DirstateEntry { flags: Flags, - mode: i32, - size: i32, - mtime: i32, + mode_size: Option<(i32, i32)>, + mtime: Option, } bitflags! { - pub struct Flags: u8 { + struct Flags: u8 { const WDIR_TRACKED = 1 << 0; const P1_TRACKED = 1 << 1; - const P2_TRACKED = 1 << 2; - const POSSIBLY_DIRTY = 1 << 3; - const MERGED = 1 << 4; - const CLEAN_P1 = 1 << 5; - const CLEAN_P2 = 1 << 6; - const ENTRYLESS_TREE_NODE = 1 << 7; + const P2_INFO = 1 << 2; } } @@ -48,15 +42,19 @@ impl DirstateEntry { pub fn new( - flags: Flags, - mode_size_mtime: Option<(i32, i32, i32)>, + wdir_tracked: bool, + p1_tracked: bool, + p2_info: bool, + mode_size: Option<(i32, i32)>, + mtime: Option, ) -> Self { - let (mode, size, mtime) = - mode_size_mtime.unwrap_or((0, SIZE_NON_NORMAL, MTIME_UNSET)); + let mut flags = Flags::empty(); + flags.set(Flags::WDIR_TRACKED, wdir_tracked); + flags.set(Flags::P1_TRACKED, p1_tracked); + flags.set(Flags::P2_INFO, p2_info); Self { flags, - mode, - size, + mode_size, mtime, } } @@ -75,12 +73,9 @@ Self::new_possibly_dirty() } else if mtime == MTIME_UNSET { Self { - flags: Flags::WDIR_TRACKED - | Flags::P1_TRACKED - | Flags::POSSIBLY_DIRTY, - mode, - size, - mtime: 0, + flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED, + mode_size: Some((mode, size)), + mtime: None, } } else { Self::new_normal(mode, size, mtime) @@ -89,18 +84,15 @@ EntryState::Added => Self::new_added(), EntryState::Removed => Self { flags: if size == SIZE_NON_NORMAL { - Flags::P1_TRACKED // might not be true because of rename ? - | Flags::P2_TRACKED // might not be true because of rename ? - | Flags::MERGED + Flags::P1_TRACKED | Flags::P2_INFO } else if size == SIZE_FROM_OTHER_PARENT { // We don’t know if P1_TRACKED should be set (file history) - Flags::P2_TRACKED | Flags::CLEAN_P2 + Flags::P2_INFO } else { Flags::P1_TRACKED }, - mode: 0, - size: 0, - mtime: 0, + mode_size: None, + mtime: None, }, EntryState::Merged => Self::new_merged(), } @@ -109,30 +101,25 @@ pub fn new_from_p2() -> Self { Self { // might be missing P1_TRACKED - flags: Flags::WDIR_TRACKED | Flags::P2_TRACKED | Flags::CLEAN_P2, - mode: 0, - size: SIZE_FROM_OTHER_PARENT, - mtime: MTIME_UNSET, + flags: Flags::WDIR_TRACKED | Flags::P2_INFO, + mode_size: None, + mtime: None, } } pub fn new_possibly_dirty() -> Self { Self { - flags: Flags::WDIR_TRACKED - | Flags::P1_TRACKED - | Flags::POSSIBLY_DIRTY, - mode: 0, - size: SIZE_NON_NORMAL, - mtime: MTIME_UNSET, + flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED, + mode_size: None, + mtime: None, } } pub fn new_added() -> Self { Self { flags: Flags::WDIR_TRACKED, - mode: 0, - size: SIZE_NON_NORMAL, - mtime: MTIME_UNSET, + mode_size: None, + mtime: None, } } @@ -140,20 +127,17 @@ Self { flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED // might not be true because of rename ? - | Flags::P2_TRACKED // might not be true because of rename ? - | Flags::MERGED, - mode: 0, - size: SIZE_NON_NORMAL, - mtime: MTIME_UNSET, + | Flags::P2_INFO, // might not be true because of rename ? + mode_size: None, + mtime: None, } } pub fn new_normal(mode: i32, size: i32, mtime: i32) -> Self { Self { flags: Flags::WDIR_TRACKED | Flags::P1_TRACKED, - mode, - size, - mtime, + mode_size: Some((mode, size)), + mtime: Some(mtime), } } @@ -169,36 +153,34 @@ self.flags.contains(Flags::WDIR_TRACKED) } - fn tracked_in_any_parent(&self) -> bool { - self.flags.intersects(Flags::P1_TRACKED | Flags::P2_TRACKED) + fn in_either_parent(&self) -> bool { + self.flags.intersects(Flags::P1_TRACKED | Flags::P2_INFO) } pub fn removed(&self) -> bool { - self.tracked_in_any_parent() - && !self.flags.contains(Flags::WDIR_TRACKED) + self.in_either_parent() && !self.flags.contains(Flags::WDIR_TRACKED) } pub fn merged(&self) -> bool { - self.flags.contains(Flags::WDIR_TRACKED | Flags::MERGED) + self.flags + .contains(Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO) } pub fn added(&self) -> bool { - self.flags.contains(Flags::WDIR_TRACKED) - && !self.tracked_in_any_parent() + self.flags.contains(Flags::WDIR_TRACKED) && !self.in_either_parent() } pub fn from_p2(&self) -> bool { - self.flags.contains(Flags::WDIR_TRACKED | Flags::CLEAN_P2) + self.flags.contains(Flags::WDIR_TRACKED | Flags::P2_INFO) + && !self.flags.contains(Flags::P1_TRACKED) } pub fn maybe_clean(&self) -> bool { if !self.flags.contains(Flags::WDIR_TRACKED) { false - } else if self.added() { + } else if !self.flags.contains(Flags::P1_TRACKED) { false - } else if self.flags.contains(Flags::MERGED) { - false - } else if self.flags.contains(Flags::CLEAN_P2) { + } else if self.flags.contains(Flags::P2_INFO) { false } else { true @@ -207,11 +189,15 @@ pub fn any_tracked(&self) -> bool { self.flags.intersects( - Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_TRACKED, + Flags::WDIR_TRACKED | Flags::P1_TRACKED | Flags::P2_INFO, ) } - pub fn state(&self) -> EntryState { + fn v1_state(&self) -> EntryState { + if !self.any_tracked() { + // TODO: return an Option instead? + panic!("Accessing v1_state of an untracked DirstateEntry") + } if self.removed() { EntryState::Removed } else if self.merged() { @@ -223,14 +209,24 @@ } } - pub fn mode(&self) -> i32 { - self.mode + fn v1_mode(&self) -> i32 { + if let Some((mode, _size)) = self.mode_size { + mode + } else { + 0 + } } - pub fn size(&self) -> i32 { - if self.removed() && self.flags.contains(Flags::MERGED) { + fn v1_size(&self) -> i32 { + if !self.any_tracked() { + // TODO: return an Option instead? + panic!("Accessing v1_size of an untracked DirstateEntry") + } + if self.removed() + && self.flags.contains(Flags::P1_TRACKED | Flags::P2_INFO) + { SIZE_NON_NORMAL - } else if self.removed() && self.flags.contains(Flags::CLEAN_P2) { + } else if self.removed() && self.flags.contains(Flags::P2_INFO) { SIZE_FROM_OTHER_PARENT } else if self.removed() { 0 @@ -240,87 +236,81 @@ SIZE_NON_NORMAL } else if self.from_p2() { SIZE_FROM_OTHER_PARENT - } else if self.flags.contains(Flags::POSSIBLY_DIRTY) { - self.size // TODO: SIZE_NON_NORMAL ? + } else if let Some((_mode, size)) = self.mode_size { + size } else { - self.size + SIZE_NON_NORMAL } } - pub fn mtime(&self) -> i32 { + fn v1_mtime(&self) -> i32 { + if !self.any_tracked() { + // TODO: return an Option instead? + panic!("Accessing v1_mtime of an untracked DirstateEntry") + } if self.removed() { 0 - } else if self.flags.contains(Flags::POSSIBLY_DIRTY) { - MTIME_UNSET - } else if self.merged() { + } else if self.flags.contains(Flags::P2_INFO) { MTIME_UNSET - } else if self.added() { - MTIME_UNSET - } else if self.from_p2() { + } else if !self.flags.contains(Flags::P1_TRACKED) { MTIME_UNSET } else { - self.mtime + self.mtime.unwrap_or(MTIME_UNSET) } } + // TODO: return `Option`? None when `!self.any_tracked` + pub fn state(&self) -> EntryState { + self.v1_state() + } + + // TODO: return Option? + pub fn mode(&self) -> i32 { + self.v1_mode() + } + + // TODO: return Option? + pub fn size(&self) -> i32 { + self.v1_size() + } + + // TODO: return Option? + pub fn mtime(&self) -> i32 { + self.v1_mtime() + } + pub fn drop_merge_data(&mut self) { - if self.flags.contains(Flags::CLEAN_P1) - || self.flags.contains(Flags::CLEAN_P2) - || self.flags.contains(Flags::MERGED) - || self.flags.contains(Flags::P2_TRACKED) - { - if self.flags.contains(Flags::MERGED) { - self.flags.insert(Flags::P1_TRACKED); - } else { - self.flags.remove(Flags::P1_TRACKED); - } - self.flags.remove( - Flags::MERGED - | Flags::CLEAN_P1 - | Flags::CLEAN_P2 - | Flags::P2_TRACKED, - ); - self.flags.insert(Flags::POSSIBLY_DIRTY); - self.mode = 0; - self.mtime = 0; - // size = None on the python size turn into size = NON_NORMAL when - // accessed. So the next line is currently required, but a some - // future clean up would be welcome. - self.size = SIZE_NON_NORMAL; + if self.flags.contains(Flags::P2_INFO) { + self.flags.remove(Flags::P2_INFO); + self.mode_size = None; + self.mtime = None; } } pub fn set_possibly_dirty(&mut self) { - self.flags.insert(Flags::POSSIBLY_DIRTY) + self.mtime = None } pub fn set_clean(&mut self, mode: i32, size: i32, mtime: i32) { self.flags.insert(Flags::WDIR_TRACKED | Flags::P1_TRACKED); - self.flags.remove( - Flags::P2_TRACKED // This might be wrong - | Flags::MERGED - | Flags::CLEAN_P2 - | Flags::POSSIBLY_DIRTY, - ); - self.mode = mode; - self.size = size; - self.mtime = mtime; + self.mode_size = Some((mode, size)); + self.mtime = Some(mtime); } pub fn set_tracked(&mut self) { - self.flags - .insert(Flags::WDIR_TRACKED | Flags::POSSIBLY_DIRTY); - // size = None on the python size turn into size = NON_NORMAL when - // accessed. So the next line is currently required, but a some future - // clean up would be welcome. - self.size = SIZE_NON_NORMAL; + self.flags.insert(Flags::WDIR_TRACKED); + // `set_tracked` is replacing various `normallookup` call. So we mark + // the files as needing lookup + // + // Consider dropping this in the future in favor of something less + // broad. + self.mtime = None; } pub fn set_untracked(&mut self) { self.flags.remove(Flags::WDIR_TRACKED); - self.mode = 0; - self.size = 0; - self.mtime = 0; + self.mode_size = None; + self.mtime = None; } /// Returns `(state, mode, size, mtime)` for the puprose of serialization @@ -330,7 +320,12 @@ /// want to not represent these cases that way in memory, but serialization /// will need to keep the same format. pub fn v1_data(&self) -> (u8, i32, i32, i32) { - (self.state().into(), self.mode(), self.size(), self.mtime()) + ( + self.v1_state().into(), + self.v1_mode(), + self.v1_size(), + self.v1_mtime(), + ) } pub(crate) fn is_from_other_parent(&self) -> bool { @@ -354,12 +349,7 @@ /// Returns a `(state, mode, size, mtime)` tuple as for /// `DirstateMapMethods::debug_iter`. pub fn debug_tuple(&self) -> (u8, i32, i32, i32) { - let state = if self.flags.contains(Flags::ENTRYLESS_TREE_NODE) { - b' ' - } else { - self.state().into() - }; - (state, self.mode(), self.size(), self.mtime()) + (self.state().into(), self.mode(), self.size(), self.mtime()) } pub fn mtime_is_ambiguous(&self, now: i32) -> bool { @@ -378,14 +368,10 @@ // dirstate, forcing future 'status' calls to compare the // contents of the file if the size is the same. This prevents // mistakenly treating such files as clean. - self.clear_mtime() + self.set_possibly_dirty() } ambiguous } - - pub fn clear_mtime(&mut self) { - self.mtime = -1; - } } impl EntryState { diff --git a/rust/hg-core/src/dirstate_tree/dirstate_map.rs b/rust/hg-core/src/dirstate_tree/dirstate_map.rs --- a/rust/hg-core/src/dirstate_tree/dirstate_map.rs +++ b/rust/hg-core/src/dirstate_tree/dirstate_map.rs @@ -695,7 +695,7 @@ path.as_ref(), )? { if let NodeData::Entry(entry) = &mut node.data { - entry.clear_mtime(); + entry.set_possibly_dirty(); } } } 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 @@ -6,7 +6,6 @@ use cpython::PyResult; use cpython::Python; use cpython::PythonObject; -use hg::dirstate::entry::Flags; use hg::dirstate::DirstateEntry; use hg::dirstate::EntryState; use std::cell::Cell; @@ -19,23 +18,25 @@ _cls, wc_tracked: bool = false, p1_tracked: bool = false, - p2_tracked: bool = false, - merged: bool = false, - clean_p1: bool = false, - clean_p2: bool = false, - possibly_dirty: bool = false, + p2_info: bool = false, + has_meaningful_data: bool = true, + has_meaningful_mtime: bool = true, parentfiledata: Option<(i32, i32, i32)> = None, ) -> PyResult { - let mut flags = Flags::empty(); - flags.set(Flags::WDIR_TRACKED, wc_tracked); - flags.set(Flags::P1_TRACKED, p1_tracked); - flags.set(Flags::P2_TRACKED, p2_tracked); - flags.set(Flags::MERGED, merged); - flags.set(Flags::CLEAN_P1, clean_p1); - flags.set(Flags::CLEAN_P2, clean_p2); - flags.set(Flags::POSSIBLY_DIRTY, possibly_dirty); - let entry = DirstateEntry::new(flags, parentfiledata); + let mut mode_size_opt = None; + let mut mtime_opt = None; + if let Some((mode, size, mtime)) = parentfiledata { + if has_meaningful_data { + mode_size_opt = Some((mode, size)) + } + if has_meaningful_mtime { + mtime_opt = Some(mtime) + } + } + let entry = DirstateEntry::new( + wc_tracked, p1_tracked, p2_info, mode_size_opt, mtime_opt, + ); DirstateItem::create_instance(py, Cell::new(entry)) }