Like RCS, CVS is a version control system. Unlike RCS, it allows multiple developers to work on a file at the same time; the C in CVS stands for "concurrent".
This document is a simple introduction from a user's point of view. It assume that a repository already exists. That is to say, someone has already run cvs init, to initialize the repository, and cvs import to add the first group of files.
As a general reference, the main CVS manual is available online at http://ximbiot.com/cvs/manual/feature.
Before using CVS, you'll need to set up a environment variables.
setenv CVSROOT /path/to/cvsroot # C-shell syntax CVSROOT=/path/to/cvsroot; export CVSROOT # Bourne syntax
CVSROOT should be the directory path to the repository. Place these in your .cshrc, .profile, or where ever you normally put such things.
Another useful variable is CVSEDITOR. When ever you commit files, cvs will invoke this program and allow you to provide comments about the change you are making. As a personal preference, I like "emacs -nw --no-init-file"
CVS commands take the following form
cvs cvs-options subcommand subcommand-options
subcommand is the thing you are asking cvs to do -- check files in, check files out, do diffs, etc. To see the syntax of a given command
cvs -H subcommandcvs --help will tell you how to get a list of what the different subcommands are.
When working with CVS, there are 2 copies of files that you need to be concerned with:
Before doing anything else, you'll need to checkout a local copy of the repository files. Here's an example:
$ cvs checkout mymodule cvs checkout: Updating mymodule U mymodule/file1 $ ls total 1 1 mymodule/
So what just happened? "mymodule" is a module in the repository. Checking the module out placed a local copy in the current directory. Changes can be made to files here, then put back (committed) to the repository.
Modules are really just directories underneath $CVSROOT. In other words:
$ ls $CVSROOT total 2 1 CVSROOT/ 1 mymodule/
CVSROOT contains configuration and administrative files used by CVS. We won't worry about that now.
Editing files is easy - once you have local copies, just edit them. None of your changes will be visible to other users until after you've committed them.
If you mess up a local copy of a file, starting over is easy. Delete the file, and use cvs update to get a fresh copy from the repository.
If you're working with a group of developers, remember that they're making changes too. Periodically, you'll want to update your working copies. This is done with the cvs update command.
$ cvs update -P -d cvs update: Updating . U file1
Above, we see that someone had modified file1, and the copy in the current directory was out of date; cvs updated file1 to the current version. The flags to update are optional. -P "prunes" directories that are empty, and -d tells cvs to include any new directories that aren't in your local workspace (the default behavior is only to include directories that you have checked out). Once you have a local copy, cvs checkout and cvs update -d are more or less equivalent.
update can also take arguments, if you want to update specific directories, or specific files with a directory. If no arguments are given, cvs recursively updates the directory tree rooted at the current directory.
To see if someone has changed a particular file, use cvs status.
$ cvs status file1 =================================================================== File: file1 Status: Up-to-date Working revision: 1.2 Thu Oct 10 14:49:15 2002 Repository revision: 1.2 /home/srevilak/c/mymodule/file1,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: (none)
"Up-to-date" means that the file is current.
To get a quick look at everything, the following command is handy:
$ cvs -n update
The -n option tells cvs "don't change the disk". Where local files don't match the repository copies, you'll see the name of the file and a status code (M for locally modified, ? for something that's not in the repository, and so fourth).
Okay, you've done some work and you're happy with the results. To incorporate your changes into the repository, use cvs commit.
$ cvs commit filename
CVS will invoke CVSEDITOR so that you can make comments. Once you quit the editor, the changes will be put back into the repository.
Commit comments should be a short description of what you did, enough to allow other developers to look at the file's log and get a sense of what's been done to it. The GNU folks have an entire article dedicated to the subject of documenting changes: http://www.gnu.org/prep/standards/html_node/Change-Logs.html#Change-Logs.
Files and directories are added with the cvs add command. To add a directory:
mkdir newdir cvs add newdir
To add a file
# create the file, edit it cvs add newfile cvs commit newfile
To add a binary file
cvs add -kb newfile cvs commit newfile
-kb tells cvs that the file is a binary file, and that it shouldn't try to expand tags (such as $Id$.) that appear in the file's body. CVS understands many of the same tags that RCS does. See http://ximbiot.com/cvs/manual/feature/cvs_12.html#SEC100, or the co(1) manpage.
Normally, it's best to edit files in the directory that you're using
for checkouts. This way, cvs will automatically take care of merging
in changes, just by running
cvs update. However, in some
cases that might not always be possible.
Hypothetical Situation: you took a copy of
Myfile.java home, and did some work on it. In the
meantime, your fellow developers have committed changes to the file.
The dilemna - you'd like to incorporate what you've done, but your
copy of the file is now out of date. Of course, you also don't want
to undo work that others have done. Here's a way to deal with this
$Revision$tags. If you can't determine the revision, this approach won't work, and you'll need to do a manual merge.
cvs updateto refresh your repository copy.
cvs log MyFile.java(in the appropriate directory) to get the revision number of the copy that you just checked out of the repository.
For the sake of illustration, lets say that the copy of
MyFile.java that you were working on at home is revision
1.6, and the current repository version is 1.10.
MyFile.java that you worked on at home to your
checkout directory. We now have the following arrangement:
To pick up the modifications made from 1.7 - 1.10, you need to merge:
cvs update -j 1.7 -j 1.10 MyFile.java
In cvs-speak, this means "take the changes from revision 1.7 through revision 1.10, and apply them to the local copy of the file." Assuming that there were no merge conflicts, examine the results:
cvs diff -w MyFile.java
Make sure it compiles, then commit.
If things didn't go well, you'll need to examine the results and resolve any conflicts that happened as a result of the merge.
On a related note,
update -j ... can also be used to
back out a bad commit, simply by reversing the
Eventually, something like this will happen:
$ cvs commit foo.java cvs commit: Up-to-date check failed for `foo.java' cvs [commit aborted]: correct above errors first!
Here, you've made changes to the foo.java, but someone else has already committed a new version to the repository (eg - the repository version has a higher number than your local copy). Before you can commit the file, you'll need to update your working copy.
If you and the other developer were working on different areas of the file, cvs is pretty intelligent about merging the changes together; it might see that the last set of modifications are in lines 75-100, and your changes are in lines 12-36. In this situation, the file can be patched and your work is unaffected.
However, if the two of you changed the same area of the file it's possible to have conflicts:
$ cvs update foo.java RCS file: /home/srevilak/c/mymodule/foo.java,v retrieving revision 1.1 retrieving revision 1.2 Merging differences between 1.1 and 1.2 into foo.java rcsmerge: warning: conflicts during merge cvs update: conflicts found in foo.java C foo.java
Oh dear! What do we do now? The answer is "fix the merge". Two things have been done to help you with this.
$ ls -a .#* 1 .#foo.java.1.1However, being a dotfile, it's presence isn't immediately obvious
<<<<<<< foo.java static final int MYCONST = 3; ======= static final int MYCONST = 2; >>>>>>> 1.2
The conflict lies between the rows of greater than and less than signs. The thing to do now is decide what version is right, remove the conflict markers, and commit the file.
Let's suppose that you've commited a file, but this ended up breaking something horribly. Here's how to undo your commit:
Now do this:
cvs update -j 1.5 -j 1.4 filename cvs commit filename
The above is an example of a merge. You've asked cvs to take the difference between versions 1.5 and 1.4 and apply them to your working copy. The ordering of version numbers is significant - think of it as removing changes, or going backward in version history.
To get rid of files, use cvs delete:
rm filename # must remove working copy first cvs delete filename cvs commit
CVS uses a "lazy" system for file deletion; delete just changes the way that the file is stored in the repository. It's still possible to undelete the file, or to check out revisions that existed before the file was deleted. However, the file will no longer appear when you do checkouts or updates.
Because of this, it's not possible to delete a directory entirely. However, one can use the -P flag with cvs checkout and cvs update to prevent empty directories from being retrieved.
There are a variety of other useful cvs commands. Here are a few examples:
|cvs diff filename||Shows differences between your local copy of filename and the repository version that filename was based on.|
|cvs diff -r 1.2 filename||Shows differences between your local copy of filename and version 1.2 of filename.|
|cvs diff -r 1.2 -r 1.3 filename||Shows differences between versions 1.2 and 1.3. (regardless of what version your local copy is).|
|cvs log filename||Show the commit log for filename (like rlog does with rcs).|
|cvs annotate filename||Shows each line of filename, prefixed with the version number where the line was added, and the name of the person who added it. Useful for seeing who made a particular set of changes.|
Branching is a process that allows different versions of a file to be developed in parallel. The cvs manual gives a visual representation here: http://ximbiot.com/cvs/manual/feature/cvs_5.html#SEC60.
[srevilak - I'll hold off on this section until it's needed. Why
complicate things prematurely ;]
$Id: cvs.html,v 1.10 2009/07/17 01:13:03 srevilak Exp $