Simulating an SVN-style merge with Mercurial

posted on 06/28/12 at 07:57:35 am by Joel Ross

Up until about a month ago, we used Subversion as our source control at TrackAbout. For a variety of reasons, we've switched to Mercurial, and so far, I've been extremely happy with the switch. It's been a little bit of a learning process, but using a DVCS on a regular basis has been something I've wanted to do for a while.

Things were going smoothly until I ran into a situation where I needed to merge a change from our development branch back to the latest release branch. It sounded simple enough - I've done it in SVN. Doing it in Mercurial should be just as smooth, right?

Here's my repository as it stands right now. We created a release back at revision 1, and continued doing development and are up to revision 5.


Now, I want to merge just revision 5 into the release branch. My first try (hint: it doesn't work!) was to update my repository to the release branch and run "hg merge -r 5". Running "hg st" shows the following:

HG merging

Our change is in there - file2.txt is added. But file1.txt was also added, even though that was done in revision 4. Also, readme.txt is modified, which was done in revisions 2 and 3. That's not what we wanted! We said to merge revision 5, but it did revisions 2 through 5. With Mercurial, merging a revision from the default branch to the release branch basically means to make the release branch look like the default branch as of revision 5.

After some searching, I found two solutions: transplant, which is an extension that has to be enabled, and graft, which is built in to Mercurial 2.0 and above.  Since graft works out of the box, we'll use that.

Running "hg graft 5" yields the following results:


Our one file, file2.txt, has been moved over. file1.txt has not been moved over, and readme.txt does not include the changes made to the default branch. Running "thg log" shows that the one revision has been "copied" into the release branch.


At this point, I'd be able to push my changes to the central repository with the one revision "merged" into it. One quick note about that: you need to do the push including a revision, otherwise you'll get an error about creating multiple heads. You do that by running "hg push release -r tip" since we want to push just the path that includes tip.

Once we're done, we need to get back to the default branch, and merge our local repository so we no longer have two heads. First, update to the default branch ("hg up default") and then merge the release branch into it ("hg merge release"). Once that's done, commit the merge ("hg ci -m 'merge'"), and our log looks like this:

After Merge

I'm back to a single head, and ready to continue on my way with development. This covers working local, but in most cases, you'll have a remote repository to pull and push to, so below is an in-order list of the commands I use to move a single revision from one branch to another. This assumes we don't have any uncommitted changes (although they may not be pushed anywhere yet). It also assumes that we have two remote branches with aliases devel (for current development) and release (for the latest release).

  • hg pull devel
  • hg up
  • hg pull release
  • hg up release
  • hg graft <revision>
  • hg push release -r tip
  • hg up devel
  • hg merge release
  • hg ci -m 'merge'
  • hg push devel

Once done with the list above, my revisions have been moved to the release branch on the server and I'm back to being able to work against the development branch again.

Categories: Development, Software, TrackAbout, Inc