diff -r 111f5a0cbcaa -r 568f63b5a30f mercurial/dirstate.py --- a/mercurial/dirstate.py Mon Jan 31 14:26:35 2022 +0100 +++ b/mercurial/dirstate.py Mon Jan 31 08:44:48 2022 +0100 @@ -12,6 +12,7 @@ import errno import os import stat +import uuid from .i18n import _ from .pycompat import delattr @@ -23,6 +24,7 @@ encoding, error, match as matchmod, + node, pathutil, policy, pycompat, @@ -99,6 +101,7 @@ sparsematchfn, nodeconstants, use_dirstate_v2, + use_tracked_key=False, ): """Create a new dirstate object. @@ -107,6 +110,7 @@ the dirstate. """ self._use_dirstate_v2 = use_dirstate_v2 + self._use_tracked_key = use_tracked_key self._nodeconstants = nodeconstants self._opener = opener self._validate = validate @@ -115,11 +119,15 @@ # ntpath.join(root, '') of Python 2.7.9 does not add sep if root is # UNC path pointing to root share (issue4557) self._rootdir = pathutil.normasprefix(root) + # True is any internal state may be different self._dirty = False + # True if the set of tracked file may be different + self._dirty_tracked_set = False self._ui = ui self._filecache = {} self._parentwriters = 0 self._filename = b'dirstate' + self._filename_tk = b'dirstate-tracked-key' self._pendingfilename = b'%s.pending' % self._filename self._plchangecallbacks = {} self._origpl = None @@ -409,6 +417,7 @@ if a in self.__dict__: delattr(self, a) self._dirty = False + self._dirty_tracked_set = False self._parentwriters = 0 self._origpl = None @@ -446,6 +455,8 @@ pre_tracked = self._map.set_tracked(filename) if reset_copy: self._map.copymap.pop(filename, None) + if pre_tracked: + self._dirty_tracked_set = True return pre_tracked @requires_no_parents_change @@ -460,6 +471,7 @@ ret = self._map.set_untracked(filename) if ret: self._dirty = True + self._dirty_tracked_set = True return ret @requires_no_parents_change @@ -544,6 +556,13 @@ # this. The test agrees self._dirty = True + old_entry = self._map.get(filename) + if old_entry is None: + prev_tracked = False + else: + prev_tracked = old_entry.tracked + if prev_tracked != wc_tracked: + self._dirty_tracked_set = True self._map.reset_state( filename, @@ -702,20 +721,44 @@ if not self._dirty: return - filename = self._filename + write_key = self._use_tracked_key and self._dirty_tracked_set if tr: # delay writing in-memory changes out + if write_key: + tr.addfilegenerator( + b'dirstate-0-key-pre', + (self._filename_tk,), + lambda f: self._write_tracked_key(tr, f), + location=b'plain', + ) tr.addfilegenerator( b'dirstate-1-main', (self._filename,), lambda f: self._writedirstate(tr, f), location=b'plain', ) + if write_key: + tr.addfilegenerator( + b'dirstate-2-key-post', + (self._filename_tk,), + lambda f: self._write_tracked_key(tr, f), + location=b'plain', + ) return file = lambda f: self._opener(f, b"w", atomictemp=True, checkambig=True) + if write_key: + # we change the key-file before changing the dirstate to make sure + # reading invalidate there cache before we start writing + with file(self._filename_tk) as f: + self._write_tracked_key(tr, f) with file(self._filename) as f: self._writedirstate(tr, f) + if write_key: + # we update the key-file after writing to make sure reader have a + # key that match the newly written content + with file(self._filename_tk) as f: + self._write_tracked_key(tr, f) def addparentchangecallback(self, category, callback): """add a callback to be called when the wd parents are changed @@ -736,9 +779,13 @@ ): callback(self, self._origpl, self._pl) self._origpl = None - self._map.write(tr, st) self._dirty = False + self._dirty_tracked_set = False + + def _write_tracked_key(self, tr, f): + key = node.hex(uuid.uuid4().bytes) + f.write(b"1\n%s\n" % key) # 1 is the format version def _dirignore(self, f): if self._ignore(f):