Mercurial > public > mercurial-scm > hg
diff hgext/rebase.py @ 19969:ad9db007656f stable
rebase: fix selection of base used when rebasing merge (issue4041)
Prior this changeset, rebasing a merge whose first parent was not in
the rebase lead to wrong and highly conflicting merge. See the in-line
comment for details.
Test have been updated with the data provided by the reported.
author | Pierre-Yves David <pierre-yves.david@ens-lyon.org> |
---|---|
date | Wed, 30 Oct 2013 19:45:14 +0100 |
parents | 78ab0f85e249 |
children | 2a9bb64faa0b |
line wrap: on
line diff
--- a/hgext/rebase.py Tue Oct 29 21:54:49 2013 +0200 +++ b/hgext/rebase.py Wed Oct 30 19:45:14 2013 +0100 @@ -447,9 +447,44 @@ repo.ui.debug(" already in target\n") repo.dirstate.write() repo.ui.debug(" merge against %d:%s\n" % (repo[rev].rev(), repo[rev])) - base = None - if repo[rev].rev() != repo[min(state)].rev(): + if repo[rev].rev() == repo[min(state)].rev(): + # Case (1) initial changeset of a non-detaching rebase. + # Let the merge mechanism find the base itself. + base = None + elif not repo[rev].p2(): + # Case (2) detaching the node with a single parent, use this parent base = repo[rev].p1().node() + else: + # In case of merge, we need to pick the right parent as merge base. + # + # Imagine we have: + # - M: currently rebase revision in this step + # - A: one parent of M + # - B: second parent of M + # - D: destination of this merge step (p1 var) + # + # If we are rebasing on D, D is the successors of A or B. The right + # merge base is the one D succeed to. We pretend it is B for the rest + # of this comment + # + # If we pick B as the base, the merge involves: + # - changes from B to M (actual changeset payload) + # - changes from B to D (induced by rebase) as D is a rebased + # version of B) + # Which exactly represent the rebase operation. + # + # If we pick the A as the base, the merge involves + # - changes from A to M (actual changeset payload) + # - changes from A to D (with include changes between unrelated A and B + # plus changes induced by rebase) + # Which does not represent anything sensible and creates a lot of + # conflicts. + for p in repo[rev].parents(): + if state.get(p.rev()) == repo[p1].rev(): + base = p.node() + break + if base is not None: + repo.ui.debug(" detach base %d:%s\n" % (repo[base].rev(), repo[base])) # When collapsing in-place, the parent is the common ancestor, we # have to allow merging with it. return merge.update(repo, rev, True, True, False, base, collapse)