Mercurial has many different ways of
handling branches. While there is plenty of information available on the
different branching models, much of it is incomplete, and it can be
difficult to decide what branching model to use when starting to use
Mercurial.
Steve Losh has written an excellent guide to
branching in Mercurial,
but it mostly deals with how the different branching models work, how the
branches are stored and what commands are used to handle them.
This guide intends to give more practical information on when to use what
model and what the pros and cons are for each one.
Separate repositories
This is the simplest branching model and the one used by the Mercurial
developers. You handle separate branches by simply having separate
repositories. You might have one repository called stable that contains
released versions of your software, and one called dev that contains code
under development that will eventually go out in the next release.
Mercurial supports this type of branching very well. You create a branch by
simply cloning an existing repository, delete it by deleting the repository
and move changes between branches by pulling or pushing from one repository to
another.
The main problem with this model is that branches are very heavyweight,
especially if you have lots of source code and large repositories. Cloning
an entire repository can take a while. If you have very long-lived branches
like stable and dev in the example above, that is probably ok, but for
shorter lived feature branches it becomes cumbersome.
Another consequence of this model is that there is no permanent record of
where a certain commit originated. If you pull changes from one branch
repository to another you can see the separate lines of development, but
you cannot always easily tell which commits came from stable and which
came from dev.
Named branches
Named branches try to fix the problems with using separate repositories as
branches. They work by optionally stamping commits you make with a branch
name.
Since named branches are just some extra meta data on commits, switching
between branches is fast, the time it takes is proportional to the
difference between the two branches. And you can always tell in which
branch a commit was made, since the branch name is part of the commit.
However, this also means that you can never really delete branches (since
that would mean altering older commits). And there is no way to rename
branches either, so if you made a commit in a named branch called foo,
everyone you ever share that commit with will have a named branch called
foo in their history.
There are ways to manage this somewhat, you can mark branches as closed
which means they don’t show up by default in certain places, but they’re
still there. Closing branches is mostly an indication to Mercurial that
you’re not interested in those particular branches any more.
Since branches are often given very common names like dev, master or
1.0, this can cause problems if you intend to share your code with many
other people in an ad hoc fashion. They might want a different branch
called dev.
But for long lived branches, where you only care about collaborating with a
known group of people, named branches work well. They do not work as well
for short lived feature branches (since the branch name will be a part of
the commits forever) or if you do not want to impose your branch naming on
people you share your code with.
Anonymous branches
Anonymous branches are just a Mercurial repository with multiple heads and
no named branches. You refer to the branches by revision id or some other
available name like tip.
This works well for very short lived feature branches (anything that is
merged within five minutes up to a day), where you know that the branch very
soon will be integrated with the main line of development. For these cases,
naming the branch is often unnecessary since you know what you’re working on
anyway.
Anonymous branching has excellent support in Mercurial, and is very easy to
work with, but it has obvious problems for longer lived branches. It’s hard
to keep track of which branch is which and it’s cumbersome to communicate
which one you’re talking about if multiple people are involved.
Anonymous branches work very well for very short lived feature branches that
never leave your local repository, but poorly for anything else.
Bookmarks
Bookmarks aren’t part of core Mercurial but the
Bookmarks extension
ships with Mercurial and is easy to enable. It is the branching model in
Mercurial that is most similar to the one in Git.
Bookmarks are essentially pointers or references to commits, stored outside
the commit graph itself. This means that you can add, delete and rename
bookmarks without altering history (unlike named branches).
As of Mercurial 1.6, it is also possible to push and pull bookmarks between
repositories. If you’re using earlier versions of Mercurial you will have
to transfer bookmarks manually or with the help of something like rsync.
Bookmarks enables fast switching between branches in a single repository
and don’t place many restrictions on other people that you want to
collaborate with. Bookmarks can be deleted and renamed without altering
history, since they aren’t a part of the commit graph, they just reference
it. But there is still only one namespace for bookmarks, so if you want to
share your bookmarks you probably want to ensure their names are unlikely
to collide with other peoples bookmarks.
However, the support for bookmarks in older versions of Mercurial isn’t
that hot. You need to transfer them manually between repositories and they
interact badly with extensions that rewrite history like ‘'hg strip’‘ or
’‘hg rollback’‘. I have tried to use bookmarks in many older versions of
Mercurial but always given up because I’d end up with crashes in the
bookmarks extension because one of my bookmarks was referring to a commit
that no longer existed.
I also missed the ability to have a bookmark refer to upstream development
and have it update automatically when I pulled from there. Mercurial
bookmarks only update when you make a commit, something which works well
for your own line of development (because then you make the commits) but
leads to problems if you want to refer to a line of development done by
someone else with a bookmark. Because you’re not making the commits, only
pulling them into the repository, the bookmark will not move forward as you
pull in new commits, forcing you to update the bookmark manually.
If everyone you’re collaborating with is using Mercurial 1.6, and they use
bookmarks to keep track of branches, this isn’t a problem because then they
will update the bookmark when they make commits, and you will get both the
bookmark and the commits when you pull.
Conclusion
If you’re unsure what branching model to pick, use separate repositories.
This model has great support in Mercurial, you rarely need to add options
or extra parameters to commands, and everything works in a very clear and
simple manner. As an example, when repositories are branches, push and pull
by default only update the current branch, and you have to give explicit
options to move commits between branches. With any other sort of branching,
Mercurial updates all branches on push or pull, which is rarely what I
want. This forces me to constantly supply the branch name to some commands,
which is extra work.
If you frequently create feature branches that live for some time (days at
least) and then die, and are annoyed by the time it takes to clone the
repository to create a branch, consider using bookmarks. It’s one of the
more flexible branching models in Mercurial and has good support in the
most recent versions.
If you only want a couple of very long-lived branches, and you only care
about collaborating with a known group of people (like say the other
programmers in your team), use named branches.