Patchwork [4,of,4,V2] histedit: remove bootstrap dependencies on parentctx

login
register
mail settings
Submitter Mateusz Kwapich
Date Feb. 3, 2015, 12:32 a.m.
Message ID <497852d2397b0a6f9ad9.1422923549@dev1429.prn1.facebook.com>
Download mbox | patch
Permalink /patch/7615/
State Changes Requested
Headers show

Comments

Mateusz Kwapich - Feb. 3, 2015, 12:32 a.m.
# HG changeset patch
# User Mateusz Kwapich <mitrandir@fb.com>
# Date 1421374093 28800
#      Thu Jan 15 18:08:13 2015 -0800
# Node ID 497852d2397b0a6f9ad9467c64d3441d912a8924
# Parent  0850d3f920a11370d564d5fe9a67c35fc8c347ba
histedit: remove bootstrap dependencies on parentctx

To make histedit more robust to commits disappearing in between actions, let's
make bootstrapcontinue() not depend on the old parentctx existing.
Pierre-Yves David - Feb. 3, 2015, 11:18 a.m.
On 02/03/2015 12:32 AM, Mateusz Kwapich wrote:
> # HG changeset patch
> # User Mateusz Kwapich <mitrandir@fb.com>
> # Date 1421374093 28800
> #      Thu Jan 15 18:08:13 2015 -0800
> # Node ID 497852d2397b0a6f9ad9467c64d3441d912a8924
> # Parent  0850d3f920a11370d564d5fe9a67c35fc8c347ba
> histedit: remove bootstrap dependencies on parentctx
>
> To make histedit more robust to commits disappearing in between actions, let's
> make bootstrapcontinue() not depend on the old parentctx existing.

You are dropping the old behavior (rely on old wc parent existing). But 
what is the new behavior and what does it implies?

Patch

diff --git a/hgext/histedit.py b/hgext/histedit.py
--- a/hgext/histedit.py
+++ b/hgext/histedit.py
@@ -715,29 +715,29 @@ 
     if os.path.exists(repo.sjoin('undo')):
         os.unlink(repo.sjoin('undo'))
 
-def gatherchildren(repo, ctx):
-    # is there any new commit between the expected parent and "."
-    #
-    # note: does not take non linear new change in account (but previous
-    #       implementation didn't used them anyway (issue3655)
-    newchildren = [c.node() for c in repo.set('(%d::.)', ctx)]
-    if ctx.node() != node.nullid:
-        if not newchildren:
-            # `ctx` should match but no result. This means that
-            # currentnode is not a descendant from ctx.
-            msg = _('%s is not an ancestor of working directory')
-            hint = _('use "histedit --abort" to clear broken state')
-            raise util.Abort(msg % ctx, hint=hint)
-        newchildren.pop(0)  # remove ctx
-    return newchildren
+def bootstrapcontinue(ui, state, opts):
+    repo, oldparentctxnode = state.repo, state.parentctxnode
+    action, rulenode = state.rules.pop(0)
+    rulenode = node.bin(rulenode)
+    rulectx = None
+    if rulenode in repo:
+        rulectx = repo[rulenode]
 
-def bootstrapcontinue(ui, state, opts):
-    repo, parentctxnode = state.repo, state.parentctxnode
-    parentctx = repo[parentctxnode]
-    action, currentnode = state.rules.pop(0)
-    ctx = repo[currentnode]
+    # Record new commits between the original parent and the wctx, if any.
+    newchildren = [c.node() for c in repo.set('(%n::. - %n)',
+        oldparentctxnode, oldparentctxnode)]
 
-    newchildren = gatherchildren(repo, parentctx)
+    reusectx = rulectx
+    if not reusectx and action in ('r', 'roll'):
+        # roll is just going to remove the commit anyway, so just use
+        # anything
+        reusectx = repo['.']
+    if not reusectx:
+        raise util.Abort(_("unable to commit pending changes - "
+            "previous commit %s is not available to reuse the commit "
+            "message") % node.short(rulenode))
+
+    parentctxnode = repo['.'].node()
 
     # Commit dirty working directory if necessary
     new = None
@@ -745,26 +745,26 @@ 
     if s.modified or s.added or s.removed or s.deleted:
         # prepare the message for the commit to comes
         if action in ('f', 'fold', 'r', 'roll'):
-            message = 'fold-temp-revision %s' % currentnode[:12]
+            message = 'fold-temp-revision %s' % node.short(rulenode)
         else:
-            message = ctx.description()
+            message = reusectx.description()
         editopt = action in ('e', 'edit', 'm', 'mess')
         canonaction = {'e': 'edit', 'm': 'mess', 'p': 'pick'}
         editform = 'histedit.%s' % canonaction.get(action, action)
         editor = cmdutil.getcommiteditor(edit=editopt, editform=editform)
-        commit = commitfuncfor(repo, ctx)
-        new = commit(text=message, user=ctx.user(), date=ctx.date(),
-                     extra=ctx.extra(), editor=editor)
+        commit = commitfuncfor(repo, reusectx)
+        new = commit(text=message, user=reusectx.user(), date=reusectx.date(),
+                     extra=reusectx.extra(), editor=editor)
         if new is not None:
             newchildren.append(new)
 
     replacements = []
     # track replacements
-    if ctx.node() not in newchildren:
+    if rulenode not in newchildren:
         # note: new children may be empty when the changeset is dropped.
         # this happen e.g during conflicting pick where we revert content
         # to parent.
-        replacements.append((ctx.node(), tuple(newchildren)))
+        replacements.append((rulenode, tuple(newchildren)))
 
     if action in ('f', 'fold', 'r', 'roll'):
         if newchildren:
@@ -777,20 +777,26 @@ 
             if action in ('r', 'roll'):
                 foldopts = foldopts.copy()
                 foldopts['rollup'] = True
-            parentctx, repl = finishfold(ui, repo, parentctx, ctx.node(),
-                                         ctx.description(), new, foldopts,
-                                         newchildren)
+            foldintoctx = None
+            if oldparentctxnode in repo:
+                foldintoctx = repo[oldparentctxnode]
+            else:
+                foldintoctx = repo['.'].parents()[0]
+            parentctx, repl = finishfold(ui, repo, foldintoctx, rulenode,
+                                         reusectx.description(), new,
+                                         foldopts, newchildren)
+            parentctxnode = parentctx.node()
             replacements.extend(repl)
         else:
             # newchildren is empty if the fold did not result in any commit
             # this happen when all folded change are discarded during the
             # merge.
-            replacements.append((ctx.node(), (parentctx.node(),)))
+            replacements.append((rulenode, (parentctxnode,)))
     elif newchildren:
         # otherwise update "parentctx" before proceeding to further operation
-        parentctx = repo[newchildren[-1]]
+        parentctxnode = repo[newchildren[-1]].node()
 
-    state.parentctxnode = parentctx.node()
+    state.parentctxnode = parentctxnode
     state.replacements.extend(replacements)
 
     return state
diff --git a/tests/test-histedit-arguments.t b/tests/test-histedit-arguments.t
--- a/tests/test-histedit-arguments.t
+++ b/tests/test-histedit-arguments.t
@@ -103,35 +103,6 @@ 
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   $ hg up --quiet
 
-Run on a revision not descendants of the initial parent
---------------------------------------------------------------------
-
-Test the message shown for inconsistent histedit state, which may be
-created (and forgotten) by Mercurial earlier than 2.7. This emulates
-Mercurial earlier than 2.7 by renaming ".hg/histedit-state"
-temporarily.
-
-  $ HGEDITOR=cat hg histedit -r 4 --commands - << EOF
-  > edit 08d98a8350f3 4 five
-  > EOF
-  1 files updated, 0 files merged, 0 files removed, 0 files unresolved
-  reverting alpha
-  Make changes as needed, you may commit or record as needed now.
-  When you are finished, run hg histedit --continue to resume.
-  [1]
-
-  $ mv .hg/histedit-state .hg/histedit-state.back
-  $ hg update --quiet --clean 2
-  $ mv .hg/histedit-state.back .hg/histedit-state
-
-  $ hg histedit --continue
-  abort: c8e68270e35a is not an ancestor of working directory
-  (use "histedit --abort" to clear broken state)
-  [255]
-
-  $ hg histedit --abort
-  $ hg update --quiet --clean
-
 Test that missing revisions are detected
 ---------------------------------------
 
diff --git a/tests/test-histedit-edit.t b/tests/test-histedit-edit.t
--- a/tests/test-histedit-edit.t
+++ b/tests/test-histedit-edit.t
@@ -343,3 +343,5 @@ 
   $ HGEDITOR=true hg histedit --continue
   0 files updated, 0 files merged, 0 files removed, 0 files unresolved
   saved backup bundle to $TESTTMP/r0/.hg/strip-backup/cb9a9f314b8b-cc5ccb0b-backup.hg (glob)
+
+
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
@@ -457,3 +457,50 @@ 
   abort: cannot edit history that contains merges
   [255]
   $ cd ..
+
+Run continue on amended parent
+------------------------------
+Test if histedit can succesfully continue after amending initial parent
+in the middle of histedit.
+
+  $ cat > $TESTTMP/histedit-edit-editor.py <<EOF
+  > #!/usr/bin/env python
+  > import sys
+  > filename = sys.argv[1]
+  > with open(filename) as f:
+  >     line1 = f.readline()
+  >     line2 = f.readline()
+  >     line3 = f.readline()
+  > with open(filename, "w") as f:
+  >     f.write(line1)
+  >     f.write(line2.replace('pick', 'edit'))
+  >     f.write(line3)
+  > EOF
+  $ chmod +x $TESTTMP/histedit-edit-editor.py
+
+  $ hg init histedittest
+  $ cd histedittest
+  $ echo "abc" > file1
+  $ echo "123" > file2
+  $ hg add file1 file2
+  $ hg ci -m "initial"
+  $ echo "def" >> file1
+  $ echo "foo" >> file2
+  $ hg ci -m "commit 1"
+  $ echo "456" >> file2
+  $ hg ci -m "commit 2"
+  $ HGEDITOR="$TESTTMP/histedit-edit-editor.py" hg histedit tip~2
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  reverting file1
+  reverting file2
+  Make changes as needed, you may commit or record as needed now.
+  When you are finished, run hg histedit --continue to resume.
+  [1]
+  $ hg commit --amend -m "initial" file1
+  $ hg histedit --continue --traceback --config extensions.histedit=/data/users/mitrandir/hg/hgext/histedit.py
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ hg log
+  6:911fe08e3eea (draft) commit 2
+  5:77b81cdf6a31 (draft) commit 1
+  4:d133778d3f09 (draft) initial