The concurrent versions system (CVS) is a version control system that allows old version of files to be maintained (generally source code), saving a log of who made any changes, when and why. Unlike other systems, CVS does not work with a file/directory per occasion, but rather acts on hierarchical groups of the directories it controls.
The purpose of CVS is to help to manage software versions and to control the concurrent editing of source files by multiple authors. CVS uses another package called RCS (revision control system) internally as a low level layer. Although RCS can be used independently, it is not recommended, because in addition to its own functionality CVS offers all the capabilities of RCS but with notable improvements in terms of stability, functioning and maintenance. Among which we would highlight: decentralised operation (every user can have their own code tree), concurrent editing, adaptable behaviour through shell scripts etc. [Ced, CVS, Vasa, Kie]
As already explained in the introduction, Subversion (http://subversion.tigris.org/) is a version control system software specifically designed to replace the popular CVS, and to extend its capabilities. It is free software under an Apache/BSD type license and is also known as svn for the name on the command line. An important feature of Subversion is that unlike CVS the versioned files do not each have an independent revision number and instead the entire repository has a single version number which identifies a common status of all the repository's files at the time that it was "versioned". Among the main features we would mention:
File and directory history can be followed through backups and renamings.
Atomic and secure modifications (including changes to several files).
Efficient and simple creation of branches and labels.
Only the differences are sent in both directions (in CVS, complete files are always sent to the server).
It can be served by Apache, over WebDAV/DeltaV.
It handles binary files efficiently (unlike CVS, which treats them internally as if they were text).
There is an interesting free book that explains everything related to Subversion http://svnbook.red-bean.com/index.es.html and the translation is fairly advanced (http://svnbook.red-bean.com/nightly/es/index.html).
Given that CVS is based on RCS and is still used on some systems, we will offer a few brief explanations of this software. RCS consists of a set of programs for its different RCS activities: rcs (program that controls file attributes under RCS), ci and co (which verify the entry and exit of files under RCS control), ident (searches RCS for files using key words/attributes), rcsclean (cleans files that are not used or that have not changed), rcsdiff (runs the diff command to compare versions), rcsmerge (joins two branches [files] into a single file), and rlog (prints log messages).
The format of the files stored by RCS can be text or another format, like binary for example. An RCS file consists of an initial revision file called 1.1 and a series of files of changes, one for each revision. Every time a copy of the repository is made to the work directory with the co (which obtains a revision of every RCS file and puts it in the work file) or ci (which stores new revisions in the RCS) commands is used, the version number is increased (for example, 1.2, 1.3,...). The files are (generally) in the /RCS directory and the operating system needs to have the diff and diff3 commands installed in order for it to function properly. In Debian, it does not have to be compiled since it is included in the distribution.
With the rcs command we will create and modify file attributes (consult rcs man). The easiest way to create a repository is to create a directory with mkdir rcs in the directory of originals and include the originals in the repository using: ci name_files_sources.
We can use the * and should always have a backup copy to avoid problems. This will create the versions of the files with the name ./RCS/file_name and request a descriptive text for the file. Then, using co RCS/file_name, we will obtain a work copy from the repository. This file can be blocked or unblocked to prevent modifications, respectively, using:
rcs -L workfile_name rcs -U workfile_name
With rlog file_name we will be able to see the information on the different versions. [Kie]
First we need to install the concurrent versions system (CVS) from the distribution bearing in mind that we must have RCS installed and that we should also install OpenSSH if we wish to use it in conjunction with CVS for remote access. The environment variables EDITOR CVSROOT must also be initiated for example in /etc/profile (or in .bash profile):
export EDITOR = /bin/vi export CVSROOT = /usr/local/cvsroot
Obviously, users can modify these definitions using /.bash profile. We need to create the directory for the repository and to configure the permissions; as root, we have to type, for example:
export CVSROOT = /usr/local/cvsroot groupadd cvs useradd -g cvs -d $CVSROOT cvs mkdir $CVSROOT chgrp -R cvs $CVSROOT chmod o-rwx $CVSROOT chmod ug+rwx $CVSROOT
To start up the repository and save the code file in it:
cvs -d /usr/local/cvsroot init
cvs init will take into account never overwriting an already created repository to avoid the loss of other repositories. Next, we will need to add the users that will work with CVS to the cvs group; for example, to add a user called nteum:
usermod -G cvs,nteum
Now user nteum will have to save his or her files in the repository directory (/usr/local/cvsroot in our case) by typing:
export EDITOR = /bin/vi export CVSROOT = /usr/local/cvsroot export CVSREAD = yes cd directory_of_originals cvs import RepositoryName vendor_1_0 rev_1_0
The name of the repository can be a single identifier or also user/project/xxxx if the user wishes to have all their repositories organised. This will create a tree of directories in CVSROOT with that structure.
This adds a directory (/usr/local/cvsroot/RepositoryName) in the repository with the files that will be in the repository as of that moment. A test to know whether everything has been stored correctly is to save a copy in the repository and then create a copy from there and check the difference. For example, with the originals in user_directory /dir_org if we want to create a repository first_cvs/proj, we will have to execute the following commands:
cd dir_org Change to the original source code directory.
|cvs import -m \ "Original sources"|
|\primer_cvs/proj userX vers0|
|Creates the repository in first_cvs/proj with userX and vers0.|
cd.. Change to the superior directory dir_org.
|cvs checkout primer_cvs/proj|
|Generating a copy of the repository. The variable CVSROOT|
|mustbe initiated, otherwise the full path will have to be shown.|
|diff -r dir_org primer_cvs/proj|
|Shows the differences between one and the other; there|
|should not be any except for the directory first_cvs/proj/CVS|
|created by CVS.|
|rm -r dir_org|
|Deletes the originals (always make a backup copy for safety|
|and to have a reference of where the work with CVS started).|
The following figure shows the organisation and how the files are distributed between versions and branches.
Deleting the originals is not always a good idea; only in this case, after verifying that they are in the repository, so that they will not be worked on by mistake and the changes will not be reflected on the CVS. On machines where users will want to access a remote CVS server (by ssh), we must type:
export CVSROOT = ":ext:user@CVS.server.com:/home/cvsroot" export CVS_RSH = "ssh"
Where user is the user login and cvs.server.com the name of the server with CVS. CVS offers a series of commands (named as cvs command options...) to work with the revision system, including: checkout, update, add, remove, commit and diff.
The initial cvs checkout command creates its own private copy of the source code so as to later work with it without interfering with the work of other users (at minimum it creates a subdirectory where the files will be located).
cvs update must be executed from the private tree when copies of source files have to be updated with the changes made by other programmers to the repository's files.
cvs add file... this command is necessary when we need to add new files to the work directory on a module that has already previously run a checkout. These files will be sent to the CVS repository when we execute the cvs commit command.
cvs import can be used for introducing new files to the repository.
cvs remove file... this command will be used to delete files from the repository (once these have been deleted from the private file). This command has to be accompanied by a cvs commit command for the changes to become effective, since this is the command that converts all of the users requests over the repository.
cvs diff file... it can be used without affecting any of the files involved if we need to verify differences between repository and work directory or between two versions.
cvs tag -R "version" can be used for introducing a version number in project files and then typing cvs commit and a cvs checkout -r 'version' project in order to register a new version.
An interesting characteristic of cvs is that it is able to isolate the changes to files isolated on a separate line of work called a branch. When we change a file on a branch, these changes do not appear on the main files or on other branches. Later, these changes can be incorporated to other branches or to the main file (merging). To create a new branch, use cvs tag -b rel-1-0-patches within the work directory, which will assign the name rel-1-0-patches to the branch. To join the branches to the work directory involves using the cvs update -j command. Consult references for merging or accessing different branches.
Following the example of the documentation provided in the references, we will show a work session (in a general form) with cvs. Since cvs saves all the files in a centralised repository, we will assume that it has already been initiated.
Let's suppose that we are working with a set of files in C and a makefile, for example. The compiler we use is gcc and the repository is initialised to gccrep.
In the first place, we will need to obtain a copy of the repository files as our own private copy with:
cvs checkout gccrep
This will create a new directory called gccrep with the source files. If we execute cd gccrep and ls, we will see, for example, cvs makefile a.c b.c c.c, where there is a cvs directory that is created to control the private copy that normally we do not need to touch.
We could use an editor to modify a.c and introduce substantial changes in the file (see the documentation on multiple concurrent users if we need to work with more than one user on the same file), compile, change again etc.
When we decide that we have a new version with all the changes made in a.c (or in the necessary files), it is time to make a new version by saving a.c (or all those that have been touched) in the repository and making this version available to the rest of the users: cvs commit a.c.
Using the editor defined in the variable CVSEDITOR (or EDITOR if it is not initialised) we will be able to enter a comment that discusses what changes have been made to help other users or to remind what characterised this version so that a log can be made.
If we decide to eliminate the files (because the project was completed or because it will not be worked on any more), one way of doing this is at the level of the operating system (rm -r gccrep), but it is better to use the cvs itself outside of the work directory (level immediately above): cvs release -d gccrep. The command will detect whether there is any file that has not been sent to the repository, and if there is and it is erased, it means that all the changes will be lost, which is why it will ask us if we wish to continue or not.
To look at the differences for example, b.c has been changed and we do not remember what changes were made, within the work directory, we can use: cvs diff b.c. This will use the operating system's diff command to compare version b.c with the version in the repository (we must always remember to type cvs commit b.c if we want these differences to be transferred to the repository as a new version).
When more than one person works on a software project with different revisions, it is extremely complicated, because more than one user will sometimes want to edit the same file simultaneously. A potential solution is to block the file or to use verification points (reserved checkouts), which will only allow one user to edit the same file simultaneously. To do so, we must execute the command cvs admin -l command (see man for the options).
cvs uses a default model of unreserved checkouts, which allows users to edit a file in their work directory simultaneously. The first one to transfer their changes to the repository will be able to do so without any problems, but the rest will receive an error message when they wish to perform the same task, meaning that they must use cvs commands in order to transfer firstly the changes to the work directory from the repository and then update the repository with their own changes.
Consult the references to see an example of its application and other ways of working concurrently with communication between users. [Vasa].
We have a set of graphic interfaces available such as tkcvs (http://www.twobarleycorns.net/tkcvs.html) [gcus] developed in Tcl/Tk and that supports subversion, or an also very popular one, cervisia [Cerc].
In the cvs wiki (http://ximbiot.com/cvs/wiki/index.php?title=CVS_Clients) we can also find a set of clients, plugins for cvs. Next, we will look at two of the mentioned graphic interfaces (tkcvs and gcvs):