Mercurial > public > mercurial-scm > hg-stable
diff mercurial/dirstate.py @ 48698:568f63b5a30f
dirstate: introduce a "tracked-key" feature
A new format variant is introduced. When used, a `tracked-key` file will be
generated. That file will be update when the set of tracked file might have
changed. This will be useful for external automation (e.g. build tool) to be
notified when the set of relevant files changes.
One of the motivation for this changes is to mitigate effect dirstate-v2 has on
such automation. Since the dirstate file is updated much more frequently on
dirstate-v2, monitoring update to that file is no longer a viable strategy.
See the associated documentation for details about the feature
To prevent older client to update the repository without updating that file, a
new requirements is introduced.
The `postfinalizegenerators` business is a bit weird, so I'll likely clean that
up soon.
Differential Revision: https://phab.mercurial-scm.org/D12124
author | Pierre-Yves David <pierre-yves.david@octobus.net> |
---|---|
date | Mon, 31 Jan 2022 08:44:48 +0100 |
parents | 111f5a0cbcaa |
children | 21ac6aedd5e5 |
line wrap: on
line diff
--- 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):