CVS is a way of sharing a set of files between people so they can each work on their own copy without disturbing others, and merge the changes back together without too much pain. It is mainly useful for text files and we use it locally for both source control and when writing papers.
We have a CVS repository, for working on shared documents and code - both within the group and with our collaborators.
This guide is just a quick outline, covering our most basic needs and uses.
We focus here on command-line use of CVS, tips for graphical CVS clients would be gratefully incorporated. You can get a command line client for any Unix-like system, including Mac OS X or Linux, or Windows using a suitable shell environment (e.g. CygWin).
The local conventions we use apply no matter how you access CVS, so please read through this if you intend to use our CVS repository.
It is easiest to get started with CVS working with an existing project, already stored in CVS. Then you can get to grips with adding your own projects later on. If that doesn't suit you, read the initial setup, then skip ahead to adding a new project and then return to the earlier sections.
The rest of the guide is structured as follows:
man cvshas a short piece and
info cvshas the full documentation
Xcode Tools.mpkgfrom the XCode Tools folder. Or, you can download them for free from Apple.
fink install cvs
emerge cvson Gentoo;
apt-get cvson Debian).
Things the machine outputs appear like this. Things that you type are like this. Things you should replace with your own text are like this. Comments and other notes, are set like this.
To use our CVS repository at all you will need a departmental login, and proper permissions for the repository. Contact the member of the group you are working with if you find you are told 'permission denied' when following these instructions.
The CVS root used to live on
tungsten, but that machine has since expired. It is now available across the department over NFS at
/vol/aesop/cvsroot/. We will keep this symlink up-to-date, hiding any behind-the-scenes movements from you. If you have existing checkouts, with different CVS roots, please update them to the current scheme.
cvsuser.doc.ic.ac.uk is the name that our Computing Support Group recommend for this purpose, so it ought to be quite stable. If you prefer, you could replace it with any other machine in DoC that you trust in any of the commands below. Just use a machine you expect to be generally available. You may wish to change where your existing checkout points, if you want to move an old checkout to the new
cvsuser hostname, or if the machine you checked out via is having problems.
ssh, as none of this will work using
rsh, and set
CVSROOT environment variable
[in bash] $ export CVS_RSH='ssh' $ export CVSROOT='cvsuser.doc.ic.ac.uk:/vol/aesop/cvsroot/' [in (t)csh] % setenv CVS_RSH 'ssh' % setenv CVSROOT 'cvsuser.doc.ic.ac.uk:/vol/aesop/cvsroot/'
ssh configuration does not know your DoC login, you can put '
cvsuser. Alternatively, put this in your
Host *.doc.ic.ac.uk User login
Other profile tweaks.
To check out (take a local copy you can play with) a module, set the
CVSROOT environment variable as above, then:
$ cvs checkout name
Where name is either a module or a path (to a directory or file) within the repository.
Modules are just shortcuts to some path in the repository, and are defined in
CVSROOT/modules. You can find out what modules are available with:
$ cvs checkout -c
… and what directories are available to looking in
/vol/aesop/cvsroot (which is a department-wide NFS-mount of the appropriate directory). Important: you can do serious damage by messing about in the repository, don't mess with files and directories in there directly (there's likely a cvs command to list the directories and files in the repository, I just don't know it, offhand; suggestions welcome).
We have the following basic structure in the CVS archive:
cvs checkout ipc and
cvs checkout aesop/src/ipc are effectively the same (the first gets you an ipc directory, the latter places it in the tree under aesop). You can happily move checkouts around, take sub-directories and blow away the rest, it's just your local copy. Once you have checked out a module, you need not set your
CVSROOT variable, it will pick it up from the meta-information it keeps in the
You can now play with the local copy and do whatever you like, without harming the copy in the repository. To see other people's changes run (the
-d means you will also see newly added directories, by default these are omitted):
$ cvs update -d
update will never change the repository, but it could leave inline diffs in your checked out copy if there is a conflict (
C in the first column;
U mean there are changes in the repository;
M means you have local changes you have not yet commited). You can use
cvs -n update to do a dry-run, and find out if there are any conflicts.
When you are happy with your changes and want to add them back to the common pool, you have to have updated recently. So, run:
$ cvs update [resolve any conflicts] $ cvs commit filenames
cvs commit with no arguments will check in all your local changes. You will then be prompted for your comments for the log, if you write enough this will really help later on when trying to find the correct revision before a certain change.
You can also supply the log message on the command line, with
cvs commit -m "Your log message".
To add new files just create them in your local checkout, test them to make sure they work then add them with:
$ cvs add filenames
By default CVS will interpolate strings like
$Id:$ and replace them with some appropriate metadata (e.g.
$Id: cvs.cgi,v 1.3 2005/07/19 10:22:31 abkk97 Exp $). For binary files this is a terrible idea, so make sure you inform CVS that the file is binary (
-k to set an RCS flag,
b for binary) with. Even for non-binary files you need to take a little care to see you don't use any of the CVS keywords.
$ cvs add -kb binary_filenames
To fix this if you have already added the file to the repository, look up
cvs admin -kb in the documentation.
You can tell cvs how to treat files based on their filenames (so *.jpg would default to being treated as a binary) using CVS Wrappers. If your operating system stores filetype information outside the filename (as in MacOS or BeOS, for example) then you should either use a convention or explicitly flag files when you add them.
You can import a new project by standing at the top of the tree you want added to the repository and running the following:
$ cvs import -m "log message" path your_user_name initial_flag
See note regarding binary files.
paper_nameshould be something that makes sense to you, it is likely the topic, or where you sent it.
Concretely, with the examples above:
$ cvs import -m "Initial checkin" aesop/papers/paper_name-user_name user_name start
I usually create a sparse tree with the simplest text files in,
cvs import that, then use
cvs add for anything more complicated like binary files.
You may also want to add a module name for your new project. Do this by checking out
CVSROOT/modules, adding a line like those already there, and commiting your change.
cvs diff -b
cvs -n whatever
cvs -n upto see if you would get conflicts if you did an update now.
This is more about sharing text files with diverse users than anything CVS-specific. Different systems have different line-ending conventions:
For a particular text file you must have consistent line endings. It broadly doesn't matter which you choose, but it is simplest if everything in a project shares the same convention. Let's say the unix line endings for convenience:
[Mac to Unix] % cat foo.txt | tr "\r" "\n" >foo2.txt [DOS to Unix] % cat foo.txt | tr "\r" "" >foo2.txt
Beware, if you run the latter on an old-style Mac file, you'll have no line endings at all and you'll have double if you make the other error. You may prefer to use an editor that sorts these things out for you. Also, never do
cat foo | some_command >foo or you'll only get an empty file.
Whatever your whitespace conventions, you should take care when fixing a file and checking it back in. When you change lots of whitespace then
cvs diff will (rightly) see every line as having changed and might make matters complicated for merges. The civilised thing to do, then, would be to make the whitespace changes in one commit, and any substantial changes in a separate commit. If someone hasn't done this, then
cvs diff -b is your friend, as it will tell you just the non-whitespace changes.
In June 2005, our CVS root had to move from the old location on
tungsten. We now have a stable NFS name of
/vol/aesop/cvsroot/, available department-wide. To update older checkouts you can run this from a bash prompt, standing at the top of your checkout, or in a directory with several aesop checkouts (and no checkouts from elsewhere). This will not behave properly if you have sub-directories with spaces in their names.
for i in `find . | grep "/CVS/Root$"`; do echo $i; mv $i $i.bak; echo \ "cvsuser.doc.ic.ac.uk:/vol/aesop/cvsroot" >$i; done
Which will replace all your …/CVS/Root files with one referencing the new, stable CVS location. Once you are happy with the way it is working you may want to remove the backed-up files with:
for i in `find . | grep "CVS/Root\.bak$"`; do echo $i; rm $i; done
You can set the editor that this will use by setting the
CVSEDITOR environment variable, otherwise it will use
$EDITOR or the system default. Put whichever lines fit your needs [minus the
% prompt character] wherever you set env variables (for instance
[in bash] $ export CVSEDITOR='vim' # it might default to vi, otherwise $ export CVSEDITOR='emacs' # for your normal emacs $ export CVSEDITOR='emacs -nw' # to compel no-window mode and save yourself time [in (t)csh] % setenv CVSEDITOR 'emacs -nw'
You may also want to set CVSROOT, as described above, to
"cvsuser.doc.ic.ac.uk:/vol/aesop/cvsroot". Alternatively, you can make your checkouts with a line like:
CVSROOT="cvsuser.doc.ic.ac.uk:/vol/aesop/cvsroot" cvs checkout blah
… and then within the checkout you don't need to set it, it looks it up from the CVS directories in the checkout. I have an alias for this (in
alias aesopcvs="CVSROOT='cvsuser.doc.ic.ac.uk:/vol/aesop/cvsroot' cvs"
… and make the checkout with
aesopcvs checkout blah. This makes it much easier for me to work with other repositories elsewhere.
Further tips and contributions to this page would be appreciated. Please send them to Ashok.