[ 永遠的UNIX::UNIX技術資料的寶庫 ]   GB | BIG5

首頁 > 應用技術 > 其它 > 正文
使用CVS進行項目開發管理
本文出自:http://www.swm.com.cn 作者:張雲帆 (2001-11-06 07:00:00)
在多人共同開發一個大型項目時,源代碼的維護和版本維護是一件令人頭疼的事情,由多人開發,
每個開發人員都擁有此項目的副本,所以如果要手動維護同一個文件多人的修改是十分困難的事情。
另外,可能你需要的項目的版本不是當前開發的最新的版本, 如果為了這個目的而為每一個版本
保留一個備份幾乎是不可能的。Linux平台上提供了一個功能軟件:CVS。眾所周知,Linux的發展
得益互聯網的發展,大部分的軟件都是眾多開發者在互聯網上共同開發的,這些軟件的維護大部
分都使用CVS,例如Deban Linux的開發、KDE的開發,以及sourceforg上的開發項目。正確熟練使
用CVS是一個Linux程序員基本的素質。


CVS不僅可以維護源代碼,事實上,所有的文本文件都可以使用CVS來管理,當然也可以管理 二進
制文件,只是需要特殊的命令選項罷了。

CVS把文件保存在一個名叫倉庫(repository)的地方,倉庫中保存的文件並不是每個版本的副本,
而是可以從任意版本回溯到初始版本的一些代碼的主控信息,這樣,就節省了大量的存儲空間。
倉庫不僅可以建立在本機上,也可以建立在網絡上。另外CVS支持版本分支(tag),這樣可以從
任意的版本中衍生出另外一個版本進行開發,必要時,還可以把這個分支合並到主開發分支去。

CVS需要一個名叫RCS(修訂控制系統)的軟件,確切說,CVS主要管理開發項目中的目錄的改動,
而RCS則側重管理的一個文件的改變。如果要使用網絡的CVS,還需要rsh或者自己內置的
cvs-server,rsh是CVS客戶服務器的通信平台,如果需要安全的信息傳輸,可以使用ssh,
這個可以使用CVS_RSH環境變量來指定:
export CVS_RSH=rsh 使用rsh
export CVS_RSH=ssh 使用ssh

RCS使用
在使用CVS之前,先介紹一下RCS的使用方法。
正如前面所說,RCS是針對某一個特定的文件進行管理,常用到的命令是co和ci。RCS使用當 前工
作路徑下的RCS目錄來存儲管理的文件。
ci (check in)把源代碼加入到RCS源代碼倉庫中,每一個添加進代碼庫中的文件的版本是 1.1。
以每次修改文件重新ci以,此文件的版本遞增為1.2 ,1.3.……。
co(check out)把源文件從RCS源代碼倉庫中取出,缺省的版本是最新的版本,如果你需要一個
指定的版本,則需要使用-r選項指定。例如:
$mkdir RCS 建立RCS源代碼倉庫
編輯一個文件,如hello.c
#include <stdio.h>
int main (void)
{
printf ("Hello World\n");
return 0;
}
然把它加入到RCS倉庫中:
[kerberos@power zhyf]$ ci hello.c
RCS/hello.c,v <-- hello.c
enter description, terminated with single '.' or end of file:
NOTE: This is NOT the log message!
>>the inital verion. 輸入關文件改動的信息
>> . 以.結束信息輸入
initial revision: 1.1 初始版本是1.1
done
[kerberos@power zhyf]$
當文件加入到源代碼倉庫,RCS會自動刪除文件。

如果需要修改文件,則從倉庫取出:
[kerberos@power zhyf]$ co hello.c
RCS/hello.c,v --> hello.c
revision 1.1
done
此時,取出的文件是只讀的,如果需要編輯文件,需要對文件加鎖取出,防止與他人編輯沖突。 
加鎖取出使用-l (lock)選項。
[kerberos@power zhyf]$ co -l hello.c
RCS/hello.c,v --> hello.c 從RCS倉庫中取出hello.c,倉庫中的文件是以v結尾的。
revision 1.1 (locked) 指明是加鎖取出
done
這時,我們就可以修改和編輯文件了。
例如:
#include <stdio.h>
int main (void)
{
printf ("Hello World\n");
printf("checkoutwithlock\n"); 添入代碼
return 0;
}
[kerberos@power zhyf]$ ci hello.c
RCS/hello.c,v <-- hello.c
new revision: 1.2; previous revision: 1.1 重新加入到倉庫,文件版本遞增為1.2
enter log message, terminated with single '.' or end of file:
>> check out with lock and modified it .
>> .
done
如果你需要先前的hello.c,也就是1.1版本的,可以使用-r來指定取出文件的版本:
[kerberos@power zhyf]$ co -l -r1.1 hello.c
hello.c,v --> hello.c
revision 1.1 (locked) 取出1.1版本
done
可以嘗試使用兩次加鎖取出,這時候,RCS會提示警告信息。
[kerberos@power zhyf]$ co -l hello.c
hello.c,v --> hello.c
revision 1.1 (locked)
writable hello.c exists; remove it? [ny](n) 是否覆蓋當前的hello.c
如果回答n,則取出失敗,回答是則覆蓋當前文件
與-l相反的選項是-u,取出不加鎖的文件用工作。-f則在ci時強制覆蓋RCS倉庫中的文件,
 在co時強制覆蓋當前的文件。 

在RCS中,支持類似宏的關鍵字,在ci,這些關鍵字會被RCS替換成特定的信息,
如$Id$關鍵字:
/*$Id
*/
#include <stdio.h>
int main (void)
{
printf ("Hello World\n");
printf("checkoutwithlock\n");
return 0;
}
ci 之,然取出,文件將變成:
/*$Id: hello.c,v 1.3 2001/02/22 16:16:58 kerberos Exp kerberos $
*/
#include <stdio.h>
int main (void)
{
printf ("Hello World\n");
printf ("check out with lock\n");
return 0;
}
$Id$ 關鍵字是用文件名、版本、時間、作者 及代碼性質替換,如果使用-l選項取出,在Exp
面會加上登錄用戶的名稱。除了$Id$關鍵字,RCS還支持下面常用的關鍵字:
$Log$ : 你所提供的修改日志信息。
$Author$ :存入該版本的作者。
$Locker$ : 該版本的加鎖者
$State$ : 該版本的狀態 Exp(試驗版), Stabe(穩定版), Rel(發行版).缺省是Exp
$Date$ : 該版本存入的時間,使用UTC時間格式。
$Revision$ : 該版本的版本號
$RCSfile$ : RCS文件名
$Source$ : RCS全路徑名
$Name$ : 取回該版本的符號名
$Header$ : 相當$ Source $$ Revision$$Date$$Author $$State$$Locker$的組合
在UNIX/Linux下有一個diff工具可以比較兩個文件的不同,rcsdiff可以在不同的版本之間比
較文件的不同,而不必取出文件,如:
rcsdiff hello.c 比較當前hello.c文件與RCS倉庫中最新版本的區別。
rcsdiff -r1.1 hello.c 比較當前hello.c文件與hello.c的1.1版本的區別。
rcsdiff -r1.2 -r1.1 比較hello.c 文件的1.1版本和1.2版本的區別。
使用這個命令,可以很方便的做出任意版本之間的補丁文件。
另外,還有其他一些不常用的RCS命令,讀者可以參考更詳細的資料。

CVS使用
下面我們就講一下CVS的使用和管理。

在使用CVS之前,必須要指定CVS的根路徑,不像RCS是工作在工作目錄下的RCS目錄下。指定
CVS路徑的方法有兩種,一種是使用cvs-d 選項指定,如:
cvs -d /home/kerberos/cvsroot 指定/home/kerberos/cvsroot為CVS倉庫的路徑。
另外一種是使用CVSROOT環境變量指定(bash下):
export CVSROOT=/home/kerberos/cvsroot

無論何種方式,每當重新開啟一次終端會話,CVS倉庫的路徑都要重新設置,你可以把以上的
者加入到其實目錄下的.bashrc文件中,這樣開啟一次終端會話,不需要重新設置了(對
其他的環境變量的設置也是如此)。如果你的CVSROOT環境變量已經存在,可以使用cvs -d 
選項來指定不同CVSROOT所指定的CVS倉庫。然初始化CVS倉庫,建立CVS維護所需要的一些
文件和信息,CVS把這些信息存放在CVS倉庫主目錄的CVSROOT目錄下。這些文件,除了modules ,
其他文件都沒有手動修改的必要,modules 文件定義了CVS的導出模塊在CVS倉庫中的位置,
這一點在從CVS倉庫中導出一個深層目錄時是非常方便的。

cvs init
初始化之,我們使用cvs import來把需要進行版本維護的開發項目的目錄結構導入到CVS倉
庫中,如下:
建立好項目的目錄結構以,當前的testprj目錄的結構是這樣的:

[kerberos@power zhyf]$ tree testprj
testprj
|-- admin
|-- doc
|-- include
|-- libs
`-- src
|-- include
`-- main

7 directories, 0 files
cvs import有三個參數:第一個是這個目錄樹在CVS倉庫中的模塊名稱,第二個是發布者的一個
標志,第三個是這個項目的版本標記(tag)。下面,我們把testprj導入到CVS倉庫中,使用
testprj 模塊名稱。
[kerberos@power testprj]$cvs import testprj KERBEROS INITVER

執行命令以,CVS會調用CVSEDITOR指定的編輯器來讓你輸入日志,如果CVSEDITOR沒有定義,
缺省調用vi,可以使用export命令指定你自己喜歡使用的編輯器。輸入日志以,CVS把當前的
目錄結構導入CVS倉庫中,並且使用testprj這個模塊名稱,在導出文件時,就可以使用
cvs co testprj 來導出。
"/tmp/cvscVxZnW" 5L, 242C written
cvs import: Importing /home/zhyf/cvsroot/testprj/admin
cvs import: Importing /home/zhyf/cvsroot/testprj/libs
cvs import: Importing /home/zhyf/cvsroot/testprj/src
cvs import: Importing /home/zhyf/cvsroot/testprj/src/include
cvs import: Importing /home/zhyf/cvsroot/testprj/src/main
cvs import: Importing /home/zhyf/cvsroot/testprj/doc
cvs import: Importing /home/zhyf/cvsroot/testprj/include

No conflicts created by this import

看一下cvsroot目錄中的結構是不是和testprj 的相同:


../cvsroot
|-- CVSROOT
| |-- checkoutlist
| |-- checkoutlist,v
| ........................... CVS的倉庫信息,省略..
| ...........................
`-- testprj
|-- admin
|-- doc
|-- include
|-- libs
`-- src
|-- include
`-- main

這裡testprj 模塊的結構和我們自己的testprj的目錄結構是一樣的。
這樣,當需要再次使用這些源代碼時,就可以簡單的使用cvs co模塊名來導出目錄樹和文
件了。導出任意具有寫權限的目錄下:
[kerberos@power testprj]$ cd ..
[kerberos@power zhyf]$ rm -rf testprj 刪除testprj目錄
[kerberos@power zhyf]$ cvs co testprj 重新導出
cvs checkout: Updating testprj
cvs checkout: Updating testprj/admin
cvs checkout: Updating testprj/doc
cvs checkout: Updating testprj/include
cvs checkout: Updating testprj/libs
cvs checkout: Updating testprj/src
cvs checkout: Updating testprj/src/include
cvs checkout: Updating testprj/src/main
[kerberos@power zhyf]$ tree testprj
testprj
|-- CVS
| |-- Entries
| |-- Repository
| `-- Root
|-- admin
| `-- CVS
| |-- Entries
| |-- Repository
| `-- Root
|-- doc
| `-- CVS
| |-- Entries
| |-- Repository
| `-- Root
|-- include
| `-- CVS
| |-- Entries
| |-- Repository
| `-- Root
|-- libs
| `-- CVS
| |-- Entries
| |-- Repository
| `-- Root
`-- src
|-- CVS
| |-- Entries
| |-- Repository
| `-- Root
|-- include
| `-- CVS
| |-- Entries
| |-- Repository
| `-- Root
`-- main
`-- CVS
|-- Entries
|-- Repository
`-- Root

CVS在每個目錄下面都建立了一個名叫CVS的目錄,此外,目錄的結構是一樣的,事實上,
CVS目錄中存放的是一些關目錄結構在倉庫中的信息,Entries中包含了這些目錄中的子目錄,
Respository包含此目錄在倉庫中的相對位置,ROOT包含導出的模塊主目錄的絕對路徑。

cvs import命令只是在第一次把項目導入CVS倉庫時才使用,以如果再需要加入目錄或者加入
文件,則使用cvs add 命令加入,然使用cvs commit提交之,文件或者目錄才會真正加入到
CVS倉庫中。我們可以把剛才編輯的hello.c文件拷貝到testprj/src/main/中,然使用:
[kerberos@power zhyf]$ cvs add testprj/src/main/hello.c
cvs add: scheduling file `testprj/src/main/hello.c' for addition
cvs add: use 'cvs commit' to add this file permanently

使用cvs commit提交修改
[kerberos@power zhyf]$ cvs commit
cvs commit: Examining testprj
cvs commit: Examining testprj/admin
cvs commit: Examining testprj/doc
cvs commit: Examining testprj/include
cvs commit: Examining testprj/libs
cvs commit: Examining testprj/src
cvs commit: Examining testprj/src/include
cvs commit: Examining testprj/src/main
RCS file: /home/zhyf/cvsroot/testprj/src/main/hello.c,v
done
Checking in testprj/src/main/hello.c;
/home/zhyf/cvsroot/testprj/src/main/hello.c,v <-- hello.c
initial revision: 1.1

使用RCS管理單個文件
done

testprj/src/main/hello.c

由CVS使用RCS管理單個文件,所以,前面我們講的關RCS的知識也可以用到這裡,唯一不同的
是命令之前要限定是cvs,比如cvs ci, cvs co, cvs diff, 或者在導出時使用 -r 選項指定導出的
文件的版本。在源代碼文件中,同樣也可以使用RCS的關鍵字。
對非ASCII文件,如二進制可執行文件,位圖等文件,我們不能直接使用cvs add
加入到CVS倉庫中,因為CVS只是基ASCII代碼比較文件的改變,所以,對二進制文件,如果單
純使用cvs add 命令會破壞文件的完整,所在加入二進制文件時,要指定-kb參數。如果對已有文
件進行了修改,使用cvs ci, cvs commit 來提交修改。
另外一種情況是自己導出別人修改的文件,而不想覆蓋自己所作的修改,可以使用cvs update命令,
這個命令會自動比較CVS倉庫中文件和當前自己文件的最修改時間來導出最新的文件。

在cvs update 和cvs ci 時,你對此的文件修改與他人發生沖突,CVS則提示你。CVS會在文件中使
用“>>>>>>” 或者“<<<<<<<”標記標出沖突的地方,你可以與他人協商手動修改這一段代碼。

如果你想刪除倉庫中的一個文件,就要先刪除本地導出的文件,然使用cvs remove 文件名, 
cvs commit來刪除。事實上,CVS是不會真正刪除這些文件的,如果你還想得到這些文件,只需要,
使用cvs co -r 指定刪除前的版本導出就可以了。

有時候為了導出CVS倉庫中的深層目錄或者文件不得不輸入很長的路徑,這一點很討厭,你可以導
出CVS倉庫中CVSROOT目錄下,修改modules文件,定義這個目錄或者文件為一個模塊,提交修改就
可以直接使用cvs co 模塊名 就可以導出了。

CVS的版本控制功能主要體現在tag上。標記的作用如同是同時記錄了做標記時所有文件的版本,需
要的時候可以一次導出這些版本不一的文件,這常常是在項目的開發達到了一定的目標時使用的。
cvs tag 命令的參數只有一個:標記名。例如:
cvs tag RELEASE_1.0_BETA
以,如果需要這個版本,只需要使用cvs co -r RELEASE_1.0_BETA就可以導出這個版本了。或者
使用cvs diff -r 來生成patch文件和使用cvs log -r 來查看這個版本的日志。

標記的另外一個非常重要的作用是生成分支和合並分支。
通常,我們的項目的開發到某一定的程度時,需要探索某一條開發途徑是否有效,這個時候,我們
需要做一個試驗,但是這個試驗的分支的開發不能影響項目的主開發分支,這樣,我們就可以生成
一個分支開發,主開發分支也可以繼續進行,兩者並不沖突,將來如果証明分支開發有效,還可以把
分支合並到主開發分支中。建立一個CVS分支:
cvs tag -b 分支標記名

建立分支,並不影響當前我們從CVS倉庫中的文件,需要從CVS倉庫中重新導出分支BRANCH_VER:
cvs co -r 分支標記名 模塊名 

這樣,我們可以進行分支開發,主分支的開發也可以照常進行,如果此分支開發成功,我們可以把它
與主分支合並:
cvs update -j 分支標記名
cvs commit
就可以把開發分支合並到主分支中去了。

以上是本地CVS倉庫的操作,如果使用網絡CVS倉庫,只需要使用CVS_RSH環境變量指定通信shell,另外
設置CVS倉庫為網絡倉庫就可以了。網絡上的CVS倉庫的服務器有兩種:ext和dserver。對者在進行
CVS操作時,需要先login驗証用戶。者使用rlogin驗証,不需要顯式登陸。如:

ext CVS 服務器使用
export CVSROOT=:ext:kerberos@power:/home/repository 
或者cvs -d :ext:kerberos@power:/home/repository
cvs co platoon

derser CVS 服務器使用
export CVSROOT=:pserver:anonymous@anoncvs.kde.org:/home/kde
cvs login 
cvs co kdebase

其他一些常用的cvs 命令選項
-d 指定CVS倉庫的路徑
-zn 使用gzip 壓縮傳輸,到本地自動解壓。n為壓縮級別通常式 1∼4,通常使用服務器的傳輸速
度較慢的情況下。
-x 用服務器的通信使用加密算法,只有在使用kerberos驗証體系下有效

CVS 的GUI工具
幾個常用的GUI工具:
LinCVS(圖1),是Xwindow 的CVS客戶端工具,比較流行的一個,功能也很強大. 


圖1 LinCVS
Cervisia (圖2),一個基KDE的CVS GUI工具,使用KDE的讀者使用起來很容易上手. 


圖 2 cervisia 
注:文章中的環境變量的設置是在bash下,其他shell使用不同的命令

 
(http://www.fanqiang.com)
    進入【UNIX論壇

相關文章
使用CVS進行項目開發管理 (2001-11-06 07:00:00)
cvs的配置和使用 (2001-09-10 08:00:00)
用CVS來管理自己的程序 (2001-08-09 15:00:00)
CVS 簡介 (2001-06-05 18:08:00)
CVS RCS HOWTO 原始程式碼版本控制系統 (3) (2001-05-02 10:11:41)
CVS RCS HOWTO 原始程式碼版本控制系統 (2) (2001-05-02 10:10:18)
CVS RCS HOWTO 原始程式碼版本控制系統 (1) (2001-05-02 10:09:53)
PostgreSQL7.0手冊-附錄-日期/時間支持-CVS 倉庫 (2001-04-21 23:48:48)
使用CVS進行版本管理 (2001-04-21 20:24:35)
CVS 速成班 (2001-04-21 17:54:50)
 

★  樊強制作 歡迎分享  ★