My apologies to those who already know what they're doing with CVS, but I know people hate getting into stuff until they've been taken by the hand. Josh was kind enough to show me around CVS the other day, so I've written up some stuff that I hope may be helpful for others. Of course, any corrections would be appreciated. Mark and Josh sent around some web links earlier that I'll echo here so everything's in the same spot. CVS for Dummies: http://www.suse.com/us/private/support/online_help/howto/cvs/index.html A howto for CVS: http://www.perlmonks.org/index.pl?node_id=187449 CVS is the Concurrent Versions System. Basically, it keeps track of what you've changed and when so if months after you got a program to work you change something and it's now broken, you can get the old version back. It also allows you to see your progress. It works on just about any sort of text file (programs such as C and Perl, LaTeX, simple text, whatever). For more info, you can "man cvs", or go to cvshome.org, where they have an entire manual, but I know a demonstration would be helpful. Below I show the relevant lines in my .tcshrc file. ### CVS # Directory for CVS repository setenv CVSROOT /cvsroot/price/ # Who can access it? setenv CVSUMASK 003 # What do you use to edit the comments? setenv CVSEDITOR emacs ### For non-local CVS using ssh #setenv CVS_RSH ssh #setenv CVSROOT :ext:USER@HOST:/DIRECTORY Now you need to create a directory that will be your CVS repository. This is $CVSROOT when you set your environment variables. Then you need to do a: cvs init which sets up some files in your repository directory that CVS uses to keep track of things. (There should now be a $CVSROOT/CVSROOT/ directory with files.) Now, go to the directory of the project you want to put under CVS. CVS provides some cool functions that allow you to keep track of what changes you've made to a file and when, through the use of keywords. Whenever CVS sees a keyword in a file, it substitutes it. /* ** $Id: cvs.txt,v 1.1 2005/05/13 19:47:22 price Exp $ ** What it does ** Who wrote it ** Copyright? */ The $Id: cvs.txt,v 1.1 2005/05/13 19:47:22 price Exp $ tells CVS to insert file identification stuff (There need only be "Id" between the two dollar signs, but it's been expanded by my CVS; you can turn it off with "-kk" when you check in with cvs, but I'm lazy). Now, get rid of any object (*.o) files, binaries and test data, since you don't want them in the repository. Binaries can be handled by CVS, but they just get copied in, which chews up disk space. Now you can do a: cvs import PROJECT_NAME MY_NAME RELEASE_NAME where PROJECT_NAME is the name of your project (e.g. "myProgram"); MY_NAME is something to identify you (your login ID or your last name works well; it's mainly for multi-user repositories, but it's required); and RELEASE_NAME is something to identify where you are ("start" is good for the first import). CVS will then open your editor of choice ($CVSEDITOR) where you can type in what changes you've made. For an import, I type "Initial CVS import" or similar. Exit your editor and CVS will go and copy all the files to the repository. Now, in order to work on your project, go to a fresh working directory and check it out of the repository: cvs checkout PROJECT_NAME This copies the files from the repository to your working directory. Now you can play with your project - edit away. When you're done, you put things back into CVS by: cvs commit FILENAMES where FILENAMES are the names of the files that you changed. You'll get your editor of choice again to type in what you've changed, and the files will get merged into the repository (I don't think it actually saves the entire file, just the differences) and the keywords substituted again. That's basically it. If you want to add a new file into the repository, or delete a file from the repository, you can do: cvs add FILENAME or cvs delete FILENAME followed in both cases by a cvs commit FILENAME (Deleted files are actually sent to the "attic" in the repository, in case they are needed to get back an earlier version.) Renaming files is a bastard - you have to add the new filename, delete the old filename and commit on both (therefore it's not strictly a rename operation). Now, say everything's working and someone wants a copy, so you want to save where you're at. Do a: cvs tag RELEASE_NAME where RELEASE_NAME is a name that specifies where you are in the project (it has to start with a letter and is independent of the "version" that CVS assigns to a file), such as "v1", "v2"..., or "alpha", "beta"..., or "copyForFred". This takes a snapshot of how everything looks at the moment so you can get back there easily if necessary by going to a new directory and doing a: cvs checkout -r RELEASE_NAME PROJECT_NAME That's about all you need to know to get started. I understand there are GUIs out there that can run CVS for you, and there's also something under Emacs, but I haven't tried those yet. P. ================================================================================ The following is taken from http://elib.cs.berkeley.edu/admin/cvs/cvsemacs.html and http://www.badgertronics.com/writings/cvs/emacs.html C-x v i Add a file to the repository C-x C-q or Commit changes or check in. You get a window in which to enter C-x v v a log message, then do C-c C-c C-x v l CVS log C-x v u Revert a buffer to the last revision C-x v = See differences between buffer and last revision C-x v d Bring up a dired-style directory listing. Mark files for check-in by using 'm'. Actually check things in with C-x v v ================================================================================ Branches: Mainline branch | | r+---------------b1 cvs tag root | | cvs tag -b branch1 | | cvs update -r branch1 | | cvs tag root1 r1|-------------->b2 cvs update -j root -j HEAD | | cvs tag branch2 | | cvs tag root2 r2|-------------->b3 cvs update -j root1 -j HEAD | /| cvs tag branch3 | / | | / | cvs update -j branch1 -j branch3 m1|<----------- | cvs tag merge1 | | | | | b4 cvs tag branch4 | /| | / | cvs update -j branch3 -j branch4 m2|<------------ | cvs tag merge2 | | | | cvs tag root3 r3|-------------->b5 cvs update -j root2 -j HEAD | /| cvs tag branch5 | / | cvs update -j branch4 -j branch5 m3|<------------ | cvs tag merge3 | | | | v v Note that there are TWO "-j" options in merges. I believe the first specifies the "from", and the second specifies the "to". The effect is basically "get the difference between the from and the to, and apply that to what I've got in my working version". This doesn't look very friendly --- relies on tagging and merging regularly. Since I want to be able to keep up with the mainline AND have my own branch, it's more efficient, it seems, to simply have two working directories on my machine --- one for the mainline, and one for the branch. Then I pull out a branch, work on it, tag and merge into the mainline, and then stop work on the branch. This is the so-called "flying fish" approach. Mainline branch | | r1+---------------b1 cvs tag pap_root_010203 | | cvs tag -b pap_branch_010203 | | cvs update -r pap_branch_010203 | | | | Make changes... | | cvs commit -m "Some helpful message" | | | | Done with changes | b2 cvs tag pap_branch_010205 cvs update -j pap_branch_010205 m1|<-------------/ cvs tag pap_merge_010205 | No more branch development | | Identify new thing to work on | r2+---------------b3 cvs tag pap_root_010208 | | cvs tag -b pap_branch_010208 | | cvs update -r pap_branch_010208 | | Make changes... | | cvs commit -m "Some helpful message" | | | | Done with changes | b4 cvs tag pap_branch_010210 cvs update -j pap_branch_010210 m2|<-------------/ cvs tag pap_merge_010210 | No more branch development | v The merge tags probably don't really need to be there, but they help identify the position if we need to get back to it some time in the future.