Open Source Software Technical Articles

Want the Best of the Wazi Blogs Delivered Directly to your Inbox?

Subscribe to Wazi by Email

Your email:

Connect with Us!

Current Articles | RSS Feed RSS Feed

Organizing Patches with Mercurial Queues

  
  
  

If you've already tried out Mercurial and liked it, you might be interested in the Mercurial extension Mercurial Queues, which provides an incredibly useful way of managing patch queues, including multiple and versioned patch queues. It lets you pop patches on and off your working tree to work on different things, or apply other people's changes, as you need to.



While MQ is an "extension," it's shipped with the standard Mercurial installation; Mercurial is set up with a "basics" plus "extensions" structure. To enable it, just add this line to your ~/.hgrc file:



[extensions] 
mq =


Run hg --help and you should then see mq listed under "enabled extensions."



Now you can try using mq to apply your first patch. Change directory into an existing Mercurial repository directory, or set up a new one using hg init or hg clone. To initialize mq on this repository, use:



hg init --mq


To create your first patch in this repository, run:



hg qnew -m "Testing an mq patch" patchTest


This sets up a patch with the name patchTest, and a commit message for when you're finally ready to commit the patch to the main working repository. The message isn't required, but if specified it will be shown if you run hg log, so if you're using lots of patches it may help you to navigate them. Now make some edits to files in your repository, and run hg status to see that the files you edited are marked as modified. To store these changes in patchTest, type



hg qrefresh


The changes are now stored in the patch, but still not in the main Mercurial repository. If you then make some more changes, but decide that they're no good and you want to ditch them, hg revert will take you back to the last time you refreshed.



After refreshing a patch, if you run hg status, you'll see that there are no modifications marked. But if you run hg qapplied, you'll see that patchTest is shown in the list. qapplied lists every patch that is currently applied in the repository. "Applied" means that the code changes that constitute the patch are currently included in the working code of your local directory. If the patch isn't applied, you won't see those changes. However, they're not saved in the repository as a commit. (See below for more on how to apply or remove patches.) You can also run hg log to see details of the patches, tagged with the patch name and with qbase and qtip, which point to the bottommost and topmost applied patch respectively.



At this point, the changes you made are stored in the patchTest patch, but they're not committed to the main repository. You can see the patch as a staging area between uncommitted changes and the main repository. To permanently commit all the revisions in the patch to the main repository, use the command:



hg qfinish patchTest


Run hg log again to see the final changeset log. You'll notice that neither the tags beginning with "q," nor the patch name, are now shown. This is because the patch has now been committed to the main repository and is no longer managed by MQ; it's just another regular commit in the repository's history.



Multiple Patches



So far so good, but where this begins to get really useful is when you have multiple patches. Let's say you're working on a new code feature, in a patch you've called newFeature, when an urgent bugfix request comes in. Add your latest feature changes to the newFeature patch with hg qrefresh, then type hg qpop. This "pops" the patch off the top of the patch queue. Issue these two commands to see what this means:



hg qseries 
hg qapplied


hg qseries tells you which patches exist (which patches the repository knows about), while hg qapplied tells you which are currently applied to the repository. Our newFeature patch now appears in the first list (so the repository knows that there is a patch) but not in the second (because we qpopped it off); the patch isn't applied, so those changes are not currently present in the working directory. (If you run hg tip you won't see any changes tagged with newFeature, either.)



Now, let's add another patch, nastyBug, with ht qnew nastyBug. If you run hg qseries and hg qapplied again, you'll see both patches show up in the first list, and only nastyBug in the second list. (Our newly created nastyBug patch is automatically applied, but contains no actual code changes until we start editing code.) The repository knows about both patches, but only nastyBug is actually applied. Make a couple more different changes, and refresh the nastyBug patch with hg qrefresh.



You can qpop nastyBug back off again to get back to the working directory. However, these patches now exist in a set order: nastyBug, then newFeature. If you type hg qpush newFeature from the main working directory, you'll see that both nastyBug and newFeature are applied.



You can change the order of patches by manually editing the file .hg/patches/series. Be careful when doing this if there is any overlap in the changes of your patches, as you may end up with mangled code or overwrite changes you wanted to keep. You should also do this only for patches that are not currently applied, to avoid any potential confusion for Mercurial.

19a98812-f823-48dc-841e-bf029c63c6d7


Multiple Patch Queues



Obviously Mercurial queues lets you work with multiple patches; what about multiple patch queues? You might have a couple of sets of changes, which both need multiple patches, but which you'd like to work on separately. Or you might want to keep bug patches and feature patches separate. Create a couple of different queues with:



hg qqueue -c features 
hg qqueue -c bugs


Follow that up with the command hg qqueue to list the queues available, and see which one is active ("bugs," currently). To switch to the "features" queue, use hg qqueue features.



You can now add patches in either queue exactly as we did in the previous section. Note however that you can only switch queues when the patch queue is empty (i.e. there are no patches currently applied). If you want to switch queues, refresh any changes you want to keep, type hg qpop -a to pop off all patches, then change queues with hg qqueue NAME. You can push all your patches back from this queue with hg qpush -a.



Versioned Patch Queues



The MQ patch directory, .ht/patches, lives outside the working repository directory, so the patch directory itself isn't managed by Mercurial. However, you can if you want set up the patch directory as a Mercurial repository in its own right. This can be useful if you're writing complicated patches and want to be able to track the changes in the patches in the same way as you would the rest of the project (including the ability to roll back to an earlier version of the patch, check diffs between patch versions, and so on). It also makes it possible for multiple developers to work on a patch or patch series and to keep track of their changes.



Note that while this is powerful, it's also moderately complicated, so if you're not sure if you want to use it, it's probably best not to. If you do need it, though, it's a fantastic strength in MQ.



MQ has a couple of commands to help you with versioned patch queues. You can set up the .hg/patches directory as a Mercurial repository at any time by going into it and running ht init. Then add an entry to the .htignore file for the file named status; you definitely do not want to manage that in the repository as it is used for internal bookkeeping and edited automatically by MQ itself. (You should never edit it by hand either!) Alternatively, if you know at the time you're setting up your main repository to work with patches that you'll want to manage the patches in a repository as well, you can use the command



hg qinit -c


which will both create the directory and ignore the status file for you.



If .hg/patches is a repository, MQ will automatically run hg add for every patch that you create. You can also use the command qcommit, which runs hg commit in the patch directory, saving you a bit of hassle.



Note that if you make changes to the patch directory (e.g. with hg update, manual edits, or an update from an external patch repository), you'll need to manually run hg qpop hg -a; hg qpush hg -a to remove the patches and then reapply them, to get the most recent version.



MQ is a great tool for managing repository edits, and it's well worth experimenting with. Once you start working with patches, you'll quickly discover how useful it is to be able to separate out your changes like this, especially if you're working with other people or if you're the sort of person who likes to switch between problems regularly to keep your mind fresh. MQ can help make your code and files as organized as you're happy with, in the way that you work best.




This work is licensed under a Creative Commons Attribution 3.0 Unported License
Creative Commons License.


This work is licensed under a Creative Commons Attribution 3.0 Unported License
Creative Commons License.

Comments

Currently, there are no comments. Be the first to post one!
Post Comment
Name
 *
Email
 *
Website (optional)
Comment
 *

Allowed tags: <a> link, <b> bold, <i> italics