
The Concurrent Versions System (CVS) is a powerful open-source tool for source code maintenance. It is provided on the the Xcode Tools CD that accompanies Mac OS X or as a part of the Xcode development environment online. This article covers some of the most commonly used features of CVS, with emphasis on using CVS with static and interpreted web files (HTML, PHP, Perl, etc).
CVS Overview and Terminology
Before you get started, take a moment to review a few common CVS terms. A repository is a place where CVS keeps master copies of all the files it knows about, along with information about the histories of those files. A project or module is a collection of files that belong together, such as all the files that make up a particular web site. To checkout a project is to make a local copy of all the related files so you can make and test changes on your own without affecting what others see. To commit is to save your changes back to the repository, where they are available to others (or to yourself, in case you ever want to roll back to a previous version).
Getting Started
CVS comes with Mac OS X; so you dont need to install anything new if youve installed the programs on the Xcode Tools CD or downloaded Xcode from http://developer.apple.com/tools/.
To get started, create a new directory for your CVS repository as the root user:
liz@localhost:~> sudo sh
Password:
root@localhost:~> mkdir /usr/local/cvsrep
Now look at the default owner and group for the directory you just created:
root@localhost:~> ls -ld /usr/local/cvsrep
drwxr-xr-x 3 root wheel 58 Dec 9 21:30 /usr/local/cvsrep
As you see above, the new directory is owned by the root user and belongs to the wheel group. You want to make sure you can write to the directory even when you arent working as the root user. You probably want to make your CVS directory available to your systems primary user. In my Mac OS X installion, liz was the first account created, and by default Mac OS X made liz a member of wheel and other administrative groups:
root@localhost:~> groups liz
staff wheel admin
The instructions that follow work on most single-user Mac OS X installations. You can also read more about multi-user CVS below.
First you need to change the permissions to the newly created directory so that the systems defualt user can read and write to the directory.
root@localhost:~> chmod g+w /usr/local/cvsrep
root@localhost:~> exit
liz@localhost:~>
Now create the CVSROOT shell environment variable. This variable tells CVS where its repository lives. (Note: if you dont know what shell youre using, its probably tcsh.)
In bash or sh, the command is:
liz@localhost:~> export CVSROOT=/usr/local/cvsrep
Or, in tcsh:
liz@localhost:~> setenv CVSROOT "/usr/local/cvsrep"
I also like to change the CVSEDITOR environment variable to be my favorite text editor, emacs. If youre happy with vi (the default), you can ignore this variable. If youre new to the Unix world, you might want to go with the simple editing program, pico. Later in this article youll see how to use CVS with BBedit.
liz@localhost:~> export CVSEDITOR=emacs
Or, in tcsh:
liz@localhost:~> setenv CVSEDITOR "emacs"
Now youre ready to set up your CVS repository with the cvs init command:
liz@localhost:~> cvs init
liz@localhost:~>
Once you log out of the current terminal window, youll lose the CVSROOT environment variable. That means the next time you try to use CVS, youll see an error message much like this:
cvs import: No CVSROOT specified! Please use the `-d' option
cvs [import aborted]: or set the CVSROOT environment variable.
There are three ways to work around this error. You can manually set the CVSROOT (and the optional CVSEDITOR) variables when you want to use CVS. Or you can add the -d /usr/local/cvsrep switch to your CVS commands. The easiest option in the long run, however, is to insert the export or setenv command above into your shell startup file. See the manpage for your default shell with, for example, man sh or man tcsh if you dont know how to set up a startup file.
Creating Your First Project
Once you have initialized CVS, you can create a new project. First, create a directory called myproj under your home directory:
liz@localhost:wherever> cd ~
liz@localhost:~> mkdir myproj
liz@localhost:~> cd myproj
Now you can create a new file as part of your project. For test purposes, create an HTML file called ~/myproj/index.html with some basic text:
<html><head><title>First</title></head>
<body bgcolor="white">
<h1>First File in First CVS Project</h1>
</body>
</html>
Now you can add this new project to your CVS repository using the import command:
liz@localhost:myproj>cvs import -m "My First Project"
myproj vendor-tag start
N myproj/index.html
No conflicts created by this import
liz@localhost:myproj>
Creating Working Copies of a Project
Now that you have created a new CVS project containing a single file, you (and others) can use the checkout command to make working copies of the project. You can make changes to this project and test them. Then, once you are satisfied, commit the changes to the main repository.
A sensible place to check out copies of web projects is into your individual web directory. By default, this directory is /home/username/Sites/. Assuming you have Apache running on your Mac OS X machine, files placed in this directory can be viewed in a web browser via http://localhost/~username.
This is how I check out a copy of a new project to my ~/Sites directory:
liz@localhost:~> cd ~/Sites
liz@localhost:Sites> cvs checkout myproj
cvs checkout: Updating myproj
U myproj/index.html
liz@localhost:Sites> cd myproj
You can now view this page from your browser:

At this point, you can delete the original project file from your home directory (~/myproj/index.html)if you like. The file is now in the CVS repository, and you can check it out whenever you want.
Making Changes
Now you can make some changes to your local copy of myproj/index.html, the one in the ~/Sites directory. For example, you might add a line of text:
<html><head><title>First</title></head>
<body bgcolor="white">
<h1>First File in First CVS Project</h1>
I have added some text to the file.
</body>
</html>
Once youve made the changes, commit the updated file to the repository. CVS expects you to supply a comment with your commit that summarized the work youve done. If you do not supply a comment using the -m flag, CVS will dump you into a text editor (see CVSEDITOR above) and expect you to type and save a comment there before proceeding.
liz@localhost:myproj> cvs commit -m "updated index.html" index.html
Checking in index.html;
/usr/local/cvsrep/myproj/index.html,v <-- index.html
new revision: 1.2; previous revision: 1.1
done
Adding and Deleting Files
When its time to add new files to your project, you can create the files in the the ~/Sites/myproj/ directory and import them using the CVS add command:
liz@localhost:myproj> cvs add another.html
cvs add: scheduling file `another.html' for addition
cvs add: use 'cvs commit' to add this file permanently
liz@localhost:myproj>
liz@localhost:myproj> cvs commit -m "added another.html"
cvs commit: Examining .
RCS file: /usr/local/cvsrep/myproj/another.html,v
done
Checking in another.html;
/usr/local/cvsrep/myproj/another.html,v <-- another.html
initial revision: 1.1
done
Want to remove a file from a project? First delete the file itself, then tell CVS that its gone:
liz@localhost:myproj> rm another.html
liz@localhost:myproj> cvs remove another.html
cvs remove: scheduling `another.html' for removal
cvs remove: use 'cvs commit' to remove this file permanently
liz@localhost:myproj> cvs commit -m "removed another.html"
cvs commit: Examining .
Removing another.html;
/usr/local/cvsrep/myproj/another.html,v <-- another.html
new revision: delete; previous revision: 1.1
done
liz@localhost:myproj>
Restoring a Previous Version
If youd like to check out a project as it existed some time in the past, you can use the -D flag. For example, to get the project as it existed at a certain date and time, create a fresh directory (or move or delete your existing project files), and run cvs checkout -D date project. For example:
liz@localhost:Sites> cvs checkout -D "2001-11-29 18:00" myproj
cvs checkout: Updating myproj
U myproj/index.html
Working with Multiple Projects
Your CVS repository can contain as many projects as your hard disks capacity will allow. Just create new projects with the cvs import command. When you check out a version of a project, cd into your local project directory and run your CVS commit, add, remove, etc. commands from there. Actually, theres no reason you cant have more than one CVS repository as well. You can specify different repositories using the -d flag or by changing the CVSROOT environment variable. All the examples in this article, however, assume a single local repository.
Using CVS with BBEdit 6.5
Traditionally, Mac internet developers have used BBEdit to develop files locally, then FTP-ed them to a remote server. But BBEdit 6.5 runs natively on Mac OS X and it supports integrated Unix scripting. This means you can write simple shell scripts to access CVS without leaving BBEdit.
To run a Unix shell script from within BBEdit 6.5 or greater, you will use the menu option labeled #! (often pronounced shebang). First, create a directory to hold your scripts.
liz@localhost:~> mkdir scripts
Now you can create a file in ~/scripts called, for example, commit. If you already know youll be working with multiple projects, you might want to give the file a more specific name, like myproj_commit. The file would have the following contents:
#!/bin/sh
cd ~/Sites/myproj
cvs -d /usr/local/cvsrep commit
To run your new commit script, select Run File from the shebang menu in BBEdit. The first time you run it, youll have to tell BBEdit where the script is. In the future, BBEdit will remember recently executed scripts and display them for you.
Using CVS Branches
Branches can be useful for even the simplest web applications. For example, you may want to release one version of a site while youre working on the next version. With CVS branches, you cannot only keep track of multiple releases, you can easily reference your various versions by name, making bug fixes and code maintenance much simpler. You can create branches by using the -b option to the cvs tag command.
liz@localhost:myproj> cvs tag -b phase_one
cvs tag: Tagging myproj
T myproj/index.html
Now that youve tagged the project for the first time, you need to take one extra step: release your current working directory and checkout a new copy of the project, specifying the new branch name (the -d flag deletes the files currrently in use):
liz@localhost:Sites> cvs release -d myproj
You have [0] altered files in this repository.
Are you sure you want to release (and delete) directory `myproj': y
liz@localhost:Sites> cvs checkout -r phase_one myproj
cvs checkout: Updating myproj
U myproj/index.html
Publishing Code with CVS
If you look at your project directory under ~/Sites/myproj, youll see that your source files arent the only things there. There are also CVS administrative files and directories:
liz@localhost:myproj>ls -l
total 8
drwxr-xr-x 5 liz staff 264 Dec 11 23:23 CVS
-rw-r--r-- 1 liz staff 125 Dec 12 17:19 index.html
These are necessary while youre working on a project, but you dont really want them to be part of your published website. To obtain a copy of a project without any CVS extras, you can use the CVS export command. For example, heres how I would export my web project to my Apache servers main document root. Note: Im specifying the date of now because export expects either a branch tag or a date.
liz@localhost:~> cd /Library/WebServer/Documents
liz@localhost:Documents> cvs export -D "now" myproj
cvs export: Updating myproj
U myproj/index.html
liz@localhost:Documents> cd myproj
liz@localhost:myproj>ls -l
total 8
-rw-r--r-- 1 liz staff 186 Dec 12 20:29 index.html
Using CVS with Multiple Developers
CVS will let more than one person work on the same file at the same time. In fact, if two people make changes to the same file and commit them, CVS will try to accommodate both changes. If CVS cannot accomodate your changes (for example, two people have both changed the same area of the same file), it will let you know that you need to hand-edit the file — and probably communicate with the other developer — in order to resolve the conflict.
If many people access the same CVS repository on the same machine, it helps to create a new CVS login group so that all developers can have write access to the repository.
To create a new group under Mac OS X, launch the NetInfo Manager application from /Applications/Utilities, and follow these steps:
- Click the lock icon and enter your password in order to make changes.
- Click the word groups in order to display groups.
- You are going to make a duplicate copy of an existing group and re-name it. Start by clicking on daemon.
- Click the duplicate icon (the image of two folders).
- Click on the name of the newly created duplicate group and change it to cvs-user.
- Change the group id (gid) to some number that hasnt been used yet, like 501.
- Highlight the users field in the lower pane of the window.
- For each user you would like to associate with this group, select insert value from the directory menu. Double-click each newly inserted value and change the name to the short name of your desired user.
When youre done, you should see a window something like this (liz and jay are the two users I have associated with the cvs-user group):

Once youre done, you can go back to the shell to confirm that the users liz and jay are members of the cvs-user group:
liz@localhost:~groups liz
staff wheel admin cvs-user
liz@localhost:~>groups jay
staff cvs-user
Finally, change the group ownership of your CVS repository so that the members of your new cvs-user group can access it:
liz@localhost:~>sudo chgrp cvs-user /usr/local/cvsrep
Password:
CVS has several useful commands for multi-developer collaboration. These include cvs status, which shows information about files youve changed that have also been changed by others; cvs release, which tells CVS that youre done working on a project; and cvs update, which updates your working copy of a project to reflect changes made by others. For more information on these and other commands see the suggestions for further reading.
Using CVS Across a Network
So far, youve learned how to deal with repositories and projects stored on your local Mac OS X machine. CVS will also let you to get repositories residing on other machines on the Internet, providing theyre running one of a few services.
CVS pservers
You can checkout files from a remote repository by accessing a public CVS pserver (password server). Many pservers, especially those for open-source projects, allow read-only access under the username anonymous with the password anonymous. You can access a remote server (specified with the -d flag) by first using the login command, followed by checkout.
liz@localhost:Sites> cvs -d :pserver:anonymous@
remote_server:/path/to/repository login
CVS password: anonymous
liz@localhost:Sites> cvs -d :pserver:anonymous@remote_server:
/path/to/repository checkout module_name
There is a significant disadvantage to using this method for anything but anonymous access, however. CVS pservers transmit the login name and password unencrypted. The module files are transmitted in the clear as well. This means that any unfriendly folks who may be listening in on your network traffic have full access to not only your login information, but your data.
There are ways to set up cvs-only usernames and passwords (see suggestions for further reading for directions), but it can be much nicer, in my view, to use CVS entirely through an encrypted connection. For this, you can use ssh (secure shell).
Secure CVS via ssh
Its simple to use CVS entirely over ssh. If you set the environment variable CVS_RSH (CVS Remote Shell) to ssh, you can run CVS commands securely across the Internet, just as though you were running them locally. Mac OS X 10.1 users have ssh and scp (secure copy) available by default. If youre using an earlier version of Mac OS X, you can visit http://www.openssh.com/ to obtain ssh.
If youd like to use ssh without having to type your password every time (useful if you are accessing CVS via a shell script in BBEdit, for example), youll want to create a public/private key pair with the ssh-keygen command. The advantage of doing this is that your scripts will be able to run without human intervention. The disadvantage is that anyone who can access your account on your local Mac OS X box will also be able to access those remote servers which have stored your public key.
The exact commands you enter for the ssh-keygen command depend on whether you are using ssh version 1 or ssh version 2. If you are using version 1, enter the following at the shell. (Note: If you hit return when you are asked for a passphrase you will end up with an empty passphrase.)
liz@localhost:~> cd ~/.ssh
liz@localhost:> ssh-keygen -t rsa1
Generating public/private rsa1 key pair.
Enter file in which to save the key (/Users/liz/.ssh/identity):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/liz/.ssh/identity.
Your public key has been saved in /Users/liz/.ssh/identity.pub.
The key fingerprint is:
liz@localhost:.ssh>ls
identity identity.pub
And for version 2, do the following
liz@localhost:~> ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/Users/liz/.ssh/id_dsa):
Created directory '/Users/liz/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/liz/.ssh/id_dsa.
Your public key has been saved in /Users/liz/.ssh/id_dsa.pub.
The key fingerprint is:
liz@localhost:~>cd ~/.ssh
liz@localhost:~> ls
id_dsa id_dsa.pub
Once you have created your private and public keys, you need to place your public key on the remote host in a place where ssh and scp can recognize it. Use ssh to connect to the remote host(s) on which you want to publish your projects. Then add the contents of your local ~/.ssh/identity.pub or id_dsa.pub file to a file in your remote ~/.ssh directory called authorized_keys (if you dont have one already, you can create it).
Whether or not youve placed your public key on the remote server, you can run CVS securely through ssh:
liz@localhost:Sites> export CVSROOT=":ext:your_login@
your_remote_server:/path/to/repository"
liz@localhost:Sites> export CVS_RSH="ssh"
liz@localhost:Sites> cvs checkout remote_module_name
Conclusion and Suggestions for Further Reading
In this article youve seen how to create CVS repositories and projects, add and remove files, make changes and commit them, and access CVS on servers other than your own. There are many more features in CVS for you to explore, and I encourage you to follow the links below to learn more.
For more information about CVS, visit http://www.cvshome.org/. Of particular interest is the Official CVS Manual. On your local OS X system, youll also find the standard CVS documentation, when you type man cvs. Finally, theres also the handy paperback CVS Pocket Reference, written by Gregor N. Purdy, and published by OReilly.
|