I've been experimenting with Mercurial Queues with the idea of sharingtheir mysteries with OpenSolaris developers so we can all integratemultiple changesets and keep one fix per changeset.

Essentially mq allows you to take a snapshot of your changes andcreate a patch file. You may then make additional changes to beapplied to the same patch file (qrefresh) or create a new patch (qnew)These patches may then be removed (qpop) and reapplied (qpush)at your leisure. So that's the theory, lets take a look using an example:

Enabling mq

To enable mq on OpenSolaris simply add it to your extensions in.hgrc:

$ head -2 ~/.hgrc[extensions]mq=Some data to work on.

For the following examples I've created a repository and added to it asimple C 'First program' and a make(1) file to compile anddelete the program... Complete with some faults to fix.

$ hg init qexample$ cd qexample$ echo 'int\nmain(int argc, char *argv[])\n{\n\tprintf("hello word");\n}'$ 1> hi.c$ echo src='hi.c\nbin=${src:%.c=%}\nall: ${bin}\nclobber:\n\trm ${bin}'$ 1> Makefile$ hg commit -m 'First program' -u beginner -A hi.c Makefile$ cd ..Clone repository and initialise for use with mq

Create a clone of qexample called qex1 and initialise it for mqusing qinit -c.

$ hg clone qexample qex1updating working directory2 files updated, 0 files merged, 0 files removed, 0 files unresolved$ cd qex1$ hg qinit -cFix Makefile clobber target

The Makefile has some issues, firstly the clobber target.

$ make clobber hirm hirm: hi: No such file or directory*** Error code 2make: Fatal error: Command failed for target `clobber'First Patch, Makefile

So here is our first issue, the clobber build fails as 'rm' failed andcauses make to exit. To address that we can simply add a -f to rm,so lets do that for our first patch.

$ hg qnew MakefileThe qnew command states we want to create a new patch, the argumentis the name of the patch. This action also creates a changeset, don'tbe concerned about that now.

$ hg logchangeset: 1:d8ddc677fa77tag: qtiptag: Makefiletag: tiptag: qbaseuser: Stacey Marshall ‹Stacey.Marshall-AT-Sun-DOT-COM›date: Tue Oct 27 21:17:03 2009 +0000summary: [mq]: Makefilechangeset: 0:390c5f52f8e3tag: qparentuser: beginnerdate: Tue Oct 27 21:17:02 2009 +0000summary: First program$ sed 's/rm /rm -f /' Makefile > m$ mv m Makefile$ make clobber hirm -f hicc -o hi hi.c "hi.c", line 4: warning: implicit function declaration: printf$ hg qrefreshAfter applying the fix clobber target now works. However, now thecompiler's giving us warnings! That's a new issue, so I update theMakefile change using qrefresh and start a new patch:

Second patch, remove warnings.

This time qnew is used with the -m option to add a description.Note this description is also applied to the changeset, it can bechanged later if necessary:

$ hg qnew -m 'remove implicit function delcaration warnings' fix1$ (echo '#include ‹stdio.h›'; cat hi.c) > new.c$ mv new.c hi.c$ hg log -l1changeset: 2:4ffb58a8196ftag: qtiptag: tiptag: fix1user: Stacey Marshall ‹Stacey.Marshall-AT-Sun-DOT-COM›date: Tue Oct 27 21:17:03 2009 +0000summary: remove implicit function declaration warnings$$ hg qdiffdiff -r d0b382d0a781 hi.c--- a/hi.c Tue Oct 27 21:17:03 2009 +0000+++ b/hi.c Tue Oct 27 21:17:03 2009 +0000-AT--AT- -1,3 +1,4 -AT--AT-+#include ‹stdio.h› int main(int argc, char *argv[]) {$ make hicc -o hi hi.c $ hg qrefreshThe include was added to hi.c to remove the warning, and again thechange updated using qrefresh.

Third patch, retrospective patching

Its likely that you'll start making changes before remembering tostart a patch with qnew. For those occasions the -f option comesto hand to place all un-committed changes in to the patch. Verifiedbelow with qdiff:

$ ./hihello word$ # Doh! No new line, and it was meant to say 'world'!$ sed 's/hello word/Hello world!\\n/' hi.c > new.c$ mv new.c hi.c$ hg qnew -f -m 'Fix world' message$ hg qdiffdiff -r 8a838c77402f hi.c--- a/hi.c Tue Oct 27 21:17:03 2009 +0000+++ b/hi.c Tue Oct 27 21:17:04 2009 +0000-AT--AT- -2,5 +2,5 -AT--AT- int main(int argc, char *argv[]) {- printf("hello word");+ printf("Hello world!\n"); }Listing patches with qseries

The qseries command shows us which patches have been applied, use the-v option to display 'A' (applied) and 'U' (un-applied) flags:

$ hg qseries -v0 A Makefile1 A fix12 A message$ cat hi.c#include ‹stdio.h›intmain(int argc, char *argv[]){ printf("Hello world!\n");}$Traversing the patches to make other changes.

Lets assume we want to make another change to the Makefile, butwe want to keep just the one patch for changes to Makefile.To accomplish this we pop the other two patches off. In the followingthat's done by naming the patch to pop to:

$ hg qpop Makefilenow at: Makefile$ hg qseries -v0 A Makefile1 U fix12 U messageNote now that the other two patches are un-applied.

With the just the original patch applied modify the Makefile, updatethe patch using qrefresh and then re-apply all the other patchesusing qpush -a.

$(echo '# Makefile example';cat Makefile)>new$ mv new Makefile$ hg qrefresh$ hg qdiffdiff -r 390c5f52f8e3 Makefile--- a/Makefile Tue Oct 27 21:17:02 2009 +0000+++ b/Makefile Tue Oct 27 21:17:04 2009 +0000-AT--AT- -1,5 +1,6 -AT--AT-+# Makefile example src=hi.c bin=${src:%.c=%} all: ${bin} clobber:- rm ${bin}+ rm -f ${bin}$ hg qpush -aCaution!

Some caution is needed when you want two patches that edit thesame file. Its doable but because underneath patch(1) is being usedyou may need to manually merge in rejects.

Another gotcha on older versions of Mercurial is if you add a file ina patch-queue. If the patch is popped off you may need to re-add it(hg add). Lets see that in action by adding a new file which ofcourse requires adding to the Makefile too... This seems to be fixedhowever in version 1.3.1:

$ hg qpush -aapplying fix1applying messagenow at: message$ hg qnew bye$ sed 's/Hello world/Bye/' hi.c > bye.c$ hg add bye.c$ : Make changes to makefile...$ hg qrefresh$ hg qpop Makefilenow at: Makefile$ sed 's/hi.c/hi.c bye.c/' Makefile >new$ head -2 new# Makefile examplesrc=hi.c bye.c$ mv new Makefile$ hg qrefresh$ hg qpush -aapplying fix1applying messageapplying byenow at: bye$ hg log bye.c || hg add bye.c # NEED TO CONFIRM!$ hg log bye.cchangeset: 4:cdb5958dcd87tag: qtiptag: tiptag: byeuser: Stacey Marshall ‹Stacey.Marshall-AT-Sun-DOT-COM›date: Tue Oct 27 21:17:05 2009 +0000summary: imported patch bye$ makecc -o hi hi.c cc -o bye bye.c $ Updating your repository - pull and update.

It is most likely someone is going to have integrated (push) betweenyour original clone and push, and that you'd like to merge those changes.

So Firstly, for the example, add a changeset to our parent repo;

$ hg clone ../qexample tmp$ cd tmp$ echo "README... TBD" > README$ hg add README$ hg commit -u beginner -m 'Added README for support.'$ hg push$ cd ..$ hg incoming -vcomparing with /export/home/sm26363/Mercurial/Mercurial/qexamplesearching for changeschangeset: 1:2048a9e0068btag: tipuser: beginnerdate: Tue Oct 27 21:17:05 2009 +0000files: READMEdescription:Added README for support.$ Now Hold onto your seats, this is a bit of roller-coaster ride!

Firstly, we save the series of patches using qsave, with someessential options:
  • -c : Copy Patch directory
  • -n : Specify name of directory - By default it uses patches.N where N is next number in series until an non-existent directory is found within the root .hg directory.-e : Empty the queue status file - The status file holds information about which patches are applied, so deleting it makes it look as though the patch queue is empty. The changesets are stripped of their special tags.

$ hg qsave -e -c -n incomingcopy /export/home/sm26363/Mercurial/Mercurial/qex1/.hg/patches to /export/home/sm26363/Mercurial/Mercurial/qex1/.hg/incoming$ hg glog --template '{rev}:{node|short} {author|user}\n{desc|firstline}\n\n'@ 5:db6e2d5b6aa2 Stacey| hg patches saved state|o 4:cdb5958dcd87 Stacey| imported patch bye|o 3:d20726d3cc4d Stacey| Fix world|o 2:63144e5ebe68 Stacey| remove implicit function declaration warnings|o 1:be53b09aed16 Stacey| [mq]: Makefile|o 0:390c5f52f8e3 beginner First programwe're now ready to pull the incoming changes over, DON'T use the -uoption. The update needs to be done separately:

$ hg pullpulling from /export/home/sm26363/Mercurial/Mercurial/qexamplesearching for changesadding changesetsadding manifestsadding file changesadded 1 changesets with 1 changes to 1 files (+1 heads)(run 'hg heads' to see heads, 'hg merge' to merge)$ hg glog --template "{rev}:{node|short} {author|user}\n{desc|firstline}\n\n"\ -l3o 6:2048a9e0068b beginner| Added README for support.|| @ 5:db6e2d5b6aa2 Stacey| | hg patches saved state| || o 4:cdb5958dcd87 Stacey| | imported patch bye| |The pull done, as normal we have multiple heads and possibly filesthat need merging. But as we're using patches we'll simply overwrite,local changes using option -C and re-apply the patches using qpushwith the following arguments:
  • -m : Merge from another queue - This triggers a three-way merge if the patch fails to apply with the applicable changeset. The result is a new patch based on the changes.
  • -n : Name of other-queue - the backup from previous step.-a : All patches.

$ hg update -C tip3 files updated, 0 files merged, 1 files removed, 0 files unresolved$ hg qpush -m -n incoming -amerging with queue at: /export/home/sm26363/Mercurial/Mercurial/qex1/.hg/incomingapplying Makefileapplying fix1applying messageapplying byenow at: bye$ $ hg glog --template "{rev}:{node|short} {author|user}\n{desc|firstline}\n\n" @ 11:8db2598210ab Stacey|\ imported patch bye| || o 10:2d966b806fb1 Stacey| |\ Fix world| | || | o 9:e63a43ac5532 Stacey| | |\ remove implicit function declaration warnings| | | || | | o 8:a1205da4671a Stacey| | | |\ imported patch Makefile| | | | || | | | o 7:3f838495d0e4 Stacey| | | | | [mq]: merge marker| | | | || | | | o 6:2048a9e0068b beginner| | | | | Added README for support.| | | | |+---------o 5:db6e2d5b6aa2 Stacey| | | | | hg patches saved state| | | | |o | | | | 4:cdb5958dcd87 Stacey|/ / / / imported patch bye| | | |o | | | 3:d20726d3cc4d Stacey|/ / / Fix world| | |o | | 2:63144e5ebe68 Stacey|/ / remove implicit function declaration warnings| |o | 1:be53b09aed16 Stacey|/ [mq]: Makefile|o 0:390c5f52f8e3 beginner First programAs the fictional character Ford Prefect would say, “Don't Panic!”.That is what we were expecting. We're safe now to pop the patchesoff, stay with it:

$ hg qpop -apatch queue now empty$ hg glog --template "{rev}:{node|short} {author|user}\n{desc|firstline}\n\n"@ 6:2048a9e0068b beginner| Added README for support.|| o 5:db6e2d5b6aa2 Stacey| | hg patches saved state| || o 4:cdb5958dcd87 Stacey| | imported patch bye| || o 3:d20726d3cc4d Stacey| | Fix world| || o 2:63144e5ebe68 Stacey| | remove implicit function declaration warnings| || o 1:be53b09aed16 Stacey|/ [mq]: Makefile|o 0:390c5f52f8e3 beginner First program$ hg qpop -a -n incomingusing patch queue: /export/home/sm26363/Mercurial/Mercurial/qex1/.hg/incomingsaving bundle to /export/home/sm26363/Mercurial/Mercurial/qex1/.hg/strip-backup/be53b09aed16-tempadding branchadding changesetsadding manifestsadding file changesadded 1 changesets with 1 changes to 1 filespatch queue now empty$ hg qseries -v0 U Makefile1 U fix12 U message3 U bye$ hg qlog@ changeset: 1:2048a9e0068b| tag: tip| user: beginner| date: Tue Oct 27 21:17:05 2009 +0000| summary: Added README for support.|o changeset: 0:390c5f52f8e3 user: beginner date: Tue Oct 27 21:17:02 2009 +0000 summary: First programThe above shows the incoming changeset and our patches un-applied.The patches may now be re-applied:

$ hg qpush -aapplying Makefileapplying fix1applying messageapplying byenow at: bye$ hg qlog@ changeset: 5:a19f79097ba6| tag: qtip| tag: tip| tag: bye| user: Stacey Marshall ‹Stacey.Marshall@Sun-DOT-COM›| date: Tue Oct 27 21:17:07 2009 +0000| summary: imported patch bye|o changeset: 4:74fc52a79383| tag: message| user: Stacey Marshall ‹Stacey.Marshall@Sun-DOT-COM›| date: Tue Oct 27 21:17:07 2009 +0000| summary: Fix world|o changeset: 3:63ca0f087340| tag: fix1| user: Stacey Marshall ‹Stacey.Marshall@Sun-DOT-COM›| date: Tue Oct 27 21:17:07 2009 +0000| summary: remove implicit function declaration warnings|o changeset: 2:c601487f5539| tag: Makefile| tag: qbase| user: Stacey Marshall ‹Stacey.Marshall@Sun-DOT-COM›| date: Tue Oct 27 21:17:07 2009 +0000| summary: imported patch Makefile|o changeset: 1:2048a9e0068b| tag: qparent| user: beginner| date: Tue Oct 27 21:17:05 2009 +0000| summary: Added README for support.|o changeset: 0:390c5f52f8e3 user: beginner date: Tue Oct 27 21:17:02 2009 +0000 summary: First programPreparing to push

The final step is to remove the queues, as with those intact we'reunable to push. To accomplish this use qfinish But before weclear the queues take a backup of them using qcommit and cloneas its likely we'll have to pull again as we raise to push.

Note: Simply doing a qcommit is not sufficient because qfinish tidiesup the patches, a peek into the working files shows this best - I'veavoided showing these details previously:

$ hg qcommit -m "pre-finish"$ ls .hg/patchesbye fix1 Makefile message series status$ hg qfinish -apatch Makefile finalized without changeset messagepatch bye finalized without changeset message$ ls .hg/patchesseries status$ By cloning the patches before qfinish we can then recover them afterstripping off our patched changesets, Lets look at another example.

$ hg qcommit -m 'Pre-finish'$ hg clone .hg/patches .hg/patches-backupupdating working directory6 files updated, 0 files merged, 0 files removed, 0 files unresolved$ hg qpush -aall patches are currently applied$ hg qfinish -apatch Makefile finalized without changeset messagepatch bye finalized without changeset message$ Now lets assume there is something to pull, we'll have to remove ouroutgoing changes using strip:

$ hg outgoingcomparing with /export/home/sm26363/Mercurial/Mercurial/qexamplesearching for changeschangeset: 2:c601487f5539user: Stacey Marshall ‹Stacey.Marshall@Sun-DOT-COM›date: Tue Oct 27 21:17:07 2009 +0000summary: imported patch Makefilechangeset: 3:63ca0f087340user: Stacey Marshall ‹Stacey.Marshall@Sun-DOT-COM›date: Tue Oct 27 21:17:07 2009 +0000summary: remove implicit function declaration warningschangeset: 4:74fc52a79383user: Stacey Marshall ‹Stacey.Marshall@Sun-DOT-COM›date: Tue Oct 27 21:17:07 2009 +0000summary: Fix worldchangeset: 5:a19f79097ba6tag: tipuser: Stacey Marshall ‹Stacey.Marshall@Sun-DOT-COM›date: Tue Oct 27 21:17:07 2009 +0000summary: imported patch bye$ hg strip 22 files updated, 0 files merged, 1 files removed, 0 files unresolvedsaving bundle to /export/home/sm26363/Mercurial/Mercurial/qex1/.hg/strip-backup/59b69da7d392-backup$ hg logchangeset: 1:2048a9e0068btag: tipuser: beginnerdate: Tue Oct 27 21:17:05 2009 +0000summary: Added README for support.changeset: 0:390c5f52f8e3user: beginnerdate: Tue Oct 27 21:17:02 2009 +0000summary: First program$ Recover the backups:

$ rm -rf .hg/patches $ hg clone .hg/patches-backup .hg/patchesupdating working directory6 files updated, 0 files merged, 0 files removed, 0 files unresolved$ hg qseries -v0 U Makefile1 U fix12 U message3 U bye$ So there we have it, we can then check the incoming, if it touchesanything we've edited then we can go through the merge operationabove and or pull, update re-apply patches, finish and patch.

Changing changeset message

As seen above qfinish nicely told us that some changesets weremissing messages. These can be applied using qrefresh -m:

$ hg qseries -v0 U Makefile1 U fix12 U message3 U bye$ hg qpush Makefileapplying Makefilenow at: Makefile$ hg qrefresh -m "Makefile changes"$ hg qpush byeapplying fix1applying messageapplying byenow at: bye$ hg qrefresh -m "Bye: says goodbye"$ hg glog@ changeset: 5:202431aaf294| tag: qtip| tag: tip| tag: bye| user: Stacey Marshall ‹Stacey.Marshall@Sun-DOT-COM›| date: Tue Oct 27 22:45:47 2009 +0000| summary: Bye: says goodbye|o changeset: 4:7494b600f084| tag: message| user: Stacey Marshall ‹Stacey.Marshall@Sun-DOT-COM›| date: Tue Oct 27 22:45:21 2009 +0000| summary: Fix world|o changeset: 3:69c047974d13| tag: fix1| user: Stacey Marshall ‹Stacey.Marshall@Sun-DOT-COM›| date: Tue Oct 27 22:45:21 2009 +0000| summary: remove implicit function declaration warnings|o changeset: 2:439652f08146| tag: Makefile| tag: qbase| user: Stacey Marshall ‹Stacey.Marshall@Sun-DOT-COM›| date: Tue Oct 27 22:45:15 2009 +0000| summary: Makefile changes|o changeset: 1:2048a9e0068b| tag: qparent| user: beginner| date: Tue Oct 27 21:17:05 2009 +0000| summary: Added README for support.|o changeset: 0:390c5f52f8e3 user: beginner date: Tue Oct 27 21:17:02 2009 +0000 summary: First programNote: Obviously for OpenSolaris repository the messages must followthe bug-id synopsis format.



More...