dirstate: read from pending file under HG_PENDING mode if it exists
True/False value of '_pendingmode' means whether 'dirstate.pending' is
used to initialize own '_map' and so on. When it is None, neither
'dirstate' nor 'dirstate.pending' is read in yet.
This is used to keep consistent view between '_pl()' and '_read()'.
Once '_pendingmode' is determined by reading one of 'dirstate' or
'dirstate.pending' in, '_pendingmode' is kept even if 'invalidate()'
is invoked. This should be reasonable, because:
- effective 'invalidate()' invocation should occur only in wlock scope, and
- wlock can't be gotten under HG_PENDING mode
'_trypending()' is defined as a normal function to factor similar code
path (in bookmarks and phases) out in the future easily.
--- a/mercurial/dirstate.py Wed Oct 14 02:49:17 2015 +0900
+++ b/mercurial/dirstate.py Wed Oct 14 02:49:17 2015 +0900
@@ -36,6 +36,22 @@
os.close(tmpfd)
vfs.unlink(tmpname)
+def _trypending(root, vfs, filename):
+ '''Open file to be read according to HG_PENDING environment variable
+
+ This opens '.pending' of specified 'filename' only when HG_PENDING
+ is equal to 'root'.
+
+ This returns '(fp, is_pending_opened)' tuple.
+ '''
+ if root == os.environ.get('HG_PENDING'):
+ try:
+ return (vfs('%s.pending' % filename), True)
+ except IOError as inst:
+ if inst.errno != errno.ENOENT:
+ raise
+ return (vfs(filename), False)
+
class dirstate(object):
def __init__(self, opener, ui, root, validate):
@@ -64,6 +80,9 @@
self._filename = 'dirstate'
self._pendingfilename = '%s.pending' % self._filename
+ # for consitent view between _pl() and _read() invocations
+ self._pendingmode = None
+
def beginparentchange(self):
'''Marks the beginning of a set of changes that involve changing
the dirstate parents. If there is an exception during this time,
@@ -137,7 +156,7 @@
@propertycache
def _pl(self):
try:
- fp = self._opener(self._filename)
+ fp = self._opendirstatefile()
st = fp.read(40)
fp.close()
l = len(st)
@@ -342,11 +361,20 @@
f.discard()
raise
+ def _opendirstatefile(self):
+ fp, mode = _trypending(self._root, self._opener, self._filename)
+ if self._pendingmode is not None and self._pendingmode != mode:
+ fp.close()
+ raise error.Abort(_('working directory state may be '
+ 'changed parallelly'))
+ self._pendingmode = mode
+ return fp
+
def _read(self):
self._map = {}
self._copymap = {}
try:
- fp = self._opener.open(self._filename)
+ fp = self._opendirstatefile()
try:
st = fp.read()
finally: