<?xml version='1.0'?>
<!DOCTYPE slides SYSTEM "/usr/share/sgml/docbook/xsl-slides-1.1/slides.dtd">

<!-- $Id: maximizing-cvs.xml,v 1.11 2001/06/13 20:15:52 dyork Exp $ -->

<slides>
<slidesinfo>
  <title>Maximizing Your Use of CVS</title>

  <authorgroup>
    <author>
      <firstname>Dan</firstname>
      <surname>York</surname>
      <affiliation>
         <jobtitle>Director of Training</jobtitle>
         <orgname>Mitel Networks Corporation</orgname>
	 <orgname>Network Server Solutions Group</orgname>
	 <orgname>(formerly e-smith, inc.)</orgname>
      </affiliation>
    </author>
  </authorgroup>

  <pubdate>July 25, 2001</pubdate>

  <confgroup>
     <conftitle>Ottawa Linux Symposium</conftitle>
     <conftitle role="address">Ottawa, Ontario, Canada</conftitle>
     <confdates>25-28 July 2001</confdates>
     <!-- <confnum> 1 </confnum> -->
  </confgroup>
  <copyright>
     <year>2001</year>
     <holder>Dan York</holder>
  </copyright>
</slidesinfo>

<section id="overview">
<title>Overview</title>
<foil><title>Overview</title>
<itemizedlist>
<listitem>
<para>Basic Usage
</para>
</listitem>
<listitem>
<para>Monitoring Information
</para>
</listitem>
<listitem>
<para>Keyword Expansion
</para>
</listitem>
<listitem>
<para>Hacking the Repository
</para>
</listitem>
<listitem>
<para>Client Configuration
</para>
</listitem>
<listitem>
<para>Branching and Merging
</para>
</listitem>
<listitem>
<para>Watches
</para>
</listitem>
<listitem>
<para>CVS and RPM
</para>
</listitem>
<listitem>
<para>Add-ons to CVS
</para>
</listitem>
<listitem>
<para>Troubleshooting
</para>
</listitem>
</itemizedlist>
</foil>

<foil id="whatiscvs"><title>What is CVS?</title>
<para><emphasis>Concurrent Versions System</emphasis> is the dominant version
control system used in the open source/free software world.
</para>
<itemizedlist>
<listitem>
<para>Based on client-server architecture, with clients available for many
operating systems.
</para>
</listitem>
<listitem>
<para>Uses a <emphasis>merging</emphasis> model where changes from multiple
developers are merged together, versus a <emphasis>locking</emphasis> model
where only one developer at a time can change a file
</para>
</listitem>
<listitem>
<para>Allows for use over secure (ssh) connections
</para>
</listitem>
<listitem>
<para>Also supports anonymous, read-only connections
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>How does CVS work?</title>
<itemizedlist>
<listitem>
<para>A CVS <emphasis>repository</emphasis> exists on a server somewhere.
</para>
</listitem>
<listitem>
<para>Users <emphasis>check out</emphasis> a <emphasis>working copy</emphasis>
onto their local system.
</para>
</listitem>
<listitem>
<para>After changes are made to files in the local working copy, a user will
<emphasis>commit</emphasis> those changes to the server repository.
</para>
</listitem>
<listitem>
<para>Before starting work on the files, and periodically during work, a user
will <emphasis>update</emphasis> their local working copy with changes that others
have made to the repository.
</para>
</listitem>
<listitem>
<para>If two people change the exact lines in a file independently, the last
one to try to commit the file will receive a notice of a <emphasis>conflict</emphasis>
and will have to resolve the conflict before they can commit the file.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Why do people use CVS?</title>
<itemizedlist>
<listitem>
<para>Allows multiple people to work independently on the same set of files.
</para>
</listitem>
<listitem>
<para>Enables you to track who made what change when - and (if they wrote a
coherent log entry) - why they made that change.
</para>
</listitem>
<listitem>
<para>Provides central location for all source code.
</para>
</listitem>
<listitem>
<para>Tracks the history of a project over time.
</para>
</listitem>
<listitem>
<para>Allows changes to be easily reversed.
</para>
</listitem>
<listitem>
<para>By its nature, creates redundant backups of code. (Everyone's working
copy is a full copy of the original.)
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>What do people use CVS for?</title>
<para>Basically <emphasis>any</emphasis> collection of files, including:
</para>
<itemizedlist>
<listitem>
<para>Software development
</para>
</listitem>
<listitem>
<para>Documentation projects
</para>
</listitem>
<listitem>
<para>Web sites
</para>
</listitem>
</itemizedlist>
</foil>
</section>
<section>
<title>Basic Usage</title>
<foil><title>CVS Command</title>
<para>All CVS commands are in the form of:
</para>
<blockquote>
<para>cvs <emphasis>&lt;global options&gt;</emphasis> <emphasis>command</emphasis>
<emphasis>&lt;command options&gt;</emphasis>
</para>
</blockquote>
<para>Typical global options might include:
</para>
<informaltable>
<tgroup cols="2">
<tbody>
<row>
<entry>-d <emphasis>path-to-repository</emphasis></entry>
<entry>Path to CVS repository</entry>
</row>
<row>
<entry>-q</entry>
<entry>Quiet mode - suppress info messages</entry>
</row>
<row>
<entry>-Q</entry>
<entry>Very quiet mode - suppress all msgs except errors</entry>
</row>
<row>
<entry>-z <emphasis>number</emphasis></entry>
<entry>Compression level - 0 to 9. Typically <filename>-z3</filename></entry>
</row>
</tbody>
</tgroup>
</informaltable>
<para>Typical command options might include:
</para>
<informaltable>
<tgroup cols="2">
<tbody>
<row>
<entry>-r <emphasis>revnum or tagname</emphasis></entry>
<entry>Use the revision number or tag name</entry>
</row>
<row>
<entry>-D <emphasis>date</emphasis></entry>
<entry>Use the revision no later than this date</entry>
</row>
<row>
<entry>-k <emphasis>flag</emphasis></entry>
<entry>Determines how to perform keyword expansion</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</foil>

<foil><title>CVSROOT</title>
<itemizedlist>
<listitem>
<para>CVS commands need to know <emphasis>where</emphasis> the CVS repository
is that they need to access.
</para>
</listitem>
<listitem>
<para>Once the working copy is checked out, the repository location is stored
in a file <filename>Root</filename> inside of the <filename>CVS</filename>
subdirectory of each directory of your working copy.
</para>
</listitem>
<listitem>
<para>Until the working copy is available - and with some CVS commands - you
have to tell the commands where the repository is located.  Two methods:
</para>
  <orderedlist>
  <listitem>
  <para>Set the <filename>CVSROOT</filename> environment variable to the
  repository location.  In the Linux <filename>bash</filename> shell, this can
  be done with one of the following examples:
  </para>
<screen>
$ export CVSROOT=/home/cvsroot
$ export CVSROOT= dyork@mail.e-smith.com:/home/e-smith/files/ibays/cvsroot
</screen>
  </listitem>
  <listitem>
  <para>Supply the <filename>-d</filename> global option to a CVS command, as
  in:
  </para>
<screen>
cvs -d dyork@mail.e-smith.com:/home/e-smith/files/ibays/cvsroot co documentation
</screen>
  </listitem>
  </orderedlist>
</listitem>
</itemizedlist>
</foil>
<foil><title>Log Messages</title>
<itemizedlist>
<listitem>
<para>Meaningful log entries are critical to others being able to understand
what changes have been made.
</para>
<itemizedlist>
<listitem>
<para><emphasis>Bad:</emphasis> "Made a whole bunch of changes"
</para>
</listitem>
<listitem>
<para><emphasis>Good:</emphasis> "Updated ReadFile function to support XML
format. Added '-d' command-line option to allow choice of delimiter."
</para>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>Can be entered in editor during checkin process. (Uses
<filename>$EDITOR</filename> env variable to determine editor to use.)
</para>
</listitem>
<listitem>
<para>Can also be entered on command-line using <filename>-m</filename> option
</para>
<screen>
cvs ci -m "Added new section to documentation on using blades." userguide.sgml
</screen>
</listitem>
</itemizedlist>
</foil>
<foil><title>Dates in CVS</title>
<para>In any command or option that requires a date, CVS understands a number
of different formats, including:
</para>
<itemizedlist>
<listitem>
<para><emphasis>ISO Standard:</emphasis> YYYY-MM-DD HH:MM
</para>
</listitem>
<listitem>
<para><emphasis>Relative:</emphasis> yesterday, tomorrow, today, 3 days ago, 2
years ago, etc.
</para>
</listitem>
<listitem>
<para><emphasis>Email Standard:</emphasis> DD Month YYYY, as in 28 Aug 2001.
Per RFC 822.
</para>
</listitem>
<listitem>
<para><emphasis>Other formats:</emphasis> including MM/DD/YYYY used in U.S.
</para>
</listitem>
</itemizedlist>
<para>CVS uses UTC for all dates.
</para>
<para>When you use a relative date ('today', '3 days ago') be aware that that
day starts at midnight (UTC).
</para>
</foil>
<foil><title>Creating a Repository</title>
<para>Before you can use CVS, you need to have access to a repository
somewhere on some server.  It <emphasis>can</emphasis> be on your local
system.  Once you have a repository, you then create <emphasis>modules</emphasis>
inside that repository and do not need to again create a new repository.
</para>
<para>To create a new repository, follow these steps:
</para>
<orderedlist>
<listitem>
<para>Choose a directory for a location of your repository, such as
<filename>/home/cvsroot</filename>
</para>
</listitem>
<listitem>
<para>As root, execute <filename>cvs -d dir init</filename>
</para>
<screen>
<![CDATA[
# mkdir /home/cvsroot
# cvs -d /home/cvsroot init
# ls -l /home/cvsroot
total 4 drwxrwxr-x    3 root     root         4096 Jun  9 21:43 CVSROOT
#
]]>
</screen>
</listitem>
<listitem>
<para>Check the group permissions on the CVS repository directory to be sure
that users are able to import modules into the repository.  (Suggest you
create a group called 'cvsusers', give group write permission to repository
and assign appropriate users to group.)
</para>
</listitem>
</orderedlist>
</foil>
<foil><title>Importing a Module</title>
<itemizedlist>
<listitem>
<para>Once you have access to a repository, you need to <emphasis>import</emphasis> 
one or more modules for people to use.
</para>
</listitem>
<listitem>
<para><filename>cd</filename> into directory containing files to be imported.
</para>
</listitem>
<listitem>
<para><emphasis>Entire directory tree</emphasis> will be imported into CVS.
</para>
</listitem>
<listitem>
<para>cvs import <emphasis>modulename</emphasis> <emphasis>vendortag</emphasis> 
<emphasis>initialtag</emphasis>
</para>
<para>Example:
</para>
<screen>
<![CDATA[
$ cvs -d /home/cvsroot import -m "Initial import into CVS" hello dyork start
N hello/hello.py
N hello/README

No conflicts created by this import

$
]]>
</screen>
</listitem>
</itemizedlist>
</foil>
<foil><title>Checking Out a Module</title>
<itemizedlist>
<listitem>
<para>Before checking out module, you should be in the directory in which you
want the module to be.
</para>
</listitem>
<listitem>
<para>Checkout will create a directory inside of your current directory.
</para>
</listitem>
<listitem>
<para>cvs co <emphasis>modulename</emphasis>
</para>
<para>Example:
</para>
<screen>
<![CDATA[
$ cvs -d /home/cvsroot co hello
U hello/README
U hello/hello.py
$
]]>
</screen>
</listitem>
</itemizedlist>
</foil>
<foil><title>Committing Changes</title>
<itemizedlist>
<listitem>
<para>After changes have been made, you need to <emphasis>commit</emphasis>
changes to repository.
</para>
</listitem>
<listitem>
<para>cvs commit <emphasis>filenames</emphasis>
</para>
</listitem>
<listitem>
<para>"commit" often abbreviated as "ci" (check-in)
</para>
</listitem>
<listitem>
<para>If filenames are omitted, CVS scans current directory and all
subdirectories and commits all modified files in tree.
</para>
<para>Example:
</para>
<screen>
<![CDATA[
$ cvs ci -m "Updated text string to be printed"
cvs commit: Examining .
Checking in hello.py;
/home/cvsroot/hello/hello.py,v  <--  hello.py
new revision: 1.3; previous revision: 1.2
done
$
]]>
</screen>
</listitem>
</itemizedlist>
</foil>
<foil><title>Updating Changes</title>
<itemizedlist>
<listitem>
<para>Before working on files in your local working copy, and from time to
time during your work, you need to <emphasis>update</emphasis> your copy with
changes that others have made to the repository.
</para>
</listitem>
<listitem>
<para>Example:
</para>
<screen>
$ cvs update
cvs update: Updating .
U Makefile
M hello.py
$
</screen>
<para>Here <filename>Makefile</filename> has been updated with changes and
<filename>hello.py</filename> has been modified locally (i.e. there are
changes in the local working copy that have not yet been committed to the
repository.
</para>
</listitem>
<listitem>
<para>However, regular <filename>cvs update</filename> does not include new
directories, nor does it remove files that are no longer needed.  Need to
supply <filename>-d</filename> to add directories and <filename>-P</filename>
to "prune" files that are no longer necessary, as in the example:
</para>
<screen>
$ cvs update -dP
</screen>
</listitem>
</itemizedlist>
</foil>
<foil><title>Dealing With Conflict</title>
<itemizedlist>
<listitem>
<para>Two users independently change the same line.  First user commits change
with no problem.  Second user goes to commit change and cannot:
</para>
<screen>
$ cvs ci -m "Changed first print statement to be more specific." hello.py
cvs commit: Up-to-date check failed for `hello.py'
cvs [commit aborted]: correct above errors first!
$ </screen>
</listitem>
<listitem>
<para>Second user needs to update, and will see conflict note on doing so:
</para>
<screen>
$ cvs update
cvs update: Updating .
RCS file: /home/cvsroot/hello/hello.py,v
retrieving revision 1.9
retrieving revision 1.10
Merging differences between 1.9 and 1.10 into hello.py
rcsmerge: warning: conflicts during merge
cvs update: conflicts found in hello.py
C hello.py
$ </screen>
</listitem>
<listitem>
<para>Second user must now edit the file, find the conflict markers and decide
which text to keep (hopefully in communication with first user):
</para>
<screen><![CDATA[
<<<<<<< hello.py
print "\n\nSTRING:  " +  string
=======
print "Your string is -->", string
>>>>>>> 1.10 ]]></screen>
<para>Local version is first, followed by "=======", followed by repository
version.
</para>
</listitem>
<listitem>
<para>File with corrected changes must now be committed.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Adding Files</title>
<itemizedlist>
<listitem>
<para>Two stage process:
</para>
    <orderedlist>
    <listitem>
    <para>Add file using "cvs add":
    </para>
    <screen>
$ cvs add Makefile
cvs add: scheduling file `Makefile' for addition
cvs add: use 'cvs commit' to add this file permanently
$</screen>
    <para>Can supply as many filenames as you wish and can use wildcards.
    </para>
    </listitem>
    <listitem>
    <para>Commit the addition of files:
    </para>
    <screen>
<![CDATA[
$ cvs ci -m "Added Makefile to automate updating"
cvs commit: Examining .
RCS file: /home/cvsroot/hello/Makefile,v
done
Checking in Makefile;
/home/cvsroot/hello/Makefile,v  <--  Makefile
initial revision: 1.1
done
$ ]]></screen>
    </listitem>
    </orderedlist>
</listitem>
<listitem>
<para>Directories need to be added first, before files in that directory can
be added.
</para>
</listitem>
<listitem>
<para>Many files can be added before a single commit.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Removing Files</title>
<itemizedlist>
<listitem>
<para>Like add, two stage process: first use <filename>cvs remove</filename>
and then commit the changes.
</para>
</listitem>
<listitem>
<para>One "gotcha" - file must be removed from directory first:
</para>
<screen>
$ cvs remove test.py
cvs remove: file `test.py' still in working directory
cvs remove: 1 file exists; remove it first
$ rm test.py
$ cvs remove test.py
cvs remove: scheduling `test.py' for removal
cvs remove: use 'cvs commit' to remove this file permanently
$ </screen>
</listitem>
<listitem>
<para>Solution is to use <filename>-f</filename> option which will remove the
file and schedule it for removal from the repository:
</para>
<screen>
$ cvs remove -f test.py
cvs remove: scheduling `test.py' for removal
cvs remove: use 'cvs commit' to remove this file permanently
$ </screen>
</listitem>
<listitem>
<para>If you mistakenly remove a file and have not yet committed the change,
use <filename>cvs add</filename> to retrieve file:
</para>
<screen>
$ cvs add test.py
U test.py
cvs add: test.py, version 1.1, resurrected
$ </screen>
</listitem>
</itemizedlist>
</foil>
<foil><title>Comparing Files</title>
<itemizedlist>
<listitem>
<para><filename>cvs diff</filename> will compare a file (or files) from the
local directory with the version(s) in the repository.
</para>
</listitem>
<listitem>
<para>Can be used with <filename>-r</filename> option to compare specific
revisions or tags of a file. Example:
</para>
<screen><![CDATA[$ cvs diff -r 1.3 -r1.4 hello.py
Index: hello.py
===================================================================
RCS file: /home/cvsroot/hello/hello.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -r1.3 -r1.4
8a9
> count = 0
11c12,13
<     print letter
---
>     print " " * count, letter
>     count = count + 1
$]]></screen>
</listitem>
<listitem>
<para>Can be used with <filename>-c</filename> or <filename>-u</filename> to
provide other types of diff outputs.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Using Tags</title>
<itemizedlist>
<listitem>
<para>As each file is committed, it is assigned a revision number.  All files
in the repository will have a different revision number depending upon how
many times it has been committed. The revision number is <emphasis>per file</emphasis>.
</para>
</listitem>
<listitem>
<para>At major milestones, you might want to flag <emphasis>all</emphasis> files at the revision
point that they are currently at. CVS uses <emphasis>tags</emphasis> to 
accomplish this. 
</para>
</listitem>
<listitem>
<para>Only restriction on tag names is that they cannot contain spaces or the
period "." character.
</para>
</listitem>
<listitem>
<para>cvs tag <emphasis>tagname</emphasis> <emphasis>filenames</emphasis>
</para>
</listitem>
<listitem>
<para>If no filenames are supplied, <emphasis>all</emphasis> files in module
are tagged with the tagname.
</para>
<para>Example:
</para>
<screen>
$ cvs tag Release-1-0
cvs tag: Tagging .
T Makefile
T README
T hello.py
$
</screen>
</listitem>
<listitem>
<para>Tag names can then be used in place of revision numbers in checkout,
update and export commands.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Switching Between Versions</title>
<itemizedlist>
<listitem>
<para>If you want to go back to a previous version of a file or project, you
can revert either a single file, a group of files, or the entire directory
tree.
</para>
</listitem>
<listitem>
<para>Using <filename>cvs update -r</filename> file(s) to a specific revision or tag.
(Hence the emphasis on using tags.) Example:
</para>
<screen>
$ cvs update -r Release-1-0
cvs update: Updating .
U README
U hello.py
$ cvs status hello.py
===================================================================
File: hello.py         	Status: Up-to-date

   Working revision:	1.4	Tue Jun 12 14:25:35 2001
   Repository revision:	1.4	/home/cvsroot/hello/hello.py,v
   Sticky Tag:		Release-1-0 (revision: 1.4)
   Sticky Date:		(none)
   Sticky Options:	(none)

$</screen>
</listitem>
<listitem>
<para>Using <filename>-A</filename> option will update file(s) to the most
recent version and without any options. Example:
</para>
<screen>
$ cvs update -A
cvs update: Updating .
U README
U hello.py
$ cvs status hello.py
===================================================================
File: hello.py         	Status: Up-to-date

   Working revision:	1.7	Tue Jun 12 14:26:36 2001
   Repository revision:	1.7	/home/cvsroot/hello/hello.py,v
   Sticky Tag:		(none)
   Sticky Date:		(none)
   Sticky Options:	(none)

$</screen>
</listitem>
<listitem>
<para>Note that instead of tags and revision numbers, you can use the 
<filename>cvs update -D</filename> command with a date.  Tags are
<emphasis>strongly</emphasis> recommended instead!
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Exporting from CVS</title>
<itemizedlist>
<listitem>
<para>When you want to package a program for distribution, you can export
directory from CVS into another directory. For instance, that directory could
then be packaged using tar or zip.
</para>
</listitem>
<listitem>
<para>You must supply a tag name (using <filename>-r</filename>) or a date
(using <filename>-D</filename>)
</para>
</listitem>
<listitem>
<para>cvs export -r <emphasis>tagname</emphasis> -d <emphasis>target-dir-name</emphasis> <emphasis>modulename</emphasis>
</para>
<screen>
<![CDATA[
$ cvs export -r Release-1-0 -d /tmp/hello-1.0 hello
cvs export: Updating /tmp/hello-1.0
U /tmp/hello-1.0/Makefile
U /tmp/hello-1.0/README
U /tmp/hello-1.0/hello.py
$
]]>
</screen>
</listitem>
<listitem>
<para>If you use dates and want changes from <emphasis>today</emphasis>, it is
best to use <filename>-D tomorrow</filename> because CVS "days" start at
midnight. (So "-D today" does not get any changes made today after midnight.)
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Remote Access</title>
<para>Two forms:
</para>
<itemizedlist>
<listitem>
<para><emphasis>pserver</emphasis> - allows separate list of users from shell
accounts, but is incredibly insecure
</para>
</listitem>
<listitem>
<para><emphasis>ssh</emphasis> - requires shell account but uses secure
connection
</para>
<para>Requires that you set <emphasis>CVS_RSH</emphasis> environment variable
to <emphasis>ssh</emphasis>, as in:
</para>
<screen>
<![CDATA[
$ export CVS_RSH=ssh
]]>
</screen>
</listitem>
</itemizedlist>
<para>Other modes of access such as Kerberos are possible.
</para>
</foil>
</section>

<section>
<title>Monitoring Information</title>
<foil><title>cvs status</title>
<itemizedlist>
<listitem>
<para>Displays the status of a file, or, if no filename provided, displays
status of all files in local working copy.
</para>
<para>Example:
</para>
<screen>
<![CDATA[ $ cvs status hello.py
===================================================================
File: hello.py         	Status: Locally Modified

   Working revision:	1.4	Mon Jun 11 02:12:28 2001
   Repository revision:	1.4	/home/cvsroot/hello/hello.py,v
   Sticky Tag:		(none)
   Sticky Date:		(none)
   Sticky Options:	(none) ]]>
</screen>
</listitem>
<listitem>
<para>With <filename>-v</filename> option, will also show tags assigned to
file.
</para>
<screen>
<![CDATA[ $ cvs status -v userguide.sgml
===================================================================
File: userguide.sgml   	Status: Up-to-date

   Working revision:	1.105
   Repository revision:	1.105	/home/e-smith/files/ibays/development/files/cvsroot/documentation/manual/userguide.sgml,v
   Sticky Tag:		(none)
   Sticky Date:		(none)
   Sticky Options:	(none)

   Existing Tags:
	Release-5-0-content-completion	(revision: 1.96)
	BRANCH-4-1-fixes         	(branch: 1.81.2)
	End-of-4-1-development   	(revision: 1.81)
	Release-4-1-2nd-printing 	(revision: 1.81)
	Release-4-1              	(revision: 1.72)
	Release-4-0              	(revision: 1.21)
	start                    	(revision: 1.1.1.1)
	dyork                    	(branch: 1.1.1)
]]>
</screen>
</listitem>
</itemizedlist>
</foil>
<foil><title>cvs log</title>
<itemizedlist>
<listitem>
<para>Displays log messages associated with a file or files. (If no files
given, log info for <emphasis>all</emphasis> files will be shown.)
</para>
</listitem>
<listitem>
<para>With <filename>-r</filename> option, shows you log message for
particular revision or tag.  Can show log entries between revisions using ":"
between numbers. (Note there is no space between <filename>-r</filename> and
numbers.) Example:
</para>
<screen>
$ cvs log -r1.76:1.78 userguide.sgml
</screen>
</listitem>
<listitem>
<para>With <filename>-h</filename> option, shows only "header" info.
</para>
</listitem>
<listitem>
<para>With <filename>-w</filename><emphasis>user</emphasis> option, shows only
log entries by that userid. (Note there is no space between
<filename>-w</filename> and the userid.)
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>cvs annotate</title>
<itemizedlist>
<listitem>
<para>Shows listing of who modified each line, when it was modified and in
which version the last modification first appeared. Example:
</para>
<screen> <![CDATA[$ cvs annotate hello.py
Annotations for hello.py
***************
1.1          (dyork    10-Jun-01): #!/usr/bin/env python
1.1          (dyork    10-Jun-01): #
1.1          (dyork    10-Jun-01): # This is a very simple python program.
1.1          (dyork    10-Jun-01): #
1.1          (dyork    10-Jun-01): 
1.3          (dyork    11-Jun-01): string = "Hello, again!  Welcome to my world."
1.1          (dyork    10-Jun-01): 
1.1          (dyork    10-Jun-01): print string
1.4          (fred     11-Jun-01): count = 0
1.1          (dyork    10-Jun-01): 
1.1          (dyork    10-Jun-01): for letter in string:
1.4          (fred     11-Jun-01):     print " " * count, letter
1.4          (fred     11-Jun-01):     count = count + 1
1.1          (dyork    10-Jun-01): 
$]]> </screen>
</listitem>
<listitem>
<para>Can be used with <filename>-r</filename> or <filename>-D</filename> to
look at file at a certain revision/tag or date.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>cvs history</title>
<itemizedlist>
<listitem>
<para>Gives you historical information on <emphasis>repository</emphasis>
versus on individual files. 
</para>
</listitem>
<listitem>
<para>Common flags include <filename>-e</filename> (show everything) and
<filename>-a</filename> (for all users). Example:
</para>
<screen> <![CDATA[$ cvs history -ea
O 2001-06-11 01:22 +0000 dyork hello =hello=          ~/*
M 2001-06-11 02:04 +0000 dyork 1.2         hello.py       hello == ~/hello
M 2001-06-11 02:05 +0000 dyork 1.3         hello.py       hello == ~/hello
U 2001-06-11 02:12 +0000 dyork 1.4         hello.py       hello == ~/hello
A 2001-06-11 02:18 +0000 dyork 1.1         Makefile       hello == ~/hello
U 2001-06-11 03:09 +0000 dyork 1.2         Makefile       hello == ~/hello
E 2001-06-11 03:17 +0000 dyork [Release-1-0] hello =/tmp/hello-1.0= ~/hello//tmp/hello-1.0
O 2001-06-11 02:08 +0000 fred  hello =hello=          ~/*
O 2001-06-11 02:10 +0000 fred  hello =hello=          ~/*
M 2001-06-11 02:12 +0000 fred  1.4         hello.py       hello == ~/hello
U 2001-06-11 02:21 +0000 fred  1.1         Makefile       hello == ~/hello
M 2001-06-11 02:49 +0000 fred  1.2         Makefile       hello == ~/hello
[dyork@burrito hello]$]]> </screen>
</listitem>
<listitem>
<para>Other common options include:
</para>
<informaltable>
<tgroup cols="2">
<tbody>
<row>
<entry>-c</entry>
<entry>Report only on commits.</entry>
</row>
<row>
<entry>-m <emphasis>modulename</emphasis></entry>
<entry>Show information for only that module</entry>
</row>
<row>
<entry>-o</entry>
<entry>Show list of modules checked out</entry>
</row>
<row>
<entry>-t <emphasis>tagname</emphasis></entry>
<entry>Show history since <emphasis>tagname</emphasis></entry>
</row>
<row>
<entry>-u <emphasis>user</emphasis></entry>
<entry>Entries for a specific user</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</listitem>
</itemizedlist>
</foil>
</section>

<section>
<title>Keyword Expansion</title>
<foil><title>Possible Keywords</title>
<itemizedlist>
<listitem>
<para>Allows CVS to automagically update information inside your file
</para>
</listitem>
<listitem>
<para>Possible keywords include:
</para>
<informaltable>
<tgroup cols="2">
<tbody>
<row>
<entry>Author</entry>
<entry>$Author: dyork $</entry>
</row>
<row>
<entry>Date</entry>
<entry>$Date: 2001/06/13 20:15:52 $</entry>
</row>
<row>
<entry>Id</entry>
<entry>$Id: maximizing-cvs.xml,v 1.11 2001/06/13 20:15:52 dyork Exp $</entry>
</row>
<row>
<entry>Header</entry>
<entry>Longer version of Id.</entry>
</row>
<row>
<entry>Log</entry>
<entry>Log entries. Note that subsequent entries will be appended to the area
and the characters before the keyword (i.e. comment characters) will be preserved.
</entry>
</row>
<row>
<entry>Revision</entry>
<entry>$Revision: 1.11 $</entry>
</row>
<row>
<entry>Source</entry>
<entry>$Source: /home/e-smith/files/ibays/cvsroot/files/presos/cvs/maximizing-cvs/maximizing-cvs.xml,v $</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</listitem>
<listitem>
<para>Inserted simply by typing in keyword surrounded by '$' signs.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Flags to Modify Expansion</title>
<itemizedlist>
<listitem>
<para>Sometimes you do not want to expand keywords (binary files!) or you just
want the value instead of the keyword (exporting)
</para>
</listitem>
<listitem>
<para>Need to supply <filename>-k</filename> flag to command. Possible values
include:
</para>
<informaltable>
<tgroup cols="2">
<tbody>
<row>
<entry>b</entry>
<entry>Binary mode. No keyword expansion or newline conversion.</entry>
</row>
<row>
<entry>k</entry>
<entry>Keyword-only mode. Just the name of the keyword and no value.</entry>
</row>
<row>
<entry>v</entry>
<entry>Value-only mode. All keywords are completely replaced with their
values. <emphasis>Note:</emphasis> This mode strips out the "$"'s around the
keyword and they will no longer be expanded.</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</listitem>
<listitem>
<para>For example, in each version the "Id" keyword will be different. If you
do not want to see the Id lines in a diff, use a line similar to:
</para>
<screen>
$ cvs diff -r 1.104 -r 1.105 -kk userguide.sgml
</screen>
</listitem>
</itemizedlist>
</foil>
</section>

<section>
<title>Hacking the Repository</title>
<foil><title>Checking Out CVSROOT</title>
<itemizedlist>
<listitem>
<para>Inside of the repository, there is <filename>CVSROOT</filename>
directory which contains CVS configuration information.
</para>
</listitem>
<listitem>
<para>Permissions should be restricted so that only limited number of
(trusted!) users can check out directory. (If a user does not have
<emphasis>write</emphasis> permission for the dir, they cannot check it out.)
</para>
</listitem>
<listitem>
<para>Not all files in <filename>CVSROOT</filename> dir are checked out. 
Some, like <filename>history</filename> remain only on the server.
</para>
</listitem>
<listitem>
<para>After modification, when you commit changes, administrative databases
are rebuilt with your modifications.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Why You Would Do This</title>
<itemizedlist>
<listitem>
<para>Generating an email after each commit (loginfo)
</para>
</listitem>
<listitem>
<para>Flagging file types as binary (cvswrappers)
</para>
</listitem>
<listitem>
<para>Running a file through a pre-processing script prior to the commit
(commitinfo)
</para>
</listitem>
<listitem>
<para>Segmenting a module so people do not have to check out the entire
module (modules)
</para>
</listitem>
<listitem>
<para>Performing some action when the <filename>tag</filename> command is
executed (taginfo)
</para>
</listitem>
<listitem>
<para>Creating a standard template for log messages (rcsinfo)
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Common Syntax</title>
<itemizedlist>
<listitem>
<para>Most of the repository files, especially those ending in
<emphasis>info</emphasis> have the same syntax:
</para>
<blockquote>
<para><emphasis>regular-expression</emphasis>  <emphasis>command/file</emphasis>
</para>
</blockquote>
</listitem>
<listitem>
<para>When change is made matching regex, the command is executed or the file is
read.
</para>
</listitem>
<listitem>
<para>Regex may be simply name of module. It will then match any file inside
of that module.
</para>
</listitem>
<listitem>
<para>Two special cases supported by most, but not all, info files:
</para>
<itemizedlist>
<listitem>
<para><filename>DEFAULT</filename> - will be executed if no other match is
found.
</para>
</listitem>
<listitem>
<para><filename>ALL</filename> - will be executed <emphasis>in addition
to</emphasis> other matches. Sometimes can have multiple ALL lines.
</para>
</listitem>
</itemizedlist>
</listitem>
</itemizedlist>
</foil>
<foil><title>Commit Emails</title>
<itemizedlist>
<listitem>
<para>Can have each commit send an email to an address with the log message
and files affected.
</para>
</listitem>
<listitem>
<para>Modify <filename>loginfo</filename>, since this is affecting the log
messages.  Example:
</para>
<screen>
<![CDATA[
documentation mail -s %s documentation@e-smith.com
partnerzone  mail -s %s ffrog@e-smith.com
dev  echo %s >> /home/cvsroot/CVSROOT/commitlog
]]></screen>
</listitem>
<listitem>
<para>As show above, you could execute some other script as well.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Restricting Keyword Expansion</title>
<itemizedlist>
<listitem>
<para>CVS assumes that all files are plain text files. As each file is
committed, keywords are expanded and newlines are converted.
</para>
</listitem>
<listitem>
<para>This can be a bad thing if the file is in fact a binary format!
</para>
</listitem>
<listitem>
<para>You can restrict the filename extensions to be expanded in
<filename>cvswrappers</filename>. Example:
</para>
<screen>
*.gif -k 'b'
*.jpg -k 'b'
*.pdf -k 'b'
</screen>
</listitem>
<listitem>
<para>Can also be used to specify how to merge or copy a file, or to apply a
filter to a file type.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Pre-commit Processing</title>
<itemizedlist>
<listitem>
<para>Using the <filename>commitinfo</filename> file, it is possible to
perform pre-processing on a file before it is committed to the repository.
</para>
<para>Example:
</para>
<screen>
documentation/*.sgml  /home/cvsroot/CVSROOT/validate-sgml
</screen>
</listitem>
<listitem>
<para>Can be used to validate syntax or check whether it compiles correctly.
</para>
</listitem>
<listitem>
<para>If the function called exits with a non-zero status, the commit is
aborted.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Segmenting a Module</title>
<itemizedlist>
<listitem>
<para>If you have a very large module, you might want to allow users to check
out only a portion of the module, possibly as small as a few files.
</para>
</listitem>
<listitem>
<para>Conversely, you might want to allow someone to check out several modules
at once.
</para>
</listitem>
<listitem>
<para>The <filename>modules</filename> file has the format:
</para>
<blockquote>
<para><emphasis>modulename</emphasis>  <emphasis>directory-path</emphasis>
</para>
</blockquote>
<para>where <emphasis>directory-path</emphasis> is a path relative to the top
level of the CVS repository.  If files are included after directory path, only
those files will be checked out. Examples:
</para>
<screen>
manual         documentation/manual
manual-only    documentation/manual  userguide.sgml
</screen>
</listitem>
<listitem>
<para>Using an ampersand (&amp;) will allow you to incorporate other modules
into your new module.
Using <filename>-a</filename> will create an alias (when you check out module,
it will retain original name(s)).
</para>
<screen>
all	&amp;documentation &amp;training
doc     -a documentation
</screen>
</listitem>
</itemizedlist>
</foil>
<foil><title>Tag Processing</title>
<itemizedlist>
<listitem>
<para>The <filename>taginfo</filename> file is checked whenever the
<filename>cvs tag</filename> command is executed.
</para>
</listitem>
<listitem>
<para>Similar to <filename>commitinfo</filename>, file contains regex and
program to execute.
</para>
</listitem>
<listitem>
<para>Program is called with tag name, operation (add, del or mov), the
module/directory path, and the filenames and revision numbers for all files
being tagged.
</para>
</listitem>
<listitem>
<para>One possible use would be to create a program which keeps track of all
tags used in a repository.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Using Templates</title>
<itemizedlist>
<listitem>
<para>You can create a default template that is loaded into the editor when
someone commits (without using <filename>-m</filename> switch). For instance,
it could have a line for a bug id, comments and name of reviewer.
</para>
</listitem>
<listitem>
<para>Steps to use a template are:
</para>
<orderedlist>
<listitem>
<para>Create a text file with the template text in it and add it to
repository.
</para>
</listitem>
<listitem>
<para>Modify <filename>rcsinfo</filename> to have a regex and
<emphasis>full pathname on server</emphasis> to template files:
</para>
<screen>
hello 	 	/home/cvsroot/CVSROOT/hello.tmpl
DEFAULT		/home/cvsroot/CVSROOT/default.tmpl
</screen>
</listitem>
<listitem>
<para>Commit changes to <filename>rcsinfo</filename> and addition of template.
</para>
</listitem>
</orderedlist>
</listitem>
<listitem>
<para>Note that remote users may need to check out module again to obtain
templates.
</para>
</listitem>
<listitem>
<para>Conformance to templates can be performed through using the
<filename>verifymsg</filename> file to execute some script on the log message.
</para>
</listitem>
<listitem>
<para>Results:
</para>
<screen>
BUG ID:
CHANGE:
REVIEWED BY:
CVS: ----------------------------------------------------------------------
CVS: Enter Log.  Lines beginning with `CVS:' are removed automatically
CVS: </screen>
</listitem>
</itemizedlist>
</foil>

<foil><title>Other Files</title>
<!-- This should really be a table. -->
<para><filename>checkoutlist</filename>
</para>
<itemizedlist>
<listitem>
<para>Used to list files such as templates that are not part of "standard"
CVSROOT that should also be included whenever <filename>CVSROOT</filename> is
checked out from repository.
</para>
</listitem>
</itemizedlist>

<para><filename>config</filename>
</para>
<itemizedlist>
<listitem>
<para>(Rarely) used to set a few repository-wide variables.
</para>
</listitem>
</itemizedlist>

<para><filename>editinfo</filename> - <emphasis>obsolete</emphasis>
</para>

<para><filename>notify</filename>
</para>
<itemizedlist>
<listitem>
<para>Used by watches (discussed later) to notify users.
</para>
</listitem>
</itemizedlist>

<para><filename>passwd</filename>
</para>
<itemizedlist>
<listitem>
<para>Used by (insecure) pserver for user authentication.
</para>
</listitem>
</itemizedlist>
</foil>
</section>

<section>
<title>Client Configuration</title>
<foil><title>.cvsrc</title>
<itemizedlist>
<listitem>
<para>Located in your <emphasis>home</emphasis> directory.
</para>
</listitem>
<listitem>
<para>Includes default options that you want to pass to cvs commands.
</para>
</listitem>
<listitem>
<para>As an example:
</para>
<screen>
<![CDATA[
cvs -q
update -dP
diff -c
]]>
</screen>
</listitem>
<listitem>
<para>The <filename>.cvsrc</filename> file can be ignored (if you want to
override options that are in the file) by using the <filename>-f</filename>
global option on the cvs command. (ex. <filename>cvs -f update</filename>)
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>.cvsignore</title>
<itemizedlist>
<listitem>
<para>Can be created in each directory inside of a CVS working copy.
</para>
</listitem>
<listitem>
<para>Contains regular expressions that match file or directory names.
</para>
</listitem>
<listitem>
<para>CVS ignores files matching those expressions when performing commands
such as <command>cvs update</command>.
</para>
</listitem>
<listitem>
<para>Result is that you do not see the "?" before files not in repository.
</para>
<para>Example:
</para>
<screen>
<![CDATA[
*.swp
*.log
*.aux
html]]>
</screen>
</listitem>
<listitem>
<para>If there are some that you always want to ignore, you can create
<filename>cvsignore</filename> (without the leading '.') in the CVSROOT part
of the repository.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>.cvswrappers</title>
<itemizedlist>
<listitem>
<para>Can be created in each directory inside of a working copy.
</para>
</listitem>
<listitem>
<para>Allows you to indicate certain files that should be treated in a
specific way with regard to keyword expansion.
</para>
</listitem>
<listitem>
<para>Typically used if <filename>CVSROOT/cvswrappers</filename> is not
specific enough for your needs (or you do not have the ability to change it).
</para>
</listitem>
</itemizedlist>
<para>Example:
</para>
<screen>
*.gif 	-k 'b'
*.pdf   -k 'b'
</screen>
</foil>
</section>

<section>
<title>Branching and Merging</title>
<foil><title>Why Use Branches?</title>
<itemizedlist>
<listitem>
<para>You have a stable version of code available and are continuing
development on a new version.  Someone reports a bug in the stable
version that you would like to fix and release <emphasis>without</emphasis>
releasing all your new (and possibly unstable) changes.
</para>
</listitem>
<listitem>
<para>You would like to try out some changes that may be be disruptive to the
rest of the code. You would like to test them independently of the main code
base and then (perhaps) merge them in with the main code once they have been
debugged.
</para>
</listitem>
<listitem>
<para>Branches do <emphasis>not</emphasis> have to be merged back in to the
main development stream. You can simply have dead branches. (Example: Someone
finds a typo in an old version of a manual. You branch the files, fix and
release the change.  In the meantime, you have changed the text in the new
version and so this change will never need to be incorporated.)
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Creating a Branch - Scenario #1</title>
<itemizedlist>
<listitem>
<para>You want to create a branch from the <emphasis>current</emphasis> code
base (to try out a new feature). Three step process:
</para>
<orderedlist>
<listitem>
<para>Tag the existing code base prior to the branch so that you can go back
to it if you need to:
</para>
<screen>
$ cvs tag Release-2-1
cvs tag: Tagging .
T Makefile
T README
T hello.py
$</screen>
</listitem>
<listitem>
<para>Use the <filename>cvs tag -b</filename> command to create a new branch:
</para>
<screen>
$ cvs tag -b Release-2-1-branch
cvs tag: Tagging .
T Makefile
T README
T hello.py
$ </screen>
</listitem>
<listitem>
<para>Update your working copy to used the new branch:
</para>
<screen>
$ cvs update -r Release-2-1-branch
cvs update: Updating .
$ cvs status hello.py
===================================================================
File: hello.py         	Status: Up-to-date

   Working revision:	1.7	Tue Jun 12 13:36:09 2001
   Repository revision:	1.7	/home/cvsroot/hello/hello.py,v
   Sticky Tag:		Release-2-1-branch (branch: 1.7.2)
   Sticky Date:		(none)
   Sticky Options:	(none)

$ </screen>
</listitem>
</orderedlist>
</listitem>
</itemizedlist>
</foil>
<foil><title>Creating a Branch - Scenario #2</title>
<itemizedlist>
<listitem>
<para>You want to create a branch from a <emphasis>previous</emphasis> 
revision of the code base (to fix an issue in a previous version). Three step process:
</para>
<orderedlist>
<listitem>
<para>Revert your working copy to the previous version (Hopefully you tagged
it at that time!):
</para>
<screen>
$ cvs update -r Release-1-0
cvs update: Updating .
U README
U hello.py
$ cvs status hello.py
===================================================================
File: hello.py         	Status: Up-to-date

   Working revision:	1.4	Tue Jun 12 14:12:42 2001
   Repository revision:	1.4	/home/cvsroot/hello/hello.py,v
   Sticky Tag:		Release-1-0 (revision: 1.4)
   Sticky Date:		(none)
   Sticky Options:	(none)

$</screen>
</listitem>
<listitem>
<para>Use the <filename>cvs tag -b</filename> command to create a new branch:
</para>
<screen>
$ cvs tag -b Release-1-0-branch
cvs tag: Tagging .
T Makefile
T README
T hello.py
$ </screen>
</listitem>
<listitem>
<para>Update your working copy to used the new branch:
</para>
<screen>
$ cvs update -r Release-1-0-branch
cvs update: Updating .
$ cvs status hello.py
===================================================================
File: hello.py         	Status: Up-to-date

   Working revision:	1.4	Tue Jun 12 14:12:42 2001
   Repository revision:	1.4	/home/cvsroot/hello/hello.py,v
   Sticky Tag:		Release-1-0-branch (branch: 1.4.2)
   Sticky Date:		(none)
   Sticky Options:	(none)

$ </screen>
</listitem>
</orderedlist>
</listitem>
</itemizedlist>
</foil>
<foil><title>Working with Branches</title>
<itemizedlist>
<listitem>
<para>Once your working copy is updated to a branch, just use CVS as you
normally would.
</para>
</listitem>
<listitem>
<para>Only difference you will see is that revision numbers are 4 sets of
digits instead of two. (Branch from revision 1.4 yields branch revisions of
1.4.2.1) Example:
</para>
<screen>
$ cvs status hello.py
===================================================================
File: hello.py         	Status: Up-to-date

   Working revision:	1.4.2.1	Tue Jun 12 14:30:36 2001
   Repository revision:	1.4.2.1	/home/cvsroot/hello/hello.py,v
   Sticky Tag:		Release-1-0-branch (branch: 1.4.2)
   Sticky Date:		(none)
   Sticky Options:	(none)

$</screen>
</listitem>
<listitem>
<para>You can switch between branch and main trunk simply by using the update
command on your working copy.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Checking Out a Branch</title>
<itemizedlist>
<listitem>
<para>As an alternative to updating a working copy, you could check out a
branch into a separate directory. You can use <filename>-d</filename> option
to <filename>cvs co</filename> to put branch into separate directory.
</para>
</listitem>
<listitem>
<para>Example:
</para>
<screen>
<![CDATA[
$ cvs -d /home/cvsroot co -d hello-1-0 -r Release-1-0-branch hello
cvs checkout: Updating hello-1-0
U hello-1-0/Makefile
U hello-1-0/README
U hello-1-0/hello.py
$ ls
./  ../  hello/  hello-1-0/
$]]></screen>
</listitem>
</itemizedlist>
</foil>
<foil><title>Merging From a Branch - The Simple Case</title>
<itemizedlist>
<listitem>
<para>In the case where you have tried out a new feature and want to merge it
with the main trunk, follow this process:
</para>
<orderedlist>
<listitem>
<para>Tag the branch with a meaningful tag (we'll see why later):
</para>
<screen>
$ cvs tag Release-2-1-branch-fix1
cvs tag: Tagging .
T Makefile
T README
T hello.py
$ </screen>
</listitem>
<listitem>
<para>Update your working copy to the most recent copy of the main trunk:
</para>
<screen>
$ cvs update -A
cvs update: Updating .
U hello.py
$ </screen>
</listitem>
<listitem>
<para>Use <filename>cvs update -j</filename> to "join" the branch into the
trunk:
</para>
<screen>
$ cvs update -j Release-2-1-branch-fix1
cvs update: Updating .
RCS file: /home/cvsroot/hello/hello.py,v
retrieving revision 1.7
retrieving revision 1.7.2.1
Merging differences between 1.7 and 1.7.2.1 into hello.py
$ </screen>
</listitem>
<listitem>
<para>Resolve any conflicts and commit any changes.  The main trunk now
includes the changes from the branch.
</para>
</listitem>
</orderedlist>
</listitem>
</itemizedlist>
</foil>
<foil><title>Merging With Keywords</title>
<itemizedlist>
<listitem>
<para>One caution - if you use CVS keywords in your files, they will of course
be different. 
</para>
</listitem>
<listitem>
<para>On the merge of the branch into the trunk, you will see conflict messages for the
files where the keywords conflict.
</para>
</listitem>
<listitem>
<para>To avoid these conflicts, update with just the keywords themselves and
not their values using <filename>-kk</filename>:
</para>
<screen>
$ cvs update -kk -j Release-2-1-fix1
</screen>
</listitem>
</itemizedlist>
</foil>
<foil><title>Continuing With The Branch</title>
<itemizedlist>
<listitem>
<para>But what if you want to continue developments in the branch?  Two
choices:
</para>
<orderedlist>
<listitem>
<para>Let the old branch simply die, and follow the process earlier to create
a new branch ("Release-2-1-branch2").  Simple approach, but leaves other
branches around. (No big deal from CVS' point of view.)
</para>
</listitem>
<listitem>
<para>Continue development in the existing branch.  A couple of issues here:
</para>
   <itemizedlist>
   <listitem>
   <para>If you subsequently try to merge using the method described earlier,
   <emphasis>all</emphasis> changes in branch will be merged - creating
   conflicts with changes that have already been merged.
   </para>
   <para>Solution is to use a tag before each merge and then (in the main
   trunk), use multiple <filename>-j</filename> options:
   </para>
   <screen>
$update -j Release-2-1-branch-fix1 -j Release-2-1-branch-fix2
</screen>
   </listitem>
   <listitem>
   <para>Changes made to the main trunk will not be incorporated into the
   branch.  To include those changes, update <emphasis>in the branch copy</emphasis> 
   using the special tag
   <filename>HEAD</filename> that refers to the tip of the main trunk.
   </para>
<screen>
$ cvs update -kk -j HEAD
cvs update: Updating .
U Makefile
U README
RCS file: /home/cvsroot/hello/README,v
retrieving revision 1.7
retrieving revision 1.8
Merging differences between 1.7 and 1.8 into README
U hello.py
RCS file: /home/cvsroot/hello/hello.py,v
retrieving revision 1.7
retrieving revision 1.9
Merging differences between 1.7 and 1.9 into hello.py
rcsmerge: warning: conflicts during merge
$ </screen>
   </listitem>
   <listitem>
   <para>Resolve conflicts and commit the changes to the branch.
   </para>
   </listitem>
   </itemizedlist>
</listitem>
</orderedlist>
</listitem>
</itemizedlist>
</foil>
</section>

<section>
<title>Watches</title>
<foil><title>The Case for Watches</title>
<itemizedlist>
<listitem>
<para>CVS allows multiple users to work on the same files simultaneously.
</para>
</listitem>
<listitem>
<para>Conflicts, if any, are found and reported at commit time.
</para>
</listitem>
<listitem>
<para>Works well and without many conflicts assuming that responsibility is
clearly assigned for who is primarily responsible for what files or sections.
(And also on projects where commits are infrequent.)
</para>
</listitem>
<listitem>
<para>Problems can occur with a large project under very active development
when there is the potential for several developers to modify the same section
of code at the same time.
</para>
</listitem>
<listitem>
<para>A way to minimize conflict is to use a feature of CVS called
<emphasis>watches</emphasis>.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Watches Defined</title>
<itemizedlist>
<listitem>
<para>
When using watches, users essentially tell everyone else who
might want to change the file that they are going to be working on it.
</para>
</listitem>
<listitem>
<para>Other users are notified by e-mail when the user starts to edit the file
and again when the file is committed (or changes are aborted).
</para>
</listitem>
<listitem>
<para>Requires participation of all users to be effective.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Getting Set Up to Use Watches</title>
<itemizedlist>
<listitem>
<para>Before you can start using watches, you need to modify at least two
files in the CVSROOT directory of repository.
</para>
</listitem>
<listitem>
<para>In the <filename>notify</filename> file, you need to establish how the
notifications will be sent out.  Typically, the entry is simply:
</para>
<screen>
ALL mail %s -s "CVS notification"
</screen>
<para>The change then needs to be committed to the repository.
</para>
</listitem>
<listitem>
<para>You also need to modify the <filename>users</filename> file to include
the CVS userid of each user and their corresponding e-mail address:
</para>
<screen>
dyork:dyork@e-smith.com
</screen>
<para>With a new repository, you may need to create this file. You then need
to add and commit the file.
</para>
<para>If you are adding the file, you also need to add it to the
<filename>checkoutlist</filename> file in CVSROOT.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Watching Files</title>
<listitem>
<para>For each file that you want to be notified about, you enter the
following command:
</para>
<screen>
$ cvs watch add <emphasis>filename</emphasis>
</screen>
<para>If you omit the filename(s), a watch will be added for all files in the
directory tree.
</para>
</listitem>
<listitem>
<para>There are three actions you can be notified about: <filename>commit</filename>, 
<filename>edit</filename>, and <filename>unedit</filename>.
</para>
</listitem>
<listitem>
<para>For instance, to be notified only about commits, you enter the
following command:
</para>
<screen>
$ cvs watch add -a commit <emphasis>filename</emphasis>
</screen>
</listitem>
<listitem>
<para>You can see who is watching a file with <filename>cvs watchers</filename>:
</para>
<screen>
$ cvs watchers hello.py
hello.py	dyork	edit	unedit	commit
		fred	commit
$ </screen>
<para>Without a filename, it shows all files that are being watched.
</para>
</listitem>
</foil>
<foil><title>Removing a Watch</title>
<listitem>
<para>If you no longer want to watch a file, you can remove a watch with:
</para>
<screen>
$ cvs watch remove <emphasis>filename</emphasis>
</screen>
<para>Like add, without filenames it will assume the directory tree.
</para>
</listitem>
</foil>
<foil><title>Working with Watched Files</title>
<itemizedlist>
<listitem>
<para>If you are using watches, you need to take the following steps:
</para>
<itemizedlist>
<listitem>
<para>Before editing a file, issue the command <filename>cvs edit</filename>:
</para>
<screen>
$ cvs edit hello.py
$ </screen>
<para>Notification is then sent to all people who have an
<filename>edit</filename> watch set.  
</para>
</listitem>
<listitem>
<para>When you commit the file, notification will be sent to all watchers.
</para>
</listitem>
<listitem>
<para>If you decide you want to abort your changes, you can issue the
<filename>cvs unedit</filename> command. Note that in doing so your file will
be reverted back to the state of the last commit.  Example:
</para>
<screen>
$ cvs unedit hello.py
hello.py has been modified; revert changes? y
$</screen>
<para>Notification will be sent to all of those with an <filename>unedit</filename> watch.
</para>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>You can see who is editing a file with <filename>cvs editors</filename>
command.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Enforcing Watches</title>
<itemizedlist>
<listitem>
<para>Nothing forces users to trigger watches.
They can choose to <emphasis>not</emphasis> issue the <filename>cvs edit</filename> command
therefore watchers will only be notified when a commit occurs.
</para>
</listitem>
<listitem>
<para>A way to remind users is to use <filename>cvs watch on</filename> to
set file to read-only by default:
</para>
<screen>
$ cvs watch on <emphasis>filename(s)</emphasis>
</screen>
<para>As before, without filename(s), it modifies all files in dir tree.
</para>
</listitem>
<listitem>
<para>Does <emphasis>not</emphasis> change other users' working copies
immediately, but file(s) will be changed to "read-only" in the future 
whenever: a) the module is checked out; b) a user commits the file. 
</para>
</listitem>
<listitem>
<para>In order to change from "read-only" to "read-write" the user needs to
issue the <filename>cvs edit</filename> command for that file.
</para>
</listitem>
<listitem>
<para>File will have changed back to "read-only" after it is committed.
</para>
</listitem>
<listitem>
<para>Nothing prevents user from manually changing file with
<filename>chmod</filename>.
</para>
</listitem>
<listitem>
<para>To turn off "read-only" settings, use <filename>cvs watch off</filename>
</para>
</listitem>
</itemizedlist>
</foil>
</section>

<section>
<title>CVS and RPM</title>
<foil><title>The Problem</title>
<itemizedlist>
<listitem>
<para>You take a program (foo-2.0) and make a series of modifications to it.
Now you want to package up your modifications in a form that can be
distributed to others.
</para>
</listitem>
<listitem>
<para>The "CVS way" is to import the original package and make all of your
modifications in CVS.  Then do a
<filename>cvs export</filename> of the repository at a certain moment in time
(date, revision, tag) and package that exported directory. The advantage is
that you have everything under version control and can see logs of who has
worked on it, revert patches, branch, etc.
</para>
</listitem>
<listitem>
<para>The "RPM way" is to have a <emphasis>pristing source</emphasis> of the
original package, and then apply one or more <emphasis>patches</emphasis> to
the original source. The advantage is that you can see precisely what has been
modified in the patch files, and can potentially drop in a new version and
patch that newer version.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>The Solution</title>
<orderedlist>
<listitem>
<para>When doing the import of the original package, use a meaningful start
tag, as in:
</para>
<screen>
$ cvs import -m "Initial import into CVS" foo dyork foo-version-2-0
</screen>
</listitem>
<listitem>
<para>Use CVS as you would normally do for the development of the files.
</para>
</listitem>
<listitem>
<para>At packaging time, first tag the repository with a meaningful tag:
</para>
<screen>
$ cvs tag Release-2-1
</screen>
</listitem>
<listitem>
<para>At packaging time, first extract a copy of the original version (if you
do not have a tarball somewhere else):
</para>
<screen>
$ cvs export -r foo-version-2-0 -d foo-2.0 foo
</screen>
</listitem>
<listitem>
<para>Use <filename>cvs rdiff</filename> to generate a patch file:
</para>
<screen>
<![CDATA[
$ cvs -d /home/cvsroot cvs rdiff -u -r foo-version-2-0 -r Release-2-1 foo > foo-2.0.patch.20010611
]]></screen>
</listitem>
<listitem>
<para>Subsequent patches can be generated using the new tag as the first tag
in the sequence. (i.e. "-r Release-2-1 -r Release-2-2")
</para>
</listitem>
</orderedlist>
</foil>
</section>

<section>
<title>Add-ons to CVS</title>
<foil><title>cvsweb</title>
<itemizedlist>
<listitem>
<para><ulink url="http://stud.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi/">http://stud.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi/</ulink>
</para>
</listitem>
<listitem>
<para>Web interface for browsing through a repository.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>viewcvs</title>
<itemizedlist>
<listitem>
<para><ulink url="http://viewcvs.sourceforge.net/">http://viewcvs.sourceforge.net/</ulink>
</para>
</listitem>
<listitem>
<para>Originally based on <emphasis>cvsweb</emphasis>, but re-done in python
and with more options.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>cvsgraph</title>
<itemizedlist>
<listitem>
<para><ulink url="http://www.akhphd.au.dk/~bertho/cvsgraph/">http://www.akhphd.au.dk/~bertho/cvsgraph/</ulink>
</para>
</listitem>
<listitem>
<para>Tool to create a graphical view of the evolution of your file(s).
Example:
</para>
</listitem>
</itemizedlist>
</foil>
</section>
<section>
<title>Troubleshooting</title>
<foil><title>Tracing the Connection</title>
<para>To troubleshoot a CVS session, you can trace the connection between the
CVS client and server.
</para>
<itemizedlist>
<listitem>
<para>cvs -t <emphasis>command</emphasis>
</para>
</listitem>
</itemizedlist>
<programlisting>
<![CDATA[
$ cvs -t update
 -> main loop with CVSROOT=lodestar2.dyndns.org:/home/e-smith/files/ibays/cvsroot/files
 -> Starting server: ssh lodestar2.dyndns.org cvs server 
dyork@lodestar2.dyndns.org's password: 
 -> Sending file `maximizing-cvs.xml' to server
 -> unlink_file(CVS/Entries.Static)
 -> unlink_file(CVS/Entries.Static)
S-> unlink_file(CVS/Entries.Static)
S-> unlink_file(./CVS/Entries.Static)
S-> rename(CVS/Entries.Backup,CVS/Entries)
S-> unlink_file(CVS/Entries.Log)
S-> checkout (/home/e-smith/files/ibays/cvsroot/files/presos/cvs/maximizing-cvs/maximizing-cvs.xml,v, 1.2, , (function))
S-> unlink_file(graphics/CVS/Entries.Static)
M maximizing-cvs.xml
 -> unlink_file(CVS/Entries.Static)
S-> rename(CVS/Entries.Backup,CVS/Entries)
S-> unlink_file(CVS/Entries.Log)
S-> rename(CVS/Entries.Backup,CVS/Entries)
S-> unlink_file(CVS/Entries.Log)
$ 
]]>
</programlisting>
</foil>
<foil><title>Using CVS_CLIENT_LOG</title>
<para>You can also log <emphasis>exactly</emphasis> what gets sent by the client and by the server.
</para>
<itemizedlist>
<listitem>
<para>Set the <filename>CVS_CLIENT_LOG</filename> environment variable to the
<emphasis>base filename</emphasis> you want to use.
</para>
</listitem>
<listitem>
<para>Logs will be created as <filename>basename.in</filename> and
<filename>basename.out</filename>
</para>
</listitem>
</itemizedlist>
<para>Example:
</para>
<screen>
$ export CVS_CLIENT_LOG=log20010610
$ cvs -Q update
$ ls log*
log20010610.in  log20010610.out
$
</screen>
<para>Note that this only works with connections to <emphasis>remote</emphasis>
repositories.  It will not work if commands occur on same machine.
</para>
</foil>
<foil><title>History File Permissions</title>
<itemizedlist>
<listitem>
<para>With some earlier versions of CVS (prior to 1.11), the
<filename>history</filename> file was created with the wrong permissions when
the repository is initialized.
</para>
</listitem>
<listitem>
<para>Because of this, users cannot import or check out modules to/from the
repository.
</para>
</listitem>
<listitem>
<para>You must connect to the server and (usually as root) change the
permissions so that everyone has read <emphasis>and</emphasis> write access to
the <filename>history</filename> file.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>File Permissions</title>
<itemizedlist>
<listitem>
<para>All files in repository are set to be <emphasis>read-only</emphasis>.
</para>
</listitem>
<listitem>
<para>If a file is <emphasis>imported</emphasis> or <emphasis>added</emphasis>
with execute (x) permission, that will be retained.
</para>
</listitem>
<listitem>
<para>However, if you later want to make a file executable, you will need to
change the permissions on the actual file <emphasis>in the repository</emphasis> on the server.
</para>
</listitem>
<listitem>
<para>Suggest enabling (as root) the "set group id" bit on module in server
repository so that all subsequent files and directories get created with the
appropriate group permissions:
</para>
<screen>
# cd /home/cvsroot
# chgrp cvsusers documentation
# chmod g+s documentation
</screen>
<para>Note that you may need the <filename>-R</filename> flag if you are doing
this after module already contains data.
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Changing Log Messages</title>
<itemizedlist>
<listitem>
<para>Finally, a tip on dealing with human troubleshooting... if someone
writes an "inappropriate" log message, or makes a mistake with a log message,
it can be modified with the <filename>cvs admin</filename> command with 
<filename>-m</filename> command.
</para>
</listitem>
<listitem>
<para>Example:
</para>
<screen><![CDATA[
$ cvs admin -m 1.3:"Fixed for loop to increment counter correctly" hello.py
RCS file: /home/cvsroot/hello/hello.py,v
done
$ ]]></screen>
</listitem>
<listitem>
<para>Change takes place immediately and is irreversible.
</para>
</listitem>
</itemizedlist>
</foil>
</section>
<section>
<title>Conclusion</title>
<foil><title>Resources</title>
<para>CVS Home Page:
</para>
<itemizedlist>
<listitem>
<para><ulink url="http://www.cvshome.org/">http://www.cvshome.org/</ulink>
</para>
</listitem>
</itemizedlist>
<para>"Cederqvist" CVS user manual:
</para>
<itemizedlist>
<listitem>
<para><ulink url="http://www.cvshome.org/docs/manual/cvs.html">http://www.cvshome.org/docs/manual/cvs.html</ulink>
</para>
</listitem>
</itemizedlist>
<para>Pascal Molli's pages:
</para>
<itemizedlist>
<listitem>
<para><ulink url="http://www.loria.fr/~molli/cvs-index.html">http://www.loria.fr/~molli/cvs-index.html</ulink>
</para>
</listitem>
</itemizedlist>
<para>Books:
</para>
<itemizedlist>
<listitem>
<para><emphasis>CVS Pocket Reference</emphasis>, by Gregor N. Purdy, published
by O'Reilly &amp; Associates, August 2000
</para>
</listitem>
<listitem>
<para><emphasis>Open Source Development with CVS</emphasis>, by Karl Fogel,
published by CoriolisOpen Press, 1999, 
<ulink url="http://cvs.red-bean.com">http://cvs.red-bean.com</ulink>
</para>
</listitem>
</itemizedlist>
</foil>
<foil><title>Questions?</title>
<para>
</para>
</foil>
<foil><title>Contact Information</title>
<para>Dan York, Director of Training, e-smith, inc.
</para>
<itemizedlist>
<listitem>
<para>dyork@e-smith.com
</para>
</listitem>
<listitem>
<para>dyork@Lodestar2.com
</para>
</listitem>
<listitem>
<para>+1-613-263-4312
</para>
</listitem>
</itemizedlist>
<para>This presentation will be available from:
</para>
<itemizedlist>
<listitem>
<para><ulink url="http://www.lodestar2.com/talks/cvs/">http://www.lodestar2.com/talks/cvs/</ulink>
</para>
</listitem>
</itemizedlist>
</foil>
</section>

</slides>
