Also see the corresponding chapters, 12 and 13, in the HG Book.

Mq Tutorial

1. Mq for the impatient

It's one of those things that sounds a lot harder than it is. It's basically just orthogonal, mutable changesets. The mutability is exactly what it sounds like you're looking for. You can keep revising a changeset until it's good enough, then transfer control to regular Mercurial.

Basically, you do this:

  hg init --mq
  hg qnew some-changes-i-want-to-make
  [edit some stuff]
  hg qrefresh # update the patch with the new changes
  [edit some more stuff]
  hg qrefresh # update the patch with still yet newer changes
  hg qfinish some-changes-i-want-to-make # Make the patch a permanent commit

If you use the -c option to qinit, you get revision control of the queue itself, so you can use qcommit to save the state of your patch queue.

The patch queue aspect is actually quite nice as well. You can, for example, work on two different layers of your application (something low-level and something built on top of that) in the same queue at the same time without breaking stuff. qpush and qpop will help you move up and down while keeping your changes separate.

(original version by dustin sallings, mercurial mailing list, 2007-08-11)

2. Why would I want to use mq?

2.1. Save a known-good but not yet perfect version of a change

While working on a patch, when you arrive at a point that is not yet ideal but does work, use qrefresh to refresh your current patch with the current state of the project. Then, while further refining the patch, if you decide that you're moving in the wrong direction and want to get back to a known-good state, you can use hg's revert command to bring things back to the state you last saved in the current patch.

2.2. Preventing the tangled-working-copy problem

Often, while developing one change, you'll notice something else that could use improvement. It could be anything from a style issue to another bug, unrelated to the one you're working on.

If you're lucky, the file where you found this other problem is as yet unmodified, so you can safely commit the fix for the new problem separately from whatever you were originally working on. More probably, though, the file is already dirty with some portion of the original change.

Without mq, when this happens, you have three choices:

  1. Promise to yourself that you'll remember this other problem, then forget it a few minutes later.
  2. Write it down somewhere.
  3. Fix it now, and confound your next commit with both what you were originally working on and the fix for the problem you discovered.

With mq, the solution is easy:

  1. Momentarily stop working on the original change.
  2. Add any outstanding portions of that change into your current patch.
  3. Create a new patch.
  4. Fix the problem you discovered.
  5. Add that fix into the new patch.
  6. Return to your original patch.

If you're fastidious, you'll fall in love with this the first time you do it, because this process lets you keep separate changes in separate commits, never mixing them and rarely having to pull them apart.

2.3. Mutable, rearrangeable commits

Commits that are part of permanent history are immutable. You cannot change their contents or rearrange them, because that would change their hashes, which Mercurial uses to uniquely identify them.

Patches in your patch queue are part of history, but they are not yet permanent, so they are mutable. You can change their contents and their commit messages, change their order, or even delete them outright, as long as you haven't made them permanent yet.

3. What is all this talk about a stack?

Before we begin this tutorial, there are 3 things to keep track of when using the mq extension: the localRepository, the mqPatches, and the workingDirectory. When the mq documentation talks about a stack, it refers to the localRepository as a stack on which one can push/pop patches. mqPatches works like a queue, where the first patch you create would be the first patch to be pushed to the localRepository stack.

4. Working with mq

4.1. Get a repository to do work on

This can be done by either initializing a brand new Mercurial repository yourself (ie. using hg init) or by cloning an existing repository. For this tutorial we will use the same repository used in the Mercurial Tutorial.

hg clone http://www.selenic.com/repo/hello

4.2. Indicate that you want to use mq on this repository

mq provides the qinit command to initialize mq on a given repository. Note: Be sure to enable the mq extension first before running this command. To start using mq on our recently cloned repository use:1

{i} Note that the qinit command is deprecated. Use "hg init --mq" instead.

cd hello
hg qinit -c

You can type hg help qinit to see help on the specifics of the command. Here we are telling mq to not only initialize an mq queue, but also to keep itself versioned with Mercurial. Mq does this by creating a new repository for use by itself under hello/.hg/patches.

4.3. Creating and editing a patch

Now we are ready to start hacking the hello code away. The first step is to create a "patch holder" which will contain the changes that we make. To do this execute:

hg qnew -m "Change language of salutation" newHelloString

The -m flag allows you to pass the message to be used in the changeset. This is the same message that would be placed if you did a hg commit -m.

Let's now edit hello.c and change line 14 so that you end up with a file that looks like this:

  12 int main(int argc, char **argv)
  13 {
  14         printf("Howdy there!\n");
  15         return 0;
  16 }

At this point our changes are only recorded in the working directory. To verify this we can execute hg status and see that the hello.c file has been marked as modified. We will record the changes into our current patch (newHelloString) by executing hg qrefresh. If we run hg status again, we will see that there are no pending changes in our workingDirectory. Let's ask mq what patches it has pushed on top of the repository stack by running hg qapplied. mq tells us that it has applied newHelloString, and we can verify this by running hg log and noticing that the repository has that patch in its history. [Hmmm... where do we need to talk about push/pop?]

4.4. Dealing with more than one patch on our patch queue

We now realize that our hello.c file needs to be able to tell us who it is, and in good form, we don't want to mix our changes to the salutation with this new modification (the why of this is beyond the scope of this tutorial). So we create a new patch and make the changes:

hg qnew -m "Have hello say who he is" sayName

... edit hello.c and add line 15 below to it

  12 int main(int argc, char **argv)
  13 {
  14         printf("Howdy there!\n");
  15         printf("I'm is 'hello.c'!!!\n");
  16         return 0;
  17 }

Lets commit our changes to the mq queue by running hg qrefresh. We can now see that mq has applied the last two patches to the repository by running both hg qapplied and hg log.

4.5. Versioning our patch set

This amounts to using the standard hg commands on the patch repository in .hg/patches. MQ provides qcommit as a shorthand for commit in the patch directory, but for more advanced uses (like sharing the patch queue among multiple repositories), you might find a function like the following shell alias convenient:

alias mq='hg -R $(hg root)/.hg/patches'

If you're using Windows and Powershell, you can achieve the same thing by adding this to your Powershell Profile:

function mq() {
        $root = hg root
        hg -R $root/.hg/patches $args
}

You can also simulate the above alias under Windows for cmd.exe. Save the following to a file named mq.cmd, and make sure that script can be found in your execution path. I admit it's a little bit longer then the *nix one, but hey, it works. And if someone knows of a simpler way, please teach it to us. (Only tested with Windows 7.)

@echo off
::
::  mq.cmd
::
::  Same as 'hg' (well, almost), but works on the repo of the active
::  mq patch series. You still can give an '-R <.hg/patches-<patchname>'
::  argument, which will override the -R in this script, as it should.
::  You must already be within a Hg repo working dir however.
::  It works even with spaces in the repo-path, and with spaces in the
::  patchname. Though use proper quoting at the commandline then.
::
::  Usage (pre-1.6) : mq [-R [["]path/to["]/.hg/patches] COMMAND [ARGS]
::  Usage (pre-1.6) : mq [-R [["]path/to["]/.hg/patches] COMMAND [ARGS]
::  Usage (1.6+) : mq [-R [path/to/].hg/patches[-patchname]] COMMAND [ARGS]
::  Usage (1.6+) : mq [-R [["]path/to["]/].hg/patches[-["]patchname["]]] COMMAND [ARGS]
::
::  Copyright (C) 2010 Johan Samyn <johan.samyn@gmail.com>
::  Creation date: 2010-07-10
::
setlocal EnableDelayedExpansion
::
:: Activate the appropriate goto below, according to your Hg version.
:: The difference is that multiple mq patch queues was introduced with 1.6.
::goto hg-pre-1.6
goto hg1.6_and-up
goto done
::
:hg-pre-1.6
set v=hg-pre-1.6
set rootdirfile=%temp%.\hgroot.txt
hg root > %rootdirfile%
for /f "usebackq delims=" %%r in (%rootdirfile%) do (
    set hgroot=%%r
    hg -R "!hgroot!".\.hg\patches %*
)
goto done
::
:hg1.6_and-up
set v=hg1.6+
set rootdirfile=%temp%.\hgroot.txt
hg root > %rootdirfile%
for /f "usebackq delims=" %%r in (%rootdirfile%) do (
    set hgroot=%%r
    set patchdirfile=%temp%.\hgqq.txt
    hg qqueue | findstr /i "(active)" > !patchdirfile!
    for /f "usebackq delims=(" %%p in (!patchdirfile!) do (
        set patchname=%%p
        :: Get rid of the space between the patchname and the '(' delimiter.
        call :rtrim !patchname!
        if "!patchname!" == "patches" (
            hg -R "!hgroot!".\.hg\patches %*
        ) else (
            hg -R "!hgroot!".\.hg\patches-"!patchname!" %*
        )
    )
)
::
:done
if "%v%" == "" (
    echo Please activate the right goto in the beginning of the script.
) else (
    del /f %rootdirfile% %patchdirfile%
)
endlocal
goto :EOF
::
:rtrim
set patchname=%*
goto :EOF

4.6. Synchronizing with upstream

Time goes by and we need to synchronize with upstream. Before we do this, we need to remove our mq queue of patches from the local repository and then reapply those patches after a successful pull. So we would do:

hg qpop -a
hg pull -u
hg qpush -a

For a guide on using MQ to do three-way merges of upstream changes and the patch queue, see MqMerge.

5. Further reading

6. Other tips and tricks

6.1. Convert a patch to a permanent changeset

You can use qfinish <revision> on an applied patch (where revision can be any symbolic name for a revision, including a patch name) to turn it into a regular Mercurial changeset. qfinish -a will convert all applied patches into changesets.

qfinish replaces qdelete -r <revision>, which is deprecated since Mercurial 1.2. Previously, one of the greatest criticisms of mq was the confusing use of qdelete to make patches into permanent commits; the qfinish command exists to solve that problem.

qfinish is equivalent to doing hg import .hg/patches/<patchname>, followed by hg qdel <patchname>.

6.2. Add/Change the message

The qrefresh command has two parameters which can be used to add or change the commit message associated with that patch. Use the -m parameter to set the message or the -e parameter to edit the message.

hg qrefresh -m "This is the new commit message"

The message may also be manipulated manually. The message is stored directly in the patch file in .hg/patches/[patch name]. If there is no message, the patch starts directly with the diff. Otherwise, the first line following any comments is the message, the second line is an empty line, and subsequent lines are the diff.

6.3. Split a patch into multiple patches

Goal
End up with OP=P1 + P2, where OP=Original Patch, P1=Patch 1, P2=Patch 2

6.3.1. Splitting by files

Starting from 0.9.2, it is easy to split a patch by file:

6.3.2. Splitting by content

You can split patches at the hunk level interactively using the RecordExtension.

otherwise, you have to do it manually:

6.4. Reordering patches

In the past, the only way to reorder patches was to edit .hg/patches/series. In recent versions of Mercurial, this can be done instead:


  1. Make sure you enabled mq as described in MqExtension. (1)

MqTutorial (last edited 2015-02-25 15:14:45 by IanMoody)