Patchwork [4,of,6] dirstate: add _writepending to make pending data visible to external process

login
register
mail settings
Submitter Katsunori FUJIWARA
Date May 19, 2015, 4:42 p.m.
Message ID <32bb772580da70c05822.1432053724@feefifofum>
Download mbox | patch
Permalink /patch/9181/
State Changes Requested
Delegated to: Pierre-Yves David
Headers show

Comments

Katsunori FUJIWARA - May 19, 2015, 4:42 p.m.
# HG changeset patch
# User FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
# Date 1432051569 -32400
#      Wed May 20 01:06:09 2015 +0900
# Node ID 32bb772580da70c05822ab830349585f0245ae92
# Parent  e830ef506b9aaa01c5406d100a385961c4c0dff0
dirstate: add _writepending to make pending data visible to external process

This patch adds `_writepending()`, which is used to make pending data
visible to external process.

Combination of `_dirty` and newly introduced `_diverted` property
means status below:

  _diverted|_dirty| status
  ---------+------+-----------------------------------------------
   x       | x    | exactly clean
   x       | o    | all changes are held in memory
   o       | x    | all changes are also held in pending file (*1)
   o       | o    | pending file is out of date (*2)

For safety and consistency, `invalidate()` should remove pending file
in both (*1) and (*2) cases: remaining it may cause accidental reading
it in.

On the other hand, `write()` should:

- rename from `dirstate.pending` to `dirstate` in (*1) case, because:
  - the former works well as the latter
    (dirstate hasn't been changed since last `_writepending()`), and
  - renaming is more efficient than writing out again

- remove `dirstate.pending` and write changes into `dirstate` in (*2) case

For review-ability, this patch focuses only on writing `.pending` file
out. Then, `dirstate.pending` remains even after `write()`,
`invalidate()` and the end of transaction. But it is still safe
enough, because there is no code path invoking `_writepending()` yet.

BTW, in many cases, changes on dirstate shouldn't be `invalidate()`-ed
even if transaction is aborted. This is reason why `_writepending()`
doesn't use `tr.registertmp()`, which removes specified file before
"abort callback" invocation and may cause losing changes held in
pending file.

Patch

diff --git a/mercurial/dirstate.py b/mercurial/dirstate.py
--- a/mercurial/dirstate.py
+++ b/mercurial/dirstate.py
@@ -64,10 +64,19 @@ 
         self._filecache = {}
         self._parentwriters = 0
         self._filename = 'dirstate'
+        self._pendingfilename = '%s.pending' % self._filename
 
         # for consitent view between _pl() and _read() invocations
         self._pendingmode = None
 
+        # _diverted|_dirty| status
+        # ---------+------+-----------------------------------------------
+        #  x       | x    | exactly clean
+        #  x       | o    | all changes are held in memory
+        #  o       | x    | all changes are also held in pending file
+        #  o       | o    | pending file is out of date
+        self._diverted = False
+
     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,
@@ -611,6 +620,23 @@ 
     def write(self):
         if not self._dirty:
             return
+        self._writedirstate(self._filename)
+
+    def _writepending(self, tr):
+        '''Make pending data visible to external processes
+
+        This returns whether HG_PENDING should be defined or not.
+        '''
+        if self._dirty:
+            self._writedirstate(self._pendingfilename)
+            self._diverted = True
+        return self._diverted
+
+    def _writedirstate(self, filename):
+        if self._pendingmode:
+            # maybe "dirstate.write()" out of wlock scope
+            raise util.Abort(_('unexpected writing dirstate out'
+                               ' under PENDING mode'))
 
         # enough 'delaywrite' prevents 'pack_dirstate' from dropping
         # timestamp of each entries in dirstate, because of 'now > mtime'
@@ -619,7 +645,7 @@ 
             import time # to avoid useless import
             time.sleep(delaywrite)
 
-        st = self._opener(self._filename, "w", atomictemp=True)
+        st = self._opener(filename, "w", atomictemp=True)
         # use the modification time of the newly created temporary file as the
         # filesystem's notion of 'now'
         now = util.fstat(st).st_mtime