徐小玉的博客。
分类: 项目管理
2008-03-30 13:12:16
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 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.
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.
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.
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 or Pascal Molli's CVS site at ~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
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 .
The , the name of which is perhaps some kind of pun in French.