Mercurial_ The Definitive Guide - Bryan O'Sullivan [63]
$ echo oops > oops
$ hg add oops
$ hg status oops
A oops
$ hg revert oops
$ hg status
? oops
Similarly, if you ask Mercurial to hg remove a file, you can use hg revert to restore it to the contents it had as of the parent of the working directory.
$ hg remove file
$ hg status
R file
$ hg revert file
$ hg status
$ ls file
file
This works just as well for a file that you deleted by hand, without telling Mercurial (recall that in Mercurial terminology, this kind of file is called “missing”).
$ rm file
$ hg status
! file
$ hg revert file
$ ls file
file
If you revert a hg copy, the copied-to file remains in your working directory afterwards, untracked. Since a copy doesn’t affect the copied-from file in any way, Mercurial doesn’t do anything with the copied-from file.
$ hg copy file new-file
$ hg revert new-file
$ hg status
? new-file
Dealing with Committed Changes
Consider a case where you have committed a change a, and another change b on top of it, and you then realize that change a was incorrect. Mercurial lets you “back out” an entire changeset automatically, and building blocks that let you reverse part of a changeset by hand.
Before you read this section, here’s something to keep in mind: the hg backout command undoes the effect of a change by adding to your repository’s history, not by modifying or erasing it. It’s the right tool to use if you’re fixing bugs, but not if you’re trying to undo some change that has catastrophic consequences. To deal with those, see Changes That Should Never Have Been.
Backing Out a Changeset
The hg backout command lets you “undo” the effects of an entire changeset in an automated fashion. Because Mercurial’s history is immutable, this command does not get rid of the changeset you want to undo. Instead, it creates a new changeset that reverses the effect of the to-be-undone changeset.
The operation of the hg backout command is a little intricate, so let’s illustrate it with some examples. First, we’ll create a repository with some simple changes.
$ hg init myrepo
$ cd myrepo
$ echo first change >> myfile
$ hg add myfile
$ hg commit -m 'first change'
$ echo second change >> myfile
$ hg commit -m 'second change'
The hg backout command takes a single changeset ID as its argument; this is the changeset to back out. Normally, hg backout will drop you into a text editor to write a commit message, so you can record why you’re backing the change out. In this example, we provide a commit message on the command line using the -m option.
Backing Out the Tip Changeset
We’re going to start by backing out the last changeset we committed.
$ hg backout -m 'back out second change' tip
reverting myfile
changeset 2:d4c21ae7349f backs out changeset 1:60b1c4c78499
$ cat myfile
first change
You can see that the second line from myfile is no longer present. Taking a look at the output of hg log gives us an idea of what the hg backout command has done.
$ hg log --style compact
2[tip] d4c21ae7349f 2009-05-05 06:44 +0000 bos
back out second change
1 60b1c4c78499 2009-05-05 06:44 +0000 bos
second change
0 fea7474d43c3 2009-05-05 06:44 +0000 bos
first change
Notice that the new changeset that hg backout has created is a child of the changeset we backed out. It’s easier to see this in Figure 9-1, which presents a graphical view of the change history. As you can see, the history is nice and linear.
Figure 9-1. Backing out a change using the hg backout command
Backing Out a Non-Tip Change
If you want to back out a change other than the last one you committed, pass the --merge option to the hg backout command.
$ cd ..
$ hg clone -r1 myrepo non-tip-repo
requesting all changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 1 files
updating working directory
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd non-tip-repo
This makes backing out any changeset