说明:
笔者对于CVS的使用仅仅限于个人的(即没有团队)、本机的、浅层的使用。下面的例子中仅仅是为了说明问题,在实际应用中所用到的不外那么几个命令:import、checkout、update等等。
1、安装及配置
(1)、安装
安装过程似乎不用说也可以吧?我所用的Red Flag系统已经安装好了,但FC10上没有安装,可以这样:  
按提示进行安装就可以了。
(2)、添加用户
任何一个开发团队使用CVS都应当添加CVS组以及几个CVS用户。但本文没有使用这种方式,因为在一个人使用的情况下,添加组、用户略显麻烦,这告诉我们遇到具体的情况要具体分析并制定符合实际的方案。  
如果实在想这样做,添加组和用户也十分easy,如下:  
| 12
 3
 4
 
 | groupadd cvs  // cvs组useradd cvs_latelee // cvs用户
 usermod –g cvs cvs_latelee // 添加cvs_latelee到cvs组中
 passwd cvs_latelee  // 修改密码
 
 | 
(3)、创建CVS仓库目录
目录名称可以为cvs_root或cvs_home或者其它有意义的名称。可以在自己的家目录创建,也可以在系统的/目录下创建,如果是个人使用,可以以root权限将这些目录的权限修改为自己的,如果是团队使用,可以将仓库目录的属性修改为0774。
因为在/目录中,普通用户是不能创建目录的,所以必须以root身份来创建:
| 12
 3
 
 | cd /mkdir cvs_home
 chown –R latelee:latelee cvs_home
 
 | 
对于latelee:latelee,前一个是指用户,后一个是指用户组,也可以指定为cvs组。当然,在自己的家目录下就不存在这种问题了。
(4)、服务器配置
CVS配置文件在/etc/xinetd.d/cvs文件中(不保证所有的Linux系统中都是此路径),以root身份来修改:  
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 
 |  default: off
 description: The CVS service can record the history of your source
 
 # files. CVS stores all the versions of a file in a single
 
 # file in a clever way that only stores the differences
 
 # between versions.
 
 service cvspserver
 
 {
 
 disable = no
 
 port = 2401
 
 socket_type = stream
 
 protocol = tcp
 
 wait = no
 
 user = latelee // 用户
 
 passenv = PATH
 
 server = /usr/bin/cvs
 
 env = HOME=/var/cvs
 
 server_args = -f --allow-root=/cvs_home pserver// 目录
 
 # bind = 127.0.0.1
 
 }
 
 | 
user指定了用户,server_args指定了仓库目录(/cvs_home)以及访问方式(pserver)。  
(5)、启动服务
命令如下:
可以看一下2401端口是否处于监听状态:  
| 12
 3
 
 | # netstat -ln | grep 2401
 tcp 0 0 0.0.0.0:2401 0.0.0.0:* LISTEN
 
 | 
在fedora中可以让xinetd开机自动启动。
(6)、初始化服务器
初始化服务器有两种方式,一是在终端exportCVSROOT环境变量,二是使用在系统中初始化环境变量,前者只在一个终端有效,后者比较方便。在自己家目录的.bashrc文件最后添加:  
| 12
 
 | CVSROOT=/cvs_homeexport CVSROOT
 
 | 
注意:这里的“家目录”是指你所用的用户目录,比如有人喜欢使用root,那么就要在/root/.bashrc文件添加了。
CVS的初始化命令,普通用户与root用户都可执行,但最好是使用你所用的用户来执行该命令:
初始化成功,会在/cvs_home目录生成一个CVSROOT目录。里面有许多文件,是与CVS相关的。不用理会也行。
##2、使用例子
注意:如果设置了CVSROOT环境变量,则输入cvs命令时可以不指定仓库目录。此处设置了在.bashrc中环境变量CVSROOT=/cvs_home  
(1)、常用
检入(import),这里没有-m选项,所以会出现默认的编辑器VI,可以在里面写一些有关的信息。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | [latelee@localhost thread]$ cvs import thread threadproject ver0-1
 N thread/thread.cpp
 
 N thread/thread.h
 
 N thread/main.cpp
 
 N thread/Makefile
 
 No conflicts created by this import
 
 | 
没有check out,不能update:  
| 12
 3
 4
 5
 
 | [latelee@localhost thread]$ cvs update
 cvs update: in directory .:
 
 cvs [update aborted]: there is no version here; run 'cvs checkout' first
 
 | 
check out时,会在当前目录下创建该工程目录,注意:我们本来的目录是/home/latelee/linux-c/thread/,之后则变为:/home/latelee/linux-c/thread/thread,当然,也可以在别的目录下check out
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | [latelee@localhost thread]$ cvs co thread
 cvs checkout: Updating thread
 
 U thread/Makefile
 
 U thread/main.cpp
 
 U thread/thread.cpp
 
 U thread/thread.h
 
 | 
可以看一下某些文件的状态: 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | [latelee@localhost thread]$ cvs status -v main.cpp
 =========================================================
 
 File: main.cpp Status: Locally Modified
 
 Working revision: 1.1.1.1 Thu Apr 22 04:43:21 2010
 
 Repository revision: 1.1.1.1 /cvs_home/thread/main.cpp,v
 
 Sticky Tag: (none)
 
 Sticky Date: (none)
 
 Sticky Options: (none)
 
 Existing Tags:
 
 ver_0-1 (revision: 1.1.1.1)
 
 thread_project (branch: 1.1.1)
 
 | 
修改某些文件了,但还没有提交到仓库,更新会出现:  
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | [latelee@localhost thread]$ cvs update
 cvs update: Updating .
 
 M main.cpp
 
 M thread.cpp
 
 M thread.h
 
 ? thread
 
 | 
其中M代表当前目录下已修改了的文件,这里有三个,?是说thread在仓库里没有找到,因为我检入库时是没有这个文件的,这里是编译后生成的可执行文件,一般来说,在仓库里的基本上都是源代码和说明文档等等,在这里,我不将编译生成的结果检入库。好,那么就检入库:  
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | [latelee@localhost thread]$ cvs commit
 cvs commit: Examining .
 
 Checking in main.cpp;
 
 /cvs_home/thread/main.cpp,v <-- main.cpp
 
 new revision: 1.2; previous revision: 1.1
 
 done
 
 Checking in thread.cpp;
 
 /cvs_home/thread/thread.cpp,v <-- thread.cpp
 
 new revision: 1.2; previous revision: 1.1
 
 done
 
 Checking in thread.h;
 
 /cvs_home/thread/thread.h,v <-- thread.h
 
 new revision: 1.2; previous revision: 1.1
 
 done
 
 | 
当输入cvscommit时,我没有带参数(这里的参数是指文件),则代表目录下所有的文件都入库,这里有三个。回车后,将会出现文本编辑器(此处是VI,当然也可以修改,如-e emacs),在VI中记录修改日志。保存退出,即可。
下面再check out这个工程,在上一目录中执行cvs co thread,它不再是check out,而是update,因为当前目录已有了thread目录了。
| 12
 3
 4
 5
 6
 7
 
 | [latelee@localhost thread]$ ls
 main.cpp Makefile thread thread.cpp thread.h //注意,第三个文件thread其实是我们的工程目录
 
 [latelee@localhost thread]$ cvs co thread
 
 cvs checkout: Updating thread
 
 | 
我们再看一下main.cpp的状态:  
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
 | [latelee@localhost thread]$ cvs status -v main.cpp
 =========================================================
 
 File: main.cpp Status: Up-to-date
 
 Working revision: 1.2 Thu Apr 22 04:57:48 2010
 
 Repository revision: 1.2 /cvs_home/thread/main.cpp,v
 
 Sticky Tag: (none)
 
 Sticky Date: (none)
 
 Sticky Options: (none)
 
 Existing Tags:
 
 ver_0-1 (revision: 1.1.1.1)
 
 thread_project (branch: 1.1.1)
 
 | 
可以看到,版本已经变化了。
(2)、标记
我们创建一个标记:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | [latelee@localhost thread]$ cvs tag prealpha0-1
 cvs tag: Tagging .
 
 T Makefile
 
 T main.cpp
 
 T thread.cpp
 
 T thread.h
 
 | 
标记名为prealpha0-1,这个名称可以随意修改。
我也不知道下面这个是什么意思。在书上的:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | [latelee@localhost thread]$ cvs update -d -r prealpha0-1
 cvs update: Updating .
 
 U Makefile
 
 U main.cpp
 
 U thread.cpp
 
 U thread.h
 
 | 
再看一下main.cpp的状态:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | [latelee@localhost thread]$ cvs status -v main.cpp
 =========================================================
 
 File: main.cpp Status: Up-to-date
 
 Working revision: 1.2 Thu Apr 22 05:02:20 2010
 
 Repository revision: 1.2 /cvs_home/thread/main.cpp,v
 
 Sticky Tag: prealpha0-1 (revision: 1.2)
 
 Sticky Date: (none)
 
 Sticky Options: (none)
 
 Existing Tags:
 
 prealpha0-1 (revision: 1.2)
 
 ver_0-1 (revision: 1.1.1.1)
 
 thread_project (branch: 1.1.1)
 
 | 
改名:  
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 
 | [latelee@localhost thread]$cvs tag -r prealpha0-1 prealpha0-1branchroot
 cvs tag: Tagging .
 
 T Makefile
 
 T main.cpp
 
 T thread.cpp
 
 T thread.h
 
 [latelee@localhost thread]$ cvs status -v main.cpp
 
 =========================================================
 
 File: main.cpp Status: Up-to-date
 
 Working revision: 1.2 Thu Apr 22 05:02:20 2010
 
 Repository revision: 1.2 /cvs_home/thread/main.cpp,v
 
 Sticky Tag: prealpha0-1 (revision: 1.2)
 
 Sticky Date: (none)
 
 Sticky Options: (none)
 
 Existing Tags:
 
 prealpha0-1branchroot (revision: 1.2)
 
 prealpha0-1 (revision: 1.2)
 
 ver_0-1 (revision: 1.1.1.1)
 
 thread_project (branch: 1.1.1)
 
 | 
可见,版本1.2有两个标记,下面我们删除一个:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 
 | [latelee@localhost thread]$ cvs tag -d prealpha0-1
 cvs tag: Untagging .
 
 D Makefile
 
 D main.cpp
 
 D thread.cpp
 
 D thread.h
 
 [latelee@localhost thread]$ cvs status -v main.cpp
 
 cvs status: main.cpp is no longer in the repository
 
 =========================================================
 
 File: main.cpp Status: Entry Invalid
 
 Working revision: 1.2 Thu Apr 22 05:02:20 2010
 
 Repository revision: No revision control file
 
 Sticky Tag: prealpha0-1 - MISSING from RCS file!
 
 Sticky Date: (none)
 
 Sticky Options: (none)
 
 Existing Tags:
 
 prealpha0-1branchroot (revision: 1.2)
 
 ver_0-1 (revision: 1.1.1.1)
 
 thread_project (branch: 1.1.1)
 
 | 
是不是有点麻烦?更名过程就是将旧标记贴上新的标记,再将旧标记删除。
(3)、分支测试:
在上面的基础上,添加一个分支:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 
 | [latelee@localhost thread]$ cvs rtag -r prealpha0-1branchroot -b prealpha0-1_branch thread
 cvs rtag: Tagging thread
 
 [latelee@localhost thread]$ cvs status -v main.cpp
 
 cvs status: main.cpp is no longer in the repository
 
 =========================================================
 
 File: main.cpp Status: Entry Invalid
 
 Working revision: 1.2 Thu Apr 22 05:02:20 2010
 
 Repository revision: No revision control file
 
 Sticky Tag: prealpha0-1 - MISSING from RCS file!
 
 Sticky Date: (none)
 
 Sticky Options: (none)
 
 Existing Tags:
 
 prealpha0-1_branch (branch: 1.2.2)
 
 prealpha0-1branchroot (revision: 1.2)
 
 ver_0-1 (revision: 1.1.1.1)
 
 thread_project
 
 | 
回到上一目录,将一个分支check out出来:  
| 12
 3
 
 | [latelee@localhost thread]$ cvs co -r prealpha0-1_branch thread
 cvs checkout: Updating thread
 
 | 
看一下main.cpp的状态:  
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | [latelee@localhost thread]$ cvs status main.cpp
 =========================================================
 
 File: main.cpp Status: Up-to-date
 
 Working revision: 1.2 Thu Apr 22 05:34:38 2010
 
 Repository revision: 1.2 /cvs_home/thread/main.cpp,v
 
 Sticky Tag: prealpha0-1_branch (branch: 1.2.2)
 
 Sticky Date: (none)
 
 Sticky Options: (none)
 
 | 
注意:在实际开发中,分支最好不要跟主干的目录混在一起。
(4)、远程访问(检出代码)
下面这种方法只适合一个终端,因为export只在一个终端有效。
| 12
 3
 4
 5
 6
 7
 
 | [latelee@FightNow latelee]$ export CVSROOT=:pserver:latelee@192.168.1.13/cvsroot
 [latelee@FightNow latelee]$ cvs login
 
 Logging in to :pserver:latelee@192.168.1.13:2401/cvsroot
 
 CVS password: (在这里输入密码)
 
 | 
没有错误,说明已经连接上服务器了。这里进入到一个测试目录:  
| 12
 3
 4
 5
 
 | [latelee@FightNow latelee]$ cd work/nfs
 [latelee@FightNow nfs]$ ls
 
 hello-arm
 
 | 
将代码checkout出来  
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 
 | [latelee@FightNow nfs]$ cvs co camera-server
 cvs checkout: Updating camera-server
 
 U camera-server/Makefile
 
 U camera-server/video/utils.h
 
 U camera-server/video/v4l2uvc.h
 
 [latelee@FightNow nfs]$ cvs co camera-client
 
 cvs checkout: Updating camera-client
 
 U camera-client/Makefile
 
 U camera-client/main.c
 
 U camera-client/my-types.h
 
 U camera-client/my_udp.c
 
 U camera-client/v4l2/v4l2uvc.h
 
 | 
目录中多了两个文件夹。  
| 12
 3
 4
 5
 
 | [latelee@FightNow nfs]$ ls
 camera-client camera-server hello-arm
 
 [latelee@FightNow nfs]$
 
 | 
上述例子中没有讲如何在开发过程中添加或删除文件(或目录),下面讲一下。在thread工程中添加了fork.cpp文件。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | [latelee@51 thread]$ cvs add fork.cpp
 cvs add: scheduling file `fork.cpp' for addition
 
 cvs add: use 'cvs commit' to add this file permanently
 
 [latelee@51 thread]$ cvs commit
 
 cvs commit: Examining .
 
 cvs commit: Up-to-date check failed for `main.cpp'
 
 cvs commit: Up-to-date check failed for `thread.cpp'
 
 cvs commit: Up-to-date check failed for `thread.h'
 
 cvs [commit aborted]: correct above errors first!
 
 | 
出错了!因为没有check out,正确的cvs commit显示如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | [latelee@51 thread]$ cvs commit
 cvs commit: Examining .
 
 RCS file: /cvs_home/thread/fork.cpp,v
 
 done
 
 Checking in fork.cpp;
 
 /cvs_home/thread/fork.cpp,v <-- fork.cpp
 
 initial revision: 1.1
 
 done
 
 | 
删除一个文件(fork.cpp)过程如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | [latelee@51 thread]$ rm fork.cpp
 [latelee@51 thread]$ cvs remove fork.cpp
 
 cvs remove: scheduling `fork.cpp' for removal
 
 cvs remove: use 'cvs commit' to remove this file permanently
 
 [latelee@51 thread]$ cvs commit
 
 cvs commit: Examining .
 
 Removing fork.cpp;
 
 /cvs_home/thread/fork.cpp,v <-- fork.cpp
 
 new revision: delete; previous revision: 1.1
 
 done
 
 | 
至于目录的添加删除,就不显示了。
又:本文的例子不是同一时间、同一台PC上完成的,但对于使用CVS来说,是没有关系的。