We will begin with the basics of Mercurial. We believe the best way to learn is to do things yourself, so we encourage you to follow along and execute the commands you see “Alice” and “Bob” execute. You can tell who is executing the command by the color of their terminal: Alice has a pale peach colored terminal, Bob has a pale turquoise color, and Carla (introduced later) has a lavender color.
There are also some exercises later that cover the same things that Alice and Bob did.
Contents
Mercurial is available for most platforms, including Windows, Mac OS X, and GNU/Linux:
Please only install TortoiseHg, which gives you a complete installation with Mercurial and a set of graphical tools for examining and manipulating the repository.
After installation, you will have a right-click menu in Windows Explorer that gives you access to the graphical tools. After logging out and in again, you will also have a hg and a thg program available in a Command Prompt. You can use the thg program to start the graphical TortoiseHg tools.
Tip
While the examples will use a few Unix commands, you can easily translate them back to the equivalent commands for the Command Prompt:
ls -> dir cat -> type echo "abc" -> echo abc
Please install Mercurial using your package manager. If you cannot find it there, then you can install from source: grab the latest stable release, unpack it somewhere and run make local inside. Now symlink the hg script to a directory in your PATH and you are done.
To install TortoiseHg, you need the PyQt bindings. They are available in most distributions. Then follow these instructions. That gives you the thg program.
We will be using the command line in our examples but will sometime show the repository state in the TortoiseHg Workbench.
After installing Mercurial, try running hg version:
alice$ hg version Mercurial Distributed SCM (version 2.2) (see http://mercurial.selenic.com for more information) Copyright (C) 2005-2012 Matt Mackall and others This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
It is of course fine if you see a version number greater than 2.2. Mercurial 1.4 is also okay for our purposes, but we always recommend upgrading to the newest version available for your platform. You can verify your installation with:
alice$ hg debuginstall Checking encoding (UTF-8)... Checking installed modules (/usr/share/pyshared/mercurial)... Checking templates (/usr/lib/python2.7/dist-packages/mercurial)... Checking commit editor... Checking username... no username supplied (see "hg help config") (specify a username in your configuration file) 1 problems detected, please check your install!
The username is normally missing immediately after installation. You set it by putting the name in a configuration file. On Unix-like systems this is $HOME/.hgrc and on Windows the file is called %HOME%\Mercurial.ini (see hg help config for all locations or use thg userconfig if you prefer). Create the file with this content:
[ui] username = Firstname Lastname <example@example.net>
but with your own name. We will use Alice <alice@example.net> first and her terminals look like the two you saw above. Running hg debuginstall should no longer report any problems.
We will now make a swift tour through the basic commands. These are all similar to older systems such as Subversion or even CVS. If you are using TortoiseHg under Windows, then you might want to also refer to their quick start guide.
Create a repository with the hg init command:
alice$ ls alice$ hg init example alice$ ls example
The hg init command created a new directory which is the new repository. We could also have made the example directory ourselves and issued a blank hg init inside that directory to turn it into a repository.
Entering example we can look around:
alice$ cd example alice$ ls -a . .. .hg
As you can see, the new repository is empty except for a .hg directory in the repository root. Mercurial stores the history and some other administrative files inside this directory.
Let us create a file and ask Mercurial about the status of the repository:
alice$ echo "Hello World" > hello.txt alice$ hg status ? hello.txt
The reply tells us that hello.txt is unknown to Mercurial, i.e., it is not yet tracked. We can add the file:
alice$ hg add hello.txt alice$ hg status A hello.txt
which changes the status to “A” for “added”. The file is not yet stored in the repository history — we’ll do this by making a commit:
alice$ hg commit -m "First version of hello"
No output indicates success — Mercurial won’t interrupt you unless necessary.
You can see the newly created changeset with hg log:
alice$ hg log changeset: 0:d312da7770f4 tag: tip user: Alice <alice@example.net> date: Wed Mar 10 20:10:05 2010 +0000 summary: First version of hello
Success, you have made a commit with Mercurial! Let us modify the file and ask Mercurial to show us how the files in working copy differ from the last revision:
alice$ echo "Goodbye!" > hello.txt alice$ hg diff diff -r d312da7770f4 hello.txt --- a/hello.txt Wed Mar 10 20:10:05 2010 +0000 +++ b/hello.txt Mon Mar 10 20:10:10 2010 +0000 @@ -1,1 +1,1 @@ -Hello World +Goodbye!
We can change our mind and revert the file back to how it looked before:
alice$ hg revert hello.txt alice$ cat hello.txt Hello World
Let us make another change to our file:
alice$ echo >> hello.txt alice$ echo "by Alice." >> hello.txt alice$ hg commit -m "Added author."
We can now ask Mercurial to annotate the file, that is, to show when each line was last modified:
alice$ hg annotate hello.txt 0: Hello World 1: 1: by Alice.
Quite right, Mercurial tells us that the first line is from revision 0, and that the two final lines are from revision 1. TortoiseHg offers a very nice interactive annotate tool, see thg annotate.
Annotating files is an invaluable help when fixing bugs: after finding the bug, you can use hg annotate to find out when the offending line was introduced in your file. Then use hg log to retrieve the associated commit message, which can hopefully explain what the committer was trying to do when he changed the line:
alice$ hg log -r 1 changeset: 1:2e982bdc137f tag: tip user: Alice <alice@example.net> date: Wed Mar 10 20:15:00 2010 +0000 summary: Added author.
The two changesets in our repository are in sequence. This is best illustrated with the help of the standard graphlog extension. To keep the core of Mercurial slim, a lot of functionality is delegated to extensions. Many of the best extensions are shipped with Mercurial and they are easy to enable. To enable graphlog, Alice simply adds:
[extensions] graphlog =
to her configuration file. She can then run hg help graphlog to learn more about the extension. This extension is particularly simple, it just gives Alice a new command:
alice$ hg glog @ changeset: 1:2e982bdc137f | tag: tip | user: Alice <alice@example.net> | date: Wed Mar 10 20:15:00 2010 +0000 | summary: Added author. | o changeset: 0:d312da7770f4 user: Alice <alice@example.net> date: Wed Mar 10 20:10:05 2010 +0000 summary: First version of hello
This shows how the changeset 2e982bdc137f is a child of d312da7770f4. The same can be seen in thg log:
The files in the working copy are always synchronized to a particular changeset. In thg log, this is shown with a circled bullet in the changeset graph. In the output of hg glog, the working directory parent revision is highlighted with a @-sign.
You can change the working copy parent revision with the hg update command. This is useful to go back and examine old versions of your code, for instance if you need to test an old version of your software with a new operating system. Here Alice will just go back to revision 0 to look at her old file. She first uses the hg parents command to ascertain where she is in the graph:
alice$ hg parents changeset: 1:2e982bdc137f tag: tip user: Alice <alice@example.net> date: Wed Mar 10 20:15:00 2010 +0000 summary: Added author.
and she then makes the jump:
alice$ hg update 0 1 files updated, 0 files merged, 0 files removed, 0 files unresolved alice$ hg parents changeset: 0:d312da7770f4 user: Alice <alice@example.net> date: Wed Mar 10 20:10:05 2010 +0000 summary: First version of hello
Her file is now back at the old version:
alice$ cat hello.txt Hello World
but the newer version is not forgotten. Mercurial can easily retrieve it for her:
alice$ hg cat -r 1 hello.txt Hello World by Alice.
The hg cat command is useful to quickly see old versions of files instead of using hg update to change the entire working directory to an old version. Going back to the tip revision is easy, just do hg update without an argument:
alice$ hg update 1 files updated, 0 files merged, 0 files removed, 0 files unresolved alice$ cat hello.txt Hello World by Alice.
Use hg init to create a repository called kick-start.
Enter kick-start and create a couple of files. Schedule them for addition with hg add. You can use hg revert to cancel a pending addition if you change your mind.
Use hg commit to commit the files. An editor will be started if you do not specify a commit message on the command line.
Make a couple of more commits.
Use hg update to update the working directory to an older revision. Notice how the output of hg parents changes to match and compare this with hg tip, which stays fixed then you use hg update.
Try making a commit when the working parent is an old revision. You will be told that you have created a new “head” — a head is a changeset without any children. Use hg heads to see the repository heads and look at the graph in thg log.
Notice how the newly created changeset has the changeset listed previously in hg parents as its parent changeset. This is an invariant in Mercurial: the working copy parent revision becomes the parent revision of the next changeset.
Multiple heads represent multiple divergent lines of development. You will normally merge these lines using hg merge. If your changes do not overlap, then the merge will be easy. Otherwise you will have to resolve the conflicts — more about this later.
Notice how hg parents print two changesets after you issued hg merge. Again, this means that the next commit will have two parents. You can see the parents in hg glog or thg log after you commit.
Right now Alice’s repository is in her home directory. Her co-worker, Bob, also needs to work on the project. We will show his commands with a light blue background color.
In these examples, we will let Alice and Bob share a local filesystem. This could very well be a company-wide network filesystem. Changes can also be shared over SSH and HTTP connections — people even use emails or USB sticks to move around changesets. They then typically use a compressed binary format produced by the hg bundle command. This is not necessary here, instead Bob uses hg clone with a relative path to Alice’s repository:
bob$ hg clone ../alice/example destination directory: example updating to branch default 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
Bob now has his own fully independent copy of Alice’s repository. He can make such a clone as long as he has read access to Alice’s files. Bob can make his own changesets in his clone, without affecting Alice. He makes a couple of changes:
bob$ cd example bob$ echo "Goodbye!" > goodbye.txt bob$ echo >> goodbye.txt bob$ echo "by Bob." >> goodbye.txt bob$ hg add adding goodbye.txt bob$ hg commit -m "My goodbye file." bob$ echo >> hello.txt bob$ echo "not by Bob." >> hello.txt bob$ hg commit -m "Not my file."
The repository now has four changesets:
The clone knows where it originates from and Bob can ask if there are any changesets in Alice’s clone that he does not already have:
bob$ hg incoming comparing with /home/alice/example searching for changes no changes found
He can also ask Mercurial what changeset he has that Alice lacks:
bob$ hg outgoing comparing with /home/alice/example searching for changes changeset: 2:659410363fe0 user: Bob <bob@example.net> date: Thu Mar 11 10:00:00 2010 +0000 summary: My goodbye file. changeset: 3:51b2976b01e8 tag: tip user: Bob <bob@example.net> date: Thu Mar 11 10:03:00 2010 +0000 summary: Not my file.
This hopefully makes sense: Bob created two new changesets in his own clone, so Alice does not have them yet.
Bob does not have write access to the files in Alice’s home directory, so he cannot write his changesets to her clone. But he lets her know that he has made a clone and asks her to retrieve the changesets. She does this with the hg pull command. Before using the command, she enters the path to Bob’s clone in the .hg/hgrc file. This a repository-local configuration file. She enters:
[paths] default = /home/bob/example
to make Bob’s clone the default target when doing hg pull and other commands that involve a remote repository. She can see what will be pulled with hg incoming:
alice$ hg incoming comparing with /home/bob/example searching for changes changeset: 2:659410363fe0 user: Bob <bob@example.net> date: Thu Mar 11 10:00:00 2010 +0000 summary: My goodbye file. changeset: 3:51b2976b01e8 tag: tip user: Bob <bob@example.net> date: Thu Mar 11 10:03:00 2010 +0000 summary: Not my file.
Notice how this is symmetric to when Bob executed hg outgoing in his clone. She decides to go ahead and pull Bob’s changes:
alice$ hg pull pulling from /home/bob/example searching for changes adding changesets adding manifests adding file changes added 2 changesets with 2 changes to 2 files (run 'hg update' to get a working copy)
The final message is a hint to Alice to her know that her working copy parent revision has not changed:
The parent revision is unchanged since Alice may have been working on a change of her own, and so she might not be interested in updating right away. In this case she does update:
alice$ hg update 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
You will almost always want to run hg update after hg pull and for this reason, there is a shortcut: hg pull -u will automatically update after pulling in new changes.
Alice reworks the hello.txt file a bit:
alice$ hg diff diff -r 51b2976b01e8 hello.txt --- a/hello.txt Thu Mar 11 10:03:00 2010 +0000 +++ b/hello.txt Thu Mar 11 11:00:00 2010 +0000 @@ -1,4 +1,4 @@ -Hello World +Hello, Wonderful World! by Alice. alice$ hg commit -m "Happy, happy!"
At the same time, Bob is working. He creates a new file:
bob$ echo "Welcome!" > welcome.txt bob$ hg add adding welcome.txt bob$ hg commit -m "Welcome file."
Both Alice and Bob have now made a changeset that is a child of 51b2976b01e8, but neither of them knows about this since they have not yet pulled or pushed the changes anywhere. Alice decides to pull from Bob:
alice$ hg pull pulling from /home/bob/example 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)
What happened? As mentioned above, a “head” changeset is a changeset without any children. Since Alice and Bob have made different changes based on 51b2976b01e8, there are now two changesets in the repository without any children. These heads are divergent lines of development, as can be seen in thg log:
The two lines of development should be reconciled, and the hg merge command is the way to do that:
alice$ hg merge 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit)
There were no conflicts since Alice and Bob edited different files. After hg merge, the working copy has two parents — this is how you can tell that a merge is in progress if you have left your computer and forgotten about it. The merge is not yet committed since you might need to edit the files to make your code compile. Just because there were no overlapping edits (no conflicts), there might still be some semantic conflicts which Mercurial cannot detect. It is therefore a good idea to run the test suite after a merge. In our case, Alice goes ahead with the commit:
alice$ hg commit -m "Merge with Bob."
The two heads are now bound together in the changeset graph:
The merge changeset contains changes from both of its parent revisions. You should think of a merge changeset as your way of saying to the world: this is how you should combine changeset X with changeset Y. When others pull the merge changeset from your repository, it will go into the right place in their changeset graph. They will therefore not have to do the same merge. We can see this if we let Bob pull from Alice:
bob$ hg pull pulling from /home/alice/example searching for changes adding changesets adding manifests adding file changes added 2 changesets with 1 changes to 1 files (run 'hg update' to get a working copy)
Notice how the changesets appear in a different order in Bob’s repository compared to Alice’s repository — the “Happy, happy!” changeset has switched order with the “Welcome file.” changeset. Changesets always retain their ID, the longer hexadecimal number, but the revision number can change. The revision number simply depends on the order in which changesets was added to a repository, and when two people pull from each other, they will most likely end up with different revision numbers for the same changesets. You should therefore always refer to a changeset by its ID when talking to others. But you can still use the revision numbers with commands like hg update that work inside your local repository.
Create a repository and make a couple of commits inside it.
Make two clones of your repository. Notice how the .hg/hgrc file records the location of the original repository. The hg paths command will show you the paths defined.
Add another path to .hg/hgrc, such as:
bob = /home/bob/test
You will now be able to execute hg pull bob, hg outgoing bob, etc. It is a good idea to add shortcuts for people you collaborate with often.
Experiment with pulling and pushing changes. Note how the working directory is not changed even though history is added to the repository. The working copy parent revision is only changed with hg update.
Mercurial supports disconnected collaboration. This works by exchanging changesets in what is called bundles. A bundle is a compressed, binary representation of a number of changesets.
Create a new changeset in one of your repositories. Use hg bundle --base X out.bundle to create a bundle in out.bundle that contains all changesets following X. The idea is that you record somewhere the last known shared changeset between you and the target, and use that as the base.
Go to the target repository and use hg unbundle to import the changesets from the bundle. In this example you will most likely have both repositories on your own machine, but in reality the two repositories could be on machines that are never online at the same time. In that case, bundles offer a way for you to move changesets over, say, email.
You can also publish your repository via HTTP:
Execute hg serve in a repository. You can now access the repository in a browser with http://localhost:8000. Try to start a server for both Alice’s and Bob’s repository.
Try to pull from a repository published with hg serve.
Note hg serve is intended for ad-hoc publishing. For permanent publishing you can for example use Apache with the hgweb.cgi script supplied with Mercurial.
You have seen most of the basic commands in Mercurial and you have seen how the history graph works. The important commands are:
hg init: create a new repository.
hg add: schedule a file for addition.
hg diff: see changes that will be included in the next commit.
hg commit: save your changes in the current repository.
hg log: see all changes in your repository.
hg pull: get all changes from another repository into the current one.
hg push: send all changes from your repository to another one.
hg merge: join different lines of history.
And very importantly:
hg help