diff --git a/mercurial/merge.py b/mercurial/merge.py --- a/mercurial/merge.py +++ b/mercurial/merge.py @@ -723,12 +723,69 @@ def calculateupdates(repo, wctx, mctx, a acceptremote, followcopies): "Calculate the actions needed to merge mctx into wctx using ancestors" - ancestor = ancestors[0] + if len(ancestors) == 1: # default + actions = manifestmerge(repo, wctx, mctx, ancestors[0], + branchmerge, force, + partial, acceptremote, followcopies) + + else: # only when merge.preferancestor=* - experimentalish code + # Call for bids + fbids = {} # mapping filename to list af action bids + for ancestor in ancestors: + repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor) + actions = manifestmerge(repo, wctx, mctx, ancestor, + branchmerge, force, + partial, acceptremote, followcopies) + for a in sorted(actions): + repo.ui.debug(' %s: %s\n' % (a[0], a[1])) + f = a[0] + if f in fbids: + fbids[f].append(a) + else: + fbids[f] = [a] - actions = manifestmerge(repo, wctx, mctx, - ancestor, - branchmerge, force, - partial, acceptremote, followcopies) + # Pick the best bid for each file + repo.ui.note(_('\nauction for merging merge bids\n')) + actions = [] + for f, bidsl in sorted(fbids.items()): + # Consensus? + a0 = bidsl[0] + if util.all(a == a0 for a in bidsl[1:]): # len(bidsl) is > 1 + repo.ui.note(" %s: consensus for %s\n" % (f, a0[1])) + actions.append(a0) + continue + # Group bids by kind of action + bids = {} + for a in bidsl: + m = a[1] + if m in bids: + bids[m].append(a) + else: + bids[m] = [a] + # If keep is an option, just do it. + if "k" in bids: + repo.ui.note(" %s: picking 'keep' action\n" % f) + actions.append(bids["k"][0]) + continue + # If all gets agree [how could they not?], just do it. + if "g" in bids: + ga0 = bids["g"][0] + if util.all(a == ga0 for a in bids["g"][1:]): + repo.ui.note(" %s: picking 'get' action\n" % f) + actions.append(ga0) + continue + # TODO: Consider other simple actions such as mode changes + # Handle inefficient democrazy. + repo.ui.note(_(' %s: multiple merge bids:\n') % (f, m)) + for a in bidsl: + repo.ui.note(' %s: %s\n' % (f, a[1])) + # Pick random action. TODO: Instead, prompt user when resolving + a0 = bidsl[0] + repo.ui.warn(_(' %s: ambiguous merge - picked %s action)\n') % + (f, a0[1])) + actions.append(a0) + continue + repo.ui.note(_('end of auction\n\n')) # Filter out prompts. newactions, prompts = [], [] @@ -926,7 +983,11 @@ def update(repo, node, branchmerge, forc p2 = repo[node] if pas[0] is None: - pas = [p1.ancestor(p2)] + if repo.ui.config("merge", "preferancestor") == '*': + cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node()) + pas = [repo[anc] for anc in (sorted(cahs) or [nullid])] + else: + pas = [p1.ancestor(p2)] fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2) diff --git a/tests/test-merge-criss-cross.t b/tests/test-merge-criss-cross.t --- a/tests/test-merge-criss-cross.t +++ b/tests/test-merge-criss-cross.t @@ -123,4 +123,145 @@ Criss cross merging use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon [1] +Redo merge with merge.preferancestor="*" to enable bid merge + + $ rm f* + $ hg up -qC . + $ hg merge -v --debug --tool internal:dump 5 --config merge.preferancestor="*" + + calculating bids for ancestor 0f6b37dbe527 + searching for copies back to rev 3 + resolving manifests + branchmerge: True, force: False, partial: False + ancestor: 0f6b37dbe527, local: 3b08d01b0ab5+, remote: adfe50279922 + f1: g + f2: m + + calculating bids for ancestor 40663881a6dd + searching for copies back to rev 3 + resolving manifests + branchmerge: True, force: False, partial: False + ancestor: 40663881a6dd, local: 3b08d01b0ab5+, remote: adfe50279922 + f1: m + f2: k + + auction for merging merge bids + f1: picking 'get' action + f2: picking 'keep' action + end of auction + + f1: remote is newer -> g + f2: keep -> k + getting f1 + updating: f1 1/1 files (100.00%) + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + + $ head * + ==> f1 <== + 5 second change + + ==> f2 <== + 6 second change + + +The other way around: + + $ hg up -C -r5 + note: using 0f6b37dbe527 as ancestor of 3b08d01b0ab5 and adfe50279922 + alternatively, use --config merge.preferancestor=40663881a6dd + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg merge -v --debug --config merge.preferancestor="*" + + calculating bids for ancestor 0f6b37dbe527 + searching for copies back to rev 3 + resolving manifests + branchmerge: True, force: False, partial: False + ancestor: 0f6b37dbe527, local: adfe50279922+, remote: 3b08d01b0ab5 + f1: k + f2: m + + calculating bids for ancestor 40663881a6dd + searching for copies back to rev 3 + resolving manifests + branchmerge: True, force: False, partial: False + ancestor: 40663881a6dd, local: adfe50279922+, remote: 3b08d01b0ab5 + f1: m + f2: g + + auction for merging merge bids + f1: picking 'keep' action + f2: picking 'get' action + end of auction + + f1: keep -> k + f2: remote is newer -> g + getting f2 + updating: f2 1/1 files (100.00%) + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + + $ head * + ==> f1 <== + 5 second change + + ==> f2 <== + 6 second change + +Verify how the output looks and and how verbose it is: + + $ hg up -qC + $ hg merge --config merge.preferancestor="*" + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + + $ hg up -qC + $ hg merge -v --config merge.preferancestor="*" + + calculating bids for ancestor 0f6b37dbe527 + resolving manifests + + calculating bids for ancestor 40663881a6dd + resolving manifests + + auction for merging merge bids + f1: picking 'get' action + f2: picking 'keep' action + end of auction + + getting f1 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + + $ hg up -qC + $ hg merge -v --debug --config merge.preferancestor="*" + + calculating bids for ancestor 0f6b37dbe527 + searching for copies back to rev 3 + resolving manifests + branchmerge: True, force: False, partial: False + ancestor: 0f6b37dbe527, local: 3b08d01b0ab5+, remote: adfe50279922 + f1: g + f2: m + + calculating bids for ancestor 40663881a6dd + searching for copies back to rev 3 + resolving manifests + branchmerge: True, force: False, partial: False + ancestor: 40663881a6dd, local: 3b08d01b0ab5+, remote: adfe50279922 + f1: m + f2: k + + auction for merging merge bids + f1: picking 'get' action + f2: picking 'keep' action + end of auction + + f1: remote is newer -> g + f2: keep -> k + getting f1 + updating: f1 1/1 files (100.00%) + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + $ cd ..