Mercurial > hg
changeset 26634:09bb1ee7e73e
dirstate: make writing in-memory changes aware of transaction activity
This patch delays writing in-memory changes out, if transaction is
running.
'_getfsnow()' is defined as a function, to hook it easily for
ambiguous timestamp tests (see also fakedirstatewritetime.py)
'if tr:' code path in this patch is still disabled at this revision,
because there is no client invoking 'dirstate.write()' with repo
object.
BTW, this patch changes 'dirstate.invalidate()' semantics around
'dirstate.write()' in a transaction scope:
before:
with repo.transaction():
dirstate.CHANGE('A')
dirstate.write() # change for A is written out here
dirstate.CHANGE('B')
dirstate.invalidate() # discards only change for B
after:
with repo.transaction():
dirstate.CHANGE('A')
dirstate.write() # change for A is still kept in memory
dirstate.CHANGE('B')
dirstate.invalidate() # discards changes for A and B
Fortunately, there is no code path expecting the former, at least, in
Mercurial itself, because 'dirstateguard' was introduced to remove
such 'dirstate.invalidate()'.
author | FUJIWARA Katsunori <foozy@lares.dti.ne.jp> |
---|---|
date | Wed, 14 Oct 2015 02:49:17 +0900 |
parents | 020b12d591f3 |
children | 79d86ab65c9d |
files | mercurial/dirstate.py mercurial/localrepo.py tests/fakedirstatewritetime.py |
diffstat | 3 files changed, 53 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial/dirstate.py Wed Oct 14 02:49:17 2015 +0900 +++ b/mercurial/dirstate.py Wed Oct 14 02:49:17 2015 +0900 @@ -27,6 +27,15 @@ def join(self, obj, fname): return obj._join(fname) +def _getfsnow(vfs): + '''Get "now" timestamp on filesystem''' + tmpfd, tmpname = vfs.mkstemp() + try: + return util.statmtimesec(os.fstat(tmpfd)) + finally: + os.close(tmpfd) + vfs.unlink(tmpname) + class dirstate(object): def __init__(self, opener, ui, root, validate): @@ -611,7 +620,7 @@ self._pl = (parent, nullid) self._dirty = True - def write(self): + def write(self, repo=None): if not self._dirty: return @@ -622,7 +631,40 @@ import time # to avoid useless import time.sleep(delaywrite) - st = self._opener(self._filename, "w", atomictemp=True) + filename = self._filename + if not repo: + tr = None + if self._opener.lexists(self._pendingfilename): + # if pending file already exists, in-memory changes + # should be written into it, because it has priority + # to '.hg/dirstate' at reading under HG_PENDING mode + filename = self._pendingfilename + else: + tr = repo.currenttransaction() + + if tr: + # 'dirstate.write()' is not only for writing in-memory + # changes out, but also for dropping ambiguous timestamp. + # delayed writing re-raise "ambiguous timestamp issue". + # See also the wiki page below for detail: + # https://www.mercurial-scm.org/wiki/DirstateTransactionPlan + + # emulate dropping timestamp in 'parsers.pack_dirstate' + now = _getfsnow(repo.vfs) + dmap = self._map + for f, e in dmap.iteritems(): + if e[0] == 'n' and e[3] == now: + dmap[f] = dirstatetuple(e[0], e[1], e[2], -1) + + # emulate that all 'dirstate.normal' results are written out + self._lastnormaltime = 0 + + # delay writing in-memory changes out + tr.addfilegenerator('dirstate', (self._filename,), + self._writedirstate, location='plain') + return + + st = self._opener(filename, "w", atomictemp=True) self._writedirstate(st) def _writedirstate(self, st):
--- a/mercurial/localrepo.py Wed Oct 14 02:49:17 2015 +0900 +++ b/mercurial/localrepo.py Wed Oct 14 02:49:17 2015 +0900 @@ -1000,6 +1000,11 @@ def releasefn(tr, success): repo = reporef() if success: + # this should be explicitly invoked here, because + # in-memory changes aren't written out at closing + # transaction, if tr.addfilegenerator (via + # dirstate.write or so) isn't invoked while + # transaction running repo.dirstate.write() else: # prevent in-memory changes from being written out at
--- a/tests/fakedirstatewritetime.py Wed Oct 14 02:49:17 2015 +0900 +++ b/tests/fakedirstatewritetime.py Wed Oct 14 02:49:17 2015 +0900 @@ -5,7 +5,7 @@ # - 'workingctx._checklookup()' (= 'repo.status()') # - 'committablectx.markcommitted()' -from mercurial import context, extensions, parsers, util +from mercurial import context, dirstate, extensions, parsers, util def pack_dirstate(fakenow, orig, dmap, copymap, pl, now): # execute what original parsers.pack_dirstate should do actually @@ -34,13 +34,16 @@ fakenow = util.parsedate(fakenow, ['%Y%m%d%H%M'])[0] orig_pack_dirstate = parsers.pack_dirstate + orig_dirstate_getfsnow = dirstate._getfsnow wrapper = lambda *args: pack_dirstate(fakenow, orig_pack_dirstate, *args) parsers.pack_dirstate = wrapper + dirstate._getfsnow = lambda *args: fakenow try: return func() finally: parsers.pack_dirstate = orig_pack_dirstate + dirstate._getfsnow = orig_dirstate_getfsnow def _checklookup(orig, workingctx, files): ui = workingctx.repo().ui