Download
paper, slides & reference material (599 kB)
Document Version Control with CVS
by Paul Spain, The
Excellent Programming Company, Melbourne, Australia.
Contact: paul@xpro.com.au Internet: http://www.xpro.com.au/
Summary
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: http://wwwsel.iit.nrc.ca/favs/CMfavs.html.
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 http://www.cvshome.org/, 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 http://www.wincvs.org.
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.
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:
:local:c:\cvs
:pserver:pvspain@cvs.tigris.org:/cvs
:ext:paul@xpro.com.au:/usr/local/cvs
This is parameterised as :
:method:[user@host:]/path/to/repository
: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.
|
Command
|
Description
|
|
Repository and workspace management
|
|
init
|
Create a new repository
|
|
checkout
|
Create a new workspace.
Pass the name of the project to checkout as a command option
|
|
export
|
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
|
|
release
|
Removes a workspace.
Checks (fails) for uncommitted changes, and updates repository
history. Invoke from workspace’s parent directory.
|
|
Project management
|
|
import
|
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
|
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.
|
|
remove
|
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
|
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
|
Commit changed file(s)
from the workspace to the repository. Also used to action
add and remove commands.
|
|
Version labelling and branching
|
|
tag
|
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.
|
|
rtag
|
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
|
|
diff
|
Show the difference
between two versions in Unix diff format
|
|
rdiff
|
Similar to diff, based solely on repository
versions - no workspace required
|
|
Change review / Audit trail
|
|
log
|
Display files(s)
log messages
|
|
status
|
Display status information
for file(s)
|
|
annotate
|
Line-by-line version
information for specified version of file(s)
|
|
history
|
Display repository
activity log
|
|
File locking
|
|
admin
|
Most commonly used
for exclusive file access - locking/unlocking file(s). Also
used to modify log messages in repository.
|
|
File activity notifications - ‘Watch’ facility
|
|
watch
|
Administers a ‘watch’
on a file
|
|
edit
|
Signals start of
editing session for a ‘watched’ file
|
|
unedit
|
Signals end of editing
session for a ‘watched’ file
|
|
editors
|
Displays current
editors of a ‘watched’ file.
|
|
watchers
|
Display current watchers
of a file
|
|
Pserver connections
|
|
login
|
Confirms and caches
pserver password for a repository
|
|
logout
|
Removes cached pserver
password
|
|
Server mode invocations
|
|
pserver
|
Invokes CVS in pserver
mode. Not a user command.
|
|
server
|
Invokes CVS in server
mode. Not a user command.
|
|
Software version
|
|
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.
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 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, 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. 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 - 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. 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.
Branching
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:
- 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.
|
|
- 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.
- 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 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
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 (http://subversion.tigris.org/), 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 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…
Resources
- CVS home page: http://www.cvshome.org/
- 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 : http://www.cvshome.org/docs/manual/
- WinCVS home and links for other GUI interfaces and
NT port of server: http://www.wincvs.org/
- 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: http://cvsbook.red-bean.com/. 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
- CVS mailing-lists: http://www.cvshome.org/communication.html
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: http://mail.gnu.org/mailman/listinfo/info-cvs
- Secure Shell (SSH) resources, including software:
http://www.cs.toronto.edu/~djast/ssh.html
Note: To use SSH with CVS, you’ll need a command line
version of SSH, not a terminal emulator which supports
SSH sessions.
- Subversion: The next generation of CVS:
Project home page: http://subversion.tigris.org/
Goals; comparison with CVS: http://www.tigris.org/files/documents/15/48/svn-design.html
- 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.