mirror of
https://github.com/facebook/sapling.git
synced 2024-10-06 23:07:18 +03:00
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()'.
This commit is contained in:
parent
bde9721b18
commit
2ef2ab5d6d
@ -27,6 +27,15 @@ class rootcache(filecache):
|
||||
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 @@ class dirstate(object):
|
||||
self._pl = (parent, nullid)
|
||||
self._dirty = True
|
||||
|
||||
def write(self):
|
||||
def write(self, repo=None):
|
||||
if not self._dirty:
|
||||
return
|
||||
|
||||
@ -622,7 +631,40 @@ class dirstate(object):
|
||||
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):
|
||||
|
@ -1000,6 +1000,11 @@ class localrepository(object):
|
||||
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
|
||||
|
@ -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 @@ def fakewrite(ui, func):
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user