jasonwryan.com

Miscellaneous ephemera…

Using Mercurial Queues

Over the last week or so, I have finally gotten around to digging into the whole concept of using Mercurial Queues to manage patches against an upstream project; in my case dwm.1 Essentially, this mercurial extension helps you manage a stack of patches on top of a directory tree. This gives you quite a lot of fine-grained control over your patchset and assists immeasurably with automating the application of patches when the underlying codebase changes.

After having played around with queues for the last couple of days, I am quite impressed: both the concept and the execution are simple and powerful. The concept of Mercurial Queues is best described in the Mercurial book: the aptly titled, Mercurial: The Definitive Guide:2

MQ's marriage of distributed revision control with patches makes it much easier to isolate your work. Your patches live on top of normal revision history, and you can make them disappear or reappear at will. If you don't like a patch, you can drop it. If a patch isn't quite as you want it to be, simply fix it—as many times as you need to, until you have refined it into the form you desire.

I followed Filippo Negroni’s excellent tutorial on the dwm site to setup my own dwm patchset based on tip, and the process was surprisingly straightforward; the only caveat being it is a good idea to plan quite carefully what changes you want in each particular patch. Once I had completed the setup, it got me thinking about the fact that the final install is done without pacman, just using make install. And, as a proof-of-concept more than anything else, I wondered whether this functionality could be used with makepkg.3

Turns out, as you might expect with Arch, it was not much of a job to set it up at all.

Note: this doesn’t mean I think it is a good idea; I was just interested to see if it could be done and how it would work once it was set up. For something like dwm, this is definitely a case of introducing a huge amount of complexity for no apparent benefit (for those people running Gnome, this may quite appeal to you…).

I have a working PKGBUILD that you can use if you want to follow along at home. Once you have downloaded it into dwm-hg/, run makepkg to clone the dwm repository and build the binary. Then you need to make sure that the mercurial queue extension is enabled, so your .hgrc needs to contain:

1
2
[extensions]
hgext.mq =

Now, you need to setup the queue repository. We do this in $srcdir, which is dwm-hg/src/dwm:

1
hg qinit -c

The next step is to start applying the various patches that you want to comprise your patchset on dwm. In my case that is base customizations to config.def.h and three other patches: statuscolours, cycle, and push. There are other patches in my repository, one of which patches the appropriate settings for an Arch build in the relevant makefiles. This is taken care of by the PKGBUILD in this case.

From here, it is very much as Filippo describes it in his tutorial: make some changes, add them to the queue and rinse and repeat:

1
2
3
4
hg qnew base.customizations
 # “hack hack hack…”
hg qrefresh
hg qcommit -m 'Added changes to config.def.h to customize'

After committing each of your changes as a discrete patch, you can review the queue with hg qseries:

1
2
3
4
5
6
┌─[Centurion ~/Build/dwm-hg/src/dwm]
└─╼ hg qseries
config.customizations
statuscolours
cycle
push

And hg qapplied will tell you which of the patches is currently applied in the working repository; at this stage the list should look the same as that in hg qseries. The next step is to remove all of the patches from the queue so that we have a clean repository:

1
2
3
4
5
6
7
┌─[Centurion ~/Build/dwm-hg/src/dwm]
└─╼ hg qpop -a
popping push
popping cycle
popping statuscolours
popping config.customizations
patch queue now empty

Should you need to edit a patch, it is simply a matter of popping to that spot in the stack (you can do so by name or by index number, beginning at 0):

1
2
3
4
5
6
7
8
9
┌─[Centurion ~/Build/dwm-hg/src/dwm]
└─╼ hg qpop config.customizations
popping push
popping cycle
popping statuscolours
now at: config.customizations
 # “hack hack hack…”
hg qrefresh
hg qpush -a

Once you are satisfied with the state of the stack, remove all of the patches to return to the original clean working directory and cd back to dwm-hg/. You can now use makepkg -fi to rebuild the package and you should see the pushed patches as part of the output:

1
2
3
4
5
6
7
8
==> Starting build()...
==> Pushing queued patchset
applying config.customizations
applying statuscolours
applying cycle
applying push
now at: push
dwm build options:

…and when you restart dwm, your customizations will be applied. When new changes are pushed to the dwm repo you can just makepkg -fi, and, if any of the patches fail due to changes in the underlying code, pop the stack to the failed patch, rebase the code, hg qrefresh and continue until done.

As I said at the beginning of this post, this isn’t a particularly smart way to use Mercurial Queues, PKGBUILDs or a combination of the two. You are much better off just cloning dwm to your local repository, initializing a queue repository and, once you are done setting up your patchset, issuing make && sudo make install.

If you have more than one machine, you can easily setup a repository for your patchset on bitbucket and host it there: then just pull the patchset queue from your other boxes and hg qpush -a. If you require a slightly different patchset for each box, you can use MQ to manage this process as well. This is where MQ really excels; I’ll cover this in more detail in my next post.

Notes

  1. See related dwm posts.
  2. I can’t recommend this {book,wiki} highly enough; it is an excellent example of thorough, accessible documentation.
  3. In the case of something like dwm, this is really irrelevant, as there is a single tiny binary and a man page, so keeping track of these files on your system is not an issue at all.

Flickr Creative Commons image by RachelH

Comments