Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411430 Posts in 69363 Topics- by 58416 Members - Latest Member: JamesAGreen

April 19, 2024, 11:11:49 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityTownhallForum IssuesArchived subforums (read only)TutorialsVersion control with Mercurial
Pages: [1] 2
Print
Author Topic: Version control with Mercurial  (Read 13535 times)
muku
Level 10
*****


View Profile
« on: October 09, 2008, 05:01:34 PM »

There seems to be some interest in a tutorial on version control, so I thought I'd write something on my current favorite SCM, Mercurial. Since I'm a bit starved on time, I'll do this in installments. The first one is just on installation and basic operations, but I'll hopefully get to the more interesting stuff soon.


Part 1: Basics

Source Code Management (SCM) systems give you a way to handle source code repositories (or any other set of preferrably text-based files) more efficiently. In recent years, Distributed SCMs (or DSCM) have gained wide-spread acceptance for their light-weight approach and excellent scalability. Some notable DSCMs are Git, Darcs, Bazaar and Mercurial.

If you have already used classic SCMs like CVS or Subversion, the major difference to keep in mind is this: in distributed systems, there is no central repository. All repositories are created equal and carry the full history of all revisions within them.

For this tutorial, we will use Mercurial because it is more portable than Git and faster than Bazaar.

Installing Mercurial

Head on over to http://www.selenic.com/mercurial/wiki/index.cgi/BinaryPackages and download and install the binary package for your platform. If you are on Windows, you probably also have to change the PATH environment variable in order to include the Mercurial directory since we will be using it from the command line. There are GUIs available like TortoiseHg for Windows, but I don't personally use them. Anyway, if you understand the basics covered here, using a GUI should come naturally.

One last thing you might want to do before we start is setting a username. For this you have to find your Mercurial configuration file (at ~/.hgrc on UNIX-like systems, at %USERPROFILE%\Mercurial.ini on Windows) and add a line like the following to the UI section:

Code:
[ui]
username = John Doe <[email protected]>

This is the name that will be attached to all commits that you make.

Basic commands

Well now, let's get started. Drop into a terminal window/command prompt/whatever your OS chooses to call it, create a new directory (I'll call it test) and cd to it. All Mercurial commands start with hg. To create a new repository, simply type

hg init

That's it. If you got no response, everything worked fine and our directory now contains a repository. There is no remote server involved, no background service to handle your files, just a self-contained repository right there in our directory.

Of course, our repository doesn't have any files yet. To remedy that, create a file, say hello.c, by whatever means you feel comfortable with and add some code to it, let's say the classic hello world example:

Code:
#include <stdio.h>

void main(void)
{
puts("Hello, world!");
}

We still have to tell Mercurial that we want it to track this file, so we issue the command

hg add hello.c

One very useful command for checking the state of your repository is hg status:

D:\test>hg status
A hello.c


This tells us that the repository contains one file, hello.c, and the A in front of it informs us that the file will be added with the next commit. So let's do that. Whenever you have finished work on a new feature or have fixed a bug in your software, you should commit your changes to the repository with an appropriate commit message. To commit, type

hg commit

Mercurial will bring up an appropriate editor for you to type your commit message in. You can ignore the additional lines that your editor will show; just type in the first line and you'll be fine. In this case I'll just type "Initial commit.", hit save and quit the editor. Mercurial now takes all uncommitted changes from the working directory and stuffs them into a new changeset. (Note: If you ever mess up and want to abort a commit, simply quit the editor without saving.)

If we now again check the status of our repository,

D:\test>hg status


we see that there are no longer any pending changes to be committed. Also, if we request a log of all changes so far with hg log, we get


D:\test>hg log
changeset:   0:293ec2344b39
tag:         tip
user:        Clemens <...>
date:        Fri Oct 10 02:29:02 2008 +0200
summary:     Initial commit.


So we see that there has been one commit, we get the user who did the commit as well as its date and comment, and we see that the corresponding revision has the number 0 (the bit before the : in the changeset number).

Now, let's change our code a little. Open up hello.c in your editor and make it read like this:

Code:
#include <stdio.h>

void main(void)
{
puts("Hello, world!");
puts("Hello again!");
}

If we now check the status,

D:\test>hg status
M hello.c


we see that Mercurial recognizes that our file has been modified (M). If you want to see the changes you have made in detail, you can use the diff command:


D:\test>hg diff
diff -r 293ec2344b39 hello.c
--- a/hello.c   Fri Oct 10 02:29:02 2008 +0200
+++ b/hello.c   Fri Oct 10 02:40:45 2008 +0200
@@ -3,4 +3,5 @@
 void main(void)
 {
        puts("Hello, world!");
+       puts("Hello again!");
 }


Finally, let's commit these new changes by issuing hg commit. After that is done, we can again check the log of our repository:


D:\test>hg log
changeset:   1:6467ebe7dd54
tag:         tip
user:        Clemens <...>
date:        Fri Oct 10 02:41:55 2008 +0200
summary:     Added a second hello message.

changeset:   0:293ec2344b39
user:        Clemens <...>
date:        Fri Oct 10 02:29:02 2008 +0200
summary:     Initial commit.


Summary of part 1

So, in summary, these are the most important commands for working with a local Mercurial repository:
  • add new files with hg add
  • commit changes with hg commit
  • check repository state with hg status
  • check detailed changes with hg diff

Play around with them for a bit to get comfortable with them. You might also need hg remove for deleting files and hg rename for moving/renaming files. Simply typing hg will give you a list of the most important commands.

Note that all commands can be abbreviated as long as they are unique, so you can e.g. say hg st instead of hg status.



Part 2: Advanced operations

Using .hgignore

Let's say we work with the repository from the end of part 1 and have compiled our Hello World application. This might have resulted in an object file hello.obj and an executable hello.exe (mentally adjust for whatever extensions your platform uses). Let's do another hg status:

D:\test>hg stat
? hello.exe
? hello.obj


Mercurial realizes there are two new files, but it's not sure what to do about them. If you simply leave it alone, it will not version those files, which is what you typically want for your binaries. On the other hand, if you issue a hg add command without further arguments, Mercurial will add all files marked with ? to the repository. This can be helpful when you want to add a large batch of files to the repository all at once, but we certainly want to exclude binaries from that process. To do that, let's add a file with the magic name .hgignore (note the leading period) and the following contents to the repository's root directory:

Code:
syntax: glob

*.obj
*.exe
bin/*

The first line gives the type of syntax we want to use. glob is basically what you would use at a shell, and regexp for regular expressions is also supported. You can even mix them in the same file if you want.

The rest of the file is just a straightforward list of file name patterns we want Mercurial to ignore. As you can see from the last file, we can also easily tell it to exclude entire directories from versioning. (If your platform doesn't use a specific extension for executables, and you don't keep the executable in some specific subdirectory, you might have to add your executable's file name in full to .hgignore.)

That should do the trick. If we now issue another status command, we get

D:\test>hg stat
? .hgignore


So Mercurial now rightfully ignores our binaries; it's however still unsure what to do about the .hgignore file itself. It's not a bad idea to version that file, so simply do

D:\test>hg add
adding .hgignore

D:\test>hg commit


to get a new revision which now has our .hgignore file included.

Accessing old revisions

A version control system would hardly deserve its name if it wouldn't allow you to go "back in time," so to speak, in order to view older revisions of your code. The simplest way to do this with Mercurial is the hg update command.

Let's again continue from the repository we built up over the previous sections. Our source file hello.c currently looks as follows:

D:\test>type hello.c
#include <stdio.h>

void main(void)
{
        puts("Hello, world!");
        puts("Hello again!");
}


Assume that we want to go back to the original version of our repository, identified by revision number 0 (you can get these from the output of hg log, as described before). That's easy enough:

D:\test>hg update 0
1 files updated, 0 files merged, 1 files removed, 0 files unresolved

D:\test>type hello.c
#include <stdio.h>

void main(void)
{
        puts("Hello, world!");
}


It's magic! The source file has gone back to the state it was in the original commit. You will also notice that the .hgignore file has vanished from the directory since it wasn't yet a part of the repository in revision 0. Of course these changes aren't lost. In order to go back to the latest revision (also called the "tip"), we simply do:

D:\test>hg update
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

D:\test>type hello.c
#include <stdio.h>

void main(void)
{
        puts("Hello, world!");
        puts("Hello again!");
}


We are back where we started; also, the .hgignore file has magically reappeared. There is an important observation to be made here: Mercurial never assigns revision numbers to individual files (as e.g. CVS does), but always to the state of the repository as a whole.

When you start making changes to your repository, always make sure that you are at the tip, unless you indeed want to branch off from an earlier revision (which can be very useful and is in fact one of the strengths of distributed SCMs).

[... to be continued ...]



Supplemental reading:
http://www.selenic.com/mercurial/
http://www.selenic.com/mercurial/wiki/index.cgi/QuickStart
http://www.selenic.com/mercurial/wiki/index.cgi/Tutorial



Feel free to ask questions below. I'll bump the thread when new installments are added.
« Last Edit: October 11, 2008, 05:29:33 PM by muku » Logged
increpare
Guest
« Reply #1 on: October 10, 2008, 03:59:54 PM »

That was...painless Smiley  Next step: copying to and from a 'central' repository?

Actually: here's a question.  I have many many projects.  Should I start right away creating reps for each of them, or is there some smart way to go about dealing with having many different ones?
« Last Edit: October 10, 2008, 04:04:23 PM by increpare » Logged
muku
Level 10
*****


View Profile
« Reply #2 on: October 10, 2008, 04:36:11 PM »

That was...painless Smiley  Next step: copying to and from a 'central' repository?

Yes, I guess I'll do that next. Or... some more advanced repo operations? Ah, I hope I'll get to it all in due time.

Quote
Actually: here's a question.  I have many many projects.  Should I start right away creating reps for each of them, or is there some smart way to go about dealing with having many different ones?

I'm not quite sure what you mean... are you thinking of automating the process of checking your files into a repository? It's very easy: a hg init in the relevant directory to set up a repo, a hg add (without further arguments) to add all files, and a hg commit to commit them. The only catch here is that hg add will by default add all files, even stuff you don't want versioned like binaries and such, but once we get to .hgignore files (which I wanted to cover next) that will be resolved too.

Or, if that was your question: you should definitely have one repo per project, and not one huge über-repo containing all your projects. And it also doesn't matter where they are in your file system, they are all self-contained.
Logged
increpare
Guest
« Reply #3 on: October 10, 2008, 05:07:15 PM »

Advanced repo operations would also be good.  I'll let you judge!

I meant that I have like, loads of project directories, and .... .hgignore sounds like what I should learn about before I start manually putting them into their respective archives.  Might save me some work?

Or, if that was your question: you should definitely have one repo per project, and not one huge über-repo containing all your projects.
That question wasn't actively on my mind, but I have wondered about it before Wink  So...thanks for the response...
Logged
muku
Level 10
*****


View Profile
« Reply #4 on: October 10, 2008, 05:31:16 PM »

Added a part about .hgignore.
Logged
increpare
Guest
« Reply #5 on: October 10, 2008, 05:40:34 PM »

Cool.  I want to add bitmap/resource files to my repo, right?
Logged
muku
Level 10
*****


View Profile
« Reply #6 on: October 10, 2008, 05:45:41 PM »

Cool.  I want to add bitmap/resource files to my repo, right?

Generally, yes. Mercurial doesn't have problems with handling binary files, though of course it cannot do automatic merging on them like it does with text files (another thing I might cover later).

Incidentally, since I forgot to mention it, you'll also definitely want to add makefiles, project files and such to the repo.
Logged
muku
Level 10
*****


View Profile
« Reply #7 on: October 11, 2008, 05:29:57 PM »

Added a section on accessing old revisions. I'm wondering if this isn't getting a bit too long and messy...
Logged
increpare
Guest
« Reply #8 on: October 12, 2008, 03:08:04 PM »

I just set up a bunch of scripts with Code::Blocks to commit, and also (skipping ahead a little) to push to a repository on another server (though I haven't done anything to take stuff back off it, I'm happy enough to have it has a backup for now Wink  ).

I have cygwin installed, and needed to add c:\cygwin\bin to my path so that mercurial could access ssh, but the commands look like this (from the Tools->Edit Tools menu):


<- this commits to the local archive


<- this *initializes* a blank remote archive (you only need to call it once per project)


<- this pushes the contents of your current repo to the remote archive (you should commit first)

Not perfect (they may not work if your project filename has spaces in its title), but they work.  I think.  And that's nice.   

You don't spot any obvious issues that are going to arise, do you muku?

I've left the local initialization of projects still as a manual task.  Don't feel like scripting that just yet.  (I may, in fact, never feel such a desire). 

I'll have to do something similar for xcode soon I think.
Logged
muku
Level 10
*****


View Profile
« Reply #9 on: October 12, 2008, 03:46:07 PM »

That looks fine to me. I personally prefer doing it from the command line because I use hg status and hg diff all the time, but this should work just as well.

The only thing to be aware of is that if you add a new file to your project, you have to run hg add in some form or another, or else Mercurial won't track the file. Also, when deleting or moving files, don't do it manually but use the corresponding Mercurial commands.
Logged
SplinterOfChaos
Level 3
***



View Profile
« Reply #10 on: November 15, 2008, 09:50:09 AM »

This is surprisingly simple, but I couldn't get very far in the tutorial.
Quote
C:\Documents and Settings\Owner\Desktop\Programming\C++\WorkSpace\Multi Thread logger>hg init

C:\Documents and Settings\Owner\Desktop\Programming\C++\WorkSpace\Multi Thread logger>hg add main.cp
p

C:\Documents and Settings\Owner\Desktop\Programming\C++\WorkSpace\Multi Thread logger>Fun
'Fun' is not recognized as an internal or external command,
operable program or batch file.

C:\Documents and Settings\Owner\Desktop\Programming\C++\WorkSpace\Multi Thread logger>...

C:\Documents and Settings\Owner\Desktop\Programming\C++\WorkSpace\Multi Thread logger>hg commit
'C:\Documents' is not recognized as an internal or external command,
operable program or batch file.
transaction abort!
rollback completed
abort: edit failed: Documents exited with status 1

C:\Documents and Settings\Owner\Desktop\Programming\C++\WorkSpace\Multi Thread logger>
Logged

increpare
Guest
« Reply #11 on: November 15, 2008, 09:56:11 AM »

This is surprisingly simple, but I couldn't get very far in the tutorial.
Quote
C:\Documents and Settings\Owner\Desktop\Programming\C++\WorkSpace\Multi Thread logger>hg init

C:\Documents and Settings\Owner\Desktop\Programming\C++\WorkSpace\Multi Thread logger>hg add main.cp
p

C:\Documents and Settings\Owner\Desktop\Programming\C++\WorkSpace\Multi Thread logger>Fun
'Fun' is not recognized as an internal or external command,
operable program or batch file.

C:\Documents and Settings\Owner\Desktop\Programming\C++\WorkSpace\Multi Thread logger>...

C:\Documents and Settings\Owner\Desktop\Programming\C++\WorkSpace\Multi Thread logger>hg commit
'C:\Documents' is not recognized as an internal or external command,
operable program or batch file.
transaction abort!
rollback completed
abort: edit failed: Documents exited with status 1

C:\Documents and Settings\Owner\Desktop\Programming\C++\WorkSpace\Multi Thread logger>

 Huh?

Why did you type in 'Fun'? 

After

hg add main.cpp

hg commit should work.  The 'C:\Documents' comment is a bit queer, and hints that there might be something that you did during the "..." phase of your stuff, possibly an incorrect paste, that screwed things up?
Logged
SplinterOfChaos
Level 3
***



View Profile
« Reply #12 on: November 15, 2008, 01:10:58 PM »

I didn't do anything during the ... phase. I actually typed in ... for the same reason I typed in Fun. Because I was playing with DOS. And getting Mercurial to almost work was...fun.

I'm thinking the problem is more that Mercurial, as are many programming tools I've used, is afraid of spaces in the address. Even my compiler couldn't handle a space! I'm partially here for confirmation.
Logged

george
Level 7
**



View Profile
« Reply #13 on: November 15, 2008, 01:32:53 PM »

That is pretty strange. I use Bazaar from the Windows terminal and it works fine. It seems rather weird that Mercurial wouldn't handle spaces in Windows file names. Just for the record if you type ... in a Windows terminal you get '...' is not recognized as an internal or external command, operable program or batch file.
Logged
increpare
Guest
« Reply #14 on: November 15, 2008, 05:12:48 PM »

Splinter, I haven't encountered any problems with spaces in directory names myself.  It does look like that might be a possibility.  You're in the best position to check whether this is what's happening on your system though Smiley
Logged
increpare
Guest
« Reply #15 on: February 11, 2009, 06:06:28 PM »

ha ha ha ha ha

I was bitching to terry earlier about how there is no nice windows integration for mercurial like tortoisesvn, but it turns out there's TortoiseHg.  Hurrah! Just downloadng it now to try out.

EDIT: it works great, hurrah!
« Last Edit: February 12, 2009, 05:59:01 AM by stephen lavelle » Logged
Greg
Level 0
***


has a compass, lost my map


View Profile
« Reply #16 on: February 24, 2009, 02:45:07 PM »

I'm going to learn this now I've decided.  I've been on svn & cvs for years, and started to learn git, but didn't immediately grasp what they hell they were talking about.  anyway, icculus switched to hg too, so I's might as well look into 'er.  Thanks.

edit: I knew I had to switch when I tried to check my svn logs offline. Nope, outta luck. See revisions -- no can do.  yeah, distributed is good.
« Last Edit: February 24, 2009, 02:51:33 PM by Gregoin » Logged

A day for firm decisions!!! Or is it?
nihilocrat
Level 10
*****


Full of stars.


View Profile WWW
« Reply #17 on: April 14, 2009, 09:06:10 AM »

I hate to make a comment that sort of invites "RTFM!", but I'm wondering if anyone's had better experiences getting a central repository to work with SFTP and/or HTTP. I remember trying out hg awhile ago, wanting to use it in the same way as I do bzr, and there was a hangup about this.

I'll take another look, but I think it can do SFTP but not HTTP. This means that I can host my own internal repos fine, but it is not possible to host them publicly without extra shenanigans / a daemon / something like that. With bzr, you can post your repo to some of your webspace and voila, you now have a copy of your repo that the public can readily copy and which you can pretty easily commit to, all without the need for a shell account, daemons, or anything like that.
Logged

muku
Level 10
*****


View Profile
« Reply #18 on: April 14, 2009, 11:40:04 PM »

I'll take another look, but I think it can do SFTP but not HTTP. This means that I can host my own internal repos fine, but it is not possible to host them publicly without extra shenanigans / a daemon / something like that.

Easiest way is to get an account at some free Mercurial host. I used assembla.com for a while, with the caveat that the accounts there are now either public or non-free. I guess there are others.

Hosting yourself, I'm not so sure... maybe this helps?


Quote
With bzr, you can post your repo to some of your webspace and voila, you now have a copy of your repo that the public can readily copy and which you can pretty easily commit to, all without the need for a shell account, daemons, or anything like that.

How does committing work? You still need some kind of write access to the repo?
Logged
gnat
Level 1
*



View Profile WWW
« Reply #19 on: April 16, 2009, 11:22:10 AM »

I'll take another look, but I think it can do SFTP but not HTTP. This means that I can host my own internal repos fine, but it is not possible to host them publicly without extra shenanigans / a daemon / something like that.
http://bitbucket.org is also good. I'm using it to host the public repository for my awesome, yet to be pimped minimalist opensource content management system.
Logged

LAN Party List - The definitive LAN party list. Also Game Jams, etc.
GitHub
Pages: [1] 2
Print
Jump to:  

Theme orange-lt created by panic