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 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 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% >>