[PATCH] histedit: handle obsolete commits unknown to histedit state (issue4800)
Kostia Balytskyi
ikostia at fb.com
Mon Jan 18 12:02:22 UTC 2016
# HG changeset patch
# User Kostia Balytskyi <ikostia at fb.com>
# Date 1453118372 0
# Mon Jan 18 11:59:32 2016 +0000
# Node ID 289562ec0ef6a732c54900842224a04932009d05
# Parent 443848eece189002c542339dc1cf84f49a94c824
histedit: handle obsolete commits unknown to histedit state (issue4800)
This fix takes sets of final and new commits from
histedit's state and replaces them with their successor sets.
diff --git a/hgext/histedit.py b/hgext/histedit.py
--- a/hgext/histedit.py
+++ b/hgext/histedit.py
@@ -1362,6 +1362,32 @@
tmpnodes = allsuccs & replaced
return newnodes, tmpnodes
+def nonobsoletesuccessors(repo, nodes):
+ """find all non-obsolete successors for each of the nodes
+
+ This fails if any of the nodes has more than one successor
+ set, e.g. if it diverged at some point and then became obsolete.
+ """
+ if not obsolete.isenabled(repo, obsolete.createmarkersopt):
+ # no obsoletion is enabled, no successors should exist
+ return nodes
+
+ result = []
+ for cl in nodes:
+ succset = obsolete.successorssets(repo, cl)
+ if len(succset) > 1:
+ msg = _("changeset %s split or divergent,"
+ " cannot determine unique successor")
+ hint =_("solve the issue and run histedit --continue"
+ " or abort with histedit --abort")
+ raise error.Abort(msg % node.hex(cl), hint=hint)
+ elif len(succset) == 1:
+ # non-divergent successor set
+ result += succset[0]
+ else:
+ # changeset has been pruned, nothing needs to be done
+ pass
+ return result
def processreplacement(state):
"""process the list of replacements to return
@@ -1408,11 +1434,13 @@
# turn `final` into list (topologically sorted)
nm = state.repo.changelog.nodemap
for prec, succs in final.items():
+ succs = nonobsoletesuccessors(state.repo, succs)
final[prec] = sorted(succs, key=nm.get)
+ nonobsoletenew = nonobsoletesuccessors(state.repo, new)
# computed topmost element (necessary for bookmark)
- if new:
- newtopmost = sorted(new, key=state.repo.changelog.rev)[-1]
+ if nonobsoletenew:
+ newtopmost = sorted(nonobsoletenew, key=state.repo.changelog.rev)[-1]
elif not final:
# Nothing rewritten at all. we won't need `newtopmost`
# It is the same as `oldtopmost` and `processreplacement` know it
@@ -1422,7 +1450,7 @@
r = state.repo.changelog.rev
newtopmost = state.repo[sorted(final, key=r)[0]].p1().node()
- return final, tmpnodes, new, newtopmost
+ return final, tmpnodes, nonobsoletenew, newtopmost
def movebookmarks(ui, repo, mapping, oldtopmost, newtopmost):
"""Move bookmark from old to newly created node"""
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
@@ -477,3 +477,33 @@
# f, fold = use commit, but combine it with the one above
# r, roll = like fold, but discard this commit's description
#
+
+Ensure that creating obsole revisions while editing commits does not break histedit
+
+ $ hg init boo
+ $ cd boo
+ $ echo a > aaaa
+ $ hg ci -Am a
+ adding aaaa
+ $ echo a > bbbb
+ $ echo a > cccc
+ $ echo a > cccc
+ $ hg ci -Am b
+ adding bbbb
+ adding cccc
+ $ echo a > dddd
+ $ hg ci -Am c
+ adding dddd
+ $ echo "pick `hg log -r -1 -T '{node|short}'`" > plan
+ $ echo "pick `hg log -r -3 -T '{node|short}'`" >> plan
+ $ echo "edit `hg log -r -2 -T '{node|short}'`" >> plan
+ $ hg histedit -qr 'all()' --commands plan
+ Editing \([0-9a-f]{12}\), you may commit or record as needed now. (re)
+ (hg histedit --continue to resume)
+ [1]
+ $ hg st
+ A bbbb
+ A cccc
+ ? plan
+ $ hg commit --amend bbbb
+ saved backup bundle to $TESTTMP/r0/boo/.hg/strip-backup/*-amend-backup.hg (glob)
More information about the Mercurial-devel
mailing list