Tree growing with CvsCvs work with two interwoven trees at the same time : one across space and one across time. The space one is familiar, it is just your usual directories & file arragement. For instance, in the cvs, webpages could be stored in the directory html, and password protected pages stored in html/internal.
To bring existing files into a new cvs directorycd <location of the files> cvs -d ~/cvsroot import <cvs-directory> <vendor-branch-tag> <branch-point-tag>
export CVSROOT=~/cvsroot bash setenv CVSROOT ~/cvsroot tcsh set CVSROOT=c:\home\cvsroot command.com (ick!)
The time-tree is unique to version control. In this tree, each branch keeps complete copy the files of the project such that development on one branch does not influence the other branches.
,---------o x===^============o Think of every character in this ascii as containing a complete set of al the files of the project. x marks the initial imported files, o's mark the lastest files. = mark the main trunk of the development tree and - is some other line of development. The ^ is the branch point : the last moment before work started getting done in parallel. At each state of the project (each 'character'), each file has a revision number that allow to retrieve the said files, for the said revision. The version number are automaticly generated every time you checkin a file. They are numberous (pun intended) and grow complex as the number of branches grows. It is not unusual to see revision number like "1.10.2.1.2.2.10.2.2.1.2.1". You shall not worry about them and treat them as tokens. If you ever need to rool back a file, you will use the cvsweb interface to find the revision number of the desired version.
To bring a file out of cvscvs -d ~/cvsroot checkout -r <revision-number (or tag name)> <directory/subdir/file.ext ...>
cvs -d ~/cvsroot checkout -r <revision-number (or tag name)> <directory/subdir/ ...>
cvs -d ~/cvsroot checkout -D "24 Sep 1972 20 :05" <file(s) ...>
cd <location of the cvs-working-directory> cvs -d ~/cvsroot update -r <revision number (or tag name)> <file>
cd <location of the cvs working directory> cvs -d ~/cvsroot commit
The argument of the -r can either be a revision-tag name or a branch-tag name. They are different animals yet they both mark retrievable version. More to come, right after we discussed 'cvs update' better.
To never clober changes, to never impede change (Interlude)With cvs, two developers are never forbiden to apply changes in parallel - even to the same file. Other systems tend to enforce only one modificator at a time, per file, through various locking mecanisims. However, historicaly this scheme has only generated lineups for locks, yelling and begings for locks to be released quicker, and, eventualy, bugs from hurried up programmers. Thus cvs was born lock-less. Cvs employs a semi-inteligent merging method based on the unix programs 'diff' and 'patch'. Typical situations goes as follows :
Note 2 : If Bob had never heard of Alice's changes and just went on, at his next commit he would have got an explicit request to go throught the change-merging process with the following fairly cryptic message : /home/bob/cvs-working-dir > cvs commit cvs commit : Examining . cvs commit : Up-to-date check failed for `main.cc' cvs [commit aborted]: correct above errors first!The correct answer is, of course, 'cvs update main.cc'. The standart piece of advice given here is : Automatic merges works better with small, incremental changes. Commit often for best results. Also, all this is no subtitute for team communication. If you don't talk to your fellows, automatic-merging will turn into automatic-bug-generation, and that sure ain't fun. To revision-tag a version of the projectRevision tags allow to assign names to nodes in the time-tree. Since revision number only work for one file at a time, they are your only way to retrieve the complete project at a particular time (beside dated checkout). When done well, they can be extremely useful. Cast yourself in the future and look for statement like "It seem realy broken now, but I do remember it working well right before that big demo", or "Gosh, looks like that big restructuring wasn't such a good idea after all." Tagging right before the big demo, and right before the attempted restructuring garantee an easy, mess-free roll-back if something goes wrong.
cd <location of the cvs working directory> cvs tag <tag-name>
cvs rtag -D <date> <tag-name>
cvs rtag -d <tag-to-delete> cvs rtag -r <tag><another-tag-for-the-same-version>
Now that you know about revision-tags, you can go back to the template cvs import and understand the branch-point-tag argument. The import command automaticly revision-tags the root of the tree, strategicly marked with an x on the ascii tree.
Branch tagsAs opposed to revision-tags, which never moves until you delete them, branch-tags hop along to always point to the most recent revision of a branch (its "head"). Since there alway one and only one head to every branch, the name of the branch-tag is also used as the name of the branch itself. In the ascii tree, branch-tags are marked with an o. The head of the main trunk is always the same easy to remember toponym : it is called HEAD. You will have to name the other branches as you create them. No typos are allowed as neither branches nor branch-tags can be deleted (at least without generating some humongous revision numbers).
To create a branch
cd <location of cvs working directory> cvs tag -b <branch-tag-name> cvs update -r <branch-tag-name> cvs commit
cvs status
cvs status <file>
Cvs provide plenny of automatic merging facility, which I never use. Just the though of machine-generated bugs sends shivers down my spine. Instead, I suggest you use the highly useful ediff interative merge program. It might not be as fast, but it give you an excellent occasion to revise the changes with your neighboor and confirm that no straddling happened.
To merge changes with emacs' ediff
mkdir tmp cd tmp cvs checkout -r branch-one <module> mv <module> <module>-one cvs checkout -r branch-two <module> mv <module> <module>-two diff -rq <module>-one <module>-two > differences emacs differences
M-x ediff-files <module>-one/<file> <module>-two/<file>
The following emacs lisp code can go in your .emacs file. It is meant to be used on the output of a 'diff -rq oldVersionDir newVersionDir' command, it will pick up the two file names on the current line and start an ediff session. Very helpful to complete a manual merge quickly. (defun pickup-differing-files-and-ediff () (interactive) (save-excursion (beginning-of-line) (if (not (looking-at "^Files \\(.*\\) and \\(.*\\) differ$")) (error "Not on a diff line - try 'diff -rq dir1 dir2'"))) (forward-line) (ediff-files (match-string 1) (match-string 2)))
Happy cvsing!
|