Thoughts on 3.0 (quilt) format

Note: I wrote most of this before Neil Williams’ recent comments on the 3.0 family of formats, so despite the timing this isn’t really a reaction to that although I do have a couple of responses. On the whole I think I agree that the Lintian message is a bit heavy-handed and I’m not sure I’m thrilled about the idea of the default source format being changed (though I can see why the dpkg maintainers are interested in that). That said, as far as I personally am concerned, there is a vast cognitive benefit to me in having as much as possible be common to all my packages. Once I have more than a couple of packages that require patching and benefit from the 3.0 (quilt) format as a result, I find it in my interest to use it for all my non-native packages even if they’re patchless right now, so that for instance if they need patches in the future I can handle them the same way. It’s not unheard of for me to apply temporary patches even to packages I actively maintain upstream, so I don’t discount those either. I haven’t decided what to do with my native packages yet; unless they’re big enough for bzip2 compression to be worthwhile, there doesn’t seem to be much immediate advantage to 3.0 (native).

Anyway, on to the main body of this post:

I’ve been one of the holdouts resisting use of patch systems for a long time, on the basis that I felt strongly that dpkg-source -x ought to give you the source that’s actually built, rather than having to mess around with debian/rules targets in order to see it. Now that the 3.0 (quilt) format is available to fix this bug, I felt that I ought to revisit my resistance and start trying to use it. Migrating to it from monolithic diffs is of course a bit more work than migrating to it from other patch systems, so it’s taken me a little while to get round to it. I’d been thinking about holding off until there was better integration with revision control (e.g. bzr looms), as I feel that patch files really ought to be an export format, but I eventually decided that I shouldn’t let the perfect be the enemy of the good. I have enough experience with co-maintaining packages that use build-time patch systems to be able to compare my reactions.

After experimenting with a couple of small packages, I moved over to the deep end and converted openssh a few weekends ago, since quite a few people have requested over the years that the Debian changes to openssh be easier to audit. This was a substantial job - over 6000 lines of upstream patches - but not actually as much work as I expected. I took a fairly simplistic approach: first, I unapplied all the upstream patches from my tree; then I ran bzr di | interdiff -q /dev/stdin /dev/null >x, reduced it to a single logically-discrete patch, applied it to a new quilt patch using quilt fold, and repeated until x was empty. This was maybe an hour or two of work, and then I went through and tagged all the patches according to DEP-3, which took another few hours. After the first pass, I ended up with 38 patches and a much clearer idea of what has been forwarded upstream and what hasn’t; I currently have 5 patches to forward or eliminate, down from 18.

Good things:

  • I don’t lose any of my history. Since all the patches remain applied to the tree in revision control (this is what dpkg-source -x gives you, so it’s the natural representation in revision control too), bzr blame works just as you’d expect and displays both upstream and Debian changes at once. I rely on tools like blame a lot, and I really hate the way build-time patch systems make it hard to use revision control when the tree is in a built state, so this was a hard requirement for me.
  • I’ve used patch tagging before, so I was expecting some benefits, but viscerally I feel much more in control. It’s so much less laborious now to see what I need to do by way of forwarding. I don’t regret waiting for 3.0 (quilt) to become available, but I hadn’t realised quite how much I was being held back beforehand.
  • Adding new patches is pretty natural, much more so than with build-time patch systems. You can create and apply the patch, test-build, and commit when it works. I much prefer this over having to clean the tree before committing (or commit just part of the tree, which is error-prone). The more that committing to a Debian package feels like committing to an upstream project, the better.
  • There’s definitely something to be said for patch-tracker being more useful. It deals with DEP-3 to the extent of linkifying URLs, although it might be nice if patch descriptions were displayed on the overview page for each version.

Bad things:

  • It’s a bit awkward to set things up when checking out from revision control; I didn’t really want to check in the .pc directory, and the tree checks out in the patched state (as it should), so I needed some way for developers to get quilt working easily after a checkout. This is sort of the reverse of the previous problem, where users had to do something special after dpkg-source -x, and I consider it less serious so I’m willing to put up with it. I ended up with a rune in debian/rules that ought to live somewhere more common.
  • Everything ends up represented twice in revision control: the patch files, plus the changes to the patched files themselves. I’m OK with this although it is a little inelegant.
  • Although I haven’t had to do it yet, I expect that merging new upstream releases will be a bit harder. bzr will deal with resolving conflicts in the patched files themselves, and that’s why I use a revision control system after all, but then I’ll have to go and refresh all the patches and will probably end up doing some of the same conflict resolution a second time. I think the best answer right now is to quilt pop -a, force a merge despite the modified working tree, and then quilt push && quilt refresh -pab until I get back to the top of the stack, modulo slight fiddliness when a patch disappears entirely; thus effectively using quilt’s conflict resolution rather than bzr’s. I suppose this will serve as additional incentive to reduce my patch count. I know that people have been working on making this work nicely with topgit, although I’m certainly not going to put up with the rest of git due to that; I’m happy to wait for looms to become usable and integrated. :-)
  • It would be nice if there were some standard DEP-3 way to note that a patch has been accepted or rejected upstream, beyond just putting it in the description. In particular, it seems to me that listing patches accepted upstream could be used to speed up the process of merging new upstream releases.

On the whole I’m satisfied with this, and the benefits definitely outweigh the costs. Thanks to the dpkg team for all their work on this!