歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux資訊 >> 更多Linux

使用CVS進行項目開發管理

在多人共同開發一個大型項目時,源代碼的維護和版本維護是一件令人頭疼的事情,由於多人開發,每個開發人員都擁有此項目的副本,所以如果要手動維護同一個文件多人的修改是十分困難的事情。   另外,可能你需要的項目的版本不是當前開發的最新的版本, 如果為了這個目的而為每一個版本保留一個備份幾乎是不可能的。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:[email protected]:/home/kde   cvs login   cvs co kdebase     其他一些常用的cvs 命令選項   -d 指定CVS倉庫的路徑   -zn 使用gzip 壓縮後傳輸,到本地後自動解壓。n為壓縮級別通常式 1~4,通常使用於服務器的傳輸速度較慢的情況下。   -x 用於服務器的通信使用加密算法,只有在使用kerberos驗證體系下有效     CVS 的GUI工具   幾個常用的GUI工具:   LinCVS(圖1),是Xwindow 的CVS客戶端工具,比較流行的一個,功能也很強大.




 



Copyright © Linux教程網 All Rights Reserved