This is a fast introduction to CVS, aimed more at those who want to use a CVS repository, than those who want to set up and manage one of their own. It concentrates on the concepts required, and on the basic commands needed from day-to-day.
There are pointers to other more comprehensive documentation
The persistent URL for this page is
$Revision: 1.10 $
In the examples below, I presume that we're trying to use a CVS
cvs.example.org, and that our username on
that server is
jim. Also, we use the CVS server
ssh; the instructions for other access
methods such as
pserver are basically the same,
except that the repository specification is slightly different.
CVS is intended to be used in a cooperative environment, supporting a group of folk working reasonably closely on a project. It does not aim to impose a methodology on such groups. As a consequence, it aims to be generic, and permissive. For example, by default everyone can read and write everything, there is no file-locking to protect against collisions (though there is support for dealing with the consequences), and there is no built-in support for code review and supervision and the like.
CVS therefore provides a good deal of rope with which groups can hang themselves, and it is possibly surprising that this happens very rarely in fact. The flexibility can support just about any practice we decide on, and if we stay awake and in touch we don't trip over the infrastructure. CVS can almost act as a canary -- if there's commit/merge confusion, it suggests that folk aren't cooperating as closely as they ought to.
All code is held in a `repository', which may be on the local
machine, but is more typically held remotely, in our case on
cvs.example.org. This is the master code
You work on code copies which you check out from the repository. You can try any experiments on this copy without any danger of affecting others. If the experiment doesn't work, you can abandon it, delete the wrong files (or a hopelessly messed-up tree), and check out or update the repository copy again.
At appropriate times, you can update your copy of the code from the most recent version in the repository. This synchronises your checked-out copy by merging in any changes which have been committed by others (see below). This can happen even if you have already modified some of these files (but presumably you were expecting that, otherwise perhaps you should have a chat with whomever it was who made the change), in which case the repository changes are interleaved with yours. Again, this sounds potentially dangerous, but is safe in fact: if there is any way that the merge cannot be done reliably, you are warned of the conflict, and you cannot commit the file until you have resolved the conflict by eye.
The other way code can be extracted from the repository is when you export it. This is a one-way operation in the sense that exported code cannot be returned to the repository. It is typically done as one of the stages in packaging software for distribution.
When you are satisfied with changes you have made, and they pass any regression tests you can run, you can commit the changes back to the repository. This simply installs your changes in the repository, where they can be picked up by others when they wish.
When you commit a file, you have to supply a commit message explaining the changes you've made. This isn't a big deal -- a sentence or two suffices. You can commit (and add the same message) to a group of files at once, or you might need to commit groups of files separately, if different messaged would be appropriate for them.
Each file has a revision number, such as
1.17 for example, though you might occasionally see
18.104.22.168. There is no significance to
the numbers (they do not correspond to software versions or
release numbers), other than that later revisions have higher
numbers. You can see the revision numbers in the file, along
with other similar information, if you include one of CVS's
keywords in the file, in the format
$Keyword: value $. The most important ones
|Revision||The revision number of the file, such as
|Date||The date the file was committed, such as
|Id||Various bits of information, including Revision and Date|
You can group a set of files -- a snapshot of the repository --
by giving them a tag. This allows you to
subsequently extract them from the repository as a unit. For
example, you might tag the files in a particular release with a
version-1-2, so that you could check out
or export the files in that release at any subsequent time.
Tagging allows CVS to support the idea of a branch, which is a deliberate fork in the code. The main line which branches branch off of is called the trunk. Branching is potentially extremely confusing, as it can get seriously out of hand. However, it is extremely valuable when done with appropriate discipline: cases where it is useful include
There are some pointers to CVS use patterns which cover this in the collection of documentation below.
The material on a branch can be merged back on to the trunk, or vice versa, though obviously the further the two branches have diverged, the more complicated this is.
Because of the potential for confusion, you should be careful to discuss working practices in a fair amount of detail before starting to use branches.
The following are examples of basic usage, and are offered as recipes which you can use without having to understand any more than the concepts outlined above.
For any more elaborate usage, including access to some of the features mentioned above, you should understand the documentation, and probably discuss your plans with your colleagues.
Check out the initial version of the Java repository with the following incantation
% export CVS_RSH=ssh % cvs -d :ext:firstname.lastname@example.org:/cvs checkout java
This repository specification is for a CVS server which we
indicates this), getting to a machine called
cvs.example.org, on which our username is
jim. The repository root happens to be located at
/cvs on that machine, and we're checking out a tree
java. See the documentation for the repository spec appropriate for
javafrom the repository, along with all its subdirectories. If you only wanted a particular part of the tree for some reason, you could replace
CVS_RSHis required if you are to use
sshrather than the default (and insecure)
-doption indicates the location of the repository and the means of contacting it (
ssh). You can also indicate or default this with an environment variable,
CVSROOT, but since the checked out directory will probably be checked out pretty long-term, and since you might have occasion to use other CVS repositories from time to time, I feel that just creates the possibility for confusion; but of course others will disagree.
Do note that the position of options is significant. CVS commands are of the form
% cvs <global-options> <command> <command-options> <command-arguments>
<global-options> modify the behaviour of CVS as
<command-options> affect a single one
of the subcommands. The difference is important, since there
are some options like
are common command options, but which are also global options
with a different meaning. Option
-d in this
context is a global option.
If you want to extract a version which was given a specific
version-1-2, then do so
% cvs checkout -r version-1-2 java
-r is an option to
checkout and thus goes after the command). You can
also check out the version which was current on a given date
Update your checked-out version to the current repository version with
% cvs update -d
CVS will list all the files it examines, prefixed by single letters indicating the file's status. The one you have to watch for is `C', which indicates that CVS found a conflict when it tried to merge the file. In this case, you need to search through the indicated file and look for the conflict which will be marked with lines of chevrons like this:
... >>>>>> one version of a block of code ====== the conflicting version of the same lines <<<<<<
You must resolve these conflicts by hand before CVS will allow you to commit the file again.
-d option creates in your checked-out copy any
directories which have been added to the repository. This is,
oddly enough, not the default, and if you don't include it then
you can get yourself briefly confused.
If you have a file in your checked-out copy which you wish to add to the repository, then create the file in the working directory and give the command
% cvs add <filename>
If you have a large collection of code to add to the repository, such as a project which you are only now putting into CVS, or third-party sources you are tracking, you need to use the import command, which you should read about in the documentation.
If you have a file you wish to remove, delete it from your checked-out copy, and
% cvs remove <filename>
Neither action will have any effect until you commit the
file. That is, you can
add a file and later
remove it without any change to the
repository. If you wish to rename a file,
you do so by removing it from the repository under its old
name, and adding it with the new one.
% cvs status <filename>
to check the status of a checked-out file, or omit
<filename> to see the status of all the
files. Give option
-v after the
status command for more detail, including the
tags which are attached to this file. This status output
is rather verbose: I use the (bash) alias
alias cvsstatus="cvs status 2>&1|grep '\(^File\|Examining\)'|grep -v 'Up-to-date'"
to cut the output down a bit (anyone got a csh equivalent?).
The `status' output shows a `Sticky Tag': this shows the branch which the file is on, or `(none)' if it is on the trunk.
A handy trick is to use
% cvs -n update
This runs the update command but, because of the
-n, doesn't actually do anything. By giving
just the feedback which would be reported from an `update'
operation, it therefore
provides a very compact status report. It lists all the
files which are not fully up-to-date, prefixed by one of the
characters documented in the CVS manual. This turns out to be
particularly useful for those files (marked with a `?')
which are in the local directory but unknown to CVS, so if
you have created a file but forgotten to add it to CVS, this
will show up here.
You can see the log of changes to a particular file with
% cvs log <filename>
history command does something
different and less immediately useful).
if you want to see how a file has changed, use diff:
% cvs diff <filename>
(you can add various options to modify the format of the
output, such as
-c to get context diffs, for
example). That shows the diff between the repository version of
the file, and the file in your checkout directory; that's
generally all you ever need to do. If you need to examine the
diffs against some other version in the repository, such as the
version which was given a particular tag, or the repository as
it stood on a certain date, you can do so using suitable
arguments to the
-D options --
see the appropriate section of the manual for details.
To commit a file or files:
% cvs commit <filespec>
The file or files indicated are committed to the the repository
after you are prompted for a message to describe the commit.
This doesn't have to be an epic, but overly terse or generic
comments are very unhelpful to your colleagues (don't even
think of just `fixed bug'!). Aim to give some
indication of roughly what the change was, and why it was
necessary. If you do not give a
then all uncommitted files in the current directory and below
If the commit message is short, it can be specified on the command line:
% cvs commit -m 'A few words about the commit' <filespec>
CVS can be set up to do arbitrarily clever things to files on
committal. All we do is notify interested parties about the
commit with a suitable line in the repository's
loginfo file. If you wish to be notified about
commits to a particular part of the tree, then do the
% cd /tmp % cvs -d XXX checkout CVSROOT/loginfo % vi CVSROOT/loginfo % cvs commit -m 'message...' CVSROOT/loginfo % cvs release -d CVSROOT
XXX is any repository spec which you needed in
the checkout instructions above). The format of the loginfo
file should be fairly clear, but do be careful, and try not to
be imaginative -- if you stuff this up, you'll likely become
unpopular. It's probably best to do this just before you plan
to commit something, to make sure that the commit works as you
Most of us shouldn't need to to any more with CVS than the few simple commands described in this primer. However, there's a good deal of documentation for CVS available for those who want or need to do more.
Complete documetation is available within Emacs Info:
i to get into Info, then
get the correct node. Alternatively
info cvs at the
command line gets you the same stuff.