Workflows¶
Mercurial is a flexible tool that allows you to choose from several workflows and variations thereof.
Important
Before reading on, it is important to have a firm grasp on the concepts described in The DAG and Mercurial. That article describes how Mercurial models repository history as a directional acyclic graph (DAG) and understanding of this is critical for many workflows.
Feature Branches and Head-Based Development¶
As mentioned in The DAG and Mercurial, DAG branches are commonly used to work on isolated units of change. DAG branches used this way are called feature branches because each DAG branch tracks a specific feature or line of work.
Another way to think about this is as head-based development. Each DAG branch has its own head node (this is a basic property of directed acyclic graphs). So, working on different DAG branches is effectively working on different heads.
The general way feature branches/head-based development work is:
Set your starting point via
hg up <starting node>
.Modify files
hg commit
Repeat #2 and #3 until work is done
Integrate DAG branch somehow (typically a rebase or merge)
To help understand this, let’s start with the following state:
$ hg log -G -T '{node|short} {desc}'
@ 2bf9b23b2d03 D
|
o 0f165760af41 C
|
o 7175417717e8 B
|
o 8febb2b7339e A
The working directory is based on D
. But we don’t like the state
of D
, so we decide to start working from B
instead:
$ hg up 7175417717e8
$ echo changes > file
$ hg commit -m E
created new head
$ echo 'more changes' > file
$ hg commit -m F
You get bored working on that feature. Or, you run into some obstacles and want to try a fresh approach. So, you decide to start a new feature branch:
$ hg up 7175417717e8
$ echo 'new feature' > file
$ hg commit -m G
$ echo 'more new features' > file
$ hg commit -m H
Then you have a revelation about the first feature branch you were working on and go back to make a change:
$ hg up 82123e512a06
$ echo revelation > file
$ hg commit -m I
Our repository now looks like:
$ hg log -G -T '{node|short} {desc}'
@ bcb7c3592ba2 I
|
| o 2ca760d1e4fe H
| |
| o d6248a455a1b G
| |
o | 82123e512a06 F
| |
o | bed01724d682 E
|/
| o 2bf9b23b2d03 D
| |
| o 0f165760af41 C
|/
o 7175417717e8 B
|
o 8febb2b7339e A
To the uninitiated, this view can look complicated because it kind of
is. You’ve got a number of commits and lines going every which way.
And, you can imagine how complicated things can become if you are
working on several heads and/or the repository history is large and/or
fast moving. We need tools beyond hg log -G
to help us sort through
these commits.
Finding Heads¶
The hg heads
command can be used to quickly see all repository heads.
Running it on our current repository will reveal something like:
$ hg heads
changeset: 8:bcb7c3592ba2
tag: tip
parent: 5:82123e512a06
user: Gregory Szorc <gps@mozilla.com>
date: Wed Aug 12 12:57:28 2015 -0700
summary: I
changeset: 7:2ca760d1e4fe
user: Gregory Szorc <gps@mozilla.com>
date: Wed Aug 12 12:52:26 2015 -0700
summary: H
changeset: 3:2bf9b23b2d03
user: Gregory Szorc <gps@mozilla.com>
date: Wed Aug 12 11:57:08 2015 -0700
summary: D
Tip
hg heads
is roughly equivalent to hg log -r 'head()'
, which
uses the head()
revision set function to only select head
changesets/nodes.
hg heads
can be useful to get a quick overview of all unmerged
DAG branches. If the canonical repository only has a single head, then
hg heads
will be a good approximation for what work hasn’t been
merged yet. But if the canonical repository has many heads (this is
frequently the case), then hg heads
may lose some of its utility
because it will display all heads, not just the ones you care about.
Read on for some ways to deal with this.
Labeling¶
Up until this point, all our Mercurial commands were interacting with the 12 character hex abbreviation of the full SHA-1 changeset. These values are effectively random, opaque, and difficult to memorize. It can be annoying and possibly difficult for humans to grasp with them. This is why Mercurial provides facilitites for labeling heads and changesets. There are many forms of labels in Mercurial.
Bookmarks¶
Bookmarks are specially behaving labels attached to changesets. When you commit when a bookmark is active, the active label/bookmark automatically moves to the just-committed changeset.
For more on bookmarks, see Using Bookmarks.
Bookmark users may find the hg bookmarks
command useful,
as it prints a concise summary of all bookmarks. This is arguably
a better version of hg heads
, which we learned about above.
However, a downside of hg bookmarks
is that it only shows the
changesets with bookmarks: it doesn’t show other changesets in
that head or the overall DAG. For that, we’ll need more powerful
tools. Keep reading to learn more.
Branches¶
Mercurial branches (not to be confused with generic DAG branches) are a more heavyweight label that can be applied to changesets. Unlike bookmarks whose labels move as changesets are committed, branches are stored inside the changeset itself and are permanent.
When you make a Mercurial branch active, all subsequent commits will forever be associated with that branch.
Branches are useful for long-running heads, such as tracking releases. However, their utility for short-lived feature development is widely considered to be marginal. And for large repositories, the presence of hundreds or even thousands of branches over time or from hundreds of developers can lead to a lot of clutter and confusion.
Important
The use of Mercurial branches for feature development is highly discouraged. For Firefox, Mercurial branches are never used for tracking features.
Because the use of Mercurial branches is discouraged, we won’t describe how they are used.
MQ¶
Mercurial Queues (MQ) is a workflow extension that focuses on interacting with stacks of labeled patches. Contrast this with head-based workflows, where you are interacting with nodes and heads on the repository DAG.
Some like MQ because it hides the complexity of the DAG. It takes a simple and easily comprehended approach to working on things. However, it also has numerous setbacks:
MQ doesn’t perform 3-way merges and thus merge conflicts (in the form of .rej files) are much more common.
Managing labels for every single changeset can be cumbersome, introducing overhead that encourages fewer, larger, and harder-to-review commits.
Performance on large repositories can be horrible.
The extension isn’t actively developed and bugs often go unfixed.
Important
The Mercurial project doesn’t recommend MQ, especially for new Mercurial users. At Mozilla, we also recommend not using MQ. Use a head-based workflow instead.
Refining What Changesets are Shown¶
hg heads
, hg bookmarks
, hg branches
, hg qseries
,
and other commands meant to summarize common entities within the
repository each suffer from the limitation that they often show
too little information. When doing development, you often want
to see all the changesets in a head or want to see the shape of
the DAG. We need a way to view the important information from
the aforementioned commands, without the overload that hg log -G
gives us. Fortunately, Mercurial has an answer.
The output from the hg log
command can be highly configurable
via the use of revision sets (revsets) and templates. The former
determines what to show and the latter how to show it.
When we run hg log -G
, Mercurial will display information for
all changesets and render it according to the default command
line template. As you’ll quickly learn, this is far from an ideal
way to find changesets you care about.
For a fast and information rich display of changesets relevant to
you - a view on the heads/features you’ve been working on - we
highly recommend the hg wip
command described at
Customizing Mercurial Like a Pro.
To Label or Not to Label¶
Before we learned about bookmarks, branches, and MQ patches, we learned how to create label-less DAG branches. Various Mercurial workflows use labels because they are more human friendly than SHA-1 fragments. But, they aren’t required.
Note
The concept of label-less heads does not exist in Git: Git requires all heads to have a label (a Git branch name) or the head and the commits unique to it will eventually be deleted via garbage collection.
Because Git requires labels and Mercurial does not, it is accurate to say that Mercurial has lighter weight DAG branches than Git!
Since Mercurial doesn’t require labels, it raises an interesting question: should you use labels?
The answer, like most things, depends.
Custom and powerful query and rendering tools like the
aforementioned hg wip
command are sufficient for many to simply
not need labels and to use anonymous, unlabeled changesets and heads
for everything. A benefit to this approach is less overhead
interacting with and managing labels: you don’t need to make
a bookmark or branch active: you just update to a changeset, make
changes, and commit. You don’t need to clean up labels when you
are done. It’s all very low-level and feels fast. It also contributes
to understanding of the DAG and its concepts.
A downside of label-less workflows is you have to interact with SHA-1s or SHA-1 fragments all the time. There is a lot of copying and pasting of these values in order to run commands. And, this is simply too much for some people. Some just need human-friendly labels.