This is ossdev.info, produced by makeinfo version 4.3 from
../book/ossdev/main.texi.

START-INFO-DIR-ENTRY
* CVS Book: (cvsbook).          A CVS tutorial and reference.
END-INFO-DIR-ENTRY

Copyright (C) 1999, 2000 Karl Fogel <kfogel@red-bean.com>

This document is free software; you can redistribute and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.

This document is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

This manual describes how to use and administer CVS (Concurrent Versions
System).  It is part of a larger work entitled `Open Source Development
With CVS'; please see the introduction for details.

This is version 1.21 of this manual.


File: ossdev.info,  Node: How Watches Work,  Next: Enabling Watches In The Repository,  Up: Watches (CVS As Telephone)

How Watches Work
----------------

In its default behavior, CVS treats each working copy as an isolated
sandbox.  No one knows what you're doing in your working copy until you
commit your changes.  In turn, you don't know what others are doing in
theirs - except via the usual methods of communication, such as
shouting down the hallway, "Hey, I'm going to work on parse.c now.  Let
me know if you're editing it so we can avoid conflicts!"

This informality works for projects where people have a general idea of
who's responsible for what.  However, this process can break down when a
large number of developers are active in all parts of a code base and
want to avoid conflicts.  In such cases, they frequently have to cross
each others' areas of responsibility but can't shout down the hallway at
each other because they're geographically distributed.

A feature of CVS called `watches' provides developers with a way to
notify each other about who is working on what files at a given time.
By "setting a watch" on a file, a developer can have CVS notify her if
anyone else starts to work on that file.  The notifications are normally
sent via email, although it is possible to set up other notification
methods.

To use watches, you must modify one or two files in the repository
administrative area, and developers must add some extra steps to the
usual checkout/update/commit cycle.  The changes on the repository side
are fairly simple: You may need to edit the `CVSROOT/notify' file so
that CVS knows how notifications are to be performed.  You may also
have to add lines to the `CVSROOT/users' file, which supplies external
email addresses.

On the working copy side, developers have to tell CVS which files they
want to watch so that CVS can send them notifications when someone else
starts editing those files.  They also need to tell CVS when they start
or stop editing a file, so CVS can send out notifications to others who
may be watching.  The following commands are used to implement these
extra steps:

   * cvs watch

   * cvs edit

   * cvs unedit

The command `watch' differs from the usual CVS command pattern in that
it requires further subcommands, such as `cvs watch add...',
`cvs watch remove...', and so on.

In the following example, we'll look at how to turn on watches in the
repository and then how to use watches from the developer's side.  The
two example users, jrandom and qsmith, each have their own separate
working copies of the same project; the working copies may even be on
different machines.  As usual, all examples assume that the $CVSROOT
environment variable has already been set, so there's no need to pass -d
<REPOS> to any CVS commands.


File: ossdev.info,  Node: Enabling Watches In The Repository,  Next: Using Watches In Development,  Prev: How Watches Work,  Up: Watches (CVS As Telephone)

Enabling Watches In The Repository
----------------------------------

First, the CVSROOT/notify file must be edited to turn on email
notification.  One of the developers can do this, or the repository
administrator can if the developers don't have permission to change the
repository's administrative files.  In any case, the first thing to do
is check out the administrative area and edit the notify file:

     floss$ cvs -q co CVSROOT
     U CVSROOT/checkoutlist
     U CVSROOT/commitinfo
     U CVSROOT/config
     U CVSROOT/cvswrappers
     U CVSROOT/editinfo
     U CVSROOT/loginfo
     U CVSROOT/modules
     U CVSROOT/notify
     U CVSROOT/rcsinfo
     U CVSROOT/taginfo
     U CVSROOT/verifymsg
     floss$ cd CVSROOT
     floss$ emacs notify
     ...

When you edit the notify file for the first time, you'll see something
like this:

     # The "notify" file controls where notifications from watches set by
     # "cvs watch add" or "cvs edit" are sent. The first entry on a line is
     # a regular expression which is tested against the directory that the
     # change is being made to, relative to the $CVSROOT. If it matches,
     # then the remainder of the line is a filter program that should contain
     # one occurrence of %s for the user to notify, and information on its
     # standard input.
     #
     # "ALL" or "DEFAULT" can be used in place of the regular expression.
     #
     # For example:
     # ALL mail %s -s "CVS notification"

All you really need to do is uncomment the last line by removing the
initial `#' mark.  Although the notify file provides the same flexible
interface as the other administrative files, with regular expressions
matching against directory names, the truth is that you almost never
want to use any of that flexibility.  The only reason to have multiple
lines, with each line's regular expression matching a particular part
of the repository, would be if you wanted to use a different
notification method for each project.  However, normal email is a
perfectly good notification mechanism, so most projects just use that.

To specify email notification, the line

     ALL mail %s -s "CVS notification"

should work on any standard Unix machine.  This command causes
notifications to be sent as emails with the subject line `CVS
notification' (the special expression ALL matches any directory, as
usual).  Having uncommented that line, commit the notify file so the
repository is aware of the change:

     floss$ cvs ci -m "turned on watch notification"
     cvs commit: Examining .
     Checking in notify;
     /usr/local/newrepos/CVSROOT/notify,v  <--  notify
     new revision: 1.2; previous revision: 1.1
     done
     cvs commit: Rebuilding administrative file database
     floss$

Editing the notify file in this way may be all that you'll need to do
for watches in the repository.  However, if there are remote developers
working on the project, you may need to edit the `CVSROOT/users' file,
too.  The purpose of the users file is to tell CVS where to send email
notifications for those users who have external email addresses.  The
format of each line in the users file is:

     CVS_USERNAME:EMAIL_ADDRESS

For example,

     qsmith:quentinsmith@farawayplace.com

The CVS username at the beginning of the line corresponds to a CVS
username in `CVSROOT/password' (if present and the pserver access
method is being used), or failing that, the server-side system username
of the person running CVS.  Following the colon is an external email
address to which CVS should send watch notifications for that user.

Unfortunately, as of this writing, the users file does not exist in the
stock CVS distribution.  Because it's an administrative file, you must
not only create, cvs add, and commit it in the usual way, but also add
it to `CVSROOT/checkoutlist' so that a checked-out copy is always
maintained in the repository.

Here is a sample session demonstrating this:

     floss$ emacs checkoutlist
       ... (add the line for the users file) ...
     floss$ emacs users
       ... (add the line for qsmith) ...
     floss$ cvs add users
     floss$ cvs ci -m "added users to checkoutlist, qsmith to users"
     cvs commit: Examining .
     Checking in checkoutlist;
     /usr/local/newrepos/CVSROOT/checkoutlist,v  <--  checkoutlist
     new revision: 1.2; previous revision: 1.1
     done
     Checking in users;
     /usr/local/newrepos/CVSROOT/users,v  <--  users
     new revision: 1.2; previous revision: 1.1
     done
     cvs commit: Rebuilding administrative file database
     floss$

It's possible to use expanded-format email addresses in
`CVSROOT/users', but you have to be careful to encapsulate all
whitespace within quotes.  For example, the following will work

     qsmith:"Quentin Q. Smith <quentinsmith@farawayplace.com>"

or

     qsmith:'Quentin Q. Smith <quentinsmith@farawayplace.com>'

However, this will not work:

     qsmith:"Quentin Q. Smith" <quentinsmith@farawayplace.com>

When in doubt, you should test by running the command line given in the
notify file manually.  Just replace the `%s' in

     mail %s -s "CVS notification"

with what you have following the colon in users.  If it works when you
run it at a command prompt, it should work in the users file, too.

When it's over, the checkout file will look like this:

     # The "checkoutlist" file is used to support additional version controlled
     # administrative files in $CVSROOT/CVSROOT, such as template files.
     #
     # The first entry on a line is a filename which will be checked out from
     # the corresponding RCS file in the $CVSROOT/CVSROOT directory.
     # The remainder of the line is an error message to use if the file cannot
     # be checked out.
     #
     # File format:
     #
     #       [<whitespace>]<filename><whitespace><error message><end-of-line>
     #
     # comment lines begin with '#'
     
     users   Unable to check out 'users' file in CVSROOT.

The users file will look like this:

     qsmith:quentinsmith@farawayplace.com

Now that the repository is set up for watches, let's look at what
developers need to do in their working copies.


File: ossdev.info,  Node: Using Watches In Development,  Next: Ending An Editing Session,  Prev: Enabling Watches In The Repository,  Up: Watches (CVS As Telephone)

Using Watches In Development
----------------------------

First, a developer checks out a working copy and adds herself to the
list of watchers for one of the files in the project:

     floss$ whoami
     jrandom
     floss$ cvs -q co myproj
     U myproj/README.txt
     U myproj/foo.gif
     U myproj/hello.c
     U myproj/a-subdir/whatever.c
     U myproj/a-subdir/subsubdir/fish.c
     U myproj/b-subdir/random.c
     floss$ cd myproj
     floss$ cvs watch add hello.c
     floss$

The last command, cvs watch add hello.c, tells CVS to notify jrandom if
anyone else starts working on hello.c (that is, it adds jrandom to
hello.c's watch list).  For CVS to send notifications as soon as a file
is being edited, the user who is editing it has to announce the fact by
running cvs edit on the file first.  CVS has no other way of knowing
when someone starts working on a file.  Once checkout is done, CVS isn't
usually invoked until the next update or commit, which happens after the
file has already been edited:

     paste$ whoami
     qsmith
     paste$ cvs -q co myproj
     U myproj/README.txt
     U myproj/foo.gif
     U myproj/hello.c
     U myproj/a-subdir/whatever.c
     U myproj/a-subdir/subsubdir/fish.c
     U myproj/b-subdir/random.c
     paste$ cd myproj
     paste$ cvs edit hello.c
     paste$ emacs hello.c
     ...

When qsmith runs cvs edit hello.c, CVS looks at the watch list for
hello.c, sees that jrandom is on it, and sends email to jrandom telling
her that qsmith has started editing the file.  The email even appears to
come from qsmith:

     From: qsmith
     Subject: CVS notification
     To: jrandom
     Date: Sat, 17 Jul 1999 22:14:43 -0500
     
     myproj hello.c
     --
     Triggered edit watch on /usr/local/newrepos/myproj
     By qsmith
     
     Furthermore, every time that qsmith (or anyone) commits a new revision of hello.c, jrandom will receive another email:
     
     myproj hello.c
     --
     Triggered commit watch on /usr/local/newrepos/myproj
     By qsmith

After receiving these emails, jrandom may want to update hello.c
immediately to see what qsmith has done, or perhaps she'll email qsmith
to find out why he's working on that file.  Note that nothing forced
qsmith to remember to run cvs edit - presumably he did it because he
wanted jrandom to know what he was up to (anyway, even if he forgot to
do cvs edit, his commits would still trigger notifications).  The reason
to use cvs edit is that it notifies watchers before you start to work on
a file.  The watchers can contact you if they think there may be a
conflict, before you've wasted a lot of time.

CVS assumes that anyone who runs cvs edit on a file wants to be added to
the file's watch list, at least temporarily, in case someone else starts
to edit it.  When qsmith ran cvs edit, he became a watcher of hello.c.
Both he and jrandom would have received notification if a third party
had run cvs edit on that file (or committed it).

However, CVS also assumes that the person editing the file only wants to
be on its watch list while he or she is editing it.  Such users are
taken off the watch list when they're done editing.  If they prefer to
be permanent watchers of the file, they would have to run cvs watch add.
CVS makes a default assumption that someone is done editing when he or
she commits a file (until the next time, anyway).

Anyone who gets on a file's watch list solely by virtue of having run
`cvs edit' on that file is known as a "temporary watcher" and is taken
off the watch list as soon as she commits a change to the file.  If she
wants to edit it again, she has to rerun `cvs edit'.

CVS's assumption that the first commit ends the editing session is only
a best guess, of course, because CVS doesn't know how many commits the
person will need to finish their changes.  The guess is probably
accurate for "one-off" changes - changes where someone just needs to
make one quick fix to a file and commit it.  For more prolonged editing
sessions involving several commits, users should add themselves
permanently to the file's watch list:

     paste$ cvs watch add hello.c
     paste$ cvs edit hello.c
     paste$ emacs hello.c
     ...
     paste$ cvs commit -m "print hello in Sanskrit"

Even after the commit, qsmith remains a watcher of hello.c because he
ran watch add on it.  (By the way, qsmith will not receive notification
of his own edits; only other watchers will.  CVS is smart enough not to
notify you about actions that you took.)


File: ossdev.info,  Node: Ending An Editing Session,  Next: Controlling What Actions Are Watched,  Prev: Using Watches In Development,  Up: Watches (CVS As Telephone)

Ending An Editing Session
-------------------------

If you don't want to commit but want to explicitly end an editing
session, you can do so by running cvs unedit:

     paste$ cvs unedit hello.c

But beware!  This does more than just notify all watchers that you're
done editing - it also offers to revert any uncommitted changes that
you've made to the file:

     paste$ cvs unedit hello.c
     hello.c has been modified; revert changes? y
     paste$

If you answer `y', CVS undoes all your changes and notifies watchers
that you're not editing the file anymore.  If you answer `n', CVS keeps
your changes and also keeps you registered as an editor of the file (so
no notification goes out - in fact, it's as if you never ran
`cvs unedit' at all).  The possibility of CVS undoing all of your
changes at a single keystroke is a bit scary, but the rationale is easy
to understand: If you declare to the world that you're ending an
editing session, then any changes you haven't committed are probably
changes you don't mean to keep.  At least, that's the way CVS sees it.
Needless to say, be careful!


File: ossdev.info,  Node: Controlling What Actions Are Watched,  Next: Finding Out Who Is Watching What,  Prev: Ending An Editing Session,  Up: Watches (CVS As Telephone)

Controlling What Actions Are Watched
------------------------------------

By default, watchers are notified about three kinds of action: edits,
commits, and unedits.  However, if you only want to be notified about,
say, commits, you can restrict notifications by adjusting your watch
with the -a flag (a for action):

     floss$ cvs watch add -a commit hello.c

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

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

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

     floss$ cvs watch add -a commit hello.c

has no effect - you'll still be a watcher for all three actions.  To
remove watches, you should run

     floss$ cvs watch remove hello.c

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

     floss$ cvs watch remove -a commit hello.c

This means that you want to stop receiving notifications about commits
but continue to receive notifications about edits and unedits (assuming
you were watching edits and unedits to begin with, that is).

There are two special actions you can pass to the -a flag: all or none.
The former means all actions that are eligible for watching (edits,
commits, and unedits, as of this writing), and the latter means none of
these.  Because CVS's default behavior, in the absence of -a, is to
watch all actions, and because watching none is the same as removing
yourself from the watch list entirely, it's hard to imagine a situation
in which it would be useful to specify either of these two special
actions.  However, cvs edit also takes the -a option, and in this case,
it can be useful to specify all or none.  For example, someone working
on a file very briefly may not want to receive any notifications about
what other people do with the file.  Thus, this command

     paste$ whoami
     qsmith
     paste$ cvs edit -a none README.txt

causes watchers of README.txt to be notified that qsmith is about to
work on it, but qsmith would not be added as a temporary watcher of
README.txt during his editing session (which he normally would have
been), because he asked not to watch any actions.

Remember that you can only affect your own watches with the cvs watch
command.  You may stop watching a certain file yourself, but that won't
change anyone else's watches.


File: ossdev.info,  Node: Finding Out Who Is Watching What,  Next: Reminding People To Use Watches,  Prev: Controlling What Actions Are Watched,  Up: Watches (CVS As Telephone)

Finding Out Who Is Watching What
--------------------------------

Sometimes you may want to know who's watching before you even run cvs
edit or want to see who is editing what without adding yourself to any
watch lists.  Or you may have forgotten exactly what your own status is.
After setting and unsetting a few watches and committing some files,
it's easy to lose track of what you're watching and editing.

CVS provides two commands to show who's watching and who's editing files
- cvs watchers and cvs editors:

     floss$ whoami
     jrandom
     floss$ cvs watch add hello.c
     floss$ cvs watchers hello.c
     hello.c jrandom  edit unedit  commit
     floss$ cvs watch remove -a unedit hello.c
     floss$ cvs watchers hello.c
     hello.c jrandom  edit commit
     floss$ cvs watch add README.txt
     floss$ cvs watchers
     README.txt      jrandom edit    unedit  commit
     hello.c jrandom edit    commit
     floss$

Notice that the last cvs watchers command doesn't specify any files and,
therefore, shows watchers for all files (all those that have watchers,
that is).

All of the watch and edit commands have this behavior in common with
other CVS commands.  If you specify file names, they act on those files.
If you specify directory names, they act on everything in that directory
and its subdirectories.  If you don't specify anything, they act on the
current directory and everything underneath it, to as many levels of
depth as are available.  For example (continuing with the same session):

     floss$ cvs watch add a-subdir/whatever.c
     floss$ cvs watchers
     README.txt      jrandom edit    unedit  commit
     hello.c jrandom edit    commit
     a-subdir/whatever.c     jrandom edit    unedit  commit
     floss$ cvs watch add
     floss$ cvs watchers
     README.txt      jrandom edit    unedit  commit
     foo.gif jrandom edit    unedit  commit
     hello.c jrandom edit    commit  unedit
     a-subdir/whatever.c     jrandom edit    unedit  commit
     a-subdir/subsubdir/fish.c       jrandom edit    unedit  commit
     b-subdir/random.c       jrandom edit    unedit  commit
     floss$

The last two commands made jrandom a watcher of every file in the
project and then showed the watch list for every file in the project,
respectively.  The output of `cvs watchers' doesn't always line up
perfectly in columns because it mixes tab stops with information of
varying length, but the lines are consistently formatted:

     [FILENAME] [whitespace] WATCHER [whitespace] ACTIONS-BEING-WATCHED...

Now watch what happens when qsmith starts to edit one of the files:

     paste$ cvs edit hello.c
     paste$ cvs watchers
     README.txt      jrandom edit    unedit  commit
     foo.gif jrandom edit    unedit  commit
     hello.c jrandom edit    commit  unedit
            qsmith  tedit   tunedit tcommit
     a-subdir/whatever.c     jrandom edit    unedit  commit
     a-subdir/subsubdir/fish.c       jrandom edit    unedit  commit
     b-subdir/random.c       jrandom edit    unedit  commit

The file hello.c has acquired another watcher: qsmith himself (note that
the file name is not repeated but is left as white space at the
beginning of the line - this would be important if you ever wanted to
write a program that parses watchers output).  Because he's editing
hello.c, qsmith has a "temporary watch" on the file; it goes away as
soon as he commits a new revision of hello.c.  The prefix `t' in front
of each of the actions indicates that these are temporary watches.  If
qsmith adds himself as a regular watcher of hello.c as well

     paste$ cvs watch add hello.c
     README.txt      jrandom edit    unedit  commit
     foo.gif jrandom edit    unedit  commit
     hello.c jrandom edit    commit  unedit
            qsmith  tedit   tunedit tcommit edit    unedit  commit
     a-subdir/whatever.c     jrandom edit    unedit  commit
     a-subdir/subsubdir/fish.c       jrandom edit    unedit  commit
     b-subdir/random.c       jrandom edit    unedit  commit

he is listed as both a temporary watcher and a permanent watcher.  You
may think that the permanent watch status would simply override the
temporary, so that the line would look like this:

             qsmith  edit    unedit  commit

However, CVS can't just replace the temporary watches because it doesn't
know in what order things happen.  Will qsmith remove himself from the
permanent watch list before ending his editing session, or will he
finish the edits while still remaining a watcher?  If the former, the
edit/unedit/commit actions disappear while the tedit/tunedit/tcommit
ones remain; if the latter, the reverse would happen.

Anyway, that side of the watch list is usually not of great concern.
Most of the time, what you want to do is run

     floss$ cvs watchers

or

     floss$ cvs editors

from the top level of a project and see who's doing what.  You don't
really need to know the details of who cares about what actions: the
important things are people and files.


File: ossdev.info,  Node: Reminding People To Use Watches,  Next: What Watches Look Like In The Repository,  Prev: Finding Out Who Is Watching What,  Up: Watches (CVS As Telephone)

Reminding People To Use Watches
-------------------------------

You've probably noticed that the watch features are utterly dependent on
the cooperation of all the developers.  If someone just starts editing a
file without first running cvs edit, no one else will know about it
until the changes get committed.  Because cvs edit is an additional
step, not part of the normal development routine, people can easily
forget to do it.

Although CVS can't force someone to use cvs edit, it does have a
mechanism for reminding people to do so - the watch on command:

     floss$ cvs -q co myproj
     U myproj/README.txt
     U myproj/foo.gif
     U myproj/hello.c
     U myproj/a-subdir/whatever.c
     U myproj/a-subdir/subsubdir/fish.c
     U myproj/b-subdir/random.c
     floss$ cd myproj
     floss$ cvs watch on hello.c
     floss$

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

     paste$ cvs -q co myproj
     U myproj/README.txt
     U myproj/foo.gif
     U myproj/hello.c
     U myproj/a-subdir/whatever.c
     U myproj/a-subdir/subsubdir/fish.c
     U myproj/b-subdir/random.c
     paste$ cd myproj
     paste$ ls -l
     total 6
     drwxr-xr-x   2 qsmith    users        1024 Jul 19 01:06 CVS/
     -rw-r--r--   1 qsmith    users          38 Jul 12 11:28 README.txt
     drwxr-xr-x   4 qsmith    users        1024 Jul 19 01:06 a-subdir/
     drwxr-xr-x   3 qsmith    users        1024 Jul 19 01:06 b-subdir/
     -rw-r--r--   1 qsmith    users         673 Jun 20 22:47 foo.gif
     -r--r--r--   1 qsmith    users         188 Jul 18 01:20 hello.c
     paste$

When he does so, the file becomes read-write.  He can then edit it, and
when he commits, it becomes read-only again:

     paste$ cvs edit hello.c
     paste$ ls -l hello.c
     -rw-r--r--   1 qsmith    users         188 Jul 18 01:20 hello.c
     paste$ emacs hello.c
       ...
     paste$ cvs commit -m "say hello in Aramaic" hello.c
     Checking in hello.c;
     /usr/local/newrepos/myproj/hello.c,v  <--  hello.c
     new revision: 1.12; previous revision: 1.11
     done
     paste$ ls -l hello.c
     -r--r--r--   1 qsmith    users         210 Jul 19 01:12 hello.c
     paste$

His edit and commit will send notification to all watchers of hello.c.
Note that jrandom isn't necessarily one of them.  By running cvs watch
on hello.c, jrandom did not add herself to the watch list for that file;
she merely specified that it should be checked out read-only.  People
who want to watch a file must remember to add themselves to its watch
list - CVS cannot help them with that.

Turning on watches for a single file may be the exception.  Generally,
it's more common to turn on watches project-wide:

     floss$ cvs -q co myproj
     U myproj/README.txt
     U myproj/foo.gif
     U myproj/hello.c
     U myproj/a-subdir/whatever.c
     U myproj/a-subdir/subsubdir/fish.c
     U myproj/b-subdir/random.c
     floss$ cd myproj
     floss$ cvs watch on
     floss$

This action amounts to announcing a policy decision for the entire
project: "Please use cvs edit to tell watchers what you're working on,
and feel free to watch any file you're interested in or responsible
for."  Every file in the project will be checked out read-only, and thus
people will be reminded that they're expected to use cvs edit before
working on anything.

Curiously, although checkouts of watched files make them read-only,
updates do not.  If qsmith had checked out his working copy before
jrandom ran cvs watch on, his files would have stayed read-write,
remaining so even after updates.  However, any file he commits after
jrandom turns watching on will become read-only.  If jrandom turns off
watches

     floss$ cvs watch off

qsmith's read-only files do not magically become read-write.  On the
other hand, after he commits one, it will not revert to read-only again
(as it would have if watches were still on).

It's worth noting that qsmith could, were he truly devious, make files
in his working copy writeable by using the standard Unix `chmod'
command, bypassing `cvs edit' entirely

     paste$ chmod u+w hello.c

or if he wanted to get everything in one fell swoop:

     paste$ chmod -R u+w .

There is nothing CVS can do about this.  Working copies by their nature
are private sandboxes - the watch features can open them up to public
scrutiny a little bit, but only as far as the developer permits.  Only
when a developer does something that affects the repository (such as
commits) is her privacy unconditionally lost.

The relationship among watch add, watch remove, watch on, and watch off
probably seems a bit confusing.  It may help to summarize the overall
scheme: `add' and `remove' are about adding or removing users from a
file's watch list; they don't have anything to do with whether files
are read-only on checkout or after commits.  `on' and `off' are only
about file permissions.  They don't have anything to do with who is on
a file's watch list; rather, they are tools to help remind developers
of the watch policy by causing working-copy files to become read-only.

All of this may seem a little inconsistent.  In a sense, using watches
works against the grain of CVS.  It deviates from the idealized universe
of multiple developers editing freely in their working copies, hidden
from each other until they choose to commit.  With watches, CVS gives
developers convenient shortcuts for informing each other of what's going
on in their working copies; however, it has no way to enforce
observation policies, nor does it have a definitive concept of what
constitutes an editing session.  Nevertheless, watches can be helpful in
certain circumstances if developers work with them.


File: ossdev.info,  Node: What Watches Look Like In The Repository,  Prev: Reminding People To Use Watches,  Up: Watches (CVS As Telephone)

What Watches Look Like In The Repository
----------------------------------------

In the interests of stamping out black boxes and needless mystery, let's
take a quick look at how watches are implemented in the repository.
We'll only take a quick look, though, because it's not pretty.

When you set a watch

     floss$ pwd
     /home/jrandom/myproj
     floss$ cvs watch add hello.c
     floss$ cvs watchers
     hello.c jrandom edit    unedit  commit
     floss$

CVS records it in the special file, `CVS/fileattr', in the appropriate
repository subdirectory:

     floss$ cd /usr/local/newrepos
     floss$ ls
     CVSROOT/   myproj/
     floss$ cd myproj
     floss$ ls
     CVS/          a-subdir/     foo.gif,v
     README.txt,v  b-subdir/     hello.c,v
     floss$ cd CVS
     floss$ ls
     fileattr
     floss$ cat fileattr
     Fhello.c        _watchers=jrandom>edit+unedit+commit
     floss$

The fact that fileattr is stored in a CVS subdirectory in the repository
does not mean that the repository has become a working copy.  It's
simply that the name `CVS' was already reserved for bookkeeping in the
working copy, so CVS can be sure no project will ever need a
subdirectory of that name in the repository.

I won't describe the format of `fileattr' formally; you can probably
grok it pretty well just by watching it change from command to command:

     floss$ cvs watch add hello.c
     floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
     Fhello.c        _watchers=jrandom>edit+unedit+commit
     floss$ cvs watch add README.txt
     floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
     Fhello.c        _watchers=jrandom>edit+unedit+commit
     FREADME.txt     _watchers=jrandom>edit+unedit+commit
     floss$ cvs watch on hello.c
     floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
     Fhello.c        _watchers=jrandom>edit+unedit+commit;_watched=
     FREADME.txt     _watchers=jrandom>edit+unedit+commit
     floss$ cvs watch remove hello.c
     floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
     Fhello.c        _watched=
     FREADME.txt     _watchers=jrandom>edit+unedit+commit
     floss$ cvs watch off hello.c
     floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
     FREADME.txt     _watchers=jrandom>edit+unedit+commit
     floss$

Edit records are stored in fileattr, too.  Here's what happens when
qsmith adds himself as an editor:

     paste$ cvs edit hello.c
     
     floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
     Fhello.c        _watched=;_editors=qsmith>Tue Jul 20 04:53:23 1999 GMT+floss\
     +/home/qsmith/myproj;_watchers=qsmith>tedit+tunedit+tcommit
     FREADME.txt     _watchers=jrandom>edit+unedit+commit

Finally, note that CVS removes fileattr and the CVS subdirectory when
there are no more watchers or editors for any of the files in that
directory:

     paste$ cvs unedit
     
     floss$ cvs watch off
     floss$ cvs watch remove
     floss$ cat /usr/local/newrepos/myproj/CVS/fileattr
     cat: /usr/local/newrepos/myproj/CVS/fileattr: No such file or directory
     floss$

It should be clear after this brief exposure that the details of parsing
fileattr format are better left to CVS.  The main reason to have a basic
understanding of the format - aside from the inherent satisfaction of
knowing what's going on behind the curtain - is if you try to write an
extension to the CVS watch features or debug some problem in them.  It's
sufficient to know that you shouldn't be alarmed if you see CVS/
subdirectories popping up in your repository.  They're the only safe
place CVS has to store meta-information such as watch lists.


File: ossdev.info,  Node: Log Messages And Commit Emails,  Next: Changing A Log Message After Commit,  Prev: Watches (CVS As Telephone),  Up: Advanced CVS

Log Messages And Commit Emails
==============================

Commit emails are notices sent out at commit time, showing the log
message and files involved in the commit.  They usually go to all
project participants and sometimes to other interested parties.  The
details of setting up commit emails were covered in *Note Repository
Administration::, so I won't repeat them here.  I have noticed, however,
that commit emails can sometimes result in unexpected side effects to
projects, effects that you may want to take into account if you set up
commit emails for your project.

First, be prepared for the messages to be mostly ignored.  Whether
people read them depends, at least partly, on the frequency of commits
in your project.  Do developers tend to commit one big change at the end
of the day, or many small changes throughout the day?  The closer your
project is to the latter, the thicker the barrage of tiny commit notices
raining down on the developers all day long, and the less inclined they
will be to pay attention to each message.

This doesn't mean the notices aren't useful, just that you shouldn't
count on every person reading every message.  It's still a convenient
way for people to keep tabs on who's doing what (without the
intrusiveness of watches).  When the emails go to a publicly
subscribable mailing list, they are a wonderful mechanism for giving
interested users (and future developers!) a chance to see what happens
in the code on a daily basis.

You may want to consider having a designated developer who watches all
log messages and has an overview of activity across the entire project
(of course, a good project leader will probably be doing this anyway).
If there are clear divisions of responsibility - say, certain
developers are "in charge of" certain subdirectories of the project -
you could do some fancy scripting in CVSROOT/loginfo to see that each
responsible party receives specially marked notices of changes made in
their area.  This will help ensure that the developers will at least
read the email that pertains to their subdirectories.

A more interesting side effect happens when commit emails aren't
ignored.  People start to use them as a realtime communications method.
Here's the kind of log message that can result:

     Finished feedback form; fixed the fonts and background colors on the
     home page.  Whew!  Anyone want to go to Mon Lung for lunch?

There's nothing wrong with this, and it makes the logs more fun to read
over later.  However, people need to be aware that log messages, such as
the following, are not only distributed by email but is also preserved
forever in the project's history.  For example, griping about customer
specifications is a frequent pastime among programmers; it's not hard to
imagine someone committing a log message like this one, knowing that the
other programmers will soon see it in their email:

     Truncate four-digit years to two-digits in input.  What the customer
     wants, the customer gets, no matter how silly & wrong.  Sigh.

This makes for an amusing email, but what happens if the customer
reviews the logs someday?  (I'll bet similar concerns have led more than
one site to set up CVSROOT/loginfo so that it invokes scripts to guard
against offensive words in log messages!)

The overall effect of commit emails seems to be that people become less
willing to write short or obscure log messages, which is probably a good
thing.  However, they may need to be reminded that their audience is
anyone who might ever read the logs, not just the people receiving
commit emails.


File: ossdev.info,  Node: Changing A Log Message After Commit,  Next: Getting Rid Of A Working Copy,  Prev: Log Messages And Commit Emails,  Up: Advanced CVS

Changing A Log Message After Commit
===================================

Just in case someone does commit a regrettable log message, CVS enables
you to rewrite logs after they've been committed.  It's done with the -m
option to the admin command (this command is covered in more detail
later in this chapter) and allows you to change one log message (per
revision, per file) at a time.  Here's how it works:

     floss$ cvs admin -m 1.7:"Truncate four-digit years to two in input." date.c
     RCS file: /usr/local/newrepos/someproj/date.c,v
     done
     floss$

The original, offensive log message that was committed with revision 1.7
has been replaced with a perfectly innocent - albeit duller - message.
Don't forget the colon separating the revision number from the new log
message.

If the new log message consists of multiple lines, put it in a file and
do this:

     floss$ cvs admin -m 1.7:"`cat new-log-message.txt`" date.c

(This example was sent in by Peter Ross <peter.ross@miscrit.be>; note
that it only works for Unix users.)

If the bad message was committed into multiple files, you'll have to run
cvs admin separately for each one, because the revision number is
different for each file.  Therefore, this is one of the few commands in
CVS that requires you to pass a single file name as argument:

     floss$ cvs admin -m 1.2:"very boring log message" hello.c README.txt foo.gif
     cvs admin: while processing more than one file:
     cvs [admin aborted]: attempt to specify a numeric revision
     floss$

Confusingly, you get the same error if you pass no file names (because
CVS then assumes all the files in the current directory and below are
implied arguments):

     floss$ cvs admin -m 1.2:"very boring log message"
     cvs admin: while processing more than one file:
     cvs [admin aborted]: attempt to specify a numeric revision
     floss$

(As is unfortunately often the case with CVS error messages, you have to
see things from CVS's point of view before the message makes sense!)

Invoking `admin -m' actually changes the project's history, so use it
with care.  There will be no record that the log message was ever
changed - it will simply appear as if that revision had been originally
committed with the new log message.  No trace of the old message will be
left anywhere (unless you saved the original commit email).

Although its name might seem to imply that only the designated CVS
administrator can use it, in fact anyone can run `cvs admin', as long
as they have write access to the project in question.  Nevertheless, it
is best used with caution; the ability to change log messages is mild
compared with other potentially damaging things it can do.  See *Note
CVS Reference:: for more about `admin', as well as a way to restrict
its use.


File: ossdev.info,  Node: Getting Rid Of A Working Copy,  Next: History -- A Summary Of Repository Activity,  Prev: Changing A Log Message After Commit,  Up: Advanced CVS

Getting Rid Of A Working Copy
=============================

In typical CVS usage, the way to get rid of a working copy directory
tree is to remove it like any other directory tree:

     paste$ rm -rf myproj

However, if you eliminate your working copy this way, other developers
will not know that you have stopped using it.  CVS provides a command to
relinquish a working copy explicitly.  Think of release as the opposite
of checkout - you're telling the repository that you're done with the
working copy now.  Like checkout, release is invoked from the parent
directory of the tree:

     paste$ pwd
     /home/qsmith/myproj
     paste$ cd ..
     paste$ ls
     myproj
     paste$ cvs release myproj
     You have [0] altered files in this repository.
     Are you sure you want to release directory 'myproj': y
     paste$

If there are any uncommitted changes in the repository, the release
fails, meaning that it just lists the modified files and otherwise has
no effect.  Assuming the tree is clean (totally up to date), release
records in the repository that the working copy has been released.

You can also have release automatically delete the working tree for you,
by passing the -d flag:

     paste$ ls
     myproj
     paste$ cvs release -d myproj
     You have [0] altered files in this repository.
     Are you sure you want to release (and delete) directory 'myproj: y
     paste$ ls
     paste$

As of CVS version 1.10.6, the release command is not able to deduce the
repository's location by examining the working copy (this is because
release is invoked from above the working copy, not within it).  You
must pass the `-d <REPOS>' global option or make sure that your CVSROOT
environment variable is set correctly.  (This bug may be fixed in
future versions of CVS.)

The Cederqvist claims that if you use release instead of just deleting
the working tree, people with watches set on the released files will be
notified just as if you had run `unedit'.  However, I tried to verify
this experimentally, and it does not seem to be true.


File: ossdev.info,  Node: History -- A Summary Of Repository Activity,  Next: Annotations -- A Detailed View Of Project Activity,  Prev: Getting Rid Of A Working Copy,  Up: Advanced CVS

History - A Summary Of Repository Activity
==========================================

In *Note Repository Administration::, I briefly mentioned the cvs
history command.  This command displays a summary of all checkouts,
commits, updates, rtags, and releases done in the repository (at least,
since logging was enabled by the creation of the CVSROOT/history file
in the repository).  You can control the format and contents of the
summary with various options.

The first step is to make sure that logging is enabled in your
repository.  The repository administrator should first make sure there
is a history file

     floss$ cd /usr/local/newrepos/CVSROOT
     floss$ ls -l history
     ls: history: No such file or directory
     floss$

and if there isn't one, create it, as follows:

     floss$ touch history
     floss$ ls -l history
     -rw-r--r--   1 jrandom   cvs           0 Jul 22 14:57 history
     floss$

This history file also needs to be writeable by everyone who uses the
repository, otherwise they'll get an error every time they try to run a
CVS command that modifies that file.  The easiest way is simply to make
the file world-writeable:

     floss$ chmod a+rw history
     floss$ ls -l history
     -rw-rw-rw-   1 jrandom   cvs           0 Jul 22 14:57 history
     floss$

If the repository was created with the `cvs init' command, the history
file already exists.  You may still have to fix its permissions,
however.

The rest of these examples assume that history logging has been enabled
for a while, so that data has had time to accumulate in the history
file.

The output of cvs history is somewhat terse (it's probably intended to
be parsed by programs rather than humans, although it is readable with a
little study).  Let's run it once and see what we get:

     paste$ pwd
     /home/qsmith/myproj
     paste$ cvs history -e -a
     O 07/25 15:14 +0000 qsmith  myproj =mp=     ~/*
     M 07/25 15:16 +0000 qsmith  1.14 hello.c    myproj == ~/mp
     U 07/25 15:21 +0000 qsmith  1.14 README.txt myproj == ~/mp
     G 07/25 15:21 +0000 qsmith  1.15 hello.c    myproj == ~/mp
     A 07/25 15:22 +0000 qsmith  1.1  goodbye.c  myproj == ~/mp
     M 07/25 15:23 +0000 qsmith  1.16 hello.c    myproj == ~/mp
     M 07/25 15:26 +0000 qsmith  1.17 hello.c    myproj == ~/mp
     U 07/25 15:29 +0000 qsmith  1.2  goodbye.c  myproj == ~/mp
     G 07/25 15:29 +0000 qsmith  1.18 hello.c    myproj == ~/mp
     M 07/25 15:30 +0000 qsmith  1.19 hello.c    myproj == ~/mp
     O 07/23 03:45 +0000 jrandom myproj =myproj= ~/src/*
     F 07/23 03:48 +0000 jrandom        =myproj= ~/src/*
     F 07/23 04:06 +0000 jrandom        =myproj= ~/src/*
     M 07/25 15:12 +0000 jrandom 1.13 README.txt myproj == ~/src/myproj
     U 07/25 15:17 +0000 jrandom 1.14 hello.c    myproj == ~/src/myproj
     M 07/25 15:18 +0000 jrandom 1.14 README.txt myproj == ~/src/myproj
     M 07/25 15:18 +0000 jrandom 1.15 hello.c    myproj == ~/src/myproj
     U 07/25 15:23 +0000 jrandom 1.1  goodbye.c  myproj == ~/src/myproj
     U 07/25 15:23 +0000 jrandom 1.16 hello.c    myproj == ~/src/myproj
     U 07/25 15:26 +0000 jrandom 1.1  goodbye.c  myproj == ~/src/myproj
     G 07/25 15:26 +0000 jrandom 1.17 hello.c    myproj == ~/src/myproj
     M 07/25 15:27 +0000 jrandom 1.18 hello.c    myproj == ~/src/myproj
     C 07/25 15:30 +0000 jrandom 1.19 hello.c    myproj == ~/src/myproj
     M 07/25 15:31 +0000 jrandom 1.20 hello.c    myproj == ~/src/myproj
     M 07/25 16:29 +0000 jrandom 1.3  whatever.c myproj/a-subdir == ~/src/myproj
     paste$

There, isn't that clear?

Before we examine the output, notice that the invocation included two
options: -e and -a.  When you run history, you almost always want to
pass options telling it what data to report and how to report it.  In
this respect, it differs from most other CVS commands, which usually do
something useful when invoked without any options.  In this example, the
two flags meant "everything" (show every kind of event that happened)
and "all" (for all users), respectively.

Another way that history differs from other commands is that, although
it is usually invoked from within a working copy, it does not restrict
its output to that working copy's project.  Instead, it shows all
history events from all projects in the repository - the working copy
merely serves to tell CVS from which repository to retrieve the history
data.  (In the preceding example, the only history data in that
repository is for the `myproj' project, so that's all we see.)

The general format of the output is:

     CODE DATE USER [REVISION] [FILE] PATH_IN_REPOSITORY ACTUAL_WORKING_COPY_NAME

The code letters refer to various CVS operations, as shown in Table 6.1.

For operations (such as checkout) that are about the project as a whole
rather than about individual files, the revision and file are omitted,
and the repository path is placed between the equal signs.

Although the output of the history command was designed to be compact,
parseable input for other programs, CVS still gives you a lot of control
over its scope and content.  The options shown in Table 6.2 control what
types of events get reported.

     Table 6.1  The meaning of the code letters.
     
     Letter	        Meaning
     ======          =========================================================
     O		Checkout
     T		Tag
     F		Release
     W		Update (no user file, remove from entries file)
     U		Update (file overwrote unmodified user file)
     G		Update (file was merged successfully into modified user file)
     C		Update (file was merged, but conflicts w/ modified user file)
     M		Commit (from modified file)
     A		Commit (an added file)
     R		Commit (the removal of a file)
     E		Export

     Table 6.2  Options to filter by event type.
     
     Option	        Meaning
     ==========      =========================================================
     -m MODULE	Show historical events affecting MODULE.
     -c		Show commit events.
     -o		Show checkout events.
     -T		Show tag events.
     -x CODE(S)	Show all events of type CODE (one or more of OTFWUGCMARE).
     -e		Show all types of events, period.  Once you have
                     selected what type of events you want reported, you can
                     filter further with the options shown in Table 6.3.

     Table 6.3  Options to filter by user.
     
     Option	        Meaning
     ==========      =========================================================
     -a		Show actions taken by all users
     -w		Show only actions taken from within this working copy
     -l		Show only the last time this user took the action
     -u USER 	Show records for USER

