CVS for Version Control
David Wallace Croft
Unless stated otherwise, quoted material was taken from the CVS
"Version Management with CVS for CVS 1.10"
by Per Cederqvist et al.
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.
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
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
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
setenv CVSROOT /usr/local/cvsroot
If you are using sh or bash instead, append the following
to your ".profile" file:
An alternative is to modify your .bash_profile file:
You may wish to set your editor environment variable as well
since the editor specified will be called by CVS to enter
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 :pserver:email@example.com:/usr/local/cvs 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.
cvs import -kb -I ! -m "initial import" core CroftSoft r1
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
$ 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
cvs import -I ! -m "initial import" core CroftSoft r1
A better way to prevent your multimedia files from being
corrupted is to modify the
as described in Appendix B
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
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:
cvs add util
To add a Java class file "com.croftsoft.core.lang.StringLib"
into the CVS tree, you would do the following:
cvs add StringLib.java
cvs add *.java
cvs add -kb *.jpg
You must first delete the file in your working copy.
cvs remove ObjectLib.java
cvs commit ObjectLib.java
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 ImageLib.java
If you delete a file accidentally and wish to recover it
from the version control repository, you can do the following:
cvs update DateFormatLib.java
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.
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.
You can examine the revision numbers and comments associated with
revisions using the following command:
cvs log WindowLib.java
You can observe changes between revisions using one of the following:
cvs diff ThreadLib.java
cvs diff -r 1.1 -r 1.2 ThreadLib.java
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
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
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
Command: cvs watch on [-lR] files ...
To set a "watch" on a directory and all sub-directories,
use the following command, replacing "core" with the directory name:
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
cvs watch on -R core
Use the following command to make a source code file temporarily read-write
for modification by yourself:
cvs edit Testable.java
When done, use the following to commit your change to the repository
and to make your local copy read-only again:
cvs commit Testable.java
If you wish to release the file to make it read-only again without committing,
use the following command:
cvs unedit Testable.java
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
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.
Note that this is recursive; all subdirectories will be included.
$ cvs tag rel-1-0 .
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
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
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
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
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
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.