Team Coding With CVS

#### Problem definition

Forget you've heard of RCS.  Just think about the problem.

### Problem 1: team coding

_How do you get multiple programmers to safely work on the same large
code base_?

Possible strategies:

o One dir of stuff.  Programmers can change any file they want as long
  as nobody is modifying it.  How to enforce "ownership" of files?
  What if you *need* to modify a file somebody is working on as you
  are making a broad-sweeping change?

o Each programmer gets a complete copy and can do what they want:
  code, test, etc...  They own the sandbox, but how do you get one
  main trunk again?  Do a recursive diff (diff -r) on the directories
  and on each file that is different, run a merge.

Don't get revisions / implicit backup with any of these strategies.

### Problem 2: tracking revisions, releases

_How do you track evolution of a program and how do you make
releases_?

Need because:

1 You want to be able to record exact code state for each release to
  handle bug reports etc...

1 You want to find out "what has changed since last version" because
  it doesn't work anymore.

Possible strategies:

o Make a "snapshot" (i.e., complete copy) every time you make a
  release or otherwise want to record the state or your system.  Would
  have to record in a README the changes etc...  Make sure your dir
  structure was good and you timestamped it etc...  Pretty laborious
  'cause have to snapshot every time you finish a change.  *Side
  benefit*: get back up every time you snapshot.  Could be space
  inefficient though.

#### Concepts

CVS (concurrent versions system) is a widely-used, free, open-source
tool that manages the above strategies for you.  It's "designed by
committee" but great for a free tool.  *Perforce* is better, imho, but
costs $600 per seat.

CVS has the notion of a _repository_ that stores your your code,
tracks all changes, and records your revision comments.  CVS doesn't
do transactions and doesn't have a real db...just a bunch files on the
disk.  You can wack trees off the repository (and remove local CVS
dirs) w/o consequence.  CVS works off a repository directory
{$CVSROOT} and stores some local information in your directories that
it manages.  You'll set {CVSROOT=~userid/cvsroot} in your {.cshrc} or
{.bashrc} etc...

You do not mess with repository, you _checkout_ directories/files,
make changes, and then _commit_ your changes and add comments for the
log.

Command line usage: {cvs command ...}

RCS -- Walter Tichy -- works on single files to track revisions.
Think of CVS as just a recursive RCS that works on multiple files /
dirs.  When you check out some code, it does a {cp -r repository
CWD}.  When you commit, it does a diff of your current files against
repository and then merges your stuff into repository.  After a
commit, the repository will be a dup of your CWD.

You check out *once*, commit many time.

There is an implied *main branch* (main trunk). 1.1, 1.2, 1.3, ... for
all files.  The {HEAD} is the latest version of each file.

<<
http://cvsbook.red-bean.com/cvsbook.html
Copyright B) 1999, 2000 Karl Fogel <kfogel@red-bean.com>
     File A      File B      File C      File D      File E
     ------      ------      ------      ------      ------
     1.1         1.1         1.1         1.1         1.1
 ----1.2-.       1.2         1.2         1.2         1.2
     1.3 |       1.3         1.3         1.3         1.3
          \      1.4       .-1.4-.       1.4         1.4
           \     1.5      /  1.5  \      1.5         1.5
            \    1.6     /   1.6   |     1.6         1.6
             \   1.7    /          |     1.7         1.7
              \  1.8   /           |     1.8       .-1.8------->
               \ 1.9  /            |     1.9      /  1.9
                `1.10'             |     1.10    /   1.10
                 1.11              |     1.11    |
                                   |     1.12    |
                                   |     1.13    |
                                    \    1.14    |
                                     \   1.15   /
                                      \  1.16  /
                                       `-1.17-'
>>


<<
http://cvsbook.red-bean.com/cvsbook.html
Copyright B) 1999, 2000 Karl Fogel <kfogel@red-bean.com>

But if you pull the string taut and sight directly along it, you'll
see a particular moment in the project's history - namely, the moment
that the tag was set (Figure 2.2).

     File A      File B      File C      File D      File E
     ------      ------      ------      ------      ------
                                         1.1
                                         1.2
                                         1.3
                                         1.4
                                         1.5
                                         1.6
                                         1.7
                 1.1                     1.8
                 1.2                     1.9
                 1.3                     1.10        1.1
                 1.4                     1.11        1.2
                 1.5                     1.12        1.3
                 1.6                     1.13        1.4
                 1.7         1.1         1.14        1.5
                 1.8         1.2         1.15        1.6
     1.1         1.9         1.3         1.16        1.7
 ----1.2---------1.10--------1.4---------1.17--------1.8------->
     1.3         1.11        1.5         1.17        1.9
                             1.6         1.17        1.10
>>

I expect you students to play around to get the feel.  It takes
experience.

#### Single coder usage

You will only need a few CVS commands as a single coder:

o {cvs import -m "comment" http USF start}.  Jump to area where you
have some files to import.  The import will add whatever is in that
dir under dir $CVSROOT/http in the repository.  Another example,
<<
cvs import -m 'Initial TML import' org/antlr/tml ANTLR start
>>

o {cvs add file-or-dir}.  Add a new file to the system.

o {cvs commit}.  Commit your changes.  Merge current dir (branch) into
respository.

o {cvs diff T.java}. What is the difference between current state of
{T.java} in my local dir with {HEAD} revision of {T.java} in
repository?

o {cvs log T.java}.  Tell me about changes to {T.java}.

o {cvs tag -b release-alpha} (from the main line). Tag a moment in
time.  Doesn't touch current dir--updates repository info.

o {cvs checkout -r release-alpha http}. Puts a copy of {http/*} in
current directory.  Like {cp -r ...}.  Don't modify a release branch.
This just makes a complete (uncompressed) copy of a release.  Not
really needed.

I suggest strongly the following dir structure:

<<
~/projects/Server/main/http/*.java
~/projects/Server/release/alpha/http/*.java
~/projects/Server/release/1_0/http/*.java
...
>>

where http is the package name so http.Server maps to 

<<
~/projects/Server/main/http/Server.class
>>

if your {CLASSPATH} is set to {.:~/projects/Server/main}.

#### Team usage

Same concept as single-user mode--you'll just be doing that stuff in
your own branch.

o There is a main branch and each coder has his/her own "sandbox" or
  _branch_.

o Main branch always compiles and has passed your tests.

o You do your work in a dev branch and then merge with main, do your
tests, *then* commit effectively pinching your dev branch back into
main.  You *never* use this dev branch again (limitation of CVS--leads
to trouble otherwise).
<<
----main----------------->
 |  |          ^  ^
 |  v          |  |
 |  ---john01--/  |
 |                |
 v                |
 ------mary01-----/
>>
Do not push bug fixes from main line into dev branch (limitation of
CVS).  Bug fixes manually done in >=2 dev brances will cause conflicts
during merge with mainline, but same code so it's ok.

o Release branch: any code state that has merit.  You'll cut a release
  for every demo period.  Terminal branch--never merge back into
  mainline.

I suggest strongly the following dir structure:

<<
~/projects/Server/main/http/*.java
~/projects/Server/dev/john-fixing-GET/http/*.java
~/projects/Server/dev/mary-adding-logging/http/*.java
~/projects/Server/release/alpha/http/*.java
~/projects/Server/release/1_0/http/*.java
...
>>

*Note*: you can have as many of these branches checked out as you want
or you can {release} them.  Cancels the effect of {cvs checkout}.

### Example

To make a dev branch and merge your changes back into the main line,
follow this procedure:

1 Create the branch in repository.
<<
$ cd ~/projects/Server/main/http
$ cvs tag -b john01.dev
>>

1 Move to an appropriate dev dir and check that branch out.  Now you
have the main and the {john01.dev} branches checked out.
<<
$ cd ~/projects/Server/dev/john01.dev
$ cvs checkout -r john01.dev http
>>
Note that {-b} and {-r} flags are different for some stupid reason.

1 Check the status to see that you are indeed in the right spot.
<<
$ cvs status
>>
Will show
<<
   Sticky Tag:          john01.dev (branch: 1.3.2)
>>

1 Work on this branch and commit changes.
<<
$ cvs commit
>>

1 Get a version of your dev branch merged with mainline.  Jump back to
main branch area, make sure it's fresh, do merge, resolve conflicts.
<<
$ cd ~/projects/Server/main/http
$ cvs update # are we fresh?
$ cvs update -j john01.dev # merge john01.dev into main
>>

1 Test and commit
<<
$ cvs commit
>>

1 Set dev branch as "dead"
<<
$ cd ~/projects/Server/dev/john01.dev
$ cvs release
$ chmod -R a-w . # if you want (makes read only)
>>

### A simple walk-through

1 Make T.java (http.T is fully qualified name) in /tmp then import.
1 checkout mainline.
1 Show CVS dirs and repository.
1 Show status and log.
1 Add file U.java (http.U).
1 Commit.
1 Show status and log.
1 Make changes and commit.

1 Tag a release, jump to release dir (mk it), checkout, show status,
set readonly.

1 Make dev branch, jump to branch, checkout, show status, make change
to T.java, commit.

1 Jump back to main dir, update, status shows main. Merge in
dev. compile/test.  Commit.

1 Release dev.

Here is the script output (cleaned up minus the release branch item):

<<
[jazz:~] parrt% cd /tmp
[jazz:/tmp] parrt% mkdir http
[jazz:/tmp] parrt% cd http
[jazz:/tmp/http] parrt% cat > T.java
package http;

public class T {
  int i = 3;
}
[jazz:/tmp/http] parrt% cvs import -m 'course test' http USF start
N http/T.java

No conflicts created by this import
[jazz:/tmp/http] parrt% ls -l /var/data/cvsroot
total 0
drwxr-xr-x  37 parrt  wheel  1258 Sep  7 11:31 CVSROOT
drwxrwxr-x  21 parrt  wheel   714 Sep 11 15:34 http
[jazz:/tmp/http] parrt% ls -l /var/data/cvsroot/http
[jazz:/tmp/http] parrt% mkdir -p /tmp/projects/Server/main
[jazz:/tmp/http] parrt% cd /tmp/projects/Server/main
[jazz:projects/Server/main] parrt% cvs checkout http
cvs checkout: Updating http
U http/T.java
[jazz:projects/Server/main] parrt% cd http
[jazz:Server/main/http] parrt% ls
CVS    T.java
[jazz:Server/main/http] parrt% ls CVS
Entries    Repository Root
[jazz:Server/main/http] parrt% cvs status
cvs status: Examining .
===================================================================
File: T.java           	Status: Up-to-date

   Working revision:	1.1.1.1	Wed Sep 11 22:35:48 2002
   Repository revision:	1.1.1.1	/var/data/cvsroot/http/T.java,v
   Sticky Tag:		(none)
   Sticky Date:		(none)
   Sticky Options:	(none)

[jazz:Server/main/http] parrt% cvs log
cvs log: Logging .

RCS file: /var/data/cvsroot/http/T.java,v
Working file: T.java
head: 1.1
branch: 1.1.1
locks: strict
access list:
symbolic names:
	start: 1.1.1.1
	USF: 1.1.1
keyword substitution: kv
total revisions: 2;	selected revisions: 2
description:
----------------------------
revision 1.1
date: 2002/09/11 22:35:48;  author: parrt;  state: Exp;
branches:  1.1.1;
Initial revision
----------------------------
revision 1.1.1.1
date: 2002/09/11 22:35:48;  author: parrt;  state: Exp;  lines: +0 -0
course test
=============================================================================
[jazz:Server/main/http] parrt% cat > U.java
package http;
public class U {
  int j = 5;
}
[jazz:Server/main/http] parrt% cvs add U.java
cvs add: scheduling file `U.java' for addition
cvs add: use 'cvs commit' to add this file permanently
[jazz:Server/main/http] parrt% cvs commit
cvs commit: Examining .
...
Checking in U.java;
/var/data/cvsroot/http/U.java,v  <--  U.java
initial revision: 1.1
done
[jazz:Server/main/http] parrt% cvs log
cvs log: Logging .

RCS file: /var/data/cvsroot/http/T.java,v
Working file: T.java
head: 1.1
branch: 1.1.1
locks: strict
access list:
symbolic names:
	start: 1.1.1.1
	USF: 1.1.1
keyword substitution: kv
total revisions: 2;	selected revisions: 2
description:
----------------------------
revision 1.1
date: 2002/09/11 22:35:48;  author: parrt;  state: Exp;
branches:  1.1.1;
Initial revision
----------------------------
revision 1.1.1.1
date: 2002/09/11 22:35:48;  author: parrt;  state: Exp;  lines: +0 -0
course test
=============================================================================

RCS file: /var/data/cvsroot/http/U.java,v
Working file: U.java
head: 1.1
branch:
locks: strict
access list:
symbolic names:
keyword substitution: kv
total revisions: 1;	selected revisions: 1
description:
----------------------------
revision 1.1
date: 2002/09/11 22:37:29;  author: parrt;  state: Exp;
added a file
=============================================================================
[jazz:Server/main/http] parrt% vi T.java
[jazz:Server/main/http] parrt% cvs commit
cvs commit: Examining .
Checking in T.java;
/var/data/cvsroot/http/T.java,v  <--  T.java
new revision: 1.2; previous revision: 1.1
done
[jazz:Server/main/http] parrt% cvs tag -b dev01
cvs tag: Tagging .
T T.java
T U.java
[jazz:Server/main/http] parrt% cd /tmp/projects/Server
[jazz:/tmp/projects/Server] parrt% mkdir dev
[jazz:/tmp/projects/Server] parrt% cd dev
[jazz:/tmp/projects/Server/dev] parrt% mkdir dev01
[jazz:/tmp/projects/Server/dev] parrt% cd dev01
[jazz:projects/Server/dev/dev01] parrt% cvs checkout -r dev01 http
cvs checkout: Updating http
U http/T.java
U http/U.java
[jazz:projects/Server/dev/dev01] parrt% cd http
[jazz:Server/dev/dev01/http] parrt% cvs status
cvs status: Examining .
===================================================================
File: T.java           	Status: Up-to-date

   Working revision:	1.2	Wed Sep 11 22:37:55 2002
   Repository revision:	1.2	/var/data/cvsroot/http/T.java,v
   Sticky Tag:		dev01 (branch: 1.2.2)
   Sticky Date:		(none)
   Sticky Options:	(none)

===================================================================
File: U.java           	Status: Up-to-date

   Working revision:	1.1	Wed Sep 11 22:37:29 2002
   Repository revision:	1.1	/var/data/cvsroot/http/U.java,v
   Sticky Tag:		dev01 (branch: 1.1.2)
   Sticky Date:		(none)
   Sticky Options:	(none)

[jazz:Server/dev01/http] parrt% vi T.java
[jazz:Server/dev01/http] parrt% cvs commit
cvs commit: Examining .
Checking in T.java;
/var/data/cvsroot/http/T.java,v  <--  T.java
new revision: 1.2.2.1; previous revision: 1.2
done
[jazz:Server/dev01/http] parrt% cvs diff T.java
[jazz:Server/dev01/http] parrt% cd /tmp/projects/Server/main/http
[jazz:Server/main/http] parrt% cvs update
cvs update: Updating .
[jazz:Server/main/http] parrt% cvs status
cvs status: Examining .
===================================================================
File: T.java           	Status: Up-to-date

   Working revision:	1.2	Wed Sep 11 22:37:49 2002
   Repository revision:	1.2	/var/data/cvsroot/http/T.java,v
   Sticky Tag:		(none)
   Sticky Date:		(none)
   Sticky Options:	(none)

===================================================================
File: U.java           	Status: Up-to-date

   Working revision:	1.1	Wed Sep 11 22:37:11 2002
   Repository revision:	1.1	/var/data/cvsroot/http/U.java,v
   Sticky Tag:		(none)
   Sticky Date:		(none)
   Sticky Options:	(none)

[jazz:Server/main/http] parrt% cvs update -j dev01
cvs update: Updating .
RCS file: /var/data/cvsroot/http/T.java,v
retrieving revision 1.2
retrieving revision 1.2.2.1
Merging differences between 1.2 and 1.2.2.1 into T.java
[jazz:Server/main/http] parrt% javac *.java # and test
[jazz:Server/main/http] parrt% cvs commit
Checking in T.java;
/var/data/cvsroot/http/T.java,v  <--  T.java
new revision: 1.3; previous revision: 1.2
done
[jazz:Server/main/http] parrt% cd /tmp/projects/Server/dev/dev01
[jazz:Server/dev/dev01] parrt% cvs release http
[jazz:Server/dev/dev01] parrt% 
>>