Mercurial Tips

I should say first of all that I’m not an expert of Mercurial, or hg for short. It’s the preferred version control system at Mozilla, and it takes some getting used to—especially if you, like me, have git built in to your muscle memory.

The first thing you need to do is to reset your perception about how and what a version control system should do for you. Mercurial follows a philosophy of minimalism where very little is given by default, where extensions provide most of the functionality. Things you take for granted in git aren’t included by default, and you will need extensions. Lots of them.

This document recounts the hurdles I’ve had using hg and serves mostly as a personal reference. But it might still be useful for others who are just getting into contributing to Mozilla.

Setup

By far the most frustrating experience with hg is that you cannot expect the default installation to come with sensible defaults. The argument is that keeping “advanced” functionality out of core prevents new users from accidentally using it.

An unindended consequence is that it’s sometimes difficult for other people to help when you’re stuck because more often than not their environment won’t match yours. This is something you just have to get used to; that hg users are very fond of their own quirky workflow and that it more often than not is subtly different from yours.

Because different extensions imply different styles of workflows, their user interface and interoperability is not always compatible. To use a certain workflow you often need to change the default behaviour of commands to be aware of your chosen work style. For example if you want hg log to be aware of commits on the current bookmark, you need to change the default aguments.

The setup process is difficult for beginners because the rudimentary stock features are sparse, My impression is that the plethora of extensions and different workflows have left Mercurial in somewhat of a schizophrenic state, where no specific workflow is being recommended (because anything is possible!) and they are leading to mental conflict because the different styles are subtly

One frequently lauded aspect of hg is that the user interface is easier to understand. hilst I appreciate difference in taste, my experience is the opposite. If you follow the HEAD-based, bookmark-style workflow I describe below, I find many cognitive dissonances in how the bookmark feature interacts with other commands.

Mozilla Setup

Since only a few organisations use Mercurial (far more use git), documentation and examples are often a problem. I’ll go through a basic setup for working on the Mozilla code base, what extensions you need and which are useful, as well as describe my workflow.

The most important built-in Mercurial extensions to have are rebase, purge, histedit, and shelve. Furthermore you need mq, if you want the qbackout extension to back out patches you mistakingly push to inbound, and extdiff if you want to use an external diff viewer. Note that ome of these extensions only ship with fairly modern versions of Mercurial.

rebase
Reapplying your unpushed commits on top of another branch is such a common occurrence that it's bewildering that it's not enabled by default.
purge
Enables you to do hg clean to removing any untracked files.
histedit
Equivalent to the indispensible git rebase -i (interactive rebase) which allows you to squash, reorder, fixup, and rename commits.
shelve
An equivalent to git stash that lets you put away uncommitted changes temporarily without committing them. Useful when you need to switch to another bookmark or do some other work.
mq
Adds patch queues. Before bookmarks this was the preferred Mozilla workflow. Nowadays it’s only required for qbackout.
extdiff
Provides integration with external diff viewers such as kdiff and meld, if that’s your cup of tea.

There are additionally a number of custom extensions you’ll need to work efficiently on the Mozilla codebase that are specific to Mozilla’s integration environment. Most of them are available and documented in Mozilla's Version Control Tools.

The firefoxtree extension lets you consolidate the different remote staging repositories in a single hg repository locally. This saves space and provides an all-around better experience as you move commits from one repo to another.

I have version-control-tools and trychooser checked out in ~/.mozbuild.

firefoxtree
Consolidates mozilla-central, mozilla-inbound, and fx-team repositories in your one, single hg repository locally.
reviewboard
Lets you push bookmarks to the Review Board server to start a new code review.
trychooser
Push commits to our try server to test it with a custom syntax.
qbackout
Back out commits you by mistake pushed to inbound. Useful when you cock up.

My Workflow

Pulling most recent changes from central, if you have central checked out.

Similarily, pulling changes from inbound, without updating.

To create a new feature branch, or bookmark, in hg lingua.

Then to commit the code, which brings up EDITOR.

To see last commit made.

Or to see all commits made on your bookmark, where N indicates the number of commits to show.

To push a bookmark to review.

And to see what is staged for pushing and compare that against a remote.

To push to try.

If you want to rebase just one commit on the current bookmark, where DEST can be “inbound”.

Or when you have multiple commits on the same bookmark and you want to rebase them all (ancestors up to the branch point).

Finally to push your bookmark to a remote, e.g. inbound.

When your patch gets backed out from inbound or central and you need to re-land it, I create a new bookmark off inbound/central and graft (analogous to cherry-picking in git) them onto the new bookmark.

When I need to fix up (amend) the previous commit, I do.

And whenever I need to squash and rewrite the history of a bookmark, where REV it the revision number or commit SHA.

Useful Mercurial Commands

This is a gathering of useful Mercurial commands. I'd like to call out Andrew Halberstad's New Mercurial Workflow article as a particularly good starting point for learning modern hg with bookmarks.

hg log -f to follow rename or copy history of files. Showing the history of your current bookmark can be achieved with hg log -r 'bookmark()'.

hg outgoing -r BOOKMARK inbound to see the diff between your bookmark and inbound.

hg log -r . to see the last commit on tip.

hg log -lN, where N is the number of commits to show the log message of.

hg histedit is roughly equivalent to squashing using git rebase -i, only that it happens without rebasing.

hg pull -u is equivalent to git pull; it fetches and updates.

hg trychooser -m ARGS, where ARGS are the try arguments to use.

To push all commits on given BOOKMARK one can use hg push -r BOOKMARK.

And to rebase do hg rebase -b BOOKMARK -d inbound --collapse. The --collapse argument lets you fold/squash commits.

There's a shelve extension that works like git's stash functionality. You invoke it for any modified code with hg shelve, and use hg unshelve to get the changes back.