Patchwork [5,of,6,V2] context: avoid breaking already fixed self._status at ctx.status()

login
register
mail settings
Submitter Katsunori FUJIWARA
Date Dec. 31, 2014, 8:58 a.m.
Message ID <10ae70180737bf74303d.1420016290@feefifofum>
Download mbox | patch
Permalink /patch/7282/
State Accepted
Commit 1e6fb8db666ebe09f62cc2abfc08cdb8e8109aa0
Headers show

Comments

Katsunori FUJIWARA - Dec. 31, 2014, 8:58 a.m.
# HG changeset patch
# User FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
# Date 1420016143 -32400
#      Wed Dec 31 17:55:43 2014 +0900
# Node ID 10ae70180737bf74303df75359d40ef5066552df
# Parent  61c95f20082157684270ad05f52f0890e3c4f544
context: avoid breaking already fixed self._status at ctx.status()

Before this patch, "status()" on "workingcommitctx" with "always
match" object causes breaking "self._status" in
"workingctx._buildstatus()", because "workingctx._buildstatus()"
caches the result of "dirstate.status()" into "self._status" for
efficiency, even though it should be fixed at construction for
committing.

For example, template function "diff()" without any patterns in
"committemplate" implies "status()" on "workingcommitctx" with "always
match" object, via "basectx.diff()" and "patch.diff()".

Then, broken "self._status" causes committing unexpected files.

To avoid breaking already fixed "self._status" at "ctx.status()", this
patch overrides "_buildstatus" in "workingcommitctx".

This patch doesn't write out the result of template function "diff()"
in "committemplate" in "test-commit.t", because matching against files
to be committed still has an issue fixed in subsequent patch.

Patch

diff --git a/mercurial/context.py b/mercurial/context.py
--- a/mercurial/context.py
+++ b/mercurial/context.py
@@ -1569,6 +1569,18 @@ 
         super(workingctx, self).__init__(repo, text, user, date, extra,
                                          changes)
 
+    def _buildstatus(self, other, s, match,
+                     listignored, listclean, listunknown):
+        """Prevent ``workingctx._buildstatus`` from changing ``self._status``
+        """
+        s = self._dirstatestatus(match, listignored, listclean, listunknown)
+        if other != self._repo['.']:
+            # workingctx._buildstatus doesn't change self._status in this case
+            superself = super(workingcommitctx, self)
+            s = superself._buildstatus(other, s, match,
+                                       listignored, listclean, listunknown)
+        return s
+
 class memctx(committablectx):
     """Use memctx to perform in-memory commits via localrepo.commitctx().
 
diff --git a/tests/test-commit.t b/tests/test-commit.t
--- a/tests/test-commit.t
+++ b/tests/test-commit.t
@@ -430,6 +430,34 @@ 
   [255]
 
   $ cat >> .hg/hgrc <<EOF
+  > [committemplate]
+  > changeset = {desc}
+  >     HG: files={files}
+  >     HG:
+  >     {splitlines(diff()) % ''
+  >    }HG:
+  >     HG: files={files}\n
+  > EOF
+  $ hg status -amr
+  M changed
+  A added
+  R removed
+  $ HGEDITOR=cat hg commit -q -e -m "foo bar" changed
+  foo bar
+  HG: files=changed
+  HG:
+  HG:
+  HG: files=changed
+  $ hg status -amr
+  A added
+  R removed
+  $ hg parents --template "M {file_mods}\nA {file_adds}\nR {file_dels}\n"
+  M changed
+  A 
+  R 
+  $ hg rollback -q
+
+  $ cat >> .hg/hgrc <<EOF
   > # disable customizing for subsequent tests
   > [committemplate]
   > changeset =
diff --git a/tests/test-context.py b/tests/test-context.py
--- a/tests/test-context.py
+++ b/tests/test-context.py
@@ -96,3 +96,25 @@ 
 print 'wctx._status=%s' % (str(wctx._status))
 print actx2.status(other=wctx, listclean=True)
 print 'wctx._status=%s' % (str(wctx._status))
+
+print "== checking workingcommitctx.status:"
+
+wcctx = context.workingcommitctx(repo,
+                                 scmutil.status(['bar-m'],
+                                                ['bar-a'],
+                                                [],
+                                                [], [], [], []),
+                                 text='', date='0 0')
+print 'wcctx._status=%s' % (str(wcctx._status))
+
+print '=== with "always match":'
+actx1.status(other=wcctx)
+print 'wcctx._status=%s' % (str(wcctx._status))
+actx2.status(other=wcctx)
+print 'wcctx._status=%s' % (str(wcctx._status))
+
+print '=== with "always match" and "listclean=True":'
+actx1.status(other=wcctx, listclean=True)
+print 'wcctx._status=%s' % (str(wcctx._status))
+actx2.status(other=wcctx, listclean=True)
+print 'wcctx._status=%s' % (str(wcctx._status))
diff --git a/tests/test-context.py.out b/tests/test-context.py.out
--- a/tests/test-context.py.out
+++ b/tests/test-context.py.out
@@ -24,3 +24,11 @@ 
 wctx._status=<status modified=['bar-m'], added=['bar-a'], removed=['bar-r'], deleted=[], unknown=[], ignored=[], clean=[]>
 <status modified=[], added=['bar-a', 'bar-m'], removed=[], deleted=[], unknown=[], ignored=[], clean=['foo']>
 wctx._status=<status modified=['bar-m'], added=['bar-a'], removed=['bar-r'], deleted=[], unknown=[], ignored=[], clean=[]>
+== checking workingcommitctx.status:
+wcctx._status=<status modified=['bar-m'], added=['bar-a'], removed=[], deleted=[], unknown=[], ignored=[], clean=[]>
+=== with "always match":
+wcctx._status=<status modified=['bar-m'], added=['bar-a'], removed=[], deleted=[], unknown=[], ignored=[], clean=[]>
+wcctx._status=<status modified=['bar-m'], added=['bar-a'], removed=[], deleted=[], unknown=[], ignored=[], clean=[]>
+=== with "always match" and "listclean=True":
+wcctx._status=<status modified=['bar-m'], added=['bar-a'], removed=[], deleted=[], unknown=[], ignored=[], clean=[]>
+wcctx._status=<status modified=['bar-m'], added=['bar-a'], removed=[], deleted=[], unknown=[], ignored=[], clean=[]>