Email: info at
Phone: +61 3 9479 3988 (Don) +61 0425 784 187(Paul)
Fax: +61 3 9479 1675
Suite 3B La Trobe R&D Park,
2 Research Ave, Bundoora Vic 3083, Australia

Download paper, slides & reference material (599 kB)

Document Version Control with CVS

by Paul Spain,  The Excellent Programming Company, Melbourne, Australia.
Contact:  Internet:


This paper gives a very brief introduction to the topic of Version Control, followed by an introduction to the CVS tool. We cover installing the software, and performing common version control tasks. We will then touch on some advanced features, before looking at the shortcomings of CVS, and current and future development of the product.

What is Document Version Control?

A precise, commonly accepted definition of Version Control is not available. I would start to describe it as a series of tasks:

  • Archiving the change history of documents - the ultimate ‘undo’ feature.
  • Retrieving particular archived versions.
  • Coordinating changes by a collection of document authors.
  • Collective branding of a group of files at significant points in development.
  • Facilitating the creation, ongoing development, and merging of concurrent tailored versions.

An encompassing term is Configuration Management (CM), which is sometimes interpreted as the embodiment of an organisation’s development process. A good reference collection is:

While most people associate Version Control with software, it is commonly being used to archive Web sites, and is suitable for charting the development of any (primarily) text-based document collection.

What is CVS?

In common with most version control systems, CVS uses a repository/workspace model of development. The archived versions of your files are stored and managed in the repository. To edit the files you must first create a workspace or sandbox, which contains a local copy of your project. This is your private development area to use or abuse as you see fit. You then use your version control tool, CVS, to communicate changes between the repository and your workspace.

Concurrent Versions System (CVS) is a network-transparent, hierarchical, file-based version control system. The same tools and techniques are used to work with CVS on a single machine, on a LAN, or across the Internet. The change history of documents is stored on a per-file basis, so the structure of the repository mirrors the structure of your workspace. The operation of CVS can be customised via a collection of text-based administrative files. You administer these files as a typical project using the repository/workspace metaphor.

CVS is the standard tool for Open Source development, itself an Open Source project, distributed under the GPL licence. Work began in 1986, as a collection of hierarchy-aware scripts overlying the standard Unix version control offering, RCS. Now written entirely in C, development is largely by accretion, with the custodians of the source tree changing several times in the last few years. Currently, CVS lives at, hosted by CollabNet, an Internet/Open Source super group. CVS scales from a single developer on a desktop machine to very large, globally distributed projects, such as Apache, GNU and Mozilla (the Open Source flavour of Netscape Navigator).

CVS has been ported to many platforms, including Unix, Linux, Windows, MacOS, OS/2, VMS and DOS. There are command line, GUI and web browser interfaces, written in C/C++, OS-native GUI code, Java and Tk/Tcl.

CVS is now well documented, and has an active user community. See the Resources section of this paper for details.

The Enterprise version of JBuilder 4+ has built-in support for CVS. For the rest of us, the open sourced WinCVS seems to be the most common interface for Windows development. I shall refer to both WinCVS and the command line client for examples.

Installing CVS

WinCVS is available from This site is the home and reference site for many CVS interface projects, and a Windows NT server port. The WinCVS self-extracting archive includes a separate Win32 command line client (cvs.exe).

To use the command line client you need to establish two environment variables, HOME and CVSROOT. HOME is the directory location of your personal CVS preferences and configuration. CVSROOT is the connection string for your default repository. Though not strictly essential, it will save you a lot of typing.

WinCVS is self-contained, so you must set up the equivalent information to HOME and CVSROOT via the Admin|Preferences menu item in WinCVS.

Creating a repository

Creating the repository is a trivial operation. On the repository host machine, run:

 cvs -d /path/to/repository init

This will create a subdirectory CVSROOT, in the specified location which contains the CVS administrative files. The identical naming of this directory and the aforementioned environment variable is unfortunate and a common point of confusion.

Projects are added to this new repository via the cvs import command, covered below in Adding an existing project to a repository, and will appear as additional subdirectories of the repository directory.

The hardware requirements of CVS are quite modest. The most memory intensive operations are creating a new workspace and committing changes to the repository. Two rule-of-thumb metrics for memory requirements are:

·         Allow 5-10 times the size of the largest file you want to commit. This is a peak value, and needn’t be multiplied for concurrent clients.

·         For workspace creation allow the greater of 2MB and the size of the largest directory, for each concurrent client connection.

The allocated memory can be largely swap space rather than physical memory. 32MB of physical memory will provide reasonable performance for typical repositories.

A repository disk space rule-of-thumb is to initially allow three times the size of the projects to be managed by CVS. How quickly you exceed this value will depend on the rate of development and the proportion of binary files in the repository. Text file versions are stored in diff format and are space efficient, whereas binary file versions are appended to the repository as a complete file image.

CVS largely relies upon the host operating system and external software to manage repository security. CVS provides some services - refer to Pserver connections in the references. It is important to note that CVS uses lock-files to control concurrent file access. By default, these files are created in the same directory as the accessed files, so even read-only users will require write authority for lock-files. Later versions of CVS allow a separate lock-file directory to be specified.

Connecting to a repository

Connections fall broadly into two categories - local and client/server. Local repositories are suitable for desktop and LAN installations. Client/server connections come in several flavours, run over the TCP/IP protocol, and are suitable for any kind of remote connection: LAN, Internet or Dial-Up. A client/server setup is recommended for production systems.

We shall briefly look at three connection styles.

CVS is a stateless tool - each command invocation is a separate transaction with a repository and must supply a connection string. This is done either explicitly as a command argument, or implicitly via workspace parameters or the CVSROOT environment variable discussed above. Here are some example connection strings:


This is parameterised as :


:local: in the first example indicates a local repository, followed by the path to the root of the repository. The path specification is in OS-specific format. c:\cvs obviously refers to a Windows, DOS or OS/2 file system.

All other connection methods, :ext: and :pserver: here, use the CVS client/server protocol, and require that user@host be specified. user is a shell account or CVS-specific username on the machine given by the local or internet domain name, host.

:pserver: is probably still the dominant client/server connection method. It describes a generic authentication framework, but is more commonly construed as the internal CVS password-authenticated connection mechanism. Pserver is inherently insecure as the user password on the server is transmitted as trivially-encoded text from the client. Also, the traffic is unencrypted, but optionally compressed. Despite these limitations, it is very popular, especially for read-only anonymous access to open source projects.

The first time you connect to a repository via pserver, you will be prompted for your password, which is cached on the client machine in a file named .cvspass for subsequent commands. This file is stored in the location given by the HOME environment variable for the command line CVS client. In WinCVS, this location is again configured via “Admin|Preferences|WinCVS|HOME folder” off the main menu

:ext: uses a user-specified external program to authenticate and manage the connection. Originally developed for the Unix rsh (remote shell) program, any program with an rsh-like command line interface can be used. The location (path) of the program to be used is specified by CVS_RSH environment variable for the CVS command line client. If not specified, rsh is assumed (must be in your OS path). CVS will execute this application, passing cvs server as command line arguments. This invokes CVS on the remote machine in server mode. I run the SSH (secure shell) protocol on our Linux server, and connect via a Win32 port of the SSH client application. This provides compressed and encrypted transmission of all traffic between client and server. We use RSA public key encryption, but this boils down to SSH configuration and your preferences.

Using CVS

WinCVS (and I suppose most CVS GUI interfaces) translate GUI actions to command line equivalents. All the good reference material is based on the command line CVS interface, so familiarity with this interface is prudent, and somewhat unavoidable, for the informed CVS user. However, unless you are a command line fiend and machine gun typist, I would recommend using a GUI interface, as there is a plethora of command options, and the commands tend to get quite long.

Command line CVS follows the pattern below.

cvs [global options] command [command options] [files]
[] indicates optional fields.

If [files] are not specified, most commands default to the current directory. Most commands act recursively, by default, through the specified or implicit directories. Apply a command across the whole project by specifying (explicitly or implicitly) the root directory of the workspace. The complete command-set is tabulated below. Commonly used commands are highlighted in yellow. Refer to the references in the Resources section below for description of global and command options.




Repository and workspace management


Create a new repository


Create a new workspace. Pass the name of the project to checkout as a command option


Extract a project copy like checkout, without the administrative subdirectories, so not a workspace. Pass the name of the project to export as a command option


Removes a workspace. Checks (fails) for uncommitted changes, and updates repository history. Invoke from workspace’s parent directory.

Project management


Create a new project. Acts recursively from current directory, adding directories and files encountered. Uses administrative files cvswrappers and cvsignore to determine binary files and files to be skipped, respectively. An esoteric use of this command is to update the vendor branch - useful for third-party sources.


Add directory or file(s) to an existing project. Repository must be subsequently updated with commit. Explicitly specify binary files - default is text. Also used to negate an uncommitted remove. Directories must be added in a separate (and prior) command to files contained therein.


Removes file(s) from a project. Repository must be subsequently updated with commit. Directories are not explicitly removed. Pass the -P option to update to prune empty directories.

File change transmission


Update workspace file(s) with changes from repository. Also used for merging branches, retrieving tags (snapshots) or branches or specific versions, rolling back committed changes, and patching particular version changes to your workspace copy.


Commit changed file(s) from the workspace to the repository. Also used to action add and remove commands.

Version labelling and branching


Create a label or a branch on selected file(s). Based on workspace version numbers. A tag applied across an entire project (a snapshot) is useful for recreating project release images, debugging specific versions, or rolling back projects to a known and/or stable state.


Create a label or a branch. Based on latest (or tagged, if supplied) repository versions - no workspace required. Tag is always applied across entire project.

Version differences


Show the difference between two versions in Unix diff format


Similar to diff, based solely on repository versions - no workspace required

Change review / Audit trail


Display files(s) log messages


Display status information for file(s)


Line-by-line version information for specified version of file(s)


Display repository activity log

File locking


Most commonly used for exclusive file access - locking/unlocking file(s). Also used to modify log messages in repository.

File activity notifications - ‘Watch’ facility


Administers a ‘watch’ on a file


Signals start of editing session for a ‘watched’ file


Signals end of editing session for a ‘watched’ file


Displays current editors of a ‘watched’ file.


Display current watchers of a file

Pserver connections


Confirms and caches pserver password for a repository


Removes cached pserver password

Server mode invocations


Invokes CVS in pserver mode. Not a user command.


Invokes CVS in server mode. Not a user command.

Software version


Returns CVS version number


Creating a repository:

Using WinCVS, select “Create|Create a new repository…” off the main menu, and complete the dialogue. Read the instructions carefully.

These command line commands should be executed from the repository host machine

On a Unix/Linux file system: cvs -d /MyRepository init

On a Win32 file system: cvs -d :local:C:\MyRepository init

Note the differences between the directory separator characters, and the requirement for the :local: prefix in the Win32 version to stop misinterpretation of the colon in the drive specification, C:

Adding an existing project to a repository:

There are two preparatory steps before importing a project from existing sources:

Determine which files are candidates for version control.

Typically, I exclude files which don’t contain human input, explicitly or implicitly. This is typically automatically generated binary files (eg *.obj,*.dcu) , backup files, and files which don’t contain project configuration.

From the files to be version-controlled, identify the binary files.

CVS performs line-end conversion and CVS keyword substitution on text files. In all repositories, text files are stored with Unix line-endings (a single linefeed character (ASCII 10)). These are then translated as appropriate for the workspace file system. This makes repositories easily portable across different file systems. CVS also has a series of keywords, enclosed by $ characters. When files containing these keywords are updated or committed, by default the workspace copy keyword strings are modified to reflect the latest repository information. I am using the Header keyword as the footer for this document, which is a grab bag of most of the information available. These line-ending and keyword translations are not made for binary files. This is why it is important to correctly identify them, otherwise occurrences of line-ending or keyword byte patterns will undergo translation.

Using WinCVS, select “Create|Import module…” off the main menu and follow the wizard. The wizard will examine your sources and make reasonably intelligent guesses about which files are binary, allowing you to override any of its choices. Unfortunately, WinCVS versions up to (and probably including) the current 1.2 don’t allow you to exclude files from the repository within the import wizard. You will need to manually clean up your import sources before running the wizard.

Command line CVS allows for automation of both preparatory steps. WinCVS could leverage this facility, but its author has chosen otherwise. There are two administrative files used to control these two steps. They are edited by creating a workspace for the CVSROOT project. Then make your changes and commit them back to the repository. The changes take effect immediately. The files in question are named CVSROOT/cvswrappers and CVSROOT/cvsignore. The first file lists all the repositories binary file types, and the latter lists the files and file types to be ignored. The administrative files are typically line-based and specify files via regular expressions. I have included my customised versions of these files, with entries for Delphi and C+++Builder, and many common binary file types. Use or adapt these as you see fit. Note that cvsignore and cvswrappers can also be specified via environment variables and by configuration files in your home directory and/or throughout your sources. Refer to the references for more details.

From the command line, change your current directory to the root directory of your project sources, and run, as an example:

cvs import -m ”Initial import into CVS” MyProject MyOrganisation Version_0

All elements of this example command are compulsory.

  • import indicates the CVS operation
  • -m ”Initial import into CVS” is a comment of your design for the file log
  • MyProject is the name of the new module in the repository, and represents a new subdirectory at the root of the repository
  • MyOrganisation is known as the vendor tag.
  • Version_0 is the release tag, which is a snapshot of the project at this point.

You will notice that we have specified neither the repository (the default value is given by the CVSROOT environment variable), nor the sources to import. Unless told otherwise, CVS will recursively traverse the directory tree rooted at the current directory, adding encountered files that pass through the filters of cvswappers and cvsignore.

Adding a new project to the repository

Adding a new (empty) project to a CVS repository is a similar process to an existing project. Run the same command as above, with the current directory being an empty directory. You can later add files and directories as you create them via cvs add. This is covered later in Adding and removing files and directories.

Creating a workspace

As previously mentioned, all file editing occurs in your local private copy of the project, known as your workspace. There is nothing to stop you having multiple concurrent workspaces. This can be handy if you are required to work on new development and bug fixes simultaneously. You can have a workspace for each. Typically your bug-fix workspace will be based on a branch or an earlier released version, identified by a tag. Tagging is explained in Advanced Features.

To create a workspace in WinCVS, select “Create|Checkout module…” from the main menu and follow the wizard.

From the command line, move the current directory to the root of your new workspace and run:

cvs checkout MyProject

MyProject is the name of the module (project) in the repository

This will create a new subdirectory of the current directory called MyProject, and a copy of the latest versions of all the files and directories in MyProject. There are many command options, one of which allows you to specify a tag to extract a snapshot or the latest versions on a branch. Note that once again, we are relying on the environment variable, CVSROOT, to specify the repository.

Getting changes from the repository

If you are part of a team using CVS, you should think of the repository as a database. Like a database, its contents are frequently changing, and any extracted information is only current at the time of extraction. With this in mind, it is prudent to get the latest version of a file immediately prior to editing [1] .

From the root of your MyProject workspace, ie in the MyProject directory, run:

cvs update

This will traverse the entire workspace, updating all files to the latest versions [2] in our example. It is also possible to retrieve a particular version of a file, by specifying a version number or date. Note that when you do, CVS will create a sticky tag on that file.

Sticky tags are created automatically and serve two purposes:

They protect any changes you make to the particular version in your workspace from being merged with more recent repository versions in subsequent update operations.

They protect the integrity of the repository. You can’t commit changes from a file with a sticky tag. If this was allowed, you could destroy any evidence of more recent versions of the file, since your modified old version would become the latest version. To preserve your changes in the repository, you can either merge your changes with the latest version of the file, using the -A option of the update command, or create a branch version for your changes. Branches will be covered under Advanced features.

Editing a file

Many version control products employ a file locking protocol, ensuring exclusive write access to the file for the lock-holder. CVS supports this protocol [3] , but not by default. This protocol certainly prevents any change conflicts, but it can become a restrictive process for team development and common files. At best, any other writers must wait till the lock is released. At worst, it can result in hasty/buggy changes, modifications to earlier versions which must be subsequently reconciled (merged), or even back door hacks to circumvent the file locking. Stop me if you’ve heard this one before…

By default, CVS uses an optimistic multiple writers approach [4] . Everybody has write permission on a file, and changes are merged as the editors commit their changes to the repository. Changes won’t commit without merging. At first, this protocol appears problematic to most people who haven’t used it. In practice, manual intervention is only required for lines which have been modified by both editors involved in the merge. As with file-locking, this protocol is not without risk. It is possible to introduce logical errors to a file which has merged without incident, as added or deleted lines can change the semantics of the prior version. This technique is most effective when changes are committed frequently and the number of simultaneous writers is minimised.

Both protocols work better when developers work at high currency - frequently committing and updating their workspaces, so that individual repository file changes are small. Division of a project and labour into distinct, non-overlapping modules will greatly reduce the number of version control collisions, either from waiting on locks or merge conflicts.

There is no universally right answer to this problem. It will depend on the combined makeup of your team and your project, and you’ll never have the luxury of knowing you chose correctly.

Transferring changes to the repository

You’ve made changes to one or more files in your workspace. You now use the commit command to transfer those changes to the repository.

In WinCVS, select the files and choose “Modify|Commit selection…” off the main menu, or use the button on the toolbar.

From the command line:

cvs commit MyFile.ext

By default, this will commit MyFile.ext in the current directory and any subdirectories. You can list multiple files or use regular expressions, or specify no files to commit all changed files. If you don’t specify a log message (this example) the default editor will be invoked for you.

Commits can fail for a number of reasons. The most common are that the workspace version is the same as the repository version, or that someone has beaten you to the punch, and you must first merge your changes with the version they have committed. An error message will be generated in this case. Run cvs update to pick up their changes. Resolve any conflicts in your editor. Conflicts will appear in standard diff format [5] - lookup the references for details. Then run cvs commit again. Perform the equivalent actions if you are using WinCVS.

Adding and removing files and directories

Remember that your workspace is your private space to use or abuse as you see fit. The repository only finds about changes to your repository when you choose to tell it. CVS is only involved when you want to communicate changes to the repository.

Add directories to the repository before you add any files contained therein.

To add a directory using WinCVS, select the directory in your workspace, and choose “Modify|Add selection” off the main menu, or press the associated button  on the toolbar.

From the command line, make the current directory a parent directory of the new directory and run:

cvs add MyDirectory

To add a file using WinCVS, select the file(s), and choose “Modify|Add selection” or Modify|Add selection binary” off the main menu, or use the speed button, for text and binary files respectively.

To add a binary file from the command line, run one of , for example:

cvs add -kb MyPicture.gif

cvs add MyPicture.gif

The first version explicitly identifies the file as binary via the -kb command option.
The second version relies on the file type being recognised as binary by the cvswrappers mechanism (see the command line notes in Adding an existing project to a repository)

Adding files as text is the default behaviour and requires no command options:

cvs add MyFile.txt

Removing files from the repository is a two-step process. Mark the file(s) for removal with the cvs remove command, then perform the action in the repository via cvs commit. Note that the files are not deleted from the repository. They are transferred to the “Attic”, as they are still required to recreate any earlier versions which may participate in old project snapshots.

In WinCVS, select the file(s), and choose “Modify|Remove selection” from the main menu, or press the corresponding button on the toolbar. Then commit the change to the repository by selecting “Modify|Commit selection…” off the main menu, or the corresponding button on the toolbar.

From the command line, in the same directory as the file, run:

cvs remove -f MyFile.txt

cvs commit MyFile.txt

The -f ‘force’ command option instructs CVS to delete the workspace file. If this option is not specified, you must manually delete the file before running the remove command. There is no corresponding CVS action to remove directories from the repository. If all the files have been removed from a workspace directory as above, and the directory is empty, you can update your workspace with the -P command option enabled. This will remove or “prune” any empty directories:

cvs update -P

There is a corresponding checkbox “Prune (remove) empty directories” on the Globals tab of the WinCVS update dialogue.

Comparing file versions

It is often helpful or necessary to compare different versions of a file. For example, if you are committing your changes to the repository, reviewing what has changed compared to the repository version helps to write better commit log notes.

CVS provides two different commands for file comparison. diff and rdiff. The output of both commands is in Unix diff format [6] . If you specify two revisions in either diff command, the ordering is significant. See the third and fourth example commentaries below.

cvs diff compares your workspace file versions against repository versions.

1) cvs diff MyFile.txt
2) cvs diff
3) cvs diff -r 1.1 -r 1.2 MyFile.txt
4) cvs diff -r 1.2 -r 1.1 MyFile.txt
5) cvs diff -r 1.1 MyFile.txt

 Example 1 diffs the workspace copy of MyFile.txt. against the same revision number in the repository. It also recurses down from the current directory looking for other versions of MyFile.txt. This recursive behaviour seems more appropriate when a regular expression is used to specify the file argument.

Example 2 diffs all files starting from the current directory.

Examples 3 and 4 ignore the workspace versions and generate diffs between explicit versions of the file. Example 3 generates the diff which transforms revision 1.1 into revision 1.2. Example 4 generates the reverse diff - it transforms revision 1.2 back to revision 1.1. This reverse diff technique can be used with the cvs update -j command to roll back changes in the repository.

Example 5 uses the workspace revision as the implicit second argument, and generates the diff which transforms revision 1.1 into the workspace copy.

cvs rdiff compares two slices through the entire project, using only the repository.

1) cvs rdiff -r 1.1 MyProject
2) cvs rdiff -r 1.1 -r 1.2 MyProject
3) cvs rdiff -r Release_2 MyProject

The implicit second argument in example 1 is the latest trunk revision in the repository. As before, the generated diff transforms revision 1.1 into the latest trunk revision.

TIP: The symbol HEAD can be used in CVS commands to indicate the latest trunk revision

Example 2 compares two revision numbers across the project

Example 3 is more typical. It uses a tag instead of a numeric revision and recursively generates the difference between Release_2 and the latest trunk revisions. This technique is very useful for distributing source patches, as the output of the command is in the format required by the Unix patch utility. A Win32 port of patch ships with WinCVS.

rdiff is not accessible through WinCVS’s GUI elements. You can use the terminal window to type in command line equivalents, or write your own solution using a Tcl script plug-in.

WinCVS supports diff for a selected file via a dialogue accessible from “Query|Diff selection..” on the main menu, or the toolbar button, or the Alt+= hotkey.

Better still, WinCVS can link to an external diff tool. You can specify the path to the tool on the WinCVS tab of the Preferences dialogue invoked from the “Admin” menu item.

Invoke the tool by checking the “Use the external diff ” checkbox on the “Diff settings” tab of the diff dialogue. This setting is persistent.

I recommend that you use a GUI diff tool. These typically provide side-by-side file comparison which is much easier to understand than Unix diff-formatted output. There are several freeware visual diff tools available. I use one from Starbase. There is a link to another on the WinCVS site.

WinCVS has another great feature which alone is worth the price of admission. This is the Graph facility (“Query|Graph selection”, toolbar button, or Ctrl+G hotkey). It is a graphical representation of the output of cvs log, which is the history of the selected file on a revision-by-revision basis. From this display you can visually select two versions to diff. Click the first revision node, then shift-click the second node. The diff is performed in ascending revision number order, irrespective of the order you click the nodes.

Advanced Features

To date, we have only covered the basic functionality of CVS. In this section, I will introduce some topics that are especially useful for team development, and experienced version control practitioners. There are several commands listed in the table in Using CVS that I won’t cover at all. Refer to the materials in Resources for more details.

Project snapshots

Tags are text labels that are associated with file versions. Tags are a powerful tool for identifying the versions of a collection of files at a particular point in time - a project snapshot. For example, you can identify release versions of a product, branch points, or where bug fixes have been incorporated, or before and after new features have been developed. It is a good idea to tag any point that you may need to revisit.

CVS provides two tag commands, tag and rtag. Similar to diff and rdiff, tag works with the file versions in your workspace, whereas rtag works with the repository. rtag applies across the whole project, whereas tag can be localised to specific files or act recursively across a directory tree.

To use tag in WinCVS, select the file(s), then choose “Modify|Create a tag on selection” and complete the dialogue. To use rtag, choose “Create|Create a tag by module” and complete the dialogue.

From the command line:

cvs rtag Release_3 MyProject

This example, executed from anywhere, will create an new repository tag named Release_3, using the latest trunk versions of all files

From the root directory of your workspace for MyProject:

cvs tag -c FeatureComplete

This example will create a tag called FeatureComplete, across the entire workspace, using the file revision numbers of the workspace. The -c command option will cause CVS to check for uncommitted changes before applying the tag. If found, a message will be displayed and the command fails.


Branches are at once a very powerful and an unreasonably feared feature. The power comes from facilitating parallel streams of development. For example, this allows isolation of bug fixing and new feature development from a stable main trunk, or development of specialised product versions with a common base of code. The aforementioned fear comes from merging the branches with the main trunk. Provided merging occurs relatively frequently, and the number of concurrent branches is minimised, merging is not painful.

Branches are implemented by CVS as special tags. Use -b command option of tag or rtag to administer branches. WinCVS surfaces tag -b via “Modify|Create a branch on selection…” and a toolbar button. rtag -b is surfaced as “Create|Create a branch by module…”.

Karl Fogel has two very good sections on branching basics and practical techniques in his book (See Resources). I refer you to his treatment of the topic, and use and recommend his Dovetail branching technique.

There are three points which are worth reiterating:

  1. When you create a branch, the branch occurs in the repository - your workspace is unaffected. To work on the branch, you must update your workspace location. It is essential that you commit any changed files to the repository before you “move”, otherwise the uncommitted changes will be merged into your updated workspace!

To move to the branch in your workspace, run this command from the root of your workspace: cvs update -r YourBranchTag.

 In WinCVS, select and focus the root directory of your workspace, invoke the Update dialogue, and specify the branch tag on the “Sticky options” tab.

To move back to the trunk in your workspace, run this command from the root of your workspace: cvs update -A.

In WinCVS, select and focus the root directory of your workspace, invoke the Update dialogue, and check the “Reset any sticky date/tag/’-k’ options” checkbox on the “Update settings” tab.

  1. It is imperative to keep your frame of reference in mind when merging branches. Your workspace is always the merge destination. To merge trunk changes to a branch, your workspace must be the branch. To merge branch changes to the trunk, your workspace must be the trunk.
  2. It is tedious to perform multiple merges of the same code. But this is the default behaviour every time you merge a particular branch with the trunk, since by definition all changes from the root of the branch to the tip are merged. We can use tags to avoid this behaviour - by tagging the source of every merge. This is a tag on the branch [7] when you merge from the branch to the trunk, and a trunk tag when you merge from the trunk to the branch. On subsequent merges, you can then specify to merge from the last merge point to the tip, rather than the root of the branch to the tip. See the examples below.

Merging from the branch to the trunk (in the trunk workspace):

cvs update -j LastMergeSourceTagOnBranch -j BranchName

In WinCVS, select and focus the root directory of your workspace, invoke the Update dialogue, and specify the tags on the “Merge options” tab.

Merging from the trunk to the branch (in the branch workspace):

cvs update -j LastMergeSourceTagOnTrunk -j HEAD

In WinCVS, select and focus the root directory of your workspace, invoke the Update dialogue, and specify the tags on the “Merge options” tab.

Note the use of the special CVS symbol, HEAD, to denote the latest trunk versions.


Watches are an automatic email notification scheme. You administer persistent interest in file(s) via the cvs watch command, using command options to add or remove the watch, and to indicate the activities you want to track.

Any editing of watched files must be preceded by cvs edit and terminated by a cvs commit or cvs unedit to abandon changes. Both these events generate notifications. By running cvs edit you will automatically be added to the list of watchers for the selected file(s) until a subsequent cvs unedit or cvs commit.

To see who is currently editing MyFile.txt, run cvs editors MyFile.txt. To see who is currently watching MyFile.txt run cvs watchers MyFile.txt. Both these commands exhibit the usual CVS behaviour regarding recursion and missing file specification.

The scheme relies on the religious application of the edit and commit/unedit commands by all editors. It helps to check out files read-only (a global WinCVS option) as this provides a cue in most editor software when you attempt to edit the file. A side effect of cvs edit is to make the selected files read-write. A subsequent cvs commit or cvs unedit reverts the permissions to read-only.

My personal experience with watches is that they generate a lot of email which consequently tends to be ignored. It may be more useful for distributed teams with intermittent or dial-up access to the repository host.

Administrative files

The CVS administration files provide hooks for extensive customisation and integration of CVS into your development process. You can specify message templates for commit messages. You can write scripts or binaries which will fire in response to tagging or commit operations, optionally failing the commit action. This enables you to automate regression testing or some other QA protocol on all code in the repository. You can also aggregate portions of projects or a collection of projects under a collective label, and use this label as the file specification in many cvs commands. CVS also provides predefined and user-defined variables which are expanded within the scope of the administrative files. These can be used as arguments in script snippets in these files. We have already encountered the cvsignore and cvswrappers files in Adding an existing project to a repository

All these facilities are covered in greatest detail in the CVS manual (see Resources).

Problems with CVS

The most common complaint with CVS is the difficulty in renaming or moving files. There is no simple command to accomplish this - the usual scenario being:

  • In your workspace, copy the file(s) to the new location.
  • Move to the new location, and invoke CVS to add and commit the file.
  • Return to the old location, and invoke CVS to remove and commit the old file

As well as being tedious, there is no record of the move apart from a couple of entries in the repository transaction history and any commit notes, AND there is no linkage between the old and new file version histories. Versions in the new location start over from 1.1.

DUBIOUS TIP: If you have access to the repository, backups and a testosterone surplus, you can copy the file archives to the new location (or new name). This will give you a continuous version history in the new location (or name).But be warned, reverting to any old tags or branches will have files appearing in both old and new locations.

Another common complaint with mature projects and/or slow servers is the time taken to place a tag on the project. This should really be a ‘constant-time’ operation, but instead has a strong correlation to archive size (version history).

Future development

CVS development continues, but relates more to bug-fixes than new features. Its growth over time has been more a process of accretion than design. However, take heart, there is another project a-brewing, with the backing/input of some heavyweight CVS and Open Source talent.

The project is named Subversion (, and will be command interface-compatible with CVS wherever possible. The software has been cleanly architected from the ground up and addresses the aforementioned weaknesses of CVS and adds a lot of great new features.

It all looks very cool, using the WebDAV [8] extensions to HTTP in lieu of the CVS client-server protocol. This means remote connections will run on port 80 (HTTP), which gets around the usual firewall access problems. The server version of Subversion will use an Apache module to communicate with the back-end database, leveraging the Apache web server for security and link management with attached clients.

A major objective of the project is to import CVS repositories, so there will be a clean migration path for current and new users of CVS. Milestone 3 is/was self-hosting, ie migrating the Subversion project source from its CVS repository to a Subversion repository - a good test of the migration tools. The project will hopefully be in beta or even released by the time you are reading this paper. However, Milestone 3 has slipped by a couple of months as I write this…


  1. CVS home page:
  2. Current CVS manual, aka Cederqvist. This is now more current than Karl Fogel’s book. Good for reference, but can be hard going for general reading :
  3. WinCVS home and links for other GUI interfaces and NT port of server:
  4. The CVS book: “Open Source Development with CVS”, by Karl Fogel.
    As well as being a complete reference, there are tutorial style chapters and good discussion on branching/merging techniques. The best all-round reference available for CVS. On-line chapters released under GPL licence: The non-GPL’d chapters are an excellent guide for managing an Open Source project. The HTML version of the free chapters is included on the BorCon proceedings CD as CVS Book
  5. CVS mailing-lists:
    Subscribe to the info-cvs list for usage help or general discussion. Be warned, this list has quite a lot of traffic, with the usual quota of Volvo drivers…
    Subscribe via:
  6. Secure Shell (SSH) resources, including software:
    Note: To use SSH with CVS, you’ll need a command line version of SSH, not a terminal emulator which supports SSH sessions.
  7. Subversion: The next generation of CVS:
    Project home page:
    Goals; comparison with CVS:
  8. I have also enclosed two sample versions of the CVS administrative files, cvsignore and cvswrappers, which include ignorable and binary file types, respectively, for Delphi and C++Builder developers. I have also included may common binary file types not handled automatically by the command line client.

[1] This practice is assumes you are all working on the same branch of the project. The main branch of the project is known as the trunk. If you’re not using branches, you’re working on the trunk. Branching is covered in ‘Advanced features’.

[2] This statement holds true as long as you are working at the tip, or latest, versions of the trunk branch. There is a CVS keyword to describe this situation, HEAD, which can be used in various CVS commands where an argument represents the latest trunk version.

[3] Use the -l and -u options of the cvs admin command to lock and unlock files respectively. Read the reference material, as there are additional cvs admin commands to initiate and terminate the usage of this protocol on the repository.

[4] The Interbase database also uses an optimistic, multiple writers scheme. From memory, the first completed transaction is written to file. Subsequent concurrent transactions fail if a prior transaction has changed the data in question. At least as developers we (usually!) have the option of cooperative changes or negotiation.

[5] If you are reading this file from the material bundled on the BorCon proceedings CD, follow this link: Diff format explained

[6] If you are reading this file from the material bundled on the BorCon proceedings CD, follow this link: Diff format explained

[7] The difference between a tag on the branch and a branch tag is significant. A branch tag identifies the branch itself, whereas a tag on the branch behaves and is used like any other tag.

[8] Web-based Distributed Authoring and Versioning. Extensions to the HTTP protocol being managed by three working groups of the Internet Engineering Task Force (IETF), which is the closest thing we have to an Internet standards body. WebDAV is an project which even enjoys the support of Microsoft! Let’s hope it won’t fall prey to their infamous ‘embrace and extend’ tactics…

Copyright © 2001-2003, The Excellent Programming Company