Mercurial > public > mercurial-scm > hg-stable
diff mercurial/merge.py @ 21128:f4014f646f71
merge: with merge.preferancestor=*, run an auction with bids from ancestors
The basic idea is to do the merge planning with all the available ancestors,
consider the resulting actions as "bids", make an "auction" and
automatically pick the most favourable action for each file.
This implements the basic functionality and will only consider "keep" and
"get" actions. The heuristics for picking the best action can be tweaked later
on.
By default it will only pass ctx.ancestor as the single ancestor to
calculateupdates. The code path for merging with a single ancestor is not
changed.
author | Mads Kiilerich <madski@unity3d.com> |
---|---|
date | Fri, 28 Feb 2014 02:52:32 +0100 |
parents | 0d67fccc0d43 |
children | 1ce131b3221e |
line wrap: on
line diff
--- a/mercurial/merge.py Fri Feb 28 15:10:56 2014 -0800 +++ b/mercurial/merge.py Fri Feb 28 02:52:32 2014 +0100 @@ -723,12 +723,69 @@ 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 @@ 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)