Using CVS

CVS is the "Concurrent Versions System," a project management tool which is available for free from www.cvshome.org. There are several similar project management tools available commercially, but we use this one because (1) it is good, (2) its basic features are very similar to the commercial products available, (3) it is easy to use, and (4) it is free. CVS is also designed to absorb pre-existing RCS (Revision Control System) projects, so for those users of RCS (available from The Free Software Foundation), migration to CVS should be relatively simple.

There are three main advantages to using CVS over RCS. First, CVS allows for multi-directory projects, which RCS does not. Second, CVS allows for concurrent management of projects - in other words, multiple programmers can work on the same project, and CVS will assist in merging each programmer's work into the revisions of the others. Third, CVS allows for remote access to repositories, so you can make your projects available to potential users and developers across the Internet.

There are two main features of CVS that we will use. One is its provision for maintaining a complete history of each document (source code or documentation page) in your projects. At any time you can recover earlier versions of files almost as easily as you can work with the current version. The second feature is the facility that CVS provides for documenting changes you make to your files as you develop a project. There are many more features that CVS can provide, but we will not take advantage of them in this course. If you want to learn more, consult the man page for cvs and the Cederqvist, the main documentation for the CVS project. If you find the manual name puzzling, or have other questions about CVS, you may also wish to consult the official CVS FAQ.

Setting Up the Repository

CVS requires a central repository directory for all projects it manages on a local machine. You can have multiple projects in a single repository. Once you create the repository, CVS' internal recordkeeping is stored within that repository, but you can work on your projects anywhere.

To create the main CVS repository, first choose a directory on your local machine. A typical choice is /usr/local/cvsroot. Assuming that this is the directory you have chosen, you can initialize your repository with the command:

   $ cvs -d /usr/local/cvsroot init
You should set the environment variable CVSROOT to point to the CVS repository. In C shell environments, this is achieved with the command:

   $ setenv CVSROOT /usr/local/cvsroot
while in BASH or KornShell environments, you would use:

   $ export CVSROOT=/usr/local/cvsroot
You may wish to place these environment variable settings in a global location such as /etc/profile, or in your local user initialization scripts, so that they are always accessible.

Defining the Module

Because CVS can handle multiple projects within a single repository, each project must be defined by a module to CVS' internal recordkeeping. Modules are usually defined as a project existant under a single directory tree structure. The first thing to do in creating your project is to create a master directory, appropriately-named for that project. You will place all files related to that project in that directory or its subdirectories, so that CVS can easily import the entire directory tree. In general, you should create the directories you know you'll need first; for instance, if you are creating a project called project1, you would create the following directories:

   $ mkdir project1
   $ mkdir project1/man
After that, you would use the following commands to register these directories inside the CVS repositories:
   $ cd project1
   $ cvs import -m "Created directory structure" project1 yourname start
The format of the import command is as follows: the -m directive provides a message that will be stored in CVS' logs, specified by the string after the directive. project1 is the directory under the CVSROOT in which the project will be stored. yourname should be replaced by your name - this is the vendor tag which specifies who has made the revisions; while the concept of vendors won't be used in this course, CVS requires that you specify one in the initial revision. Finally, start tells CVS to initialize the project within the repository.

The next step is to add the project to the CVS modules database, so that CVS can identify the project with a simple project name. To do this, you must first get a working copy of the CVS modules file:

   $ cvs checkout CVSROOT/modules
   $ cd CVSROOT
Open the modules file in a text editor and add the following at the bottom:
   project1   project1
The first project1 specifies the module name, which you will use to refer to the project. The second refers to the directory under the CVSROOT in which the file will be stored.

After you have saved the file, you must commit the changes you have made:

   $ cvs commit -m "Added the project1 module." modules
And finally, release the modules file.
   $ cd ..
   $ cvs release -d CVSROOT
The checkout command seen above is used to check a module out from the repository - in other words, to obtain a copy for editing, review, or compilation. The CVSROOT/modules module seen above is automatically provided by CVS; in the case of your project, you will check out the project1 module you created above using the command:
   $ cvs checkout project1
The syntax of the release command is discussed in more detail in the CVS documentation.

CVS Headers

Whenever you start a new programming project, create an appropriately-named directory for that project. You will place all files related to that project in that directory or its subdirectories, so that CVS can easily import the entire directory tree. Each new source file, whether it is a .cc or a header file, starts like this:
   // $Id$

   /* SUMMARY
    *
    *
    * REVISION HISTORY
    *  $Log$
    */
The complete form of this file header is described in the Coding Guidelines for this course. Our attention here is on the $Id$ and $Log$ lines, which must be written with exactly the capitalization and punctuation shown here in order to work properly with CVS. These are examples of CVS keywords (sometimes called CVS macros) which will be modified by CVS as you work on your project. There is a list of all CVS keywords you can look at if you wish, but you only need the two lines listed here for this course. Note that these keywords appear on comment lines. If they were placed anywhere else the compiler would recognize that they are not valid C or C++ code, and would produce syntax errors.

The other files that you will create for the projects in this course are the Makefile that goes in each project directory, and the man pages that you use to document your projects. Comment lines in Makefiles start with a hash symbol in column 1:

   #  $Id$

   #  comments ...
   #
   #  $Log$
   #
Comments in man pages start with dot-backslash-quote:
   .\"  $Id$
   .\"
   .\"  comments ...
   .\"
   .\"  $Log$
   .\"
   .TH ...
Once you are comfortable working with these two CVS keywords, you might want to put some of the other keywords inside character strings so your program can use them as data for one reason or another (such as including the program's version number in the "About" message of a Help window). However, that topic goes beyond the scope of this document. We will look at $Id$ and $Log$ again after going over some basic CVS concepts.

CVS Revisions

You write the first version of your program just as you would if you weren't using CVS. Use a text editor to create source files, compile them, test the code, and iterate through this process until you have a working program. If the progam is simple, the development process might be complete at this stage. For other programs, the first working program might be just the first step of a multi-step assignment. In either case, you are now ready to put the source files under CVS management. When you are adding a new source file to the repository, this is a two-step process; afterwards you will be able to check in your modifications with a single CVS command.

After you have created a new file or directory inside the project tree, enter the following command from the directory in which the new file or directory resides:

   $ cvs add filename
This will inform CVS to watch the file and prepare to add it to the repository. You can add as many files as you like before you commit the changes; if you change your mind about adding the file, you can issue the command
   $ cvs remove filename
before you commit, and the file will be removed from the list of watched files. After adding the file, you commit it:
   $ cvs commit filename
This copies the file into the CVS repository for the project. What this really means is that the file, along with some additional information, will be copied into the CVS subdirectory and given a new name, which will consist of the original file name with ",v" at the end. You can list the contents of the CVS subdirectory to verify this. Do a long listing, and you will also notice that the database file (also called the "history" file) is made read-only. From now on you can make changes to the file only by telling CVS that you want to do so. You could change permissions on the history file and change its contents manually, but don't. Really don't.

When you commit the changes, you must provide a message that will be stored in the history file, providing you with a continuous log of modifications made to the file. This message should be a short summary of just what it is that makes this version of the file different from the previous version. If you fixed a bug, tell what the bug was. If you added a new feature, tell what the feature does. If you changed comments, tell what kind of changes you made. The log message does not have to be long, but it must be concrete and meaningful, not vague.

You have two options as to how to provide this message. If you use the cvs commit filename command above, CVS will attempt to open an editor - specified by the environment variable EDITOR. $EDITOR usually points to vi, but you can change the environment variable to select another editor. Alternatively, you can specify a short message directly at the command line using the -m directive seen above:

   $ cvs commit -m "Made some minor spelling corrections." filename
A file need only be added to the repository once; once that is done, you report changes by using the commit command. So from now on, after editing the file, be sure to issue cvs commit filename to copy those changes into the CVS repository and make them publically available. This gives you the benefit of having a stable copy of a source file and a working copy - while the CVS repository may only contain code that you want to make public, you can carry around the working copy and make as many changes as you wish, finally only committing them when you are certain that the code works well.

As another option, you can issue the cvs commit directive in a directory without the filename to commit all of the files in that directory that have been edited. However, as a rule, it's best to commit the files one at a time so that you can keep track of which files have been changed.

Versioning and Tags

At this point you have entered version 1.1 of each of your source files into the CVS repository. Each time you add a file, it will be added into the repository as version 1.1; each time you modify a version and commit it to the repository, the version number will be incremented. You should not be alarmed that CVS keeps different version numbers for each file in a project - treat the revision numbers as internal recordkeeping numbers for CVS, and not as any meaningful 'release version' for your own use.

If for some reason you want to bring all of your files up to a certain revision number, you can use the -r option - for instance

   $ cvs commit -r 3.0
will bring all of the files in the repository - including those you haven't edited - up to version 3.0. Correspondingly, you can use -r to retrieve a specific version of a file. If the file foobar.cc is currently at version 3.0, and you decide that you want to re-examine revision 1.2, the command
   $ cvs checkout -r 1.2 foobar.cc
will check out the required version.

However, since revision numbers are usually not used for this purpose, and are difficult to keep aligned, CVS includes support for tagging. You can use tags to mark a milestone release of a project without bringing all of the version numbers in synch. To apply a tag, enter the base directory of your checked-out project, and use the tag directive to apply a tag name to the current version of your project. For instance, if you wanted to label the current release of your project rel-1-0, you would issue the command:

   $ cvs tag rel-1-0
This tag will be applied to all of the current versions of the source files; after later revisions, if you wanted to return and check out rel-1-0 of your project, you could use the -r directive:
   $ cvs checkout -r rel-1-0 project1

Keywords Again

Once a file is under CVS management, you will see that checking it out gives you a file in which the keywords have been expanded. The $Id$ keyword will tell you the name of the file, its version number, who last modified it, and when. We put this keyword on the first line of the file so this basic summary information is always the first thing anyone sees when they first look at the file. The $Log$ keyword will be expanded to give the complete modification history of the file, that is, comment lines from all the log messages you entered when checking in revised copies of the file along with the revision dates and numbers. Be sure these log messages provide useful information for programmers who might need to work on a file after you.

Multiple Developers

The primary difference between RCS and CVS is in the way the two systems handle multi-developer projects. RCS uses file locking or reserved checkouts; in this system the first developer to want to edit a file checks out (or reserves) a copy of the source file. Once that copy is reserved, no other developer can get a copy for editing, although they can check out a copy for examination or compilation. Only a reserved checkout can be checked back into the repository with modifications.

CVS provides support for reserved checkouts using the -l flag. To lock a file or branch, issue the command:

   $ cvs admin -l
Conversely, you can unlock the file or branch with the -u flag:
   $ cvs admin -u
However, file locking is considered a primitive method of revision control, and CVS was chiefly designed to provide a more sophisticated paradigm for multi-developer programming. The Cederqvist contains CVS' rationale for supporting newer methods in the section Choosing Between Reserved or Unreserved Checkouts; this material is too detailed to cover here. In CVS' model, multiple developers can check out, edit, and commit their changes to a source file. The first developer to check the file back in knows nothing about the subsequent developers; however the later developers are required to integrate their changes on check-in. The CVS documentation covers this topic in far greater detail.

CVS Documentation

The chief documentation on CVS is found in the man page and at the main CVS webpage, at www.cvshome.org. There is a pocket reference book for CVS commands:
   Purdy, Gregor N CVS Pocket Reference.
   O'Reilly & Associates, 2000.  ISBN 0-596-00003-0.
There is also a book by the same publisher on RCS and a similar package that comes with Solaris, called SCCS:
   Bolinger, D. & Bronson, T. Applying RCS and SCCS.
   O'Reilly & Associates, 1995.  ISBN 1-56592-117-9.
Additionally, the same publisher has a book on make:
   Oram, A. & Talbott, S. Managing Projects with Make.
   O'Reilly & Associates, 1991. ISBN 0-937175-90-0.
You can contact this publisher at www.oreilly.com.
Christopher Vickery and Eric Shamow
Queens College of CUNY

Home Page