back to tutorial index
  back to homepage


CVS Basics

By Michael Hearn


This tutorial will teach you the basic principles of CVS source code control and some of the SourceForge specific implementation issues. By the end of it you should hopefully understand what CVS is, why it's cool and how to perform basic operations with it. You should ideally read this tutorial before any others. We'll start with a brief summary of CVS.

CVS stands for Concurrent Versions System and is a program that controls and manages program source code. If you don't know what source code is then I'm afraid you're in the wrong place. I'm going to assume basic programming skills here. Basically, CVS resolves the problem of multiple developers working on a project by allowing people to work on the same text file independently and then merging their changes together seamlessly. In this way many programmers can work on a project without worrying about accidentally blatting somebody else's code. It has many other features like file journaling which means that it keeps track of every change to a file since it's creation and any file can be reverted to an earlier state of development, even undeleted. This means that if somebody changes code that you don't want changed, you can "undo" the change, even if it changed multiple parts of multiple files. You can also "branch" the source code which allows you to have multiple versions of the same code being developed concurrently, with the branches of the source "tree" being merged together later. These are just some of the features of CVS, there are many more.

CVS has a long history and has been around since 1986, when it first appeared in the form of UNIX shell scripts written by Dick Grune on a UNIX newsgroup. It continued to develop on the UNIX platform which is why CVS is comparatively strange to most Windows users, and although no code from the original shell scripts is still in the CVS program today most of the code is based on them. The CVS we use today was originally written by Brian Berliner in 1989 with later help from Jeff Polk. CVS is free and open source, although it receives commercial support through Cyclic Software who provide a range of services for those willing to pay. 

Today CVS is in use around the world for many projects, as it has the benefit of being flexible, free, powerful and reliable (although you may doubt this as you learn to use CVS, believe me it is true!)


So how does CVS work and how does it change the way you develop programs? Firstly when you start using CVS you abandon your current copy of the code. Instead you checkout the code into a new directory which is monitored and controlled by CVS. This is called your working copy and is where all development now takes place. Every developer has their own working copy which is kept synchronized with the copy on the remote server by CVS. This is done using the two most basic CVS commands: update and commit. The first of these downloads changes from the remote repository on SourceForge and merges them with your working copy. It never destroys changes you've made and where possible attempts to merge them together. The commit command uploads your changes to the repository and merges them with the remote version. If changes are made to a file by CVS, it is always backed up into a file looking something like this:

#myfile.pas.1.16

The file myfile.pas now contains the merged text. If the merge fails for some reason (ie. the changes are at the same point) the two different versions are surrounded by <<<<<<<<<<< and >>>>>>>>>>>> marks like this:

function AnyFunction :boolean;
begin
  <<<<<<<<<<<< 1.5
  a + b := c;
  ==============
  a - b := c;
  >>>>>>>>>>>
end;

You must then remove the version that you don't want.

Before any changes are made to a CVS repository you must commit them, which is where the changes are uploaded along with a log message describing the change. This log message is important because it can be used to work out exactly what changed at any point in the development process which as you know is always useful for debugging. When I say "changes" I don't just mean changes you've made to the code but also things like adding and removing files/directories, tagging files (not important right now) and so on. Any changes you make to the repository are only confirmed when you run commit and if you change your mind about anything before you commit, you can cancel the changes. This is explained later. Remember that you can commit files and folders individually but it is more usual to commit the whole module at once, this way you don't miss any files. Only the things that have been changed will be committed. CVS won't let you commit your changes if something has changed in the meantime as a safety measure, if you try to commit and somebody else's changes would conflict with yours you will receive an error message like this: "CVS Error: up to date check failed".  The solution? Update to merge the changes then recommit (once you've checked everything still works!)

Perhaps the biggest change is in the development cycle. Before you worked like this:

- Code
- Test and debug
- Go back to the start until feature or upgrade is complete.

With CVS there are a few more stages added to this cycle:

- Run a CVS "update"
- Code
- Test and debug
- Go back to code until feature or upgrade is complete
- Run another "Update" to merge any changes other developers have made to the code in the meantime with your working copy.
- Make sure everything still works!
- Run a CVS "commit" to merge the changes you have made to the code with the code on the remote server. When the other developers run the update at the beginning of the cycle they will download your changes.

An example might make things clearer: Imagine you have a file with 2 functions defined in it, funcA and funcB. You have 3 developers all of whom have checked out their own working copy of the source from the CVS server. Developer A adds a new function, funcC and commits. Developer B changes their copy of the file at the same time and changes funcB. Developer C adds a completely new file. This is what happens when they update and commit: Developer A commits his change to the repository with a log message describing what's different. Developer B tries to commit her working copy of the file but can't. This is important, if a file has changed since you last updated and you try and commit a change CVS won't let you until you have updated to merge other changes with your own. So B runs an update and sees CVS merge the changes A made with her own. The file is now merged together automatically and B can successfully commit the changes. Finally, Developer C commits the new file to the repository and updates to receive the combined changes of A and B.

Finally, each file controlled by CVS has its own revision number that looks something like this: 1.14
The number is incremented by 1 each time changes to that file are committed. Don't be fooled, the 1 at the front never changes, it's the branch number and revision numbers can look more complex than that, but for now we won't worry about it. Using revision numbers you can access the file at any stage in its history and compare revisions.

I know that's not as clear as it might have been, but really once you clock what's going on it's quite simple. If you still don't get it try re-reading the last section a few times.


Here are a few CVS terms. I'll use these terms throughout the tutorial so you might as well learn them now:

These are only a few, there are many more. However, these are the essential ones.


CVS and the real world

So how do you do basic tasks with CVS then? This section will take you through some of them.

Adding files/folders: To add files you must first copy them to your working directory. In WinCVS you will see the file listed as "NonCVS file" which means CVS is not monitoring it:

This folder contains a mix of files and folders, as you can see some are monitored and some are not. The revision numbers are in the second column. If I wanted to make CVS monitor and distribute the file to the other developers I need to add it to the repository. You can do this using the buttons I've circled in red. Make sure you use the correct button, the left hand side is for text files, the right hand button is for binary - if you get it wrong you must remove the file and add it again. Once I've added the file (you need to be connected to the net to do this) the file will go red, showing that it's been modified. When you next commit the file will be uploaded and will get a revision number of 1.1

Folders are the same except that adding a folder doesn't add any of its children, you must do this manually. If you have a lot of folders you want to add you can use import to do this, but this is a relatively advanced technique.

Removing files/folders:  Removing files and folders under CVS is interested because it's impossible. Yes, that's right: you can't actually ever remove anything added to CVS, all you can do is effectively hide it. This is because CVS must be able to reconstruct the history of a file even after it has been deleted. You can't even hide folders either, the only way to remove a directory is to remove every file in it, and then CVS will automatically delete empty directories when you update. So how do you do it?

Well, you press the X button on the toolbar to delete files, which will then be marked as deleted (again this requires a connection to the server). WinCVS will delete the files from your hard disk automatically (well, move to recycle bin, which is the same thing). When you commit it'll be removed (hidden) in the repository and in the next update you'll see something like this:

cvs update: Updating .
cvs update: warning: PythianProject.dsk is not (any longer) pertinent

and it will be deleted. If you make a mistake and delete a file it can be resurrected before you commit by doing an update on the file. If you commit a file and then later want it back, well you can do this too but i'm not going to cover that here because I'm beginning to feel quite tired. Bed soon I think.

Moving files/folders: Can't be done as such, you must remove them and then re-add. Bear in mind that this will splat the revision history. Again, it's possible to preserve them but that's advanced stuff.

Canceling changes: If you make some changes to a file and want to cancel them you can delete the file from the local working copy and then run update. One of the features of update is that if a file is missing it will be re-downloaded from the repository, so this makes a good way of restoring to the last commit.


I hope this tutorial has helped you understand what you can do with CVS, and how to do it. If you have any comments please tell me about them, and I may incorporate them into this document.