這分文件包含兩個部份。首先是一般使用者如使用 CVS。例如如何透過 CVS 取得最新的原始碼,並且更新到最近的檔案。另一部份就是如何用 CVS 來共同開發程式,包括修改、新增、移除 CVS 上的檔案。建議您先閱讀第一部份,然後再接著讀第二部份。如果您之前已經有一些 CVS 的使用經驗,但是您第一次以 CVS 來開發程式,您可以在第二部份找到所有的資料,但或許您可以看看的一部份來複習。
CVS 是一個主從架構的系統,讓發展者可以藉它存放他們的專案在叫做儲藏庫(repository) 的地方。發展者可以藉著 CVS 客戶端工具來修改儲藏庫內的檔案,CVS 會追蹤每個檔案的改變,並且建立這個專案完整的發展過程。發展者可以取得專案中某個檔案的較舊的版本,看修改紀錄,或是作其他有用的工作。
許多的自由軟體有他們自己的 CVS 伺服器,當作程式發展者主要儲存的地方。發展者常常會每天對程式作一些改進,而且他們通常是散佈在世界各地,因此 CVS 提供一個讓所有人聯繫起來的機制。CVS 建立一種團隊的聯繫讓開發人員改進程式碼而不會蓋掉其他人的修改,也不會遺失重要的資料或是重要的更新。
當程式發展差不多的時候,他們就會把 CVS 上目前的檔案壓縮成 .tar.gz 然後以新的版本發表。然而,因為各種原因,最近的發表版本未必是最新的檔案。在這分教學的第一部份將會教您如何用 CVS 取得最新的程式碼來使用。
要安裝 CVS,只需要輸入 emerge cvs。
原始碼 1.1: 安裝 CVS |
# emerge cvs |
在開始之前,有一些 CVS 的基礎您必須要先知道。首先,為了和 CVS 連接,您必須要先知道一個叫 "CVSROOT" 的路徑。CVSROOT 是一個類似網址的字串,告訴cvs程式遠端的伺服器儲藏庫位置以及連線方式。根據位置在本機或遠端,以及您連線的方式,為了讓生活精彩點,CVSROOT 有許多不同的格式。底下是各種格式的範例以及它的說明。
原始碼 1.2: 設定 CVSROOT |
CVSROOT=/var/cvsroot |
這是一個本機的 CVSROOT 路徑範例,您可能會用像這樣的路徑連到本機上位於 /var/cvsroot 的儲藏庫,或是經由 NFS 掛上 /var/cvsroot 的儲藏庫。
原始碼 1.3: 設定要認證的 CVSROOT |
CVSROOT=:pserver:[email protected]:/var/cvsroot |
這是一個連接到 foo.bar.com 上位於 /var/cvsroot 儲藏庫的例子。開頭的 ":pserver:" 告訴程式使用包含在 CVS 內的 CVS 密碼伺服器協定連接。一般而言,公開的 CVS 使用這種協定讓匿名使用者存取。
原始碼 1.4: RSH/SSH CVSROOT |
[email protected]:/data/cvs |
這是一個使用 RSH 或 SSH 協定連接的例子,在這個範例中,CVS伺服器嘗試用 drobbins 帳號來存取放在 foo.bar.com 上的儲藏庫。如果 CVS_RSH 環境變數設定成 "ssh",cvs客戶端將會以 ssh 來連線,否則就使用 rsh。ssh連線方法在注重安全時相當受歡迎。RSH 或 SSH 也可以讓匿名使用者存取。如果想使用這種方法,您必須在 foo.bar.com 有一個登入帳號。
除了 CVSROOT 之外,您必須要知道您要您要下載軟您的模組(程式碼的集合)名稱,就算您以匿名的密碼伺服器方式連接時也是一樣。不像匿名的 FTP,匿名使用者的密碼並沒有一定的格式,所以您必須要從軟體的網站或發展者那得到密碼。一但您知道這些資訊以後,就可以準備開始了。
取得原始碼包括兩階段的過程。首先,先登入密碼伺服器。然後再以 checkout 指令取得原始碼。底下是一個取得最新的 Samba 原始碼範例:
原始碼 1.5: 設定 CVSROOT |
# export CVSROOT=:pserver:[email protected]:/cvsroot |
第一行的指令設定 CVSROOT 環境變數。如果您沒有設定的話,您必須要在 cvs 指令後加上 -d:pserver:[email protected]:/cvsroot,設定 CVSROOT 可以減少需多打字的時間。
接下來是取得最新的原始碼。您或許需要跳到下一部份取得這些指令的說明,然後再回頭看:
原始碼 1.6: 下載原始碼 |
# cvs login(登入 to [email protected])CVS password: (在這裡輸入密碼)# cvs -z5 co sambaU samba/COPYINGU samba/ManifestU samba/READMEU samba/Read-Manifest-NowU samba/RoadmapU samba/WHATSNEW.txt(這只是 cvs co 輸出的片段) |
上方的第一行指令登入 CVS,然後第二行告訴cvs客戶端下載 (check out,co) samba模組,並且用 gzip 第五級的壓縮 ("-z5") 來加速下載。任何會在本機建立的新檔案,cvs 標示"U [path]" 表示本機上的檔案已經被更新了。
一旦 checkout 命令完成之後,您會在您目前的目錄下看到包含最新原始碼的 "samba" 目錄。您也會注意到每個目錄底下都有一個名為 "CVS" 的目錄,這個目錄被用來存放一些 CVS 的資料,您可以安全的忽略他。接下來,您並不用擔心是否設定了 CVSROOT 環境變數,因為這些資訊已經被紀錄在 "CVS" 目錄中。記得:您只需要在第一次登入及下載時指定 CVSROOT。
好了,現在您就有最新的程式碼了!現在您可以去編譯、安裝、觀看原始碼、或是作任何您想作的事情。
有時候,您也許要更新到 CVS 上最新的原始碼。首先,您要先登入到伺服器,您的所有認證資料都存在 "CVS" 目錄中,先進入要下載的目錄中(這個例子中為 samba),接著打:
原始碼 1.7: 更新原始碼 |
# cvs update -dP |
如果有任何新的檔案,cvs 會在那一個項目顯示 "U [path]"。而且,如果您已經編譯過的話,您可能會看到 "? [path]",因為編譯時產生的目的檔 cvs 並不能從遠端找到。
然後注意到我們在命令列加在 "cvs update" 的參數,"-d" 告訴 cvs 建立任何遠端儲藏庫新增的目錄(他預設不這麼作),"-P" 則是移除本機原始碼中任何空的目錄,因為 cvs 頃向保留任何曾經使用過,但是現在不再被使用的目錄。
當您輕鬆的取得最新的原始碼以後,您就完成了。接下來看如何以 CVS 協助開發程式。
身為一個發展者,您常常會需要修改 CVS 上的檔案。修改時,您只需要修改本機上從儲藏庫下載的檔案。您在檔案上做的改變並不會加到遠端的檔案,直到您告訴 cvs 去"提交 (commit)"修改。當您確認過您的修改沒有問題後,作底下兩個步驟。首先,在原始碼的目錄中輸入下列指令更新原始碼:
原始碼 2.1: 更新原始碼及目錄 |
# cvs update -dP |
如同我們之前所看到的,"cvs update" 將會更新您的原始碼到最新的狀態--但是我作的修改呢?不用擔心,他們並沒有被覆蓋。如果另一位開發者在本機上不存在的檔案作修改,本機上的檔案將會被更新到儲藏庫中的版本。
此外,如果您修改了本機檔案的 1-10 行,而另一位開發者刪除了 40-50 行,在檔案結尾新增 20 行,修改 30-40 行,而且在您之前就提交了他的修改,cvs 會聰明的在本機上的檔案加入這些改變,而且您的所有修改並不會遺失。這讓兩個以上的發展者可以同時修改同一個檔案。
然而,如果有多個發展者在修改檔案中同一部份,那事情就變得有點複雜。當他發生時,cvs 會告訴您有衝突發生。之前作的改變並不會遺失,但是當 cvs 要求您做一些調整時,您必須要手動作一些修改。
我們將會稍微看一下衝突如何解決,但是現在我們假設在您輸入 "cvs update -dP" 時,並沒有衝突發生。您的程式碼目前已經是最新的,接下來就在目錄中輸入下列的指令來提交您的修改:
原始碼 2.2: 提交修改 |
# cvs commit |
"cvs commit" 不僅僅是讓儲藏庫中的檔案接受您的修改,cvs 會啟動預設的編輯器讓您輸入一些針對修改的說明。一但您輸入指令,儲存檔案並且離開編輯器,您的改變(包括說明)會被加入遠端的儲藏庫中,並且可以讓團隊中的其他人看到。
要看特定檔案(包括所有人作改變時加入的說明)的完整紀錄相當的容易,要查看這些資訊,輸入:
原始碼 2.3: 檢視紀錄 |
# cvs log myfile.c |
"cvs log" 是遞迴的,所以您只要輸入下列的指令就可以看所有檔案的紀錄:
原始碼 2.4: 一頁頁的檢視紀錄 |
# cvs log | less |
您或許會想在輸入 "cvs commit" 時使用其他的編輯器。只要設定 EDITOR 環境變數為您要用的編輯器名稱,在您的 ~/.bashrc 中放入下面這一行會是一個好點子:
原始碼 2.5: 設定編輯器 |
export EDITOR=jpico |
除此之外,您也可以在命令列中直接以參數加入說明,如此 cvs 就不用載入任何的編輯器:
原始碼 2.6: 提交時送出小幅度的紀錄 |
# cvs commit -m 'I fixed a few silly bugs in portage.py' |
在繼續介紹更多的指令之前,我建議先設定 ~/.cvsrc。在您的家目錄中設定這個檔案之後,可以告訴 cvs 預設的指令參數,而不用去記得每次都要輸入他。底下建議一個 .cvsrc 的設定:
原始碼 2.7: 建議設定 |
cvs -qdiff -u -b -Bcheckout -Pupdate -d -P |
除了設定一系列 cvs 命令有用的參數,.cvsrc 第一行的指令讓 cvs 進入安靜模式,這會讓 cvs update 輸出比較一致且容易閱讀的文字,而且當您設定了 .cvsrc 之後,可以只打 cvs update 取代 cvs update -dP。
首先,用您喜歡的編輯器新增檔案,然後輸入下列的文字:
原始碼 2.8: 新增檔案 |
# cvs add myfile.ccvs server: use 'cvs commit' to add this file permanently |
這會告訴 cvs 在下一次執行 cvs commit 時新增這個檔案。在那之前,其他的發展者並不會看到。
加入目錄的過程也很類似:
原始碼 2.9: 新增目錄 |
# mkdir foo# cvs add fooDirectory /var/cvsroot/mycode/foo added to the repository |
不像新增檔案,當您加入一個目錄後他會立即在儲藏庫中顯示出來,一旦您將本機的目錄加入 cvs 後,您會發現 "CVS" 目錄會在那個目錄內產生來儲存 cvs 的資料。因此,您可以靠看裡面有沒有 "CVS" 目錄來判斷這個目錄是否加入 CVS 了。
對了,跟您猜的一樣,在把目錄或當案加入儲藏庫前,您要確定他的上層目錄已經加到 CVS 中。否則,您會得到像這樣的錯誤:
原始碼 2.10: 新增檔案,但傳回了錯誤 |
# cvs add myfile.ccvs add: cannot open CVS/Entries for reading: No such file or directorycvs [add aborted]: no repository |
在了解如何處理衝突之前,先來了解 "cvs update" 輸出內容。如果您的 ~/.cvsrc 中有 " cvs -q" 您會發現 "cvs update" 的輸出結果很容易閱讀,"cvs update" 靠輸出一個字母,空白,或是檔名來告訴您他作什麼以及觀察到什麼,如同底下的例子:
原始碼 2.11: 更新 CVS |
# cvs update -dP? distfiles? packages? profiles |
"cvs update" 用 "?" 告訴您關於那個檔案他並不知道任何事,那並不是在儲藏庫中的檔案,也尚未被安排加到儲藏庫中,底下是所有 CVS 所使用的單一字元的列表:
原始碼 2.12: 訊息: U |
U [path] |
當有一個檔案在本機上的儲藏庫被建立,或是一個您尚未建立的檔案被更新時使用這個字元。
原始碼 2.13: 訊息: A |
A [path] |
這個檔案已經被安排要加入並且將會在執行 cvs commit 時被加入。
原始碼 2.14: 訊息: R |
R [path] |
這個跟 "A" 差不多,"R" 告訴您這個檔案已經被安排要刪除了,當您執行 cvs commit 時,這個檔案將會從儲藏庫中移除。
原始碼 2.15: 訊息: M |
M [path] |
這代表這個檔案已經被您修改過。此外,這也可能是儲藏庫中作的改變已經成功的加到這個檔案。
原始碼 2.16: 訊息: C |
C [path] |
"C" 字元指出這個檔案有衝突,而且您必須要在您作 "cvs commit" 之前手動作一些調整。
現在我們來看看如何解決衝突。我參與 Gentoo Linux 計畫,而我們在 cvs.gentoo.org 設立我們自己的 cvs 伺服器,發展者花了大部份的時間修改 "gentoo-x86" 模組裡面的原始碼。在這個模組裡面,有一個叫做 "ChangeLog" 的檔案(您大概猜到)存放在這個儲藏庫裡面所有檔案的修改說明。
因為幾乎每位發展者在 CVS 上做修改時都會去修改這個檔案,這是主要的衝突來源。底下是一個衝突的範例,假設我在 ChangeLog 的頂端加入下面幾行:
原始碼 2.17: 更新紀錄 |
date 25 Feb 2001這是我自己加入的東西 |
然而,假設在我可以提交之前,另一位發展者在 ChangeLog 的頂端加入下面幾行並且提交了他的修改:
原始碼 2.18: 更新紀錄 2 |
date 25 Feb 2001這是另一位發展者加入的部份 |
現在當我執行 cvs update -dP (提交前您應該都做這個動作),cvs 並不能夠把他的修改合併到我的檔案當中,因為我們都加在這的檔案的相同部份 -- cvs 如何判斷使用那一個版本?所以,我得的了底下的錯誤:
原始碼 2.19: CVS 錯誤 |
RCS file: /var/cvsroot/gentoo-x86/ChangeLog,vretrieving revision 1.362retrieving revision 1.363Merging differences between 1.362 and 1.363 into ChangeLogrcsmerge: warning: conflicts during mergecvs server: conflicts found in ChangeLogC ChangeLog |
唉--有衝突!幸好這很容易修正。如果我用編輯器打開這個檔案,我在檔案頂端看到下列的文字:
原始碼 2.20: 更新紀錄衝突 |
<<<<<<< ChangeLogdate 25 Feb 2001這是我自己加入的東西=======date 25 Feb 2001這是另一位發展者加入的部份 >>>>>>> 1.363 |
cvs 不是以一個版本蓋掉另一個版本,而是在 ChangeLog 檔案中加入兩個版本,並且以特殊的分隔符號清楚的標記衝突的地方。現在,由您決定在這部份應該出現的內容,在這個範例中,取代後的文字並不是兩個版本中的一個,而是合併兩者:
原始碼 2.21: 更新紀錄 |
date 25 Feb 2001這是我自己加入的東西這是另一位發展者加入的部份 |
現在我已經用上列的文字取代衝突的部份(並且移除了 "=======" 之類的標記),我現在可以毫無問題的提交我的修改。
當您需要去編輯衝突的檔案時,確認您已經看過整個檔案來找到所有的衝突,如果有衝突您忘了修改,cvs 在您修改前並不允許您提交。移除 cvs 加到檔案中的標記也相當重要。還有另一個技巧,如果您在修正時犯了錯誤並且意外的儲存了,您可以在 ".#filename.version" 檔案中找到原始的備份版本。
現在是學習 CVS 技能最後一項--移除儲藏庫中的檔案。移除檔案是兩階段的過程。首先,刪除在本機上的檔案,然後執行 cvs remove 指令:
原始碼 2.22: 移除檔案 |
# rm myoldfile.c# cvs remove myoldfile.c |
這個檔案會被預定在下一次提交時從儲藏庫中刪除。一旦提交之後,檔案將會儲藏庫中移除,但是 cvs 不會把他丟棄,而會保存完整的內容及修改紀錄,以便未來當您需要時可以找到。這是 cvs 保護您的原始碼的其中一個方法。
cvs remove 是遞迴的,這代表您可以刪除一系列的檔案,然後執行 cvs remove 而不需要外加其他的參數。這樣的話會讓所有被刪除的檔案在下次提交時刪除。
如果您想要移除整個目錄,我建議您依照下列的步驟。首先,刪除在這個目錄中的所有檔案:
原始碼 2.23: 移除目錄 |
# rm *.c# cvs remove |
然後,執行提交:
原始碼 2.24: 提交改變 |
# cvs commit |
這裡有一個技巧。執行下列的步驟刪除目錄:
原始碼 2.25: 刪除目錄 |
# cd ..# cvs remove mydir# rm -rf mydir |
注意這裡移除目錄並不用執行提交,目錄立即從儲藏庫中移除。
CVS 的介紹已經完畢了,我希望這份教學很有用。有更多關於 CVS 的資料我可以加到這篇教學中,但是感謝這裡有許多很棒的 CVS 資源讓您可以獲得更多 CVS 的知識:
這分文件的原始版本是發表在 IBM developerWorks,所有權屬於 Westtech Information Services。這分文件是從他的原始版本修改而來,並且包含許多 Gentoo Linux 文件團隊做的改進。
==============================================