1版本控制的概念以及几种版本控制工具的简介
1.1源文件的标示与版本的概念
源文件是一个软件最为重要的一个组成单元,因此源文件的管理也是整个软件组成管理中最重要的一环,是进行高效软件开发的关键岁所在.对源文件进行管理,必须对源文件进行标示.通常认为只需文件名.而实际上,在一个版本维护工具下对于源文件的标示包括两部分:文件名与版本.即:源文件可以用如下的二元组表示 {filename,version};
所谓版本,一般的软件开发人员都有一个直观的理解,但却很难做出准确的定义.版本,是指某一特定对象的具体实例的潜在存在.这里的某一特定对象是指由版本维护工具管理的如软件组成单元,一般指源文件.具体实例则是指软件开发人员从软件储藏室中恢复出来的软件组成单元的具有一定内容和属性的一个真实拷贝.
把版本定义成一个潜在的存在是基于以下考虑:版本作为源文件的一个表示部分,软件人员对它的引用实际上想得到不同的版本对应的不同源文件,所以说版本是一种抽象.它用来定义一个具体实例应该具有的内容与属性.也就是说版本是一个具体实例的潜在存在,是源文件不同化身的抽象.
有了以上对版本概念的认识.大家对版本维护工具就有了一定的理解:版本维护工具,应该对开发人员屏蔽源文件的存储方式对开发人员是透明的,开发人员不需要明白软件存储库里有什么源文件,只需要说明需要什么样的源文件以及要存储什么样的源文件,版本维护工具自动完成这一切工作.关于版本维护的理论还包括:版本的空间,维数,版本的表示,存储,合并等理论,因为我们这里主要说明cvs 的linux 服务器,所以一切从简,对这方面有兴趣朋友可以阅读相关书籍
本章所讲的cvs 就是这样的一个强大的工具.在讲述cvs 前我们先介绍其他几个版本维护工具.
1.2 几种版本维护工具的简介
1.2..1 SCCS
SCCS的全称是Source Code Control System .在介绍之前先定义工作文件的概念.所谓工作文件,是指从软件储藏室得到的有”写”权限的源文件.
SCCS 是一种基本的源文件版本控制工具,它适用于任何正文文件的版本维护.它基于单一文件的版本控制,通常,它的软件储藏室和要维护的文件在同一目录下.
SCCS 工作时,有一个专门的SCCS 格式的文件保留其源文件的编码版本,其记录了足够的信息来生成新的版本,并记录了谁对文件有修改权,拥有该版本的”锁”.
SCCS的版本好事一个四元组,即:发行号,级号,分支号,序号(release,level,banch,sequence)
1.2.2 RCS
RCS是另一种基本的源代码管理工具,是WALTER.f.Tichy 于1980 年在Indina的 Purdue 大学开发的.RCS和SCCS 类似,也是基于单一文件的版本维护系统.RSC 通过RSC 文件进行文件管理;使用RCS 进行维护的过程与SCCS 相似,也是按恢复提交模式进行的,不多赘述
RCS文件,是RCS 系统中源文件的储藏室,它是一种特殊的编码文件,包含了开发人员恢复老版本的源文件以供开发使用的足够信息.它通常是以.V 为后缀
它的结构如下:
? RCS 头,这一部分记录了对应文件的版本树的头版本号,
? 版本描述: 这一部分描述RCS树上的各个节点的属性性质
? 初始信息:之一部分是在创建第一个RCS 版本时的表述内容
? 文件内容
RCS与SCCS 相似,RCS将所维护的版本也组织成树形结构.但RCS允许多重分支,即,RCS的版本号不像SCCS那样是一个四元组.形式为;发行号,级号,[分支号,序号].版本树如下
1.2.3综述:
这两种版本维护工具的共同点是:采用了”锁”的方式,对当前问坚持有”锁”的用户才有对文件的修改权.他们采用的机制是所谓的” lock-modify-unlock”.采用这种即只有一个知名的弱点,那就是不至此多用户并发的使用.
2 在linux 下构建cvs 服务器
2.1 CVS简介及基本原理
CVS 的全称是Current Version Control. CVS是一种GNU 软件包.由Intersolv公司开发,最新的版本是1.10.8.它是一种基于RCS系统的维护工具.它明确的将源文件的存储和用户的工作空间独立开来,有在一定的模式上扩展了RCS的恢复提交功能. 并使其有利与并行开发.
CVS 将源文件的RCS 文件根据其源码树的层次集中在一个目录下,该目录的绝对路径由环境变量CVSROOT 定义
可见该目录可以分成两部分:一部分为${CVSROOT}/CVSROOT,它包含CVS所需的一些管理 文件.另一部分为源文件所形成的RCS 文件,并按软件开发的源码树的结构来构成.
关于CVSROOT 下的文件,在以后的章节,有详细的讲解,,而源代码目录下的工作空间结构
CVS 在进行源代码管理是有以下特点:
1,源代码空间与用户空间分离.
CVS 系统将源代码文件放在repository下,用户要修改文件必须将repository 下的文件作一个拷贝之后才能进行,
2,并发访问
CVS 系统允许多个开发人员同时获取同一文件的的同版本源文件.当然这也是CVS获得广泛应用的主要原因.开发人员提取一个文件时,将在自己的工作空间建立一个与其他开发人员相互独立的拷贝,此文件的版本号与文件“头”版本相同,除非他用commit 命令完成版本的永久性升级.而此时,其他用户可用undate 命令是自己的版本号与”最新的头版本号”相一致.:若用户在checkout 后发现头版本改变了,可用RCS系统的rcsmerge 命令形成一个新文件,这个新文件及包括原来的内容,又包括用户修改的内容.此示弱与其他同时在对同一文件修改的开发人员发生冲突,可通知他们进行手工修改
所以说,CVS系统是一个Copy-Modify-Merge 的算法而不是以上我们提到的那两个系统所采用的lock-modify-unlockj机制 这种算法的好处在于,软件开发人员可以得到一份源文件的拷贝(Copy),并不会对该文件上”锁”,因此为并行开发提供了可能,在得到 拷贝后, 开发人员可以在自己的开发环境下进行修改(Modify),然后提交自己修改后的文件,与源文件进行合并(Merge).形成新的版本,
3,源文件共享
CVS对${CVSROOT}的使用是不同的用户可按自己的需要拷贝不同的模板,修改后载体交给${CVSROOT}.这样用户可共享源文件.这当然是我们建立CVS服务器所必需的
4,独立的工作环境
用户在自己的工作环境下进行修改开发,自然有独立的工作环境,值得说的实.CVS 也支持”锁”的机制.允许用户对自己获得的模板拷贝进行锁定
5,标记
CVS为了方便用户,引入了一个tag文件,该文件位于用户工作目录下,与被他标记的文件一级的CVS 目录下对特定的tag 文件操作,即对相应版本的操作,即使这个版本被修改过.
2.2.CVS 的获得安装
CVS 在一般的linux发行版本中都有默认的安装.如果你的系统没有安装也没有关系,CVS可以在intenet 上很方便的得到. 它的源码在ftp://202.113.29.4/pub1/unix/cvs 它的说明文档在ftp://202.113.29.4/doc/cvs.任何人可以很方便的下载.目前他的最新版本是2..10.8..
安装过程大致如下:
1,在任一目录下解开下载的压缩文件.
2利用文件包内的安装工具,完成安装,(内有说明文档). 通常是:
make config
make install
(不通版本的,安装方法可能不同,具体的请参见,它自身所带的安装文档)
2.3服务器的安装使用
在安装完CVS 系统后我们便可以开始CVS仓库的安装. 在linux环境里,CVS的使用一般是以命令行方式,也有一些GUI的前端工具,如TKCVS等.这里我们将应用CVS 的一般命令即 cvs [cvs的选项] cvs-command [command 选项] 的方式完成服务器的配置.
2.3.1软件仓库(repository)的管理
1创建CVSROOT根目录
首先编辑有关的环境变量.(CVS的几个重要的环境变量如下:
CVSROOT 仓库根目录的完整路径名
CVSREAD 如果设置,表明在checkout操作时所有的文件都置成只读
CVSBIN CVS利用了很多RCS的命令,指定乐RCS工具的路径
CVSEDITOR 指定用户书写日志信息所使用的编辑器
CVS_RSH 启动一个远程CVS服务器时,所使用的shell的名称
CVS_SERVER 决定""cvs server""的名字,缺省是CVS
CVSWRAPPERS cvswrapper脚本, 用来指定包装文件名.)
其中中重要的是CVSROOT,它指明了仓库所在的位置,在创立新的仓库时,它是必不可少的.因此一般需要加入环境变量CVSROOT的定义.如在 /etc/bashrc 文件中加入下面两行
CVSROOT=/cvsroot
export CVSROOT
或者直接在命令行上执行
$ export CVSROOT=/cvsroot
然后在相应位置开始创建CVSROOT
$mkdir cvsroot
$cvs init
如果没有定义变量CVSROOT 会出现这样的提示:
cvs init : No CVSROOT specified! Please use the ‘-d’ option
cvs [initn aborted]:or set the CVSROOT environemnt variable
如果你是在不想定义环境变量,你可以用这样的命令:
cvs –d /cvroot init 不过即使你定义了CVSROOT,参数 –d 后的内容也会覆盖它.如果没有错误提示, 恭喜你, 你的CVS 软件库已经建立好了.
剩下的问题就是怎样时多用户来使用这个仓库来进行并行的软件开发与版本控制.还有作为CVS 管理员你应该设置你的用户的权限. 此时,你的cvsroot 下有一CVSROOT 子目录.他下面的文件时CVS 的配置文件,用
ls /cvsroot/CVSROOT 有一系列文件,他们的用途分别是:
checkoutlist 支持CVSROOT目录的其它管理文件,允许为各种CVS命令定置信息
commitinfo 在cvs commit命令执行时,这个文件指定乐文件提交时执行的命令
cvswrappers 定义乐一个包装程序当文件登记或检取时就会执行.
editinfo 允许你在commit命令启动前在日志信息被记录后执行的脚本
history 跟踪所有影响仓库的命令
loginfo 类似coimmitinfo, 只是在文件提交后执行
modules 允许为一组文件定义一个符号,否则必须为每一个要引用的文件指定cvs仓库的路径名($CVSROOT)
nitify 控制从""watch""来的通知.""watch""由""cvs watch add""和""cvs edit"" 设置
rcsinfo 为commit log回话指定一个模板.
taginfo 定义乐在任意""tag""操作后执行的程序.
Passwd 缺省没有.存储用户passworld的文件
设置管理权限:
源码管理员应对仓库下的文件和目录设置恰当的许可权限来控制访问. 所有的RCS文件(以,v结尾)是只读方式,仓库中的目录应当对使用者有写权,以便允许其更改.
3.2.2多个软件库的建立
如果你有几个开发组, 他们的工作毫不此相干,你完全可以建立几个不同的软件库.你要做的只是要重新定义一下环境变量CVSROOT,或者,使用-d 来设置,使用多个软件库的好处是,他们可以在不同的sever上,CVS 1.0 版还不能用一条命令来从不同的软件库中取出文件,在她以后的版本中,你可以将不同SEVER 上的源码取到你的工作目录下. 以下是一个怎样在多软件库下建立工作目录的例子:
cvs -d server1:/cvs co dir1
cd dir1
cvs -d server2:/root co sdir
cvs update
第一条命令建立了一个工作目录,在sever1上取出了文件第三条命令则在sever2 上的软件库中取出了一些文件.然后用所有的文件使sever2 上的文件升级.
3.2.3软件库的备份与移动
备份软件库中的文件和备份其他文件并没有不同,但你需要一个备份工具开锁住CVS.这样,你必须先注册一个可以读取软件库的用户,打开CVS软件库, 你需要编辑``#cvs.rfl’文件.
当你想用一备份的软库,恢复原来的库时,如果原库在你备份后作过改动.CVS将报错.你必须按以下的步骤来
? 得到一个新的工作目录
? 拷贝上一次提交失败的文件(,当然不能拷贝CVS的目录文件)
? 在新目录下工作,使用cvs update 或cvs diff 等命令指出那些作过改动,在使用cvs commit 将改动保存到软件库.
你如果想移动一个软件库到其他地方也很简单.最简单的方法是将向要移动的目录创到一个新的目录中去.如果你想重新使用原来的目录的话,只能手工修改``CVS/Repository 和 ``CVS/Root,除非你精于此道,否则不建议使用.
3.2.4软件库的远程使用我们放到下一节中详细讲解
3.3CVS 的远程用户管理
作为一个CVS 服务器,应该满足两点.首先保证软件库有足够大的空间. 其次保证有不小于32M 的内存, 服务器为每一个连接它的用户产生两个进程,在子进程上的花销较小.但如果网络带宽不够,在父进程上的花销是巨大的.
另外一个大的花销是diff 文件,当源文件很大时,对他进行验证或检测的花销也很大.
3.3.1下面我们已普遍使用的rsh 进行远程登录为例子,讲一下远程用户的登录认证方式:
Cvs 服务器使用rsh 允许用户连接时应该配置.rshosts ,假设在远程主机tom.exsample.com 上的用户tom 需要连接到funame.simple.com 这个cvs 服务器上工作.应该编辑服务器端的bach 目录的.rshosts ,在其中加入
Tom.exsample.com tom
客户端可运行 rsh –l bach funame.simple.com ‘ echo @PATH
以确定自己可以连接到CVS 服务器上,并且客户应将显示的这个路径写入 .bashrc 或 cshrc (而不是.logiin 或.profile.)
除此之外,客户端还可以定义环境变量CVS_SEVER.来定义主机的位置. 在服务器端,修改/etc/ineted.conf文件,来告诉服务器,在获得特定端口的连接时,运行cvssever .这个端口号缺省值是:2041.当然为了不再每次使用时都要进行设置,可以在用户端通过定义环境变量CVS_AUTH_PORT来使用.
如客户端的ineted 允许使用原始调用,只需在/etc/ineted.conf 文件中添加下面的句子即可2401 stream tcp nowait root/usr/local/bin/cvs cvs –f –allow –root=/cvsroot pserver
另外还可以用- T 选项来定义缓冲目录.
—allow – root 定义了可供用户使用的软件库,如果服务器上有更多的软件库需要重新使用这个句子. 另外,内部用户可以这样定义环境变量CVSROOT
:pseve:
[email protected]:/cvsroot
其中 usr是用户名(linux系统的)funam.simple.com 是cvs 服务器,并假设 /cvsroot是软件库的目录名.远程用户可用下面的形式:
:etx:psever:
[email protected]:/cvsroot
:etx: 定义了是远程用户,其余同上.
如果客户的ineted 采用一般调用,只需在 /etc/seveice 中加入下面这一句:
cvspsever 2401/tcp
重新启动ineted 使其读取初始化文件即可.
CVS 对于用户的管理.在缺省状态下(如用telnet登陆),是和linux 系统使用同样的用户名与密码.即:只要你登陆linux 系统,就拥有$CVSROOT的只读权限..当然,对某一软件的开发者来说必须拥有相对应目录的读写权限,才能完成源代码的升级等工作.因此,CVS 也提供了自己的用户认证体系以更加方便的管理用户.
在服务器端的$CVSROOT/CVSROOT 下可以有一个passwd 文件(可以用环境变量CVS_PASSFILE 来定义.)来记录cvs的用户信息.他采用的是和linux etc/passwd 同样的组织形式 .同样,它的passwold 也是经过linux标准加密方式的.下面是一个passwd 的内容
anyone:
tom:xyzkue
mary:yuio:pubcvs
第一行的意思是,当用anyone 的身份登录时,不需要任何密码.即使打入空串也可以.当然在这种方式下你得到的可能只是只读权.第二行的意思是,tom 登录时需要敲入passwold.该passworld 加密后是xyzkue.以这种方式登录后.一般可以获得较高的权限.
第三行的意思是,当mary 登录时需要敲入密码.进入和系统用户相同的软件库.这样作的目的是因为.cvs 将纪录它的用户在软件库中的所有的动作.定义和系统使用者不同的身份.使用cvs.
例如;使用用户tom可以用以下的方式登录:
$cvs –d :etx:
[email protected]:/cvsroot login
(如果tom 这个用户名在远端可内部是相同的,则tom@ 还可以省略)
同时,可以使用cvs loginout 将所有的环境变量撤销.
3.3.2,使用GASSAPI 接入
CVS 还支持使用 GASSAPI 的TCP 直接接入.在使用安转之前,需要将CVS 重新编译以获得 GASSAPI 的支持.运用 –with –gassapi 来连接,或者用-a 选向来连接.使用GASSAPI 的认证等方式余地一种方式相同.
但在登录时.需要重新定义$CVSROOT 如:
$cvs –d :gsever:
[email protected]:/cvsroot login
3.3.3使用kerberos 直接连接
最简单的使用rsh 的方法如上说述.这种方法的主要特点是所有数据都通过一个额外的程序.非常费时.如果安装了kerberos.可以直接用TCP 连接.
同样CVS 需要重新编译以获得keberos的支持可用--with-krb4 来连接.这样传输的数据是没有加密的.如果要获得安全.必须在服务端和客户端都用``--enable-encryption来连接.此时,你应用通用的变量-x来要求加密.
在服务端需要编辑inetd.conf 来运行cvs kserver .客户端的缺省端口号是1999,如果想用其他端口号.需在用户端定义CVS_CLIENT_PORT
此时的登录命令应是
cvs -d :kserver:faun.example.org:/usr/local/cvsroot checkout foo
3.3.4用fork连接
用这方式,可以通过远程协议连接本地硬盘的软件库.换句话说,他能和:local:有相同的功能用这种方式的登录命令是
cvs -d :fork:/usr/local/cvsroot login
和用:etx:一样.缺省的主机名是cvs
4 cvs服务器架与应用实例
4.1服务器安装配置
假设目前有一工作项目,需要用cvs 来管理员码.这个开发组为内部3人,器ip 分别是:192.168..1.2(用户名t1),192.168.1.3)(用户名时t3),193.168.1.3(用户名t3),远程用户一人,其主机是 tom.example.com.(用户名是tom)
如果你已经安装了cvs,建立一cvs 主机为 cvs.exam.org的服务器非常简单.步骤大致如下:
1)以root 身份登录.建立软件库(repository)
$export CVSROOT=/cvsroot
$mkdir /cvsroot
$cd /cvsroot
$cvs init
2)设置执行权限
$chmod /cvsroot/CVSROOT 744 (/ccvsroo/CVSOOT设置为只读)
$chmod /cvsroot 764 组号 (/cvsroot 对开发组可读写)
3)修改文件以利于其他人使用,因为开发组人员是系统用户,所以在这里用系统的用户名和password是比较好的.先编辑.rhosts件如下:
tom.example.com. tom
修改服务器上的 /etc/inetd.conf文件,加入如下的句子
2401 stream tcp nowait root /usr/local/bin/cvs cvs -f --allow-root=/usr/cvsroot pserver
这样一来,一个基本的cvs 服务器就建好了.
我们这里只是提供了一个基本的应用.cvs 真正的细节应用非常复杂.我们这里只进行一些抛砖引玉的介绍.与兴趣的朋友可以细读它的安装文档.
4.2应用实例
(使用主机上的cvs 系统,可以telnet 到主机上,然后使用,和本机使用没什么差别,所以本文一直不提)
假设上例中的tom 想使用 cvs 主机上的cvs系统以和大家协作开发.首先它营配置自己的一些环境变量.首先编辑/etc/services加入:
cvspserver 2401/tcp
然后在.profile 文件中加入如下代码
CVSROOT=:etx:psever:
[email protected]:/cvsroot
Export CVSROOT
这样他便可以使用$cvs login 来登录了.下面我们便以tom 的具体使用为例子讲一下cvs 的应用.和其他linux 命令一样.cvs 也有大量的参数.我么将在下一节做一个列表.
Tom 在/usr/test 目录下有如下文件
Ecample.c exampl2.c tes1.c test2.c utimel.c tty.c
如果想在软件库建立自己的目录
$cd /usr/test
~test$ cvs import –m “tom first creation” tomdir tom tomwork
N tomdit/example.c
N tomdit/exampl2.c
N tomdir/test1.c
N tomdir/test2.c
N tomdir/utmel.c
N tomdit/tty.c
No conflicts creat by this import
命令说明 import 提交命令 –m 后加描述.tom 发行商 tomwork 是发行标号.如果提示CVSROOT 不对,可以用- -d 加CVSROOT
注:一切cvs 的命令 可以用 cvs command –H 来获得帮助. 这时服务器端的/cvs 目录下多了一个 tomdir 子目录.内容下:
example.c,v exampl2.c,v test1.c,v test2.c,v tty.c,v utimel.c,v
至此首次提交完成.
这样便是一不小心删除了test 目录也不用担心.使用如下命令,可从软件库中检出源文件的备份
$/cd usr
``usr$/cvs checkout test tomdir-r 1.1
U test/exsample.c
U test/exampl1.c
U test/test1.c
U test/test2.c
U test/tty.c
U test/ulnem.c
cvs checkout命令缺省是得到最新版本.我们也可以得到某一个老版本,此命令是将tomdir的1.1版的代码取出. 恢复后test 目录增加了一个CVS 目录是用来管理的,以便在你下一次提交或修改的时候,和服务器上的管理文件相接口,保证版本好.这个例子非常小,现实中工程非常可能文件非常多,这样可以先恢复 CVSROOT 的模块
~usr/$cvs checkout CVSROOT/mouldes
~/usr/$vi CVSROOT/mouldes 编辑模块名,如我们在文件尾加上
src project/src
print project/src/print
cvs commit
以后我们就可以用cvs checkout print来代替
cvs checkout project/src/print
编辑完后即可以提交文件
~usr/$cvs commit –m “edit of mouldes name” CVSROOT/mould除了可以恢复整个目录后也可以恢复单个文件或模块
~usr/test/$cvs checkout –m “newer file “ tty.c
在恢复了文件以后, tom 便可以使用各种编辑器,对源文件进行修改,修改完成以后就可以提交它的工作成果了
~/usr/cvs commit test tomdir.
这样,tom 便完成了一次源文件的升级.其他的同理可得
4..3在管理中的相关技术
4.3.1 在管理源文件的一种技术叫""关键字替换"".在每次执行""cvs commit""操作后源文件的某些关键字会被替换为可用的词
$AUTHOR$ 用户名
$Data$ 登记时的时间
$Header$ 标准的首部,包含RCS的完整路径名,日期,作者
$Id$ 除RCS文件名不完整外与$Header$同.
$Log$ 包含RCS的完整路径名,版本号,日期,作者和在提交时提供的日志信息.
$RCSfile$ 包含RCS的文件名,不包括路径名
$Revision$ 分配的版本号
$Source$ RCS文件的完整名
$State$ 分配的版本的状态,由 cvs admin -s 分配.
例: 在cvs commit之前,main.c里有
static char *rcsid=""$Id$"";
执行cvs commit后
main.c的改行变为:
static char *rcsid=""$Id: main.c,v 1.2 1999/04/29 15:10:14 trimblef Exp$"";
当然,这里只是一个演示.在实际的程序开发中,这种技术有非常有用的作用,在此不多赘述.有兴趣的朋友可参考相关书籍.
4.3.2 创建分支可以使用户对一些文件使用命令commit进行修改时不会影响主干. 创建分支应首先为拟作修改的那些文件创建一个标签(tag).
标签是赋于一个文件或一组文件的符号.在源代码的生命周期里,组成一组模块的文件被赋于相同的标签.在工作目录中执行
~usr/teat/$cvs tag release-1-0
标签创建后, 就可以为其创建一个分支:
~usr/teat/$cvs rtag -b -r release-1-0 release-1-0-path print
-b :创建分支
-r release-1-0 :指定存在的标签
releas-1-0-patch:分支
print: 模块名
使用cvs update -j 选项可以将分支上的改变与本地文件拷贝合并.
~usr/teat/$cvs update -j release-1-0 print.c
对源文件作必要修改后, 可以用cvs release 删除本地工作拷贝
并通知其他开发者这个模块不再使用.
~use/$cvs release -d test
4.3.3 冲突解决
在有多个用户对同一个文件进行修改时,如果修改了其中的相同部分,而修改后的内容如果有不同的话,出现冲突是不可避免的。
例如在CVS 文件仓库中有一个文件 test.c ,它的版本是 1.4, 用户A 先检出该文件进行修改,而稍后有用户B 检出该文件进行修改,并提前提交成 1.5,这样在用户A再提交时就会出现冲突,这时CVS会提示需要手工解决。
例如,文件仓库中的版本1.4:内容为:
#include
main()
{
int i;
for(i = 0; i < 100; i++)
printf(“Count: %d
”, i);
}
用户B 1.5:
#include
main()
{
int i;
for(i = 0; i < 10; i++)
printf(“Count: %d
”, i);
printf(“Over
”);
}
用户A :
#include
main()
{
int i;
for(i = 0; i < 50; i++)
printf(“Count: %d
”, i);
return;
}
提交时会提示有冲突,这样需要手工编辑,这时如果用户A运行了$cvs update 之后,再编辑test.c, 会看到test.c 的内容是这样的:
#include
main()
{
int i;
<<<<<<< test.c
for(i = 0; i < 50; i++)
=======
for(i = 0; i < 10; i++)
>>>>>>> 1.5
printf(""Count: %d
"", i);
<<<<<<< test.c
return;
=======
printf(""Over
"");
>>>>>>> 1.5
}
这样就需要,根据任务的不同,来手工修改,这是比较麻烦的,所以在真正的协作开发中,很少,对同一个文件给与,很多人相同的提交权限.
4.3.4文件版本管理
版本管理系统,最重要的莫过于对文件版本的管理,系统默认的版本升级使用的版本号是一定的.如果由于特殊需要,要自己定义出文件的版本号时,你可以用一下命令:
cvs log [-lR][-r rev][-d date][-w login][files…]
其中,参数的意义如下:
-l 不处理子目录
-R 对子目录做同样处理
-r 指定版本号
-d 指定时间
-w 指定登录名
使用下面的命令可以参看当前模块的版本号或指定文件的所有历史版本信息。
cvs annotate [-lR][-r rev|-D date] files
其中,参数的意义如下:
-l 不处理子目录
-R 对子目录做同样处理
-r 指定版本号
使用下面的命令可以参看指定文件(检出之后)的所有修改信息。
$cvs annotate cvstest/c/test.c
输出依次为:版本 修改人 修改时间 源代码
1.1 (tang 18-Jan-00): #include
1.1 (tang 18-Jan-00): #include
1.1 (tang 18-Jan-00):
1.1 (tang 18-Jan-00): main()
1.1 (tang 18-Jan-00): {
1.1 (tang 18-Jan-00): int i = 0 ;
1.1 (tang 18-Jan-00):
1.1 (tang 18-Jan-00): for(i = 0; i < 20; i++)
1.1 (tang 18-Jan-00): printf(""Count: %d
"", i);
1.1 (tang 18-Jan-00):
1.3 (tang 18-Jan-00): printf(""222222
"");
1.4 (tang 18-Jan-00): printf(""333333
"");
1.1 (tang 18-Jan-00): }
使用下面的命令可以生成相对于一个指定主版本的分支版本:
cvs rtag –b –r rev_root rev_branch file_name
其中,参数的意义如下:
-b 指定生成一个分支版本
-r 指定该分支的主干节点版本号
rev_root 主干版本号
rev_branch 分支版本号
file_name 指定文件,使用“.”表示当前目录下所有文件
使用下面的命令可以生成一个对应版本号的分支版本,由于CVS 版本号是用数字表示的,而且在同一个模块下不同文件的版本完全可能是不同的,所以使用标识会更方便。
例:
$cvs rtag –b –r 1.2 tlb-1 SOURCE
以后要访问该分支版本,可以使用“-r” 选项
$cvs checkout –r tlb-1 SOURCE
从当前检出的版本切换到一个分支版本:
$cvs update –r tlb-1 SOURCE
使用下面的命令可以看版本信息:
cvs status [–vlR] files
其中,参数的意义如下:
-v 显示所有信息
-l 不显示子目录信息
-R 显示子目录信息
命令:cvs update –j rev module 把当前所做的修改与指定版本的文件进行合并。
如:主干 1.1 1.2 1.3 1.4 1.5 1.6 ↓
分支tlb-1 1.2.2.1 1.2.2.2 1.2.2.3
如果要合并分支tlb-1上的版本:
$cvs update –j 1.2.2.3 –j tlb-1 test.c
其中1.2.2.3可以通过tag命令生成一个容易记忆的标识。
如果要合并分支tlb-1到主干上1.2 :
$cvs update –j tlb-1 test.c
如果要合并主干上的不同版本(注意顺序很重要,同时在指定版本之间的所有修改将被丢弃):
$cvs update –j 1.5 –j 1.2 test.c
如果在不同版本之间模块的文件有增减,则可以:
$cvs update –A
$cvs updata –jbranch_name
5, 命令集
在本章的例子中,介绍了很多,命令的详细用法,其大多数是以应用的角度,来分析的.实际上.cvs 拥有,大量的命令.如gcc 一样cvs 常用的命令也不是很多,在本节中,我们列出了一些常用的命令.力图不和以上各节中介绍的相重复.当然,限于时间和水平,在此也不可能列出cvs 所有的命令.有兴趣的朋友.可以,参考,cvs的说明文档,与linux 的man文档,详细学习,也可来此做出指导
(1)检出源文件
cvs checkout [-r rev][-D date][-d dir][-j merg1] [-j merg2] modules
其中,参数的意义如下:
-r 检出指定版本的模块
-D 检出指定日期的模块
-d 检出指定目录而不是模块
-j 合并当前版本和指定版本
使用下面的命令会检出刚才生成的模块,并在当前目录下生成与文件仓库中完全一样的目录结构:
usr$ cvs checkout project
usr$ cvs checkout project/src/main
cvs checkout的详细用法见cvs -H checkout的输出.
2)CVS commit 命令 在对文件的修改完成后,用cvs commit提交到仓库.
cvs commit -m ""Update by xxxxx"" project
cvs commit -m ""Update main.c"" main.c
提交完成后,当前的版本号会更新,如原来为1.1,现为1.2. 这两个版本都在仓库的主干(maintrunk)上.
-m选项可以记录有关提交的注释.如果没有指定-m选项,在环境变量CVSEDITOR中指定的编辑器被调用(vi是缺省的),提示键入文本,修改记录注释.
3) 删除、增加、重命名文件和目录
cvs add [-k kflags][-m message] files...
其中,参数的意义如下:
-k 指定以后该文件的缺省检出目录
-m 对文件的描述
上述命令会加入一个新的文件到文件仓库里,但直到使用了提交命令它才会真正更新文件仓库。
cvs remove [options] files
上述命令会从文件仓库中删除文件,但也要到提交之后才有作用。
例1:增加文件
$cvs checkout SOURCE
$cd cvstest/c
$touch test.c
$cvs add test.c
$cvs commit –m “add test.c”
例2:删除文件
$cvs checkout SOURCE
$cd cvstest/c
$rm test.c
$cvs remove test.c
使用 –f 选项能上面两步合做一步。
$cvs remove –f test.c
如果在提交之前想恢复刚才删除的文件,可以如下:
$cvs add test.c
如果只执行了第一步删除(rm),则可以用下面的方法恢复:
$cvs update test.c
对于重命名的文件,可以先删除再添加。
对于目录的修改(重命名),可能需要修改cvs 管理文件,一般应该遵循以下步骤:假设tom正在修改文件的一部分,现想合并更新自己的本地拷贝(checkout)和另一个人所做的修改(已经放在仓库里),可用
~usr/test/$cvs update
确认所有有关的修改都已经提交;
进入文件仓库中要修改的模块目录,对相应的目录进行修改(重命名或删除)
$cd $CVSROOT/modules
$mv old_dir new_dir
如果有必要,修改管理文件,比如modules 文件,如果要删除目录,则应该先对目录中每个文件都进行了删除(包括使用cvs remove )处理之后再执行上面的第2步。
(4提交源文件
cvs commit [-Rl][-m mesg] files
-R 连子目录一起提交
-l 只提交本地目录(不提交子目录)
-m 注释信息
在检出源文件之后,在工作目录中对源文件进行的所有修改都必须在提交之后才能使文件仓库中的源文件起作用,并且新的文件才能够被分配一个新的版本号。
(5)释放工作目录
cvs release –d SOURCE
这个命令会删除工作目录 cvstest/c (建议在提交了修改的模块后执行这一步), 它比使用 rm –rf cvstest 要好。
在此介绍了,使用cvs 服务器进行.并行开发中常用的.命令.希望能起一个抛砖引玉的作用
5小结
本章,介绍了一些版本控制的知识.与几种办控制系统.并从易用的角度,讲述了在linux机器上,构架cvs服务器的过程,与cvs 简单的使用方法
cvs 也可以说是一种网络应用程序.它的功能在于,他能提供在并行的条件下,对多用户同时开发,便利,安全的源码.管理模式.个人认为,cvs的出现是自由的linux的必然产物.
他也会在linux这片热土下,得到更有前途的发展.
本章,旨在易于入手.对于cvs复杂的应用,管理过程.笔者也不是很熟悉.这需要在实际的应用中,积累经验.学习提高.