技术开发 频道

第N次亲密接触CVS :Eclipse开发必备

        【IT168 专稿】说起CVS,作为版本控制与管理工具,几乎是无人不知、无人不晓。好吧,我承认。但是说起CVS的全称,未必所有人都知道吧?索性我们拿关键字CVS去Google英文搜索一下。有意思的是,第一条是http://www.cvs.com,但却是一家在线药品与医疗网站,并非我们想要的。第二条CVS维基百科才是我们想要的结果。

  维基百科对CVS的表述为:CVS的全称是Concurrent Versions System或者Concurrent Versioning System。无论老外是何种叫法,中文翻译过来都是并发版本系统。不言而喻,CVS有三个特征,并发性,版本控制,系统性。

  为什么我会写这篇文章呢。原因也很简单,我天天使用Eclipse做软件开发,我天天使用CVS。可是,我并非天天只做update/commit操作。当有一天,我需要一个patch,一个branch,一个tag,我就开始疑惑了,这些东西怎么用啊?怎么网上讲的总是东一块、西一块的,不怎么系统。索性,我就萌生了写一篇文章,系统性地介绍Eclipse的CVS功能。

  你可以说,具有这三大特征的版本控制与管理工具多的是,坦白来讲,CVS也不是完美的。比如由于实现方式的不同,SVN比CVS的速度要快。为什么我会选择CVS呢?主要原因我是Eclipse的忠实粉丝,而Eclipse天生就集成了一个CVS客户端。所以就变成了爱屋及乌了。

  前面介绍过,与一般性的介绍性文章相比,本文更注重系统性。再者,本文注重CVS的关键特性,特别是一些容易被忽视但却实用的高级特性。不但如此,文章会深入CVS的原理,将SVN与CVS做对比,以及总结一些实战技巧。本文目标读者是经常使用Eclipse CVS开发项目的开发人员。

  CVS三部曲:拷贝,修改,合并

  简单的说,CVS是一个遵循GNU的开源客户机/服务器端系统,允许多个开发人员通过一个中心版本控制系统来记录文件版本,从而达到保证文件同步的目的。这种客户机/服务器工作模式使得开发者可以从任何因特网的接入点存取最新的代码。

  CVS的工作原理很简单,就是Copy-Modify-Merge(拷贝、修改、合并)。具体来讲:CVS服务器端创建一个源代码库(CVS Repository),库里可以存放许多不同项目的源程序,由管理员统一管理。CVS客户端支持不通的平台。在使用CVS任何源代码库之前,开发人员需要将项目文件检出(Check Out)到本地,然后根据需要编辑(Edit),最后叫文件提交(Check in)至CVS服务器。

  CVS Repository

  CVS源代码库(CVS Repository)指的是CVS 存储所有修订版本历史记录的地方。每个项目都有自己的一个确定的源代码库。需要指出的是,创建一个新的Repository需要管理员权限。

  我们可以通过Eclipse创建一个新的CVS Repostority。具体方法如下:

  选择CVS Repository透视图 -> 右键New –> Repository Location ,弹出Add CVS Repository对话框,如下图所示。

 

  1. Location

  Location区描述的是CVS Repository的定位信息,包括主机(Host)与存储路径(Repository)。CVS Repository支持local与remote两种方式。对于本地,主机名为localhost或者127.0.0.1,存储路径前缀为“:local:”。例如,:local:/usr/local/cvsroot表示CVS Repository位于本地/usr/local/cvsroot目录。其URL的写法也因操作系统而有所差别。对于Linux,:local:/usr/local/cvsroot等价于/usr/local/cvsroot。对于Windows,:local:c:/src/cvsroot等价于c:\src\cvsroot。

  Repository的目录分为两部分:$CVSROOT/CVSROOT包含的是CVS的管理文件,而其余部分为用户自定义模块。我们除了使用Eclipse CVS客户端的方式指定Repository之外,还可以通过CVS命令行的方式,如下:

  cvs -d /usr/local/cvsroot checkout yoyodyne/tc #–d选项表示direcotry,即CVS Repository对应的目录。

  它等价于:

  setenv CVSROOT /usr/local/cvsroot

  export CVSROOT

  checkout yoyodyne/tc

  我们来分析一下CVS Repository对应的数据结构。我们假设当前的CVS Repository为/usr/local/cvsroot,如图。CVS Repository目录包含两部分,一部分为administrative files, 给CVS系统管理员使用,记录一些Repository相关的元数据等。另一部分就是源代码目录结构,这里源代码项目的根目录为yoyodyne。

 

  2. Authentication

  Authentication区描述的是认证信息。前面提到,匿名用户不允许创建Repository,只有管理员才有权限。

  3. Connection

  Connection区描述的是CVS客户端与CVS 服务器端的Repository之间的通信协议。当然,CVS客户端与服务器端可以是同一台机器,此时的主机名为localhost或者127.0.0.1。按照类型与需求的不同,又分为以下三类协议,分别为:pserver,ext/extssh,pserverssh2。具体含义如下:

  pserver协议:指CVS客户端向服务器发送的密码以明文的方式传送。对于匿名用户,URL为cvs -d :pserver:fun.example.com:/usr/local/cvsroot; 而对于密码用户,按照显式与隐式划分,URL分别表示为:

  cvs -d :pserver:doublelife@fun.example.com:/usr/local/cvsroot login

  CVS password:

  或者

  cvs -d :pserver:doublelife:p4ss30rd@fun.example.com:/usr/local/cvsroot login

  注意:cvs –d 命令表示指定CVS Repository。

  ext/extssh:指使用SSH建立CVS客户端/服务器间的安全连接。因此,从应用场景上说,Pserver通常适用于普通用户或匿名用户,而对于要求安全性较高的开发人员,则推荐使用ext/extsssh。ext与extssh的区别在于extssh只支持SSH1,而ext支持SSH1与SSH2。换句话说,extssh是ext的子集,推荐使用ext。需要说明的是,Eclipse CVS Repository透视图本身已经内置有SSH客户端,无需额外的插件。下面是演示的是ext协议对应的CVS命令:

  export CVS_RSH=ssh

  cvs -d :ext:doublelife:p4ss30rd@fun.example.com:/usr/local/cvsroot

  pserverssh2:指的是pserver over SSH2。我们简单的理解为pserverssh2是SSH2与pserver两种协议的叠加。相比pserver与ext/extssh来说,并不常用,这里就不给出示例了。

  对于CVS客户端/服务器间的通信协议,除连接类型外,还需要指定服务器端监听端口。服务器既可以使用默认端口,也可以根据需要,指定端口。比如,pserver协议对应的默认端口为2401,如果该端口被其他的服务器占用,处于侦听状态,此时CVS 服务器可以为Repository指定可用端口。

  在完成所有字段的填充后,我们推荐选中“是否在向导完成后验证连接有效”单选框,确保所填信息完整、有效。

  作为示例,我们演示一下使用Eclipse来连接著名开源网站sourceforge的FTP软件filezilla的CVS Repository,如下图所示。这里我们采用的是匿名用户,对应的连接协议为pserver,端口默认值。

  如之前所述,CVS Repository为树状层级结构。在正式开始copy->modify->merge三部曲之前,有必要花些时间熟悉上图CVS的几个关键概念:Module,HEAD,Branches,Versions,Dates。

  Module

  如前所述,CVS Repository分为两部分,一部分是CVSROOT,描述CVS工程相关的元数据。另一部分即为Module,表示工程的模块。比如filezilla分为三大模块,分别为FileZilla,Filezilla Server,Filezilla3。

  HEAD

  简单的说,HEAD表示CVS代码的主干与主体部分,正常情况下,我们对CVS的操作三部曲均发生在HEAD目录中。

  Branch

  而对于一些特殊的情况,我们采用的Branch方式。Branch相对于HEAD而言,指的是代码的分支部分,我们简单的理解为补丁。举个例子,假设我们的项目发布了第一个版本V1.0,第二版本V2.0正在开发当中,处于不稳定状态,随时有代码的改动。而与此同时,客户报告重大的bug,需要我们立即修复。于是,我们check out V 1.0的代码,调试,并找到解决办法,并发布补丁。为了让补丁与开发代码隔离,我们可以创建一个新的branch,用于V 1.0的补丁,用户从branch中check out的是V1.0的补丁,而不会得到位于HEAD目录的尚在开发阶段V2.0代码。当然根据需要,开发人员可以将branch代码合并至HEAD中。

  为更好地说明问题,我们从版本Revision控制的角度来对比HEAD与Branch的关系。一般说来,HEAD的Revision历史呈线性增长趋势,如下图:

  由于Branch概念的介入,使得CVS并不局限于线性的开发,HEAD版本可以分为若干不同的Branch,每一个Branch是一个独立开发的自我维护的开发线。如下图所示,出现了三个不同的分支,分支号的编排依赖于它分离出的主线版本。使用分支号允许一个特定版本分离出多个分支,图中Revision 1.2同时派生出两个不同的Branch。一个分支也允许派生出多个子分子,Branch 1.2.2派生了一个sub branch。

 

  一个Branch的变更可以很容易转移到HEAD中。可以通过CVS命令update配合-j选项实现合并。这里的-j表示join。

  举个例子,我们当前的HEAD版本为1.4。现在需要将分支1.2.2合并到HEAD。

 

      我们假设模块”mod”只包含一个文件”mod.c”。分支1.2.2分配了一个名字叫R1fix。

  CVS checkout mod #检出最新版本1.4

  CVS update –j R1fix m.c #合并所有分支中的变更,即1.2与1.2.2.2.2的合并

  CVS commit –m “Included F1fix” #建立新版本1.5

  下图就是合并后的Revision历史结构。

  在合并过程中可能会发生冲突,可以通过手工地方式解决。

  Versions/Tag

  Version是标签Tag的集合。所谓Tag指的是当项目达到某一个milestone时,对所有文件做一个标签,记录历史记录。通常对一个release做一个tag,如下图所示。

  为更好地理解tag的工作原理,我们可以将 tag 想象成为一条在由文件名和修订号组成的矩阵上穿过的“曲线”,如下图所示:

  

1
相关文章