Concurrent Version System
From the cvs(1) man page:CVS is a front end to the rcs(1) revision control system which extends the notion of revision control from a collection of files in a single directory to a hierarchical collection of directories consisting of revision controlled files. (Actually, it doesn't use rcs any more.)
There are a number of things about the CVS system that are not obvious from
the man pages. One of the first that you come across is that if a file is
imported as /foo/bar/hello.c, when you use "cvs checkout" to get a local
copy, it will be copied as foo/bar/hello.c relative to the current
directory. So most of the time, if the directory structure of the
repository is reasonable, you can checkout from your home directory, or a
directory just below it. (But see
For example, to check out a local copy of SparcVersion1_2 of the elfSparc
file decoder into the ~/loaders/elfSparc directory and make it:
Note when using the import command:
The third last argument (repository) is a directory (relative
to $CVSROOT), not a
module name (as I expected). You don't need a module to check in files; in
fact, you don't need a module to check them out either; it just makes it
easier (see adding a module).
Also,
by using the -kb option, you can specify all files as binary, but not
selected files. The workaround if importing a mixture of binary and non binary
files is to use the cvs admin -kkv <file(s)> command to
change a file (or a wildcarded group of files) to non binary. However, you
still need to manually edit the CVS/Entries file to remove the
"-kb" strings. (This may make more sense when you look inside the Entries file,
and realise that the cvs admin command works only on the repository).
In case you don't like the import command, there is an alternative.
The only special files to CVS are the files in the CVS directory in your
working directory, the repository itself, and a handful of special files
in $CVSROOT/CVSROOT. The latter are created during installation,
and should only be changed by adding to the modules file (see Adding a module for details).
The CVS directory in your working directory is set up by the checkout
command; there should never be need to change these directly.
This leaves the respository. The files of the repository are in $CVSROOT;
for example, if module elfDecoder specifies a path of loaders/elf,
then the repository is the directory $CVSROOT/loaders/elf. The
files in the repository are all rcs(1) format files, usually with the
name of the original file and ",v" appended.
So the easiest way to set up a group of files (if you don't like import
is as follows; the
broad strategy is to copy the source files directly into the repository
and use the ci command (RCS Check In) to convert the files into
RCS format.
1) Copy the files to the repository, using cp -r or tar
or any means.
2) Change to the respository. Remove any binary files, such as *.o and
executables, etc.
For each directory:
4) Edit the file and prepend "ci -tdescript " to the start of
each line. In vi, use ":1,$s/^/ci -tdescript /". Note no space
after the "-t".
5) Create a file descript with the description (1 line) of
what the project is about.
6) Make files executable (chmod +x files), and execute
it.
7) Remove the files descript and files.
Repeat for subdirectories.
To test that it has worked, create a module for the project, and check it
out into a test directory (say testdir):
More likely, you want to use the original directory to make changes to the
project. If so, go to the parent, and check out the files into the original
directory. It takes courage to do this, but remember that CVS won't let you
overwrite a file that has changed, so it's quite safe. If a file has changed
between when you created the repository and are ready to check it out, then
CVS will issue a message like "move away dir/file; it is in the way".
Rename the file, and check out the files again. Use diff (ordinary diff;
cvs does not know about your renamed file). Fix the file to your satisfaction,
(probably my moving the renamed file back to its original name), and commit.
Remake the project (all the sources now have a later time) to make sure all
is well.
Once this is working, you can start deleting files that are no longer
needed in the next version, and updating files that have just changed
contents. For each file that is deleted, remove it from the repository
as well (don't worry: it will still be available (via the attic) for
when you checkout version one):
For each new file added to the project, use the add command:
When all is well, test, then tag the current files with the next version's
tag.
The first case is the easiest, and is covered in the documentation. Just create a branch tag with cvs tag -b branchname, then check out a new copy of that source with cvs co -r branchname.
In the second case, you have a set of files that has been extensively modified, and occasionally updated, but never checked in, and you don't want the changes to be reflected in the main branch. In this case, you begin as above by creating a branch tag:
Or you can just do this:
I had some problems checking in the whole project in one hit; I had to check
in each subdirectory separately, and then check in the root directory with
cvs commit -l -r branchname
(to only look at the local files, not subdirectories).
Files can be added and removed as usual (e.g. cvs add filename),
without having to specify the -r branchname.
This appears to be a rare case where the admin -o (outdate) is useful:
Note that when a deletion fails as above because of tags, you have to do it
all again to really do the delete. The revision numbers are not
inclusive. When you outdate a revision, you obviously can't request that
revision back again, so this is not recommended for non generated files.
If you are using pserver and your password changes on the machine running the cvs client, you can find yourself unable to use cvs commands.
This is because your password is cached in a file, $CVS_PASSFILE (if CVS_PASSFILE is not set, $HOME/.cvspass).
You could remove that file, or the entry relating to the cvs server you are using;
that will prompt you to enter the "cvs login" command.
Or of course just enter the "cvs login" command.
The .cvspass file makes it easy for you so you don't have to enter the password
all the time, but if your password changes, you need to send your new password.
Last modified 01/Jul/2005: Use -d and -A with "update -j HEAD"
Creating the initial modules file
Even the excellent online help for CVS is somewhat unclear about how you
should create the initial modules file. Here is a step by step guide:
cd ~ ; Some suitable writable directory
cvs checkout CVSROOT ; This should create ~/CVSROOT
cd CVSROOT
vi modules
{Add lines similar to these:
modules CVSROOT modules
CVSROOT CVSROOT
elfDecoder loaders/elf
}
cvs add modules ; So cvs knows about this file
cvs commit ; To actually add the file
You will be prompted for a log file entry.Adding a module
Adding a module to CVS is similar. Pick a convenient directory, e.g. your
home directory. Checkout the pre-exising module named "modules":
cd ~
cvs checkout modules
cd modules
You can then add an entry to the modules file, in the format
"modulename path". For example:
elfDecoder loaders/elfV0
Then commit the changes:
cvs commit
When this is done, the modules directory can simply be removed,
or left there for future additions.
Other sources of information
Current Projects
The number of projects booked into CVS is continally expanding. We attempt
to keep up to date the file bintrans/README.CVS. Consult this file for
(hopefully) the latest modules and tag names.
cd ~/loaders
cvs checkout -r SparcVersion1_2 elfDecoder
cd elfDecoder
make
To check it out into directory ~/loaders/foo inistead, use
cvs checkout -r SparcVersion1_2 -d foo elfDecoder
For our purposes, these are the commonly used commands (in addition to
checkout above):
cvs tag MyNewTag elfDecoder
Adding a New Project to CVS
One of the most baffling things to do is to start an existing project with
CVS. The import command appears to be designed for this purpose,
but the writers of CVS seem to have it in mind that this command would
be used only for Vendor code, and hence all files are placed on a
branch. This means that all software revisions will have four digits,
e.g. the first revision will be 1.1.1.1. However, it is easy to "move
the files to the main branch" using the cvs update -A command.
This will have no immediate effect, but any subsequent changes will
give sensible revision numbers (e.g. 1.2) to the changed files.
3) Make a list of files with ls > files. Manually remove
the names of subdirectories, and the file files.
cvs checkout -d testdir ModuleName
cd testdir
You can omit the -d testdir if ModuleName is a suitable
directory name.
Done!
When File Names Change
Sometimes you want to book in a project with a bit of history, where some
of the names change (save verbose.c to verbose.cc), from one tag to the
next. To accomplish this, first enter the files for the first version (tag)
as outlined above, and use a command like
cvs tag Version1
to make a tag for the first version. Test.
cvs remove file1.c file2.c
(You can't remove *.c, because the files have to be deleted first).
cvs add *.cc
But note: using wildcards with add may not always have the same effect as
adding the files separately (if there are error messages).When CVS repository paths change
I received a nasty shock today when I tried to check out an old version
of a module. This was the error message:
cvs checkout: existing repository /net/olympic/u6/olympic/CVSROOT/loaders/Loader
does not match /net/olympic/u6/olympic/CVSROOT/loaders/elf
cvs checkout: ignoring module loaders/elf
No files were checked out. It seems that $CVSROOT/loaders/elf
was called $CVSROOT/loaders/Loader at one stage. I just renamed
the directory, did the checkout, and restored the directory name, and all
was fine. Just a minor gotcha, but it can save a lot of grief if you
know what to expect.
Reversing a commit
Sometimes you just want to reverse a commit (ci). Rather than attempt to delete
the original version, it's best to get a copy of the appropriate old version of
the file (use cvs status and cvs diff -rprev-rev
to do this). Don't use
cvs update -r prev-rev filename
as this will make the revision sticky, so you can't book it in again.
Instead, use
cvs update -p -r prev-rev filename > filename.
This gets a non
sticky old version to stdout, and you redirect that over the top of the file.
You can then check that all is well with cvs diff, then just commit
the file again. Be sure to add decent comments, so you can figure out what the
hell happened in the future with cvs log.
Using Branches
Branches are useful things, but it's not always obvious how to use them.
There seems to be two main uses for them: when starting a "side project", and
also when a "side project" has developed, but hasn't been booked in yet.
cvs tag -b branchname
Now you want to check in the modified files, to the branch. Use the -r option, as follows:
cvs commit -r branchname
Now the modified files will have a sticky tag. They can be modified and
checked in with the usual cvs commit command, and they won't affect
files on the main branch. Improvements made on the main branch can be
incorporated by merging with the main branch, without sticking to the main
branch, but first you need to create a tag on the main branch:
cd path to files on main branch
cvs tag maintag
cd path to files on side branch
cvs update -j maintag
This will find the changes between the latest main branch and the point where
the side branch originated, and applies those changes to the current branch
files. This is ideal where you have a subproject with much of the code common
with the main branch, and yet significant local modifications.
cvs update -j HEAD -d -A
There will be many conflicts, mostly just due to revision numbers. The -d
is to get new directories created on the main trunk, and the -A is to
remove the stickyness (assumes you are ready to book back into the main
trunk). Delaying the -A until later means a second set of merges.Generated Files
If you have a large file that is generated from another file, and that
generated file has something like "#line" statements in it (guaranteeing that
any small change to the source file will cause thousands of changes to the
generated file), then the repository file (,v file) can get very large very
quickly. This is annoying, since you probably don't want the revision history
for the generated file; you just want it in the repository for completeness
(it may be difficult to generate on some platforms, for example).
$ cvs admin -o1.2::1.28 decoder.cc
RCS file: /u0/luna/CVSROOT/bintrans/driver/386Dir/decoder.cc,v
deleting revision 1.27
deleting revision 1.26
cvs admin: cannot remove revision 1.25 because it has tags
cvs admin: cannot modify RCS file for `decoder.cc'
$ cvs admin -o1.2::1.24 decoder.cc
RCS file: /u0/luna/CVSROOT/bintrans/driver/386Dir/decoder.cc,v
deleting revision 1.23
deleting revision 1.22
deleting revision 1.21
deleting revision 1.20
deleting revision 1.19
cvs admin: cannot remove revision 1.18 because it has tags
cvs admin: cannot modify RCS file for `decoder.cc'
You can get an idea of what version numbers can be deleted by simply examining
the first few pages of the ,v file (e.g.
less $CVSROOT/bintrans/driver/386Dir/decoder.cc,v). It is in
relatively plain ascii format.If you change your password
There are several ways that a cvs client can talk to a cvs server. One of these is the "pserver" (passwords handled by the server) method.
You are using this if your CVS/Root entry starts with ":pserver".
