CVS

General CVS Set Up

Make sure that this is in ~/.bashrc.

export CVSROOT=/home/chris/cvsroot

For remote CVS use, put these in the .bashrc:

export CVSROOT=:ext:xed@$XED/home/xed/cvsroot/
export CVS_RSH=ssh
Note
There is some question about whether there should be a colon separating the host and the path on that host. It looks like it’s optional. If you’re having trouble getting it to work, try adding it (or taking it away if it’s there).

Source your .bashrc if echo $CVSROOT produces nothing.

$ . ~/.bashrc

Go to the repository.

$ cd $CVSROOT

Set it up while in the repository dir. Only do this once for a new place for CVS to store stuff.

$ cvs init

Go back to the directory you were interested in archiving.

$ cd -

Optional Check out the config CVS file to have a look This can go anywhere and you can make modifications to the config files that are stored by CVS. Do an update to make changes take effect.

$ cvs co CVSROOT
$ cvs release CVSROOT/
$ rm -r ./CVSROOT*

Start Using CVS On A Project - Directory Of All Good Files

Go to where the source code is. Here imagine that the source is being kept in a series of directories that get copied every week (a crude RCS).

$ cd ~/software/GeoGad.050423

Set up project (i.e. "module"). This must be done from the project’s directory. All files in the current directory will recursively be tracked. The module name is "GeoGad". Here "XedTech" is a "vendor tag". Here "START" is a "initial tag".

$ cvs import GeoGad XedTech START

After creating a module, the files involved have to be checked out before they can be properly used by CVS. This makes a new directory called GeoGad so you might have to go up a directory if you want to to have the CVS version repopulate the GeoGad directory you’re already in.

$ cd ..
$ cvs checkout GeoGad

Intend To Use CVS Before Writing Anything - No Files Exist

To set up a module in CVS before any source code is written, use any empty directory.

$ mkdir nothing
$ cd nothing
$ cvs import nova XedTech START
$ cd ..
$ rmdir nothing

Go to where you want to work on this project.

$ cd ~/funnewprojects

Get a CVS ready directory (called "nova") to work in.

$ cvs co nova

Change to this directory, create files and then "cvs add" them.

$ cd nova

Have A Nasty Untracked Mess - Some Files Good Some Bad

Imagine a project that’s a total mess, some files in the directory are backups done by hand and therefore shouldn’t be part of a CVS import. Start by moving the directory so that you can use this name for a new and improved CVS version:

$ mv /projects/jcsg/typical /projects/jcsg/typical.orig

Make a new empty version of the directory:

mkdir /projects/jcsg/typical

Go to that directory:

$ cd /projects/jcsg/typical

Copy in the files that need to be there; leave the bogus stuff.

$ cp ../typical.orig/wheelinvention.pl .

Make a CVS project out of it:

$ cvs import typical JCSG START

Make the source directory a working CVS directory:

$ cd ..
$ cvs co typical

Optionally delete the original directory:

$ rm -r ./typical.orig

Adding Files

To add files to the CVS module, set it up for addition and when a commit is done the file will be tracked too.

$ vi Makefile
$ cvs add Makefile
$ cvs commit

Deleting files from CVS’s control is similar to add.

$ rm planningnotes.txt
$ cvs delete planningnotes.txt
$ cvs commit

Before doing anything that might have been touched by other developers (or you from somewhere else), it’s important to make sure you are working with the latest versions. The -d will create any new files that don’t exist on your current version.

$ cvs update -d

Binary Files

If you need to add binary files (images, Word docs, etc.):

$ cvs add -kb logo.png

What if you forgot to mark it as binary? Try this:

$ cvs admin -kb icon.png
$ cvs update -A icon.png
$ cvs commit -m "make it binary" icon.png

Using CVS From A Remote System

Put this in the .bashrc (assumes $XED is defined as the IP#)

export XCVSROOT="xed@$XED:/home/xed/cvsroot"

Check out like this:

[catalyst][~/xfile/python]$ cvs -d $XCVSROOT co bildam

Renaming A File

CVS can be a bit unimpressive when trying to reorganize your files in a project. Simply renaming doesn’t have a concept in CVS. I need to do this quite a bit, so I wrote a script:

#!/bin/bash
if [ -z "$1" ]
then
    echo "Missing filename argument."
    exit -1
fi
OLD=$1
# Make up your own renaming scheme here or use $2.
NEW=_${OLD##SRC.}.hts
mv ${OLD} ${NEW}
cvs remove ${OLD}
cvs add ${NEW}
cvs ci -m "Renamed ${OLD} to ${NEW}" ${OLD} ${NEW}

If that’s not satisfying enough you can explore more tricksy techniques.

Renaming A CVS Project

If you start a project with a certain name, but later think of a better one, then you need to change the CVS name. There are other ways to do this, but this is pretty safe. Go to the CVSROOT directory where this project lives. Then make an exact copy to the new name:

cp -va jerkcontrol guestrelations

Go back to your working directory and:

cvs co guestrelations

Start using it. If everything is ok, then you can delete the old working directories and the old version in CVSROOT:

rm -r $CVSROOT/jerkcontrol

Note that there may be some references to the old project in the history file of $CVSROOT/CVSROOT.

Removing Stuff

To remove files, use the:

cvs remove file_to_stop_tracking

To remove a directory, make sure it’s empty, including removing all the file using the cvs remove command and then do and then use the -P option (prune) when updating or checking out again.

Note that CVS doesn’t really delete the directories. This is in case it needs to bring them back in some kind of rollback. This can be frustrating if you have a directory that really, really should be deleted. People report that just deleting the empty directory in the actual CVSROOT is ok. But this source seems to make me more cautious about that. I have used the harsh delete from the repository method and it does seem to work.

Timestamps

CVS seems to know when things are out of date and one would reasonably assume that it will work hard to preserve time stamps. Wrong! If you create a complex repository developed over time and then check out another copy of it elsewhere, the copy’s files will all have the date of the checkout. That’s very annoying. I had a project where I was relying on those time stamps. Fortunately, CVS does know when things were updated, but you have to ask it directly. Here’s what you can use:

cvs log ${FILE} | sed -n 0,/^date:/p | tail -n 1 | awk '{print $2}'

Or if you love sed:

cvs log ${FILE} | sed '/^date:/!d;s@^date: \([12][0-9/]*\) .*$@\1@' | sed q

Permissions

CVS does some half-assed stuff with permissions. It doesn’t just track the state of the files as you set them in your checked out version. No, it tends to keep the setting of the file that was originally checked in. This can by very annoying. Apparently the only effective way to change this is to go to the CVS server’s cvsroot and find the project and go to the path/file of interest and change the filename,v file which corresponds to the wrongly permissioned file. This can often require administrative privilege. Another way is to cvs remove the file and cvs add it back with the correct permissions. Of course you lose continuous history on that file. There appears to be no good answer.

CVS Keywords

The following keywords can be embedded in source code files and will get expanded into something more meaningful. The format is

$Keyword:$

Replace "Keyword" with one of these:

  • Author

  • Date

  • Header

  • Id

  • Log

  • Locker

  • Name

  • RCSfile

  • Revision

  • Source

  • State

Personally I use $Date:$ and $Revision:$ which seem the simplest and most useful.

However, I’m starting to get the feeling that the idea of checking out a file that is not exactly the same as the one you checked in makes some people kind of uncomfortable. Mercurial only does keyword expansion through an unrecommended extension and Subversion is compatible with CVS but discourages automatic expansion. Maybe best to not base important functionality on this "feature".

General Usage

If you need to work on a project that is being tracked with CVS the general strategy is to go to the directory where you want your local copy of the project directory to live and do:

cvs co the_project_name

This should create a directory full of the latest version of the project. Next, you should edit the project or otherwise make your contribution. If you are adding files you need to do something like:

cvs add some_new_file

To let the rest of the development group know about your work, you need to check in your changes to the master repository. But before doing that, you should always update your current copy to see if any conflicts have arisen by anyone else concurrently working on it. Do:

cvs up
Note
If someone else creates a bunch of new directories, it is possible that you will not see them with a cvs up. This can be very puzzling and problematic. One solution to this is to back out to the parent directory of the main CVS project directory and do a cvs co the_project_name once again. This will update not just the files but the new updated file structure too. Also try cvs up -d which seems to work too.

If there are conflicts with the update operation, you must resolve them some how.

If that goes cleanly then commit (or "check in") your work:

cvs ci -m "Provides an excuse to illustrate the update message."

Status Codes

When you check out a repository or update one, CVS often prints a single letter status code to indicate what the particular operation was all about. Those can be:

U

updated (with a new version of the file)

P

patched (like U but with patches, not a whole new file)

M

modified (in your workspace, not in the repo)

C

conflict

A

added (but not committed)

R

removed (but not committed)

?

file is private to your workspace and not in repository

Conflict

If two people work on the same file at roughly the same time (or one person just forgets to check in their work properly) you can wind up with a conflict. In most cases where the changes are possible to clearly sort out, CVS will make a new file containing both of the changes. This obviously happens when completely different parts of the file are modified. But if the exact same lines are changed by two different operations and then an attempt to check that in occurs, a conflict status arises.

When this happens, the unmodified version of the file is moved to .#filename.revision. In the normal file in the directory the conflicted region is marked with <<<<<<< and >>>>>>> which denotes what has been added and what has been removed. The two possibilities are separated by a line of dashes. Just edit out the one you don’t want and check it in. If you’re pretty sure you didn’t change anything or you definitely want to defer to changes made elsewhere, then just delete the file and do a cvs up.

Troubleshooting

For reasons that are unclear, users were receiving the message:

cvs add: SomeUserFile added independently by second party

This wasn’t exactly true, but it was possible that typos or other mistakes were made. The file in question was not present in the repository. Rather it was in the cvsroot structure’s "Attic" directory. This normally is for branching. Basically there was no correct copy of this file in CVS. To get rid of what was there preventing the proper version from being added, the Attic file needed to be "merged" back in and then deleted. Something like the following was able to fix the problem:

cvs update -j 1.3 SomeUserFile
rm SomeUserFile
cvs remove SomeUserFile
cvs ci SomeUserFile
cp /home/${USER}/the_good_version/SomeUserFile .
cvs add SomeUserFile
cvs ci SomeUserFile

Don’t forget to use cvs log SomeUserFile and see if the offending file has a Attic in its "RCS file" path.

SUBVERSION

Create A Repository

$ sudo mkdir /aleph1/svn $ sudo chmod 770 /aleph1/svn $ sudo svnadmin create /aleph1/svn $ sudo chgrp -R aleph1 /aleph1/svn $ sudo chmod -R g+rw /aleph1/svn/db /aleph1/svn/locks

Note- I did all of this already so normal users can jump right to the next step.

Creating A SVN Virtual Directory

SVN keeps track of stuff as if it was in a special file system. You can customize the contents of this file system. For example, you might want to start with a sub directory for all your projects:

$ svn mkdir file:///aleph1/svn/xed -m "Software that Chris is managing."
Committed revision 1.
$ svn ls file:///aleph1/svn/
xed/

And then you can start an empty project if you’re about to embark on one with no pre-existing files:

$ svn mkdir file:///aleph1/svn/xed/chnotify -m "Monitor the file system for changes."
$ svn co file:///aleph1/svn/xed/chnotify
$ ls -a chnotify
. .. .svn

Notice that you’ve created a directory in the virtual svn file system and then checked it out to a place in the real file system. Doing it in this way is important because now the directory in the real file system is trackable by Subversion as indicated by the existence of the .svn housekeeping directory.

Importing A Project

If you already have a bunch of files under development and want to begin tracking them all with svn, use the "svn import" command. This will take an existing directory tree and install it into Subversion management:

$ svn import -m "chnotify- Tracks file system changes" \
/home/xed/xfile/project/programming/bash/chnotify/ \
file://localhost/aleph1/svn/xed/chnotify

This does not make the existing directory understand Subversion, however. This just allows subversion to get some files. To have Subversion be able to care for files you can actually work with, you have to check the import back out.

Starting From Scratch and Adding Files

If you’ve started with the intent to use Subversion from before your project has any files, you need to add the files to the repository. Do this by:

$ vi chnotify.py
$ svn add chnotify.py -m "First sketch of program."
A        chnotify.py
$ svn ci
Adding       chnotify.py
Transmitting file data .
Committed revision 3.

The "svn add" tells Subversion that you want this file to be tracked. The "svn ci" commits your addition. Before doing the commit, someone else doing a checkout wouldn’t see this.

Checking Out A Project

$ svn co file:///aleph1/svn/xed/chnotify

Editing And Committing The Files

Change some things. For example:

$ vi libchnotify chnotify

Now that some files have been edited, it’s best to see if there are any conflicts, i.e. did some other user make the same changes while you did?

$ svn update
$ svn ci -m "Just some sample changes."

Using SVN Remotely With SSH

From the remote machine (for example, princeton):

$ svn co svn+ssh://lj/aleph1/svn/chnotify

(lj is a host alias for lajolla, see /etc/hosts)

Finding Log Info

Hopefully you’ve been in the good habit of describing your check ins with a little descriptive statement using "-m". That wouldn’t be very useful if you couldn’t do anything with that. Here’s how to review it: $ svn log chnotify.py