Undoing Work in Mercurial

Some context

The genesis of this post was a training session I had with a coworker who was new to distributed version control. Two things to keep in mind:

  1. Up until fairly recently, our dev shop was using TFS.
  2. We’re now using Mercurial, which is a bit different in mindset than TFS. (Please, no flame wares about how I should use Git.)

One advantage of having distributed version control is that you can commit as often as you like locally — you don’t even need a network connection! For some reason, I thought in my head that you could simply jump to any revision in the chain just like you can seek to a part of a song on a music player.

rev-history-like-slider

Apparently it’s slightly more complicated.

This post outlines five common ways to undo previous work. (I won’t cover the rollback command.)

Method 1: Undo since last commit

If you’ve added, modified, or removed files since your last hg commit, this solution is simple:

hg revert --all

This simply puts the state back to where things were at the last commit.

Analogy: You’ve saved your document two minutes ago, made some changes in the interim, then decide to close the document without saving changes.

Method 2: Undo since last push

When I’ve gotten the code into such a state that it’s best to just start over, this is my tactical nuke approach.

  1. Delete the local repository.
  2. Pull from the remote repository.

Analogy: You’re coworker e-mailed you a document yesterday. You’ve done several rounds of (saved) revisions, but you don’t like where things stand. It will be too much effort to get out of the corner you’re painted into. The solution is to delete the working document and re-download it anew from the e-mail.

Method 3: Update to a different version

You can tell Mercurial to treat any revision (changset) as the working version. Let’s say you’re on revision 168 and want to put things back to where they were at revision 164.

hg update --rev 164 --clean

Two things have happened here:

  • The clean option discards any changes that have happened between revisions 164 and 168.
  • The commits are still there, so Mercurial maintains that line of work. This is what gives the dreaded “multiple heads” warning.

update-to-previous-revision

Although the contents of the directory are indeed where you’d like them, there’s the history of stuff you’re rather didn’t exist. I find this approach rather confusing given the stray branch shouting, “I need merge attention!” when that’s really not the case.

Analogy: You’re on a road trip and are using your trusty GPS. Nature calls, so you exit the interstate. Your GPS calmly tells you at every intersection to turn around and get back on track.

Method 4: Backout

Backing out a revision is done by making the necessary changes at the current state to make it appear as if that specific revision never occurred.

hg backout 164

This could be useful if a revision introduced a bug, and you want history (via a commit) that the changes will be undone.

Analogy: A strange salad recipe…

  1. Add lettuce
  2. Add cucumbers
  3. Add tomatoes
  4. Remove cucumbers
  5. Add carrots

Method 5: Strip

At last, the rewind capability I wanted! This command puts the working directory back to a specific state and pretends nothing happened beyond that point.

hg strip 168 --force

Note that you’ll need to enable the Mercurial Queues Extension (which ships with stock Mercurial); see the section below for instructions.

Analogy: Undo (but no redo).

Enabling the Mercurial Queues Extension

You can manually enable it via the configuration file, which is most likely in %HOME%\Mercurial.ini.

[extensions]
mq =

If you use TortoiseHg Workbench:

  1. Choose Settings from the File menu.
  2. Select Extensions in the left pane, then check mq.
  3. Click OK. (You will have to restart Workbench to get “Strip” in the context menu.)

References