CVS Foundations

The CVS examples in this chapter are ran against CVS version 1.11.13, running on a standard LINUX server. While it is possible to run CVS on the Windows platform, you are most likely to encounter it running on a UNIX or LINUX system when working on open source projects.

The CVS Local Server

In order to acquaint you with the process of using CVS, a test cvs repository is needed. Rather than incur the cost of setting up a complete remote repository, we will create a CVS local repository and use that for the client-side examples. A complete step-by-step for creating a remote CVS repository is covered in the latter half of this chapter. From this point forward, let us make the assumption that we are a user “wrox” with a home directory on a Linux server called “linuxserver”. To most closely follow the examples, create a user called wrox on your Linux server. To create the local repository, log in to linuxserver. For the first examples, we’ll tell CVS to create a repository right here in wrox’s home directory, and then connect to it. A CVS local repository is one that cannot be connected to via a network connection from machines other than the one CVS is running on. In our case, we don’t need to share the repository either, so we’ll create it in our /home/wrox directory.

  1. Create a directory cvsroot in the wrox home directory. The complete path should be /home/wrox/cvsroot.

  2. Now, to simplify future operations, add an environment variable called CVSROOT. Capitalization matters, as always. This variable will point to the CVS directory created in Step 1.
  3. Create the variable by editing /home/wrox/.bash_profile and adding the following lines of code:
    CVSROOT=/home/wrox/cvsroot export CVSROOT

To make the changes take effect, type the following:

.bash_profile on the command line.

This environment variable is very significant. In the absence of command line arguments specifying the CVS repository location to use, CVS will pull this value from the environment. Now, we are ready to tell CVS to create a new repository. In act of creating a repository CVS creates administrative files necessary to use the directory as a repository. Installation is simple; type the following at the command line.

[wrox@linuxserver wrox]$ cvs init

Now, cd to /home/wrox/cvsroot and type ls CVSROOT. You should see something very similar to the following output.

[wrox@linuxserver cvsroot]$ ls CVSROOT/
checkoutlist config.v Emptydir modules.v taginfo checkoutlist.v cvswrappers history notify taginfo.v commitinfo cvswrappers.v loginfo notify.v val-tags commitinfo.v editinfo loginfo.v rcsinfo verifymsg config editinfo.v modules rcsinfo.v verifymag.v
[wrox@linuxserver cvsroot]$

The next step is to create a directory within our $CVSROOT to contain files for the examples, rather than keep them in root. Make a directory called /home/wrox/cvsroot/client for testing CVS client commands. We are now ready to begin versioning source code.

CVS Commands

CVS commands share a generally common command structure that might seem a bit odd at first. The first part is always “cvs”, directly following by any arguments to cvs itself, followed by the cvs command you wish to execute and finally any arguments to that cvs command. Thus, we can say cvs commands have the following form:

cvs –"cvs arguments here" "cvs command name" "arguments to the command"

We will see this form for the login command below. Note, the CVS log in step can be skipped for the local repository we’ve created. Keep remote logins in mind, but skip to the section entitled “Checking Out Code” below. CVS commands, by default, use recursive behavior. So, if you are at the top of a large development tree and issue a cvs commit or update command, the entire tree from that point down will be affected. This may sometimes be what you want, but it can be a headache when committing large numbers of files at once unintentionally. You can cancel a CVS commit operation by exiting the text editor without saving changes, and choosing the abort option.

Connecting to a CVS Server

The first step to accessing a CVS repository is determining the CVS connection information and logging in. You must have valid user credentials on the remote server. Many open source projects have set up an “anoncvs” user with read-only access to the repository; if you are part of a development team the sysadmin will probably give you this information. The CVS client must know what login protocol will be used, what user credentials to send, where the server is, and the path to the repository on that server. The general form for a cvs login statement, is:


The “pserver” method, short for password server, is the most common authentication method, and the only one we will focus on in this chapter. For the rest of the chapter, assume the following cvs login information:

cvs -d :pserver:wrox@localhost:/usr/local/cvsroot login

What are the pieces of this login command? As stated above, the first part is always cvs, followed by any arguments to the cvs command. The –d argument tells cvs what CVS ROOT to use for the operations. If this argument is missing, the cvs command searches your environment for a CVSROOT value. This is then used without the need to include in on the command line. Often this is the most convenient way to use cvs, so it is wise to add an export CVSROOT=your cvs root here in whatever login scripts are appropriate to your shell. In the standard bash shell, this is accomplished by adding the information above to the .bash_profile file in the user home directory. Note that we have not specified the password, cvs will ask for the user password. Typing the login string shown above should produce output similar to the following:

[wrox@linuxserver wrox]$ cvs -d :pserver:wrox@localhost:/usr/local/cvsroot login Logging in to :pserver:wrox@localhost:2401/usr/local/cvsroot
CVS password:
[wrox@linuxserver wrox]$

Typing ls –a at the command prompt, you will see that there is now a file called .cvspass found in your home directory. This file stores your password, but not in plain text. This, combined with the $CVSROOT environment setting, will allow us to work without specifying login information or the location of the server until such time as we log out. Should you need to connect to different cvs servers, use the cvs logout command and log in with new information.

Checking Out Code

Now logged in, we are ready to begin using the repository. The command to get a local copy of the files in the repository for the first time is cvs co (check out). The command form is

cvs co {checkout options} {modules to check out}

There are many arguments to the checkout command, but we need only concern ourselves with the basics here.

There are no files in the repository yet so of course checkout won’t produce any source files. The cvs co command will tell CVS to initialize our working directory with the CVS specific files it needs, similary to cvs init creating the repository. Therefore, cvs co is the first command we issue. From /home/wrox, type the following:

[wrox@linuxserver wrox]$ cvs co client cvs checkout: Updating client
? client/
[wrox@linuxserver wrox]$

The cvs client will report the status of the operations you ask it to perform, unless you suppress these messages. After issuing the checkout command, you will notice that there is a directory called CVS created underneath client. These files should not be edited by hand—they are administrative files created by cvs to store data about the status of files. A CVS directory will be created in every directory you add to the repository.

Adding Files

Now, we will add a Java file into the repository. Create the following simple Java file in the /home/wrox/client/ directory.

package com.wrox.projosdev.client;
 * A class to test the cvs client commands
public class Test {
 public static void main(String[] args) {
 System.out.println("CVS is the best!");
 Test test = new Test();
 test.doSomething( InnerPeace());
 public void doSomething(Object object) {
 System.out.println("You gave me " + object.getClass().getName() );
 private static void printArgs(String[] args) {
 for (int i = 0; i < args.length; ++i) {
 System.out.println("Arg[" + i + "] " + args[i]);
 private class InnerClash {
 private class InnerPeace {

Assume for now that this file performs some business purpose. On any project the structure of the source directory should of course match the Java package structure, which is skipped here for expediency. Now its time to add to the repository. The command to add files to the repository is

cvs add {options} {files}

The relevant options to cvs add will be discussed below. For now, type

cvs add 

You should see output similar to the following:

[wrox@linuxserver client]$ cvs add cvs add: scheduling file '' for addition cvs add: use 'cvs commit' to add this file permanently
[wrox@linuxserver client]$

Adding to CVS is a two-stage process then, with part one being add and part two being commit. The semantics of cvs commit will be discussed in detail below in the “Updating Modules” section. For now, trust that typing

command cvs commit 

is the next step. Upon hitting enter, you are greeted with a change log message screen.

Type your change message here: Version 1.0 of Test program CVS: ----------------------------------------------------------------------
CVS: Enter Log. Lines beginning with 'CVS:' are removed automatically
CVS: Added Files:
CVS: CVS: ----------------------------------------------------------------------

For the default “wrox” user created, the editor is the venerable vi. This is configurable within the shell environment. Save the log message, and you should see output indicating the file is in the repository.

RCS file: /home/wrox/cvsroot/client/,v done Checking in;
/home/wrox/cvsroot/client/,v <-- initial version: 1.1
[wrox@linuxserver client]$

Now the file is in the repository! Take note of the line stating “initial version: 1.1”. This will be important later. This is our first version of a file; we can use the version number to get back to this state at any time. CVS uses the notion of “sticky tags” and “sticky options” to store information about files, such as a tag the file is part of. The sticky tag most likely to be used on a Java project is the binary keyword substitution. Remember that the core functionality of CVS is versioning text files, so we must tell it to treat binary files specially. Suppose there is a certain version of the Tomcat servlet engine needed to compile a project, distributed as servlet.jar. On most projects, jar files are kept in their own directory. This is a good time to introduce another concept: adding directories. To add a directory in CVS, create the local directory, lib in this case, and issue the cvs add command. You should see:

[wrox@linuxserver client]$ cvs add lib Directory /home/wrox/cvsroot/client/lib added to the repository

Some versions of CVS have difficulty removing directories; make sure you want to add a directory before committing it to the repository. Now that the lib directory is part of the repository, we can copy the servlet.jar file into it. To add the file to the repository, issue the “cvs add” command again but this time with extra arguments.

cvs add –kb servlet.jar

With these options, CVS will treat the file as binary and not mangle it with text-processing routines. At this point we introduce another cvs command we can use to see what information CVS might be keeping about files. To check on the status of the JAR file we just added, we will use the cvs status command:

cvs status {files}

The output should look like this:

 [wrox@linuxserver lib]$ cvs status servlet.jar
File: servlet.jar Status: Up-to-date
 Working version: 1.1 Fri Dec 26 02:31:32 2002
 Repository version: 1.1 /home/wrox/cvsroot/client/lib/servlet.jar,v
 Sticky Tag: (none)
 Sticky Date: (none)
 Sticky Options: -kb
 [wrox@linuxserver lib]$

The cvs status command delivers several useful pieces of information. Most prominent are the Status and, in this case, the Sticky Options data. Status can tell you the status of your local file related to the file in the repository. Status messages you will most commonly see are listed below. In the Sticky Options section, you can see that CVS recognized the -kb options. Following are several useful status messages that CVS produces:

Updating Modules

Updating modules in CVS is a two way street. A developer must store changes in the repository and also be sure to stay in sync with the changes being made by other developers. This process may take place several times per week, or several times per day depending on the pace of your project. When working on a development team, many people are likely to be changing the same files, sometimes at the same time. It is a CVS best practice to always perform a CVS Sanity Check (shown below) before checking in new code. The CVS update command format, used to bring working directories in sync with the repository.

cvs update {options} {files}

The most common options to the cvs update command are as follows:

The next step in updating modules we have already seen. The cvs commit command is used to send changes to the server. The “CVS Sanity Check” was mentioned earlier. This is a sequence of steps that should be taken in this order for the smoothest operation of a project using CVS for version control. Performing a CVS Sanity Check:

  1. Update project files. The importance of this cannot be overstated. You must bring your source tree in sync with the repository.

  2. Resolve any merge conflicts that arise. It is best to confer with other developers if possible.
  3. Make sure the project still compiles. This is quick and painless with Ant.
  4. If your project uses an automated Unit Testing strategy, make sure the unit test suite(s) for the project still completes successfully. Unit testing with JUnit is a great way to ensure the project is always in a running state.
  5. You have now taken the extra time to be a responsible developer. Your colleagues and project manager will thank you. Go ahead and commit your changes.

Retrieving the changes made by other developers is accomplished using the cvs update command. Storing changes in the repository is accomplished using the cvs commit command.

Locking Files

Several other version control systems use a “check out and lock” model, whereby the client tool sets file permissions on your local copy to keep you from changing the file unless you ask the server to give you exclusive write access to that file. CVS does not attempt to do this sort of policing, code is checked out writeable on the local filesystem. Anyone who sees fit can edit any file at any time and, if they have write access to the repository, commit their changes.

Creating a Branch in CVS

Often it may be useful to mark the current state of the repository with a tag, such that the development or testing team can always guarantee the ability to role a project back to the state it is in at a specific moment in time. Branching and tags can be one of the most confusing topics within CVS; this need not be the case. Suppose the two files we have so far added to the local repository have undergone several iterations, and are ready to go through user testing of some sort. According to the project milestones, this milestone shall be called Release Candidate 1, or RC1 for short. The development team needs to keep working on the source tree, adding bug fixes and new features slated for Version 2 of the system. By creating a branch, development can continue on the new version, but Release Candidate 1 can be recreated at any time as well. What is the difference between a branch and a version? A version is a specific version of a file. The first version of the file was 1.1, the second 1.2, and so on. Since a branch captures a certain state of the source tree, consider a branch “a collection of certain versions.” The cvs command for creating a branch is

cvs tag –b {branch name}

Let's create a branch in the local repository called RC1. Remember that CVS commands use a recursive behavior by default, so change directory to /home/wrox/client, the top of our working directory. Type cvs tag –b RC1. You should see the following output:

[wrox@linuxserver client]$ cvs tag -b RC1
cvs tag: Tagging .
T cvs tag: Tagging lib T lib/servlet.jar
[wrox@linuxserver client]$

The tag has now been created in the repository. This means the state of the system at the time the RC1 tag was created has been saved. However, type

cvs status"

and you will see there are no sticky tags on the file. This means the working directory is still pointing to the main development trunk. Depending on your situation, there are two ways to proceed. If, for the foreseeable future, you need to keep working on Release Candidate 1, the cvs update command can be used to bring the RC1 tag into the working directory. Often, developers will find themselves in the position of being simultaneously in two roles. On one hand they must make bug fixes to a stable branch, RC1 in our case, and also begin implementing new features in the main development trunk. In this scenario, it is easiest to keep two working directories, one of which has the RC1 stick tag. Consider the following list of commands to accomplish this task, using the same local example:

  1. Create a directory to contain the RC1 branch.

  2. Issue a cvs checkout command, with the -r RC1 option to get all files from the RC1 branch.
  3. Use cvs status to obtain a visual confirmation that the checkout command worked:
    File: Status: Up-to-date
     Working version: 1.5 Sat Feb 27 01:05:58 2004
     Repository version: 1.5 /home/wrox/cvsroot/client/
     Sticky Tag: RC1 (branch: 1.5.2)
     Sticky Date: (none)
     Sticky Options: (none)
    [wrox@linuxserver client]$

Notice that now that we have specified the RC1 branch, all files in the source tree will have the RC1 sticky tag. Changes can be made to these files without affecting the main development trunk, and changes in the main trunk will not be reflected in the RC1 branch.

Removing Files from CVS

Now that we have done many examples with, the time has come to remove it from the repository, as it is no longer needed on the project and is just in the way.

 [wrox@linuxserver client]$ cvs remove cvs remove: file `' still in working directory cvs remove: 1 file exists; remove it first
[wrox@linuxserver client]$

So, working files must be deleted from the file system first. Delete the file with the command rm. Now issue the cvs remove command again.

/home/wrox/cvsroot/client/,v <-- new version: delete; previous version: 1.2
[wrox@linuxserver client]$

Now the file has been removed from the repository.