Mercurial is a revision control system. That means it is designed to help track work on a collection of files, giving you the ability to identify and retrieve the revisions that existed at various points during the process. The huge motivation for revision control is when there are several people all working on the same program (or collection of programs), because then it is very helpful to be able to fugure out what was changed and by whom. But revision control is also helpful when you are working by yourself: if you manage to make a mess, you can back up to a coherent prior state.
Mercurial can do much more than we need in this class. If you get really interested, or you need something more, there is a very good online tutorial and reference, This page is limited to the features you are most likely to need in this class.
Mercurial manages a subtree of files: a directory and all of its
subdirectories. This matches the way we usually work on projects
(including labs): we make a new directory for each new project. When
we are working in a Mercurial-managed tree, we see the files there as
we would normally. But Mercurial also maintains, behind the scenes,
additional files that keep the history and other information; we call
that hidden collection the repository. (The repository is
actually hiding in a dirctory named .hg
, taking advantage
of the fact that names beginning with dot are not normally shown.) The visible
files we actually work with are called a working copy. We
manage the repository and its relationship with the working copy using
the hg
commmand.
The main Mercurial command hg
is always followed by
another command that tells Mercurial what to do. It is very helpful
to remember that one of those commands is help
. So you
can always do
$ hg helpto get general help, or add a command, such as
update
, to
the end of the help to get a reminder of what that command does and
what its arguments can be; for example,
$ hg help updateor even
$ hg help help
Before you can make very much use of Mercurial, you need to do a
tiny bit of setting up (which has already been done for the lab
accounts, and in rudimentary form for the accounts of students in this
class). Whenever you run hg
, it will look for a file
named .hgrc
in your home directory for some settings and
options. You can add to that later on if you want to set some more options.
In this class, you'll often start from a repository that already exists; you'll create your own working copy and repository by cloning the existing repository. When that repository is local, you can name it with its filename; so the command
$ hg clone /cslab/class/csci235/in-class/09-10 .will clone the repository under
/cslab/class/csci235/in-class/09-10
to the current working directory (that's the dot); it will make a new
directory named 09-10
to hold the working copy and repository.
If you already have a directory for which you would like to start using Mercurial, you can create a repository by (1) making sure that you are in the top directory of the tree you want to manage and then (2) issuing the command
$ hg init
When you create a repository, you will need to remember to use the
hg add
command to tell the repository what files it
should manage (see below).
Because there are usually files in the working copy that you don't
want in the repository (such as the compiled versions of the source
files), you can use a file .hgignore
to tell Mercurial
the filenames it should ignore. That will be included when you clone
a repository, but you can edit it if you need to. That file has
patterns in it (a lesson in itself), but you can get a reasonable
starting file for this class by copying the file found at
/cslab/class/csci235/misc/dot.hgignoreThat file ignores
.class
files, Emacs backups, and
dot-files. You will want to remember to explicitly add the
.hgignore
file, because it will otherwise be ignored.
hg summary
Show what revision the working copy is based on.
hg status
Show the status of files in the working copy. The status letters you'll see most often are
?
this file is not known to the repository (and not ignored)
!
this file is in the repository, but missing
from the working copy
M
the working copy has been modified since the
last commit
A
the file has been added to the repository, but
not yet committed
hg add filenames
Add the files to the set that are tracked by the repository.
hg remove filenames
Remove the files from both the working copy and the repository.
hg forget filenames
No longer track these files.
hg log filenames
Show the history of commits to the named files.
hg update
Update the working copy to match the repository.
hg commit -m "message"
Commit the changes in the working copy to the repository. Replace message with a brief description of what has changed; this is the information that will be shown by the log command.
hg revert filenames
Restore the working copy of the named files to the latest version in the repository. This "undoes" uncommitted changes.
The general pattern you will use most of the time is
hg clone
hg add
to tell the
repository about it. If you want to get rid of a file,
use hg remove
to remove it from the working copy and
tell the repository that it goes away. Or, if you just want it to
go away in the repository, use hg forget
.
hg status
to check whether you've missed
anything (and use add
or forget
as
needed).
hg commit
to add a snapshot of the working
copy to the repository.
If you make a mess out of (or remove) one or more files, you can
revert to a saved version; as long as you are reverting to
the latest committed version, it is fairly simple. To revert the file
Palindrome.java
, you would simeply use
$ hg revert Palindrome.javaAs the message printed tells you, it rename what is in the working copy by adding
.orig
to the end of the name. That way
you can use an editor to pick pieces from that file.
To get revert all files that have been changed, you would use
--all
instead of the filename(s).
You can revert a file to a particular version of a file by adding a
-r
and a revision number after revert
.
Doing that may make a future commit more complicated--requiring a
merge. That's beyond the scope of this introduction; so you
might want to do your experimenting with a copy of the repository.
Note that you can also make another copy from the repository by using the clone command. From the top directory in your repository, you can do
$ hg clone . ../new-copyto make a new repository and working copy in the directory
../new-copy
. You can then move into that copy and
manipulate it without messing up the current one. That's especially
useful if you want to try reverting files to several different
revisions or you want to look at an entire revision other than the
most recent one.
You can discard a working copy and its copy of the repository by
removing the entire directory. Warning: this is
dangerous. If you wanted to get rid of the copy created above
and you are currently in the directory new-copy
, you
would need to
$ cd ..
$ rm -rf new-copy
You can also reset the working copy to match any committed revision
by adding -r version
to the update
command. That is easy if you want to just look; you can ask which
version the working copy is based on by using hg summary
.
Editing based on a revision other than the latest one is allowed--and even useful. (It is essential when you have more than one person working on a program.) But doing that will leave you with more than one "latest" version, which makes working with all of the revisions more complicated. If you do that, you'll need to learn how to merge, which is beyond the cope of these notes.
There are special ways to copy a directory with a Mercurial repository. If you are making a new copy, it is called cloning; there are other commands for making two existing copies match.
A repository can be local, in which case you name it specifying the name of the directory where it exists; that is what the example clone command above does.
When a repository is remote, you need to tell how to reach
it, which you can do with a URL using ssh. If the other computer is
named cslab25
and the account name there is
mortimer.snerd
, the URL will have the form
ssh://mortimer.snerd@cslab25/directory-path
(see
the example below).
If user mortimer.snerd
wants to create a new copy of
the repository in the current directory (in a lab account) as the directory
csci235/lab4
under his home directory, the command would be
$ hg clone . ssh://mortimer.snerd@cslab25/csci235/lab4The dot is the source (here), and the URL is the remote name.
He'll be prompted for his password, twice.
When he logs in to his own account and changes into the
csci235/lab4
directory, all that will be there is the
repository--no working copy. To set up the working copy, he would
need to
$ hg updatewhich makes the working copy match the last committed version from the repository.
When another copy of a repository already exists, you push local changes to the remote copy, and you pull remote changes to the local copy. Usually you want to check what will happen before you do it.
To push changes out, our friend Mortimer would first
$ hg outgoing ssh://mortimer.snerd@cslab25/csci235/lab4to see what changes need to go, then
$ hg push ssh://mortimer.snerd@cslab25/csci235/lab4to transfer them.
To pull changes in, the sequence would be
$ hg incoming ssh://mortimer.snerd@cslab25/csci235/lab4to check, then
$ hg pull ssh://mortimer.snerd@cslab25/csci235/lab4to transfer the changes.
If you are doing this to move files to and from your own computer, you'll probably want to run the push and pull commands on your computer, because the lab computers are easier to name.
A later installment will provide more information about working with multiple copies of a repository and how you can use Mercurial to work cooperatively with other programmers.
Last modified: Thu Sep 18 07:36:27 CDT 2014