6

I'm trying to do a complicated merge in a complicated hg repository. I'm not happy with the "newest shared ancestor" that Mercurial chooses to use as the "base" to perform the merge.

I'd like to specify a specific commit of my own choice to use as base.

Is this possible, and if so, how?

Martin Geisler
  • 72,968
  • 25
  • 171
  • 229
Lucas Meijer
  • 4,424
  • 6
  • 36
  • 53

3 Answers3

15

Mercurial 3.0: You can now select the ancestor to use as a merge base. You do that by setting merge.preferancestor. Mercurial will tell you about it when this makes sense. With the example below, you would see:

$ hg merge
note: using eb49ad46fd72 as ancestor of 333411d2f751 and 7d1f71140c74
      alternatively, use --config merge.preferancestor=fdf4b78f5292
merging x
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)

Mercurial before version 3.0: Lazy Badger is correct that you cannot pick the ancestor picked by Mercurial when using it from the command line. However, you can do it internally and it's not too difficult to write an extension for this:

from mercurial import extensions, commands, scmutil
from mercurial import merge as mergemod

saved_ancestor = None

def update(orig, repo, node, branchmerge, force, partial, ancestor=None):
    if saved_ancestor:
        ancestor = scmutil.revsingle(repo, saved_ancestor).node()
    return orig(repo, node, branchmerge, force, partial, ancestor)

def merge(orig, ui, repo, node=None, **opts):
    global saved_ancestor
    saved_ancestor = opts.get('ancestor')
    return orig(ui, repo, node, **opts)

def extsetup(ui):
    extensions.wrapfunction(mergemod, 'update', update)
    entry = extensions.wrapcommand(commands.table, 'merge', merge)
    entry[1].append(('', 'ancestor', '', 'override ancestor', 'REV'))

Put this in a file and load the extension. You can now use

hg merge --ancestor X

to override the normal ancestor. As you've found out, this does make a difference if there are several possible ancestors. That situation arises if you have criss-cross merges. You can create such a case with these commands:

hg init; echo a > x; hg commit -A -m a x
hg update 0; echo b >> x; hg commit -m b
hg update 0; echo c >> x; hg commit -m c
hg update 1; hg merge --tool internal:local 2; echo c >> x; hg commit -m bc
hg update 2; hg merge --tool internal:local 1; echo b >> x; hg commit -m cb

The graph looks like this:

@    changeset: 4:333411d2f751
|\
+---o  changeset: 3:7d1f71140c74
| |/
| o  changeset: 2:fdf4b78f5292
| |
o |  changeset: 1:eb49ad46fd72
|/
o  changeset: 0:e72ddea4d238

If you merge normally you get changeset eb49ad46fd72 as the ancestor and the file x contains:

a
c
b
c

If you instead use hg merge --ancestor 2 you get a different result:

a
b
c
b

In both cases, my KDiff3 were able to handle the merge automatically without reporting any conflicts. If I use the "recursive" merge strategy and pick e72ddea4d238 as the ancestor, then I'm presented with a sensible conflict. Git uses the recursive merge strategy by default.

Martin Geisler
  • 72,968
  • 25
  • 171
  • 229
  • It's a WOW! Works perfect. Thank you very much. In fact, I don't understand why this feature is not included into Mercurial. In most cases one won't need it, but in cases when it's necessary it can save hours of conflicts resolving, or even prevent incorrect auto merges. I've just encountered such issues after inaccurate changes to the repo. – Pavel Gatilov Mar 07 '13 at 10:12
1

Base is just used as another input to your merge tool. If you disable premerge in your Merge Tool Configuration (premerge makes the "obvious choices" for you when there are no conflicts) and invoke your merge tool manually providing copies of the 3 revisions you want as local, remote, and base, you can get whatever you want in your merge tool. Only the left parent and right parent are actually recorded in the merge.

Ry4an Brase
  • 78,112
  • 7
  • 148
  • 169
  • yeah, I could go the totally manual route, and for each mergeconflict, manually copy paste in the base I want. I'm looking at 20+ merge conflicts though, and figured we must be able to figure out something automatic. – Lucas Meijer Feb 19 '12 at 17:28
-1

You can't do it. Because newest shared ancestor IS real base for your merge

If you want to perform merge and don't want re-think (because your logic-base show /me/ wrong assumptions and solution-path) you can go clone-rebase-merge-export-import patch route

Lazy Badger
  • 94,711
  • 9
  • 78
  • 110
  • 3
    there can be multiple candidate shared ancestors that are "equally fine", but different. see http://man.he.net/man1/git-merge-base – Lucas Meijer Feb 19 '12 at 17:23
  • @LucasMeijer - 1. we said about Mercurial, not Git 2. They **aren't** "equally fine", because newest ancestor is *"finer"* – Lazy Badger Feb 19 '12 at 17:35
  • 2
    What is your reason to know that the newest is _always_ the best one. Imo, there is no way to know that. (allthough usually the newest is the best). the git link merely illustrates a situation where there is no "best", a situation which can happen in mercurial just the same. – Lucas Meijer Feb 19 '12 at 18:09
  • 1. Mercurial have not octopus-merge 2. Two *diverged* nodes must have common parent (where separation still not happened) 3. Newest parent is best, because it *doesn't contain excessive chunks*, irrelevant mergeable data – Lazy Badger Feb 19 '12 at 18:26