CVS
管理
---
以本次建立的
CVS
系统为例
1.
CVS
简介
CVS
(Concurrent Versions
System)是一个版本控制系统。使用它,你可以记录下你的源文件的历史。例如:修改软件时可能会产生一些bug,而且可能过了很久你才会察觉到它们的存在。有了CVS,你可以很容易地恢复旧版本,并从中看出到底是哪个修改导致了这个bug
。有时这是很有用的。当然了,你可能把所有版本的所有文件都保存了下来。但这会浪费大量的磁盘空间。而CVS用一种聪明的办法来保存一个文件的所有版本——仅仅保存不同版本之间的区别——在一个文件里。如果你是项目开发组的一员,CVS也会帮助你。除非极为小心,成员之间很容易互相覆盖文件。一些编辑器,如GNU
Emacs,会保证同一时间内同一文件绝不会被两个人修改。不幸的是,如果有人用了另外的编辑器,这种保护就没用了。CVS用隔离开不同的开发者解决了这个问题。每个开发者在他自己的目录里工作,等每一个开发者都完成了他们自己的工作后,CVS会将它们合并到一起。CVS最初由Dick Grune在1986年12月以shell scripts的形式发布在comp.sources.unix的新闻组第6卷里。虽然当前的CVS中没什么代码来自于这些shell scripts,但许多CVS的冲突解决算法是从它们来的。1989年4月,Brian Berliner设计了CVS并编写了代码,之后Jeff Polk帮助Brian设计了CVS模块发行分支。
CVS
是一个C/S系统,多个开发人员通过一个中心版本控制系统来记录文件版本,从而达到保证文件同步的目的。工作模式如下:
CVS
服务器(文件版本库)
/ | \
(版 本 同 步)
/ |
\
开发者1
开发者2 开发者3
2.
建立
CVS
服务器
1)
管理员
可以专门建立一个
CVS
管理员帐号,供
CVS
管理员使用,以后一切关于
CVS
管理的事情,都通过该帐号来完成。
这里,我们以
192.168.0.148
为
CVS
服务器的主机,在里面开设帐号
casic
为管理员帐号,帐号所在的组设为
casic
,凡是想加入
CVS
系统的用户,都必须加入
casic
组。
我们也会为每个开发人员开设帐号,以方便大家使用
CVS
系统。
2)
建立
CVS
服务器
以帐号
casic
登录
192.168.0.148
。
I.
创建
CVS
源代码仓库
创建目录
/home/casic/cvsroot
作为
CVS
源代码仓库。
II.
设置环境变量
CVSROOT
csh:
在
home
目录下的
.cshrc
文件中加入
setenv CVSROOT
/home/casic/cvsroot
bash:
在
home
目录下的
.bashrc
文件中加入
export
CVSROOT=/home/casic/cvsroot
具体的关于环境变量设置的文件请见附录给出的样例。
III.
初始化
CVS
运行命令:
cvs init
前提是刚才设置的环境变量已经起作用,如果没有起作用,使用命令
cvs
–d /home/casic/cvsroot init
当然,尽快使刚才设置的环境变量起作用,以方便以后的操作。
初始化以后,会在
/home/casic/cvsroot
目录下出现一个子目录
CVSROOT
,里面放置了
CVS
服务器的配置文件。至于如何配置,下面再讲。
至此,
CVS
服务器已经搭建好了,但只能在本地使用,想要远程使用,还得让服务器运行起来才行。
IV.
运行
CVS
服务器
使用
root
帐号登录,在
/etc/xinetd.d/
目录下创建文件
cvspserver
,内容如下:
service cvspserver
{
port
= 2401
socket_type = stream
protocol
= tcp
wait
= no
user
= root
passenv
= PATH
server
= /usr/bin/cvs
server_args = -f
--allow-root=/home/casic/cvsroot pserver
}
注意:
server :
后面为可执行程序
cvs
的全路径
server_args :
后面
/home/casic/cvsroot
为
CVS
源码库的路径
修改该文件权限:
chmod 644
cvspserver
然后重新启动
xinetd
:
/etc/rc.d/init.d/xinetd
restart
然后察看
cvs
服务器是否已经运行:
netstat -lnp|grep
2401
若出现以下信息,则服务器已经运行起来了。
tcp 0 0 0.0.0.0:2401
0.0.0.0:* LISTEN 7866/xinetd
3.
设置
CVS
服务器
以帐号
casic
登录。
这里我们只做极简单的改动,以使在
/home/casic/cvsroot/CVSROOT
目录下产生一个文件
commitlog
记录每次文件的修改。
基本命令操作请参照另一份文档《
CVS
常用命令》。我们可以直接对
CVS
仓库进行操作,但这样做容易出错且不容易恢复,所以不到万不得以,我们对
CVS
库的操作都应该通过
CVS
的命令进行。
这里,我们只修改文件
/home/casic/cvsroot/CVSROOT/loginfo
,首先提取它:
cvs co
CVSROOT/loginfo
进入工作目录
CVSROOT
,对
loginfo
作如下修改:
a)
并在文件的开头添加一行:
# $Id$
b)
将其最后一行的行首的注释符删除
c)
在最下面添加两行:
ALL (echo ""; id; echo %{sVv}; date; cat) > $CVSROOT/CVSROOT/.temp_commitlog
ALL cat | $CVSROOT/CVSROOT/commit_mail %s
注意大小写,保存该文件,然后将其提交。提交以后再打开该文件,你会发现刚刚加入
的
$Id$
已经被
cvs
自动扩展了,记录了文件名、版本号、修改时间和修改人。
在上面
loginfo
的修改中,我们使用了一个命令:
$CVSROOT/CVSROOT/commit_mail
,这是一个用来发信的脚本,需要自己写。
该文件的内容如下:
$CVSROOT/CVSROOT/commit_mail:
#! /bin/csh
# $Id$
set tmplog="$CVSROOT/CVSROOT/.temp_commitlog"
cat $tmplog | grep "$CVSROOT/pub" >> /dev/null
if ( $status ) then
set maillist=`cat
$CVSROOT/CVSROOT/cvs-maillist`
mail -s "CASIC CVS
Mail: $1" $maillist < $tmplog
endif
这个脚本的作用是:在每次有人提交的时候,都会发信给所有使用
CVS
的人。当然,排除了
pub
这个目录的提交,即大家在
pub
里练习
cvs
命令的时候所作的提交不会发信给大家。
同时,在把
commit_mail
拷贝到目录
$CVSROOT/CVSROOT
的时候,也把它提交到
CVS
库里。
同时还要在
$CVSROOT/CVSROOT
里维护一个
CVS
邮件列表:文件
cvs-maillist
,里面是所有可以使用
CVS
的用户的列表。格式很简单:
casic
ynding
dyn
…
这样,简单的设置就结束了。
4.
导入源代码
下面要做的就是要将以前的源代码导入
CVS
库了。
在建立
CVS
服务器以前,我们已经积累了相当多的代码了,现在将这些代码存入
CVS
源码库。
1)
导入命令
cvs import
我们第一次提交源代码时使用命令
cvs import
。将要导入的源代码放到一个目录下,该目录下只有要导入的源代码文件和子目录。
进入该目录,运行命令:
cvs import –m “comment
for project_name” project_name vendor_tag release_tag
其中:
vendor_log
为开发商标志,
release_tag
为版本标志,
release_tag
很有用,下面会讲到怎么用它。
运行结束后,就将该目录下的文件、子目录都导入到
CVS
库了。
cvs import
命令并不改变你运行它的当前目录,也不把当前目录当作
CVS
的工作目录。导入以后,如果你想要修改文件,可以在其他目录里把它提取出来再修改。
2)
导入举例
以本次导入的
EP7312
项目为例。
CVS
主要是用来记录源代码的历史,所以在导入的时候,我们就不能一下子把最新的版本导入进去,得分层次地把源代码导进去,首先把最初始的从网上下载的没有改动的源代码导进去,然后将移植的补丁打进去,再把打过补丁的源代码导进去。
I.
导入初始未改动的源代码
建立目录
/home/casic/temp/ep7312
,将
linux-2.4.13.tar.gz
解压缩到目录
/home/casic/temp/ep7312
下,移除
linux-2.4.13.tar.gz
,这样,在目录
/home/casic/temp/ep7312
下只有一个
linux
子目录。
进入目录
/home/casic/temp/ep7312
,运行命令:
cvs import
–m “Project EP7312” ep7312 EP7312
EP7312_1_0
这里源代码比较多,导入需要比较长的时间,耐性等待到导入结束。
这时可以查看一下目录
/home/casic/cvsroot
,可以发现里面多了一个目录
ep7312
,该目录下就就刚才导入的源代码文件,不过文件名后面都有
“,v”
标记(目录没有),说明这些文件已经入库了。
II.
导入添加了补丁的源代码
回到刚才的目录,将补丁程序拷贝到目录
/home/casic/temp
下,然后进入目录
/home/casic/temp/ep7312/linux
,依次将补丁添加进来:
gunzip < ../../patch-2.4.13-ac4.gz | patch -p1 -t
gunzip < ../../patch-2.4.13-ac4-rmk1.gz | patch -p1 -t
gunzip < ../../patch-2.4.13-ac4-rmk1-codegen1.gz | patch -p1
–t
然后
cp
edb7312 arch/arm/def-configs
这样,补丁就添加完了。
下面开始导入添加完补丁的代码,进入目录
/home/casic/temp/ep7312
,运行命令:
cvs import –m “patches for EP7312” ep7312
EP7312 EP7312_1_1
注意
release_tag
的变化,下面就要利用
release_tag
来将这个分支合并到主干上。
III.
合并分支到主干
进入目录
/home/casic/temp
,建立目录
merge
,进入目录
merge
,运行命令:
cvs co –jEP7312_1_0
–jEP7312_1_1 ep7312 >& log
之所以要将
cvs co –jEP7312_1_0 –jEP7312_1_1 ep7312
的输出到文件
log
,是因为输出的信息下面我们要用到。
执行完成后,打开
log
文件,会看到很多文件前打了
R
标记,表示该文件被移除了,需要使用命令
cvs ci
来确认。下面再介绍一下其它标记的含义(对
cvs update
也一样):
U
提取的文件跟库里一样。
P
跟
U
差不多,不过提取的是一个补丁而不是整个文件。
A
文件被
cvs add
添加了,但没有用
cvs ci
确认。
R
文件被
cvs rm
删除了,但没有用
cvs ci
确认。
M
文件被改动了,可以用
cvs ci
提交了。
C
合并的时候存在冲突。
?
文件在工作目录里,但不在库里。
检查
log
的内容,看看有哪些标记,结果只有
U
和
R
,将标
R
的文件找出来,然后将其一一用命令
cvs ci
提交。
这样,合并就完成了。
3)
其他导入类似
目前已导入的
CVS
源码库的目录结构如下:
/home/casic/cvsroot/at91
CVSROOT
dev-tools
ep7312
misc-docs
pub
at91/bootloader
dev-docs
uClinux-dist
CVSROOT:
配置文件
dev-tools/cross-compiler-2.95.3-ep7312
cross-compiler-at91
ep7312/dev-docs
linux
romdisk
user
misc-docs:
存放其他各种文档。
pub:
供大家练习
cvs
命令
5.
设置客户端
上面的一切操作都是在
CVS
服务器本地机器上进行的。但实际情况下,很多开发人员的机器并不
CVS
服务器,需要在自己的机器上进行操作开发;而且如果大家都跑到
CVS
服务器上来操作开发的话,
CVS
服务器也会不堪重负,无论是系统负荷还是硬盘空间。
下面就讲讲如何远程访问
CVS
服务器。
首先要在
CVS
服务器上开设用户帐号,并加入
casic
组,这里假设开设了帐号
ynding
。给帐号
ynding
设置环境变量
CVSROOT
的值为
/home/casic/cvsroot
这样,在
CVS
本地机上帐号
ynding
就可以访问
CVS
库了。
在远程机器上也建立帐号
ynding
(当然也可以是其他帐号,开发时为了不混淆,取相同的帐号名为好),也给该
ynding
设置环境变量
CVSROOT
:
setenv CVSROOT
:pserver:ynding@192.168.0.148:/home/casic/cvsroot
或者
export
CVSROOT=:pserver:ynding@192.168.0.148:/home/casic/cvsroot
设置完以后,并让环境变量起作用以后,就可以使用
CVS
服务器了。
先要登录到
CVS
服务器上:
cvs login
会提示输入密码,输入帐号
ynding
在
CVS
服务器上的密码,就能登录成功了。以后的对
CVS
服务器的操作跟在
CVS
服务器本地操作一样。完事以后,记得使用命令
cvs logout
退出
CVS
服务器。
6.
打标签,建分支
1)
确认项目里程碑
当项目开发的一定时间后,可以给所有文件指定一个阶段里程碑版本号,方便以后按照这个里程碑版本号导出项目,同时,也是多个项目分支开发的基础。
指定里程碑版本号就是给当前最新的版本打上一个标签,运行命令:
cvs tag
release_1_0_project_name
这样,就给项目打上了一个标签,表示这就是
1.0
版本了,以后要提取这个
1.0
版本只要运行命令:
cvs co –r
release_1_0_project_name project_name
就可以把
1.0
版本提取出来了。
这样,就可以进入
2.x
的开发了。运行命令:
cvs ci –r
2
就可以使所有的文件版本号变为
2.0
,从而进入
2.x
的开发了。
注意:
项目里文件的版本号跟项目的发布版本没有直接关系,但所有文件的版本号跟发布版本一直有助于维护。所以,将所有文件的版本号都改为
2.x
不是必需的。
还有,在以标签提取出来的项目里,对文件的改动不能提交到
CVS
库里,会提示错误信息:这不是一个分支。
2)
建立分支
在开发
2.x
的时候,如果发现
1.0
版本有问题需要修复。这时一般并不在
2.x
里修改,一来是因为
2.x
还处于开发阶段,还不稳定,二来是在
2.x
里可能已经添加了很多新的功能,并不想让
1.0
的用户白白得到。
这时,我们可以为
1.0
建立一个分支,在这个分支了修改
bug
。这时,上面介绍的打的标签就起了作用,运行命令:
cvs rtag –b –r release_1_0_project_name
release_1_0_project_name_patch project_name
这样就建立了一以
1.0
版本为基础的分支。在改分支里修改完
bug
以后,将修改提交到该分支里。
这时也可以在该分支里打一个标签:
cvs tag
release_1_0_project_name_bug_fixed_1
当然了,也可以在当前工作目录下打标签,不管你提取的项目是以前的版本、补丁还是分支等等,直接在当前目录下运行命令:
cvs tag –b
release_1_0_project_name_branch
这样就建立起一个分支了。提取分支和按发布版本提取的方法一样:
cvs co –r
release_1_0_project_name_branch project_name
7.
分支操作
CVS
允许你修改代码到不同的开发线上,这就是分支(
branch
)
。当你改变一个分支中的文件时,这些更改不会出现在主开发主干
(main trunk)
和其它分支中。
1)
访问分支
有两种方式可以进入分支:重新提取出一份或从当前工作目录切换过去。
A.
重新提取一份
运行命令:
cvs co –r release_1_0_project_name project_name
-r
后面跟的是标签名。
B.
切换
在当前工作目录下运行命令:
cvs update –r
release_1_0_project_name project_name
这对现有拷贝是主干代码或是其他分支都是有效的,上面的命令把它转移到命名的分支。跟
cvs update
相似,
cvs update –r
将合并你所做的任何改动,请注意有没有冲突出现。
一旦你的工作拷贝转移到一个特定的分支,它将一直保持在这个分支内,除非你做了其他操作。这意味着从这个工作拷贝的提交将添加到这个分支的新版本中,而不影响主干和其他分支。
想看一个工作拷贝是基于哪一个分支的,可以使用命令
cvs status
命令,在输出的结果中查看“
Sticky Tag
”项,如果为
none
,则处于主干中,如果不是,则处于该分支中。
例如,在
pub
目录下运行命令:
cvs st
test.txt
结果:
File: test.txt Status: Locally Modified
Working revision: 1.4
Fri Aug 1 01:18:12
2003
Repository revision:
1.4
/home/casic/cvsroot/pub/test.txt,v
Sticky Tag: release_08_03_pub (revision:
1.4)
Sticky Date: (none)
Sticky Options: (none)
则处于分支
releae_08_03_pub
中。
使用
-v
选项还可以看出该文件处于哪些分之中,运行命令:
cvs st –v
test.txt
结果:
File: test.txt Status: Locally Modified
Working revision: 1.4
Fri Aug 1 01:18:12
2003
Repository revision:
1.4
/home/casic/cvsroot/pub/test.txt,v
Sticky Tag: release_08_03_pub (revision:
1.4)
Sticky Date: (none)
Sticky Options: (none)
Existing Tags:
release_08_03_pub_patch_2
(branch: 1.4.4)
release_08_03_pub_patches
(branch: 1.4.2)
release_08_03_pub (revision: 1.4)
注意结果:
如果在标签名后面的括号里的注释为
revision
,则是单纯的一个标签;如果是
branch
,则是一个分支。
2)
合并整个分支
你可以合并另一个分支上的修改到你的工作目录,只要在
update
命令中加
-j branchname
的标识。使用
-j branchname
将合并这个派生分支点与原版本的最新版之间的变更
(
到你的工作目录
)
。
-j
的意思是“
join
”。
例如,我们要将分支
release_1_0_project_name_branch
合并到主干中:
cvs co
project_name
进入工作目录:
cvs update
–jrelease_1_0_project_name_branch
注意有没有冲突发生,有的话解决以后再提交:
cvs ci –m “include
release_1_0_project_name_branch”
也可以在提取的时候加上
-j
选项:
cvs co
–jrelease_1_1_project_name_branch project_name
cvs ci –m “include
release_1_0_project_name_branch”
效果和上面讲到的相同,当然也要注意处理冲突的问题。
3)
从一个分支多次合并
经过上面的合并以后,分支
release_1_0_project_name_branch
继续开发,经过一段时间后,又要合并到主干中。如果仍使用命令
cvs update –j release_1_0_project_name_branch
则将试图合并你已经合并过的东西,这可能导致一些不希望发生的事情。
因此,必须表达清楚你希望合并未被合并的内容,这样需要两个“
-j
”选项。
CVS
合并从第一个“
-j
”的版本到第二个“
-j
”的版本的变化。
例如:
cvs update
–j1.2.2.2 -j release_1_0_project_name_branch
如果出现的问题是你必需手工指定版本号
1.2.2.2
,一个更好的方法是使用:
cvs update –j release_1_0_project_name_branch:yesterday
-j release_1_0_project_name_branch
(
应该是在同一行
)
然而,最好的方法是在每一次合并以后给分支加一个标签,然后可以使用标签进行以后的合并:
cvs update -j
release_1_0_project_name_branch_02
-j
release_1_0_project_name_branch (
应该在同一行
)
4)
合并两个任意版本之间的不同
使用两个
”-j revision”
选项,
cvs update
和
cvs co
命令能合并任意两个不同的版本的差异到你目录:
cvs update –j1.5 –j1.3
test.txt
这个命令将把
1.5
版本恢复到
1.3
版本,所以一定要注意版本的顺序。
如果你在操作多个文件时使用这个选择项,你必须了解在不同的文件之间,版本的数字可能是完全不同的。你必须使用标笺(
tag
)而不是使用版本号来完成多个文件的操作。
使用两个
-j
操作也能恢复增加或删除的文件。例如,假定你有一个叫
file1
的文件在在于
1.1
版本中,然后你删除了它(因此增加了一个
dead
版本
1.2
)。现在你又打算恢复它,并用它原先的内容。下面是如何操作的例子:
cvs update -j 1.2 -j 1.1
file1
然后再提交一下:
cvs ci –m “restore
file1” file1
5)
合并能添加和删除文件
如果你在合并时做的改变涉及到添加或删除一些文件,
cvs update –j
和
cvs co -j
将反映这些变化。
示例请参考本文档开头导入源代码时合并两个分支的情形。
6)
合并与关键字
如果你要合并的文件中包含关键字,你会得到一大堆冲突,这是因为关键字与合并的版本关联。因此,你需要指定
-kk
在合并的命令行里面。这样只会替换关键字名而不包括里面的值。这个选项认为那些都是相同的,使合并避免产生假的冲突。
8.
添加
CVS
用户
1)
添加系统用户
在
CVS
服务器上添加一个系统用户,并加入
casic
组。
然后给该用户设置环境变量
CVSROOT
。
2)
修改
CVS
邮件列表
直接修改文件
/home/casic/cvsroot/cvs-maillist
,在里面添加新加入的用户名。
3)
修改
Sendmail
别名列表
修改文件
/etc/mail/aliases
,在里面添加新加入用户的邮箱别名。
9.
附录
1)
bash
刚刚建立帐号时,
home
目录下可能还没有环境的配置文件,如果想用
bash
,则要在自己的
home
目录下建立两个文件:
.bash_profile
和
.bashrc
以帐号
dyn
为例,在
/home/dyn
目录下建立文件
.bash_profile
和
.bashrc
,文件的内容如下:
.bash_profile:
#
.bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
PATH=$PATH:$HOME/bin
export PATH
unset USERNAME
.bashrc:
#
.bashrc
# User specific aliases and functions
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
CVSROOT=:pserver:dyn@192.168.0.148:/home/casic/cvsroot; export
CVSROOT
2)
csh
刚刚建立帐号时,
home
目录下可能还没有环境的配置文件,如果想用
csh
,则要在自己的
home
目录下建立一个文件:
.cshrc
以帐号
ynding
为例,在
/home/ynding
目录下建立文件
.cshrc
,文件的内容如下:
.chsrc:
#!/usr/bin/csh
###set path###
set path=( . \
/usr/bin \
/usr/local/arm/2.95.3/bin
\
/usr/local/arm-elf/bin
\
/usr/local/bin \
/usr/bin \
/bin \
/usr/sbin \
/usr/local/sbin
\
/usr/X11R6/bin \
/usr/X11R6/LessTif/Motif1.2/bin \
)
###ENVIRONMENTAL SETTING
setenv CVSROOT /home/casic/cvsroot
###END
umask 027