More Fun with Mercurial

More Fun with Mercurial

mercurial superfly!

It doesn't have to be named "myrepo"!

I used a directory named "myrepo" for a demonstration, but you can use Mercurial in any directory. You can even cd into an existing directory full of files and start version-controlling it with Mercurial; just do hg init .. (Remember that in Unix, . refers to the current directory). All this does is create the hidden .hg directory where Mercurial tracks the history of your files, it doesn't change your files in any other way. You'll still have to hg add each file you want tracked.

You can mix tracked and untracked files in the same directory; just hg add some and put the names of others in your .hgignore file, so they don't always show up in hg status.

Directories, sub-directories, and sub-sub-directories...

If you hg init a directory and turn it into a Mercurial repo, you can then version-track any files inside that directory, and inside any sub-directories, on down the chain.

An example

mypages/
 - things_to_remember.txt
 - mysite/
    - index.html
    - css/
       - style.css
       - print.css
    - img/
       - grimacing_soccer_player.jpg
 - anothersite/
    - ...

If you cd mypages/ and then hg init mysite, it will create mypages/mysite/.hg/ (it would also create the mysite directory if it didn't already exist).

An example, continued

mypages/
 - things_to_remember.txt
 - mysite/
    - index.html
    - css/
       - style.css
       - print.css
    - img/
       - grimacing_soccer_player.jpg
 - anothersite/
    - ...

Then you can hg add index.html, or hg add img/grimacing_soccer_player.jpg, or cd css and hg add style.css print.css (or just hg add *). But you can't add things_to_remember.txt or anything in the anothersite directory.

Bring out your repos

If you wanted to version control everything, you could of course hg init the mypages directory instead, and keep everything in one big repo. But if mysite and anothersite are separate sites that you maintain independently from each other, you'd probably want to manage them as two separate repos with separate version histories instead.

You can even nest one Mercurial repo inside another; the inner one will manage only the files inside it, of course, and the outer one will completely ignore all the files inside the inner one).

Repos vs working directories

Technically, the repository is just the .hg directory where Mercurial stores all its version-history metadata, and the directory containing it (where your files live) is the working directory. The working directory represents a snapshot of your files at some point in the version history.

- mysite/            <---- working directory
   - .hg/               <---- repository
   - index.html         
   - css/
      - style.css
      - print.css

Time travel, continued

So far, we've always kept our working directory at the most recent version, or tip. But we can revert to any previous state with the hg update command.

cd into the working directory where you did last week's homework. First do an hg status to see if there are any uncommitted modifications; if there are, do hg diff to see the changes and then either hg commit -m 'some message' to commit them, or hg revert filename.py to abandon them. (If you only ever committed a single change, make some change and commit it so hg log shows you at least two revisions.)

Time travel

Run hg log, and pick some previous revision. I'll pick 0:

carljm@kale:~/testproj$ hg log
changeset:   1:c38c63ca48cc
tag:         tip
user:        Carl Meyer 
date:        Thu Sep 17 16:10:27 2009 -0400
summary:     changes

changeset:   0:db16fd315f06
user:        carljm@dj.goshen.edu
date:        Thu Sep 17 14:16:09 2009 -0400
summary:     first commit

More time travel

Run hg parents to see what revision the working directory is currently based on; should be the tip (latest) revision. Then:

carljm@kale:~/testproj$ hg update 0
1 files updated, 0 files merged, 0 files removed, 0 files unresolved

Mercurial tells that one file was changed between the revision I was at and the one I just updated to. Now hg parents will tell me that my working directory is based on revision 0; and if I look at my files, I will see that they are indeed the versions I had right after my first commit (revision 0). my recent changes aren't lost, of course; hg log still shows the full history, and hg update 1 (or hg update tip, or just hg update) will return me to my most recent version.

Clean your house before time travelling

If you have uncommitted changes in the files in your working directory, hg update will try to "carry them along" in your time travelling, by applying the "same" changes to the older versions of your file(s). This can be very handy but most of the time you don't need it: just commit first, or abandon your changes by providing the -C flag: hg update -C.

Using dj.goshen.edu

$ ssh dj.goshen.edu

It's a (virtual) Linux server dedicated to this class; but it gives you a Unix-style bash shell, just like on your Macs (or even more like www.goshen.edu). You have a home directory in /home/yourname (in place of /Users/yourname on a Mac), and that's where you are placed initially when you ssh in (pwd to verify). You can also ssh into it from off-campus, which will help some of you out.

A new place for homework

First off, mkdir homework to create a directory where you'll put your homework assignments from here on out. Within it, create numbered directories for each week's homework (just to help me find things more easily).

Transferring files using scp

If you need to transfer files to/from dj.goshen.edu:

carljm@kale:~$ scp my_file.txt carljm@dj.goshen.edu:~/some_dir/
my_file.txt                      100%    5     0.0KB/s   00:00 

Or to pull a file down:

carljm@kale:~$ scp carljm@dj.goshen.edu:~/some_dir/my_file.txt .
my_file.txt                      100%    5     0.0KB/s   00:00 

In both cases, I can leave out the carljm@ if my local username is the same. On a campus machine, it will be, since both are my GC username.

You can also transfer files using any GUI FTP client that supports the SFTP protocol.

But we'd rather use Mercurial!

Rather than copying individual files, you'll often have a Mercurial repo that you want to keep in sync between two different machines. Or you keep the primary copy on dj.goshen.edu, but you want to work on it on a lab Mac, so you don't have to deal with network latency for your editing. This is where hg clone, hg pull, and hg push come into play.

hg clone creates a new copy of an existing repo.

hg pull pulls changesets from some other repo into your current one.

hg push pushes changesets from your current repo into some other one.

Cloning, pulling, and pushing

Say I've got a local repository on my computer, and I want to create a clone of it on dj.goshen.edu:

carljm@kale:~/testproj$ hg clone . ssh://dj.goshen.edu/newtest
searching for changes
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 2 changesets with 2 changes to 1 files

. refers to my current directory, which is a Mercurial working directory. The second argument _could_ just be a path to some other (not-yet-existing) directory on my own computer, but in this case I give it an ssh://dj.goshen.edu URL. The path is relative to my home directory, so this will create a copy of my local testproj in a directory named newtest on dj.goshen.edu.

Need to update after clone/push/pull

If I now ssh into dj.goshen.edu and ls the newrepo directory, it won't have anything in it except the .hg repo dir. This is because neither hg clone nor hg push nor hg pull will modify the working directory; they just copy changesets in the version history.

In the case of a brand-new clone, this means it has no working directory contents at all yet, even though all your version history is there inside the .hg directory. All you need to do is cd newrepo and then hg update to give yourself an up-to-date working directory.

hg pull provides a shortcut, hg pull -u which just performs an automatic hg update after pulling. It's the only one with this shortcut because it's the only one that brings new changesets into your current (local) repo.

Make changes locally and push them

In your local repository, make some changes and make a new commit. Then run:

carljm@kale:~/testproj$ hg push ssh://dj.goshen.edu/newtest
pushing to ssh://dj.goshen.edu/newtest
searching for changes
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 1 changes to 1 files

Again, need to update the remote repo

carljm@dj:~/newtest$ hg log
changeset:   2:4970659539b4
tag:         tip
user:        Carl Meyer 
date:        Thu Sep 17 18:04:14 2009 -0400
summary:     more changes

changeset:   1:c38c63ca48cc
user:        Carl Meyer 
date:        Thu Sep 17 16:10:27 2009 -0400
summary:     changes

changeset:   0:db16fd315f06
user:        carljm@dj.goshen.edu
date:        Thu Sep 17 14:16:09 2009 -0400
summary:     first commit

...

carljm@dj:~/newtest$ hg parents
changeset:   1:c38c63ca48cc
user:        Carl Meyer 
date:        Thu Sep 17 16:10:27 2009 -0400
summary:     changes

carljm@dj:~/newtest\$ hg update
1 files updated, 0 files merged, 0 files removed, 0 files unresolved

carljm@dj:~/newtest\$ hg parents
changeset:   2:4970659539b4
tag:         tip
user:        Carl Meyer 
date:        Thu Sep 17 18:04:14 2009 -0400
summary:     more changes

And now make changes remotely and pull them

carljm@dj:~/newtest$ emacs file.txt

carljm@dj:~/newtest$ hg commit -m 'yet another change'

carljm@kale:~/testproj$ hg pull ssh://dj.goshen.edu/newtest
pulling from ssh://dj.goshen.edu/newtest
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
(run 'hg update' to get a working copy)

carljm@kale:~/testproj$ hg update
1 files updated, 0 files merged, 0 files removed, 0 files unresolved

Homework