What the merge

Posted on March 15, 2011

Analyze merges in Mercurial to see what a colleague did with your changes

We all love merging

A great feature of a DVCS like Mercurial is that it makes merging concurrent lines of development so easy. Indeed merges occur quite often because the local workflow of a DVCS motivates to commit often and to deal with the synchronization of changes from coworkers at a later time, when you're done with your current task. Then, merging usually is straight forward. Most concurrent changes get merged automatically. The rest is easy to handle manually when you follow the principle to merge regularly.

What happened to my changes?

However, there's one aspect of merging that is not so obvious to handle in Mercurial. Suppose you and your colleague -- Bob -- work on the same file (and in that file on the same part). You commit and push first, everything is fine. Then Bob wants to push his commits too but he get's a warning about creating a new remote head. As an exemplary developer Bob decides to pull and merge the remote changes before pushing. During the merge he manually merges the conflicting changes made by him and you. Subsequent he pushes the result, which might look like this:

No alternative text available

Both you and Bob worked on the file README. Bob merged the conflicting changes. Now you wonder if and how Bob's merge affected things you've worked on. And here's the problem: there's no straight forward way to inspect the merge with regard to that question.

The first idea one might have is to inspect the difference between R2 and R5:

$ hg diff -r 2:5

However, while this shows what Bob did with your changes during the merge, it may be hard to find these pieces among the other changes shown:

--- a/README    ...
+++ b/README    ...
@@ -1,1 +1,1 @@
-hey world
+hey folks
--- /dev/null       ...
+++ b/util.c        ...
@@ -0,0 +1,1 @@
... many lines of code ...

The problem is that the difference between R2 and R5 includes everything Bob did since the common base revision R0, i.e. R3 and R4.

The commands hg diff -r 4:5 and hg diff -c 5 work the other way around, i.e. they include the changes from R1 and R2.

Show a partial diff of the merge

The information you should get served in this situation is the fact that Bob changed your edits to the README file. The trick is to limit the output of the command

$ hg diff -r 2:5

to only consider the files which actually have been touched during the merge. You get this list of files using the log command with templates:

$ hg log --template '{files}' -r 5
README

The important thing here is that neither util.c nor main.c are in this list. Now, feed this list to the diff command:

$ hg diff -r 2:5 `hg log --template '{files}' -r 5`
--- a/README    ...
+++ b/README    ...
@@ -1,1 +1,1 @@
-hey world
+hey folks

That's it, all other changes from Bob (you're not interested in right now) are filtered. To complete the merge inspection, you could also check what Bob did with his own changes during the merge:

$ hg diff -r 4:5 `hg log --template '{files}' -r 5`
--- a/README        ...
+++ b/README        ...
@@ -1,1 +1,1 @@
-hello folks
+hey folks

Now you see that Bob was not so selfish to use his changes only but combined his and your changes.

This post is based on an answer I gave to a related question on stackoverflow.com.