[PATCH v3] histedit: make histedit aware of obsoletions not stored in state (issue4800)

Kostia Balytskyi ikostia at fb.com
Mon Feb 22 16:16:14 UTC 2016


# HG changeset patch
# User Kostia Balytskyi <ikostia at fb.com>
# Date 1456157593 0
#      Mon Feb 22 16:13:13 2016 +0000
# Node ID 5042d9526f0328f8a43eeeb9801c0aa4df3659bf
# Parent  28e1694ca60056d609ae2c8e0ad5cb2891416ea3
histedit: make histedit aware of obsoletions not stored in state (issue4800)

Before this change, when histedit exited to interactive session (during edit
command for example), user could introduce obsolescence markers that would not
be known to histedit. For example, user could've amended one of the commits
(introducing an obsoletion change). The fact of this amendment would not be
stored in histedit's state file and later, when histedit would try to process
all the replacements that happened, one of the final successors (in its opinion)
would turn out to be hidden. This behavior is described in issue4800. This
commit fixes it.

diff --git a/hg b/hg
--- a/hg
+++ b/hg
@@ -39,5 +39,4 @@
 
 for fp in (sys.stdin, sys.stdout, sys.stderr):
     mercurial.util.setbinary(fp)
-
 mercurial.dispatch.run()
diff --git a/hgext/histedit.py b/hgext/histedit.py
--- a/hgext/histedit.py
+++ b/hgext/histedit.py
@@ -1386,6 +1386,36 @@
                 hint=_('use "drop %s" to discard, see also: '
                        '"hg help -e histedit.config"') % missing[0][:12])
 
+def adjustreplacementsfrommarkers(repo, allsuccs, replaced, fullmapping):
+    """Adjusts replacement structures with data from obsoletion markers
+
+    These structures are originally generated only based on
+    histedit state and do not account for changes that are
+    not recorded there. This function fixes that"""
+    if not obsolete.isenabled(repo, obsolete.createmarkersopt):
+        return
+
+    succstocheck = list(allsuccs)
+    unfi = repo.unfiltered()
+    while succstocheck:
+        n = succstocheck.pop()
+        try:
+            ctx = unfi[n]
+        except error.RepoError:
+            # node unknown locally, should be treated as replaced
+            replaced.add(n)
+            continue
+
+        for marker in obsolete.successormarkers(ctx):
+            replaced.add(n)
+            for nsucc in marker.succnodes():
+                # nsucc is a successor of n
+                if nsucc not in allsuccs:
+                    # save nsucc for further investigation
+                    allsuccs.add(nsucc)
+                    succstocheck.append(nsucc)
+                fullmapping.setdefault(n, set()).add(nsucc)
+
 def processreplacement(state):
     """process the list of replacements to return
 
@@ -1402,6 +1432,7 @@
         allsuccs.update(rep[1])
         replaced.add(rep[0])
         fullmapping.setdefault(rep[0], set()).update(rep[1])
+    adjustreplacementsfrommarkers(state.repo, allsuccs, replaced, fullmapping)
     new = allsuccs - replaced
     tmpnodes = allsuccs & replaced
     # Reduce content fullmapping into direct relation between original nodes
diff --git a/tests/test-histedit-obsolete.t b/tests/test-histedit-obsolete.t
--- a/tests/test-histedit-obsolete.t
+++ b/tests/test-histedit-obsolete.t
@@ -14,6 +14,36 @@
   > rebase=
   > EOF
 
+Test that histedit learns about obsoletions not stored in histedit state
+  $ hg init boo
+  $ cd boo
+  $ echo a > a
+  $ hg ci -Am a
+  adding a
+  $ echo a > b
+  $ echo a > c
+  $ echo a > c
+  $ hg ci -Am b
+  adding b
+  adding c
+  $ echo a > d
+  $ hg ci -Am c
+  adding d
+  $ echo "pick `hg log -r 0 -T '{node|short}'`" > plan
+  $ echo "pick `hg log -r 2 -T '{node|short}'`" >> plan
+  $ echo "edit `hg log -r 1 -T '{node|short}'`" >> plan
+  $ hg histedit -r 'all()' --commands plan
+  Editing (1b2d564fad96), you may commit or record as needed now.
+  (hg histedit --continue to resume)
+  [1]
+  $ hg st
+  A b
+  A c
+  ? plan
+  $ hg commit --amend b
+  $ hg histedit --continue
+  $ cd ..
+
   $ hg init base
   $ cd base
 


More information about the Mercurial-devel mailing list