Thursday, 1 November 2012

Using Named Branches in Mercurial

Mercurial offers a variety of approaches to branching, including “named branches”, “bookmarks” (most similar to git), “anonymous branches” and using clones. For a good comparison of these techniques, I suggest reading Steve Losh’s Guide to Mercurial Branching, which explains it well although is a little out of date now.

In this article I will walk through the process of how you can use a named branch for maintenance releases, and the workflow for both contributors and for accepting contributions. I’ll explain at the end why I’ve decided that named branches are the best option for NAudio.

Step 1: Creating an initial repository

We’ll start with a fresh repository with just two commits

hg init
// make changes
hg commit -m "first version"
// make changes
hg commit -m "version 1.0 release"

Now we’ve released version 1, let’s start work on version 2. No branches have been made yet.

// more changes
hg commit -m "beginning work on v2"

Step 2: Creating a Maintenance Branch

Now we’ve had a bug report and need to fix version 1, without shipping any of our version 2 changes. We’ll create a branch by going back to the v1.0 commit (revision 1 in our repository) and using the branch command

// go back to v1 release
hg update 1

// create a named branch
hg branch "v1.0 maintenance"

// branch will be created when we commit to it
// fix the bug
hg commit -m "bugfix for v1.0"

Step 3: Switching between branches

To get back to working on our main branch (which is called the default branch in Mercurial), we simply use the update command:

// get back onto the v2 branch:
hg update default
// make changes
hg commit -m "more work on v2"

Step 4: Making forks

Imagine at this point, we have a couple of contributors, who want to fork our repository and make some changes.

Our first contributor makes a clone, and is contributing a v2 feature, so they can simply commit to the default branch:

hg clone public-repo-address my-feature-fork
hg update default // not actually needed
hg commit -m "contributing a new feature in v2"

Our second contributor is offering a bugfix, so they must remember to switch to the named maintenance branch (they can use hg branches to see what branch names are available):

hg clone public-repo-address my-bugfix-fork
hg update "v1.0 maintenance"
hg commit -m "contributing a bugfix for v1.0"

Their commit will me marked as being on the v1.0 maintenance branch (as named branches are stored in the commits, unlike git branches which are simply pointers to commits)

Step 5: Accepting Contributions

If our contributors issued pull requests now, things would be nice and easy, but let’s imagine that more work has gone on in both branches in the meantime:

hg update default
hg commit -m "another change on v2 after the fork"

hg update "v1.0 maintenance"
hg commit -m "another v1.0 bugfix after the fork"

First, lets pull in the new v2.0 feature (n.b. it is often a good idea to use a local integration clone so that if you want to reject the contribution you can do so easily).

hg pull newfeaturefork
// need to be on the default branch to merge
hg update default
hg merge
// resolve any merge conflicts
hg commit -m "merged in the new feature"

Now we can do the same with the contribution on the maintenance branch (n.b. hg merge won’t do anything if you are still on the default branch, as it knows that the contribution is on a different branch):

hg pull bugfixfork
// get onto the branch we are merging into
hg update "v1.0 maintenance"
hg merge
hg commit -m "merged in a bugfix"

Step 6: Merging from maintenance branch into default

We have a few bugfixes now in our v1.0 branch, and we’d like to get them into v2.0 as well. How do we do that? Go to the default branch and ask to merge from the maintenance branch.

hg update default
hg merge "v1.0 maintenance"
// fix conflicts
hg commit -m "merged in v1.0 bugfixes"

And that is pretty much all you need to know to work with named branches. With your repository in this state you still have two branches (default and v1.0 maintenance) which you can continue work on separately. Here’s a screenshot of a repository which has had the steps above performed on it:

 image

Why Named branches?

I actually think that branching with clones is easier for users to understand than named branches, but for NAudio it is not a viable option. This is because CodePlex allows only one repository per project. I’d either have to create a new project for each NAudio maintenance branch, or create a fork, but both options would cause confusion.

Anonymous branches are a bad idea because you open the door to merge the wrong things together, and it’s hard to keep track of which head relates to what. Their use is mainly limited to short-lived, experimental branches.

Bookmarks are appealing as they are closest to the git philosophy, but because Mercurial requires you to explicitly ask to push them, and there can be naming collisions, I think they are best used simply as short-lived markers for local development (I might do another blog post on the workflow for this).

So with NAudio I am thinking of creating a single maintenance branch for each major release (only created when it is needed). Most people who fork can just ignore the maintenance branch and work exclusively in the default branch (I can always use hg transplant to move a fix into a maintenance branch).

Post a Comment