Jesper Noehr

Pythonista, RESTafarian, Binary Poet & Proud Bucketeer

Mercurial powertip: Move changesets out of the way momentarily

with 2 comments

Sometimes you may be working in a repository, and want to momentarily move changesets out of the way. From what I can gather, you can get the same results as you get with “git stash”, but it offers much more.

Say that you have been working on an experimental feature, but need to fix a bug. You don’t want to sit and be careful only to commit the files modified by the bugfix, especially if the bugfix touches files you’ve already modified.

Your log could look like this:

$ hg log
changeset:   2:41009a6aa783
tag:         tip
summary:     adding B

changeset:   1:419ab519b195
summary:     adding C

changeset:   0:8f276b14c116
summary:     adding A



Now, changeset 1 & 2 are the experimental changes. You need to get rid of these before you can fix the bug.

It’s important that you have a “patch queue” repository inside your repository first, this is what “qinit” is for. Afterwards, we’ll import the changesets into the patch queue, using qimport:

$ hg qinit -c # tell hg to create a versioned patch queue in .hg/patches/
$ hg qimport -r 2:1


Now lets take a look at the log:

$ hg log
changeset:   2:41009a6aa783
tag:         qtip
tag:         2.diff
tag:         tip
summary:     adding B

changeset:   1:419ab519b195
tag:         1.diff
tag:         qbase
summary:     adding C

changeset:   0:8f276b14c116
tag:         qparent
summary:     adding A



The changesets are still there, but they’re a little different; They’ve been tagged with a couple of things – first, is the filename the changeset was saved as. In this case, your changes are in ‘.hg/patches/1.diff’ and ‘.hg/patches/2.diff’. Go on, have a look. There’s also some new semantic, namely ‘qbase’, ‘qtip’ and ‘qparent’. This is a way for MQ to keep track of the queue tip, the queue base and the parent.

But, you may notice that the changesets are still present. This is because they are “applied” to the repository. To get rid of them, we use qpop:

$ hg qpop -a # pop all patches from the stack
patch queue is now empty
$ hg log
changeset:   0:8f276b14c116
tag:         tip
summary:     adding A



Lovely. You can now see which patches are available via qseries:

$ hg qseries
1.diff
2.diff



To pop them back on the stack, you can use ‘qpush -a’. But first, we have a bug to fix:

$ echo 'D' > D
$ hg add D
$ hg ci -m "Adding D, which we'll pretend fixes a bug."



And the log:

$ hg log
changeset:   1:5d41625a80b5
tag:         tip
summary:     adding D (which is a bugfix)

changeset:   0:8f276b14c116
summary:     adding A



Now push that fix out, or whatever you want to. Time to get the experimental changesets back. We’ll use ‘qpush -a’ for that:

$ hg qpush -a
applying 1.diff
applying 2.diff
now at: 2.diff



You can run log to see what happened. Needless to say, your patches are there. Lets turn them back into normal changesets:

$ hg qfinish 3:2 # they're not 2:1 anymore, we have another changeset
                   in before them now, consult 'hg log' for details
$ hg log
changeset:   3:1a07541824d3
tag:         tip
summary:     adding B

changeset:   2:b4a1402f9b50
summary:     adding C

changeset:   1:5d41625a80b5
summary:     adding D (which is a bugfix)

changeset:   0:8f276b14c116
summary:     adding A



Et viola.

There’s much more you can do with MQ. If you’re only importing a single changeset, you can name the patch via ‘qimport -n’. You can give your patches to other people, and you can even push your patch queue around. ‘qimport’ will even import patches from outside your repository. You can move the order of patches around, you can do guards, .. MQ is really a wonderful addition to Mercurial.

Written by jespern

April 10th, 2009 at 12:04 pm

Posted in hg

2 Responses to 'Mercurial powertip: Move changesets out of the way momentarily'

Subscribe to comments with RSS or TrackBack to 'Mercurial powertip: Move changesets out of the way momentarily'.

  1. While this definitely works, why use MQ for it? You could just use branching and merging to avoid destroying history while getting the same effects, or use rebasing to edit the history with less commands to remember.

    For example (I hope Markdown works in the comments…):

    $ hg glog
    @ changeset: 2:45ef854eb6aa tip
    | summary: add B
    |
    o changeset: 1:7bcb1d228f55
    | summary: add C
    |
    o changeset: 0:491e35bba6dd
    summary: add A

    $ hg update -C 0

    $ touch D

    $ hg commit -Am ‘add D’

    $ hg glog
    @ changeset: 3:23207da82cce tip
    | summary: add D
    |
    | o changeset: 2:45ef854eb6aa
    | | summary: add B
    | |
    | o changeset: 1:7bcb1d228f55
    |/ summary: add C
    |
    o changeset: 0:491e35bba6dd
    summary: add A

    $ hg update -C 2
    2 files updated, 0 files merged, 1 files removed, 0 files unresolved

    $ hg merge tip
    1 files updated, 0 files merged, 0 files removed, 0 files unresolved
    (branch merge, don’t forget to commit)

    $ hg commit -m ‘Pull the bugfix into the experimental branch.’

    $ hg glog
    @ changeset: 4:0ddd228568c5 tip
    |\ summary: Pull the bugfix into the experimental branch.
    | |
    | o changeset: 3:cd7196bb6bd1
    | | summary: add D (this is a bugfix)
    | |
    o | changeset: 2:45ef854eb6aa
    | | summary: add B
    | |
    o | changeset: 1:7bcb1d228f55
    |/ summary: add C
    |
    o changeset: 0:491e35bba6dd
    summary: add A

    or

    $ hg glog
    @ changeset: 3:23207da82cce tip
    | summary: add D
    |
    | o changeset: 2:45ef854eb6aa
    | | summary: add B
    | |
    | o changeset: 1:7bcb1d228f55
    |/ summary: add C
    |
    o changeset: 0:491e35bba6dd
    summary: add A

    $ hg rebase –source 3 –dest 2
    saving bundle to …path here…
    adding branch
    adding changesets
    adding manifests
    adding file changes
    added 1 changesets with 1 changes to 1 files
    rebase completed

    $ hg glog
    @ changeset: 3:ca6d9cbfdbef tip
    | summary: add D (this is a bugfix)
    |
    o changeset: 2:45ef854eb6aa
    | summary: add B
    |
    o changeset: 1:7bcb1d228f55
    | summary: add C
    |
    o changeset: 0:491e35bba6dd
    summary: add A

    MQ is extremely powerful and great for stuff like folding together or rearranging commits, but it’s a pretty complex tool to use if all you want to do is move a changeset temporarily.

    Steve Losh

    10 Apr 09 at 3:24 pm

  2. Steve,

    You’re right, TIMTOWTDI. Since this is something that can be done without the use of MQ, I figured it’d also serve as a gentle introduction to MQ :-)

    Thanks for adding the instructions on how to do this with normal Hg and rebase (I haven’t used it much), I for one found it very informative, and I’m sure other people will too.

    jespern

    10 Apr 09 at 3:36 pm

Leave a Reply