comparison 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
comparison
equal deleted inserted replaced
21127:69402eb72115 21128:f4014f646f71
721 721
722 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial, 722 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
723 acceptremote, followcopies): 723 acceptremote, followcopies):
724 "Calculate the actions needed to merge mctx into wctx using ancestors" 724 "Calculate the actions needed to merge mctx into wctx using ancestors"
725 725
726 ancestor = ancestors[0] 726 if len(ancestors) == 1: # default
727 727 actions = manifestmerge(repo, wctx, mctx, ancestors[0],
728 actions = manifestmerge(repo, wctx, mctx, 728 branchmerge, force,
729 ancestor, 729 partial, acceptremote, followcopies)
730 branchmerge, force, 730
731 partial, acceptremote, followcopies) 731 else: # only when merge.preferancestor=* - experimentalish code
732 # Call for bids
733 fbids = {} # mapping filename to list af action bids
734 for ancestor in ancestors:
735 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
736 actions = manifestmerge(repo, wctx, mctx, ancestor,
737 branchmerge, force,
738 partial, acceptremote, followcopies)
739 for a in sorted(actions):
740 repo.ui.debug(' %s: %s\n' % (a[0], a[1]))
741 f = a[0]
742 if f in fbids:
743 fbids[f].append(a)
744 else:
745 fbids[f] = [a]
746
747 # Pick the best bid for each file
748 repo.ui.note(_('\nauction for merging merge bids\n'))
749 actions = []
750 for f, bidsl in sorted(fbids.items()):
751 # Consensus?
752 a0 = bidsl[0]
753 if util.all(a == a0 for a in bidsl[1:]): # len(bidsl) is > 1
754 repo.ui.note(" %s: consensus for %s\n" % (f, a0[1]))
755 actions.append(a0)
756 continue
757 # Group bids by kind of action
758 bids = {}
759 for a in bidsl:
760 m = a[1]
761 if m in bids:
762 bids[m].append(a)
763 else:
764 bids[m] = [a]
765 # If keep is an option, just do it.
766 if "k" in bids:
767 repo.ui.note(" %s: picking 'keep' action\n" % f)
768 actions.append(bids["k"][0])
769 continue
770 # If all gets agree [how could they not?], just do it.
771 if "g" in bids:
772 ga0 = bids["g"][0]
773 if util.all(a == ga0 for a in bids["g"][1:]):
774 repo.ui.note(" %s: picking 'get' action\n" % f)
775 actions.append(ga0)
776 continue
777 # TODO: Consider other simple actions such as mode changes
778 # Handle inefficient democrazy.
779 repo.ui.note(_(' %s: multiple merge bids:\n') % (f, m))
780 for a in bidsl:
781 repo.ui.note(' %s: %s\n' % (f, a[1]))
782 # Pick random action. TODO: Instead, prompt user when resolving
783 a0 = bidsl[0]
784 repo.ui.warn(_(' %s: ambiguous merge - picked %s action)\n') %
785 (f, a0[1]))
786 actions.append(a0)
787 continue
788 repo.ui.note(_('end of auction\n\n'))
732 789
733 # Filter out prompts. 790 # Filter out prompts.
734 newactions, prompts = [], [] 791 newactions, prompts = [], []
735 for a in actions: 792 for a in actions:
736 if a[1] in ("cd", "dc"): 793 if a[1] in ("cd", "dc"):
924 981
925 overwrite = force and not branchmerge 982 overwrite = force and not branchmerge
926 983
927 p2 = repo[node] 984 p2 = repo[node]
928 if pas[0] is None: 985 if pas[0] is None:
929 pas = [p1.ancestor(p2)] 986 if repo.ui.config("merge", "preferancestor") == '*':
987 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
988 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
989 else:
990 pas = [p1.ancestor(p2)]
930 991
931 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2) 992 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
932 993
933 ### check phase 994 ### check phase
934 if not overwrite and len(pl) > 1: 995 if not overwrite and len(pl) > 1: