My Mercurial Setup (Plus Some Useful Shims and Jigs)

| Comments

In his book, The Productive Programmer, Neal Ford talks about using shims or jigs to help productivity. Jigs and shims are quickly created little snippets of code that automate repetitive tasks or make them easy enough that they’re worth doing. They’re little tools that help make your job easier and let you avoid using brute force to solve all of your problems.

My home directory has a bin folder in it that’s continually getting new jigs added to it, and my zshrc file is an ever-expanding list of quick shell functions.

Recently, I’ve been doing a lot more work with Mercurial as the team that I’m on switched from Subversion a couple of months ago on our Grails project. The initial transition was a little difficult for some people, but I think just about everyone is pretty happy with the transition now that we’ve made it.

Something that has helped everyone get comfortable with more complex Distributed Version Control System like Mercurial has been the distribution of shims and jigs amongst the team. I thought these tips might be useful to others as well.

Configuring Mercurial

After installing mercurial (the easiest way is to just get the appropriate binary package for your OS), you’ll want to get it configured.

A good place to start is with someone else’s configuration file. Here’s my my current ~/.hgrc configuration file (with the username replaced):

rdiff=~/Documents/workspace/rdiff/ =

git = 1

username = YOUR NAME 

cmd.kdiff3 =
cmd.chdiff =
opts.chdiff = --wait

kdiff3.args = $base $local $other -o $output

In it, you’ll see a number of useful extensions, including:

  • hg fetch - automates the pull/update working copy/merge/commit merge steps
  • extdiff extension - Mercurial allows you to set up external tools to do diffs and merges. I’m using 2 different graphical merge tools for different purposes
    • hg chdiff - for just comparing two files in a diff. Changes is a beautiful shareware diff tool for the mac, it unfortunately doesn’t do a 3-way merge.
    • hg kdiff3 - For merging, (and some diff situations), I need the power of a 3-way merge tool and I’ve found kdiff3 to be the best one that I’ve used. It’s an open source, free, cross platform 3-way merge tool. Using a 3-way merge tool is imperative for getting your merges correct.
  • color extension - just colorizes some output including the status command
  • hg glog - an ascii graphical log of the commit tree (enhanced “hg log”)
  • hg shelve - simplified patch management that allows you to “shelve” changes without committing them locally, for instance if you want to do a fetch, I asked about this on Stack Overflow
  • hg rdiff - remote diff, allows you to compare a file checked into your local repo with a file in the remote repository, I also asked about this on Stack Overflow
  • mq extension - mercurial queues, a more advanced topic that I don’t think anyone else at my workplace uses, but there’s a lot of power in mqueue if you look into it. The Mozilla guys have a really nice introductory tutorial to using this plugin.

Setting up KDiff3 so you can do 3-way merges

The simplest way to get KDiff3 is to download the binary from the main website (click on the download link and then choose the binary appropriate for your platform).

For the mac, you’ll just get a zip file with a file in it that you should drag to your /Applications directory. Then, you need to make sure that the actual executable in the application is in your path. I just create a symlink in my path to the executable inside the application package:

ln -s /Applications/ /usr/local/bin/kdiff3

You might not have /usr/local/bin in your path by default. If you can execute kdiff3 —help on the command line, you’re ready to go.

Setting up the ”shelve” extension

The easiest way to get the shelve extention set up is to simply clone the project that holds the python script to your local filesystem:

hg clone hgshelve

Then just make sure that the path in your ~/.hgrc file points to the correct location of the file in the cloned repository.

You can test that it’s working by just executing:

hg help shelve

If you don’t get an error message, it’s set up right and you can start using it.

Setting up the ”rdiff” extension

Setting up the rdiff extension is similar to the shelve extension above. Just clone the repository:

hg clone -r c3ac1c497bf0

We clone an older revision currently as the tip in that project uses some unsupported stuff that isn’t in mercurial 1.0.2, if you’ve got a newer version of mercurial, you might be able to get the tip of the rdiff extension and have it work.

Then make sure that your ~/.hgrc is pointed to the right path for the file in the repo you just cloned.

It’s a little quirky in that it actually modifies the existing “hg diff” command by detecting if the first parameter is a remote URL. If it is then it will diff that file against your tip file in your local repo (not the working copy). This as the remote repo is first in the arguments, it’s the reverse of what I’d expect, but you can pass “—reverse” to the hg diff command to switch that around.

I could see these being potential enhancements to the extension, but for now, I can work around them with a bash/zsh shell function in my startup file. It does a temp checkin of my working copy (held by the mercurial transaction so it can be rolled back), executes the reverse diff, and then rolls the transaction back to return things back to the way they were:

hgrdiff() {
    hg commit -m "temp commit for remote diff" && 
    hg diff --reverse http://my_hardcoded_repo $* &&
    hg rollback      # revert the temporary commit

And then call it with:


Other Mercurial Jigs and Shims

Here are some other jigs and shims that I’ve found useful.

Jig to Get a List of Files With the Same Status

One jig that I’ve gotten a ton of use out of, is a modification to the normal “hg status” command. Normally, “hg status” returns a list of all files that are different in the working directory when compared to the tip if the repository. In the output of status, each file is printed with a character in the first column that signifies the status of that file. Ex:

% hg status
M build.xml
A src/groovy/Foobar.groovy
A src/groovy/Quux.groovy
? src/groovy/Bar.groovy
? src/groovy/Baz.groovy

Some of the most common statuses are:

  • ? - not tracked, unknown to the source control repository
  • M - modified, this file is different in the working copy than on the file system
  • A - added, source control is aware of this file, but it hasn’t yet been committed
  • R - removed, this file has been marked for deletion on the next commit
  • ! - missing, this file is not present in the working copy but is in source control

Often, I’ll want to do something with all of the files in a particular status, but I can’t easily do that as they’re mixed in with the other file types. Also, the status flag at the beginning of the line prevents me from piping the status into another command.

I’ve come up with this shell function to allow me to filter lists of files (it actually works for subversion too, so there are 2 aliases). Just paste this into your bash/zsh startup file:

vcst() {
    # print out all of the files with a passed in status flag (M - modified, A - added, ? - unknown, etc) (default ?)
    # expects first parameter to be the version control command (likely svn or hg)
    if [ -n "$2" ]
    $1 status | egrep "^$STATUS" | awk '{print $2}'

alias svnst='vcst svn'
alias hgst='vcst hg'

Then you can use the “hgst” command to see all the files in a particular status, one per line. By default, it shows files that aren’t tracked (“?”), but you can pass a parameter that specifies another flag type if it’s other than “?”.

To see the list of files that aren’t in source control, just use:

% hgst

If you want to delete all of the files that aren’t in source control, all you have to do is send the results of that command to rm:

rm `hgst`
# removes src/groovy/Bar.groovy and src/groovy/Baz.groovy

If you want to edit all of the files that have been added in TextMate:

mate `hgst A`
# opens src/groovy/Foobar.groovy and src/groovy/Quux.groovy in TextMate

If you want to do a diff and see only the files that have been modified using kdiff3:

hg kdiff3 `hgst M`
# would show a diff of build.xml vs the working copy

Shim to See a List Incoming and Outgoing Files

When I’m about to push changes out to a remote server, or pull changes down from that server, I often want to know what files will be affected by this action. Surprisingly, there isn’t an easy built-in way to do this.

There is the hg incoming -v command, which will print out a log format of all of the different changesets. But I don’t really care about all the changesets, I just want to know which files will be affected. Here’s a sample run of incoming with 3 changesets:

% hg incoming -v
comparing with /Users/ted.naleid/temp/foo
searching for changes
changeset:   1:5c5f71bda234
user:        Ted Naleid 
date:        Tue Nov 25 20:30:51 2008 -0600
files:       src/groovy/Foobar.groovy
initial commit of foobar.groovy

changeset:   2:fe4a615747af
user:        Ted Naleid 
date:        Tue Nov 25 20:31:36 2008 -0600
files:       src/groovy/Bar.groovy src/groovy/Foobar.groovy
committing 2 files

changeset:   3:21e3d26bc3eb
tag:         tip
user:        Ted Naleid 
date:        Tue Nov 25 20:31:54 2008 -0600
files:       build.xml src/groovy/Baz.groovy src/groovy/Foobar.groovy src/groovy/Quux.groovy
3rd commit of files

That’s not very useful as it’s hard to parse out the actual files in that list (and see only the unique list).

I use the following shim (just put in your bash/zsh rc file) to make this simple:

# look for lists of files in piped output, sort the unique set of them and print them one per line
lf() {
    egrep "^files:" | awk '{for (i=2; i<=NF; i++) print $i}' | sort | uniq 

alias ic="hg incoming -v | lf"
alias og="hg outgoing -v | lf"

Once this is installed, you can simply use the og (outgoing) or ic (incoming) aliases to see what you’ll affect if you actually do a push or a pull.

Here’s the output using the incoming shim on same changeset above:

% ic

That makes it quick and easy to see the real things that you’ll affect by your actions.

Enhancing Your Shell Prompt With Mercurial Info

A couple of other commands that I have in my zshrc (they should also work in a bash startup file):

hgtarget() {
    hg_root=`hg root 2>&1 | egrep -v "$abort:"` 
    if [ $hg_root ]; then
        if [ -f $hg_root/.hg/hgrc ]; then
            hg_target=`cat $hg_root/.hg/hgrc | egrep "^default =" | sed 's/\(^default = \(http:\/\/\)*\)\(.*@\)*//'`                
            echo "$hg_target"

hgbranch() {
    hg_root=`hg root 2>&1 | egrep -v "$abort:"` 
    if [ $hg_root ]; then
        hg_branch=`hg branch`
        echo "$hg_branch"

These commands will tell you the path to the default push/pull target for your repository (hgtarget) as well as the current branch that you’re working on (hgbranch) in your repo.

I’ve added these to my shell prompt so that when I’m in a mercurial repository I’m more informed of the context that I’m working in. My prompt is a little complicated (lots more going on there besides this), but there are a number of good tutorials out there for modifying yours.