Gentoo Logo

Gentoo Linux CVS教程

内容:

1.  简介

本教程的章节说明

本教程由两部分组成,第一部分是为非开发用户(non-developer)准备的,告诉他们如何使用CVS从别人的源上取得更新。第二部分是为开发者(developer)准备的,此部分告诉你如何从CVS上更改、添加、移除文件以及执行其他与开发者相关的任务。如果你只是接触过CVS,我们建议你先从第一部分开始,然后再学习第二章;如果你有一些CVS经验但是是第一次准备使用CVS来把自己修炼成一个熟练的开发人员,你应该可以在第二章找到你任何你需要的,但是你应该复习一下第一章。

CVS是什么,CVS用来做什么?

CVS是一个客户端/服务器系统,它可以让开发人员用来把他们的项目存储在一个统一的位置,这个统一的位置我们称为仓库,通过CVS客户端工具,开发人员可以更改仓库的目录。换句话说,cvs仓库会跟踪每次改变所涉及到所有文件,创建一个关于该开发项目的完整的发展历史。开发人员可以通过请求特定的源文件的旧版本文件,来查看日志的改变,以及执行其他所需的任务。

CVS的作用

有很多开源项目都有他们自己的CVS服务器,该服务器是项目开发者们为他们所有的作品而搭建的一个中央仓库。开发人员经常日复一日的不断改进他们的仓库里的代码。通常而言,这些开发者是零落的分散在世界各地的,然而CVS提供了必要的机制将他们的项目联合成为一个统一的整体。CVS把所有人的工作有机的结合在一起,可以使他们的开发人员在改进他们的代码的时候,不会相互影响,也不用担心损失重要数据或者丢失彼此对某些特定文件的重要更新。

CVS——获取最新的开发源文件

当开发人员们准备好发布新版本后,他们将会把CVS上现有的代码用tar.gz格式打包,并把它作为他们的软件的一个新的官方版本放出。但是,最近一次放出的官方版本有时候出于种种原因并不是最新的。在第一章,我将会告诉你如何使用CVS来完成一个目的——让自己用上最新的并且是最好的开发版本。

安装CVS

安装cvs,只需要输入emerge cvs:

代码 1.1: 安装CVS

# emerge cvs

关于CVSROOT

在开始之前,你需要知道一些基本的CVS常识,第一点是连接到CVS仓库的指令,首先你需要知道一个叫做“CVSROOT”的路径。CVSROOT是一个字符串,像URL一样,他是用来告诉cvs命令远程的仓库在哪以及我们是如何连接到它的。有趣的是,CVSROOT有数种格式,取决于CVS仓库是在本地还是在远程机器上,以及连接到它使用什么方法,这里有一些CVSROOT的例子,参看说明……

一个本地CVSROOT

代码 1.2: 设置CVSROOT变量

CVSROOT=/var/cvsroot

这是一个本地的CVSROOT路径的例子;你应该使用类似这样CVSROOT,如果你想连接到一个存在于/var/csvroot/的本地仓库的话;当然也有可能这个仓库是通过NFS挂载到/var/cvsroot。

一个使用密码连接到远程的服务器的CVSROOT

代码 1.3: 设置具有带身份验证的CVSROOT变量

CVSROOT=:pserver:cvs@foo.bar.com:/var/cvsroot

这是一个远程连接的例子,该例是连接到foo.bar.com上的/var/cvsroot目录,":pserver:"部分是告诉我们的客户端使用CVS password server协议连接到远端的机器,该协议是内置在CVS中的,通常而言,公共CVS仓库使用的password server协议允许匿名用户访问。

一个使用rsh/ssh连接的CVSROOT

代码 1.4: 使用RSH/SSH的CVSROOT变量设置

CVSROOT=drobbins@foo.bar.com:/data/cvs

这是一个使用RSH/SSH协议的CVSROOT实例;在本例中,CVS服务将使用drobbins帐户试图访问foo.bar.com上的仓库。如果CVS_RSH环境设置为SSH,那么我们的cvs客户端将会尝试使用ssh连接,否则将会使用rsh。对于关注安全性的用户来说ssh连接方法是很实用的;但是,不要对匿名帐户开放获取源文件的RSH或SSH方法。要想使用这种方法,你必须要在foo.bar.com上拥有一个可登录的帐户。

其他注意事项

除了CVSROOT,你还需要知道你想要检出的模块(源代码的集合)名称,以及匿名帐户的密码。不同于可匿名登录的FTP,匿名用户并没有“标准”的密码,因此你需要从开发者的网站或者是开发人员那里获得指定的密码。一旦你有了这些资料,你就可以开始使用CVS了。

CVS交互,第一部分

获取源代码有两个步骤,首先,我们使用密码登录到服务器,然后我们使用checkout命令获取源,以下是一个关于如何使用命令来检测最新版本的Samba源的例子,Samba是一个流行的UNIX/Windows整合项目:

代码 1.5: 设置CVSROOT

# export CVSROOT=:pserver:cvs@pserver.samba.org:/cvsroot

第一个命令是设置CVSROOT环境变量,如果你不设置该变量,那么之后的两个命令需要在cvs命令之后追加一个选项-d :pserver:cvs@pserver.samba.org:/cvsroot。导出CVSROOT可以省了我们输入的麻烦。

CVS交互, 第二部分

以下是得到当前开发者源文件的副本的命令。你可以先跳过本节到下一节去查看这些命令说明,然后再返回这里:

代码 1.6: 检出源文件

# cvs login
(Logging in to cvs@pserver.samba.org)
CVS password: (在此输入密码)

# cvs -z5 co samba
U samba/COPYING
U samba/Manifest
U samba/README
U samba/Read-Manifest-Now
U samba/Roadmap
U samba/WHATSNEW.txt
(这只是完整的cvs co输出的一部分)

CVS交互——说明

上述的第一个命令的作用是让我们登录到pserver,第二个命令则是告诉我们的CVS客户端使用gzip的压缩级别5去检出samba模块,对于较慢网络连接这样可加速检出过程。对于每一个在本地新创建的文件,cvs会输出“U [path]”来标识这个文件已经在硬盘上更新。

完成检出

一旦命令执行完毕,你将会在当前工作目录看到一个包含最近版本的源文件的“samba”目录。你也会注意到所有的目录都有一个“CVS”目录——CVS在该目录里统计信息,而且这些文件完全可以不理。从这个角度看,我们不需要关心是否将CVSROOT声明为环境变量或是在命令行中作出了声明。因为CVSROOT现在被缓存到额外的“CVS”目录中。记住——你只需要在初始化和检验登录的才需要设置CVSROOT。

更新你的源文件

OK,现在你就有了新的源码了!对于在你手中的这些源码,你可以阅读,编译或者安装它们,或者做任何你想对它做的事。

你可能经常要将自己的版本和CVS服务器同步,如果进行此操作的话,你不需要再登录到pserver;你的验证信息已经被cvs缓存在那些“CVS”目录。首先,进入主验证目录(本例为“samba”),然后输入:

代码 1.7: 更新你的源文件

# cvs update -dP

使用cvs update,第一部分

如果有任何新文件,cvs将会对每个更新的文件输出“U [path]”。同样的,如果你编译过之前的源文件,你将会看到有很多“? [path]”行,那些目标文件是cvs发现的不存在于远程仓库的文件。

使用cvs update,第二部分

同样地,注意这两个“cvs update”命令行选项。“-d”告诉cvs创建任何已经添加到仓库的新目录(默认这是不会发生的),“-P”告诉cvs从你自己本地的拷贝的源文件中把任何空目录移除。“-P”是一个好的建议,因为cvs随着时间的推移,会累积大量空(曾经使用过,但是现在已经废弃的)目录。

以上就是你从CVS获取源文件所要知道的知识, 下面我们来看看作为开发人员如何与CVS交互。

2.  CVS的开发应用

修改文件

做为一个开发人员,你将会在CVS里更改你的文件。要想进行此操作,直接更改仓库本地的副本就可以了。你对源文件的修改只有在你告诉cvs去“commit”的时候才会在远程的仓库上生效。当你测试过你所有的更改,确认他们能够正常的工作,并且你已经做好了将这些改变应用到仓库的准备后,做下面两步。首先,在你的源文件的主目录输入以下命令来更新你的源文件。

代码 2.1: 更新源文件和目录

# cvs update -dP

CVS能够合并其他人的更改

正如我们之前看到的,“cvs update”将会让你的源文件升级到respository中的最新的版本——那么,你所做的修改怎么样了呢?不用担心,他们并没有丢失。如果有另一个开发者改变了一个你没有碰过的文件,你的本地文件将会被更新以使它的版本与仓库保持一致。

此外,如果你改变了一个本地文件的1到10行,另一个开发人员删除了第40-50行,并且在文件的末尾添加了12行,改变了第30-40行然后在你提交到仓库之前提交上去,cvs将会巧妙的将这些改变应用到你本地的副本从而保证了你的改变不会丢失。这个机制允许多个开发人员在相同的时间处理相同文件的不同部分。

合并功能并不总能完美的合并

但是,如果多个开发人员对相同文件的相同区域作出更改,这种事情会变得有些复杂的。如果发生了这种事情,那么cvs将会告知你此处有一个冲突。所有的改动都不会丢失,但是此时就必须要手动修正了,因为cvs需要你来决定如何合并相互冲突的改变。

关于commit

我们一会将会详细的讨论一下冲突是如何解决的,但是在开始之前,让我们假定在你输入“cvs update -dP”之后是没有冲突的——通常是不会发生冲突的。没有冲突的话,你的本地源文件就被更新得和仓库一致了,然后你就可以把你的修改提交到仓库了,在你的源文件主目录下输入如下命令即可:

代码 2.2: 提交修改

# cvs commit

commit做了什么

“cvs commit”不仅仅是将你的修改应用于仓库。事实上在把你的修改提交到远程仓库之前,cvs将会激活你默认的编辑器来让你输入对修改的描述。一旦你添加了注解,保存文件并且退出编辑器,你的修改(包括注释)将会在远程的仓库生效并且你的团队中的其他开发人员将能够看到这些修改。

查看日志

我们可以很容易的访问一个特定文件的完整历史,在提交时追溯开发人员建立的注释(包括你的)。想要查看此信息,输入:

代码 2.3: 查看日志信息

# cvs log myfile.c

“cvs log”命令是递归的,因此如果你想查看整个目录树的完整的日志,只需要进入目录然后输入:

代码 2.4: 使用分页程序查看日志

# cvs log | less

Commit选项

在你输入cvs commit的时候,你可以使用另外的编辑器来取代默认的值,如果你打算这么做的话,你只需要将你的EDITOR环境变量设置成你想要使用的编辑器的名称。在~/.bashrc中设置该变量将是个很好的主意:

代码 2.5: 设置我们的编辑器

export EDITOR=jpico

此外,你也可以指定日志信息作为一个命令行选项,使CVS在commit时不启动编辑器:

代码 2.6: 使用少量的日志信息来提交修改

# cvs commit -m 'I fixed a few silly bugs in portage.py'

关于.cvsrc文件

在我们继续学习更多的cvs命令之前,我建议设置~/.cvsrc文件,在你home目录下的.cvsrc可以告诉cvs使用首选的命令行的默认选项,这样可以让你不需要每次都记住这些命令,下面是我们建议的默认 .cvsrc 文件

代码 2.7: 推荐的默认设置

cvs -q  
diff -u -b -B
checkout -P
update -d -P

继续设置.cvsrc文件

在这一系列有用的选项中,.cvsrc的第一行把cvs设置为安静模式,有利于使 cvs update输出更多的consise以及增强可读性。同样的,一旦你设置了cvsrc文件,你可以输入cvs update来代替cvs update -dP

将文件添加到仓库

我们可以很容易的把源文件添加到CVS。首先,用你最喜欢的编辑器创建一个文件,然后输入如下内容:

代码 2.8: 添加文件

# cvs add myfile.c
cvs server: use 'cvs commit' to add this file permanently

上述提示的意思是只有在你使用了cvs commit命令之后其他开发人员才能看到新添加的文件。

将一个目录添加到仓库

仿照如下步骤将目录添加到CVS的方法:

代码 2.9: 添加目录

# mkdir foo
# cvs add foo
Directory /var/cvsroot/mycode/foo added to the repository

添加目录不同于添加文件,在你添加目录的时候它就出现在仓库中了;你不需要使用cvs commit命令,在你添加本地目录到cvs的时候,你会注意到一个“CVS”目录会该目录内创建,他的作用是保存cvs的帐户数据。因此,通过查找“CVS”目录,你能够轻易的判断出现行的目录是否已经加入到cvs中。

“cvs add”注意事项

哦,正如你所假想的,在你添加文件或者添加目录到仓库的时候,你必须要确定他的父目录已经添加到了CVS,否则,你可能会得到一个类似如下的错误:

代码 2.10: 在添加文件时失败

# cvs add myfile.c
cvs add: cannot open CVS/Entries for reading: No such file or directory
cvs [add aborted]: no repository

熟悉“cvs update”命令,第一部分

在我们学习如何解决冲突之前,让我们先熟悉一下“cvs update”命令。如果你创建了~/.cvsrc文件并且在文件中包含了“cvs -q”行,你会发现“cvs update”的输出容易读的多。“cvs update”会把它完成的和看到的按照一个单独的字符、一个空格加一个文件名的格式输出来通知你,就像下面的例子:

代码 2.11: 更新CVS

# cvs update -dP
? distfiles
? packages
? profiles 

熟悉“cvs update”命令,第二部分

“cvs update”使用“?”来告诉你它对于在你的本地仓库副本中找到的这些特定的文件一无所知。这不是官方仓库的一部分,或者他们只是准备要添加的文件。这里有一个其他所有的单字母信息的清单。

代码 2.12: U标志的相关信息

U [path]

该标志说明:该文件是你在本地仓库创建的,或者该文件被其他人修改了并本地副本已经被更新。

代码 2.13: A标志的相关信息

A [path]

该标志说明:当前文件将要被添加,并且将在你使用cvs commit命令的时候添加到官方的仓库

熟悉“cvs update”命令,第三部分

代码 2.14: R标志的相关信息

R [path]

类似于“A”,“R”标志,该标志会通知你该文件将在你使用cvs commit命令的时候被移除。

代码 2.15: M标志的相关信息

M [path]

该标志意味着此文件已经被你更改;此外,它也可能是仓库成功合并文件后产生的更改。

代码 2.16: C标志的相关信息

C [path]

“C”标志表示该文件产生了冲突并且在你使用“cvs commit”提交前需要手动修复。

解决冲突概述

现在,让我们看看如何解决冲突。我在Gentoo Linux项目中非常活跃,我们在cvs.gentoo.org建立了自己的cvs服务器。我们的开发人员把他们绝大多数的时间都花在开发gentoo-x86模块中的代码上。我们有一个叫做“ChangeLog”的文件,我们把我们对仓库中的文件作出主要更改都记录在这个文件里。

一个冲突的例子

由于我们在对CVS做了主要的更改后都要去修改该文件,这就产生了源文件的一个主要冲突——这里有一个冲突的例子。我在Changelog文件的顶端添加了如下的行:

代码 2.17: ChangeLog条目

date 25 Feb 2001
 
This is the thing I added myself

但是,让在我提交这三行之前,另一个开发人员添加了以下这些行并且提交了他们。

代码 2.18: ChangeLog条目2

date 25 Feb 2001
 
This is the part added by another developer

开始之前,一个冲突的实例

现在,当我运行cvs update -dP(你每次提交之前都应该这么做), cvs不能在我的本地Changelog副本中合并这些改变,因为我们都在相同的位置添加了额外的行——cvs怎么可能知道我们使用的是哪个版本?因此,CVS会通知我得到了如下错误:

代码 2.19: CVS错误信息

RCS file: /var/cvsroot/gentoo-x86/ChangeLog,v
retrieving revision 1.362
retrieving revision 1.363
Merging differences between 1.362 and 1.363 into ChangeLog
rcsmerge: warning: conflicts during merge
cvs server: conflicts found in ChangeLog
C ChangeLog

解决冲突,第一部分

啊~——出现冲突!所幸的是,解决该冲突是很简单的。当我用我喜欢的文本编辑器打开这个文件,那么会在Changelog的顶端看到如下的文本

代码 2.20: ChangeLog冲突

<<<<<<< ChangeLog
date 25 Feb 2001
 
This is the thing I added myself
 
=======
date 25 Feb 2001
 
This is the part added by another developer
 
>>>>>>> 1.363

解决冲突,第2部分

在这里,CVS并不是用一个版本取代了另一个版本,而是把两个版本都添加到了ChangeLog文件中,使用特殊的分隔符清楚的标记出冲突。现在还得由我来决定哪些应该出现在ChangeLog中;在这种情况下,我们不是对两个版本进行替换,而是将两者合并:

代码 2.21: ChangeLog新条目

date 25 Feb 2001

This is the thing I added myself

This is the part added by another developer

现在我已经解决了冲突,(并且移除了“=======”等等标记),我现在提交我的修改已经没有任何问题了。

解决冲突的技巧

每当你编辑出现冲突的文件的时候,请确定你已经遍阅整个文件发现了所有的冲突;如果你漏掉了某个冲突,cvs是不允许你提交它的,直到你把所有的冲突都解决掉才可以提交!移除那些特定的分隔符也是很重要的。另一方面——如果你在解决冲突的时候发生了错误而且("D'oh!")不小心保存了更改,你可以在“.#filename.version”可以找到你原始的副本。

移除文件

现在,是我们学习CVS最后的技能的时候了——从仓库中移除文件。移除一个文件分两步。首先,从你本地的源文件副本中删除该文件,然后执行cvs remove命令:

代码 2.22: 移除文件

# rm myoldfile.c
# cvs remove myoldfile.c

移除文件的后续过程

这个文件将在你下次commit的时候被从仓库中移除。一旦commit过的话,该文件将会正式的被从官方的仓库上移除。但是,cvs不能把这个文件丢弃,他仍然会保持该文件完整的记录和它的历史,万一你在将来会需要他们可以找到他们。这只是CVS保护你宝贵的源代码的诸多方法中的一种而已。

cvs remove是递归的,它意味着你可以批量删除文件,在父文件目录不调用其他的参数运行cvs remove命令。这样将会导致所有已经被删除的文件在下次commit的时候被从服务器上移除。

移除目录

如果你想要移除完整的目录,我建议你遵循以下过程。首先,彻底删除文件然后使用“cvs remove”命令移除整个文件目录。

代码 2.23: 移除文件

# rm *.c
# cvs remove

移除目录的后续过程

然后,执行commit:

代码 2.24: 提交更改

# cvs commit

移除目录的时机到了,执行以下步骤删除该目录:

代码 2.25: 移除目录

# cd ..
# cvs remove mydir
# rm -rf mydir

注意,删除目录不需要再次commit——目录在添加和移除的时候是实时的。

还原旧版本

如果CVS不具备还原旧版本的功能,那它就称不上一个好的版本控制系统,你可以依据指定日期来还原文件,当然你也可以依据指定版本号来还原。如下的例子将把现行的filename覆写为版本1.202的filename

代码 2.26: 用版本号来还原文件

$ cvs update -p -r 1.202 filename > filename

如果你打算使用日期来还原文件,使用-D 参数。你可以使用规范的时间、日期格式,但你也可以使用与时间有关的名词,例如yesterday或者last week

学习完成!

对CVS的讲解已经完成——我希望这份教程是有用的,CVS所涉及的知识不是本教程所能覆盖的,但是所幸这里有很多的CVS资源可以方便你使用,在将来你可以用它们来扩展你的CVS知识:

关于本教程

本教程的原始版本第一次刊登在IBM developerWorks,所有权属于Westtech Information Services. 当前的文档对原教程做了更新, 由Gentoo Linux documentation team做了很多改进.



打印

更新于2008年 5月 20日

本翻译的原始版本已经不再被维护

总结: 本教程向读者介绍了一个叫做CVS的版本控制软件,由于它的灵活性而被全球的开发者广泛使用。作为为新接触CVS的人而设计的教程,本文可以让一般用户和新开发者快速的上手。不管你是想用CVS来获取指定文件的最新版本,还是想像一个熟练的开发者一样使用CVS,本教程都会适合你。

Daniel Robbins
Author

Xavier Neys
Editor

王文沛
Translator

张乐
Editor

Donate to support our development efforts.

Copyright 2001-2014 Gentoo Foundation, Inc. Questions, Comments? Contact us.