Using Mercurial (hg)
This document is a slighly more reader-friendly version of the notes I
took while learning to use Mercurial. The purpose of this
document is to provide a simple guide to the immediate use cases for
hg. The assumption is that you're using Linux, OS X, or similar.
toc
The .hgrc file
The .hgignore file
Your project repository
Working with Mercurial
Viewing the project history in your web browser, download via Mercurial's web server
Getting a Do-Over
Switching to an earlier working revision
The .hgrc file
Creating a .hgrc file in your home directory will allow for hg to
source information from that file, thereby saving you the hassle of
providing the info yourself when you run hg or having hg guess what the
answers should be. You can create the .hgrc file using vi, and
populate it with something like:
[ui]
username = Charles Lockhart <lockhart@ifa.hawaii.edu>
This is taken from my .hgrc file. Obviously your file should have your name and email address.
The .hgignore file
The .hgignore file goes in your project directory (right next to the
.hg directory). It's a listing of what file types shouldn't be
included. Here's the contents of the .hgignore file I made:
syntax: glob
*.class
*~
*.o
syntax: regexp
.*\#.*\#$
The .hgignore file gets added to the repository, so after you create the file, run hg add and hg commit.
Your Project Repository
Your project will have a repository for hg to track files. There are two ways for this to get set up.
CVS Note: unlike CVS/SVN, hg
doesn't have a primary repository. Instead, it's distributed, and
essentially you have the master copy. Each project member also
has their own master copy. As changes are made, you push and pull
changes to and from each other. However, de facto there is
typically one person in charge of "the release" for a project, and they
will typically end up having the master copy. Pushes and pulls
are most likely going to go through that master copy. So while
the nature of the program is for everything to be flat, in actual usage
it's more likely to be similar to CVS/SVN.
Creating a New Repository
The assumption is that you have a bunch of files and folders that
contain code and are in a main folder for the project. To convert
the project folder to an hg repostiory. Here's how:
- cd into the project directory.
- run the command hg init
This creates the .hg directory and the initial setup files used
by hg. At this point, there are no files added to the hg
project.
- run the command hg add
This adds all files that aren't currently in the hg project file
list to it's file list. Because we just created the list (via hg init), this adds everything.
- run the command hg commit This commits all changes to the project. With hg add we included the files, now we secure the file contents.
And that's it. Repository created, files populated.
Getting an Existing Repository (Cloning a repository)
The assumption is that somewhere out there is an hg project that you
want a copy of for your own development purposes. In Mercurial
this is called cloning, and we use the clone command. Here will
be shown two ways of doing this, one for cloning a local repository,
the other for cloning a remote repository by riding over ssh.
Cloning a Local Repository
Cloning a local repository is simple. Run the command:
hg clone original_repository_directory_name new_repository_directory_name
As long as the original repository directory exists and has a workable
.hg folder in it, a new clone of that original directory will be
created that is up to date with the repository files.
NOTE: the files in the original
repository directory may not be up to date with the actual repository
files. Every project directory is a working directory.
Cloning a Remote Repository
Coning a remote repository over ssh is also fairly simple. Run the command:
hg clone ssh://username@machinename/path/to/repository
Unless you have ssh keys set up, you will be prompted for username's
password. Enter the password, and if everything is set up
correctly on the remote machine (path to hg is correct, path to
repository is correct, etc.), you will end up with a clone of the
repository on th e local machine.
It should be noted that the path to repository is relative to the users home directory. So
hg clone ssh://lockhart@stefan.ifa.hawaii.edu/src
will clone the src directory located in user lockhart's home directory.
To specify an absolute path, double slahes are needed, as in this
example:
hg clone ssh://lockhart@stefan.ifa.hawaii.edu//tmp/project
Errors usually come from one of two places, either the path is wrong,
or for new installs, the hg binary isn't in the PATH.
Here's an example for using hg & ssh to clone when the server has a different port setup:
hg -v clone ssh://sgir@stefan.ifa.hawaii.edu:2266/src/idl/noise/
Working With Mercurial
The essentials of any project are adding content, backing up that
content, and merging in other people's content. All of this is
easily done in Mercurial.
Let's assume there are three users, Charles, Tony and Bob. Tony
creates the project and initializes it. Charles and Bob use the
clone command to get copies. Now everyone starts out on an even
playing field. All three users keep the project directory in
their ~/src directory, and the name is our_project.
The initial files in our_project are: Makefile, test.c, test.h
Changing a file, simple
Tony makes a change to test.c. He then commits that change by running:
hg commit
NOTE: hg commit pops you into a vi session that makes you write in some change notes. If for some insane reason you don't know vi, use [hg commit -m "my message about the code here"], without the brackets on either side.
which commits all file modifications to the repository. Now, at
this point, Tony has made a change, but the other users, Charles and
Bob, don't know that. Charles can see what changes Tony has made
by running:
hg incoming
which will produce a list of changes (list entry items include the user
who made the change, the date the change was made, and the summary
(written by the editor at the time the change is committed)). At
this point they can decide to update their own repository.
To update a repository that has been cloned from someone elses, run the command:
hg pull
Charles and Bob decide to update their repositories, so they run hg
pull. The file content in their reporitories is updated.
However, their working files are not! The changes that Tony
made are not shown in their actual files.
To update the content from the repository, assuming Charles and Bob
haven't made and committed any changes themselves, they need to also
run:
hg update
This will re-synchronize their working directory with the repository.
Using hg pull / hg update, users can download and update their repositories quite easily.
What if users want to update someone else's repository? What if
Charles makes a change, and wants to update Tony's repository with the
change?
Charles makes a change to test.h. He then commits that change
using hg commit. He can view any changes that would be uploaded
by running:
hg outgoing
To upload these changes to the repository he originally cloned from, he runs:
hg push
which updates the original repository, but not the working files held there.
Multiple users changing files, not as simple, but almost
Both Tony and Charles make and commit changes to their repositories.
Charles then goes and pulls Tony's version, and fins out that
they both made changes. To see what changes they both made that
need to be merged, Charles can run:
hg heads
which will print out all changes that are temporarily being held.
To go ahead and have Mercurial merge the files together
automatically, Charles then runs:
hg merge
Running hg merge gets
Mercurial to combine the files into a single set having all changes.
However, the results of the merge are changes to the working
files, not the files in the repository. Charles must them commit
those changes to have them be in the repository. This allows
Charles to review the results of the merge, and make changes if
necessary. Charles then runs hg commit and hg push to commit his changes and to upload them to Tony, who can then run hg update to update his working files.
Now, what happens if Charles makes some changes, commits them, then
uploads them to Tony. And then Tony commits a change he's made.
Simple, two branches have been created, which can be seen by
running hg heads. To unite the two branches, Tony can then run hg merge.
Managing merge conflicts
Mercurial's merge tool will at times have problems, and will return indicating conflicts that must then be fixed. The conflicts will be highlighted within the file by merge markers (<<<<<<< ======= >>>>>>>) to indicate the local code, the other code, and a seperator in between.
At that point the most straight forward thing is to go through and merge those conflicting sections by hand. Many guides refer to using a GUI based tool, we don't bother. We edit the file, selecting which section we want (all of the conflicting sections must be fixed), then save the file. We then run hg resove -m filename to mark the file as having been fixed up, then check in the changes.
Comparing working files with what's in the repository
The easiest way to see what's different between your working files and the repository is to run:
hg diff
which prints out the accumulation of all changes. Once changes are committed, using hg commit, hg diff will return nothing.
Now, hg diff simply
indicates if you've made any changes since the last time you've run
commit. It does not show you if there have been changes that have
pushed to your repository by someone else using hg push. To
check, run:
hg log
which shows the change history of your repository.
Adding a new file, files, directory or directories
This is very simple. If new files are added, or new directories are added, simply run:
hg add
and
hg commit
which creates entries in the repository for the files/directories, and then commits their contents.
Removing files or directories
This is also very simple. To remove a file from the repository, run:
hg rm path_to_file/filename
This removes the file from the repository AND deletes the file from the
directory. The file will no longer exist. If the directory
is then empty, the directory will be removed/deleted as well.
Copying files or directories
Again, simple. To copy a file or directory in the repository, run:
hg cp file_or_directory_name new_file_or_directory_name
Note, you must then run hg commit to have these new copies included in the repository.
Moving files or directories
Again, simple. To move a file or directory in the repository, run:
hg mv file_or_directory_name new_file_or_directory_name
Note, you must rhen run hg commit to have these changes included in the repository. The hg mv command is essentially hg cp and hg rm.
Viewing the project history in your web browser
One neat little feature of Mercurial is that you can view the change
log in a fairly user friendly way by using it's built in web server.
Run:
hg serve
And Mercurial will start up a web server (default port is 8000, you can assign a port using the -p PORTNUM
option). Open up your browser, browse to http://localhost:8000
and you can see the projects history and track changes in a fairly nice
way.
Mercurial can use this server to serve the project. Here is an
example of the command you could use to clone from the web server:
hg clone http://localhost:8000/ myproject
This will download the project being served to the directory myproject.
To push changes back to the server you need to set up an SSL
certificate.
Getting a Do-Over
You can use the
hg rollback
command to undo the very last change you made to your local project.
Switching to an earlier version to work on something
To get a working set of files from a previous revision, you can run:
hg update -C REVISION
For example, if you run
hg log
and the output is something like:
changeset: 7:2497fceb1c9d
tag: tip
parent: 10:526b195e810d
parent: 15:9f75160e0484
user: Charles Lockhart <lockhart@ifa.hawaii.edu>
date: Thu Apr 30 08:16:27 2009 -1000
summary: merge
changeset: 6:d3d77c23b5ab
user: Charles Lockhart <lockhart@ifa.hawaii.edu>
date: Wed Apr 29 14:36:04 2009 -1000
summary: Added done message
changeset: 5:1e148e4558c6
parent: 3:57b9cd5b4d13
parent: 4:d48bee4e80e4
user: Charles Lockhart <lockhart@ifa.hawaii.edu>
date: Wed Apr 29 14:29:56 2009 -1000
summary: merged Charles and Tony's changes
changeset: 4:d48bee4e80e4
parent: 1:780ef1879d22
user: Charles Lockhart <lockhart@ifa.hawaii.edu>
date: Wed Apr 29 14:23:59 2009 -1000
summary: changed authors
changeset: 3:57b9cd5b4d13
user: Charles Lockhart <lockhart@ifa.hawaii.edu>
date: Wed Apr 29 14:24:34 2009 -1000
summary: changed hello there message
changeset: 2:3636dad0a768
user: Charles Lockhart <lockhart@ifa.hawaii.edu>
date: Wed Apr 29 14:21:08 2009 -1000
summary: Added a struct to test.h
changeset: 1:780ef1879d22
user: Charles Lockhart <lockhart@ifa.hawaii.edu>
date: Wed Apr 29 14:06:29 2009 -1000
summary: changed return value for main().
changeset: 0:83117260c893
user: Charles Lockhart <lockhart@ifa.hawaii.edu>
date: Wed Apr 29 14:05:36 2009 -1000
summary: initial entry
If you want to go back to revision 5 (changeset: 5:1e148e4558c6), you would run:
hg update -C 5
If you're working with a public server, where the short revision numbers don't match, you would run:
hg update -C 1e148e4558c6