23

Having two branches, how can I find the latest revision(s) where the two branches were merged? Is there a standard Mercurial command to do that?

This is the same as question How to find the common ancestor of two branches in SVN?, but for Mercurial instead of subversion.


I didn't understand why Lazy Badger's answer was the right one, so I had to make a little drawing, and now I get it:

When two branches are merged, they are not really "merged", but the changes from one branch are integrated into a second branch. This means that the merge commit only belongs to the originating branch, and not to the merged branch. This is why the merge revision is one of the two children of the ancestor revision.

This is probably best seen with a picture:

default o----o----a---b---o---o
         \         \
other     `-o---o---m---o

ancestor(default,other) == a
children(ancestor(default,other)) == (b,m)
children(ancestor(default,other)) and merge() == m
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
daniel kullmann
  • 13,653
  • 8
  • 51
  • 67

7 Answers7

20
hg log -r "children(ancestor(default, Cleanup)) and merge()" --template "{rev}\n"

is latest merge for default and Cleanup branches (polished version of Tim Henigan's answer).

Martin Geisler
  • 72,968
  • 25
  • 171
  • 229
Lazy Badger
  • 94,711
  • 9
  • 78
  • 110
  • +1 for finding the right answer! But I don't understand why I have to use children(). – daniel kullmann Nov 10 '11 at 09:49
  • 1
    ancestor(default, Cleanup) will give you **one parent** of latest merge (I used Tim's code on start). You can at see my repo http://hg.assembla.com/seeker (I'm lazy to show glog output here), which I used to debug. I have to find children of latest merge (one of *many* possible childrens), which is merge – Lazy Badger Nov 10 '11 at 14:24
  • 2
    `hg help revsets` says: "ancestor(single, single)" Greatest common ancestor of the two changesets. Why is that not the merge? – daniel kullmann Nov 10 '11 at 14:31
  • 2
    It's only one *parent* of merge, but you asked about latest **mergeset**, yes? – Lazy Badger Nov 10 '11 at 14:40
4

In recent versions of Mercurial (>1.7), you can do this with revsets:

hg log -r "max(ancestor(<branch1>, <branch2>))"

The same revset filter also works in the Filter toolbar of TortoiseHg.

Tim Henigan
  • 60,452
  • 11
  • 85
  • 78
4

You can rewrite this query to:

  • The closest single changeset that is an ancestor changeset of the tip of both branches

Let's assume you have two branches, identified by R1 and R2, be it the name of the branches or the revision number or hash of a changeset in each, or whatnot.

Then you can find the changeset you're looking for as:

hg log --rev "ancestor(R1,R2)"
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
3

If you have more than two branches then you have to add an additional filter to Lazy Badger's answer, because children() might give you also children that are not in your branches.

hg log -r "children(ancestor(default, Cleanup)) and merge() and branch(default|Cleanup)" --template "{rev}\n"

other     o---o-----m1---o
                   /
default   o---o---a---b---o
                   \
another   o---o-----m2---o

ancestor(default, other) == a
children(ancestor(default, other)) == (m1,b,m2)
children(ancestor(default, other)) and merge() == (m1,m2)
children(ancestor(default, other)) and merge() and branch(default, other) == m1
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
orko
  • 31
  • 1
2

You can find all the merges from a source branch into a destination branch with this revset query:

children(p2(::DESTINATION and merge()) and branch(SOURCE)) and branch(DESTINATION)
  • ::DESTINATION and merge() gets all merges in the destination branch.
  • p2(set) returns the second parent of each merge, which is always the changeset on the source branch which was merged into the destination branch
  • and branch(SOURCE) filters all those merges into just those that came from the source branch.
  • children(set) returns all the children of p2(), one of which is the merge we're looking for
  • and branch(DESTINATION) filters the children of p2() to just show changesets on the destination branch, which, it just so happens, are the merges we're looking for.

So, to get the last merge from source into destination, wrap the above query with the max(set) query. So the final command would be:

max(children(p2(::DESTINATION and merge()) and branch(SOURCE)) and branch(DESTINATION))
Aaron Jensen
  • 25,861
  • 15
  • 82
  • 91
1

Just for the posterity this is not right in the case the left branch has not been merged with the right branch, but the left branch has lately been merged into right.

The correct solution is:

last(parents((ancestors('{left_branch}') and branch('{left_branch}') and merge())) and branch({right_branch}))
piotr
  • 5,657
  • 1
  • 35
  • 60
0
hg log -r "last(ancestors(target) & branch(source))"

This gives you the least common ancestor of target and source branch ON source branch.

source         o---a---o---m2---o
                    \      /
intermediate   o--o--m1---b---o
                               \
target         o---o------------m3---o

The least common ancestor of source and target branch in this example would be b, but b is not on the source branch, we therefore want to get a back.

To get b back you can use the solution of Lasse:

hg log -r "ancestor(source, target)"

I often want to know the latest "default" revision in some task branch and I don't care if my task branch was directly merged with default or via some intermediate path.

Ewaryst Schulz
  • 236
  • 2
  • 4