When I joined Mozilla I decided to leap head first into Mercurial, as most of Mozilla's code is stored in Mercurial. Coming from a
git background, the first little while was a bit rough, but I'm increasingly finding I prefer Mercurial's approach to things.
I really do find the staging area too complex, and branches heavierweight than necessary (see my earlier post Grinding my Gears with Git), and increasingly I really appreciate the plugin architecture that allows the creation of a Mercurial that works for you.
I have to say though, where I do envy git is that they've got some great looking docs theses days, and Mercurial is pretty far behind there, as often the best docs are those on the wiki, and it doesn't always feel very maintained.
With that in mind, let's talk about how I work with Mercurial. This post is heavily inspired by (and my workflow definitely inspired by) Steve Fink's post from last year.
I mostly used the initial setup of
.hgrc provided by Mozilla's
bootstrap.py. I have made a couple tweaks though:
[diff] git = true showfunc = true unified = 8
The above stanza helps get diffs in a friendly format for your reviewers. Last time I checked,
bootstrap.py didn't set
unified = 8.
[color] diff.trailingwhitespace = bold red_background
The SpiderMonkey team is a stickler for some things, including trailing whitespace. This colourization rule helps it stand out when you inspect a change with
- I'm a huge fan of the
bootstrap.pysets up. It lists recent heads in graph format with a terse log, along with colourized output.
hg loghas a different philosophy than
git log. Where
git logshows you a relative view of history from your current working directory or specified revision, Mercurial's
logcommand by default shows a global view of history in your repository. In a small project, I can imagine that making sense, but to be honest, 95% of the time I find
hg logdoes the wrong thing for what I want. So:
[alias] flog = log -f --template=wip
hg flogas an alias for following-log, which is closer in behaviour to the git log. The
--template-wipbit uses the colourization and line formatting already provided for the
Honestly though, I use
hg wipabout 10x more often than I use
One of the cool things about Mercurial is its well developed reasoning about history rewriting. One key element to that is the notion of 'phases' which help define when a rewrite of a changeset can happen. There's a darn good chance this will be a sensible default for you in your
[phases] publish = false
Getting to work
I use a clone of
mozilla-unified as the starting point. When I start working on something new, unless I have a good reason not to, I'll typically start work off of the most recent
central tag in the repo.
$ hg pull && hg up central
When working in Mercurial, one of the things you get to decide is whether or not you label you commits or not. This article goes into more detail, but suffice it to say, there's no requirement, as there is in in git, to label your lightweight branches (using Bookmarks).
I have experimented both with labelling and not these days, and I have to say, so long as I have
hg wip, I think it's pretty reasonable to get by without bookmarks, especially as my commit messages typically end up having the bug numbers in them, so it feels almost like redundant info to label the branch. Maybe if you work on a project where commit messages aren't associated with a bug or other identifier labelling might be more useful.
When developing, I tend to use commits as checkpoints. Then later, what I will do is use history rewriting tools to create the commits that tell the story I want to tell. In Mercurial, this means you'll want to enable the Evolve and Histedit extensions (Facebook's
chistedit.py is also nice, but not necessary). You'll also want
rebase (unlike in git,
histedit are two distinct operations).
A tip with
histedit: When I first started with mercurial, I found myself more uncomfortable with
histeditthan I was with git. This was because I was used to doing 'experimental' history editing, always knowing I could get back to the repo state I stared from just by moving my branch pointer back to the commit I left behind.
Mercurial, with the evolve extension enabled, has a more complicated story for how history editing works. Over time, you'll learn about it, but in the mean time, if you want to be able to keep your old history:
hg histedit --keepwill preserve the old history and create the new history under a new head.
Evolve knows some pretty cool tricks, but I think I'll save that for later once I can explain the the magic a little more clearly.
Absorb is the coolest extension for mercurial I know of. What it does is automate applying edits to a stack of patches, exactly the kind of edits that show up in a code-review based workflow. If you create stacks of commits and get them reviewed as a stack, it's worth looking into this.
The best explanation I know of is this little whitepaer written by Jun Wu, the original author.
One extension I adore, is the
share extension, which ships with Mercurial. It's very similar in spirit to
git-worktree This allows me to have multiple working copies, but a common repo storage. Even better, it works great to have a working copy inside my VM that's backed by my current repo.
So that was a long, rambling blog post: Mostly I just wanted to share the pieces of Mercurial that make me really happy I stuck to it, and to encourage others to give it a shot again. While I doubt mercurial will ever supplant Git, as Git has mindshare for days, at the very least I think Mercurial is worth exploring as a different point in the DVCS design space.
Please, if you've got tips and tricks you'd like to share, or cool extensions, feel free to reach out or leave a comment.