Even though this article details the implementation of a CVS Repository under Solaris 10, the techniques described can easily be applied to any other *nix - just grab CVS compiled for your system, or compile it yourself.
As always, these notes are terse, and assume that you've arrived here knowing what CVS is and why you want to use it. I want to use it to store configuration files (i.e. /etc) from each of my *nix hosts - that way I can track the changes that I make to my configuration files, and roll back to a previous version if required.
You can have more than one CVSROOT on a server (something I have done at work in our production environment, one CVSROOT for configuration files, and another for scripts and code).
Anyway, onto the implementation on my Solaris 10 box, with a single CVSROOT for configuration files. Also; if this all seems too much - Google for "CVS Tutorial" and learn the basics. Also, read man cvs before embarking.
First, we need to install the SFWcvs package from the Solaris 10 Companion CDROM if it is not already installed. I have volume management disabled on my boxes, so I mount the CD manually prior to installing the pacakge.
# mount -F hsfs -o ro /dev/dsk/c0t6d0s0 /mnt
# # note you can find out the correct device by viewing the output
# # from iostat -En
# cd /mnt/components/sparc/Packages
# pkgadd -d . SFWcvs
Once the package is installed, unmount the CD and eject it.
# umount /mnt
# eject /dev/dsk/c0t6d0s0
You'll need to add /opt/sfw/bin to your PATH if you've not already done so - if you want to make this permanent add it to your shell initialisation files (.profile, .bash_profile, whatever).
# echo $PATH
/usr/sbin:/usr/bin
# PATH=$PATH:/opt/sfw/bin
# export PATH
# echo $PATH
/usr/sbin:/usr/bin:/opt/sfw/bin
# which cvs
/opt/sfw/bin/cvs
I've decided to use /usr/local/cvs for my repository, so...
# mkdir -p /usr/local/cvs
Then...
# CVSROOT=/usr/local/cvs
# export CVSROOT
I want to make this CVSROOT available to all users, so I add it to /etc/profile
# vi /etc/profile
# tail -2 /etc/profile
CVSROOT=/usr/local/cvs
export CVSROOT
Now...
# echo $CVSROOT
/usr/local/cvs
Cool. First, I want to initialise the repository. This will create and populate the CVSROOT directory (not to be confused with the CVSROOT environment variable) with various administrative files.
# cvs -d /usr/local/cvs init
Now, you'll see the CVSROOT directory in your $CVSROOT ;-)
# ls $CVSROOT
CVSROOT
That's it - your repository is ready for use! I've decided that I'll create a CVS module for each host on my LAN, and then check in the contents of /etc on each host into that module.
To show you what I mean, I'll perform this step on one of my hosts.
First I'll grab a list of the files I want to check in (this will serve as an index - I could even check this in, although there's no real need).
somehost# find /etc -type f -print > /var/tmp/`hostname`-list
Next, I'll tar the files up
somehost# output_file=/var/tmp/`hostname`.tar
somehost# while read filename; do
> if [ ! -e "${output_file}" ]; then
> tar cvf ${output_file} ${filename}
> else
> tar uvf ${output_file} ${filename}
> fi
> done < /var/tmp/`hostname`-list
somehost# gzip -d ${output_file}
somehost# ls /var/tmp/*.tar.gz
/var/tmp/somehost.tar.gz
Now, I'm using tar on a Solaris box, so the leading / is not stripped from the absolute path name like it is with GNU tar. I could have cd'd into /etc and tar'd up relative to that directory, but for me it is just as easy to scp the tarball over to my CVS host, and then use pax to translate these paths relative to a working directory.
The tarball is created, so I'll scp the tar.gz over to the CVS host.
cvshost# mkdir -p /var/tmp/working/somehost
somehost# scp /var/tmp/somehost.tar.gz user@cvshost:/var/tmp/working/somehost
From this point forward, the rest of the steps are being carried out on cvshost, so I'll show a simple root prompt (#) once more for clarity. We want to gunzip the tarball next.
# cd /var/tmp/working/somehost
# gzip -d ./somehost.tar.gz
A quick check of the tar file confirms that absolute paths are used. If we just go ahead and tar xvf this file as root, kaboom goes our current /etc filesystem!
# tar tvf ./somehost.tar.gz | head
So... we use pax to translate the /etc/ in each files absolute path to /var/tmp/working/somehost/etc/ during extraction.
# pax -r -s ',^/etc/,/var/tmp/working/somehost/etc/,' -f somehost.tar
Now we can see the files extracted relative to the somehost working directory.
# ls /var/tmp/working/somehost/etc/inet/hosts
/var/tmp/working/somehost/etc/inet/hosts
Now we're ready to import this tree into the repository
# cd /var/tmp/working/somehost
# cvs import -m "Initial Import somehost:/etc" somehost zazzybob "initial_`date +%d%m%y`"
somehost is the module, zazzybob is the vendor tag, and I've used the date in the release tag.
That's it, we're good to go. All of our files will be checked into our CVS repository! We can now remove our working directory
# cd ..
# pwd
/var/tmp/working
# rm -rf ./somehost
Let's checkout our module to work on it (you can extract single files, a list of files, the entire module, etc)... we'll extract the entire module
# pwd
/var/tmp/working
# cvs checkout somehost >/dev/null 2>&1
# ls /var/tmp/working/somehost/etc/inet/hosts
/var/tmp/working/somehost/etc/inet/hosts
We'll assume taht we've modified the hosts file on somehost and scp'd the new version to /tmp/incoming/somehost-hosts on the CVS server. Let's update our repository with the new version. I always perform a cvs update incase somebody else is working with the file.
# cp -p /tmp/incoming/somehost-hosts /var/tmp/working/somehost/etc/inet/hosts
# pwd
/var/tmp/working
# cd /var/tmp/working/somehost/etc/inet
# cvs update hosts
# cvs commit
This updates the repository and checks in our new hosts file. Your EDITOR will open for you to update the log entry for the commit. Once we've done this and the file has been committed to the repository, we can now remove our working directory.
Want to add a new file?
# cvs checkout somehost >/dev/null 2>&1
# cd /var/tmp/working/somehost/etc
# touch myconfig
# cvs add myconfig
# cvs commit
# cd /var/tmp/working
# rm -rf ./somehost
OK, myconfig was crappy, let's delete it...
# cvs checkout somehost >/dev/null 2>&1
# cd /var/tmp/working/somehost/etc
# rm ./myconfig
# cvs delete myconfig
# cvs commit
# cd /var/tmp/working
# rm -rf ./somehost
The main thing to note with a deletion is that you must rm the file that you've checked out before executing the cvs delete command.
That was a crash-course introduction to configuring and utilising a CVS repository on a Solaris 10 box. You can use CVS for storing pretty much any type of flat text files (binaries can be stored with a little work). There is a heap of documentation available if you want to learn more about CVS. Google for it, or purchase one of the many good books on CVS.
Cheers
Kevin Waldron
kevin@zazzybob.com
Disclaimer! - This article is provided for guidance only, and does not replace the relevant official documentation and manuals. I will not be held liable for any hosed systems and/or data.