<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<article id="CVS">
  <articleinfo>
    <title>CVS</title>
    <authorgroup>
      <author>
        <firstname>Ashley</firstname>
        <othername>J.S</othername>
        <surname>Mills</surname>
        <affiliation><address><email>ashley@ashleymills.com</email></address></affiliation>
      </author>
      <author>
        <firstname>Alan</firstname>
        <othername>P</othername>
        <surname>Sexton</surname>
        <affiliation><address><email>A.P.Sexton@cs.bham.ac.uk</email></address></affiliation>
        <contrib>Contributed <link linkend="CVS-Survival">Survival Guide For BHAM CS Students</link></contrib>
      </author>
    </authorgroup>

    <copyright>
      <year>2005</year>
      <holder role="mailto:ashley@ashleymills.com">The University Of Birmingham</holder>
    </copyright>
  </articleinfo>

  <sect1 id="CVS-Introduction"><title>Introduction</title>
    <para>
      CVS(Concurrent Versioning System) is a version control system designed to assist in the production of multi-person concurrent access software projects, the official CVS home page is <ulink url="http://www.gnu.org/software/cvs/cvs.html">http://www.gnu.org/software/cvs/cvs.html</ulink>. CVS is used to manage the access of software projects.
    </para>
      
    <para>
      There are chapters of the book <citetitle>Open Source Development with CVS</citetitle> by Karl Fogel available online at <ulink url="http://cvsbook.red-bean.com/">http://cvsbook.red-bean.com</ulink>, you can even download the free chapters for viewing offline. This is more than adequate for any user wanting to use CVS, beginners and experienced alike.  I will not try and replicate what can be found elsewhere, especially when what can be found elsewhere is totally sufficient.  If this guide is not enough for you, go read the the one mentioned. If you don't have the Internet, or even have access to the Internet, I do not believe you, there are Internet cafes all over the world, and I doubt there is any University out there without an Internet connection.
    </para>
  </sect1>

  <sect1 id="CVS-Basics"><title>CVS Basics</title>
    <sect2 id="CVS-Basics-Introduction"><title>CVS Basics Introduction</title>
      <para>
        The canonical reference for CVS is <citetitle>Version Management with CVS by Per Cederqvist et al</citetitle> known as &quot;The Cederqvist&quot; after it's author. Another excellent CVS resource is <citetitle>The CVS Book</citetitle> by Karl Fogel, it can be found at <ulink url="http://cvsbook.red-bean.com/">http://cvsbook.red-bean.com/</ulink>.
      </para>

      <para>
        This next few sections should be read sequentially because each section has dependencies on the last (apart from the first of course, which has no last, ;) ).  For reference, it has been organised into sections that should be sufficient to illustrate how a command is used on it's own, independent of the dependencies.
      </para>
    </sect2>

    <sect2 id="CVS-Basics-Installation"><title>Installation</title> 
      <para>
        In order to use the tools we are about to install it is necessary to setup the operating environment so that the tools know where to find stuff they need and the operating system knows where to find the tools. A understanding of how to do this is essential as you will be asked to change the operating environment. I have comprehensively covered this in documents entitled <ulink url="../winenvars/winenvarshome.html"><citetitle>Configuring A Windows Working Environment</citetitle></ulink> and <ulink url="../unixenvars/unixenvarshome.html"><citetitle>Configuring A Unix Working Environment</citetitle></ulink>.
      </para>

      <para>If you are using a Unix system it is likely that CVS is already installed, test this by executing:</para>

      <screen><userinput><command>cvs</command> <option>--version</option></userinput></screen>

      <para>
        If you get no output, CVS is not installed, download and install it from <ulink url="http://ccvs.cvshome.org/servlets/ProjectDownloadList">http://ccvs.cvshome.org/servlets/ProjectDownloadList</ulink>.
      </para>

      <para>
        If you are a Windows user you can download the CVS binaries from here: <ulink url="http://www.cvshome.org/dev/codewindow.html">http://www.cvshome.org/dev/codewindow.html</ulink>, take notice of the description of the binaries offered on the web page. You will not be able to setup a CVS server on a Windows box unless you are willing to tryout experimental software. You will only be able to setup a local repository and be able to checkout remote projects. The zip file available on this page contains the single file <filename>cvs.exe</filename>, place this in one of the directories listed in your <envar>PATH</envar>.
      </para>

      <para>
       To use <acronym>SSH</acronym> with <acronym>CVS</acronym> it is necessary set the <envar>CVS_RSH</envar> to <emphasis>ssh</emphasis>. Users will therefore require a working ssh binary, on Unix this is most likely already setup, if you are using Windows see <ulink url="http://www.mindon.net/illinar/studio/cvs/win.html">http://www.mindon.net/illinar/studio/cvs/win.html</ulink>.
      </para>
    </sect2>

    <sect2 id="CVS-Basics-Root"><title>CVSROOT</title>
      <para>
        CVS works by maintaining a directory tree known as <quote>The Repository</quote>, the repository is a collection of files that maintain details regarding the current status of all the files in every project and every submitted revision.  The location at which this occurs is user defined in the environment variable <envar>CVSROOT</envar>. Set this environment variable to something reasonable, for instance on a Unix system you might set it to <filename>/usr/local/share/cvs</filename>. On a Windows system you will have to make sure that if you are setting up a local repository that you prefix <envar>CVSROOT</envar> with the string <emphasis>:local:</emphasis>, for example: <filename>:local:c:/cvs</filename>. The exact location is not important and you may have multiple CVS repositories, perhaps with different file permissions for different local users. Create the directory and set <envar>CVSROOT</envar> to point to it. You should then execute:
      </para>

      <screen><userinput>cvs <option>init</option></userinput></screen>

      <para>
        This will create the directory <filename>CVSROOT</filename> under the directory specified by the environment variable <envar>CVSROOT</envar>. CVS will place various startup files within this directory: 
      </para>

      <screen>
.              ..
.#checkoutlist Emptydir       history   taginfo,v
.#commitinfo   checkoutlist   loginfo   val-tags.db
.#config       checkoutlist,v loginfo,v verifymsg
.#cvswrappers  commitinfo     modules   verifymsg,v
.#editinfo     commitinfo,v   modules,v 
.#loginfo      config         modules.db 
.#modules      config,v       notify
.#notify       cvswrappers    notify,v
.#rcsinfo      cvswrappers,v  rcsinfo
.#taginfo      editinfo       rcsinfo,v
.#verifymsg    editinfo,v     taginfo
      </screen>
    </sect2>

    <sect2 id="CVS-Import"><title>Import: Setup a new project</title>
      <para>
        The <option>import</option> option allows one to <emphasis>import</emphasis> files into a project, this is usually done right at the beginning of a project.  The import command takes the form:
      </para>

      <programlisting>
import [options] repository vendor-tag release-tags... 
Import files into CVS, using vendor branches.
-b bra
Import to vendor branch bra.
-d
Use the file's modification time as the time of import.
-k kflag
Set default keyword substitution mode.
-m msg
Use msg for log message.
-I ign
More files to ignore (! to reset).
-W spec
More wrappers.
      </programlisting> 

      <para>
        The usual way to setup a project is to <command>cd</command> to the directory which contains the project and type:
      </para>

      <screen><userinput><command>cvs</command> <option>import</option>  <option>-m</option> &quot;Initial CVS Import&quot; <replaceable>ProjectName</replaceable> <replaceable>MySoftwareCompany</replaceable> <replaceable>Start</replaceable></userinput></screen>

      <para>The more generic form is:</para>

      <screen><userinput><command>cvs</command> <option>import</option> [<option>-m</option> msg] <replaceable>repository</replaceable> <replaceable>vendor-tag</replaceable> <replaceable>release-tags</replaceable></userinput></screen>

      <para>
        The <option>-m</option> option indicates a message that will accompany this import. The last two options are obligatory and are mainly concerned with the importation of previous projects to CVS. Set the first option of these two to the name of your software company (or whatever) and set the second option of these two to &quot;start&quot;, to indicate that this is the start of the project. When the command is executed, CVS will add a new project to the CVS repository; the project you just imported, the initial release value will be set to 1.1.1.1 which is how CVS specifies that this is the very first release, you should see a confirmation message similar to the following:
      </para>

      <screen>
cvs import: Importing ${CVSROOT}/ProjectName/
N ProjectName/Main.java
N ProjectName/ExitControl.java
N ProjectName/numbers
N ProjectName/text

No conflicts created by this import.
      </screen>

      <para>
        Indicating that the project has been successfully imported into the CVS repository, the preceding <emphasis role="strong">'N'</emphasis> on each line indicates a <emphasis role="strong">N</emphasis>ewfile.  If you get errors make sure that the <envar>CVSROOT</envar> environment variable is setup and that the CVS repository has been correctly initiated.
      </para>
        
      <para>
        Create a directory in some temporary location called <filename>chemlink</filename> and create a file in it called <filename>chemlink.java</filename> containing:
      </para>

      <programlisting>
public class Chemlink {
   public static void main(String[] args) {
      System.out.println(&quot;Welcome To ChemLink!&quot;);
   }
}        
      </programlisting>
        
      <para>
        Make a new directory called <filename>images</filename> and create an empty file in it called <filename>oxygen.png</filename>, you should have the following directory (and file) tree:
      </para>

      <programlisting>
<filename>/path/to/temp/chemlink/</filename>
<filename>/path/to/temp/chemlink/chemlink.java</filename>
<filename>/path/to/temp/chemlink/images/</filename>
<filename>/path/to/temp/chemlink/images/oxygen.png</filename>
      </programlisting>

      <para>Import the project into CVS like this:</para>

      <screen>
        <userinput><command>cvs</command> <command>import</command> <option>-m</option> &quot;Initial Import - Chemistry Project&quot; chem MySoftwareCompany start</userinput>
      </screen>

      <para>You should see the project imported correctly:</para>

      <screen>
N chem/Chemlink.java
cvs import: Importing /usr/local/cvs/chem/images
N chem/images/oxygen.png

No conflicts created by this import
      </screen>

      <para>
        This will create a new directory in the location specified by <envar>CVSROOT</envar> called <filename>chemlink</filename>. It will contain the files:
      </para>

      <screen>
.:
Chemlink.java,v
d
images

./images:
oxygen.png,v
      </screen>

      <para>
        Notice the files that end in &quot;,v&quot;, these are the files used by CVS to keep track of the status of the individual files of the project.  Let's take a look at the file <filename>Chemlink.java,v</filename>:
      </para>

      <programlisting>
head     1.1;
branch   1.1.1;
access   ;
symbols  start:1.1.1.1 MySoftwareCompany:1.1.1;
locks    ; strict;
comment  @# @;


1.1
date     2002.08.13.10.38.22;  author YourUserName;  state Exp;
branches 1.1.1.1;
next     ;

1.1.1.1
date     2002.08.13.10.38.22;  author YourUserName;  state Exp;
branches ;
next     ;


desc
@@



1.1
log
@Initial revision
@
text
@public class Chemlink {
   public static void main(String[] args) { 
      System.out.println(&quot;Welcome To ChemLink!&quot;);
   }
}
@


1.1.1.1
log
@Intial Import - Chemistry Project
@
text
@@
      </programlisting>

      <para>
        It contains information about the current release as well as the actual content of the file it represents.  CVS will only have one &quot;,v&quot; file for each file in your project.  When you checkout a copy of the file, you will get a copy of the latest release, this will be the actual file, not the &quot;,v&quot; file.  When you check your copy back in, any changes will be reflected in the &quot;v&quot; file, hence CVS can keep a history of every little change made to each file so that one can, for instance, go back to previous versions if the current version is broken.
      </para>
    </sect2>

    <sect2 id="CVS-Checkout"><title>Checkout</title>
      <para>
        Once you have created your new project (or have decided you would like to checkout an already existing project), you will have to <emphasis>checkout</emphasis> the project so that you can modify it. The command synopsis for checkout is:
      </para>

      <programlisting>
checkout [options] modules... 
Get a copy of the sources. 
-A 
Reset any sticky tags/date/options. 
-c 
Output the module database. 
-D date 
Check out revisions as of date (is sticky). 
-d dir 
Check out into dir. 
-f 
Use head revision if tag/date not found. 
-j rev 
Merge in changes. 
-k kflag 
Use kflag keyword expansion. 
-l 
Local; run only in current working directory. 
-N 
Don't &quot;shorten&quot; module paths if -d specified. 
-n 
Do not run module program (if any). 
-P 
Prune empty directories. 
-p 
Check out files to standard output (avoids stickiness). 
-R 
Operate recursively (default). 
-r tag 
Checkout revision tag (is sticky). 
-s 
Like -c, but include module status. 
      </programlisting>

      <para>
        Assume we want to access some project named <emphasis>chem</emphasis>, (which was described in the last section).  Make a directory called <filename>user1</filename> and go into it. The project is checked out like this: 
      </para>

      <screen><userinput><command>cvs</command> <option>co</option> chem</userinput></screen>

      <para>More generically:</para>

      <screen><userinput><command>cvs</command> <option>co</option> ProjectName</userinput></screen>
      
      <para>
        The option <option>co</option> is just a shortcut for <emphasis>checkout</emphasis>, and is legacy from the days of <acronym>RCS</acronym>.  This will <emphasis>checkout</emphasis> all the files in the project or module specified, in this case, all the files contained in the <emphasis role="strong">chem</emphasis> project, to the directory current directory, all necessary subdirectories will be created. The output generated when the above command sequence is executed is: 
      </para>

      <screen>
cvs checkout: Updating chem
cvs checkout: Updating chem
U chem/Chemlink.java
cvs checkout: Updating chem/images
U chem/images/oxygen.png
      </screen>

      <para>
        <emphasis role="strong">U</emphasis> is for <emphasis>Update</emphasis>.  The output indicates that everything has occurred successfully and every file in the project has been checked out. These files can be edited as much as is required.
      </para>
    </sect2>

    <sect2 id="CVS-Basics-Remove"><title>Remove</title>
      <para>
        The <option>remove</option> option removes files from the repository:
      </para>

      <programlisting>
remove [options] [files...] 
Remove an entry from the repository. 
-f 
Delete the file before removing it. 
-l 
Local; run only in current working directory. 
-R 
Operate recursively (default). 
      </programlisting>

      <para>
        If there is a file in the project that is no longer used, you may wish to remove it, this can be achieved like this:
      </para>

      <screen>
        <userinput><command>cvs</command> <option>remove</option> <filename>File.Extension</filename></userinput>
      </screen>

      <para>Causing the output:</para>

      <screen>
cvs remove: scheduling 'File.Extension' for removal
cvs remove: use 'cvs commit' to remove this file permanently
      </screen>
    </sect2>

    <sect2 id="CVS-Basics-Add"><title>Add</title>
      <para>
        The <option>add</option> option adds files to the project:
      </para>

      <programlisting>
add [options] [files...] 
Add a new file/directory. 
-k kflag 
Set keyword expansion. 
-m msg 
Set file description. 
      </programlisting>

      <para>
        This will be illustrated by adding a new file to the <emphasis>chemlink</emphasis> project.  Create an empty file called <filename>ChemElectrovalence.java</filename> and add it to the project like this:
      </para>

      <screen>
        <userinput><command>cvs</command> <option>add</option> <option>-m</option> &quot;Electrovalent bond simulation module&quot; <filename>ChemElectrovalence.java</filename></userinput>
      </screen>

      <para>You will be given the following in reply:</para>

      <screen>
cvs add: scheduling file `ChemElectrovalence.java' for addition
cvs add: use 'cvs commit' to add this file permanently.
      </screen>
    </sect2>

    <sect2 id="CVS-Basics-Commit"><title>Commit</title> 
      <para>
        After you have done whatever you wanted to do with the files, if you have edited them, it is assumed you will want to <emphasis>commit</emphasis> your changes to the repository. Here is the synopsis for the <option>commit</option> option:
     </para>

     <programlisting>
commit [options] [files...] 
Check changes into the repository. 
-F file 
Read log message from file. 
-f 
Force the file to be committed; disables recursion. 
-l 
Local; run only in current working directory. 
-m msg 
Use msg as log message. 
-n 
Do not run module program (if any). 
-R 
Operate recursively (default). 
-r rev 
Commit to rev. 
     </programlisting>

     <para>In the previous section some changes were made, let's commit them:</para>

     <screen><userinput><command>cvs</command> <option>commit</option></userinput></screen>

     <para>CVS will prompt you to enter a message to be used enter one. This will cause CVS to output the messages:</para>

     <screen>
cvs commit: Examining .
cvs commit: Examining images
RCS file: /usr/local/cvs/chem/ChemElectrovalence.java,v &lt;-- ChemElectrovalence.java
initial revision: 1.1
done
     </screen>

      <note>
        <para>
          One may also use the pseudonym <option>ci</option> which stands for check in and is legacy left over from the days of <acronym>RCS</acronym>(Revision Control System) upon which CVS is based upon and uses.
        </para>
      </note>
    </sect2>

    <sect2 id="CVS-Basics-Update"><title>Update</title>
      <para>
        The <option>update</option> option is used to bring your checked out version of the project upto date with any changes other users may have made since you checked out your copy.  In order to simulate this we will trick the CVS system into believing that somebody else has been messing with the project.
      </para>
        
      <para>
        <command>cd</command> to the base of the working directory that you originally checked out the project from.  Create a directory called <filename>user2</filename> and <command>cd</command> into it. Checkout a new copy of the project for <emphasis>user2</emphasis>:
      </para>

      <screen>
        <userinput><command>cvs</command> <option>co</option> chem</userinput>
      </screen>

      <para>
        Edit this copy of <filename>Chemlink.java</filename> and add the line:
      </para>

      <programlisting>
System.out.println(&quot;It's the best!&quot;);
      </programlisting>

      <para>After the other print statement and commit the changes like so:</para>

      <screen>
        <userinput><command>cvs</command> <option>commit</option> <option>-m</option> &quot;Added a line to Chemlink.java&quot;</userinput>
      </screen>

      <para>This will cause CVS to output:</para>

      <screen>
cvs commit: Examining .
cvs commit: Examining images
Checking in Chemlink.java
/user/local/cvs/chemlink/chemlink/Chemlink.java,v &lt;-- Chemlink.java
new revision: 1.2; previous revision: 1.1
done
      </screen>

      <para>
        Notice that the revision has been incremented, this has nothing to do with the actual version of the program it is just a number used internally by CVS. To illustrate this, change the second line to:
      </para>

      <programlisting>
System.out.println(&quot;It is really good!&quot;);
      </programlisting>

      <para>And commit again, this will cause CVS to output the message:</para>

      <screen>
cvs commit: Examining .
cvs commit: Examining images
Checking in Chemlink.java;
/usr/local/cvs/chem/Chemlink.java,v &lt;-- Chemlink.java
new revision: 1.3; previous revision: 1.2
done
      </screen>

      <para>
        <command>cd</command> to user1's directory and take a look at the file <filename>Chemlink.java</filename>. The file has not been updated with the modifications user2 made, this is what it looks like:
      </para>

      <programlisting>
public class Chemlink {
   public static void main(String[] args) {
      System.out.println(&quot;Welcome To ChemLink!&quot;);
   }
}
      </programlisting>
        
        
      <para>
        <emphasis>update</emphasis> is used to bring these modifications in to this copy of the project:
      </para>

      <screen><userinput><command>cvs</command> <option>update</option></userinput></screen>        

      <para>This will cause CVS to produce the output:</para>

      <screen>
cvs update: Updating .
U Chemlink.java
cvs update: Updating images 
      </screen>

      <para>
        Notice the 'U' signifying that <filename>Chemlink.java</filename> was updated in the process. This copy of <filename>Chemlink.java</filename> now looks like:
      </para>

      <programlisting>
public class Chemlink {
   public static void main(String[] args) {
      System.out.println(&quot;Welcome To ChemLink!&quot;);
      System.out.println(&quot;It is really good!&quot;);
   }
}
      </programlisting>

      <para>
        Notice that it has imported the changes submitted by user2. To make things a little more interesting let's add our own changes to the file.  Edit <filename>Chemlink.java</filename> and, between the other print statements, add the line:
      </para>

      <programlisting>
System.out.println(&quot;It is fantastic!&quot;);
      </programlisting>

      <para>
        <command>cd</command> to user2's directory and edit his copy of <filename>Chemlink.java</filename>, between the two print statements, add the line:
      </para>

      <programlisting>
        System.out.println(&quot;It is perfect!&quot;);
      </programlisting>

      <para>Commit the changes:</para>

      <screen>
        <userinput><command>cvs</command> <option>commit</option> <option>-m</option> &quot;Added another intro msg&quot;&quot;</userinput>
      </screen>

      <para>CVS outputs:</para>

      <screen>
cvs commit: Examining .
cvs commit: Examining images
Checking in Chemlink.java;
/usr/local/cvs/chem/Chemlink.java,v  &lt;-- Chemlink.java
new revision: 1.4; previous revision: 1.3
done
      </screen>

      <para>
        Everything is now set up to create a conflict. <command>cd</command> user1's directory out version and commit user1's changes:
      </para>

      <screen>
        <userinput><command>cvs</command> <option>commit</option> <option>-m</option> &quot;Added another intro msg&quot;&quot;</userinput>
      </screen>

      <para>CVS outputs:</para>

      <screen>
cvs commit: Examining .
cvs commit: Up-to-date check failed for `Chemlink.java'
cvs commit: Examining images
cvs [commit aborted]: correct above errors first!
      </screen>

      <para>
        Indicating that this copy of <filename>Chemlink.java</filename> is not up-to-date, and that update must be called before committing:
      </para>

      <screen>
        <userinput><command>cvs</command> <option>update</option></userinput>
      </screen>

      <para>CVS outputs:</para>

      <screen>
cvs update: Updating .
RCS file: /usr/local/cvs/chem/Chemlink.java,v
retrieving revision 1.3
retrieving revision 1.4
Merging differences between 1.3 and 1.4 into Chemlink.java
rcsmerge: warning: conflicts during merge
cvs update: conflicts found in Chemlink.java
C Chemlink.java
cvs update: Updating images
      </screen>

      <para>
        Conflicts have been found in the file <filename>Chemlink.java</filename>, hence the capital <emphasis role="strong">C</emphasis> for conflict before the filename <filename>Chemlink.java</filename>.  These conflicts have to be resolved before CVS will allow a commit. The merged <filename>Chemlink.java</filename> file looks like this: 
      </para>

      <programlisting>
public class Chemlink {
   public static void main(String[] args) {
      System.out.println(&quot;Welcome To Chemlink!&quot;);
&lt;&lt;&lt;&lt;&lt;&lt;&lt; Chemlink.java
      System.out.println(&quot;It is fantastic!&quot;);
=======
      System.out.println(&quot;It is perfect!&quot;);
&gt;&gt;&gt;&gt;&gt;&gt;&gt; 1.4
      System.out.println(&quot;It is really good!&quot;);
   }
}        
      </programlisting>

      <para>
        The content after the line of '&lt;' is the content that the user that called the update has in his file and the content after the line of '=' is the content that is in the repository copy that is conflicting.  At this point it is up-to the people editing the documents to decide how to the resolve the conflict, this may not require actually speaking to the person as it may just be a case of rearranging the text.  Assume that some discussion has occurred and the result is that the line &quot;It is perfect!&quot; should be removed, edit <filename>Chemlink.java</filename> and remove the unwanted line:
      </para>

      <programlisting>
public class Chemlink {
   public static void main(String[] args) {
      System.out.println(&quot;Welcome To Chemlink!&quot;);
      System.out.println(&quot;It is fantastic!&quot;);
      System.out.println(&quot;It is really good!&quot;);
   }
}
      </programlisting>

      <para>Call update again and CVS outputs the following:</para>
      
      <screen>
cvs update: Updating .
M Chemlink.java
cvs update: updating images
      </screen>

      <para>
        The file <filename>Chemlink.java</filename> has been modified, indicated by the prefixed <emphasis role="strong">M</emphasis>.  Commit the changes:
      </para>

      <screen>
        <userinput><command>cvs</command> <option>commit</option> <option>-m</option> &quot;Removed an unwanted message&quot;</userinput>
      </screen>

      <para>The response is:</para>

      <screen>
cvs commit: Examining .
cvs commit: Examining images
Checking in Chemlink.java;
/usr/local/cvs/chem/Chemlink.java,v &lt;-- Chemlink.java
new revision: 1.5; previous revision: 1.4
done
      </screen>

      <para>The conflict has been resolved.</para>
    </sect2>

    <sect2 id="CVS-Basics-Diff"><title>Diff</title>
      <para>
        Sometimes one wants to see how a currently checked out version of a file differs from the corresponding file in the repository, this is achieved using the <option>diff</option> option. Change <filename>Chemlink.java</filename> to:
      </para>

      <programlisting>
public class Chemlink {
   public static void main(String[] args) {
      System.out.println(&quot;Welcome To ChemLink!&quot;);
      System.out.println(&quot;It is really good!&quot;);
      System.exit(0);
   }
}
      </programlisting>

      <para>
        To see which files are different, use the <option>status</option> option:
      </para>

      <screen><userinput><command>cvs</command> <option>status</option></userinput></screen>

      <para>
        CVS will display details regarding every file in the project.  A specific file may be specified if one only needs to know the status of that file.  The status of the file <filename>Chemlink.java</filename> is as follows:
      </para>

      <programlisting>
===================================================================
File: Chemlink.java    	Status: Locally Modified

   Working revision:	   1.5	Tue Aug 13 13:02:12 2002
   Repository revision:	1.5	/usr/local/cvs/chem/Chemlink.java,v
   Sticky Tag:		        (none)
   Sticky Date:		       (none)
   Sticky Options:	     (none)
      </programlisting>

      <para>
        The file is &quot;Locally Modified&quot;. In order to determine exactly what this modification is, use the <option>diff</option> option:
      </para>

      <screen><userinput><command>cvs</command> <option>diff</option> <filename>Chemlink.java</filename></userinput></screen>

      <para>CVS outputs:</para>

      <screen>
Index: Chemlink.java
===================================================================
RCS file: /usr/local/cvs/chemlink/chemlink/Chemlink.java,v
retrieving revision 1.5
diff -r1.5 Chemlink.java
4d3
&lt;       System.out.println(&quot;It is fantastic!&quot;);
5a5
&gt;       System.exit(0);
      </screen>

      <para>
        The revision number is specified and then the diff begins. A line number (4 in 4d3) specifies the location in <filename>Chemlink.java</filename> that the first difference occurred. The '&lt;' on the next line indicates that the content following is not present in the local version.  (5 in 5a5) indicates a difference on line 5, the '&gt;' on the following line indicates that the content following is only present in the local version.
      </para>
    </sect2>

    <sect2 id="CVS-Basics-Other"><title>Other commands</title>
      <para>
        The <option>log</option> option produces a log of cvs project in the working directory, here is the output from executing:
      </para>

      <screen><userinput><command>cvs</command> <option>log</option></userinput></screen>

      <programlisting>
RCS file: /usr/local/cvs/chem/ChemElectrovalence.java,v
Working file: ChemElectrovalence.java
head: 1.1
branch:
locks: strict
access list:
symbolic names:
keyword substitution: kv
total revisions: 1;	selected revisions: 1
description:
Electrovalent bond simulation module
----------------------------
revision 1.1
date: 2002/08/13 12:20:49;  author: blah;  state: Exp;
Added electrovalence simulation
=============================================================================

RCS file: /usr/local/cvs/chem/Chemlink.java,v
Working file: Chemlink.java
head: 1.5
branch:
locks: strict
access list:
symbolic names:
	start: 1.1.1.1
	MySoftwareCompany: 1.1.1
keyword substitution: kv
total revisions: 6;	selected revisions: 6
description:
----------------------------
revision 1.5
date: 2002/08/13 13:03:25;  author: blah;  state: Exp;  lines: +1 -1
Removed an unwanted message
----------------------------
revision 1.4
date: 2002/08/13 12:48:32;  author: blah;  state: Exp;  lines: +1 -0
Added another intro message
----------------------------
revision 1.3
date: 2002/08/13 12:35:34;  author: blah;  state: Exp;  lines: +1 -1
Edited a line in Chemlink.java
----------------------------
revision 1.2
date: 2002/08/13 12:29:17;  author: blah;  state: Exp;  lines: +1 -0
Added a line to Chemlink.java
----------------------------
revision 1.1
date: 2002/08/13 10:38:22;  author: blah;  state: Exp;
branches:  1.1.1;
Initial revision
----------------------------
revision 1.1.1.1
date: 2002/08/13 10:38:22;  author: blah;  state: Exp;  lines: +0 -0
Intial Import - Chemistry Project
=============================================================================

RCS file: /usr/local/cvs/chem/images/oxygen.png,v
Working file: images/oxygen.png
head: 1.1
branch: 1.1.1
locks: strict
access list:
symbolic names:
	start: 1.1.1.1
	MySoftwareCompany: 1.1.1
keyword substitution: kv
total revisions: 2;	selected revisions: 2
description:
----------------------------
revision 1.1
date: 2002/08/13 10:38:22;  author: blah;  state: Exp;
branches:  1.1.1;
Initial revision
----------------------------
revision 1.1.1.1
date: 2002/08/13 10:38:22;  author: blah;  state: Exp;  lines: +0 -0
Intial Import - Chemistry Project
=============================================================================
      </programlisting>

      <para>
        Which shows the log information for all the files in the project, one can also specify a file to retrieve log information for. Notice how my log messages were not very informative, this illustrates how <emphasis>not</emphasis> to write log messages.
      </para>
    </sect2>
  </sect1>

  <sect1 id="CVS-Remote-Repos"><title>Accessing A Remote Repository</title>
    <para>
      A lot of CVS projects are set up so that the repository is on a server somewhere that can be accessed globally via the Internet. This is so that projects can be accessed or contributed to by anyone with an Internet connection.  There are two types of remote login.
    </para>

    <para>
      A remote password server, allows CVS access via a passworded server, the generic formula for accessing repositories like this is:
    </para>

    <orderedlist>
      <listitem>
        <para>
          Setup <envar>CVSROOT</envar> to point to the location of the repository.
        </para>
      </listitem>
      <listitem>
        <para>
          Execute &quot;<command>cvs</command> <option>login</option>&quot; to login to the remote repository.
        </para>
      </listitem>
      <listitem>
        <para>
          Perform whatever CVS commands you need.
        </para>
      </listitem>
    </orderedlist>

    <para>
      As an example, consider accessing the <application>Gimp</application> repository to get the latest <application>Gimp</application> sources. Gimp (GNU Image Manipulation Program) is an excellent free image manipulation program for Unix and Windows see <ulink url="http://www.gimp.org/">http://www.gimp.org/</ulink>. First of all, one has to know the address of the server, this can usually be found on the homepage of the program concerned.
    </para>

    <note>
      <para>
        Even though one usually uses CVS to version control programs, CVS can version control any collection of files. So while this document refers to programs, the actual content could be any collection of files.
      </para>
    </note>

    <para>
      Once the address of the repository is known, the address is placed in the environment variable <envar>CVSROOT</envar>. This removes the necessity to specify the location of the repository for every command issued to CVS. For the gimp project one would use:
    </para>

    <programlisting>
:pserver:anonymous@anoncvs.gimp.org:/cvs/gnome 
    </programlisting>

    <para>
      The <emphasis role="strong">:pserver:</emphasis> bit specifies that the CVS repository is stored on a server using the CVS password authentication protocol. The username is <emphasis role="strong">anonymous</emphasis> and the server is located at <emphasis role="strong">anoncvs.gimp.org</emphasis>, the cvs repository is on the remote machine in the location <emphasis role="strong">/cvs/gnome</emphasis>. After setting up CVSROOT, type the following to log in:
    </para>

    <screen><command>cvs</command> <option>login</option></screen>

    <para>A password request prompt will be presented:</para>

    <figure><title>Password Request Prompt</title>
      <mediaobject>
        <imageobject><imagedata fileref="files/images/cvslogin.png" format="PNG"/></imageobject>
      </mediaobject>
    </figure>

    <para>
      There is no password for <emphasis role="strong">cvs.gimp.org</emphasis>, there will be other servers that are passworded and it is likely that in order to actually contribute to a project one would have to have an account that had the necessary read and write permissions. Enter the correct password. Once the password has been accepted, you will be returned to the prompt you started at. CVS is now ready to accept commands from you that will be executed on the remote server.
    </para>

    <para>
      Files are grouped into <emphasis>modules</emphasis>, you have to know the name of the module you want to get the files it contains (obviously?). Assuming one wants the module <emphasis>gimp</emphasis>, to get it, issue:
    </para>

    <screen><command>cvs</command> <command>co</command> gimp</screen>

    <para>
      This will get out all the files that comprise the <emphasis>gimp</emphasis> module. In order to save bandwidth, one can use the <option>-z</option> option to specify a level of compression to use during the transfer, the command sequence below checks out the <emphasis>gimp</emphasis> module with a compression level of 3:
    </para>

    <screen><command>cvs</command> <option>-z3</option> <command>co</command> gimp</screen>

    <para>
      The compression level specified may be between 0 and 9 but most servers ask not to use a compression level higher than 3 because the losses incurred due to the high CPU load outweigh the gains reaped due to the bandwidth saved.
    </para>

    <para>Once you are done working in the cvs repository, logout with:</para>

    <screen><command>cvs</command> <option>logout</option></screen>
  </sect1>

  <sect1 id="CVS-Server"><title>Setting Up A CVS Server</title>
    <para>
      If you want to host your own project and let people from all over the world contribute to it, you will need to setup a CVS server on your machine. CVS is not setup as a separate daemon that listens continuously for a connection, it uses Inetd (Internet Daemon) to bind a connection request to the port used for CVS (2401) to the CVS server. The following commands should be executed as root. The file that supplies the information about which ports map to which services is <filename>/etc/services</filename>, edit this file and add the line:
    </para>

    <programlisting>cvspserver     2401/tcp     # CVS Pserver</programlisting>

    <para>
      It may already be listed, in which case, leave it alone. Edit the file <filename>/etc/inetd.conf</filename>, add the line:
    </para>

    <programlisting>
      cvspserver stream tcp nowait root /usr/local/bin/cvs cvs --allow-root=/usr/local/share/cvs pserver
    </programlisting>

    <para>
      This causes inetd to start up a new cvs server and connect the server to the incoming connection request.
    </para>

    <screen><userinput><command>killall</command> <option>-HUP</option> inetd</userinput></screen>

    <para>
      This restarts inetd. Logon to some other machine and test the access using the cvs login command. By default, users that already have accounts will be able to access the cvs repository with their usual passwords. This is not a very secure way to setup passwords for users since un-encrypted passwords would be being transmitted about, you should instead create the file <filename>CVSROOT/passwd</filename> in the repository, this contains the list of users that may access the CVS repository, the format is similar to <filename>/etc/passwd</filename>:
    </para>

    <programlisting>
      bob:vF3iFEoi2fOQi:freddy
    </programlisting>

    <para>
      The first field is the username, the second field is the encrypted password, the third field is optional and is a system username mapping. The third field has the effect that the person logging on as the username specified in the first field will have the same access permissions as the system user specified as the third field.  For some reason nobody has got round to adding a command to the standard distribution of <command>cvs</command> that can generate and manage the <acronym>CVS</acronym> password files. Fortunately there is a tool produced by Raymond Schneider called <command>cvspadm</command> which is designed specifically for this purpose, it can be downloaded from <ulink url="http://hackfoo.net/projs/index.html">http://hackfoo.net/projs/index.html</ulink>. Follow the installation instructions to install it. 
    </para>

    <para>
      Besides <filename>CVSROOT/passwd</filename> there are also the files <filename>CVSROOT/readers</filename> and <filename>CVSROOT/writers</filename> which contain lists of users that may read only or read and write to the repository respectively. <command>cvspadm</command> can be used interactively or manually, a screenshot from interactive use is shown below:
    </para>

    <figure><title><command>cvspadm</command> screen shot</title>
      <mediaobject>
        <imageobject><imagedata fileref="files/images/cvspadmshot.png" format="PNG"/></imageobject>
      </mediaobject>
    </figure>

    <para>This has the effect of adding the following to CVSROOT/passwd:</para>

    <programlisting>
blah:fxNYnCoaZ629k
    </programlisting>

    <para>
      <emphasis>blah</emphasis> is added to <filename>CVSROOT/readers</filename>.  The same thing can be accomplished by executing the command sequence:
    </para>

    <screen>
      <command>cvspadm</command> <option>-a</option> <option>-r</option> <option>-u</option> <replaceable>blah</replaceable> <option>-p</option> <replaceable>blah</replaceable> <option>-R</option> <filename>c:/cvs</filename>
    </screen>

    <para>
      You should now be able to accept remote CVS connections. Setting up a CVS server on Windows will not be discussed here, see <citetitle>CVSNT Enhanced CVS Server</citetitle> <ulink url="http://www.cvsnt.org/">http://www.cvsnt.org/</ulink>.
    </para>
  </sect1>

  <sect1 id="CVS-Survival"><title>Survival Guide For BHAM CS Students</title>
    <sect2 id="CVS-Survival-Setup"><title>Phase 1: Setting up your Repository</title>
      <para>
        I am assuming that your CVS repository is messed up or broken in some way so we will remove it and start the repository from scratch. I will assume that you want your repository in ~/CVS. If you already have a ~/CVS directory, remove it:
      </para>

      <screen><command>rm</command> <option>-r</option> <filename>~/CVS</filename></screen>

      <para>Make a brand new <filename>~/CVS</filename> directory and set the permissions on it:</para>

      <screen><command>mkdir</command> <filename>~/CVS</filename></screen>

      <screen><command>chmod</command> <option>go-rwx</option> <filename>~/CVS</filename></screen>

      <para>
        Now you need to make sure your CVSROOT environment variable is correct. Edit your <filename>~/.login</filename> file
      </para>

      <screen><command>xemacs</command> <filename>~/.login</filename></screen>

      <para>In that file, you should have a line</para>

      <programlisting>setenv CVSROOT ~/CVS</programlisting>

      <para>
        If you do not have <emphasis>EXACTLY</emphasis> that line there, add it (removing any old different lines that refer to <envar>CVSROOT</envar>. Note that capitals and lowercase are important.
      </para>

      <para>If you changed your .login file, log out and log in again.</para>

      <para>Now initialise your CVS repository</para>

      <screen><command>cvs</command> init</screen>

      <para>
        You should <emphasis>NOT</emphasis> have to do the above commands again, even if you log out and log in again.
      </para>
    </sect2>

    <sect2 id="CVS-Survival-Importing-Project"><title>Preparing to Import a Project</title>
      <para>You should have a directory sw1. If you do not then create one and set its permissions:</para>

      <screen><command>mkdir</command> <filename>~/sw1</filename></screen>

      <screen><command>chmod</command> <option>go-rwx</option> <filename>sw1</filename></screen>

      <para>Then change into that directory</para>

      <screen><command>cd</command> <filename>~/sw1</filename></screen>

      <para>
        If you already have an sw6 directory there, move it out of the way (you can check what files and directories you have there with the <command>ls</command> command):
      </para>

      <screen><command>mv</command> <filename>ex6</filename> <filename>ex6.old</filename></screen>

      <para>Copy the ex5 solution to ex6</para>

      <screen><command>cp</command> <option>-r</option> <filename>~aps/public/sw1/base/ex5b_Solution</filename> <filename>ex6</filename></screen>

      <para>This copies my whole ex5_solution directory to your directory ex6. Change directory to ex6</para>

      <screen><command>cd</command> <filename>ex6</filename></screen>

      <para>
        You do not need the <filename>VendingMachine.html</filename> or the <filename>stylesheet.html</filename> file so delete them (you can always get them back if you want them from the solutions directory or from the web site)
      </para>

      <screen><command>rm</command> <filename>VendingMachine.html</filename> <filename>stylesheet.css</filename></screen>

      <para>Now your files are ready for importing into CVS.</para>
    </sect2>

    <sect2 id="CVS-Survival-Getting-Started"><title>Importing and getting started</title>
      <para>
        Note that you should be in <filename>~/sw1/ex6</filename> at this time and if you give the command <command>ls</command> it should print something like the following:
      </para>


      <screen>
243_ex6% ls
Main.java VendingMachine.java
244_ex6%
      </screen>

      <para>
        The &quot;cvs import&quot; command, creates a new project in the CVS repository out of ALL the files and directories in the directory that you run the command from. In the following line, replace &quot;username&quot; with your login name.
      </para>

      <screen><command>cvs</command> import <option>-m</option> &quot;Initial Import&quot; <filename>Ex6Carton</filename> <replaceable>username</replaceable> start</screen>

      <para>It should respond with something like the following:</para>

      <screen>
N Ex6Carton/VendingMachine.java
N Ex6Carton/Main.java
No conflicts created by this import</screen>

      <important>
        <para>
          <emphasis role="strong">THIS IS WHERE MOST STUDENTS HAVE BEEN GOING WRONG</emphasis>: You cannot use the contents of the current directory for editing. After your import has worked successfully, the best thing to do with the directory you were in when you ran the cvs import command is to delete it (<emphasis role="strong">YES: DELETE IT</emphasis>)
        </para>
      </important>

      <screen><command>cd</command> ..</screen>
      <screen><command>rm</command> <option>-r</option> <filename>ex6</filename></screen>

      <para>You are now in directory <filename>~/sw1</filename>. Now you check out a working copy of the project.</para>

      <screen><command>cvs</command> checkout <filename>Ex6Carton</filename></screen>

      <para>It will show you the following output:</para>

      <screen>
cvs checkout: Updating Ex6Carton
U Ex6Carton/Main.java
U Ex6Carton/VendingMachine.java</screen>

      <para>
        In checking out the working copy it has made a <emphasis>NEW</emphasis> directory called <filename>Ex6Carton</filename> (the project name you specified in the cvs import command) Change directory into there and you can start working:
      </para>

      <screen><command>cd</command> <filename>Ex6Carton</filename></screen>

      <para>Now Edit <filename>Main.java</filename> and <filename>VendingMachine.java</filename> and add the following lines to the top of the file:</para>

      <programlisting>
// $Id$
// $Revision$
// $Date$
// $Log$</programlisting>

      <para>To put these changes into the CVS repository, use the command</para>

      <screen><command>cvs</command> commit</screen>

      <para>
        If you have a $Log$ keyword in the file that you commit, cvs will start up your editor with some lines in it that all start with &quot;CVS:&quot; You can ignore those lines but you should insert a brief (1-2 line) explanation of what changes you made in the files since the last time you committed them. In this case, a suitable comment would be:
      </para>

      <programlisting>Added CVS Keywords</programlisting>

      <para>Save the file and exit the editor and the commit will complete.</para>

      <para>If you do NOT have the $Log$ keyword, then it will not start up the editor.</para>

      <para>
        You only need the cvs update command if multiple different people are working on the same project using the same repository so that you can see the changes that they make. Since you do not share your repository with anyone in this module, you do not need to use cvs update at all (although it will do no harm if you do so, and is a good habit to get into for when you are working on shared projects).
      </para>

      <para>From now on, you mostly just need to edit files and, when you have got your changes working, do a:</para>

      <screen><command>cvs</command> commit</screen>

      <para>
        However, you may need to add new files: for example <filename>Carton.java</filename>, when you write it. Use your editor to create a new file <filename>Carton.java</filename>. Then add it to the repository:
      </para>

      <screen><command>cvs</command> add <option>-m</option> &quot;added new Carton class&quot; <filename>Carton.java</filename></screen>

      <screen><command>cvs</command> commit</screen>

      <note>
        <para>Note that you need to do the cvs commit after adding new files to put them into the repository.</para>
        <para>
          Note that cvs commit, when it finds a changed file that has to be updated into the repository, expands the keywords in the file. Thus the original
        </para>

      <programlisting>
// $Id$
// $Revision$
// $Date$
// $Log$</programlisting>
        
        <para>in my <filename>Main.java</filename>, gets expanded into:</para>

        <programlisting>
// $Id: Main.java,v 1.2 2002/11/08 21:17:20 aps Exp $
// $Revision: 1.2 $
// $Date: 2002/11/08 21:17:20 $
// $Log: Main.java,v $
// Revision 1.2 2002/11/08 21:17:20 aps
// Added CVS keywords</programlisting>

        <para>
          You <emphasis>NEVER</emphasis> need to edit the contents of these expanded keywords: cvs will update their contents automatically every time you commit a change to those files.
        </para>
      </note>

      <para>
        Finally you can see which files are up to date with respect to the repository and which are not by running the command
      </para>

      <screen><command>cvs</command> status</screen>

      <para>Also you can see the full history of all your changes for each file by running</para>

      <screen><command>cvs</command> log</screen>
    </sect2>

    <sect2 id="CVS-Survival-Problems"><title>Problems</title>
      <sect3 id="CVS-Survival-Problems-Disk-Space"><title>Disk Space</title>
        <para>
          Some students have managed to get themselves into a situation where they do a cvs import and the program keeps running until they run out of disk space (over quota). This problem is usually that you have your repostory directory <emphasis>INSIDE</emphasis> your project directory or you run the import from a parent directory of the repository. Remember, your repository directory should be <filename>~/CVS</filename>. Your initial import directory should be <filename>~/sw1/ex6</filename>. If you have made your repository directory be <filename>~/sw1/ex6</filename> as well then when you do the import you are trying to add the repository to itself. The recursion that this sets up quickly fills up you disk space. The only thing to do is to (interrupt the cvs import command with <keycombo><keycap>Ctrl</keycap><keycap>C</keycap></keycombo> and) delete the repository. Since the repository is quite big at this stage, it is difficult to do it with your usual <command>rm</command> command (because it asks for confirmation for every file) so use the following command instead (careful: this command will delete files without asking for an confirmation: used wrongly you may delete files that you don't want to delete). Let's say your repository is actually <filename>~sw1/ex6/CVS</filename>. To delete it, run the command
        </para>

        <screen><command>/bin/rm</command> <option>-rf</option> <filename>~/sw1/CVS</filename></screen>

        <para>Run 'cvs checkout' first</para>

        <para>Some students try to run cvs commit or cvs update and get a message like the following:</para>

        <screen>
cvs commit: in directory .:
cvs [commit aborted]: there is no version here; run 'cvs checkout' first</screen>

        <para>
          This means that you are trying to commit in the original import directory (the one you should have deleted after the import) instead of the checked out directory. Read carefully the section above on <link linkend="CVS-Survival-Getting-Started"><citetitle>Importing and getting started</citetitle></link>.
        </para>
      </sect3>

      <sect3 id="CVS-Survival-Problems-Repairing-Damage"><title>Repairing the damage</title>
        <para>
          Some students have not been able to get the cvs part of the exercise working, so they have gone ahead with the Java part of the project. What should they do now that they have read this and understand how to do the cvs part?
        </para>

        <para>
          Follow the instructions above exactly (<emphasis>ESPECIALLY</emphasis> the part about moving your old <filename>ex6</filename> directory out of the way: <emphasis role="strong">DO NOT DELETE YOUR MODIFIED JAVA FILES</emphasis>)
        </para>

        <para>
          When you have finished the instructions above, copy your modified java files that contain your program into the working directory, thus replacing the initial files that you copied from <filename>~aps/public/sw1/base/ex5b_Solution/</filename> with the file that you have put all your work into: Assuming that your own files are now in <filename>~sw1/ex6.Old</filename> and your working directory is <filename>~sw1/ex6Carton</filename>, then
        </para>

        <screen><command>cp</command> <option>-r</option> <filename>~/sw1/ex6.Old/*</filename> <filename>~/sw1/ex6Carton</filename></screen>

        <para>
          Now you need to edit all the files and add back in the cvs keywords if you did not have them in your own files. run cvs add on any files other than <filename>Main.java</filename> and <filename>VendingMachine.java</filename>
        </para>

        <para>Finally run cvs commit to commit all the changes to the repository.</para>
      </sect3>
    </sect2>
  </sect1>

  <sect1 id="CVS-References"><title>References (And Links You May Find Useful)</title>
    <itemizedlist>

      <listitem>
        <para><ulink url="http://www.cvshome.org/docs/manual/">http://www.cvshome.org/docs/manual/</ulink></para>

        <para>
	         <literallayout>
Version Management with CVS
Per Cederqvist et al 
          </literallayout>
	       </para>
      </listitem>

      <listitem>
        <para><ulink url="http://cvsbook.red-bean.com/">http://cvsbook.red-bean.com/</ulink></para>

        <para>
	         <literallayout>
The CVS Book
Open Source Development with CVS
by Karl Fogel 
          </literallayout>
	       </para>
      </listitem>

      <listitem>
        <para><ulink url="http://www.csc.calpoly.edu/~dbutler/tutorials/winter96/cvs/">http://www.csc.calpoly.edu/~dbutler/tutorials/winter96/cvs/</ulink></para>

        <para>
	         <literallayout>
Concurrent Versions System            
Ben Fennema
Copyright (c) 1996
          </literallayout>
	       </para>
      </listitem>

      <listitem>
        <para>
          <ulink url="http://www.gentoo.org/doc/cvs-tutorial.html">http://www.gentoo.org/doc/cvs-tutorial.html</ulink>
        </para>

        <para>Gentoo Linux CVS Tutorial</para>
      </listitem>

      <listitem>
        <para>
          <ulink url="http://www.durak.org/cvswebsites/">http://www.durak.org/cvswebsites/</ulink>
        </para>

        <para>CVS Version Control for Web Site Projects</para>
      </listitem>

      <listitem>
        <para>
          <ulink url="http://www.openbsd.org/anoncvs.html">http://www.openbsd.org/anoncvs.html</ulink>
        </para>

        <para>Anonymous CVS - A bit off-topic, since it is concerned mainly with using CVS on OpenBSD.</para>
      </listitem>
    </itemizedlist>
  </sect1>
</article>

