LINUX GAZETTE

"Linux Gazette...making Linux just a little more fun!"


CVS: Client-Server Version Control

By Kapil Sharma


  1. Overview
  2. Getting CVS
  3. The repository
  4. Multiple Developers
  5. Rolling back to previous version
  6. Some common CVS commands
  7. Other tools and add-ons to CVS
  8. More information

Overview

CVS is a version control system. Using it, you can record the history of your source files. CVS helps if you are part of a group of people working on the same project, sharing the same code. Several developers can work on the same project remotely using CVS's client-server model in which the code exists on a central server and each programmer get the source on his local machine from the CVS server (checkout) and save it back on the CVS server (checkin) after development. Each time a programmer checks in his new code into the CVS server, the difference is saved as a new version rather than overwriting the previous version. This allows the server to recreate any previous version upon request, although by default it distributes the latest version.

This article explains how to use CVS in client-server mode and get the most out of it.

Getting CVS

You can find CVS in your Linux distribution or get the source from http://www.cvshome.org/downloads.html

The home page for CVS is http://www.cvshome.org.

The repository

The CVS repository stores a complete copy of all the files and directories which are under version control. Normally, you never access any of the files in the repository directly. Instead, you use CVS commands to get your own copy of the files into a working directory, and then work on that copy. When you've finished a set of changes, you check (or commit) them back into the repository. The repository then contains the changes which you have made, as well as recording exactly what you changed, when you changed it, and other such information.

Creating a Repository
To create a repository, run the CVS init command. It will set up an empty repository in the CVS root specified in the usual way .

 
cvs -d /usr/local/cvsroot init

Here /usr/local/cvsroot will become the repository.

CVSROOT environment variable

Set the CVSROOT environment variable in your shell startup script. For instance, in ~/.bashrc:

 
$ export CVSROOT=:pserver:username@foo.com:/usr/local/cvsroot 

Backing up the Repository
There are a few issues to consider when backing up the repository:

Remote Repositories
Your working copy of the sources can be on a different machine than the repository. Using CVS in this manner is known as client/server operation.

Setting up the server:
Put the following entry in /etc/inted.conf on server:

 
2401 stream tcp nowait root /usr/local/bin/cvs cvs -f --allow-root=/usr/cvsroot pserver

If your inetd wants a symbolic service name instead of a raw port number, then put this in `/etc/services':

 
cvspserver     2401/tcp


and put cvspserver instead of 2401 in `inetd.conf'.
After making you changes, send a HUP signal to inetd.

Password authentication for remote repository
For remote password authentication put a file `$CVSROOT/CVSROOT/passwd' . The file will look like:

 
anonymous:
kapil:1sOp854gDF3DY
melissa:tGX1fS8sun6rY:pubcvs

The password is in Unix encrypted form. The first line in the example will grant access to any CVS client attempting to authenticate as user anonymous, no matter what password they use. The second and third lines will grant access to kapil if he supplies his respective plaintext passwords.

The third will grant access to melissa if she supplies the correct password, but her CVS operations will actually run on the server side under the system user pubcvs.

Note: CVS can be configured not to check the UNIX real passwd file i.e /etc/passwd for CVS authentication by setting SystemAuth=no in the CVS `config' file ($CVSROOT/CVSROOT/config).

Using the client with password authentication

You have to login to CVS server for the first time:

 
  cvs -d :pserver:kapil@foo.com:/usr/local/cvsroot login

The you can use all the commands of CVS on the remote machine:

 
  cvs -d :pserver:kapil@foo.com:/usr/local/cvsroot checkout someproj

Read only repository access
It is possible to grant read-only repository access to people using the password-authenticated server. There are two ways to specify read-only access for a user: by inclusion, and by exclusion.
"Inclusion" means listing the user in the `$CVSROOT/CVSROOT/readers' file, which is simply a newline-separated list of users. Here is a sample `readers' file:

 
kapil
yogesh
john

(Don't forget the newline after the last user.)

"Exclusion" means listing everyone who should have write access. If the file    $CVSROOT/CVSROOT/writers exists, then only those users listed in it will have write access, and everyone else will have read-only access. The `writers' file has the same format as the `readers' file.

Setting up the files in repository
If the files you want to install in CVS reside in `someproj', and you want them to appear in the repository as `$CVSROOT/someproj', you can do this:

 
$ cd someproj
$ cvs import -m "Imported sources" someproj vendor 
  rel1-1

Here The string `vendor' is a vendor tag, and `rel1-1' is a release tag.

CVS locks in repository
Any file in the repository with a name starting with `#cvs.rfl.' is a read lock. Any file in the repository with a name starting with `#cvs.wfl' is a write lock. The directory `#cvs.lock' serves as a master lock. That means one must obtain this lock first before creating any of the other locks.

To obtain a read lock, first create the `#cvs.lock' directory. If it fails because the directory already existed, wait for a while and try again. After obtaining the `#cvs.lock' lock, create a file whose name is `#cvs.rfl.' followed by information of your choice (for example, hostname and process identification number). Then remove the `#cvs.lock' directory to release the master lock. Then proceed with reading the repository. When you are done, remove the `#cvs.rfl' file to release the read lock.

To obtain a write lock, first create the `#cvs.lock' directory, as with a read lock. Then check that there are no files whose names start with `#cvs.rfl.'. If there are, remove `#cvs.lock', wait for a while, and try again. If there are no readers, then create a file whose name is `#cvs.wfl' followed by information of your choice (for example, hostname and process identification number). Hang on to the `#cvs.lock' lock. Proceed with writing the repository. When you are done, first remove the `#cvs.wfl' file and then the `#cvs.lock' directory.

Symbolic revisions using tags in CVS
The release number of final software releases are different from revisions in CVS. The revision numbers might change for several times between two releases.You can use the tag@lists.linuxgazette.netmand to give a symbolic name to a certain revision of a file.

Change to the working directory and issue the following command for tagging:

 
$ cvs tag rel1-1 file.c

This command will tag the file "file.c" as release 1.1

 
$ cvs tag rel1-1 .

This command will tag all the files under current directory recursively as revision 1.1

You can use the `-v' flag to the status command to see all tags that a file has, and which revision numbers they represent by issuing the following command:

 
$ cvs status -v file.c

Now you can checkout any revision of a module by using the following command:

 
$ cvs checkout -r rel1-1 module1

here "module1" is the name of the module. The -r flag with checkout option makes it easy to retrieve the sources that make up revision 1.1 of the module `module1' at any time in the future.

Multiple Developers

File status
The cvs status command gives a status about the states of the files. You can get a status of the file by:

 
$ cvs status [options] files

Bringing a file up to date
When you want to update or merge a file, use the update command. This brings into your working copy the changes others have recently committed. Your modifications to a file are never lost when you use update. If no newer revision exists, running update has no effect. If you have edited the file, and a newer revision is available, CVS will merge all changes into your working copy.

Resolving Conflicts

If two people simultaneously make changes to different parts of the same file, CVS is smart enough to merge the changes itself. But if two people make changes to the same part of a file, CVS cannot tell what the final result is supposed to be, so it gives up and wines, "Conflict!" Conflicts arise when one developer commits a change and a second developer, without running cvs update to receive the first developer's change, tries to commit his own incompatible change. Resolving changes can take hours or even days. In this section, I will explain how to resolve source conflicts.

When you enter the cvs commit command to automatically upload all the files you have changed or added to a project, the CVS repository server may inform you that your locally-edited files are not up-to-date with the server or that you need to manually merge one or more files with newer versions that have already been uploaded to the repository by some other developer. Here's a typical warning message that occurred during a CVS commit process:

 
$ cvs commit
cvs commit: Examining .
cvs commit: Up-to-date check failed for `andy.htm' 
cvs commit: Up-to-date check failed for `sample.htm' 
cvs commit: Up-to-date check failed for `index.htm' 
...
cvs [commit aborted]: correct above errors first!

You can use the cvs update command to update your local project copy with the latest changes in the cvs repository. To update your entire working copy of the site, open a command prompt, change to the directory containing the project you're developing, and issue the command:

 
$ cvs update

This will update and automatically merge every file that has changed since you last copied over new files from the CVS repository. Line-by-line updates to individual text files (such as HTML files) can often be handled automatically. CVS will list for you any files that require your attention for manual editing and merging.

Automatic merge example:
You are editing some project file called "index.html" locally and when you try to commit that file to CVS repository then CVS will give you the following error:

 
$ cvs commit index.html
   cvs commit: Up-to-date check failed for `index.html' 
   cvs [commit aborted]: correct above errors first!

This happens because there is a newer version of the same file on the CVS repository. You should use cvs update command to get the latest version from the CVS repository onto your local machine:

 
$ cvs update index.html
RCS file: /usr/local/cvsroot/index.html,v
retrieving revision 1.4
retrieving revision 1.5
Merging differences between 1.4 and 1.5 into index.html 
M index.htm

After the automatic merge process you should check the merged copy to check if it is working properly. When you are satisfied with the local copy of "index.html" file then you can commit it to CVS:

 
$ cvs commit index.htm
Checking in index.htm;
/usr/local/cvsroot/index.htm,v <-- index.htm
new revision: 1.6; previous revision: 1.5 
done

Manual merge example:
In some cases, your recent work on a file might be so different that the CVS needs your manual intervention in order to integrate everyone's work and put it back into the site repository.

 
$ cvs commit index.html cvs commit: Up-to-date check failed for
     `index.html' cvs [commit aborted]: correct above errors first!

Use the cvs update command to bring your local copy of the site up to date:

 
$ cvs update
cvs update: Updating .
RCS file: /usr/local/cvsroot/index.html,v
retrieving revision 1.5
retrieving revision 1.6
Merging differences between 1.5 and 1.6 into index.htm
rcsmerge: warning: conflicts during merge
cvs update: conflicts found in activity.htm
C index.htm

This time CVS was unable to merge the files automatically, so it created a special copy of the conflicting file in place of the original index.html. The file has marker lines to indicate the beginning and end of conflictiong region(s); e.g.,

 
  <<<<<<<< filename

To resolve the conflict, simply edit the index.html file and replace the text between the markers and test the result until it works. You should also delete the markers

 
<<<<<<<<========>>>>>>>> 

from the file. When you have finished correcting the file and have tested it, use the cvs commit command to put your latest copy of file into the repository:

 
$ cvs commit
Checking in index.html;
/usr/local/cvsroot/index.html,v <-- index.html
new revision: 1.7; previous revision: 1.6 
done

Watches (CVS communication)
CVS can function as a communication device as well as a record-keeper.  A "watches" feature provides multiple developers working on the same project with a way to notify each other about who is working on what files at a given time. By "setting a watch" on a file/directory , a developer can have CVS notify her if anyone else starts to work on that file by means of sending e-mail or some other method.

To use watches you have to edit two files in the repository administrative area. You have to edit the "$CVSROOT/CVSROOT/notify" file (which tells CVS how notifications are to be performed) and "$CVSROOT/CVSROOT/users" file(which supplies external e-mail addresses). The best way to modify administrative files is to checkout one copy from the repository ,edit them and then check in to repository .

To specify e-mail notification, first uncomment the following line from "$CVSROOT/CVSROOT/notify" file:

 
ALL mail %s -s "CVS notification"

This command causes notifications to be sent as e-mail with the subject line "CVS notification".

Then you have to create/edit the file "$CVSROOT/CVSROOT/users" . The format of each line in the users file is: CVS_USERNAME:EMAIL_ADDRESS. For example:

 
kapil:kapil@linux4biz.net

The CVS username at the beginning of the line corresponds to a CVS username in CVSROOT/password, or the server-side system username of the person running CVS. Following the colon is an external e-mail address to which CVS should send watch notifications for that user.

E-mail notification with logfile
CVS provides a feature of sending automated e-mail to everyone working on a project  with a log message whenever a commit takes place. The program to do the mailing - contrib/log.pl in the CVS source distribution - can be installed anywhere on your system. You can also install it into "$CVSROOT/CVSROOT". You should change the following line in log.pl :

 
$mailcmd = "| Mail -s 'CVS update: $modulepath'"; 

Once you've setup the log.pl , you can put lines similar to these into your “loginfo” file. The `loginfo' file is used to control where `cvs commit' log information is sent. You can find it in "$CVSROOT/CVSROOT".

 
projectteam1 CVSROOT/log.pl %s -f CVSROOT/commitlog -m projectteam1@linux4biz.net
projectteam2  CVSROOT/log.pl %s -f CVSROOT/commitlog -m projectteam2@linux4biz.net

The %s expands to the names of the files being committed; the -f option to log.pl takes a file name, to which the log message will be appended (so CVSROOT/commitlog is an ever-growing file of log messages); and the -m flag takes an e-mail address, to which log.pl will send a message about the commit. The address is usually a mailing list, but you can specify the -m option as
many times as necessary in one log.pl command line.

Some commands related to setting up watches on files:

If you only want to be notified about, say, commits, you can restrict notifications by adjusting your watch with the -a flag (a for action):

 
$ cvs watch add -a commit hello.c

Or if you want to watch edits and commits but don't care about unedits, you could pass the -a flag twice:

 
$ cvs watch add -a edit -a commit hello.c

Adding a watch with the -a flag will never cause any of your existing watches to be removed. If you were watching for all three kinds of actions on hello.c, running

 
$ cvs watch add -a commit hello.c

has no effect - you'll still be a watcher for all three actions.

To remove watches, run:

 
$ cvs watch remove hello.c

which is similar to add in that, by default, it removes your watches for all three actions. If you pass -a arguments, it removes only the watches you specify:

 
$ cvs watch remove -a commit hello.c

To find out who is watching files, run cvs watchers:

 
$cvs watchers
$cvs watchers hello.c

To find out who is editing files, run cvs editors:

 
$cvs editors
$cvs editors hello.c

Note: It is necessary to run "cvs edit" before editing any file to be able to watch feature working. To make sure you do, CVS has a feature to remind the someone to use cvs edit with the help of  the watch on command:

 
$ cd project
$ cvs watch on hello.c

By running cvs watch on hello.c, kapil causes future checkouts of project to create hello.c read-only in the working copy. When someone else tries to work on it, he'll discover that it's read-only and be reminded to run cvs edit first.

 
$cvs edit hello.c

Rolling back to previous version

Sometimes you need to revert back to previous version of your  project.  A project under CVS version control can quickly and conveniently revert to an earlier stage of its life. I will explain some of the common examples:

 
$ cvs checkout -D '1 year ago' preproject

Here preproject is the name of the project.

 
$ cvs checkout -r1.4 preproject

1.4 is CVS's revision number for that version.

Some common CVS commands

Some common terms:
Import:  This means taking an existing directory tree and copying it into the CVS repository, creating a new CVS project.
Commit: Apply all your changes to the CVS repository. Each changed file will be assigned a new CVS version.
Checkout: Get the working copy of files from cvs repository into the local directory.
Export: export is same as checkout. The only difference is that export does not copy out the CVS administrative directories, so you cannot run CVS commands in the resulting tree. On the other hand, this is how you create your "final" copy for distribution.
Upload: General term for Import or commit.
Download: General term for checkout or export.
Checkin: General term, same as commit.

Adding a file to the CVS repository "My_Files"

 
$ cvs add File3.txt
$ cvs commit

cvs add does not upload the file right away, but registers it to be uploaded at the next commit.

 

This invokes your default text editor and prompts you to enter a description of your changes. Save the file and quit the editor. CVS will then ask you to continue, and select the option to continue. Now you have uploaded a file to the CVS repository "My_Files".

Changing a file to the CVS repository "My_Files"

 This can be done with cvs commit command. Let us add some content to the file File2.txt and then commit it to the cvs repository.

$ ls /var >> File2.txt
$ cvs commit

Removing files
To remove files from a site, you run the cvs remove command on the desired filenames in your working copy. As a ``safeguard'', cvs remove will not work if the working copies of your files still exist.
Syntax:  $ cvs remove [options] files

$ cvs remove file.html
cvs server: file `file.html' still in working directory
cvs server: 1 file exists; remove it first
$

To get around this, you may use the -f option with the cvs remove command or remove the file first and then execute the cvs remove command.

$ cvs remove -f oldfile.html
cvs server: scheduling `oldfile.html' for removal
cvs server: use 'cvs commit' to remove this file permanently
$ cvs commit
 
Or
$ rm File3.txt
$ cvs remove File3.txt
$ cvs commit
 

This will not delete the actual file from the CVS server yet; it simply makes a note to tell the server to remove these files the next time you commit your working copy of the project.
 

Removing directories
The way that you remove a directory is to remove all the files in it. You don't remove the directory itself: there's no way to do that. Instead you specify the `-P' option to cvs update or cvs checkout, which will cause CVS to remove empty directories from working directories. (Note that cvs export always removes empty directories.) Note that `-P' is implied by the `-r' or `-D' options of checkout. This way CVS will be able to correctly create the directory or not depending on whether the particular version
you are checking out contains any files in that directory.

Creating the directory structure from number of files in the CVS repository
This cvs import command is used to put several projects in cvs repository.

$ cd source 
here source is the files that you want to put in cvs repository.

$ cvs import -m "Test Import" My_Files Revision1 start
The string ‘Revision1’ is a vendor tag, and `start' is a release tag. “My_Files” is the name of directory in cvs repository. The –m option is to put log message.

Get the working copy of files from CVS

Okay, now we want to download these files into a Working directory.
When we checkout a package from cvs, it will create a directory for us. The parameter "My_Files" that we specified when we uploaded the files into cvs will be the name of
the directory created for us when cvs downloads the package for us.

Now we need to get the cvs package.

 
$ cvs checkout My_Files

Downloading updates that other people make

If you have downloaded a package from a repository that someone else is maintaining, if you wish to download all the changes, then execute the following command,

 
$ cvs update -dP

The "d" creates any directories that are or are missing. The "P" removes any directories that were deleted from the repository.

Viewing the difference
You can easily see the difference between two file using cvs.

 
$ cd project
$ cvs diff index.html

This command runs diff to compare the version of `index.html' that you checked out with your working copy.  Here "project" is the name of the local project directory.

 
$ cvs diff -r 1.20 -r 1.21 hello.c

This command will show the difference between two versions of same file.

The annotate Command
With annotate, you can see who was the last person to touch each line of a file, and at what revision they touched it. It gives you more information than the history command:

 
$cvs annotate

View logs

 
$ cvs log -r 1.21 hello.c

This will show you the logs for hello.c version 1.21

Other tools and add-ons to CVS

Henner Zeller's CVSweb 
It has a feature of browsing the CVS repository by web browser and even shows the latest revision and log message for each file. It presents you with a web-based interface to browse any and all of the sites and projects you manage by  CVS. You can get it from http://stud.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi/

Martin Cleaver's CVSweb
 It features capabilities for file upload as well as file browsing of CVS repository. You can get this software from http://sourceforge.net/projects/cvswebclient/

LinCVS
A CVS GUI client for Linux. It provides nice features and easy to use. You can get it from: http://www.lincvs.org/

WinCVS
A CVS GUI client for Windows. It has many good features and I will recommend this software for Windows clients. You can get it from http://www.cvsgui.org/download.html

More Information

CVS Manual : http://www.cvshome.org/docs/manual/cvs.html

CVS Mailing lists: http://www.cvshome.org/communication.html

 

Kapil Sharma

Kapil is a Linux/Unix and Internet security consultant. He has been working on various Linux/Unix systems and Internet Security for over three years. He maintains a web site (http://linux4biz.net) for providing free as well as commercial support for web, Linux and Unix solutions.


Copyright © 2001, Kapil Sharma.
Copying license http://www.linuxgazette.net/copying.html
Published in Issue 66 of Linux Gazette, May 2001