Mercurial_ The Definitive Guide - Bryan O'Sullivan [33]
$ hg init missing-example
$ cd missing-example
$ echo a > a
$ hg add a
$ hg commit -m 'File about to be missing'
$ rm a
$ hg status
! a
If your repository contains a file that hg status reports as missing, and you want the file to stay gone, you can run hg remove --after at any time later on, to tell Mercurial that you really did mean to remove the file.
$ hg remove --after a
$ hg status
R a
On the other hand, if you deleted the missing file by accident, give hg revert the name of the file to recover. It will reappear in unmodified form.
$ hg revert a
$ cat a
a
$ hg status
* * *
Aside: Why tell Mercurial explicitly to remove a file?
You might wonder why Mercurial requires you to explicitly tell it that you are deleting a file. Early in its development, Mercurial let you delete a file however you pleased; it would notice the absence of the file automatically when you next ran a hg commit, and stop tracking the file. In practice, this made it too easy to accidentally remove a file without noticing.
* * *
Useful Shorthand: Adding and Removing Files in One Step
Mercurial offers a combination command, hg addremove, that adds untracked files and marks missing files as removed.
$ hg init addremove-example
$ cd addremove-example
$ echo a > a
$ echo b > b
$ hg addremove
adding a
adding b
The hg commit command also provides a -A option that performs this same add-and-remove, immediately followed by a commit.
$ echo c > c
$ hg commit -A -m 'Commit with addremove'
adding c
Copying Files
Mercurial provides a hg copy command that lets you make a new copy of a file. When you copy a file using this command, Mercurial makes a record of the fact that the new file is a copy of the original file. It treats these copied files specially when you merge your work with someone else’s.
The Results of Copying During a Merge
What happens during a merge is that changes “follow” a copy. To best illustrate what this means, let’s create an example. We’ll start with the usual tiny repository that contains a single file.
$ hg init my-copy
$ cd my-copy
$ echo line > file
$ hg add file
$ hg commit -m 'Added a file'
We need to do some work in parallel, so that we’ll have something to merge. So let’s clone our repository.
$ cd ..
$ hg clone my-copy your-copy
updating working directory
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
Back in our initial repository, let’s use the hg copy command to make a copy of the first file we created.
$ cd my-copy
$ hg copy file new-file
If we look at the output of the hg status command afterwards, the copied file looks just like a normal added file.
$ hg status
A new-file
But if we pass the -C option to hg status, it prints another line of output: this is the file that our newly added file was copied from.
$ hg status -C
A new-file
file
$ hg commit -m 'Copied file'
Now, back in the repository we cloned, let’s make a change in parallel. We’ll add a line of content to the original file that we created.
$ cd ../your-copy
$ echo 'new contents' >> file
$ hg commit -m 'Changed file'
Now we have a modified file in this repository. When we pull the changes from the first repository, and merge the two heads, Mercurial will propagate the changes that we made locally to file into its copy, new-file.
$ hg pull ../my-copy
pulling from ../my-copy
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
$ hg merge
merging file and new-file to new-file
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
$ cat new-file
line
new contents
Why Should Changes Follow Copies?
This behavior—of changes to a file propagating out to copies of the file—might seem esoteric, but in most cases it’s highly desirable.