Open Source Development With CVS 

A Day With CVS 

This section describes some basic CVS operations, then follows with a sample session covering typical CVS usage. As the guided tour progresses, we'll also start to look at how CVS works internally.

Each part of the tour may make use of knowledge introduced in previous parts. Therefore, if this is your first time, I recommend that you simply start at the beginning and take the tour sequentially, without skipping over anything.

Invoking CVS 

CVS is one program, but it can perform many different actions: updating, committing, branching, diffing, and so on. When you invoke CVS, you must specify which action you want to perform. Thus, the format of a CVS invocation is:

floss$ cvs command

For example, you can use

floss$ cvs update
floss$ cvs diff
floss$ cvs commit

and so on. (Don't bother to try running any of those particular commands yet, though; they won't do anything until you're in a working copy, which we'll get to shortly.)

Both CVS and the command can take options. Options that affect the behavior of CVS, independently of the command being run, are called global options; command-specific options are just called command options. Global options always go to the left of the command; command options, to its right. So in

floss$ cvs -Q update -p

-Q is a global option, and -p is a command option. (If you're curious, -Q means "quietly"-that is, suppress all diagnostic output, and print error messages only if the command absolutely cannot be completed for some reason; -p means to send the results of update to standard output instead of to files.)

Finding Out What You (And Others) Did - update And diff 

Previously, I've talked about updating as a way of bringing changes down from the repository into your working copy - that is, as a way of getting other people's changes. However, update is really a bit more complex; it compares the overall state of the working copy with the state of the project in the repository. Even if nothing in the repository has changed since checkout, something in the working copy may have, and update will show that, too:

floss$ cvs update
cvs update: Updating .
M hello.c
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
cvs update: Updating b-subdir

The M next to hello.c means the file has been modified since it was last checked out, and the modifications have not yet been committed to the repository.

Sometimes, merely knowing which files you've edited is all you need. However, if you want a more detailed look at the changes, you can get a full report in diff format. The diff command compares the possibly modified files in the working copy to their counterparts in the repository and displays any differences:

floss$ cvs diff
cvs diff: Diffing .
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -r1.1.1.1 hello.c
6a7
>   printf ("Goodbye, world!\n");
cvs diff: Diffing a-subdir
cvs diff: Diffing a-subdir/subsubdir
That's  helpful, if a bit obscure, but there's still a lot of cruft in
the  output. For starters, you can ignore most of the first few lines.
They  just  name  the  repository file and give the number of the last
checked-in  revision.  These  are  useful  pieces of information under
other  circumstances  (we'll look more closely at them later), but you
don't need them when you're just trying to get a sense of what changes
have been made in the working copy.
A  more  serious  impediment  to  reading  the  diff  is  that  CVS is
announcing its entry as it goes into each directory during the update.
This  can be useful during long updates on large projects, as it gives
you  a  sense  of how much longer the command will take, but right now
it's just getting in the way of reading the diff. Let's tell CVS to be
quiet about where it's working, with the -Q global option:
floss$ cvs -Q diff
Index: hello.c
===================================================================
RCS file: /usr/local/cvs/myproj/hello.c,v
retrieving revision 1.1.1.1
diff -r1.1.1.1 hello.c
6a7
>   printf ("Goodbye, world!\n");

CVS And Implied Arguments 

In each of the CVS commands so far, you may have noticed that no files were specified on the command line. We ran

floss$ cvs diff

instead of

floss$ cvs diff hello.c

and

floss$ cvs update

instead of

floss$ cvs update hello.c

The principle at work here is that if you don't name any files, CVS acts on all files for which the command could possibly be appropriate. This even includes files in subdirectories beneath the current directory; CVS automatically descends from the current directory through every subdirectory in the tree. For example, if you modified b-subdir/random.c and a-subdir/subsubdir/fish.c, running update may result in this:

floss$ cvs update
cvs update: Updating .
M hello.c
cvs update: Updating a-subdir
cvs update: Updating a-subdir/subsubdir
M a-subdir/subsubdir/fish.c
cvs update: Updating b-subdir
M b-subdir/random.c
floss$

or better yet:

floss$ cvs -q update
M hello.c
M a-subdir/subsubdir/fish.c
M b-subdir/random.c
floss$

Note: The -q flag is a less emphatic version of -Q. Had we used -Q, the command would have printed out nothing at all, because the modification notices are considered nonessential informational messages. Using the lowercase -q is less strict; it suppresses the messages we probably don't want, while allowing certain, more useful messages to pass through.

You can also name specific files for the update:

floss$ cvs update hello.c b-subdir/random.c
M hello.c
M b-subdir/random.c
floss$

and CVS will only examine those files, ignoring all others.

In truth, it's more common to run update without restricting it to certain files. In most situations, you'll want to update the entire directory tree at once. Remember, the updates we're doing here only show that some files have been locally modified, because nothing has changed yet in the repository. When other people are working on the project with you, there's always the chance that running update will pull some new changes down from the repository and incorporate them into your local files. In that case, you may find it slightly more useful to name which files you want updated.

The same principle can be applied to other CVS commands. For example, with diff, you can choose to view the changes one file at a time

floss$ cvs diff -c b-subdir/random.c