oreilly.comSafari Books Online.Conferences.


Essential CVS

Running Arbitrary Scripts Under CVS

by Jennifer Vesperman, author of Essential CVS

CVS is a useful version control tool. Version control is not the only aspect of building a project or maintaining a service, though. This article is about the hooks CVS includes to allow you to expand it and integrate it with other tools.

Five files in the CVSROOT directory of the repository enable you to run arbitrary scripts when a file is committed or tagged. Common uses for these files include interfacing CVS to a bug management system, a change tracker, or another tool; enforcing compliance with a project policy; and triggering processes such as automated export programs.

CVS uses one file during tagging and four files during committing:

Related Reading

Essential CVS
By Jennifer Vesperman

  • commitinfo before the files in a directory are committed.
  • loginfo after the files in a directory are committed.
  • verifymsg after the log message is entered, but before files are committed.
  • rcsinfo for the template for the log message in a commit.
  • taginfo before each tag or rtag operation.


Each line of these files should contain a rule with the following syntax: name_pattern action. name_pattern should match a directory in the repository, and CVS will apply this rule to all files in that directory. The pattern used is a regular expression, described in info cvs. CVS tests the pattern against the directory's relative path, starting at the repository root directory.

There are two special patterns, ALL and DEFAULT. An ALL rule is used on files in any directory. A DEFAULT rule is used if no other patterns (excluding ALL) match.

action is a command-line template for the path to a script (or commit message template), plus any parameters required for that script or template. The script or template must be able to read from standard input (stdin). CVS will append its own parameters to actions.

Lines in scripting files that start with the hash symbol (#) are comments and are ignored.


commitinfo scripts are often used to verify whether checked-in files conform to company standards or to enforce exclusive development. CVS passes the relative directory path and a list of all files in that directory involved in the commit as parameters. If the action of the rule returns a non-zero value, the commit does not proceed.

CVS makes a set of normal-format files from the data it receives from the CVS client, storing them in a temporary directory. If the action reads (or tries to modify) the file, it reads the file in the temporary directory. Any modifications are discarded after the action is processed.


The loginfo file is most often used to control where (aside from the RCS files) the log information from a commit is sent. It can also be used to trigger actions, such as notifying all developers of changes, maintaining a file that logs a record of changes, or exporting the changed files to a file server.

CVS passes the log message from the commit as well as the repository path being committed to, followed by an expanded format string, all on standard input. The format string is part of the action and controls the information that CVS passes to the script or command. The string consists of a % followed by a space, a single variable, or a set of variables enclosed in braces {}. The variables are:

Expands to the name of the current file being processed.
Expands to the file's revision number prior to the commit.
Expands to the file's revision number after the commit.

When CVS process the format string, the output is in the form variable_expansion [variable_expansion...]. CVS will generate a variable expansion for each file that was committed, consisting of a comma-separated set of strings. %{sV} would be expanded to a string like main.c,1.3. Dollar signs ($), backticks (`), backslashes (\), and quotation marks (") in the repository path or filename will be escaped with backslashes.


The scripts in the verifymsg file often either check or modify the log message you enter when you commit files. CVS passes the path to a temporary file containing the commit log message and copies the log message on standard input (stdin). If the script returns a non-zero value, the commit does not proceed.

The script can modify the log message. The RereadLogAfterVerify option in the config file in the repository's CVSROOT directory determines whether the original or the modified log message is used.

The ALL pattern is not supported in the verifymsg file.


The rcsinfo file doesn't contain scripts to run, it contains the path to a template file for CVS to display when it opens the editor for a log message. Unless you remove them in editing, the contents of the template file are stored as part of the log message in addition to whatever you add. Lines that start with CVS: will be stripped by CVS.

If CVS is running in client/server mode, it stores a copy of the template file in the Template file in the CVS subdirectory of the sandbox when the sandbox is checked out. This file is not updated with other sandbox files, so if the rcsinfo file or the templates it refers to are changed, your users should release and recreate their sandboxes.


taginfo scripts are often used to check that tagnames meet standards, to interact with other programs, or to log tag operations. CVS appends a parameter string in the following format: tagname operation repository_path file_revision_pair [file_revision_pair...]. The file_revision_pair is a space-separated pair of filenames and revision numbers, and there is a pair for each file to be tagged.

The operations CVS provides are add, mov, and del. CVS provides mov when the tag moving option -F is used, del when the tag deletion option -d is used, and add when a new tag is added. If the script exits with a non-zero exit status, the tag (or rtag) operation will not proceed.


CVS processes the scripting files separately for each directory in the current commit or tag command, calling the script in the scripting file once for each directory it matches. It runs the first rule that matches, or the DEFAULT rule (if any) if there are no matching rules. It also runs any ALL rules.

If any script contains a CVS command, the command that calls the script will not finish until the script does. This can trigger a deadlock if a directory has been locked by the original CVS command, which can't release the lock until the script is finished, but the script can't run until those same locks are released. The deadlock situation can be prevented by having your script call any CVS commands in the background or simply by calling the script in the background.

If the repository is not on the same computer as the client, the script or command in the action is run on the repository computer.


Scripts in these files are run by the user calling CVS, not necessarily the user who added the script. Ensure that only trustworthy users have write access to the CVSROOT directory and the scripting files.

Note: all users must have read and execute access to the CVSROOT directory and write access to the history and val-tags files.

Integration Example: Bugzilla

Bugzilla can accept bug-tracking data via an email form. This integration example relies on it being configured to do so.

Use the rcsinfo file to make a template with the Bugzilla @bugid, @resolution, and @message fields, and the verifymsg file to run a script that ensures that the bug ID, at least, has been filled in. In this example, the verifymsg file then emails the log message to Bugzilla. Bugzilla needs the bug ID in the email subject (which must have the format [Bug XXXX]). The rest of the log message can be sent as the email body.

The script below uses the read command to get the bug ID from the first line of the log message then mails the bug ID and the rest of the input to the user bugzilla on the same computer. The script returns 1 if the format was wrong, which cancels the commit.

#! /bin/bash
read prompt bugid
if [ $prompt != '@bugid' ]; then
	return 1
	mail -s "[Bug $bugid]" bugzilla
return 0

This is a template file for rcsinfo to call. Your users should enter the bug ID, resolution status, and a log message to the right of each of these prompts.

CVS: Put the bug ID here
CVS: Put the resolution string here. Valid resolutions are...
CVS: Record any message you want to add here.

The next example shows the lines in the verifymsg and rcsinfo files that would call these scripts for a project called example, if the scripts were called bugzilla-mail and bugzilla-template.

# In verifymsg, call the script bugzilla-mail
^example\(/\|$\) /var/lib/cvsroot/CVSROOT/scripts/bugzilla-mail

# In rcsinfo, call bugzilla-template
^example\(/\|$\) /var/lib/cvsroot/CVSROOT/scripts/bugzilla-template

Final Words

CVS follows the general Unix ethos of doing one thing and doing it well. These files can allow you expand CVS beyond source and version control, or to integrate CVS with other programs.

Further Reading

Jennifer Vesperman is the author of Essential CVS. She writes for the O'Reilly Network, the Linux Documentation Project, and occasionally Linux.Com.

Return to the Linux DevCenter

Linux Online Certification

Linux/Unix System Administration Certificate Series
Linux/Unix System Administration Certificate Series — This course series targets both beginning and intermediate Linux/Unix users who want to acquire advanced system administration skills, and to back those skills up with a Certificate from the University of Illinois Office of Continuing Education.

Enroll today!

Linux Resources
  • Linux Online
  • The Linux FAQ
  • Linux Kernel Archives
  • Kernel Traffic

  • Sponsored by: