随笔-57  评论-12  文章-0  trackbacks-0

CVS 管理

                           --- 以本次建立的 CVS 系统为例

1.        CVS 简介

CVS Concurrent Versions System)是一个版本控制系统。使用它,你可以记录下你的源文件的历史。例如:修改软件时可能会产生一些bug,而且可能过了很久你才会察觉到它们的存在。有了CVS,你可以很容易地恢复旧版本,并从中看出到底是哪个修改导致了这个bug 。有时这是很有用的。当然了,你可能把所有版本的所有文件都保存了下来。但这会浪费大量的磁盘空间。而CVS用一种聪明的办法来保存一个文件的所有版本——仅仅保存不同版本之间的区别——在一个文件里。如果你是项目开发组的一员,CVS也会帮助你。除非极为小心,成员之间很容易互相覆盖文件。一些编辑器,如GNU Emacs,会保证同一时间内同一文件绝不会被两个人修改。不幸的是,如果有人用了另外的编辑器,这种保护就没用了。CVS用隔离开不同的开发者解决了这个问题。每个开发者在他自己的目录里工作,等每一个开发者都完成了他们自己的工作后,CVS会将它们合并到一起。CVS最初由Dick Grune198612月以shell scripts的形式发布在comp.sources.unix的新闻组第6卷里。虽然当前的CVS中没什么代码来自于这些shell scripts,但许多CVS的冲突解决算法是从它们来的。19894月,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

posted on 2006-04-13 21:54 Martin 阅读(428) 评论(0)  编辑 收藏 引用 所属分类: Project Management
只有注册用户登录后才能发表评论。