CroftSoft / Library / Tutorials

CVS for Version Control

David Wallace Croft


For an updated version of this tutorial, please see Appendix B of Advanced Java Game Programming.

-- David Wallace Croft, 2005-03-30

Unless stated otherwise, quoted material was taken from the CVS documentation "Version Management with CVS for CVS 1.10" by Per Cederqvist et al.

CVS Fundamentals

Concurrent Versions System

Concurrent Versions System (CVS) is a free Open Source version control system in widespread use. It is used primarily by software developers to save their source code updates to a central repository incrementally in such a way that anyone can "undo" any number of changes in order to return to any previous version. Developers use a CVS client to periodically synchronize their local working copies of the source code with the remote central repository via the Internet.

Command-Line Client

While GUI client applications for Concurrent Versions System (CVS) exist, I prefer the command-line interface. It makes it easier as the command-line interface is available and the same on both Unix and Windows. My favorite free command-line CVS utility can be found at the downloads page.

Version Control for Javadoc

Version control is not just for source code. It may used with any file but works best with hand-typed plain-text files. For example, your javadoc overview.html and package.html files should be checked into CVS. It is unnecessary to check in javadoc HTML files that can be automatically generated from the source code.


If you are updating your working files on the same machine as the central repository, you will need to set your CVSROOT environment variable. The CVSROOT environment variable points to the root directory of the CVS repository. This is different from your local working directory where you will be doing your actual editing. In the following examples, CVSROOT is "/usr/local/cvsroot".

If you are using C shell (csh), append the following to your ".login" file:

setenv CVSROOT /usr/local/cvsroot

If you are using sh or bash instead, append the following to your ".profile" file:


export CVSROOT

An alternative is to modify your .bash_profile file:

export CVSROOT=/usr/local/cvsroot

You may wish to set your editor environment variable as well since the editor specified will be called by CVS to enter log messages:

export EDITOR=/usr/bin/vedit

Remote Access

If you are accessing the central repository from a remote machine, you do not set your environment variables. Instead you specify your remote account username, the CVS server host, and the remote CVSROOT directory as command-line arguments the first time you use the command-line client:

cvs -d login

You will be prompted for your account password.

On subsequent uses of the cvs command, you no longer need to specify the login arguments as these parameters are saved to a persistent file named ".cvspass" in your home or root directory.

Checking Out a Project

To make a working copy of a pre-existing project from the CVS repository in your working directory, use the following command, substituting "core" with the CVS project name:

cvs checkout core

Creating a Project

You can add a project to the CVS repository. When importing source code, be sure you have a copy in another location first.


cd core

cvs import -kb -I ! -m "initial import" core CroftSoft r1

cd ..

rm -r core

cvs checkout core

If the files you want to install in CVS reside in `wdir', and you want them to appear in the repository as `$CVSROOT/yoyodyne/rdir', you can do this:

$ cd wdir

$ cvs import -m "Imported sources" yoyodyne/rdir yoyo start

Unless you supply a log message with the `-m' flag, CVS starts an editor and prompts for a message. The string `yoyo' is a vendor tag, and `start' is a release tag.

Starting a project with CVS

To prevent CVS from converting the carriage return/linefeed character pair line endings used in DOS and Windows to the single linefeed character used in Unix, use the "-kb" option. As CVS cannot distinguish between a text and a binary file, this will also prevent the multimedia files in your import from being corrupted by the line ending conversion.

In retrospect, I think this was bad advice. I think you are better off without the -kb option:

cvs import -I ! -m "initial import" core CroftSoft r1

A better way to prevent your multimedia files from being corrupted is to modify the cvswrappers file as described in Appendix B of Advanced Java Game Programming and the CVS documentation.

-- David Wallace Croft, 2005-03-30

By default, CVS will ignore certain names such as "core" when importing a directory hierarchy. Use the "-I !" option to force the import command to include all directories and files.

Adding Directories

To add a Java package "com.croftsoft.core.util" in the existing package "com.croftsoft.core" in the CVS project "core", you would do the following:

cd core/com/croftsoft/core

mkdir util

cvs add util

cvs commit

Adding Files

To add a Java class file "com.croftsoft.core.lang.StringLib" into the CVS tree, you would do the following:

cd core/com/croftsoft/core1/lang

cvs add

cvs commit

Multiple files:

cvs add *.java

cvs commit

Binary files:

cvs add -kb *.jpg

cvs commit

Removing Files

You must first delete the file in your working copy.


cvs remove

cvs commit

Removing Directories

Normally, you should not remove a directory from the CVS repository without unanimous coordination with the other developers.

You can relatively safely remove the files from the CVS repository and then remove the directory from your working directory.


cvs remove

cvs commit

cd ..

rmdir image

Updating Files

If you delete a file accidentally and wish to recover it from the version control repository, you can do the following:

cvs update

If you wish to update your working directory and all sub-directories to incorporate changes made by other developers that have been checked into the repository, do the following:

cvs update .

Programming in Parallel

There are many inherent challenges when multiple developers are modifying source code in the same repository. Frequently, changes made by one developer made lead to countless hours of debugging for others. A version control system cannot by itself remove this problem entirely. A combination of development practices, documentation, and consideration are required as well.

Code Ownership

When multiple developers are working on source code in the same repository, the simplest and safest method of preventing difficulties is the practice known as "code ownership". When developers are practicing code ownership, each Java class source code file is assigned to a "code owner", usually grouped by project or Java package. When a developer realizes that a modification or addition needs to be made to the source code, he contacts the appropriate code owner and negotiates who will make this change.

When code ownership is being practiced, developers resist the desire to make a quick bug-fix to other developers' source code without informing them. This politely educates the code owner who created the bug or knows who did, obviates a subsequent debugging session by the code owner to fix the real bug created when the other developer "fixed" the first supposed bug, and can lead to source code which has a more logical and consistent design.

All developers who modify source code, for any reason whatsoever, should place their javadoc "@author" tag in the file and update the version date. This both alerts the other developers that changes in behavior may be due to modifications by other developers, provides credit where credit is due, and allows the code owner to track down those with experience and knowledge with certain code sections that may need debugging or enhancement. The latter is especially important with legacy source code and projects with high developer turnover.

Update Notification and Review

If code ownership is not being practiced, CVS provides tools to alert developers to changes by other developers in source code of interest.

You can receive automatic e-mail notifications of any updates to a project. The e-mail addresses of those monitoring updates for any given project are stored in the "loginfo" subdirectory of CVSROOT.

The `taginfo' file defines programs to execute when someone executes a tag or rtag command.

User-defined logging

You can examine the revision numbers and comments associated with revisions using the following command:

cvs log

You can observe changes between revisions using one of the following:

cvs diff

cvs diff -r 1.1 -r 1.2

Simultaneous File Editing

If multiple developers are simultaneously editing the same file, serious confusion and debugging may result when such changes are committed. Code ownership is strenuously advocated to prevent such.

Some version control systems, such as RCS, SCCS, JavaSafe, and VisualSafe, assist in automatically enforcing a form of code ownership at the individual file level. All source code files in a developer's working directory are read-only by default. When a developer wishes to modify such files, he sends a command to the central version control server. The central server then marks the file as being modified by the developer. No other developer can accidentally modify the file in his respective work directory. When the developer is done and any changes are committed to the repository, the file is again marked read-only in the working directory and a command is sent to the central server to allow some other developer to modify the file exclusively.

By default, CVS does not support file locking. Instead, simultaneous modifications are "merged" by a pseudo-intelligent software algorithm. The algorithm is not capable of handling major shifts in the file structure nor of actually comprehending the semantics of the code in a tightly bound object source file. It is reported that some software developers, who have experienced unexpected frustrations due to the less than humanly-intelligent software merge algorithms, will turn this feature off and manually merge such files upon receiving the potential conflict alert message from the version control system.

While CVS cannot prevent multiple developers from editing copies of the same source file in their respective working directories at the same time, it does provide a facility known as "watch".

For many groups, use of CVS in its default mode is perfectly satisfactory. Users may sometimes go to check in a modification only to find that another modification has intervened, but they deal with it and proceed with their check in. Other groups prefer to be able to know who is editing what files, so that if two people try to edit the same file they can choose to talk about who is doing what when rather than be surprised at check in time. The features in this section allow such coordination, while retaining the ability of two developers to edit the same file at the same time.

For maximum benefit developers should use cvs edit (not chmod) to make files read-write to edit them, and cvs release (not rm) to discard a working directory which is no longer in use, but CVS is not able to enforce this behavior.

Mechanisms to track who is editing files

Watch Commands

Command: cvs watch on [-lR] files ...

Specify that developers should run cvs edit before editing files. CVS will create working copies of files read-only, to remind developers to run the cvs edit command before working on them

Mechanisms to track who is editing files

To set a "watch" on a directory and all sub-directories, use the following command, replacing "core" with the directory name:

cvs watch on -R core

Use the following command to make a source code file temporarily read-write for modification by yourself:

cvs edit

When done, use the following to commit your change to the repository and to make your local copy read-only again:

cvs commit

If you wish to release the file to make it read-only again without committing, use the following command:

cvs unedit

A watch prevents accidental simultaneous editing by alerting "watchers" whenever a cvs edit, commit, or unedit command is issued. Generally, a "watcher" will be any developer who wishes to be alerted whenever someone is beginning to modify a given source code file. Watch notifications will not be sent to one who caused a notification event so you will not be alerted to your own edits.

To add yourself as a watcher of the files in a directory and all sub-directories, use the following command:

cvs watch add -R core

Replace "add" with "remove" above to discontinue watching those files.

To see who is editing a file at any given time, use

cvs editors -R

Revisions Numbers, Tags, and Branches

"Version" is Ambiguous

A file can have several versions, as described above. Likewise, a software product can have several versions. A software product is often given a version number such as `4.1.1'.

Versions in the first sense are called revisions in this document, and versions in the second sense are called releases. To avoid confusion, the word version is almost never used in this document.

Versions, revisions and releases

Revision Numbers vs. Tags

Normally there is no reason to care about the revision numbers--it is easier to treat them as internal numbers that CVS maintains, and tags provide a better way to distinguish between things like release 1 versus release 2 of your product (see section Tags--Symbolic revisions).

Assigning revisions


You'll want to choose some convention for naming tags, based on information such as the name of the program and the version number of the release. For example, one might take the name of the program, immediately followed by the version number with `.' changed to `-', so that CVS 1.9 would be tagged with the name cvs1-9.
The following example shows how you can add a tag to a file. The commands must be issued inside your working copy of the module. That is, you should issue the command in the directory where `backend.c' resides.

$ cvs tag rel-0-4 backend.c

There is seldom reason to tag a file in isolation. A more common use is to tag all the files that constitute a module with the same tag at strategic points in the development life-cycle, such as when a release is made.

$ cvs tag rel-1-0 .

Note that this is recursive; all subdirectories will be included.
The checkout command has a flag, `-r', that lets you check out a certain revision of a module. This flag makes it easy to retrieve the sources that make up release 1.0 of the module `tc' at any time in the future:

$ cvs checkout -r rel-1-0 tc

This is useful, for instance, if someone claims that there is a bug in that release, but you cannot find the bug in the current working copy.

Tags--Symbolic revisions

Branches for Patches

Suppose that release 1.0 of tc has been made. You are continuing to develop tc, planning to create release 1.1 in a couple of months. After a while your customers start to complain about a fatal bug. You check out release 1.0 (see section Tags--Symbolic revisions) and find the bug (which turns out to have a trivial fix). However, the current revision of the sources are in a state of flux and are not expected to be stable for at least another month. There is no way to make a bugfix release based on the newest sources.

The thing to do in a situation like this is to create a branch on the revision trees for all the files that make up release 1.0 of tc. You can then make modifications to the branch without disturbing the main trunk. When the modifications are finished you can elect to either incorporate them on the main trunk, or leave them on the branch.

What branches are good for

Creating a Branch

You can create a branch with tag -b; for example, assuming you're in a working copy:

$ cvs tag -b rel-1-0-patches

Creating a branch

Using Branches

To check out a branch from the repository, invoke `checkout' with the `-r' flag, followed by the tag name of the branch (see section Creating a branch):

$ cvs checkout -r rel-1-0-patches tc

Or, if you already have a working copy, you can switch it to a given branch with `update -r':

$ cvs update -r rel-1-0-patches tc

Accessing branches


Your branches can have sub-branches and your sub-branches can have sub-sub-branches, etc.
However, CVS is not limited to linear development. The revision tree can be split into branches, where each branch is a self-maintained line of development. Changes made on one branch can easily be moved back to the main trunk.

Each branch has a branch number, consisting of an odd number of period-separated decimal integers. The branch number is created by appending an integer to the revision number where the corresponding branch forked off. Having branch numbers allows more than one branch to be forked off from a certain revision.

Branches and revisions

Merging Branches

You can merge changes made on a branch into your working copy by giving the `-j branch' flag to the update command. With one `-j branch' option it merges the changes made between the point where the branch forked and newest revision on that branch (into your working copy).


A conflict can result from a merge operation.

Merging an entire branch


Merging branches can be complicated and error-prone. It is simpler to create a new project for each release. For example, the project core1 resource files could be copied over to a new project called core2 at a milestone. Patches to an older release can be then be made without complication. This may use more disk space than branching but it reduces complexity considerably. Disk space is always cheaper than man-hours.


CroftSoft Web

Creative Commons License
© 2005 CroftSoft Inc.
You may copy this webpage under the terms of the
Creative Commons Attribution License.