The Basics of Version Control
We now know what version control is for, and we've got our hands on some nice tools to do version control with Mercurial.
It's now time to dig in and do something useful with Mercurial. (Finally!)
I'll admit, this tutorial is a long one. But once you get done here, you'll be able to start using Mercurial effectively.
We'll start with the obvious: creating a new repository. We'll also talk about how to handle the situation in which you've already got existing stuff to add to your repository. We'll then get into what I described earlier as the typical process: make some changes, if you like them, commit, if you don't like them, revert. This tutorial will also cover deleting old files as well.
That gives us everything we need to start going with Mercurial. There will still be plenty more to learn in upcoming tutorials, but this will give us a solid foundation.
Creating a New Repository
Let's start off by assuming you've got a directory where you want to put your code. (Or something else you want to put under version control.) At the moment, it doesn't matter if there is stuff in it or not.
To start a new repository, go browse to the directory that you want to put under version control. For the sake of this tutorial, I'm going to assume the following: I created a directory for a game I'm going to make called Real Time Chess. The folder is called RealTimeChess, and I already have a document called Design.txt sitting in it.
For those of you playing along with this tutorial at home (that's you) you should not just read this tutorial, but actually follow it. Trust me, the concepts that you learn here will stick so much better if you actually do these things instead of just reading them. So go make a folder somewhere on your computer. (Or use one that you're actually planning on putting under version control. It's OK if it already has stuff in it. We'll deal with that in a second.)
To create a repository, I'll browse to my project's directory, and inside of the main directory (or in the layer above, if you right-click on the project directory itself) right-click and in the popup menu, choose TortoiseHg > Create Repository Here.
(The command line equivalent is hg init.)
This will bring up a little dialog that asks you to confirm some options, but everything should be the way you want it by default. On that dialog, just press Create and your repository will be created!
What did this do?
It added two files to that folder. (You could undo what you just did by deleting these two files, but don't do that.) The .hg folder is like Mercurial's little toolshed. This is where Mercurial dumps all of it's relevant information. In a lot of ways, this is the repository.
It also creates a file called .hgignore. This is a list of the files in your directory that should be ignored—the ones that should not be kept track of in version control. (We'll talk about how to do that later, as well as the types of files that you usually want to ignore.)
You want to just leave both of these files alone. By mucking around with them, you could break your repository, which puts a damper on the adventure faster than ripping a hole in the seat of your pants on a hot date. Just leave them alone. Mercurial can take care of itself.
Adding Files to the Repository
Now that we've got a working repository, it's time to add some files. I'll start simple, by adding my Design.txt file. Go ahead and create a new file if you don't have anything useful in your directory yet.
At the moment, my Design.txt file contains the following:
# Real Time Chess This is a document describing The Next Big Thing in Game Development™ called Real Time Chess. It is a variation on the classic game of Chess with epic proportions.
Not much, but it's a start, and I'm totally ready to save that version in my repository forever, because that description is poetry!
There are a couple of ways that we can add new files. One is a two-step approach, and the second is a one-step approach. For the most part, we'll probably prefer the one-step approach, but I want to show you the two-step approach anyway.
The two step approach is to select the file (or files) that you want to add, right-click and choose TortoiseHg > Add Files… This will bring up a dialog that allows you to select new files. The one you right-clicked on should be checked by default (check the box in the left panel if it's not) and click Add to close the dialog box.
(The command line version of this would have been hg add Design.txt.)
You'll now see that TortoiseHg has added a little icon overlay to the document you just added:
In fact, TortoiseHg's little icons are going to be one of your best friends. These help you keep your sanity when you're managing hundreds of files, because you can know the status of every item in a heart beat.
So here's the thing with adding files: you haven't actually added the file to the repository. You've just primed the file—set things up so that on the next actual commit, this new file will get committed.
So to complete the process, we'll have to do a second step and commit the new file. We're going to talk about committing in detail in a second, so I'm actually going to hold off on that for a bit.
But that describes the two-step process that you could follow. In reality though, you can skip the add step in most cases. By just jumping ahead and committing right away, you'll get the opportunity to add any new files that have appeared since your last commit. So in general, I recommend you don't waste time running the Add command upfront. However, if you're doing this command line, you'll have to do this step.
As I described earlier, the most common task that you'll do is to commit changes to the repository. At a simple level, all you're doing with a commit is saying, "The current state of my code is worth remembering. Take a picture of this and save it forever."
And on a side note, it's like you're taking pictures with a digital camera, not one that uses film. You'll make life easier for yourself if you commit frequently. With older version control systems (like SVN) committing changes inflicts those changes on everyone on your team, even if they weren't ready for it.
With a distributed version control system like Mercurial, you're just saving these changes to your local repository. There's no impact on anyone else yet. (This is the biggest advantage to distributed version control.)
So commit often. With SVN, sometimes people only commit a couple of times a week. They might even go a couple of weeks between commits if they're working on a large change. With Mercurial (or Git) you should be committing much more frequently. Probably several times a day. Maybe even every few minutes.
Anyway… where were we?
We'll start off by committing the changes we just made (adding our first file). You can start the commit process from anywhere within the repository. (If you're used to SVN or some other version control systems, this is a little different than what you're used to. With Mercurial, committing will attempt to look at changes across the entire working copy, regardless of what directory you start it in.)
You then right-click on the directory and choose Hg Commit…. Notice that this is not under the Tortoise Hg sub-menu. Committing is the most common operation that you'll do, so they make it easy to get to. (By the way, Tortoise Hg lets you customize which menu items appear at the top, and which ones don't. But that's a discussion for another time.)
This brings up the Commit dialog:
This dialog gives you the chance to see and select which files will be committed. You'll see that our Design.txt file is green and has an A by it. This tells us that this file has been added, and when we commit, it will start tracking the changes to this file.
But what's this? The .hgignore file is pink, unchecked, and has a ? by it.
This highlights why (a) I usually skip the initial add step, and (b) why I usually go up to the very top of the directory structure to do my commits. .hgignore should be added and committed, but we forgot to do it. Because we committed from the top level, it brings this file up in the list, drawing our attention to the fact that we've never told Mercurial what to do with it.
But this gives us the chance to see how to get files added without needing to manually do the add step first. We're going to want to track changes in .hgignore, so we'd better add it.
To do this, simply check the box by .hgignore and you'll be good to go. (Note that when you actually commit, it will bring up a dialog box asking you to confirm that you know you're adding new files in addition to just committing changes to existing files.)
Before committing, we need to add a commit message. Going back to our picture taking analogy, this is like the little text that people put next to the image in their photo album. (Wait, do those things still exist? I take it back. It's the caption that people put on the photo when they upload it to Facebook.) The purpose of the commit message is to tell you what changed in this commit.
I suspect there's a way to override it, but Mercurial really wants you to add a commit message. Other version control systems don't require it, but it is always a good idea to add a commit message. It doesn't have to be long. (It probably shouldn't be.) Just enough that when you look back over the version history, it can jog your memory.
The commit message goes in the text box in the upper right corner. (It's not labeled very clearly, unfortunately.) I'm going to use "Initial commit. Adding design doc." as my message. Then hit the Commit button at the bottom and you're done! (If you checked the box by .hgignore, it will ask you to confirm that at this point, as I mentioned.)
At this point, the Commit dialog will update itself to show the new state: no changed files—nothing to do. Go ahead and close the dialog now.
(To commit from the command line, you'd run hg commit -m "Initial commit. Adding design doc.".)
Remember that the typical process with version control is to make some changes and commit if you like them, revert if you don't.
So let's go make some changes. Go try out some other change if you want, but I'm going to just add some text to my Design.txt file so that it looks like this:
# Real Time Chess This is a document describing The Next Big Thing in Game Development™ called Real Time Chess. It is a variation on the classic game of Chess with epic proportions. This is the best game you'll play in your lifetime!
I like this state too, so I'm going to go commit it. I'll repeat the process that we did before. Right-click on my RealTimeChess folder and choose Hg Commit… You may notice that the dialog looks slightly different this time, especially down in the lower right part:
There's red and green text down there. This is showing you the changes that have been made, line by line. The red text, which starts with a -, indicates text from the old version that no longer exists in the new version, at least not in the exact same way. The green text, which starts with a +, indicates text that is new or modified in the new version of the file.
This particular edit comes across a little funky, because on that first line, all I did was add a new line at the end. Mercurial is seeing that as a change to the whole line, and shows me both the old (the red) and the new (the green) versions of that line.
Showing the file this way gives you a chance to take a second look at what was changed before committing it forever. If you've made any dumb edits, this is your chance to catch it.
But this change is good, so we'll move along, add a commit message ("Added a statement describing the life-changing effect players of this game will feel.") and commit our change.
Two revisions saved!
We can repeat this process as often as we want. We're on our way!
Reverting a Change
OK, so we've looked at what to do if we make an edit we like. What if we make an edit that we don't like?
Well the non-Mercurial approach is to find your edits and undo them. Let's say I was thinking about the line I just added. The one about this game being the best game you'll ever play. Maybe I started thinking about how that's a pretty bold statement. Perhaps it is just the best game you'll play today. So I start to panic, thinking that I'm over-promising, and tweak my file to say:
# Real Time Chess This is a document describing The Next Big Thing in Game Development™ called Real Time Chess. It is a variation on the classic game of Chess with epic proportions. This might possibly be the best game you'll play today. But I'm not sure.
Well… at least now I'm not over-promising.
But wait. Where did our confidence in this game go? Let's rethink this. It's going to be a great game! What were we thinking?! This will truly be a great game, and we should do what every politician does and escalate our rhetoric to the point where it can't be ignored.
Undo the changes! Full reverse! Turn to starboard! Ready the long nines! Swab the deck! (Wait, I'm not a pirate you say? Hmpf.)
So we made a change that we don't like. (Perhaps across multiple files over several hours or even several days.) But remember that we took a snapshot earlier that we do like? Let's just go back to there.
To do this, right-click within your project and choose TortoiseHg > Revert Files…. This will bring up the Revert Dialog, which will show you the changes that have been made (to allow you to double-check whether this is the right move). You can then push Revert and your project will be reverted to the last snapshot!
(On the command line, you would use hg revert [filename] or hg revert —all to revert.)
Note that we're reverting to the latest commit. Later on, we'll talk about how you can get back to commits besides the most recent one.
We've now built up the whole basic process: make changes, commit or revert. Make changes, commit or revert. We've already talked about adding new files, so I want to bring up how to delete files as well.
One of the interesting things about deleting files is that they're never truly gone, as long as they were a part of an earlier commit. We haven't looked at it yet (we will soon) but you'll be able to go back and look at the state of your files at any previous commit. So if you don't think you need a file any more, but you're nervous about deleting it just in case, fear not. If you ever need it again, you can go back and dig it up.
There are two general ways to delete a file. (This is becoming a trend, isn't it?)
The thing you have to know is that deleting a file on your file system the normal way (selecting the file and hitting the Delete key) doesn't quite cut it. Mercurial won't assume that just because a file disappears, it must be gone forever.
So the first way to handle a delete is, instead of hitting the delete key, right-click on the file and choose TortoiseHg > Remove Files…. This brings up the Delete dialog, which allows you to check and uncheck files that you want to delete. Push the Remove button to make your final selection.
You'll see that the file gets deleted off your file system after this. However, like with adding files, you've really only set this up to be deleted. You still need to commit your changes by doing a normal commit. When you do, you'll see the deleted file show up in red with an R by it in the list of file changes being committed. Finish the commit, and the file's gone.
Of course, like with adding files, the simpler way to do this is probably to just delete the file with the Delete key, like normal, then do a commit. On the Commit dialog window, you'll see the missing file with a ! by it, and colored in red. Simply check the box by it (it will never be checked by default if you delete this way) to confirm that the deletion is intentional. Commit like normal. Like when adding, Mercurial will ask you to confirm that you're certain you want to delete the file.
Going forward, I'm going to make the assumption that you're comfortable with these basics (creating a repository, adding files, committing, reverting, and deleting files). Now is a good time to spend a few minutes to stop and play around with these fundamentals.