Following the libvirt CVS repository using Mercurial, Tailor and patch queues
The libvirt project uses CVS as its primary SCM system. Our project code submission process requires that people submit patches for review & approval on the mailing list before committing them to CVS. This is great for catching stupid bugs and working through various design options, but CVS really isn’t conducive to this kind of workflow. It is not unusual to have a number of outstanding patches going through review, and you want to keep them all tracked separately during development. In other words you want a queue of pending patches.
There are many ways to implement such a workflow. A long time ago Andrew Morton had a set of scripts for managing patches, which evolved into Quilt. In these modern times, many projects (including the kernel of course) use GIT, and its ability to rebase branches to achieve this workflow. We even maintain a GIT mirror of libvirt which could be used for managing outstanding patches. While certainly a very capable tool, GIT has a somewhat steep learning curve, and even once learnt it violates the principle of least surprise on a daily basis with seemingly simple operations working in the most bizarre way. So with libvirt though I’ve been using a Mercurial mirror, and its ‘mq’ extension for patch management. This isn’t the only way of skinning the cat – you could also use branches, or the new ‘rebase’ command inspired by GIT, but my mind works in terms of patch queues, so I chose ‘mq’. For benefit of anyone else with an interest in Mecurial, here’s a quick overview of my workflow
The first task is to get a reliable process to synchronize the CVS repository to Mercurial. For this purpose I chose to use Tailor, though there are plenty of other tools which do the job fine too. For a repeatable conversion, you need to create a recipe describing what you want. I have the following script saved as $HOME/work/libvirt-hg.tailor
, with execute permission enabled:
$ sudo yum install tailor $ cd $HOME/work $ cat > libvirt-hg.tailor <<EOF #!/usr/bin/env tailor """ [DEFAULT] verbose = True [project] target = hg:target start-revision = INITIAL root-directory = /home/berrange/work state-file = libvirt-hg.state source = cvs:source subdir = libvirt-hg [cvs:source] module = libvirt repository = :pserver:anoncvs@libvirt.org:2401/data/cvs [hg:target] """ EOF $ chmod +x libvirt-hg.tailor $ ./libvirt-hg.tailor
When invoking this script for the first time it’ll take a seriously long time, checking out every revision of every file in CVS, figuring out the changesets, and populating a mercurial repository with them. When complete it will have left a directory libvirt-hg
containing the mercurial repository mirroring the CVS repo, and a file libvirt-hg.state
recording how much work it has done. On a daily basis (or more/less often as desired), re-running libvirt-hg.tailor
will make it “catch up” with any new changes in CVS, updating the libirt-hg
repository with the new changesets.
While you could work directly in the libvirt-hg
repository, my preference is to work in a clone of it, and leave that as a pristine mirror of the CVS repository. So I create a repository called ‘libvirt-work’ for my day-to-day patch queue. If I want to try out someone else’s work – for example Ben Guthro’s libvirt events patchset, I create a patch queue just for that, by again cloning the pristine libvirt-hg
repository.
$ cd $HOME/work $ hg clone libvirt-hg libvirt-work
For some reason Mercurial extensions aren’t enabled by default, so if you’ve not used the ‘mq’ extension before, create a $HOME/.hgrc containing:
$ cat $HOME/.hgrc [ui] username="Daniel P. Berrange <berrange@redhat.com>" [diff] git = 1 showfunc = 1 [extensions] hgext.hgk= hgext.mq= hgext.extdiff =
This does a number of things. The ‘showfunc’ setting makes it include function names in context diffs. The ‘hgk’ extension is a graphical repository browser, ‘mq’ is the patch queue extension, and ‘extdiff’ allows use of more interesting diff options. With that in place I can create a queue for my new bit of work in libvirt
$ cd $HOME/work $ cd libvirt-work $ hg qinit
The first patch I want to work on is refactoring mac address handling, so I’ll add a patch to track this work
$ hg qnew mac-address-refactor
Now off editing files as normal – ‘hg add’ / ‘hg remove; to create / delete files as needed, ‘hg status’ or ‘hg diff’ to see what’s changed, etc. The only difference comes when the work is complete – instead of using ‘hg commit’ to record a permanent changeset, I want to update the patch queue. This is done with the ‘hg qrefresh’ command, and the ‘hg qdiff’ command will show you the contents of the patch file
$ hg qrefresh $ hg qdiff | diffstat capabilities.c | 16 +++++++++++++++- capabilities.h | 11 +++++++++++ domain_conf.c | 34 +++++++--------------------------- domain_conf.h | 8 ++------ lxc_conf.c | 3 +++ lxc_driver.c | 4 +++- openvz_conf.c | 2 +- qemu_conf.c | 3 +++ qemu_driver.c | 6 ++++-- util.c | 24 +++++++++++++++++++++++- util.h | 12 +++++++++++- xen_internal.c | 3 +++ xend_internal.c | 8 ++++++-- xm_internal.c | 34 +++++++++++----------------------- 14 files changed, 103 insertions(+), 65 deletions(-)
This bit of work was a pre-cursor to the real thing I wanted to work on, the OpenVZ networking code. With this refactoring out of the way, I want to add support for the ‘get version’ operation in OpenVZ driver, so I can start a new patch
$ hg qnew openvz-version
Patches stack up in a series, each building on the last
$ hg qseries mac-address-refactor openvz-version
Fast forward another hour, and I’ve got the version operation implemented, and ready for the final patch, enhancing the OpenVZ networking support.
$ hg qnew openvz-network $ hg qseries mac-address-refactor openvz-version openvz-network
In working through this 3rd patch though, I realize there was a problem in the first one, so I save my current work, and then ‘pop’ patches off the queue until I get back to the first.
$ hg qrefresh $ hg qtop openvz-network $ hg qpop Now at: openvz-version $ hg qpop Now at: mac-address-refactor
Once I’ve fixed the mistakes in this patch, I can then ‘push’ patches back onto the queue. If you’re lucky Mercurial can resolve / merge changes, but as with any rebase, sometimes there are conflicts which require fixing up by hand. If this occurrs, ‘.rej’ reject files are saved which must be manually addressed, and then the updated patch saved with ‘hg qrefresh’ before continuing.
$ hg qpush applying openvz-version Now at: openvz-version $ hg qpush applying openvz-network Now at: openvz-network
When a piece of work is done the raw patch files all live in $REPO/.hg/patches/
and can be submitted for review to the mailing list.
If a bit of work goes on for a long time, it is often neccessary to re-sync with latest upstream CVS repository. First step in this re-base is to get Tailor to pull down all latest changes into the the local libvirt-hg
repository
$ cd $HOME/work $ ./libvirt-hg.tailor
Then in the work-ing repository to be rebased, first ‘pop’ off all local patches. Then pull in the new changesets from libvirt-hg
, before re-applying the patches, fixing up any merge conflicts which arise
$ hg qpop --all Patch queue now empty $ hg pull pulling from /home/berrange/work/libvirt-hg searching for changes adding changesets adding manifests adding file changes added 15 changesets with 67 changes to 48 files (run 'hg update' to get a working copy) $ hg update 48 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg qpush applying mac-address-refactor Now at: mac-address-refactor $ hg qpush applying openvz-version Now at: openvz-version $ hg qpush applying openvz-network Now at: openvz-network
This time I was lucky and didn’t have any merge conflicts.
While I remmeber, another reason for keeping the libvirt-hg
repository pristine, is that I typically do work on different features across different machines. On one machine I make be doing OpenVZ work, while another has a patch queue of KVM work, while yet another may be Xen work. Instead of running Tailor on every machine, I can just have one master mercurial mirror, and push that where needed. Then each local machine has its own independant libvirt-work
cloned repository with relevant patchqueue.
There is soo much more I could describe here – I’ve not even mentioned most of the commands available in the ‘mq’ extension – ‘hg help’ will show them all. For further reading, turn to the Mercurial mq wiki page
Helpful post.
What do you use to propagate the changes ( patches ) up to the CVS repository? Is there are workflow you adopt?
Appreciated.
“are” should be “any”