Mercurial Queues

From Eigen
Jump to: navigation, search


Mercurial Queues (MQ) is an extension for Mercurial that can make you much more productive in handling patches. But it also is dangerous and can make you lose work or do wrong things with the Eigen repository if you don't use it correctly.

Only consider using MQ if you feel really comfortable already with Mercurial. This is useful for developers who need to exchange lots of patches with Bugzilla.

MQ creates a separate hg repository in your local hg repository, specifically under .hg/patches/. Think of it as a specialized hg repo to handle patches before you want to commit them to the actual hg repo.

Lots of documentation can be found on the Web, for example there.

Setting up

In your Mercurial config file, put:

[extensions]
hgext.mq =

Then create the MQ repository: enter your local hg repository, and do:

 $ hg qinit -c

Basic usage

Creating a new patch

Suppose that you have coded something (i.e. made changes in your working copy). You now want to record these changes as a patch, i.e. as a changeset in the MQ repo. Do:

 $ hg qnew -f my-wonderful-improvements

Now you can see your patch, ready to be sent for review, as this file:

 .hg/patches/my-wonderful-improvements

Notice that qnew is the only MQ command with which we will use the -f flag. With qnew, all it means is "take all my local changes into this new patch". With other MQ commands, the -f flag often means "force" which is generally dangerous.

From there, you can continue editing your working copy. The usual command:

 $ hg diff

will return the changes not yet committed as a patch.

You can use qnew -f again to create a second patch from your new changes:

 $ hg qnew -f my-additional-improvements

You now have 2 patches in your patch series or queue.

Viewing and moving around in your patch queue

Use these commands:

 $ hg qser             # list all patches in your series
 $ hg qapp             # list all applied patches
 $ hg qunapp           # list all unapplied patches
 $ hg qpop             # unapply current patch
 $ hg qpop -a          # unapply all patches
 $ hg qpop somepatch   # unapply patches until somepatch is current
 $ hg qpush            # apply first unapplied patch
 $ hg qpush -a         # apply all patches
 $ hg qpush somepatch  # apply patches until somepatch is current

Updating an existing patch

If you want to modify an existing patch, make it current (using qpop or qpush), make changes to your working copy, and do:

 $ hg qref             # refresh current patch

You can edit its commit message:

 $ hg qref -e          # use text editor to edit commit message
 $ hg qref -m 'hello'  # directly set commit message
 $ hg qref -u 'john'   # directly set author field

Importing a patch from a file

Just do:

 $ hg qimport somefile

This will only insert this new patch into your queue, but won't apply it. To apply it, just do:

 $ hg qpush

Turning a patch into a permanent changeset

The command for this is:

 $ hg qfinish some_mq_changeset

Typically, you want to do this for all currently applied patches. The command for this is:

 $ hg qfinish -a

Your patch has now been removed from your queue and turned into a changeset in your real hg repository. You can now push it to the public Eigen repository.

Advanced usage

Showing a log of all applied patches

Your first applied patch is referred to as qbase. Your last applied patch is qtip. So you can get such a log just by doing:

 $ hg log -r qbase:qtip

Reordering your patch queue

Say you have patch1 and patch2 in your queue, and you want to have only patch2 applied.

Problem: if you do

 $ hg qpush patch2

that will push patch1 too, because it comes first in your queue.

Solution: make sure you have Mercurial 1.6 or newer, and do:

 $ hg qpush --move patch2

Combining two patches into one

You have patch1 and patch2 (in this order), and you want to turn them into one big patch. Make patch1 be the current patch, and do:

 $ hg qfold patch2

Splitting a patch, the easy case: per-file splitting

If you have a patch that modifies file1 and file2, and you want to split it into two patches each modifying only one file, do:

 $ hg qgoto my-patch
 $ hg qref -X path/to/first/file         # take changes out of current patch and back into `hg diff`
 $ hg qnew -f patch-modifying-first-file # and take that into a new MQ patch
</pre>

Here, the qref -X command takes the changes to the first file out of the patch, so that they now show up in hg diff and therefore get picked up by the hg qnew -f.

Splitting a patch, the general case, including per-hunk and per-line splitting

If you need to perform finer patch splitting, for example per-hunk or even per-line, there's a great tool for that: hg qcrecord. It's provided by the Crecord extension. Follow the instructions on that page to install it. This extension works on your current hg diff. So if you had your patch as a MQ patch, you first need to take the changes out of it, using hg qref -X.

 $ hg qref -X .          # take changes out of current patch and back into `hg diff`
 $ hg qcrecord new-patch

This will open a console-based dialog allowing you to select file-by-file, hunk-by-hunk, and even line-by-line, what changes you want to record into new-patch. When you first launch hg qcrecord, shows you a list of modified files:

SELECT CHUNKS: (j/k/up/dn/pgup/pgdn) move cursor; (space/A) toggle hunk/all
 (f)old/unfold; (c)ommit applied; (q)uit; (?) help | [X]=hunk applied **=folded
[X]**M hello.cpp

Let's now press 'f' to unfold hello.cpp:

SELECT CHUNKS: (j/k/up/dn/pgup/pgdn) move cursor; (space/A) toggle hunk/all
 (f)old/unfold; (c)ommit applied; (q)uit; (?) help | [X]=hunk applied **=folded
[X]    diff --git a/hello.cpp b/hello.cpp
       2 hunks, 4 lines changed

   [X]     @@ -1,4 +1,5 @@
            #include <iostream>
      [X]  +#include <cmath>
           #include <cstdlib>

           double square(double x)

   [X]     @@ -8,5 +9,6 @@

            int main()
            {
      [X]  -  std::cout << square(3.2) << std::endl;
      [X]  +  double x = 2.0;
      [X]  +  std::cout << std::sqrt(square(x)) << std::endl;
           }

This allows us to select the lines to record in the patch. When we're done, we press 'c'.

Undoing a bad qfinish

Make sure you have no patches applied, and do:

 $ hg qimport -r tip

Warning! This qimport command, with the -r flag, is able to import the top-most changeset even if it was already pushed to the Eigen repository. It really manipulates your real hg repository: the changeset is removed from it, and moved into the patch repository. Only ever do this on a newly qfinish'ed changeset! Never ever qimport a changeset that has already been pushed to the public Eigen repository!