histedit: fix new nodes computation with --continue (issue3534)
When running the following actions:
pick 617f94f13c0f 1 +4
drop 888f9082bf99 2 +5
fold 251d831eeec5 3 +6
if the fold fails, is fixed by the user with a new changeset, --continue
will ignore the new revision when generating the fold changelog. This
was caused by --continue detecting new changesets as descendants of the
parent not descendants of changesets in the initial list. In this case,
dropped changesets must be ignored.
Even with the computation fixed, the 'newchildren' list was always
emptied by the filtering loop and passed empty to finishfold().
Note that changesets dropped and recreated identically will still be
missed. This probably cannot be solved but is unlikely to happen.
Other things, like 'newchildren' having multiple heads, should be
checked as well.
--- a/hgext/histedit.py Wed Jul 25 11:09:51 2012 +0200
+++ b/hgext/histedit.py Wed Jul 25 16:27:26 2012 +0200
@@ -430,19 +430,26 @@
tmpnodes, existing, rules, keep, tip, replacemap) = readstate(repo)
currentparent, wantnull = repo.dirstate.parents()
parentctx = repo[parentctxnode]
- # discover any nodes the user has added in the interim
- newchildren = [c for c in parentctx.children()
- if c.node() not in existing]
+ # existing is the list of revisions initially considered by
+ # histedit. Here we use it to list new changesets, descendants
+ # of parentctx without an 'existing' changeset in-between. We
+ # also have to exclude 'existing' changesets which were
+ # previously dropped.
+ descendants = set(c.node() for c in
+ repo.set('(%n::) - %n', parentctxnode, parentctxnode))
+ existing = set(existing)
+ notdropped = set(n for n in existing if n in descendants and
+ (n not in replacemap or replacemap[n] in descendants))
+ # Discover any nodes the user has added in the interim. We can
+ # miss changesets which were dropped and recreated the same.
+ newchildren = list(c.node() for c in repo.set(
+ 'sort(%ln - (%ln or %ln::))', descendants, existing, notdropped))
action, currentnode = rules.pop(0)
- while newchildren:
- if action in ('f', 'fold'):
- tmpnodes.extend([n.node() for n in newchildren])
- else:
- created.extend([n.node() for n in newchildren])
- filtered = []
- for r in newchildren:
- filtered += [c for c in r.children() if c.node not in existing]
- newchildren = filtered
+ if action in ('f', 'fold'):
+ tmpnodes.extend(newchildren)
+ else:
+ created.extend(newchildren)
+
m, a, r, d = repo.status()[:4]
oldctx = repo[currentnode]
message = oldctx.description()
--- a/tests/test-histedit-fold.t Wed Jul 25 11:09:51 2012 +0200
+++ b/tests/test-histedit-fold.t Wed Jul 25 16:27:26 2012 +0200
@@ -183,3 +183,56 @@
$ cd ..
+
+Test corner case where folded revision is separated from its parent by a
+dropped revision.
+
+
+ $ hg init fold-with-dropped
+ $ cd fold-with-dropped
+ $ printf "1\n2\n3\n" > file
+ $ hg commit -Am '1+2+3'
+ adding file
+ $ echo 4 >> file
+ $ hg commit -m '+4'
+ $ echo 5 >> file
+ $ hg commit -m '+5'
+ $ echo 6 >> file
+ $ hg commit -m '+6'
+ $ hg log -G --template '{rev}:{node|short} {desc|firstline}\n'
+ @ 3:251d831eeec5 +6
+ |
+ o 2:888f9082bf99 +5
+ |
+ o 1:617f94f13c0f +4
+ |
+ o 0:0189ba417d34 1+2+3
+
+ $ EDITED=`pwd`/../editcommands
+ $ cat > $EDITED <<EOF
+ > pick 617f94f13c0f 1 +4
+ > drop 888f9082bf99 2 +5
+ > fold 251d831eeec5 3 +6
+ > EOF
+ $ HGEDITOR="cat $EDITED >" hg histedit 1
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ patching file file
+ Hunk #1 FAILED at 2
+ 1 out of 1 hunks FAILED -- saving rejects to file file.rej
+ abort: Fix up the change and run hg histedit --continue
+ [255]
+ $ echo 5 >> file
+ $ hg commit -m '+5.2'
+ created new head
+ $ echo 6 >> file
+ $ HGEDITOR=cat hg histedit --continue
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ +4
+ ***
+ +5.2
+ ***
+ +6
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ saved backup bundle to $TESTTMP/fold-with-dropped/.hg/strip-backup/617f94f13c0f-backup.hg (glob)
+ $ cd ..
+