<div dir="ltr">I just ran into this bug yesterday. Could you mention in what change it was introduced so we know in what range it is broken?<br><br><div class="gmail_quote"><div dir="ltr">On Tue, Mar 15, 2016 at 12:45 PM Laurent Charignon <<a href="mailto:lcharignon@fb.com">lcharignon@fb.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"># HG changeset patch<br>
# User Laurent Charignon <<a href="mailto:lcharignon@fb.com" target="_blank">lcharignon@fb.com</a>><br>
# Date 1458071050 25200<br>
# Tue Mar 15 12:44:10 2016 -0700<br>
# Node ID 98edf167ea7b101efb2bd1c59db53e46c0fd182a<br>
# Parent 70c2f8a982766b512e9d7f41f2d93fdb92f5481f<br>
rebase: persist rebaseskipobsolete state<br>
<br>
Before this patch, when a rebase aborted, we didn't persist the mapping between<br>
obsolete revisions and their successors.<br>
When running hg rebase --continue, we would crash looking for this non-existing<br>
mapping.<br>
<br>
This patch persists the mapping to disk and restores it from disk to avoid this<br>
issue. A test is added for the special case of plain prune revisions.<br>
<br>
I didn't do a test for the following case as I don't know how to create the<br>
marker between B and B' without using evolve:<br>
<br>
O C<br>
|<br>
O B' X B (precursor of B')<br>
| |<br>
O A O R (conflicting changes with A)<br>
| |<br>
| /<br>
| /<br>
| /<br>
|/<br>
O<br>
<br>
$ hg rebase -r "R::" -d B' # aborts<br>
$ ... # resolve and mark conflicts<br>
$ hg rebase --continue<br>
<br>
diff --git a/hgext/rebase.py b/hgext/rebase.py<br>
--- a/hgext/rebase.py<br>
+++ b/hgext/rebase.py<br>
@@ -236,6 +236,7 @@ def rebase(ui, repo, **opts):<br>
# keepopen is not meant for use on the command line, but by<br>
# other extensions<br>
keepopen = opts.get('keepopen', False)<br>
+ obsoletenotrebased = {}<br>
<br>
if opts.get('interactive'):<br>
try:<br>
@@ -266,7 +267,8 @@ def rebase(ui, repo, **opts):<br>
<br>
try:<br>
(originalwd, target, state, skipped, collapsef, keepf,<br>
- keepbranchesf, external, activebookmark) = restorestatus(repo)<br>
+ keepbranchesf, external, activebookmark,<br>
+ obsoletenotrebased) = restorestatus(repo)<br>
collapsemsg = restorecollapsemsg(repo)<br>
except error.RepoLookupError:<br>
if abortf:<br>
@@ -296,7 +298,6 @@ def rebase(ui, repo, **opts):<br>
" unrebased descendants"),<br>
hint=_('use --keep to keep original changesets'))<br>
<br>
- obsoletenotrebased = {}<br>
if ui.configbool('experimental', 'rebaseskipobsolete',<br>
default=True):<br>
rebasesetrevs = set(rebaseset)<br>
@@ -397,7 +398,8 @@ def rebase(ui, repo, **opts):<br>
p1, p2, base = defineparents(repo, rev, target, state,<br>
targetancestors)<br>
storestatus(repo, originalwd, target, state, collapsef, keepf,<br>
- keepbranchesf, external, activebookmark)<br>
+ keepbranchesf, external, activebookmark,<br>
+ obsoletenotrebased)<br>
storecollapsemsg(repo, collapsemsg)<br>
if len(repo[None].parents()) == 2:<br>
repo.ui.debug('resuming interrupted rebase\n')<br>
@@ -888,7 +890,7 @@ def restorecollapsemsg(repo):<br>
return collapsemsg<br>
<br>
def storestatus(repo, originalwd, target, state, collapse, keep, keepbranches,<br>
- external, activebookmark):<br>
+ external, activebookmark, obsoletenotrebased):<br>
'Store the current status to allow recovery'<br>
f = repo.vfs("rebasestate", "w")<br>
f.write(repo[originalwd].hex() + '\n')<br>
@@ -898,6 +900,17 @@ def storestatus(repo, originalwd, target<br>
f.write('%d\n' % int(keep))<br>
f.write('%d\n' % int(keepbranches))<br>
f.write('%s\n' % (activebookmark or ''))<br>
+<br>
+ # rebaseskipobsolete lines follow the format:<br>
+ # xOLDHASH:NEWHASH if NEWHASH is nullid, it means no successor<br>
+ for d, v in obsoletenotrebased.iteritems():<br>
+ oldrev = repo[d].hex()<br>
+ if v is not None:<br>
+ newrev = repo[v].hex()<br>
+ else:<br>
+ newrev = hex(nullid)<br>
+ f.write("x%s:%s\n" %(oldrev, newrev))<br>
+<br>
for d, v in state.iteritems():<br>
oldrev = repo[d].hex()<br>
if v >= 0:<br>
@@ -924,6 +937,7 @@ def restorestatus(repo):<br>
collapse = False<br>
external = nullrev<br>
activebookmark = None<br>
+ obsoletenotrebased = {}<br>
state = {}<br>
<br>
try:<br>
@@ -945,6 +959,14 @@ def restorestatus(repo):<br>
# line 6 is a recent addition, so for backwards compatibility<br>
# check that the line doesn't look like the oldrev:newrev lines<br>
activebookmark = l<br>
+ elif i >= 7 and (len(l) == 82 and ':' in l and l[0] == 'x'):<br>
+ # line 7 is a recent addition, so for backwards compatibility<br>
+ # check that the line doesn't look like the oldrev:newrev lines<br>
+ oldrev, newrev = l[1:].split(':')<br>
+ if newrev == nullid:<br>
+ obsoletenotrebased[repo[oldrev].rev()] = None<br>
+ else:<br>
+ obsoletenotrebased[repo[oldrev].rev()] = repo[newrev].rev()<br>
else:<br>
oldrev, newrev = l.split(':')<br>
if newrev in (str(nullmerge), str(revignored),<br>
@@ -977,7 +999,8 @@ def restorestatus(repo):<br>
repo.ui.debug('rebase status resumed\n')<br>
_setrebasesetvisibility(repo, state.keys())<br>
return (originalwd, target, state, skipped,<br>
- collapse, keep, keepbranches, external, activebookmark)<br>
+ collapse, keep, keepbranches, external, activebookmark,<br>
+ obsoletenotrebased)<br>
<br>
def needupdate(repo, state):<br>
'''check whether we should `update --clean` away from a merge, or if<br>
diff --git a/tests/test-rebase-obsolete.t b/tests/test-rebase-obsolete.t<br>
--- a/tests/test-rebase-obsolete.t<br>
+++ b/tests/test-rebase-obsolete.t<br>
@@ -805,3 +805,60 @@ With experimental.allowdivergence=True,<br>
phases: 8 draft<br>
divergent: 2 changesets<br>
<br>
+rebase --continue + skipped rev because their successors are in destination<br>
+we make a change in trunk and work on conflicting changes to make rebase abort.<br>
+<br>
+ $ hg log -G -r 17::<br>
+ @ 17:61bd55f69bc4 bar foo<br>
+ |<br>
+<br>
+Create the two changes in trunk<br>
+ $ printf "a" > willconflict<br>
+ $ hg add willconflict<br>
+ $ hg commit -m "willconflict first version"<br>
+<br>
+ $ printf "dummy" > C<br>
+ $ hg commit -m "dummy change successor"<br>
+<br>
+Create the changes that we will rebase<br>
+ $ hg update -C 17<br>
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved<br>
+ $ printf "b" > willconflict<br>
+ $ hg add willconflict<br>
+ $ hg commit -m "willconflict second version"<br>
+ created new head<br>
+ $ printf "dummy" > K<br>
+ $ hg add K<br>
+ $ hg commit -m "dummy change"<br>
+ $ printf "dummy" > L<br>
+ $ hg add L<br>
+ $ hg commit -m "dummy change"<br>
+ $ hg debugobsolete `hg log -r ".^" -T '{node}\n'` --config experimental.evolution=all<br>
+<br>
+ $ hg log -G -r 17::<br>
+ @ 22:7bdc8a87673d dummy change<br>
+ |<br>
+ x 21:8b31da3c4919 dummy change<br>
+ |<br>
+ o 20:b82fb57ea638 willconflict second version<br>
+ |<br>
+ | o 19:601db7a18f51 dummy change successor<br>
+ | |<br>
+ | o 18:357ddf1602d5 willconflict first version<br>
+ |/<br>
+ o 17:61bd55f69bc4 bar foo<br>
+ |<br>
+ $ hg rebase -r ".^^ + .^ + ." -d 19<br>
+ rebasing 20:b82fb57ea638 "willconflict second version"<br>
+ merging willconflict<br>
+ warning: conflicts while merging willconflict! (edit, then use 'hg resolve --mark')<br>
+ unresolved conflicts (see hg resolve, then hg rebase --continue)<br>
+ [1]<br>
+<br>
+ $ hg resolve --mark willconflict<br>
+ (no more unresolved files)<br>
+ continue: hg rebase --continue<br>
+ $ hg rebase --continue<br>
+ rebasing 20:b82fb57ea638 "willconflict second version"<br>
+ note: not rebasing 21:8b31da3c4919 "dummy change", it has no successor<br>
+ rebasing 22:7bdc8a87673d "dummy change" (tip)<br>
_______________________________________________<br>
Mercurial-devel mailing list<br>
<a href="mailto:Mercurial-devel@mercurial-scm.org" target="_blank">Mercurial-devel@mercurial-scm.org</a><br>
<a href="https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel" rel="noreferrer" target="_blank">https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel</a><br>
</blockquote></div></div>