CVS Primer

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 http://purl.org/nxg/note/cvsprimer

$Revision: 1.10 $

In the examples below, I presume that we're trying to use a CVS server at cvs.example.org, and that our username on that server is jim. Also, we use the CVS server through ssh; the instructions for other access methods such as pserver are basically the same, except that the repository specification is slightly different.

1 Concepts

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.

1.1 Repository

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 set.

1.2 Check out/update/export

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.

1.3 Commit

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.

1.4 Previous versions, revision numbers, tagging and branching

Each file has a revision number, such as 1.17 for example, though you might occasionally see things like 1.17.3.4. 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 include

Revision The revision number of the file, such as 1.17
Date The date the file was committed, such as 2002/06/27 12:11:18
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 tag like 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.

2 Usage

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.

2.1 Checkout/update

Check out the initial version of the Java repository with the following incantation

% export CVS_RSH=ssh
% cvs -d :ext:jim@cvs.example.org:/cvs checkout java

This repository specification is for a CVS server which we access through ssh (the ext part 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 called java. See the documentation for the repository spec appropriate for other cases.

Notes:

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 a whole, <command-options> affect a single one of the subcommands. The difference is important, since there are some options like -r or -d which 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 tag, say version-1-2, then do so with

% cvs checkout -r version-1-2 java

(note that -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 using the -D option.

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.

The -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.

2.2 Adding and removing files

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.

2.3 Status and diffs

Use

% cvs status <filename>

to check the status of a checked-out file, or omit the <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>

(the 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 -r and -D options -- see the appropriate section of the manual for details.

2.4 Commit

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 <filespec>, then all uncommitted files in the current directory and below are committed.

If the commit message is short, it can be specified on the command line:

% cvs commit -m 'A few words about the commit' <filespec>

2.5 Notification

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 following

% cd /tmp
% cvs -d XXX checkout CVSROOT/loginfo
% vi CVSROOT/loginfo
% cvs commit -m 'message...' CVSROOT/loginfo
% cvs release -d CVSROOT

(where 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 expect.

3 Fuller documentation

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: C-h i to get into Info, then mcvs<return> to get the correct node. Alternatively info cvs at the command line gets you the same stuff.

Norman
2003 November 25