Chapter 14. CVS and RCS
CVS, and the older RCS, offer version control (or revision control), the practice of maintaining information about a project's evolution so that prior versions may be retrieved, changes tracked, and, most importantly, the efforts of a team of developers coordinated.
14.1. Basic Concepts
RCS (Revision Control System) works within a single directory. To accommodate large projects using a hierarchy of several directories, CVS creates two new concepts called the repository and the sandbox.
The repository (also called an archive) is the centralized storage area, managed by the version control system and the repository administrator, which stores the projects' files. The repository contains information required to reconstruct historical versions of the files in a project. An administrator sets up and controls the repository using the procedures and commands later in Section 14.3, "CVS Administrator Reference".
A sandbox (also called a working directory) contains copies of versions of files from the repository. New development occurs in sandboxes, and any number of sandboxes may be created from a single repository. The sandboxes are independent of one another and may contain files from different stages of the development of the same project. Users set up and control sandboxes using the procedures and commands found in Section 14.4, "CVS User Reference", later in this chapter.
In a typical interaction with the version control system, a developer checks out the most current code from the repository, makes changes, tests the results, and then commits those changes back to the repository when they are deemed satisfactory.
14.1.1. Locking and Merging
Some systems, including RCS, use a locking model to coordinate the efforts of multiple developers by serializing file modifications. Before making changes to a file, a developer must not only obtain a copy of it, but he must also request and obtain a lock on it from the system. This lock serves to prevent (really dissuade) multiple developers from working on the same file at the same time. When the changes are committed, the developer unlocks the file, permitting other developers to gain access to it.
The locking model is pessimistic: it assumes that conflicts must be avoided. Serialization of file modifications through locks prevents conflicts. But it is cumbersome to have to lock files for editing when bug-hunting. Often, developers will circumvent the lock mechanism to keep working, which is an invitation to trouble.
Unlike RCS and SCCS, CVS uses a merging model which allows everyone to have access to the files at all times and supports concurrent development. The merging model is optimistic: it assumes that conflicts are not common and that when they do occur, it usually isn't difficult to resolve them.
CVS is capable of operating under a locking model via the -L and -l options to the admin command. Also, CVS has special commands (edit and watch) for those who want additional development coordination support. CVS uses locks internally to prevent corruption when multiple people are accessing the repository simultaneously, but this is different from the user-visible locks of the locking model discussed here.
14.1.2. Conflicts and Merging
In the event that two developers commit changes to the same version of a file, CVS automatically defers the commit of the second committer's file. The second developer then issues the cvs update command, which merges the first developer's changes into the local file. In many cases, the changes will be in different areas of the file, and the merge is successful. However, if both developers have made changes to the same area of the file, the second to commit will have to resolve the conflict. This involves examination of the problematic area(s) of the file and selection among the multiple versions or making changes that resolve the conflict.
CVS only detects textual conflicts, but conflict resolution is concerned with keeping the project as a whole logically consistent. Therefore, conflict resolution sometimes involves changing files other than the one about which CVS complained.
For example, if one developer adds a parameter to a function definition, it may be necessary for all the calls to that function to be modified to pass the additional parameter. This is a logical conflict, so its detection and resolution is the job of the developers (with support from tools like compilers and debuggers); CVS won't notice the problem.
In any merge situation, whether or not there was a conflict, the second developer to commit will often want to retest the resulting version of the project because it has changed since the original commit. Once it passes, the developer will need to recommit the file.
CVS tracks file versions by revision number, which can be used to retrieve a particular revision from the repository. In addition, it is possible to create symbolic tags so that a group of files (or an entire project) can be referred to by a single identifier even when the revision numbers of the files are not the same (which is most often the case). This capability is often used to keep track of released versions or other important project milestones.
For example, the symbolic tag hello-1_0 might refer to revision number 1.3 of hello.c and revision number 1.1 of Makefile (symbolic tags are created with the tag and rtag commands).
The simplest form of development is linear, in which there is a succession of revisions to a file, each derived from the prior revision. Many projects can get by with a completely linear development process, but larger projects (as measured by number of files, number of developers, and/or the size of the user community) often run into maintenance issues that require additional capabilities. Sometimes, it is desirable to do some speculative development while the main line of development continues uninterrupted. Other times, bugs in the currently released version must be fixed while work on the next version is underway. In both of these cases, the solution is to create a branch (fork) from an appropriate point in the development of the project. If at a future point some or all of the changes on the branch are needed back on the main line of development (or elsewhere), they can be merged in (joined).
Branches are forked with the tag -b command; they are joined with the update -j command.
Copyright © 2001 O'Reilly & Associates. All rights reserved.