The important thing to understand about CVS is that it lets any number of developers work on the same file at the same time (hence concurrency). To see why this is important, consider the following sequence of events:
At this point Ed is working on an obsolete file, because it doesn't contain the modifications made by Eddie. We will see below how CVS deals with this situation; for now it is enough to recognize what the situation is and understand that this situation is a commonly occurring one in CVS.
/export/home/class/cs-498-598/cvs-repository
which is accessible from any cslab machine.
The class CVS repository contains three directories: practice
, p2
,
and (eventually) p3
. The practice directory is available for you to
practice your CVS skills without fear of clobbering important files. The p2
directory contains the files for project 2; the p3 directory contains the files
for project 3.
The
CVSROOT
Environment Variable
CVSROOT
to contain the repository. If you're running sh
,
ksh
, or bash
, the command to set CVSROOT
properly is
export CVSROOT=/export/home/class/cs-498-598/cvs-repository
If you're running csh
, the command is
setenv CVSROOT /export/home/class/cs-498-598/cvs-repository
If you're running sh
or ksh
, you can put the proper command in the
file $HOME/.profile
and it will be executed each time you log in; if
you're running bash
, put the command in $HOME/.bashrc
; if you're
running csh
, put the command in $HOME/.chsrc
. You can check the
value of CVSROOT
using echo:
$ echo $CVSROOT /export/home/class/cs-498-598/cvs-repository $
If echo produces no output other than a newline, CVSROOT
isn't set; if
echo produces CVSROOT
, you forgot to proceed CVSROOT
with a dollar
sign.
time-data.cc
existed in both the
directories p2
and p3
, then just the file name time-data.cc
is
ambiguous because it refers to more than one file (this example is misleading,
as we'll see in a couple of paragraphs).
The solution CVS uses to solve this problem is to require that the names of
files in the repository include the path to the file from the top of the
repository (that is, the file name consists of the full path-name of the file
with the repository part removed). For example, the full path-name of the file
time-data.cc
in the directory p2
is
/export/home/class/cs-498-598/cvs-repository/p2/time-data.cc
Removing the repository part leaves the file name p2/time-data.cc
.
Now we can see the problem with the example given a few paragraphs back. The
file name time-data.cc
is not ambiguous; it refers to a file in the top
level of the repository, just as p2
refers to a directory in the top level
of the repository. Although the example is misleading, I find it a convenient
way to think about the problem and its solution.
cvs
[cvs-options] command [command-options]
If you can't find the cvs
command, make sure /usr/local/bin
is in
your PATH
environment variable.
$ cvs --help /bin/ksh: cvs: not found $ echo $PATH /home/clayton/bin:/usr/bin:/bin:/usr/bin/X11:. $ p=$PATH $ PATH=$p:/usr/local/bin $ echo $PATH /home/clayton/bin:/usr/bin:/bin:/usr/bin/X11:.:/usr/local/bin $ cvs --help Usage: cvs [cvs-options] command [command-options-and-arguments] where cvs-options are -q, -n, etc. (specify --help-options for a list of options) where command is add, admin, etc. (specify --help-commands for a list of commands or --help-synonyms for a list of command synonyms) where command-options-and-arguments depend on the specific command (specify -H followed by a command name for command-specific help) Specify --help to receive this message The Concurrent Versions System (CVS) is a tool for version control. For CVS updates and additional information, see the CVS home page at http://www.cvshome.org/ or Pascal Molli's CVS site at http://www.loria.fr/~molli/cvs-index.html $
$ cvs checkout p2 cvs checkout: Updating p2 $
This command makes a working copy of the Project 2 directory p2
in your
current directory.
Checkout can also copy individual files from the repository to your working
copy. Remember that the file given to checkout names a file in the repository,
and so needs to include the path from CVSROOT
to the file, otherwise
checkout won't find the file.
$ cvs checkout match.cc cvs checkout: cannot find module match.cc - ignored $ cvs checkout practice/match.cc U practice/match.cc $
The U
indicates that match.cc
has been updated from the repository.
One feature of checkout you need to keep in mind is that not only does it copy the file to the working copy, it also copies the given directories too. This means you have to be aware of the current directory when you issue a checkout command to avoid getting extra copies of the directories and having the file copied to the wrong place.
$ pwd /export/home/uf/rclayton/practice $ ls CVS match.cc $ cvs checkout practice/match.h U practice/match.h $ ls CVS match.cc practice $ ls practice CVS match.h $
In this example I was in the practice directory when I asked for a copy of the
repository file practice/match.h
. Because checkout creates a copy of the
directory practice
as well as the file match.h
, I ended up with an
unneeded directory and the file in the wrong place.
When this happens, delete the extra directory, change to the parent directory, and try again.
$ rm -rf practice $ cd .. $ cvs checkout practice/match.h U practice/match.h $ cd practice $ ls CVS match.cc match.h $
Once you've created a working copy of the project directory, you can keep it up-to-date with the repository using the update command.
$ cvs add date.cc date.h cvs add: scheduling file date.cc for addition cvs add: scheduling file date.h for addition cvs add: use 'cvs commit' to add these files permanently $ cvs commit -m 'the yy-mm-dd date object' date.cc date.h RCS file: /export/home/class/cs-498-598/cvs-repository/p2/date.cc,v done Checking in date.cc; /export/home/class/cs-498-598/cvs-repository/p2/date.cc,v <-- date.cc initial revision: 1.1 done RCS file: /export/home/class/cs-498-598/cvs-repository/p2/date.h,v done Checking in date.h; /export/home/class/cs-498-598/cvs-repository/p2/date.h,v <-- date.h initial revision: 1.1 done $
The argument to the -m
commit option is a brief description of the files
being added; this description is added to the history and log files that CVS
keeps. If you don't give a message via the -m
option, the commit command
will pop-up an editor for you to provide one.
You must commit files for them to appear in the repository. If you just add files without committing them, they will not appear in the repository.
$ cvs update time-data.cc M time-data.cc $ cvs commit -m 'delete the 12-24 hour flag.' time-data.cc Checking in time-data.cc; /export/home/class/cs-498-598/cvs-repository/p2/time-data.cc,v <-- time-data.cc new revision: 1.2; previous revision: 1.1 done $
The response from the update command indicates that the working-copy of the
file time-data.cc
was modified (M
), which is correct - you changed
the file.
Updating a file does not make the changes visible in the repository; to change the repository file, you must commit your copy of the file. If you update a file that you've just updated and committed to the repository, you'll see that it's the same as the repository copy:
$ cvs update time-data.cc $
It is important to understand that CVS is not very cleaver when it detects conflicting changes between working and repository copies of a file. It just checks to see if the areas changed in each file are far enough apart in each file. CVS does not notice how a file has changed; it only notices if it has been changed. Developers need to review non-conflicting changes to make sure the repository changes are consistent with the working-copy changes.
Updating and committing a working-copy file with non-conflicting changes is as straightforward as the previous case, although the update command behaves differently in this case.
$ cvs update time-data.cc RCS file: /export/home/class/cs-498-598/cvs-repository/p2/time-data.cc,v retrieving revision 1.3 retrieving revision 1.4 Merging differences between 1.3 and 1.4 into time-data.cc M time-data.cc $ cvs commit -m 'delete the 12-24 hour flag.' time-data.cc Checking in time-data.cc; /export/home/class/cs-498-598/cvs-repository/p2/time-data.cc,v <-- time-data.cc new revision: 1.2; previous revision: 1.1 done $
The update command detected changes not only in the working copy of
time-data.cc
, but also in the repository copy. Because the changes didn't
conflict, the update command incorporated the repository changes into the
working copy of the file. The commit command incorporated the working-copy
changes into the repository copy of the file.
Here's another example of how CVS handles non-conflicting changes between
working and repository copies of a file. Ed checks out the file
numbers.cc
$ cvs checkout p2/numbers.cc $ cd p2 $ cat numbers.cc 1 2 3 4 5 6 $
and edits the file, changing the last line to "six"
$ emacs numbers.cc $ cat numbers.cc 1 2 3 4 5 six $
While Ed was doing all this, Eddie checked out numbers.cc
, changed the
first line to "one", and updated and committed the changes to the
repository. When Ed updates his changes to numbers.cc
, the update command
will detect the non-conflicting changes between the repository and working
copies of the file and update the working copy:
$ cvs update numbers.cc RCS file: /home/clayton/cvs-example/p2/numbers.cc,v retrieving revision 1.1 retrieving revision 1.2 Merging differences between 1.1 and 1.2 into numbers.cc M numbers.cc $ cat numbers.cc one 2 3 4 5 six $
Ed, after reviewing the repository changes and finding them consistent, moves the working-copy changes to the repository by committing the file update:
$ cvs commit -m 'change 6 to six' numbers.cc Checking in numbers.cc; /export/home/class/cs-498-598/cvs-repository/p2/numbers.cc,v <-- numbers.cc new revision: 1.3; previous revision: 1.2 done $
"Conflict" means that the areas of the file you've changed overlap or are close enough to the areas that have been changed in the repository copy of the file.
The behavior of the update command with conflicting modifications is similar to its behavior with non-conflicting modifications, but the changes made to the working copy of the file are different.
ro cvs update date.cc RCS file: /export/home/class/cs-498-598/cvs-repository/p2/date.cc,v retrieving revision 1.1 retrieving revision 1.2 Merging differences between 1.1 and 1.2 into date.cc rcsmerge: warning: conflicts during merge cvs update: conflicts found in date.cc C date.cc $
The update command has detected conflicting modifications, indicated by the
warning message and the C
preceding the filename, and has modified the
working copy of date.cc
to indicate where the conflicts occur.
The working-copy of the file is modified by the update command to indicate where the conflicts occur. The each modification has the form
<<<<<<<
file name
working-copy changes
=======
repository-copy changes
>>>>>>>
working-copy version number
Each conflict is marked with its own modification.
The conflicts are be resolved by editing the working copy of the file, usually by incorporating the repository-copy changes into the working-copy changes. Once resolved, the file can be re-updated and committed to the repository (assuming the repository copy hasn't been changed again in the meantime).
Let us call on Ed and Eddie again to help illustrate these points. Ed checks
out the file counter.cc
:
ed$ cvs checkout p2/count.cc ed$ cd p2 ed$ cat count.cc int i = 0; void increase(void) { i++; } ed$
and documents the function
ed$ emacs count.cc ed$ cat count.cc int i = 0; void increase(void) { // Count the number of member calls. i++; } ed$
In the meantime, Eddie checks out the file, changes the variable i
to
count
, updates and commits the file.
eddie$ cvs checkout p2 cvs checkout: Updating p2 U p2/count.cc eddie$ cd p2 eddie$ cat count.cc int i = 0; void increase(void) { i++; } eddie$ emacs count.cc eddie$ cat count.cc int count = 0; void increase(void) { count++; } eddie$ cvs update count.cc M count.cc eddie$ cvs commit -m 'Rename i to count.' count.cc Checking in count.cc; /export/home/class/cs-498-598/cvs-repository/p2/count.cc,v <-- count.cc new revision: 1.2; previous revision: 1.1 done eddie$
Ed then tries to update his version of count.cc
and finds there are
conflicting changes:
ed$ cvs update count.cc RCS file: /export/home/class/cs-498-598/cvs-repository/p2/count.cc,v retrieving revision 1.1 retrieving revision 1.2 Merging differences between 1.1 and 1.2 into count.cc rcsmerge: warning: conflicts during merge cvs update: conflicts found in count.cc C count.cc ed$ cat count.cc int count = 0; void increase(void) { <<<<<<< count.cc // Count the number of member calls. i++; ======= count++; >>>>>>> 1.2 } ed$
Notice that the update command has merged Eddie's non-conflicting change to the global variable declaration into Ed's working copy. Ed edits his working copy to include the repository changes (which includes removing the modification markers) and re-updates and commits the file:
ed$ emacs count.cc ed$ cat count.cc int count = 0; void increase(void) { // Count the number of member calls. count++; } ed$ cvs update count.cc M count.cc ed$ cvs commit -m 'Document increase()' count.cc Checking in count.cc; /export/home/class/cs-498-598/cvs-repository/p2/count.cc,v <-- count.cc new revision: 1.3; previous revision: 1.2 done ed$
Use the release command. I haven't described the release command because I expect everyone will make their own working copy of the project files and use use the update command to keep the working copies up-to-date with the repository copies.
The CVS home page.
The CVS Bubbles page, the name of which is perhaps some kind of pun in French.
This page last modified on 12 July 2001.