Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1572075
  • 博文数量: 289
  • 博客积分: 11086
  • 博客等级: 上将
  • 技术积分: 3291
  • 用 户 组: 普通用户
  • 注册时间: 2006-06-22 17:06
个人简介

徐小玉的博客。

文章分类

全部博文(289)

文章存档

2023年(6)

2022年(1)

2021年(2)

2020年(9)

2019年(9)

2018年(6)

2017年(10)

2016年(10)

2014年(3)

2013年(4)

2011年(12)

2010年(16)

2009年(14)

2008年(119)

2007年(48)

2006年(20)

我的朋友

分类: 项目管理

2008-03-30 13:12:16

 

A CVS Tutorial

Extreme Programming, Summer 2001



Table of Contents

Introduction

The page is a tutorial on CVS, a system for managing project files. This tutorial is simple, describing CVS in enough detail to be able to use it, but not necessarily to use it optimally or efficiently. Those who are interested in the gory details can read the .

What is CVS?

CVS (stands for Concurrent Version System) is a system for storing, managing, and keeping track of files. CVS was designed to manage files in multi-developer software projects; that is, CVS was designed to manage access to shared files.

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:



  1. Developer Ed obtains file A from CVS and starts to modify it.


  2. Developer Eddie also obtains file A from CVS and starts to modify it.


  3. Eddie finishes her modifications to file A and returns it to CVS.

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.

The CVS Repository

CVS stores all files associated with a project in a directory called the repository. For this class the CVS repository is


/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

Most CVS commands need to know where the repository is. The easiest way to make this information available is to set the 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.

Naming Files in the Repository

One of the trickier things about using CVS is figuring out the names of files and directories in the repository. The problem is that a file name alone could be ambiguous because the file could reside in several directories in the repository. For example, if the file 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 Commands

All CVS commands have the general form:


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  or
    Pascal Molli's CVS site at ~molli/cvs-index.html

$ 

Checking Out Files

The first thing you'll need to do is create a working copy of the project files in the repository using the CVS checkout command. Go to whatever directory it is you do your work in and type the following


$ 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.

Adding Files to The Repository

To add a new file to the repository, create the file in the working copy of the directory and use the add command followed by the commit 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.

Updating Files

The usual development cycle is to check out files from CVS, change the files, and then merge the changes back into the files maintained by CVS. The CVS update command merges working-copy changes into the repository copy of the file.

Updating With No Conflicts

If the repository's copy of the file has not been changed since you last checked it out or updated it, then updating and committing the file is straightforward:


$ 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

$ 


Updating With Non-Conflicting Changes

It gets a bit more difficult if the repository copy's of a file has been changed since you last checked out or updated the file. There are two cases to consider. In the easy case, the changes made to the repository copy of the file do not conflict with your changes to the file. "Do not conflict" means that the areas of the file you've changed don't overlap with the areas that have been changed in the repository copy of the file.

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

$


Updating With Conflicting Changes

The difficult case occurs when


  1. both the repository and your working copy of the file have been changed, and


  2. the changes made to the repository copy of the file conflict with your changes to the file

"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$


Q and A


  • Once I've changed a working-copy file and merged the changes back into the repository copy, how do I tell CVS I'm no longer interested in the working copy and don't want it checked out anymore?

    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.

Links

The .

The , the name of which is perhaps some kind of pun in French.

阅读(2239) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~