Chapter 14
Adding functionality with extensions

While the core of Mercurial is quite complete from a functionality standpoint, it’s deliberately shorn of fancy features. This approach of preserving simplicity keeps the software easy to deal with for both maintainers and users.

However, Mercurial doesn’t box you in with an inflexible command set: you can add features to it as extensions (sometimes known as plugins). We’ve already discussed a few of these extensions in earlier chapters.

In this chapter, we’ll cover some of the other extensions that are available for Mercurial, and briefly touch on some of the machinery you’ll need to know about if you want to write an extension of your own.

14.1 Improve performance with the inotify extension

Are you interested in having some of the most common Mercurial operations run as much as a hundred times faster? Read on!

Mercurial has great performance under normal circumstances. For example, when you run the hg status” command, Mercurial has to scan almost every directory and file in your repository so that it can display file status. Many other Mercurial commands need to do the same work behind the scenes; for example, the hg diff” command uses the status machinery to avoid doing an expensive comparison operation on files that obviously haven’t changed.

Because obtaining file status is crucial to good performance, the authors of Mercurial have optimised this code to within an inch of its life. However, there’s no avoiding the fact that when you run hg status”, Mercurial is going to have to perform at least one expensive system call for each managed file to determine whether it’s changed since the last time Mercurial checked. For a sufficiently large repository, this can take a long time.

To put a number on the magnitude of this effect, I created a repository containing 150,000 managed files. I timed hg status” as taking ten seconds to run, even when none of those files had been modified.

Many modern operating systems contain a file notification facility. If a program signs up to an appropriate service, the operating system will notify it every time a file of interest is created, modified, or deleted. On Linux systems, the kernel component that does this is called inotify.

Mercurial’s inotify extension talks to the kernel’s inotify component to optimise hg status” commands. The extension has two components. A daemon sits in the background and receives notifications from the inotify subsystem. It also listens for connections from a regular Mercurial command. The extension modifies Mercurial’s behaviour so that instead of scanning the filesystem, it queries the daemon. Since the daemon has perfect information about the state of the repository, it can respond with a result instantaneously, avoiding the need to scan every directory and file in the repository.

Recall the ten seconds that I measured plain Mercurial as taking to run hg status” on a 150,000 file repository. With the inotify extension enabled, the time dropped to 0.1 seconds, a factor of one hundred faster.

Before we continue, please pay attention to some caveats.

The inotify extension is not yet shipped with Mercurial as of May 2007, so it’s a little more involved to set up than other extensions. But the performance improvement is worth it!

The extension currently comes in two parts: a set of patches to the Mercurial source code, and a library of Python bindings to the inotify subsystem.

Note: There are two Python inotify binding libraries. One of them is called pyinotify, and is packaged by some Linux distributions as python-inotify. This is not the one you’ll need, as it is too buggy and inefficient to be practical.
To get going, it’s best to already have a functioning copy of Mercurial installed.
Note: If you follow the instructions below, you’ll be replacing and overwriting any existing installation of Mercurial that you might already have, using the latest “bleeding edge” Mercurial code. Don’t say you weren’t warned!
  1. Clone the Python inotify binding repository. Build and install it.
    1  hg clone http://hg.kublai.com/python/inotify
    2  cd inotify
    3  python setup.py build --force
    4  sudo python setup.py install --skip-build
  2. Clone the crew Mercurial repository. Clone the inotify patch repository so that Mercurial Queues will be able to apply patches to your cope of the crew repository.
    1  hg clone http://hg.intevation.org/mercurial/crew
    2  hg clone crew inotify
    3  hg clone http://hg.kublai.com/mercurial/patches/inotify inotify/.hg/patches
  3. Make sure that you have the Mercurial Queues extension, mq, enabled. If you’ve never used MQ, read section 12.5 to get started quickly.
  4. Go into the inotify repo, and apply all of the inotify patches using the -a option to the hg qpush” command.
    1  cd inotify
    2  hg qpush -a

    If you get an error message from hg qpush”, you should not continue. Instead, ask for help.

  5. Build and install the patched version of Mercurial.
    1  python setup.py build --force
    2  sudo python setup.py install --skip-build

Once you’ve build a suitably patched version of Mercurial, all you need to do to enable the inotify extension is add an entry to your hgrc.

1  [extensions]
2  inotify =

When the inotify extension is enabled, Mercurial will automatically and transparently start the status daemon the first time you run a command that needs status in a repository. It runs one status daemon per repository.

The status daemon is started silently, and runs in the background. If you look at a list of running processes after you’ve enabled the inotify extension and run a few commands in different repositories, you’ll thus see a few hg processes sitting around, waiting for updates from the kernel and queries from Mercurial.

The first time you run a Mercurial command in a repository when you have the inotify extension enabled, it will run with about the same performance as a normal Mercurial command. This is because the status daemon needs to perform a normal status scan so that it has a baseline against which to apply later updates from the kernel. However, every subsequent command that does any kind of status check should be noticeably faster on repositories of even fairly modest size. Better yet, the bigger your repository is, the greater a performance advantage you’ll see. The inotify daemon makes status operations almost instantaneous on repositories of all sizes!

If you like, you can manually start a status daemon using the hg inserve” command. This gives you slightly finer control over how the daemon ought to run. This command will of course only be available when the inotify extension is enabled.

When you’re using the inotify extension, you should notice no difference at all in Mercurial’s behaviour, with the sole exception of status-related commands running a whole lot faster than they used to. You should specifically expect that commands will not print different output; neither should they give different results. If either of these situations occurs, please report a bug.

14.2 Flexible diff support with the extdiff extension

Mercurial’s built-in hg diff” command outputs plaintext unified diffs.

1  $ hg diff
2  diff -r 6e46da80e472 myfile
3  --- a/myfile Thu Aug 21 18:22:19 2008 +0000
4  +++ b/myfile Thu Aug 21 18:22:19 2008 +0000
5  @@ -1,1 +1,2 @@
6   The first line.
7  +The second line.

If you would like to use an external tool to display modifications, you’ll want to use the extdiff extension. This will let you use, for example, a graphical diff tool.

The extdiff extension is bundled with Mercurial, so it’s easy to set up. In the [extensions] section of your hgrc, simply add a one-line entry to enable the extension.

1  [extensions]
2  extdiff =

This introduces a command named hg extdiff”, which by default uses your system’s diff command to generate a unified diff in the same form as the built-in hg diff” command.

1  $ hg extdiff
2  --- a.6e46da80e472/myfile 2008-08-21 18:22:19.000000000 +0000
3  +++ /tmp/extdiffWVAH-7/a/myfile 2008-08-21 18:22:19.000000000 +0000
4  @@ -1 +1,2 @@
5   The first line.
6  +The second line.

The result won’t be exactly the same as with the built-in hg diff” variations, because the output of diff varies from one system to another, even when passed the same options.

As the “making snapshot” lines of output above imply, the hg extdiff” command works by creating two snapshots of your source tree. The first snapshot is of the source revision; the second, of the target revision or working directory. The hg extdiff” command generates these snapshots in a temporary directory, passes the name of each directory to an external diff viewer, then deletes the temporary directory. For efficiency, it only snapshots the directories and files that have changed between the two revisions.

Snapshot directory names have the same base name as your repository. If your repository path is /quux/bar/foo, then foo will be the name of each snapshot directory. Each snapshot directory name has its changeset ID appended, if appropriate. If a snapshot is of revision a631aca1083f, the directory will be named foo.a631aca1083f. A snapshot of the working directory won’t have a changeset ID appended, so it would just be foo in this example. To see what this looks like in practice, look again at the hg extdiff” example above. Notice that the diff has the snapshot directory names embedded in its header.

The hg extdiff” command accepts two important options. The -p option lets you choose a program to view differences with, instead of diff. With the -o option, you can change the options that hg extdiff” passes to the program (by default, these options are “-Npru”, which only make sense if you’re running diff). In other respects, the hg extdiff” command acts similarly to the built-in hg diff” command: you use the same option names, syntax, and arguments to specify the revisions you want, the files you want, and so on.

As an example, here’s how to run the normal system diff command, getting it to generate context diffs (using the -c option) instead of unified diffs, and five lines of context instead of the default three (passing 5 as the argument to the -C option).

1  $ hg extdiff -o -NprcC5
2  ⋆⋆⋆ a.6e46da80e472/myfile Thu Aug 21 18:22:20 2008
3  --- /tmp/extdiffWVAH-7/a/myfile Thu Aug 21 18:22:19 2008
4  ⋆⋆⋆⋆⋆⋆⋆⋆⋆⋆⋆⋆⋆⋆⋆
5  ⋆⋆⋆ 1 ⋆⋆⋆⋆
6  --- 1,2 ----
7    The first line.
8  + The second line.

Launching a visual diff tool is just as easy. Here’s how to launch the kdiff3 viewer.

1  hg extdiff -p kdiff3 -o ''

If your diff viewing command can’t deal with directories, you can easily work around this with a little scripting. For an example of such scripting in action with the mq extension and the interdiff command, see section 13.9.2.

14.2.1 Defining command aliases

It can be cumbersome to remember the options to both the hg extdiff” command and the diff viewer you want to use, so the extdiff extension lets you define new commands that will invoke your diff viewer with exactly the right options.

All you need to do is edit your hgrc, and add a section named [extdiff]. Inside this section, you can define multiple commands. Here’s how to add a kdiff3 command. Once you’ve defined this, you can type “hg kdiff3” and the extdiff extension will run kdiff3 for you.

1  [extdiff]
2  cmd.kdiff3 =

If you leave the right hand side of the definition empty, as above, the extdiff extension uses the name of the command you defined as the name of the external program to run. But these names don’t have to be the same. Here, we define a command named “hg wibble”, which runs kdiff3.

1  [extdiff]
2  cmd.wibble = kdiff3

You can also specify the default options that you want to invoke your diff viewing program with. The prefix to use is “opts.”, followed by the name of the command to which the options apply. This example defines a “hg vimdiff” command that runs the vim editor’s DirDiff extension.

1  [extdiff]
2  cmd.vimdiff = vim
3  opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)'

14.3 Cherrypicking changes with the transplant extension

Need to have a long chat with Brendan about this.

14.4 Send changes via email with the patchbomb extension

Many projects have a culture of “change review”, in which people send their modifications to a mailing list for others to read and comment on before they commit the final version to a shared repository. Some projects have people who act as gatekeepers; they apply changes from other people to a repository to which those others don’t have access.

Mercurial makes it easy to send changes over email for review or application, via its patchbomb extension. The extension is so namd because changes are formatted as patches, and it’s usual to send one changeset per email message. Sending a long series of changes by email is thus much like “bombing” the recipient’s inbox, hence “patchbomb”.

As usual, the basic configuration of the patchbomb extension takes just one or two lines in your hgrc.

1  [extensions]
2  patchbomb =

Once you’ve enabled the extension, you will have a new command available, named hg email”.

The safest and best way to invoke the hg email” command is to always run it first with the -n option. This will show you what the command would send, without actually sending anything. Once you’ve had a quick glance over the changes and verified that you are sending the right ones, you can rerun the same command, with the -n option removed.

The hg email” command accepts the same kind of revision syntax as every other Mercurial command. For example, this command will send every revision between 7 and tip, inclusive.

1  hg email -n 7:tip

You can also specify a repository to compare with. If you provide a repository but no revisions, the hg email” command will send all revisions in the local repository that are not present in the remote repository. If you additionally specify revisions or a branch name (the latter using the -b option), this will constrain the revisions sent.

It’s perfectly safe to run the hg email” command without the names of the people you want to send to: if you do this, it will just prompt you for those values interactively. (If you’re using a Linux or Unix-like system, you should have enhanced readline-style editing capabilities when entering those headers, too, which is useful.)

When you are sending just one revision, the hg email” command will by default use the first line of the changeset description as the subject of the single email message it sends.

If you send multiple revisions, the hg email” command will usually send one message per changeset. It will preface the series with an introductory message, in which you should describe the purpose of the series of changes you’re sending.

14.4.1 Changing the behaviour of patchbombs

Not every project has exactly the same conventions for sending changes in email; the patchbomb extension tries to accommodate a number of variations through command line options.