...composed of an indefinite, perhaps infinite number of hexagonal galleries...

© 1994-2017. David Sklar. All rights reserved.

Using CVS for collaborative website development

This document describes a method of using CVS as the centerpiece of a web development environment that enables multiple developers to work on the same or different parts of the website at the same time with a minimum of inter-developer toe-stepping-on.

Some Definitions

I'll assume you're working on a website called You also have, on a separate machine (perhaps inside your firewall), a staging server called Both of these servers have a document root of /www/ Additionally, the directory /www/support/ contains files and directories that you use for the website, but you don't want to be under the document root (.htpasswd-style authorization files, other included page parts, etc.)

The files at /www/ and /www/support/ on the staging server are a checked-out copy of the files in the CVS repository. After a developer commits changes, the staging server manager merges in the changes and updates the checked out copy that is under the staging server's document root.

Developers each have their own virtual server that points at a sandbox directory they can use. The virtual servers can be called something like The document root of the sandbox server is a directory underneath the developer's home directory, say ~username/ It is helpful if this document root doesn't really point to an actual directory, but a symlink that the developer can use to point to one of several branches he may have checked out and be working on.


When a developer wants to begin work on a new feature or bugfix, he uses the cvs rtag command to create a branch for his work:

[1]$ cvs rtag -b -r staging-current my-feature-name example
where staging-current is the tag that the staging server manager applies to the latest functional, tested, set of files on the staging server, my-feature-name is the descriptive name of the developer's branch, and example is the CVS module alias for the support/ and directories.

Once the developer has created a branch for his work, he can check out a set of files and begin editing them. To keep separate different branches the developer may have checked out at once, he should make a directory under ~username/, and then cd into it. Then, to check out the files in the newly-created branch, do:

[2]$ cvs checkout -r my-feature-name example
This will create support/ and subdirectories of the directory he is in when he issues the cvs checkout command and populate them with the files from the repository. To point the sandbox server at these directories, he should symlink ~username/ to the created directory, and ~username/ to the created support/ directory. (This assumes that the developer's sandbox server is using ~username/ as the document root and is looking in ~username/ for the support files.)

The developer can edit these files and the changes will be immediately visible on the sandbox server. When the developer has finished with the bugfix or feature addition, he needs to commit the changes. However, before he does, he should resynchronize the sandbox with any changes that may have been made to the staging server. He does this by typing, in the parent directory of the support/ and directories:

[1]$ cvs update -j staging-current
This will scan the developer's files and attempt to merge in any changes made to the staging server while you have been working on your feature or bugfix. CVS will print out any unresolvable merge conflicts, which should be resolved manually (and re-committed) before notifying the staging server manager that the feature or bugfix is complete. If the developer uses
[1]$cvs -q update -j staging-current
to synchronize the sandbox with the staging server, CVS will use a terser output format, and just print the files that were changed or had a conflict, instead of listing all of the directories into which it recursed.

Once the feature or bugfix is complete and the branch is synchronized with the staging server, the developer should notify the staging server manager that the work is done and that it should be merged into the staging server for testing.

There may be times where the developer cannot avoid merge conflicts between his branch and the main trunk of the staging server. He should make sure to notify the staging server manager of the files in which this happens.

When a developer is done with a branch, he can just remove the directory from the sandbox that the branch is in.

Merging into the Staging Server

When the staging server manager is notified that a branch is ready for integration into the staging server, it should cd to the staging server's support/ and directories and then attempt to merge the new branch in by running, in each directory:
[1]$ cvs update -j branch-name
where branch-name is the name of the new branch. CVS will attempt to merge in the changes from the branch. If the developer has properly synchronized its sandbox with the staging server before notifying the staging server manager that the branch is ready for merging, there shouldn't be any conflicts when merging in the branch. However, if there are conflicts, the staging server manager can

The staging server manager may find helpful CVS's -tn flags, which will show a trace of program execution and not execute anything that will change the disk. A command like

[1]$ cvs -tn update -j branch-name
will alert the staging server manager about potential conflicts without actually changing any of the files on the staging server.

Once the conflicts are resolved (or if there are no conflicts after the merge) the staging server manager needs to retag the staging server. (The following commands should all be executed from the parent of the and support/ directories.) First, commit all of the changes from the merge and conflict resolution:

[1]$ cvs commit -m "some helpful log message" example
where some helpful log message is a helpful log message that indicates what's being committed (e.g. what branch, what feature, etc.).

Then, make a new tag to indicate a stable revision of the staging server:

[2]$ cvs rtag -r HEAD staging-YYYYMMDD-HHMM example
where YYYYMMDD is the current (4-digit) year, (2-digit) month, and (2-digit) day, and HHMM is the current (2-digit) hour and (2-digit) minute. HH should range from 00 to 23.

Next, the staging-current tag needs to be applied to this newly tagged stable revision of the staging server. This is done by first removing the old staging-current tag:

[3]$ cvs rtag -d staging-current example
and then applying the tag to the new stable revision:
[4]$ cvs rtag -r staging-YYYYMMDD-HHMM staging-current example
where staging-YYYYMMDD-HHMM is the same daily-revision tag used in step 2.

Note that instead of removing the staging-current tag with cvs rtag -d and then re-applying it with cvs rtag -r, the staging server manager could just force a new application of the tag with cvs rtag -F. However, using -d keeps the staging-current tag closer to the top of the list of tags on a file, making it easier to find. However, this has the drawback of causing problems for developers who attempt to make a new branch between the time that the staging-current tag is removed and when it is re-created.

Copying Content To The Live Servers

First, a tag is applied to the current version of the site on the staging server that marks what will be copied:

[1]$ cvs rtag -r staging-current live-YYYYMMDD-HHMM example
where YYYYMMDD-HHMM is a daily-revision in the same style as the staging-YYYYMMDD-HHMM tags.

Then, this new tag is passed as an argument to the shoveling program:

[2]$ --tag=live-YYYYMMDD-HHMM
This program will copy files tagged with live-YYYYMMDD-HHMM to the live web server(s). It will only copy files that have changed since the last time they were shoveled. This program will output each file that gets copied to each server. Pay close attention to the output -- if something goes wrong with the shoveling, the operation may abort partway through, but some files will already have been copied to the live server. Make sure that each file gets copied to each server. If you run the same --tag=live-YYYYMMDD-HHMM command more than once, nothing will break -- files that have already been successfully copied won't be recopied.