CVSCC - Overview

    This document is a brief introduction to change control as implemented
by the CVSCC system.  Many of the statements in here ignore some of the
finer details in favor of explaining the overall philosophy.  For example,
this document puts a lot of responsibility onto a single individual (the
Module owner), when it is really possible to distribute this.  Also, the
command syntax given here is only accurate enough to get the idea across.
Many needed options, allowable abbreviations, and shortcuts are omitted.
    For details of what can and cannot be done and by whom, see the commands
reference.

Introduction
------------

CVSCC is a rudimentary change control system that wraps around CVS commands.

It is designed to work in front of CVS, so that the user never uses cvs
commands directly (there are still some exceptions to this rule, but
more functionality is being added to CVSCC to achieve this goal).  It
maintains a database separate from CVS (currently a MySQL database) in
order to track all of its objects.

Adminisrative
-------------

The first object it tracks is a User.  Each user that is going to use
CVSCC needs a user entry in the database (created with "User create").
This entry records the name and email address of the user (and some
other customizations), and is referenced by other objects as needed.

The second object is a Module.  Each CVS module that you want to use
CVSCC for needs to have a Module created for it ("Module create").
Each Module has an owner (a User).  The owner of a Module has a few
fundamental responsibilities, as detailed below.  The Module must have
the same name in CVSCC as its name in CVSROOT/modules.

Release Management
------------------
The third object is a Release.  A Release works in much the same way
as a cvs "tag".  A release is effectively a snapshot of one version
of all of the files in a Module.  When you initially create a Module,
a Release called "root" is automatically created, and points to the
current version of every file in the module.

By using a database, CVSCC can allow pretty much any name for a Release,
such as "v0_1", "0.1", "John's initial attempt" or whatever.  Simple
names are best, because you will need to type the Release name whenever
you want to manipulate it.

Each Release belongs to a Module and has an owner.  Each release (except
root) has a parent.  The idea is that to work on a Module, you have a
Release as a starting point.  You create a new Release, whose parent
is the starting point Release (Release create 0.1 --parent root).
Then, to get the sources, you check out the release (Release checkout 0.1),
make changes, and commit the release (Release commit) as necessary. At
some point.  Every time you commit changes, the release name "moves
forward".  At some point, you'll want to "freeze" the release, locking
that name into that set of files forever.  If you do a "Release report"
you'll see that your release is in the "open" state.  If you then do
a "Release close 0.1", it will change to the "closed" state.  Once closed,
no more changes can be made to the release.  However, closed Releases
are eligible to have children.  To make more changes, create a new
release: "Release create 0.2 --parent 0.1".

So far, CVSCC adds a lot of value to CVS.  The Release command hides
all of the internal structure of CVS, most importantly the parts
that deal with branching.  For example, you create your first release
in your module called 0.1.  This is a developmental release.  You
have a list of things you want to accomplish in this first run. Once
there, you close the release and start working on 0.2.  You may
progress through several releases, until you get to 0.5a1 (first alpha)
and later 0.5b1 (first beta).  When you are ready to release the product,
you create release 1.0 and close it.  You release 1.0 to the public and
start working towards 1.1, starting with 1.0.1.  Sometime later, your
release history looks like:

[...] -> 1.0 -> 1.0.1 -> 1.0.2 -> 1.0.3

    At this point, someone reports a serious error with version 1.0.
You can't fix it in 1.0.3, because it won't be ready for release
for quite some time (it is a development release and has numerous
problems, being in an intermediate state).  The correct procedure is
to create a new release off of 1.0, fix it there, and close that
release:

$ Release create 1.0p1 --parent 1.0

[...] -> 1.0 -> 1.0.1 -> 1.0.2 -> 1.0.3
            \
             1.0p1

You should apply the fix to both 1.0p1 AND 1.0.3, instead of merging the
"branch" into the main code later (see the sections on Change Control
below for the advanced CVSCC methods of managing this, so that you can
control when changes are applied, etc.).  CVS provides this functionality.
However, it does not provide any management service to track what branches
exist or why they exist.  The "Release report" and "Release view" commands
give a better picture of what is going on than CVS provides.  In the next
section, we'll see how Change Control improves this by a couple of orders
of magnitude.

Change Control
--------------

When you read this, your first reaction is probably going to be, "Oh no,
not another bug tracker".  Go ahead and react that way, but continue to
read the section, especially the section on Tracks, and you'll see why
"another bug tracker" is necessary.  In fact, CVSCC is significantly
more than just a bug tracker.

The next object in CVSCC is a Change.  A Change is a request (not a
commitment) to make a change to the code.  By default, there are two
types of Changes: defects and features.  CVSCC does not distinguish
the two with any functionality.  It is just a type field (in fact,
future plans for CVSCC include user createable change types).

Each Change has a unique integer identifier, so that each change can
be referenced by its number.  Each change has a state (open, working,
closed, canceled, returned), a creator, and an owner.  It also has a
short one-line description (the abstract).  Any user can add notes
to a Change to describe the reported problem, their thoughts on a
solution, etc.

The initial owner of a Change is the owner of the Module in which it
is created (later, there will be more flexibility on the intial owner).
The idea is that the Module owner will "assign" the Change to the
person who should fix it (You use Change report to see all changes,
which you can limit by state, owner, etc., in order to see just the
ones you own).  The new owner can then assign it to someone else,
as appropriate.

At some point, code changes may be necessary.  If you do not use
Tracks (see below), then the Module owner should probably not assign
the Change to a User until he actually wants the changes made.
Alternately, he can assign it to someone for sizing, who can then
assign it back to him once they have researched it and added notes.

To make the code changes, the owner should accept (Change accept) the
change, which puts it into the working state (other options are to
return the change [e.g., usage error or duplicates an earlier change],
or to cancel it).  Now they can checkout the Release (or Releases,
in case you want to patch an earlier release, or have simultaneous
development of the same Release), and make and commit their changes.
Once done, they close the Change (Change close).

There are several problems with this method that CVS also shares:

- No record of which changes were fixed in a given release. The project
  has to track this separately so that a report can be included with the
  release indicating which bugs were fixed and which features were added.

- No record of which releases a change was fixed in.  If the defect was
  found in 1.0, and needed to be fixed in 1.0p1 AND in the current
  development release, 1.0.3, there is no way to check that this was
  done.  More importantly, it may be that it needs to be fixed right away
  in 1.0p1, but we want to wait on fixing it in the development releases,
  but there is no way to keep track of it.

    These first two are usually handled manually, out of band (such as
mailing lists, etc.).  However, if Joe Coder fixes the defect in 1.0p1,
but is in the middle of working on the 1.0.3 fix, and is then in a car
accident, there is no record of work done so far.  We don't know if it
is fixed in 1.0p1, yet, or partially fixed in 1.0.3, etc.

- No record of what changes were made to fix the defect.  Certainly, Joe
  Coder would log the changes to each file with an explanation of the
  change and the comment (fixes Change 1378).  However, humans are
  notoriously sloppy at recording such things, and CVS provides no
  mechanism for searching for all files that have log entries such as
  that.  What if we need to back that change out later?

     Tracks solve all of these problems.  When using Tracks, one NEVER
checks out a Release, and then edits files to fix Changes.  Instead,
once a Change is accepted, you create a Track.  A Track is a sandbox
that lives on the CVS server and records the file changes needed to
fix a given change in a given release.  If a change needs to be fixed
in multiple releases, you create multiple tracks. E.g., 1.0 and 2.0 are
released to the public, and development is currently proceeding with 2.0.9:

$ Track create --change 1378 --release 1.0
$ Track create --change 1378 --release 2.0
$ Track create --change 1378 --release 2.0.9

     But wait a second... you aren't fixing the change in 1.0 and 2.0, you
are fixing it in 1.0p1 and 2.0p1, aren't you?
     No, you are not.  When you use Tracks, you fix the defect where you
found it.  Once it is fixed, your changes are _applied_ to the new release
(1.0p2, etc.).
     How does this work?  Like this.  You have created a Track for
1378 in Release 1.0.  Whenever you want to work on it, you check it
out:

$ Track checkout --change 1389 --release 1.0

     This checks out the source code into the directory foo-1.0-1389 (where
"foo" is the Module name).  The Track is initialized to being an exact copy
of the base 1.0 release.  As you make changes (ANY changes!), you check them
back into the server:

$ Track commit --change 1389 --release 1.0

THIS DOES NOT CHANGE ANY RELEASES!
     It does not change the release 1.0.  Each track has its own sandbox,
and that is where the changes are stored.  It does not change release 1.0p1.
For that matter, release 1.0p1 may not have even been created.  The idea is
that you can send your file changes to the server as often as you like, with
no damage done to ANYONE's work, no matter how bad the changes.
     You can delete your local copy if you like, and check out the Track
again later.  You should see all of the changes you have committed so far.
If you have to stop working on a Track (e.g., you are going on vacation,
you don't have time for the project anymore, whatever), you can assign the
Track to another User.  They can then check it out, and see all of the file
changes you have made so far.  Perhaps there is some code change necessary
that you are not good at, but user jane is.  You can assign the Track to her,
she can make the change, assign it back to you, and then you can continue
working the Track.
     To signal the project that you have completed all of the changes needed
in the Track, you change it to the integrate state:

$ Track integrate --change 1389 --release 1.0

     This marks the code changes as being blessed by the Track's owner as
ready for production (well, the next development build, anyway).  The
Module owner decides when it is time for a new Release.  He creates a new
release (Release create 1.0p1 --parent 1.0), and then looks for all tracks
that are in the integrate state for the parent release:

$ Track report --release 1.0 --state integrate

      He then chooses which ones are going to be used to build the
next release and integrates them:

$ Release checkout 1.0p1
$ cd foo-1.0p1
$ Release integrate --change 1389
$ Release integrate --change 1371
$ Release integrate --change 1499

     There may be issues with code conflicts.  If so, the Release integrate
command will display a warning.  The conflict can then be resolved by editing
the files (yes, this is a great oversimplification; this is covered in other
documentation).  However, nothing has been changed on the server yet.  At
this point, he can still change his mind and delete the whole directory and
start over with the checkout command.
     Once he is happy with the integrations, he can commit the release.
This writes the changes to the server, so that users checking out the
1.0p1 release will see the changes.  The Release is still open, however,
so he can integrate more Tracks later by just checking out the Release
again.  Eventually, the Release is closed, and no more Tracks can be
integrated.
     How does this answer our issues before?

- If you do a "Release view 1.0p1 --long", you'll see all of the Changes
  whose Tracks were integrated into this Release.

- If you do a "Change view 1389 --long", you'll see the Tracks created
  for this change (i.e., which releases have Tracks for this Change).

- If you do a Change view 1389 --long, you'll see all of the Tracks and
  their current state.  If one is in the integrate state, then the
  programmer believes the Change is fixed in that Release.  If it is still
  in the working state, then he is not finished.  However, you can re-assign
  the Track to someone if necessary, and they can finish it.
  Finally, any Track in the entire history of the project (well, since it
  was put into CVSCC) can be checked out to see what changes were made to
  fix that Change in that Release.
  If you do a Track report --owner whomever, you'll see the status of all
  Tracks that "whomever" was working on.  If you do the same Change report,
  you'll see the list of Changes (which could include those for which no
  Tracks have been created).

Summary
-------
    Try it!  If you are unwilling to switch to CVSCC without a trial run,
no problem.  Create a copy of the source tree inside CVS, and then create
a Module for the copy.  Then, use it as if you meant it, or create a bunch
of dummy changes and fake fixes.  In particular, do this and make considerable
use of the Change view, Change report, Track report, Release report, and
Release view commands.  All of the view commands take the --long options.
Try passing around Changes between Users, adding notes, dropping code and
creating several Releases in a couple of different chains.  Look at the
view and report commands often to see how much of a picture of your project
you can get.
    Also, almost every action can generate a notification email,  With
the User command, you can define which events you are interested in
(i.e, only send me emails when a new Change is created, or a note is
added to a Change that I own).  Might as well turn that on while you are
playing with it.
    And, most importantly, report any bugs you find to the bug report
mailing list, cvscc-defects@lists.lorentz.com!