Creating and managing patches on top of a source tree maintained in a non-distributed VCS needn’t be hard. Some people handle this with Git, but I use a tool that seems to have fallen in disuse but was once very popular to create patchsets against the Linux kernel: Quilt.
What is Quilt? As the quilt(1) manual page says:
quilt - tool to manage series of patches
Quilt is a tool to manage large sets of patches by keeping track of the changes each patch makes. Patches can be applied, un-applied, refreshed, etc. The key philosophical concept is that your primary output is patches.
In other words: Quilt is a utility that lets you create and maintain independent patches on top of a source tree. The patches you create may all be interrelated (e.g. to provide different building blocks for a bigger feature) and Quilt supports that via the concept of a stack, or series, of patches.
To illustrate how to use Quilt in practice, I’ll go over my workflow to patch the FreeBSD source tree. The FreeBSD source tree is maintained in a Subversion repository, which is non-distributed. This means that, if you are not a committer, you have to provide your contributions via patches. Some contributions are tricky enough that you should attempt to submit them as a collection of simpler patches, for easier review and incorporation. Similarly, and even if you are a committer, you may still want to work on your feature offline while preparing the different commits you will issue to the tree once you are ready: Subversion branches are not lightweight enough to use them for smallish changes.
The patches directory
Quilt stores all the patches you manage in a patches directory of your choice. This directory is created the first time you create a patch (see below) in the directory where you execute Quilt. From there on, Quilt will look for this directory in the directory you currently are on in any parent directory up to the root.
What all this means is that the first time you invoke Quilt to create a patch, you should do so from the top-most directory of the source tree. This will ensure that, no matter where you later invoke other Quilt commands within that project, Quilt will use the same patches directory.
You can inspect the contents of the patches directory by hand if you wish. If you do, you will notice that all it contains are your patches plus a series file that indicates the order in which the patches need to be applied. The data in this directory is all the information you need to share your work with the world for review or submission.
Creating a new patch
Creating a new patch is easy: just use quilt new and give this command the name of the patch file you are creating. You have to append the .diff or .patch suffix to the name explicitly. Note that all this does is register the existence of the patch, but the patch will so far be empty. (If you are curious, you can check the contents of the .pc directory that has just been created and that Quilt uses for internal tracking.)
Keeping in mind what we saw in the previous section, you should create your first patch by invoking Quilt from the top-level directory of the source tree. For example:
~$ cd /usr/src
/usr/src$ quilt new move-kyuafiles.diff
Registering changes into the patch
Now that you have a new patch registered, you must tell Quilt which files you are going to edit before editing them! This is different to the majority of the version control systems as they are able to discover this on their own (with the exception of Perforce which needs to you manually “open” files upfront, albeit for different reasons).
To do this, you can use the quilt add command or the quilt edit command. The former only registers a file for editing into the patch while the latter invokes an editor for you.
/usr/src$ quilt add lib/Makefile
/usr/src$ vim lib/Makefile
... hack, hack, hack existing file ...
/usr/src$ mkdir lib/tests
/usr/src$ quilt add lib/tests/Makefile
... hack, hack, hack new file ...
/usr/src$ vim lib/tests/Makefile
Yes: you can just edit the original files after telling Quilt you are going to do so. And you can even create new files from scratch.
Preparing the patch
Once are done with all the necessary changes to define your patch, it is time to prepare it. There are two things to do here: the first is to (re)generate the patch file to include all your changes; and the second is to write a patch header to provide details on your patch. The latter will be particularly useful if you share your patch for review later, but you can even use the description as your commit log.
To regenerate the current patch file, use the quilt refresh command. This has to be done explicitly for the files in the patches directory to be updated. Until you do so, you won’t see any files in there!
To edit the patch description, use quilt header -e. This will open an editor for you and let you type the patch description, which is later prepended to the patch file. Take the chance to write a good description.
So, for every patch you create:
/usr/src$ quilt refresh
/usr/src$ quilt header -e
... write some text, save and exit ...
Walking the patch series up and down
All we have done so far is creating a single patch, which is easy, but you could have done that as well by manually creating copies of the files before editing them or simply by invoking svn diff on the source tree. Why the hassle, then?
Where Quilt really shines is when you start defining various patches on top of each other. For any non-trivial work you do, splitting your changes into different commits is pretty much a requirement. Doing so simplifies the initial review and also makes the history of the code easier to understand.
OK, so how do we create a new patch that is “detached” from the previous one? Easy! Invoke quilt new again. When you do that, you will get a brand-new, empty patch on top of the stack that you can later populate. But more importantly, don’t forget now to also use quilt add on any files that will be part of this new patch.
And here comes the really cool thing: the quilt push and quilt pop commands. These two allow you to apply or unapply the top-most patchset and thus let you go back and forth between the various patches to work on them individually and at any time. In other words, it’s just fine to pop various patches, do some edits, do a refresh and then do the corresponding pushes to get back to the top of the set. Ideal for when you receive feedback on your patchset and you want to address the issues before checking the code in.
Lastly, to know where you are at, use the quilt series command. This will print the full list of existing patches and will use different colors to show you which patches are applied, which patch you are currently working on and which patches have not yet been applied. Play with push, pop and series to see how things move along.
Unfortunately, it’s not all roses. Quilt does not “own” your source tree and, for this reason, it cannot know when you modify files without telling Quilt upfront. As a result, it is relatively easy for unexpected changes to sneak into your patch set. There are two things to keep in mind that will prevent most uncomfortable moments.
The single most important thing you should remember is: DO NOT UPDATE THE SOURCES WHILE THERE ARE PATCHES APPLIED! Doing so will confuse your patch set and you’ll have a hard time restoring it. The reason is that a svn update may end up modifying the files you have already changed, and those changes will appear to Quilt as if they were changes of your own… which means they will be integrated into your patch incorrectly. A quick quilt pop -a && svn update && quilt push -a should do the trick though.
Also, if you intend to create a new patch to re-edit a file that you already modified in another patch, do not forget to register the file with quilt! Otherwise your changes won’t be segregated into the two different patches as you intended and you’ll have to do this later…
So there you go. You have no excuse now to contribute to open-source projects by dumping single gigantic patches that are impossible to understand nor review. Break your contributions apart. Your collaboration experience will be much better and, hopefully, your patches will be integrated much quicker.
And yes, most of this can also be achieved (ab)using Git. There goes an idea for a future post.