15 Jan 2007

Game Development Essentials: Version Control

This is the first post in a series, which - for lack of a better name - I shall call Game Development Essentials. Setting up a development infrastructure is not easy, there are many things to consider besides the fun stuff (which is programming of course). This series will deal mainly with the technical stuff. It's not about how to finance the company or how to deal with publishers. Most of the things presented here will seem obvious to many people working in the industry. That's because simple solutions seem obvious to those who already know. But getting to the simple solution of a problem is often a hard and bumpy road with many over-complicated solutions in the way...

Sooo... Version Control! If you are already working with a version control system day by day, you can simply skip this post. But I'm still amazed how many programmers fresh from university haven't used it yet, or haven't even heard of version control.

Version control is a must in any type of project. The most important feature of a version control system is to track changes on the project over time. Version control basically adds a time-slider to your project. You can go back to any point in the past and check-out the state of the project at that point in time. This is very useful even when working alone on a project. You never need to be afraid of breaking existing code. If halfway into rewriting some existing code you realize that the rewritten code will never work as intended, but you already changed 10.000 lines, and of course forgot to backup the affected files... well, that's where version control comes in handy. Just go back to the latest version that worked and you're fine!

Version control becomes even more useful when working in a team, because the version control system will not only track what has changed, but also who did the changes, and it will merge the changes of all team members into the common code base, distributing the changes of each team member to all the others. Merging even works when several team members are working simultaneously on the same file (as long as different people don't change the same piece of code, this is called a merge conflict which must usually be resolved manually).

Version control systems usually follow a simple client-server model. The server stores the so-called repository, which holds the latest valid state of the project and its entire history. This is the really important data which you want to backup regularly. The clients are the PCs of the team members, where each client holds a local copy of the project data, including local changes which haven't been committed yet.

Setting up a version control system is fairly simple. There are at least 2 popular completely free version control systems around: CVS and Subversion. CVS is the grandpa of Subversion and has a few well-known quirks and issues. Subversion is supposed to be the successor of CVS and fixes most of CVS' shortcomings and offers better scalability for larger projects and teams. At Radon Labs we're still using CVS, but when starting from scratch, Subversion may be the better choice. For a small team of 5 to 10 people a normal PC-class server is completely adequate. The important features of a CVS server seem to be RAM, hard disc size and performance, and network speed. Our very first CVS server at Radon Labs was a 100 MHz Pentium I, which nobody wanted to work with anymore because it was so slow. It admittedly reached its limits once we hit 10 people or so. When the server is running a flavor of Linux, it's often enough to install the CVS or Subversion package coming with the Linux distro. Everything necessary to make the version server run should be handled by the package manager.

On the Windows client machines, do yourself a favor and just install TortoiseCVS or TortoiseSVN. These are both absolutely stunning tools which integrate the version control clients with the Windows explorer context menus. Before TortoiseCVS showed up, using CVS under Windows was a real pain, especially for non-programmers.

From that point on, you can basically start playing around with a small test project. The Tortoise tools make it easy to explore version control features step by step.

The typical day when working under a version control system looks like this (I'll use CVS lingo because I'm more familiar with CVS):
  • In the morning, first thing you usually do is a cvs-update. This pulls in all the changes from other team member which had been committed since you last showed up in the office.
  • When you actually start to work, you're usually either changing existing stuff, delete stuff from or introduce new stuff to the project.
  • When changing existing stuff, you do a cvs-edit on the affected files. This removes the write-protection from the files, stores a local backup (in case you want to undo your changes), and lets you edit the file.
  • When removing files, you do a cvs-remove. This will just mark the file for removal, the server doesn't know about this until the changes are committed later on.
  • When adding new files, you create them on your local machine as usual, and do a cvs-add on the files. It's important to know that this will just mark the files for addition, the server won't know about those new files yet.
  • When you're finished with your shiny new feature, and you're sure your new feature plays well with existing stuff, you do cvs-commit and go home... in a perfect world at least.
cvs-commit is about the only operation where something can go wrong. That's because other team members have been happily working along all day in parallel to you, and they may have checked in their stuff and left early before you were able to get out. Now what may happen is that their changes interfere with your changes!

They may have edited the same code locations like you, or may have added files under the same name. These are called collisions, and they happen from time to time. If they happen too frequently (lets say, several times week), you have a serious communication problem in your team. Small, isolated collisions within a source file are easy to fix manually. Before the version control commits your changes to the server, it will check whether somebody else has committed changes to your edited files in the meantime and will refuse to execute the commit if this is the case. Instead it asks you to perform a cvs-update. While updating, the version system will try to merge changes from other team members into your local copy. This works surprisingly well, unless the changes affect the same piece of code in a file. In this case, the update process will notify you that a merge-conflict has happened, and will ask you to resolve the collision manually. It is important to note, that merging always happens on the client machine. So, a merge-conflict will never break the existing valid code on the server. Everything happens locally on your client, and the client will refuse to commit anything until all conflicts are resolved.

When done with the manual merge, you should definitely recompile the project and make sure everything works as intended before trying the commit again. A merge-conflict should ring the alarm bells in your head and you should make double sure that everything is fine before committing. If possible, get hold of the team member who committed the conflicting piece of code and ponder with him over the code in question.

Now, this merge-conflict stuff may sound scary. But don't distress, conflicts don't happen very often (at Radon Labs maybe once or twice every month). Just make sure everybody in the team knows what the other guys are doing, update and commit regularly, and you should be set. If you want to do changes to existing code, talk to the guy who originally wrote the code. Also, don't forget to cvs-add new files before committing, missing files are probably the number-one reason for the nightly build to break. Don't be the one to blame :)