Three simple Mercurial rules

The following is highly opinionated, but it matches most closely to what typical Git workflows are.  The nice thing about Hg is that its tools can hide the complexity of working with distributed version control systems (DVCS).  That’s also a drawback, because if you don’t know what’s actually going on behind the scenes with your commits, you can get into some rather strange situations and complicated commit graphs:


This is actually a relatively sane portion of the MVCContrib project.  Now, dealing with public repositories with forks is a little bit different than internal team repositories, but the rules are quite similar.  Here’s a picture from a team-based project, working on one line of development:


Uh…what?  There’s only supposed to be one “line” of development, one truth of the current version, but can anyone look at this picture and have any idea what’s going on?  I sure can’t.

But we can avoid spiderweb commit graphs and have a sane understanding of the progression of our software, with a three easy rules:

  1. Separate parallel areas of work with separate named branches
  2. Rebase local commits on the same named branch for incoming changes, merge between named branches.  A named branch should never merge with itself.
  3. Push and pull only one branch, not the entire repository

So how does this translate into our daily workflow?

The daily workflow

When we’re first starting our work, we have to decide if this is a separate area of work we’re working on, or is this part of an existing line of work.  If it’s existing, we can work off of that branch.  If it’s new, we’ll start a new branch.  Our workflow then is:

  • hg branch MyNewFeature
  • work work work
  • hg commit -Am “Committing whatever”
  • work work work
  • hg commit -Am “More work”

At this point, we want to push our work up to the remote server.   But we only ever want to push our current work, not everything we’ve ever done.  I really never push my entire repository, but my current line of work.  The reasoning is that pushing the entire repository assumes I’m integrating multiple lines of work.  But I only want to integrate my current line of work, and I only want to work in one line at a time.

If this is the first time I’ve pushed this branch:

  • hg push -b . –new-branch

If I’ve already pushed this branch:

  • hg push -b .

The “-b .” command means just push the current branch, and not anything else.  That’s #3 in our rules.

But what about integrating upstream changes?

  • hg pull -b . –rebase

I pull in ONLY the current branch I’m working on, rebasing my changes on top of incoming commits.  This ensures a single, linear commit history for every named branch.  A named branch never merges with itself, with is the source of most of the confusion and problems with DVCS I see these days.  People need to correct some change in the history, and can’t figure out what has happened because of the crazy commit history.  I don’t care what other people are working on, I’m only concerning myself with integrating the work that I’m doing.

What happens when we want to merge our line of work into a different line (such as a trunk/master/default branch)?

  • hg merge trunk
  • hg commit -Am “Merging trunk” –close-branch
  • hg push -b .
  • hg checkout trunk
  • hg merge MyNewFeature
  • hg commit -Am “Merging MyNewFeature”
  • hg push -b .

What’s the end result of this picture?  A commit history that logically separates lines of work into single, linear commit histories.  Named branches separate logical areas of focus.  The begin and end of logical areas of work are clearly defined in our commit history.

The nice thing about this workflow is that it doesn’t assume any sort of branching strategy – if you have branches per story/developer/release or whatever.  It just defines a manner of working effectively in a single branch, and rules for integrating changes between branches.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in Mercurial. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • So have you stopped using bookmarks, or are you still using them for local topic branches that never end up getting pushed?

    I’ve found that bookmarks are nice because they’re lightweight, but I run into trouble when something I thought was going to be a short-lived topic “branch” ends up needing to be pushed and maintained as a slightly longer-lived separate line of development.

    TeamCity in particular makes me consider using branches more often, since I can’t create a new build based on a bookmark, but I can with a branch.

  • That’s pretty close to the workflow that has worked for us – one of my coworkers blogged about it at

    Win because it works with TeamCity and it’s also been relatively easy for us to integrate in with our bug tracking software as well.

  • @Brian

    For project work, I’ve stopped using bookmarks. Instead, just named branches. This is because most of the time I want to push what I have to the central repo, and bookmarks just aren’t as well integrated with 3rd party tools.

    For OSS stuff, I do find that bookmarks work better, since I’m doing a lot more experimenting and whatnot.

    The thing is, you can always move commits on an anonymous branch/bookmark to a named branch to “graduate” them, if you use the transplant extensions.

  • Do you ever run into problems with rebase? I have been using a rebase workflow for some time now, but every now and then I have strange problems. Just the other day a rebase failed with an error about not being able to access a file, and let my repo with several commits duplicated.

    • Bart

      When you perform a rebase, the rebased changesets will get new hashtags/ids. When you have not yet pushed your changesets to other repositories, this is not an issue. But otherwise, you may get duplicated changesets after pushing the rebased changesets to the repositories that already contain the older (original) changesets. The original changesets will remain in any remote repositories you have pushed them to. They will NOT be “cleaned up” automatically.

  • G. Richard Bellamy

    This is intriguing. Maybe show a screenshot of the DAG using this workflow, for comparison?