CVS

Table of Contents

1 CVS简介

CVS is a version control system, an important component of Source Configuration Management (SCM). Using it, you can record the history of sources files, and documents. It fills a similar role to the free software RCS, PRCS, and Aegis packages.

官方网站:http://savannah.nongnu.org/projects/cvs
最好的参考书籍:Version Management with CVS(The Cederqvist)

2 CVS基本命令

2.1 命令基本格式

cvs [cvs-options] command [command-options-and-arguments]

The commands you "need to know" include:

  • init
  • import
  • checkout
  • add
  • commit
  • update
  • diff
  • tag
  • remove

2.2 查看命令帮助

cvs --help
cvs -H            #和cvs -help相同
cvs --help-options     #查看所有支持的选项。
cvs --help-commands    #查看所有支持的命令。
cvs -H checkout        #查看某个子命令(如cvs checkout)的帮助。

2.3 连接服务器(CVSROOT设置)

首先介绍一下cvs文件仓库的概念。在cvs服务器上建立文件仓库,称为Repository。cvs有两种访问仓库的方式,pserver和ext(stand for external)。
在pserver方式下我们需要首先使用cvs login指令登录,并在完成cvs操作后使用cvs logout指令注销登录(cvs自己处理连接的细节以及管理安全);而在ext方式下可使用由CVS_RSH环境变量设定的第三方远程登录工具(如ssh)进行连接。psverver方式的设置相对比较简单,这里只介绍pserver方式。

cvs使用类似于URL的字符串来标记服务器,称为CVSROOT。形式如下:

[:method:][[user][:password]@]hostname[:[port]]/path/to/repository

在使用cvs前,必须先设置CVSROOT变量。

export CVSROOT=":pserver:user@xxx.xxx.com:2401/home/export"

现在可以登录,进行其它操作了:

cvs login  #会提示输入密码
……
cvs logout  #不再需要操作了,可退出,结束cvs交互过程

注:如果不设定CVSROOT,每条命令中用-d选项指定CVSROOT也可以进行操作,只是比较麻烦,如:

cvs -d ":pserver:user@xxx.xxx.com:2401/home/export" login
cvs -d ":pserver:user@xxx.xxx.com:2401/home/export" checkout something

2.4 列出仓库中的所有modules

cvs ls

如果服务器提示不支持ls(CVSNT服务器才支持ls),则可以使用下面命令

cvs checkout -c

其中-c的含义是"cat" the module database.

2.5 签出仓库中的指定目录(checkout)

cvs checkout后跟要签出的目录名即可:

cvs checkout test/dir1
cvs co  test/dir1         #同上
cvs get  test/dir1        #同上

2.6 查看签出文件的修改记录(log)

cvs log filename
cvs log dirname    #将查看指定目录下所有文件的修改记录

2.7 查看签出文件的状态(status)

cvs status filename
cvs status dirname

如果本地文件是仓库中最新的,会显示Status: Up-to-date
如果在本地修改过文件,则会显示Status: Locally Modified
如果本地文件不是仓库中最新的,则会显示Status: Needs Patch
如果本地文件在自动merge时出现了冲突,则会显示Status: File had conflicts on merge

2.8 比较本地文件和仓库中文件(diff)

cvs diff filename

注意:上面命令并不是拿本地的文件和服务器上最新的做比较。如果你前天更新了filename(版本号为1.2),但昨天其它同事更改并提交了(版本号为1.3),但你昨天并没有更新本地文件,今天你修改后运行cvs diff filename,实际是拿你本地的文件和服务器上1.2的版本做比较。

比较指定版本号的文件差异

cvs diff -r1.1 -r1.2 filename

比较版本1.1和1.2的不同。

2.9 更新本地文件(update)

cvs update files   #更新指定文件
cvs update         #更新当前文件夹,但默认不会创建任何新目录
cvs update -d      #更新当前文件夹,服务器上有新目录也会更新下载过来

更新命令执行时,输出信息中每个文件前面有个字母标记,其含义如下:
U 完整的文件从服务器上更新到了本地(当文件为二进制文件或者本地不存在的文件)。
P 文件的部分内容从服务器更新到了本地(文件为文本文件时,cvs将只传送本地文件和位于服务器上的文件相比较不同的部分,以减少带宽的使用)。
A 这个文件目前只存在于本地目录中,你虽然使用cvs add命令向cvs服务器添加了文件,但还没有使用cvs commit命令确认添加操作。
R 这个文件你已经不存在于本地了,而且你也使用了cvs remove命令从cvs服务器上删除了这个文件,但还没有使用cvs commit命令确认删除操作。
M 这里有两种含义:一是你的本地文件修改了,但还没有用cvs commit命令提交到cvs服务器上;二是你的本地文件修改了,cvs服务器上的文件被其他人修改了,现在你们的修改已经成功的合并了。
C 你的本地文件修改了,cvs服务器上的文件被其他人修改了,但现在你们的修改发生的冲突,无法自动合并。
? 这个文件只存在于本地。

2.10 放弃本地修改(update -C)

cvs update -C file

2.11 添加本地文件到仓库(add)

cvs add file1.txt

注:cvs add命令向cvs服务器添加文件,但还要使用cvs commit命令确认添加操作。

2.11.1 添加目录及其子目录

cvs add命令既可添加文件又可添加目录,用cvs add添加一个目录时,只会增加目录本身,该目录下的文件及子目录不会增加。

用下面方法可以递归地添加目录:

find . -type d \! -name CVS -exec cvs add '{}' \;
find . \( -type d -name CVS -prune \) -o \( -type f -exec cvs add '{}' \; \)

参考:http://stackoverflow.com/questions/5071/how-to-add-cvs-directories-recursively

注:除上面方法外,还可以使用cvs import命令导入目录到cvs服务器。

2.12 提交修改(commit)

cvs commit files
cvs ci files         #同上

上面命令执行时会进入编辑器,让你输入注释。
提交更改时同时指定注释:

cvs commit -m "add your comments" files

2.13 增加和删除标记(tag/rtag)

cvs tag  xyz  .      #给当前目录下所有文件增加标记xyz
cvs tag -d xyz .     #去掉当前目录下所有文件上的标记xyz

2.13.1 tag和rtag的不同

Difference between tag and rtag is that tag command adds a tag to checked out version of files whereas rtag uses the most recent versions in the repository. These can have different effects, e.g. if someone checks in new stuff while you are working.

参考:https://netbeans.org/community/sources/branches.html

2.14 创建分支

用tag或rtag的'-b'选项可以创建分支。

如,从当前主干上创建一个名为brach1的分支:

cvs tag -b branch1 .

2.15 查看某文件已经存在的分支(status -v)

cvs status -v filename1 可以查看文件 filename1 上已经存在的分支。如:

$ cvs status -v driver.c
===================================================================
File: driver.c          Status: Up-to-date

    Version:            1.7     Sat Dec  5 18:25:54 1992
    RCS Version:        1.7     /usr/local/cvsroot/yoyodyne/tc/driver.c,v
    Sticky Tag:         release-1-0-patches (branch: 1.7.2)
    Sticky Date:        (none)
    Sticky Options:     (none)

    Existing Tags:
        release-1-0-patches             (branch: 1.7.2)
        release-1-0                     (revision: 1.7)

3 CVS技巧

3.1 检查哪些文件需要更新?哪里文件在本地被修改?

用cvs status命令可以达到这个目的,但它的输出太多,不太直观。

A 'dummy' update will give you this information.

cvs -qn update

上面命令将把当前目录及子目录中所有文件的状态直观表示出来。选项的含义为:
-q Cause CVS to be somewhat quiet.
-n Do not execute anything that will change the disk.

3.2 让cvs忽略某些文件

cvs默认会忽略一些常见临时文件。
如果要显示指定cvs忽略特定文件,可以在~/.cvsignore中指定。

参考:
Version Management with CVS(The Cederqvist) C.5 Ignoring files via cvsignore

3.3 merge实例:取消上一次的修改

假设文件filename1的上一次修改导致了其他问题的产生,需要取消上一次修改。怎么做呢?当然我们可以手动把所做的改动一行行回退,再提交一个新版本。这种方法漏掉改动,极易出错。利用cvs的merge功能可以完成这个任务。

假设文件filename1最新的版本号为1.6(这是一个有问题的版本),我们需要恢复filename1为上一个版本(如1.5)。操作步骤如下:

cvs update -j 1.6 -j 1.5 filename1
cvs commit filename1

说明1: -j 1.6 -j 1.5 这个顺序不能颠倒。
说明2:如果要回退两个版本,可以运行 cvs update -j 1.6 -j 1.4 filename1; cvs commit filename1

3.4 merge实例:把分支上改动移植到主干

假设cvs分支branch1上对文件filename1进行了修改,用下面操作可以把这个修改移植到主干代码上:

$ cvs checkout filename1
$ cvs update -j branch1 filename1
$ cvs commit filename1

注:如果有冲突,会提示"conflicts during merge"

3.5 merge实例:把主干改动反向移植到分支

假设cvs主干上文件filename1进行了修改,如何把这个修改也移植到分支branch1上呢?

首先,找到文件filename1在主干上的最新版本号,假设为1.5。
然后,用下面操作即可:

$ cvs checkout -r branch1 filename1
$ cvs update -j 1.5 filename1
$ cvs commit filename1

注:如果有冲突,会提示"conflicts during merge"

3.6 获取指定版本的文件

If you wish to get a particular version of the file (say you've added a bad change and want to scrap it), you can do this:

cvs update -r1.78 textctrl.cpp  #获取版本为1.78的textctrl.cpp

This sets the 'sticky' tag, which means that all your subsequent updates will retrieve that revision until you update with

cvs update -A textctrl.cpp  #更新textctrl.cpp到最新版本

参考:
http://www.linuxquestions.org/questions/linux-newbie-8/use-cvs-to-get-a-particular-version-328535/

3.7 查看某用户的所有代码改动

要查看某用户(如jack)的所有改动,可以用下面的命令:

$ cvs -q log -N -S -wjack .

说明:
-N Do not list tags.
-S Do not print name/header if no revisions selected.
-w和用户名jack之间不能有空格,因为这里-w是log命令的子选项,而cvs命令本身也有-w选项。

参考:http://stackoverflow.com/questions/354599/how-to-find-all-commitsfilescomments-by-a-person-in-cvs

3.8 查看某一天的提交的代码改动

要想查看2013-11-20这一天提交的代码改动,可用下面命令:

$ cvs log -N -S -d '2013-11-20<2013-11-21' .

要想查看2013-11-20到目录为止的代码改动,可用下面命令:

$ cvs log -N -S -d '2013-11-20<' .

3.9 导入目录到cvs仓库(import)

import导入当前目录到cvs repository。

假设当前文件夹为dir1,想导入到test/project中。
第一步,进入到该目录,清除不需要提交的文件。
第二步,执行 cvs import 命令。如:

cvs import -m "test log message" test/project/dir1 vendorTag releaseTag

vendorTag和releaseTag是标记名称,可以随便写。
第三步,用 cvs co 获取新导入的项目。如:

cvs co test/project/dir1

3.10 删除整个文件夹

删除文件夹,有两个任务:删除文件里所有文件,删除空文件夹。

第一个任务就是删除文件。
第二个任务无法完成,除非去cvs服务器上直接删除。

参考:
http://stackoverflow.com/questions/204991/remove-empty-directory-from-cvs
http://www.network-theory.co.uk/docs/cvsmanual/Removingdirectories.html

3.11 把分支代码merge到主干

如:release1是分支名,使用update的-j参数(意为join)可进行自动merge。

cvs update -j release1

如果没有冲突,就可以提交cvs commit了。

参考:
https://kb.wisc.edu/middleware/page.php?id=4087

4 配置cvs服务器

服务器安装启动步骤:
第一步,安装xinetd,Ubuntu中可这样安装apt-get install xinetd
第二步,在/etc/xinetd.d目录下创建一个配置文件,名为cvspserver,文件内容为:
service cvspserver{ socket_type = stream protocol = tcp wait = no user = root server = /usr/bin/cvs server_args = -f –allow-root=/home/cvsroot pserver disable = no}
第三步,重启xinetd服务。
第四步,用netstat测试是否正常启动。netstat -lnp |grep 2401如果发现类似下面内容,则已启动CVS服务。tcp 0 0 0.0.0.0:2401 0.0.0.0:* LISTEN xxxxxx/xinetd

5 其它CVS技巧

5.1 attic目录

attic目录由cvs自动维护,用户无需关心。

File is stored in the attic if and only if the head revision on the trunk has state dead. A dead state means that file has been removed, or never added, for that revision.
如:删除的文件会放入到attic目录中,在某个分支中加入的文件也在attic目录中。

5.2 -kb选项

如果要增加的文件为二进制文件,则应该加上-kb选项。
cvs设计目标主要是处理文本文件,这样可以按修改的行保存每个修订版本,而不需要为每个版本都保存一个完整的备份。但如果用cvs来对二进制文件(如doc等)进行版本控制,则那些处理文本文件的特性应该取消。
在添加非文本文件时指定-kb选项,告诉cvs把这个文件当作二进制文件处理,对每一个版本,cvs都将保存整个完整的文件!

cvs add -kb file.doc

5.3 branch number最后一位总为偶数

For obscure reasons CVS always gives branches even numbers, starting at 2.
如:在版本1.2中拉两个分支出来,新分支号分别会为1.2.2和1.2.4;在版本1.2.2.3上拉一个分支出来,新的分支为1.2.2.3.2。

                                                     +-------------+
                          Branch 1.2.2.3.2 ->        ! 1.2.2.3.2.1 !
                                                   / +-------------+
                                                  /
                                                 /
                 +---------+    +---------+    +---------+    +---------+
Branch 1.2.2 -> _! 1.2.2.1 !----! 1.2.2.2 !----! 1.2.2.3 !----! 1.2.2.4 !
               / +---------+    +---------+    +---------+    +---------+
              /
             /
+-----+    +-----+    +-----+    +-----+    +-----+
! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 !      <- The main trunk
+-----+    +-----+    +-----+    +-----+    +-----+
                !
                !
                !   +---------+    +---------+    +---------+
Branch 1.2.4 -> +---! 1.2.4.1 !----! 1.2.4.2 !----! 1.2.4.3 !
                    +---------+    +---------+    +---------+

参考:https://ftp.gnu.org/old-gnu/Manuals/cvs-1.9/html_chapter/cvs_3.html#SEC8


Author: cig01

Created: <2012-09-22 Sat 00:00>

Last updated: <2018-02-09 Fri 22:19>

Creator: Emacs 25.3.1 (Org mode 9.1.4)