Folding changesets with the Mercurial Queues extension

Mercurial is my distributed revision control system of choice, a trait I picked up at my previous job. I haven’t had the opportunity to deal with Git for any period of time, so I can’t comment on the various “Why X is better than Yarguments out there.

Most of you using Mercurial/Hg (or revision control in general) will be familiar with the concept of merging, where the changes in a source branch are merged into a target branch, creating a new revision or changeset on the target branch. But what about the times when you would like to combine two or more changesets/revisions into a single one that has the combined/overall changes of all of them? In that case, the Mercurial Queues extension provides for the concept of folding, which accomplishes just that.

Mercurial Queues

The Mercurial Queues extension (mq) is one of the most useful Hg extensions available, as it allows for much more than folding. Basically, mq allows you to import changesets to a patch queue, where they can be manipulated. In this way, previously immutable changesets can be modified. One such modification is the concept of folding, where one more more changesets are combined to produce a single changeset with the equivalent effect.

Proceed with caution

Because mq can directly modify changesets (i.e. revision history), it can have adverse on your Hg repository; data can be permanently lost, since the changes that can be made cannot be “backed out” or “reverted”. I strongly recommend cloning your Hg repo before undertaking any actions with Mercurial Queues for the first time, if only for peace of mind and the fact that cloning is such an easy operation with Mercurial.

The case for folding

Generally, it’s not a good idea to go and change revision history, which is what folding does. The purpose of revision control is to preserve history and provide a timeline for how the codebase has evolved. In the case of Mercurial, it’s especially troublesome to go and modify revision history on a central server that others are pushing to/pulling from, as this may cause inconsistencies. Having said that, there are times when combining multiple changesets into one may be beneficial.

Some examples that come to mind are:

  • You have made many intermediate commits to your local Hg repository during development, but would like to combine them into a single one before pushing to a remote/central repository for clarity. (The multiple local commits were for your own sake)
  • You accidentally committed some sensitive data (i.e. hard-coded passwords) to a repository, followed by some other changes that also removed that sensitive data. You don’t want to strip the intermediate (password-containing) revision because of the subsequent work. You can combine the two to one revision that contains the subsequent work but no record of the sensitive data.

Folding will help in both situations.

Tutorial

This tutorial will cover folding using TortoiseHg, the popular GUI front-end for Mercurial. Before getting started, you’ll have to ensure that you don’t have any uncommitted changes, as you won’t be able to accomplish some of the steps with outstanding changes.

Open up the Repository Explorer. Below, you can see the example one, with our initial commit, which contained the sensitive data, followed by another that removed it. We’ll merge them into one.

Next, you’ll need to enable the Mercurial Queues extension, either through the TortoiseHg UI or by manually editing your mercurial.ini file or per-repo setting file. (.hgrc) Below, I’ve shown it in the UI: You need to check off the “mq” box under “Extensions”.

You’ll need to close the Repository Explorer and re-open it for the mq extension to take effect in the UI.

Next, select the oldest changeset and then right-click the newest one; in the context menu, select “Import from here to selected MQ”.

Then, open the Patch Queue by clicking its icon or selecting View > Patch Queue from the menu.

You should be able to see the changesets in your patch queue on the left side drawer. They’ll be labeled 0.diff, 1.diff, etc. You can also see which patch corresponds to which changeset because the imported changesets will have be labeled in the UI with qbase, qtip, 0.diff, 1.diff, etc. (They are listed in the patch queue in opposite order)

Now, right-click the top/oldest one in the patch queue, and select “Goto”. This will bring you back to the oldest changeset and remove the previous ones from your repository. But don’t worry, the changesets still exist in your (mercurial) queue.

Now, right click the next oldest one (which was removed from your repository), click “Fold” and then “Yes”. This will effectively fold or combine the two changesets into a single one with the equivalent changes/effects. Please note that this operation cannot be reversed, so make sure you really want to do it. (You did clone/backup your repository before starting, right?)

You can now adjust the commit comment/message and username, if you like, by clicking “Commit” or by selecting Tools > Commit from the menu. You may need to select View > Advanced to change/show the username. By default the commit message of the folded/unified changeset is an aggregate of the changesets that were combined to form it. You’ll probably want to adjust this down to something more meaningful.

After you are done, be sure to click “QRefresh” before closing the window.

After you’ve done the folding and modifying the commit message, you can right click the patch file (0.diff in this case) and select “Finish Applied” to put the folded/unified changeset back into your repository.

Your MQ patch queue should now be empty and the repository browser should show only one changeset, with your updated comments and no MQ tags/labels on the changeset. You can close the patch queue sidebar now, if you’d like.

There! Now all evidence of your nefarious activities has now been removed. Only the beginning and the end remain. Use wisely.

No Comments »

Post a Comment

(required)

(will not be published) (required)

XHTML tags allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Note: rel="nofollow" will be added to all links in comments.