配置CVS服务器、客户端和使用CVS开发项目的终极完美教程(下篇)
============================================ 时间:2007-04-12 作者:飘扬 首发:飘扬 博客 网址:http://piaoyang.org Q Q: 47720194 邮箱:piaoyang168#163.com 版权声明:可以任意转载,转载时请务必标明原始出处和作者信息 ============================================
注意:本文接《配置CVS服务器、客户端和使用CVS开发项目的终极完美教程(上篇)》,上篇地址在:http://www.piaoyang.org/article.asp?id=221 。如有不明之处,请联系飘扬 ,你可以到我的博客 (飘扬 博客 )http://www.piaoyang.org 给我留言或QQ47720194,我会尽力解答你的问题。
下 篇
四、如何用CVS来开发项目 经过上面的check out之后,现在已经有了源码,我们可以继续编译和安装它们、检查它们,或者对它们执行任何操作了。现在,来看一下作为一个开发人员如何与 CVS 交互。 作为一名开发人员,您需要修改 CVS 上的文件。要修改文件,只需要对资源库的本地副本进行适当的更改。在您明确地告诉 cvs "提交"更改之前,您对源码做的更改不会应用到远程资源库。测试过所有修改以确保它们可以正常运作之后,就可以准备将这些更改后的文件运用到资源库中,遵循下面的两个步骤。1、更新源码 在提交之前需要将已检(checkout)出的源目录与 CVS 上的当前版本保持同步。为了做到这一点,您无需再次登录到 pserver;cvs 会将您的认证信息缓存到那些 "CVS"帐户目录中。首先,进入主检出目录(在这里是 "VPN"),然后输入: # cvs update -dP 如果有任何新文件或者其他人对文件有过更改,cvs就会在更新每一行的时候输出 "U [path]" 行。另外,如果本地编译了源码,您有可能会看到许多 "? [path]" 行;cvs 指出这些目标文件不来自于远程资源库。 (另外,请注意我们用于 "cvs update" 的两个命令行选项。"-d" 告诉 cvs 创建可能已添加到资源库的新目录(缺省情况下,这不会发生),"-P" 告诉 cvs 从本地已检出的源码副本中除去所有空目录。"-P" 是个不错的选择,因为 cvs 倾向于收集许多随时间产生的空(曾经使用过,但现在已经放弃)目录树。)2、提交修改后的文件 我们假设在输入 "cvs update -dP" 后没有冲突 -- 通常都没有冲突。由于没有冲突,本地源码是最新的。可以在主源码目录中输入以下命令来提交对资源库的更改: # cvs commit 3、查看日志 要查看某个特定文件完整的历史以及提交时开发人员(包括您)所加的注解是很容易的。要查看这些信息,输入: # cvs log myfile.c "cvs log" 命令是递归的,所以如果您想查看整个目录树的完整日志,只需要进入该目录,输入: # cvs log | less 4、将文件添加到资源库 要将源文件添加到 CVS 很容易。首先,用您喜爱的文本编辑器创建该文件。然后,输入以下命令: # cvs add myfile.c cvs server: use cvs commit to add this file permanently 这将告诉 cvs 在您下次执行 "cvs commit" 时,将该文件添加到资源库。在那之前,其它开发人员看不它。 5、将目录添加到资源库 将目录添加到 CVS 的过程类似于mkdir foo命令:# cvs add foo Directory /home/cvsroot/mycode/foo added to the repository 与添加文件不同,当您添加目录时,它会立即出现在资源库中;不需要 cvs commit。将本地目录添加到 cvs 后,您会注意到在远程cvs服务器的对应目录中创建了一个 "CVS" 目录,它作为包含 cvs 帐户数据的容器。因而,您只要看一下其中是否有 "CVS" 目录,就可以很容易地知道某个目录是否已添加到远程cvs服务器的 cvs中了 在将文件或目录添加到资源库之前,您必须确保它的父目录已经添加到 CVS。否则,您会看到类似于下面的错误: # cvs add myfile.c cvs add: cannot open CVS/Entries for reading: No such file or directory cvs [add aborted]: no repository 6、删除文件或目录 1) 删除文件是分两个过程。首先,从源码的本地副本删除该文件,然后执行 "cvs remove" 命令从资源库中删除该文件:# rm myoldfile.c # cvs remove myoldfile.c 在您下次提交时,该文件计划将从资源库中除去。一旦提交,该文件会从资源库当前的版本中正式删除。然而, cvs 不会将该文件抛弃,而是仍然完整地保留该文件的内容及其历史,以备您以后需要它。这只是 cvs 保护您有价值的源代码的众多方法之一。 2) 如果您想除去整个目录,首先用"cvs remove" 删除目录中的所有文件: # rm *.c # cvs remove 然后执行提交: # cvs commit 7、标签的定义和使用 源码仓库中各个文件的修订号是独立增加的,相互之间没有任何关联关系,和软件的发布号也没有任何关系,例如一个项目中的各个文件的修订号可能是这样的: ci.c 5.21 co.c 5.9 ident.c 5.3 rcs.c 5.12 rcsbase.h 5.11 rcsdiff.c 5.10 rcsutil.c 5.10 为了便于标记,可以使用标签来为某个特定版本的特定文件设定一个标记以方便访问,可以使用cvs tag和cvs rtag来定义标签,其中cvs tag用来为仓库中当前工作文件(或文件集合)指定一个符号标记;cvs rtag用来显式地为源码仓库的特定修订号的文件定义一个标记。例如下面的例子就是给文件backend.c的当前修订号定义一个标签,然后察看该文件的状态: $ cvs tag rel-0-4 backend.c T backend.c $ cvs status -v backend.c 但是在实际应用中很少会为特定文件定义一个标签,而往往是在开发过程中的特定阶段为特定项目的所有文件定义一个标签,以方便发布或者定义分支,例如: $ cvs tag rel-1-0 . cvs tag: Tagging . T Makefile T backend.c T driver.c T frontend.c T parser.c 定义标签以后,可以在随后的任何时候访问对应该标签的项目文件,例如下面这个命令就用来实现检出对应于标签rel-1-0的所有文件(可能是软件的1.0发布): $ cvs checkout -r rel-1-0 tc 当然也可以删除标签,例如: cvs rtag(/tag) -d rel-0-4 tc 删除模块tc的rel-0-4标记 8、备份源码仓库 首先断开所有的cvs连接,然后使用cp命令备份即可 9、分支的定义和使用 通过CVS可以实现将对源码的修改提交给一个独立的开发线,被称为分支(branch)。当对分支的文件进行修改时,这些修改不会对主分支和其他分支产生影响。 随后可以将将一个分支的修改合并(merging)到其他分支。合并是通过cvs update -j来合并修改到当前工作目录(本地),然后就可以提交修改来影响其他分支了。 9.1 分支的重要性 让我们假设这种情况,项目tc的1.0版本已经搞定,你将继续开发tc项目,计划在几个月内发布1.1版本,但是客户抱怨软件中有致命的错误。因此你检出1.0版本(这里就是需要使用标记的原因)并发现了这个bug的原因。然而当前的源码处于1.0和1.1版本之间因此代码处于混乱状态,而且在一个月内不大可能出现稳定版本,因此不大可能根据当前的版本得到一个修复错误的版本来发布。 对于这种情况这就需要创建一个包含错误修改的1.0分支发布,而不需要影响当前的开发,在合适的时候可以将修正合并的主发布中去。(如还有不明之处,请联系飘扬 ,你可以到我的博客 (飘扬 博客 )http://www.piaoyang.org 给我留言或QQ47720194,我会尽力解答你的问题。) 9.2 创建分支 创建分支首先为拟修改的某些文件创建一个标签(tag),标签是赋于一个文件或一组文件的符号.在源代码的生命周期里,组成一组模块的文件被赋于相同的标签。 创建标签:在工作目录里执行cvs tag 。 例: 为src创建标签: cvs checkout src(/update亦可,用来更新本地的源代码) cvs tag release-1-0(为当前最新源码加一个标签) 标签创建后, 就可以为其创建一个分支: cvs rtag -b -r release-1-0 release-1-0-path print -b :创建分支 -r release-1-0:-r参数用来标记那些包含指定的标签的文件 releas-1-0-patch:分支 print: 模块名 可以使用tag -b来创建分支,例如在工作目录中: $ cvs tag -b rel-1-0-patches 将会基于当前工作的分支分离一个分支,并将该分支命名为`rel-1-0-patches。应该理解分支是在cvs的源码仓库中创建的,而不是当前工作目录,基于当前修正创建分支不会将当前工作拷贝自动转换为新的分支,需要手工来实现的。也可以通过使用rtag命令实现不涉及工作目录的分支: $ cvs rtag -b -r rel-1-0 rel-1-0-patches tc `-r rel-1-0指示创建的新分支应该以标记`rel-1-0指定的修订为基础,而不是基于当前的工作主分支。这主要是用来从旧版本中创建一个分支(例如上面的例子)。 rtag -b指示创建分支(而不是仅仅创建标记)。应该注意的是`rel-1-0包含的各个文件的修订号可能是不一样的。 所以该命令的效果是为工程tc创建一个新分支-名字为`rel-1-0-patches,以标记`rel-1-0为基础。 9.3 访问分支 可以以两种方式访问分支:从源码仓库中检出分支代码,或者将当前的工作拷贝切换为分支。 从源码仓库中创检出新分支可以使用命令checkout -r release-tag命令: $ cvs checkout -r rel-1-0-patches tc 将当前分支切换到分支命令: $ cvs update -r rel-1-0-patches tc 或者 $ cd tc $ cvs update -r rel-1-0-patches 随后的提交等影响源码仓库的操作都仅仅对分支起作用,而不会影响主分支和其他分支。可以使用status命令来察看当前工作拷贝属于哪个分支。输出中察看sticky tag信息,这就是cvs显示当前工作拷贝是在哪个分支上: $ cvs status -v driver.c backend.c 9.4 合并分支 分支开发一段时间以后往往需要将修订合并到主分支中来,可以通过`-j branchname参数实现合并分支。使用该参数将分支和其父分支合并。 分支1.2.2被定义为标记R1fix。下面的例子假设模块mod仅仅包含一个文件m.c: $ cvs checkout mod #获取最新的1.4版本 $ cvs update -j R1fix m.c # 合并分支的所有更新到主分支 # 也就是1.2到1.2.2.2的修改合并到当前工作拷贝 $ cvs commit -m "Included R1fix" # 创建修订版本1.5. 合并时可能出现冲突情况,如果发生冲突,应该在提交以前手工处理冲突。 五、CVS常见问题分析 1、cvs用户为什么不能登录? # export CVSROOT=:pserver:cvsroot@192.168.10.11:2401/cvsroot # cvs login Logging in to :pserver:cvsroot@192.168.10.11:2401/cvsroot CVS password: no such user cvsroot in CVSROOT/passwd 解决办法:查看是/cvsroot/CVSROOT/passwd文件,看里面有没有包含该用户2、 我怎么知道别人改了哪些内容? 假如我们现在修改了,文件config.h , 现在想看看除了我之外还有什么人修改了这个文件,到底修改了什么东西? 怎么办?此时,输入以下命令即可查看:cvs diff config.h3、什么是冲突?怎么解决? 当两个或两个以上的用户对同一个文件的相同部分进行修改时,往往会引起冲突,下面用一个例子说明。 假设用户A与用户B都下载了文件example.h,内容如下:
void main(int argv,char *argc)
{
printf("I am Cather/n");
}
然后用户A把文件修改成如下,并提交到CVS服务器(一般将正常提交):
void main(int argv,char *argc)
{
printf("I am Cather/n");
printf("I am Pat/n");
}
接着用户B又把文件修改成如下:
void main(int argv,char *argc)
{
printf("I am Cather/n");
printf("I love you Cather/n");
}
如果用户B这时选择“Commit Selection”准备把修改结果提交到CVS服务器,此时将显示如下的错误提示:
cvs -z9 commit -m "update in 11:20" example.h (in directory C:/my cvs/STW/src/)
cvs server: Up-to-date check failed for `example.h'
cvs [server aborted]: correct above errors first!
***** CVS exited normally with code 1 *****
表明用户B的修改与其它用户的修改冲突,这时要先选择“Update Selection”,将显示如下提示:
cvs -z9 update example.h (in directory C:/my cvs/STW/src/)
RCS file: /home/cvsroot/STW/src/example.h,v
retrieving revision 1.9
retrieving revision 1.10
Merging differences between 1.9 and 1.10 into example.h
rcsmerge: warning: conflicts during merge
cvs server: conflicts found in example.h
C example.h
***** CVS exited normally with code 0 *****
example.h前面的C表示与其它用户的修改有冲突,并且文件的图标会加显示一个“C”,双击example.h将显示example.h的内容,如下:
void main(int argv,char *argc)
{
printf("I am Yanxi/n");
printf("I am Cather/n");
<<<<<<< example.h
printf("I love you Yanxi,too!/n"); //这部分为你的修改
=======
printf("I love you Cather!/n"); //这部分为其它用户的修改
>>>>>>> 1.10
}
这时你应该与用户A进行协商以决定最终要怎样修改。比如,可以修改成:
void main(int argv,char *argc)
{
printf("I am Yanxi/n");
printf("I am Cather/n");
printf("I love you Yanxi,too!/n"); //这部分为你的修改
printf("I love you Cather!/n"); //这部分为其它用户的修改
}
然后选择“Commit Selection”进行提交,将显示如下的提示信息:
cvs -z9 commit -m "update in 11:20" example.h (in directory C:/my cvs/STW/src/)
Checking in example.h;
/home/cvsroot/STW/src/example.h,v <-- example.h
new revision: 1.11; previous revision: 1.10
done
***** CVS exited normally with code 0 *****
表明用户A与用户的修改已经合并成功。这样,向CVS服务器提交文件所会遇到的问题也基本上就是这些,用户要根据所遇到的实际问题进行修改。
六、结束语
如果你是通篇读到这里,那么到这里,你基本上已经能为自己或公司配置一个实用的CVS服务器与CVS客户端了,无论是在Linux下,还是在Windows下。而且,你也基本掌握了怎样配合使用CVS进行系统开发或其它文档的版本管理与控制。谢谢你的耐心与信任,同时也对你的小有成就小小的祝贺一下。在最后
飘扬 要申明的是,本教程里的大部分内容是由网上多篇介绍CVS的文章提取而来,
飘扬 只是用自己的逻辑和语言将这些内容串联起来。在此,
飘扬 对这些文章的原作者深表感谢。如还有不明之处,请联系
飘扬 ,你可以到我的
博客 (
飘扬博客 )
http://www.piaoyang.org 给我留言或QQ47720194,我会尽力解答你的问题。以此拙作,希望能给大家带去些微的帮助。同时也欢迎彼此多多交流!