CVS introduction

Marc Groenewegen (marcg@dinkum.nl)

September 7 2004

Document Information
Organisation Hogeschool voor de Kunsten Utrecht (HKU)
Version 0.2
Status proposal
Abstract:
This document provides an introduction into the use of the CVS version control system.

Table Of Contents

1 Introduction

2 References

3 Getting started with CVS

4 Binary files

5 Putting a project under CVS control

6 Example


1 Introduction

CVS stands for Concurrent Versions System. CVS is a source control system. It allows you to store and retrieve multiple versions or revisions of the same file or files. This makes it possible to trace back to the history of files, see the way they developed, who worked on them and see the reasoning behind important implementation decisions.

CVS helps in efficient use of disk space needed for the archive. You could of course save every version of every file you have ever created and waste an enormous amount of disk space. Instead, CVS stores all the versions of a file in a single file in a clever way that only stores the differences between versions.

CVS also helps you if you are part of a group of people working on the same project. It is all too easy to overwrite each other's changes unless you are extremely careful. With CVS several people can work on the same files simultaneously without damaging each other's work.

An important feature of CVS is the ability to tag labels to collections of files. This is common practice for grouping together all files that are needed to construct a software release. CVS is able to extract the entire collection of files labeled for such a release at any time.

This document offers a quick start with CVS and a hands-on example. More information about the ideas behind archiving and version control can be found in the document "Archiving and version control".

1.1 Intended audience

This document is mainly targeted towards (software) developers, working solo or in a group but is also useful for those producing or maintaining documentation and other information organised in collections of computer files that evolve over time.

2 References

There is excellent documentation available about the use of CVS and it would be a bit silly to copy it here so instead let me tell you where to find it.
CVS home page cvshome.org
CVS documentation cvshome.org
Tcl/Tk tcl.sourceforge.net
TkCVS twobarleycorns.net/tkcvs.html
WinCVS for Windows and MacCvsX for OS-X www.wincvs.org
MacCVS Pro (seems to be a bit buggy) www.maccvs.org

Information about tkCVS can be found on the tkCVS site but I didn't find a useful manual there. The help menu on the tool itself is quite good though.

3 Getting started with CVS

In general, CVS consists of a client and a server program. It is possible to use CVS on the same machine that hosts the CVS repository, but a more common situation is that in which the developers all use CVS on their machines somewhere on the local network, or even the internet, and all use the same central archive which is located on a CVS server.

After downloading and installing a CVS client for your computer, the program has to be configured. The one and only thing that is required to tell your client is where to find its server, or more specifically, where it can find its repository. Other configuration is client-specific and mainly addresses preferences for displaying information or check-in and check-out behaviour.

3.1 Telling CVS which repository to use

CVS can work with a repository that resides on your own computer or on a different machine somewhere on your network or on the internet. But you'll have to tell it which repository to use. That is done by specifying the name and location of the repository. It is possible to do this on the command line for every CVS command like e.g.
cvs -d /usr/local/cvsroot update mediate
in which /usr/local/cvsroot is the name of the repository on the local machine. The disadvantage of this invocation is that you have to specify the repository in every CVS command.

A more commonly used way of telling CVS where to find your repository is to create an environment variable CVSROOT.

3.1.1 How to set the CVSROOT variable

On UNIX systems the way to specify environment variables depends on the type of shell you use. In general we have two flavours:

csh or tcsh users put the following line in their `.cshrc' or `.tcshrc' files:

setenv CVSROOT /usr/local/cvsroot

sh and bash users put these lines in their `.profile' or `.bashrc':

CVSROOT=/usr/local/cvsroot
export CVSROOT

On Windows systems, the CVSROOT variable is specified in the environment menu.

In the MacCVS client, CVSROOT is specified in the program itself.

3.1.2 Using a remote repository

Using a repository on a different computer than your own requires that you specify the protocol, your username, the name of the remote machine and the name of the repository. This could then look like

setenv CVSROOT :pserver:myname@cvs.hku.nl:/usr/local/cvsroot

In this example, the pserver protocol is used to communicate to the CVS server on cvs.hku.nl. The repository on cvs.hku.nl resides in the directory /usr/local/cvsroot. The user 'myname' must have an account on the server and to use the repository, the user must have read and/or write permissions on (parts of) the repository.

3.2 CVS on the command line

Originally, CVS was developed as a command-line application. In fact, it still is and a graphical client like tkCVS is just a pretty faced wrapper around the command-line CVS. Generally, the graphical clients can perform the same tasks as the command line interface, but for experienced users the command line can be a more powerful tool.

For those who wish to use CVS on the command line, e.g. in a telnet window on a remote machine, the following is a list of frequently used commands:

*** setting up a repository
cvs init

*** creating a new project in the repository
cvs import -m "name" project tag start
This is done in the original directory. The parameter 'name' specifies
the name of the project in CVS and is generally the same as the project
directory. The parameter 'tag' is a Vendor tag, just make something up.
The parameter 'start' is a release tag, make something up for that one
as well.

*** checking out an entire project
cvs get project

*** checking out file(s) from subdir/ under project
cvs get <project/subdir/file(s)>

*** setting tags to an entire directory
cvs tag <tagname> .	from toplevel project dir

*** show all status information, the '-v' makes sure you also get to
    see the tags
cvs status -v <file(s)>

*** show log information
cvs log [file(s)]

*** update the file(s) w.r.t. the file in the repository, optionally
    specifying from which release
cvs update [-r revision] <file(s)>

*** reset sticky tags
cvs update -A <file>

*** show differences between your working copy of the specified file and
    its counterpart in CVS, optionally specifying its revision number.
    If no file specified, CVS will report the differences to all files
    under the current directory that are under CVS control
cvs diff [-r revision] [file(s)]

*** show differences while ignoring differences in white space
cvs diff -w

3.3 Graphical clients

Though the command-line interface to CVS is a powerful way of using CVS, a graphical tool has certain advantages.

There are several web-clients for CVS but I haven't found any that can do check-ins so I didn't do further research on those. There is however a graphical tool called tkCVS that is written in tcl/tk and is in fact a shell upon CVS. It is available for UNIX and Windows. For Windows and OS-X, there is WinCVS.

Some advantages of a graphical tool compared to the command-line interface:

The following picture shows the two-window compare option of tkCVS:

4 Binary files

Storing binary files in CVS has a number of drawbacks. It is possible to store binary files but there is no CVS support for viewing differences between revisions. A more serious problem is that CVS can not assist in preventing merge conflicts for binary files. Storing binary files is also less efficient in terms of disk space than storing text files.

Examples of binary files

Examples of text files

5 Putting a project under CVS control

5.1 Preparing your files for CVS

Each file containing software source-code gets a header according to the following format:

/********************************************************************
* 	(c) Copyright 2002, Hogeschool voor de Kunsten Utrecht
*			Hilversum, the Netherlands
*********************************************************************
*
* File name	:
* System name	:
* 
* Version	: $Revision: 1.8 $
*
*
* Description	:
*
*
* Author	:
* E-mail	:
*
*
********************************************************************/

/************
   $Log: cvs_intro.xml,v $
   Revision 1.8  2005/10/12 18:06:58  marcg
   link update

   Revision 1.7  2005/09/14 20:30:43  marcg
   from draft to proposal

   Revision 1.6  2003/05/05 15:21:49  marcg
   Set current date

   Revision 1.5  2002/06/14 23:22:59  marcg
   Small changes

   Revision 1.4  2002/04/24 19:25:51  marcg
   Minor changes

   Revision 1.3  2002/04/24 17:37:15  marcg
   Changed titlepage layout according to new format
   Added LaTeX control commands for paragraphs
   Major update of the hands-on example

   Revision 1.2  2002/03/27 21:25:00  marcg
   Lots of additions

   Revision 1.1.1.1  2002/03/12 10:39:05  marcg
   HKU documenting course

*************/
To put the ID-marker in the compiled object files as well, put it into a literal character string. In C, this is done by adding following line:

static const char RCSID[] = "$Id: cvs_intro.xml,v 1.8 2005/10/12 18:06:58 marcg Exp $";

NB: This only applies to the source files, not the header files, because
this statement will allocate storage.

5.2 Bringing a new project into CVS

Go to the top level directory of your project and open the module browser. Enter the name of the project and select 'import a new module'.

5.3 Check-in of modified files

Commit checklist

The following is a checklist for checking-in files into CVS:

5.4 Adding files

In tkCVS we can add files by pressing the '+' button or selecting the 'Add' option from the File menu. Adding a file only tells CVS that the file will be placed under CVS control, but the file is NOT checked in ! Therefore, an 'add file' action is mostly followed by a check-in.

5.5 Tagging and making a release

Release checklist

Some guidelines to follow when making a new release:

If compilation and linking work out OK, proceed with the following steps:

5.6 tkCVS

The following picture is a screenshot of the main tkCVS window:

A general rule for every check-in: whatever you put into CVS, make sure it's correct and functioning. For software this means that every source file must at least be compilable and that the most recent collection of files in the archive must be linkable. Steps to take for an initial entry into CVS
  1. Clean all rubbish from the directory tree you want to put under CVS control. For software, this might be something like 'make clean' which removes all object files, executables etc. because we don't want those in the archive.
  2. remove all rubbish that isn't removed by a 'make clean'
  3. make sure that all files have the correct HEADERS
  4. change to the top-level directory you want to import BEFORE you start the import
  5. open the tkcvs module browser
  6. import current directory into CVS
  7. (not for tkCVS) checkout the directory to a different place: this will be your working copy. You can 'safely' delete the original directory after verifying that the files you retrieved from CVS are correct

5.7 Taken from the tkCVS on-line help

In CVS, groups of files are arranged into modules for easy access. Module Browser window.

- Working with files from the repository Using the Module Browser window, you can select a module to check out. Each module has a module code which is displayed as you browse through the module tree. When you check out a module, a new directory is created in your working directory with the same name as the module code.

You can use the simple file management and editing features of TkCVS to work with files in this directory, or you may use your own text editor, word processor, or whatever to work with the files.

Once you have finished your edits (or whenever you have reached a stable stage in your development, or possibly even on a daily basis), you can check files back in to TkCVS

- Tagging and Branching You can tag particular versions of a module or file in the repository. Normally you will do this when your programs are ready to be released into a testing or production environment. Tagging is useful for taking snapshots of what the repository contained at a particular date (for example you can tag all of the files associated with a particular software release).

- Exporting ! Shipping a release without the CVS directories. Through module browser ! Tip: after changing working directory press ENTER otherwise it doesn't stick

-Importing: how do we import a module as part of a bigger group of modules ? E.g. mediate-signature-database ---- module path

Once a software release has been tagged, you can use a special type of check out called an export. This allows you to more cleanly check out files from the repository, without all of the administrivia that CVS requires you to have while working on the files. This type of facility is useful for delivery of a software release to a customer.

Other features of TkCVS allow you to tag files, add new files or delete files from the repository, or view and print reports about the repository contents. You can also search the module database for module codes, module names, and keywords within a module name.

Add file, then commit (check in)

Remove... is this permanent ?

Update button

Directory tag name zie je alleen bij uitgecheckte dir van bep. revision

Customising tkcvs

tkdiff program - start it and look into its Help ---- possible diff while skipping white space ? --- yep, select 'diff -b' in edit-preferences menu, better not make this setting permanent

tkdiff right button in window

Keyboard navigation: f First diff c Center current diff l Last diff n Next diff p Previous diff 1 Merge Choice 1 2 Merge Choice 2

Merging ---- eerst merge mode kiezen, dan via Merge menu mergen

To merge the two files, go through the difference regions (via "Next", "Prev" or whatever other means you prefer) and select "Left" or "Right" (next to the "Merge Choice:" label) for each. Selecting "Left" means that the the left-most file's version of the difference will be used in creating the final result; choosing "Right" means that the right-most file's difference will be used. Each choice is recorded, and can be changed arbitrarily many times. To commit the final, merged result to disk, choose "Write Merge File..." from the Merge menu.

Merge Preview

To see a preview of the file that would be written by "Write Merge File...", select "Show Merge Window" in the View menu. A separate window is shown containing the preview. It is updated as you change merge choices. It is synchronized with the other text widgets if "Synchronize Scrollbars" is on. merging !!! cvs update

5.8 Merge conflicts

Merge conflicts can arise when two or more people are working independently on the same file. Suppose you check a file into CVS. You working copy is based upon version 1.3 in the archive. In your working copy you have removed some parameters to a function. In that case, version 1.3 that is also stored in CVS, still contains the parameters and the new version 1.4 doesn't have the parameters anymore. Suppose another developer is editing the same file while his working copy is based on version 1.3 which means that it still contains the parameters you have just removed. If the other developer doesn't touch those parameters but make some other changes and commits the file into CVS, we have a conflict because the archive version will again have the parameters you have just removed. While CVS can assist by giving a warning about this situation, it can not assist in the communication between developers. After all, it is possible that the function parameters must be in the file and you and the other developer agree about this, but CVS doesn't know that.

In general, most merge conflicts are caused by overlapping changes that can not be uniquely identified by CVS.


The following example is taken from the CVS manual.

Suppose revision 1.4 of `driver.c' contains this: 

#include <stdio.h>

void main()
{
    parse();
    if (nerr == 0)
        gencode();
    else
        fprintf(stderr, "No code generated.\n");
    exit(nerr == 0 ? 0 : 1);
}

Revision 1.6 of `driver.c' contains this: 

#include <stdio.h>

int main(int argc,char **argv)
{
    parse();
    if (argc != 1)
    {
        fprintf(stderr, "tc: No args expected.\n");
        exit(1);
    }
    if (nerr == 0)
        gencode();
    else
        fprintf(stderr, "No code generated.\n");
    exit(!!nerr);
}

Your working copy of `driver.c', based on revision 1.4, contains this before
you run `cvs update': 

#include <stdlib.h>
#include <stdio.h>

void main()
{
    init_scanner();
    parse();
    if (nerr == 0)
        gencode();
    else
        fprintf(stderr, "No code generated.\n");
    exit(nerr == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

You run `cvs update': 

$ cvs update driver.c
RCS file: /usr/local/cvsroot/yoyodyne/tc/driver.c,v
retrieving revision 1.4
retrieving revision 1.6
Merging differences between 1.4 and 1.6 into driver.c
rcsmerge warning: overlaps during merge
cvs update: conflicts found in driver.c
C driver.c

CVS tells you that there were some conflicts. Your original working file is
saved unmodified in `.#driver.c.1.4'. The new version of
`driver.c' contains this: 

#include <stdlib.h>
#include <stdio.h>

int main(int argc,
         char **argv)
{
    init_scanner();
    parse();
    if (argc != 1)
    {
        fprintf(stderr, "tc: No args expected.\n");
        exit(1);
    }
    if (nerr == 0)
        gencode();
    else
        fprintf(stderr, "No code generated.\n");
<<<<<<< driver.c
    exit(nerr == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
=======
    exit(!!nerr);
>>>>>>> 1.6
}

Note how all non-overlapping modifications are incorporated in your working
copy, and that the overlapping section is clearly marked with
`<<<<<<<', `=======' and `>>>>>>>'. 

You resolve the conflict by editing the file, removing the markers and the
erroneous line. Suppose you end up with this file: 

#include <stdlib.h>
#include <stdio.h>

int main(int argc,
         char **argv)
{
    init_scanner();
    parse();
    if (argc != 1)
    {
        fprintf(stderr, "tc: No args expected.\n");
        exit(1);
    }
    if (nerr == 0)
        gencode();
    else
        fprintf(stderr, "No code generated.\n");
    exit(nerr == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

You can now go ahead and commit this as revision 1.7. 

$ cvs commit -m "Initialize scanner. Use symbolic exit values." driver.c
Checking in driver.c;
/usr/local/cvsroot/yoyodyne/tc/driver.c,v  <--  driver.c
new revision: 1.7; previous revision: 1.6
done

For your protection, CVS will refuse to check in a file if a conflict occurred
and you have not resolved the conflict. Currently to resolve a conflict, you
must change the timestamp on the file, and must also insure that the file
contains no conflict markers. If your file legitimately contains conflict
markers (that is, occurrences of `>>>>>>> ' at the start
of a line that don't mark a conflict), then CVS has trouble handling this and
you need to start hacking on the CVS/Entries file or other such workarounds. 

6 Example

We make a little program that calculates how far we get on a bicycle. The user is asked for the number of pedal turns and the program uses that to calculate the distance, based on wheel diameter and gear ratio. The program is written in C. Every C-file has a header file defining constants and function prototypes. There is also a design document describing the design and implementation.

The following subjects will be addressed in this example:

6.1 Preparing the files

We start with a top-level directory 'bicycle' containing subdirectories src (for source files), include (for header files) and doc (for documentation) and we fill those directories with the following files:

src include doc The files bike_main.c, pedal.c, wheel.c, bike_main.h, pedal.h and wheel.h all get the standard file header.

6.2 First working code

This section shows the contents of the files bike_main.c, pedal.c, wheel.c, pedal.h and wheel.h. These files all get the standard file header but for reasons of clarity this is omitted from this overview.
/**************
* bike_main.c *
**************/

#include <stdio.h>
#include <stdlib.h>
#include <pedal.h>
#include <wheel.h>

main()
{
int pedalturns=3;
double wheelturns;
double movement;

  wheelturns=pedal(pedalturns);
  movement=wheel(wheelturns);
  printf("We're moving %f meters\n",movement);
}
/**********
* pedal.h *
**********/
extern double pedal(int);

#define RATIO   0.5
/**********
* pedal.c *
**********/
#include <stdio.h>
#include <pedal.h>

double pedal(int turns)
{
  return (double)turns * RATIO;
}
/**********
* wheel.h *
**********/
extern double wheel(double);

#define PERIMETER       3.1415

/**********
* wheel.c *
**********/

#include <stdio.h>
#include <wheel.h>

double wheel(double turns)
{
  return turns * PERIMETER;
}

We want to put the entire directory tree, containing subdirectories src, include and doc, into CVS. We do this by going to the top-level directory of our project and opening the module browser in tkCVS. In the 'Module' line we enter the name for our module, in this case 'bicycle'. In CVS, this will be the top-level name for our project. In the 'Current Directory' line, the top-level directory of our project must be shown. If that is not the case, change it.

The 'import' button will bring the entire tree under CVS control. When using CVS on the command line, the next step will be to rename the original top-level directory and check-out the project to your local file system. This step is necessary because CVS will then put some control information into the tree. When using tkCVS, this step of renaming your tree to a temporary tree and checking out the newly entered project is done automatically.

6.3 Labelling a bunch of files

We now have a first 'release' of our software. To make sure that we can retrieve all files belonging to this release, we place a tag on every file, using the tag command, or tag button. Suppose we give this release the name 'firstbike'.

6.4 Changing a file

Suppose we want to change the program in such a way that it asks the user to enter the number of pedal turns. We do this by changing the file bike_main.c as follows:
/**************
* bike_main.c *
**************/

#include <stdio.h>
#include <stdlib.h>
#include <pedal.h>
#include <wheel.h>

main(int argc,char **argv)
{
int pedalturns;
double wheelturns;
double movement;

  if(argc==2)pedalturns=atoi(argv[1]);
  else{
    printf("Please enter the number of pedal turns\n");
    exit(0);
  }

  wheelturns=pedal(pedalturns);
  movement=wheel(wheelturns);
  printf("We're moving %f meters\n",movement);
}

Change your local copy of bike_main.c according to the given example and ask tkcvs to show the differences between your local (changed) copy and the latest revision stored in the CVS repository.

To be sure that your file is in sync with the archive, update your file before check-in. Rebuild the program and when you're satisfied with the changes, commit the file to CVS, writing some words about the change in the revision log. Afterwards, take a look at the revision log.

6.5 Adding files

In the documentation directory, we now put the file bike_design.xml which we also want to put under CVS control. The file looks as follows:
/******************
* bike_design.xml *
******************/
<?xml version="1.0"?>
<!DOCTYPE doc SYSTEM "/usr/local/xslt/doc.dtd">
<doc style="main.css">
<?xml-stylesheet type="text/css" href="xmldoc.css"?>

<report>
<title author="Marc Groenewegen (marcg@dinkum.nl)">
<para>Bicycle design doc</para>
</title>

<toc/>

<chapter>
<heading>Introduction</heading>
<para>
This document describes the design of a bicycle tour.
</para>
</chapter>

</report>
</doc>
In tkCVS we can now add the file by pressing the '+' button or selecting the 'Add' option from the File menu. Adding a file only tells CVS that the file will be placed under CVS control, but the file is NOT checked in ! Therefore, an 'add file' action is mostly followed by a check-in.

6.6 Again: labelling a bunch of files

We now have a second 'release' of our software. To make sure that we can retrieve all files belonging to this release, we place a tag on every file, using the tag command, or tag button. Suppose we give this release the name 'secondbike'.

7 Check-out in a different location

We can make a copy of the entire project in a different location, thus creating a new working directory. To do this, go to a different directory than your working directory, e.g. $(HOME)/tmp, start tkCVS and open the module browser. Select the module named 'bicycle' and use the option for checking out a module to the current directory. This will make a copy into your local directory.

8 Check-out a previous release

We can get a previous release from the archive by creating a new working directory. To do this, open the module browser and type the name of the release (firstbike) into the Tag A field. Select the module named 'bicycle' and use the option for checking out a module to the current directory. This will make a copy into your local directory, only it will not be a copy of the most recent files in the archive, but of the files belonging to the release 'firstbike'.