Next Previous Contents

5. Basic CVS Commands

5.1 Preliminaries: Text Editor

CVS requires you to enter an ordinary text editor when committing any changes (and possibly at other times) to document (log) the changes.

Do Not skip this step. Whether you are using CVS for personal/private purposes with a single root or using CVS to run a multiuser project, the log documentation you enter when you commit changes is an essential part of your being able to figure out what you did in that checkin relative to the previous version three years from now when you can barely remember working on the project at all let alone what you did what day to what previous version, or your colleagues being able to figure out just what you changed (and why) when they struggle to fix something you might have broken with the change.

In either case, if you do a bad job of it, you will have people muttering imprecations under their breath. In some cases, you will be those people. In others, you might get hate mail or job demerits or even fired. Even a single line of documentation is better than none, but when checking in major changes, you'd better include major documentation of the changes. You Have Been Warned!

In order to "require" it, CVS kicks you into an editor when you try a commit before actually committing. If you don't specify an editor you actually know, it is likely to be the system default text editor, usually "vi". If you don't know what vi is, you will likely run screaming from the room when you find that you also don't know how to do simple things like type in the documentation and save and exit. it is therefore useful to be able to tell CVS to use your choice of text editor, which might be (on a typical linux system) emacs, jove, joe, pico or even vi. My own favorite is jove so in my example let's set (in bash)

EDITOR=jove
export EDITOR

in your .bashrc, or the equivalent with setenv in .cshrc. CVS uses the editor specified in the EDITOR environment variable, so you're all set.

5.2 Tutorial Introduction to Commands

There are only a handful of CVS commands that you need to know to do nearly anything that really needs to be done to control a project, whether private or shared. All the commands share a common general syntax of

cvs [-d cvs_root_path] command args...

The commands you "need to know" include:

I will present only the very simplest examples of each, although many of them take lots of options to e.g. specify particular revisions or modify their behavior in useful ways. These options you can learn about in the man page or cvs.ps manual once you've got the general idea and a functioning CVS root and working directory or two to play with.

In the following, we will create and add a file. We will commit the new file after adding it (a separate step). We will then remove the working copy (not the archive copy) and put it back it using update. We will edit the file, and commit the changes. We will tag the whole project with a revision identifier. Finally, we'll remove the file from both the working project and the archive (which doesn't actually remove it from the archive, it stores it in "the Attic" from which it can be retrieved, as it will be needed to be able to checkout the tagged revision should we ever wish to do so).

rgb@ganesh|B:1540>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER commit .
cvs commit: Examining .
cvs commit: Examining CRUFT
cvs commit: Examining doc
RCS file: /home/einstein/prof/rgb/Src/DIEHARDER/dieharder/newdoc,v
done
Checking in newdoc;
/home/einstein/prof/rgb/Src/DIEHARDER/dieharder/newdoc,v  <--  newdoc
initial revision: 1.1
done
In the middle there, jove was invoked and I entered the following:
Adding newdoc, just to prove that we can add new files.
CVS: ----------------------------------------------------------------------
CVS: Enter Log.  Lines beginning with `CVS:' are removed automatically
CVS:
CVS: Committing in .
CVS:
CVS: Added Files:
CVS:    newdoc
CVS: ----------------------------------------------------------------------
All lines beginning with CVS are automatically removed, just like it says. They are used so CVS can tell you what is being done in the commit.

Now let's work through the commands but doing a tutorial exercise for each commonly used command from the list above.

Note Well that most of the commands take as final arguments either "file1 [file2] [file2]" (a list of files that might be expanded by the shell with wildcards and so forth) or "." (signifying that the action is to be applied to all files in the current directory and its recursively descending CVS-controlled subdirectories). I include examples of both forms fairly freely in the tutorial below.

Add

Begin by changing to your CVS controlled working directory for your favorite project. Use your favorite editor to add a file called "newdoc" containing the line:

 This is a new project document!
Then:
rgb@ganesh|B:1537>cd dieharder/
rgb@ganesh|B:1538>jove newdoc
rgb@ganesh|B:1539>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER add
newdoc 
cvs add: scheduling file `newdoc' for addition
cvs add: use 'cvs commit' to add this file permanently

The files has been added to CVS, but the archive file doesn't actually exist yet. To create it, we have to "commit" the changes.

Commit

Committing changes is very simple:

rgb@ganesh|B:1008>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER commit newdoc 
RCS file: /home/einstein/prof/rgb/Src/DIEHARDER/dieharder/newdoc,v
done
Checking in newdoc;
/home/einstein/prof/rgb/Src/DIEHARDER/dieharder/newdoc,v  <--  newdoc
initial revision: 1.1
done
Note that the output messages. They tell me where the actual archive files (/home/einstein/prof/rgb/Src/DIEHARDER/dieharder/newdoc,v) is and that newdoc's changes have been commited. It tells you the revision number that has just been given to the archived file. This number will automagically increment in all future commits.

Update

Next, let's edit newdoc. With the editor of your choice, make some changes to it (it doesn't matter what they are, but they have to actually change the file's contents). Add "hip hip hooray to CVS" or something.

Suddenly, a terrible accident happens! You delete newdoc's contents and then save! (Please do this.)

You are desperate to get newdoc back as it is SO important to you. How can you do it? Enter:

rgb@ganesh|B:1009>rm newdoc
rgb@ganesh|B:1010>ls -al newdoc
/bin/ls: newdoc: No such file or directory
rgb@ganesh|B:1011>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER update newdoc 
cvs update: warning: newdoc was lost
U newdoc
rgb@ganesh|B:1012>ls -al newdoc
-rw-r--r--  1 rgb smmsp 14 Feb  8 17:00 newdoc

It is back! But is all of it back? Do e.g. cat newdoc. No, all its contents are not back. Only the contents that were last "commit"ted are back. We lost a bit of work (the "hip hip hooray" part) but not the entire file. In general, the work you are at dynamic risk for is the work done since your last commit command, plus a small static risk that the CVS root's server filesystem itself will go down without making it to backup at least one time. This is a good reason to commit changes fairly often -- once per hour of active work is not too often, as you'll find that rarely you have to redo that hour's worth of work because you didn't and something crashes. An hour is survivable with only a bit of pain; a day's worth of work is not.

Let's try again. Edit the file again, this time adding the "hip hip hooray" and immediately doing a commit:

rgb@ganesh|B:1014>jove newdoc 
rgb@ganesh|B:1015>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER commit newdoc 
Checking in newdoc;
/home/einstein/prof/rgb/Src/DIEHARDER/dieharder/newdoc,v  <--  newdoc
new revision: 1.2; previous revision: 1.1
done
Note the revision number bump. NOW remove the file, update the file to restore it, and cat the file, and you'll note that this time "hip hip hooray" is still there.

Now imagine that you really DIDN'T want "hip hip hooray" there at all, only it was really a huge number of edits scattered all over the very long file. You just want to go back to the pre-hhr image. Enter:

rgb@ganesh|B:1016>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER update -r 1.1 newdoc
U newdoc
and cat newdoc. Look! The hip hip hooray part is gone! Not only that, but:
rgb@ganesh|B:1016>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER update -r 1.2 newdoc
U newdoc
and it's back again! You can juggle numbered revisions that were previously committed and start making your major changes all over again, perhaps with better luck the second time.

One important option to update is a second "-d" flag (as an option to the update command itself) as in the following example:

rgb@ganesh|B:1060>cd dieharder/
rgb@ganesh|B:1061>rm -rf doc
rgb@ganesh|B:1062>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER update .
cvs update: Updating .
cvs update: Updating CRUFT
Note that after removing the doc directory, update will NOT automatically retrieve it from the repository. Nor will update insert a new directory that (for example) one of your co-workers added to a complex project. To force update to retrieve missing subdirectories from the current directory (.) add the -d flag as follows:
rgb@ganesh|B:1063>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER update -d .
cvs update: Updating .
cvs update: Updating CRUFT
cvs update: Updating doc
U doc/SP800-22b.pdf
U doc/cern_stats.pdf
U doc/diehard_tests.txt
U doc/fnal_prob.pdf
U doc/goodness_of_fit_nr.pdf
U doc/tests.txt

In general, when running an update before working on a shared project, you should always use the -d flag to be sure to also update any missing directories your co-workers might have added as well as the already existing working copies of the files.

Diff

I probably shouldn't put this in a mini-howto as it is pretty advanced, but if you are a Unix expert, then note that you can actually focus on just what the differences are without having to update to 1.1 and 1.2 separately (moving the result into distinct names like newdoc_1.2 and newdoc_1.1) and running diff to get a diff. CVS had a built in revision-based diff:

rgb@ganesh|B:1021>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER diff -r 1.1 -r 1.2 newdoc 
Index: newdoc
===================================================================
RCS file: /home/einstein/prof/rgb/Src/DIEHARDER/dieharder/newdoc,v
retrieving revision 1.1
retrieving revision 1.2
diff -r1.1 -r1.2
1a2
> hip hip hooray
How cool is that! Obviously you can pipe this into a file and apply it with patch and everything. CVS actually saves all changes made to the original file as a set of reversed diffs, so it can back off each successive set of changes to the original while still maintaining a faithful copy of the most recent. It has to step back through those changes to recreate particular revisions for an update or checkout.

Tag

Actually, we're SO happy with this revision that we want to somehow save whole damn dieharder project, including newdoc and all the rest. In your pre-CVS days, you would have done this by creating a copy of the source directory named something pithy like "dieharder-eleventeen-really-works". After a while, you end up with eight copies of the project none of which you dare delete and several of which don't, in fact, work.

Mind you, at this point the REAL dieharder sources have lots of sources that are up at revision 1.30 and more as the project has been around for a while now. newdoc, on the other hand is revision 1.2, and others are 1.16 and everything in between. Sure, I can figure out what all of the current revision numbers are and write them into a CVS controlled file and check it in, but this is a pain and to actually reconstruct a working snapshot would take a long time and be error prone.

Enter tags. CVS manages all that recording and reconstruction automagically when I tag one or more files in the repository. Let's tag all the dieharder sources to be physics_independent_study since I'm about to hand them off to an independent study student who will be working with them (and trying out this cvs mini-howto to get started):

rgb@ganesh|B:1026>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER tag physics_independent_study .
cvs tag: Tagging .
T Btest.c
T COPYING
T CVS_Mini_HOWTO
T Makefile
T NOTES
...
Hmmm, strangely familiar, except now there is a "T" instead of an "N" or a "U". For "T"ag, I expect. We'll see how this works in just a second. Note that (for better or worse) CVS won't permit the "." character in tags, so you cannot use "version\ 1.0.0" as a tag. version_1_0_0 is permitted. Go figure. You want to watch "."'s in directory names as well, as this also holds for arguments in the original import command.

Remove

Strangely enough, I don't really want the newdoc file in dieharder. I added it only to help illustrate how to do all this stuff. So let's remove it:

rgb@ganesh|B:1048>rm newdoc
rgb@ganesh|B:1049>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER remove newdoc
cvs remove: scheduling `newdoc' for removal
cvs remove: use 'cvs commit' to remove this file permanently
rgb@ganesh|B:1050>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER commit .
cvs commit: Examining .
cvs commit: Examining CRUFT
cvs commit: Examining doc
Removing newdoc;
/home/einstein/prof/rgb/Src/DIEHARDER/dieharder/newdoc,v  <--  newdoc
new revision: delete; previous revision: 1.2
done
Note that I first have to remove the file itself, then run the cvs remove command (at which point the file isn't yet removed) and then commit the change to actually remove the file from CVS.

But is it really removed? No, of course not, silly beanie. If it were really and irretrievably removed, I couldn't check out the tagged revision that included it, which I can (as I will momentarily demonstrate). It is put "in the Attic" (an actual directory in the dieharder archive called "Attic"). CVS can find it there, as can you, if you ever need it again but in the meantime it need not cruft up your project.

Let's get it back, just for the heck of it. In fact, let's get back the whole tagged revision it was in. I'm going to go up a directory, remove the entire dieharder working directory (why not -- I can always get it right back), and then checkout the tagged revision by name:

rgb@ganesh|B:1051>cd ..
rgb@ganesh|B:1052>rm -rf dieharder/
rgb@ganesh|B:1053>cvs -d /home/einstein/prof/rgb/Src/DIEHARDER checkout -r physics_independent_study dieharder
cvs checkout: Updating dieharder
U dieharder/Btest.c
U dieharder/COPYING
U dieharder/CVS_Mini_HOWTO
U dieharder/Makefile
...
U dieharder/newdoc
...

Note that newdoc is back! It is deleted, removed and purged, but CVS is smart enough to get it out of the Attic if it is asked to retrieve a tagged project that includes some particular revision of it. To make it go away again, repeat the above without the -r option -- a straight checkout will generally retrieve the current (most recent) revision of every file, the "working" revision.


Next Previous Contents