在linux環境下,cnix的開發中cvs的初步使用
本文主要講的是linux操作系統下,在cnix的開發中,cvs的初步使用。
一,什么是cvs? 為什么要使用cvs? cvs的作用。
CVS是Concurrent Version System(并行版本系統)的縮寫,用于版本管理,尤其在多人團隊合作的開發模式中作用巨大。
為什么要使用CVS呢? 如果各位有過團隊合作開發的經驗就會知道,在團隊合作開發中,要對整個項目的各個文件進行控制是多么繁瑣的一件事情,經常會出現不同的人修改了同一個程序,而需要人工合并整理。更糟糕的是,項目會出現多個版本,連開發者自己都不知道自己修改了那些文件,大量的精力消耗在各個版本,各個文件的整合中。這些精力完全可以避免,而省下更多的精力用在項目本身之中。
CVS還有一個更加重要的特性:能記下每個文件的每次修改,以及如何被修改。。。對于基于Internet 的合作方式來說,這些特性太重要了。一個地域上分散的自愿者組織顯然不可能投入很多的時間來訓練其成員彼此合作。因為這樣的話,當該組織有成員變更的時候,為此付出的投資將損失殆盡。所以需要指定一套基本的項目分配方案,以確保新成員能較容易的適應工作,同時也需要設置一個自動的系統來接受外來代碼,并使每個成員能及時得到最新修改的代碼。
CVS的特點--能通過網絡訪問源代碼(實際上我們后面會講到,就是拷貝一份源代碼到本地),
能同步開發,(廢話;)
能自動對修改進行合并。(這個好!!)
二,CVS的工作方式--"拷貝-修改-合并"模型。
CVS是這樣組織的:(假設基于網絡的合作模式,并且以cnix為例子)
遠程的gro.clinux.org上面保存著cnix的repository(源代碼庫),項目開發者擁有自己的一個 working copy(工作拷貝),這個工作拷貝是通過使用cvs checkout 命令從源代碼庫中申請得到。之后,項目開發者可以在自己的工作拷貝上進行修改,修改了之后可以進行提交( cvs commit)。由cvs負責對各個開發者所修改的部分進行整合。如下圖:
cnix(repository) | ---------+------------------------------------------------------------------ | | | | | cnix cnix ... (working copy) (working copy)
三,CVS基本術語
讓我們來了解一下CVS中會經常提到的一些術語:
Revision(修訂版本)--文件歷史記錄中的被開發者提交的變化。一個修訂版本就是一個時常變化的項目的snapshot(瞬態圖)。
Repository(源代碼庫)--CVS存儲所有修訂版本歷史記錄的地方。每個項目都有自己的一個確定的源代碼庫。(ft。。晦澀,簡單說,就是遠程gro上的cnix源代碼庫)
Working copy(工作拷貝)--開發者對文件作出修改時文件所在的拷貝(ft。。晦澀難懂,簡單來說,就是我自己機器上的一份遠程cnix的拷貝拉)
Check out(檢驗)--從源代碼庫中申請一份工作拷貝。該工作拷貝反映的是取出時項目的瞬時狀態。當開發者對拷貝作出修改時,必須運用commit(提交)和update(更新)命令來 "發布"變化和查看其他開發者所作的修改。
(簡單來說,就是你需要使用CVS中的check out命令來從gro上得到一份cnix的工作拷貝)
Commit(提交)--將工作拷貝中的變化輸入中央源代碼庫。
(簡單來說,就是如果你修改了你自己的工作拷貝,并且想發布這些修改,那么你需要使用 CVS中的commit命令提交這些修改到gro的cnix倉庫中;)
Log message(日志信息)--提交修訂版本的時候,附帶描述變化的注解。通過查閱記錄信息,人們可以獲得一個當前項目進程的總結。
(還是要簡單的來說一下,xixi, 你每次cvs commit的時候,它都會要求你輸入一些log,你可以寫一下你這次修改的目的,修改了那些地方。。。這樣做的好處是別人可以通過查看cvs log得知你的修改,還可以通過一個cvs到ChangeLog 的工具把這些Log自動的轉化成Gnu的ChangeLog格式)
Update(更新)--從源代碼庫中取出別人的修改數據,將其輸入自己的工作拷貝,并顯示自己的工作拷貝是否有未提交的修改。注意,不要和commit(提交)混淆,更新和提交是一對互補的指令。記住:Update將使工作拷貝和源代碼庫拷貝保持同步更新。
(簡單來說,就是是試圖使自己本地的工作拷貝更新到跟ropository一致的狀態。)
Conflicts(沖突)--倆個開發者對同一個區域所作的變化都提交給主版本時出現的情況,在 CVS覺察并指出這個沖突后,開發者必須解決該沖突。
(怎么解決,看下去,我會舉一個例子)
四,CVS中比較常用和簡單的命令介紹
(1),cvs import
使用這個命令進行源代碼的第一次導入。比如遠程gro中還沒有cnix的時候,需要使用:
[kai@kai cnix ]$ cd ~/Projects/cnix [kai@kai cnix ]$ cvs import -m "import cnix" cnix arfankai start把當前目錄下的項目導入到遠程gro的源代碼倉庫中,作為一個項目最初的開始。
(2),cvs checkout
具體使用的時候可以縮寫成cvs co。
使用這個命令從遠程的源代碼庫導出一份cnix的代碼到本地。比如:
[kai@kai / ]$ cd ~/ [kai@kai kai ]$ export CVS_RSH=ssh [kai@kai kai ]$ cvs -z3 -d arfankai@cvs.cnix.gro.clinux.org:/cvs/cnix co cnix cvs server: Updating cnix U cnix/Makefile U cnix/define.mk cvs server: Updating cnix/boot U cnix/boot/boot.S U cnix/boot/head.S cvs server: Updating cnix/driver U cnix/driver/Makefile U cnix/driver/ide.c ... ...之后,在你的家目錄里將會看到一個cnix的目錄。
(3),cvs commit
具體使用的時候,可以縮寫成cvs ci。
使用這個命令把你修改的文件提交給遠程的cvs server。假設我修改了cnix/Makefile文件:
[kai@kai cnix ]$ cvs status Makefile ====================================================== File: Makefile Status: Locally Modified Working revision: 1.1.1.1 Repository revision: 1.1.1.1 /cvs/cnix/cnix/Makefile,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: (none) [kai@kai cnix ]$然后提交之~,如下:
[kai@kai cnix <118>]$ cvs ci -m "test by arfankai" Makefile Checking in Makefile; /cvs/cnix/cnix/Makefile,v <-- Makefile new revision: 1.2; previous revision: 1.1 done [kai@kai cnix <119>]$-m選項后面接的是log msg.
(4),cvs status
簡單的用法如(3)所示。用于看一個文件的狀態。
(5),cvs add
往源代碼庫里面增加一個文件或者是一個空的目錄名。注意:你使用這個命令的時候, 文件名或者是目錄名必須是已經在你的本地工作目錄中有了,然后才能添加。注意2:如果添加的是文件,那么你使用這個命令之后,并不會立刻在遠程repository中就會出現該文件,還需要使用cvs ci命令。如果添加的是目錄名,那么會立刻放映到遠程repository。
建立一個空的本地test文件:
[kai@kai cnix <173>]$ touch test把test文件加入到工作列表:
[kai@kai cnix <174>]$ cvs add test cvs server: scheduling file `test' for addition cvs server: use 'cvs commit' to add this file permanently使用cvs ci提交文件到遠程源代碼庫:
[kai@kai cnix <175>]$ cvs ci -m "add test" test RCS file: /cvs/cnix/cnix/test,v done Checking in test; /cvs/cnix/cnix/test,v <-- test initial revision: 1.1 done經過這樣倆步,文件tmp.c才真正的加入到了源代碼倉庫中。
(6),cvs delete
刪除一個文件或者目錄。
刪除一個文件的步驟分三步:
1),刪除本地工作目錄中的該文件。(我們以刪除剛才加入的test文件為例子)
[kai@kai cnix <181>]$ rm test
2),將刪除的文件加入到工作列表:cvs remove 文件名
[kai@kai cnix <182>]$ cvs remove test cvs server: scheduling `test' for removal cvs server: use 'cvs commit' to remove this file permanently
3),提交刪除動作到遠程源代碼庫:cvs ci 文件名
(從另外一個方面來說,添加和刪除都是一個"修改",所以他們對于源代碼庫來說是一樣的,都需要提交這些"修改")
[kai@kai cnix <183>]$ cvs ci -m "remove test" test Removing test; /cvs/cnix/cnix/test,v <-- test new revision: delete; previous revision: 1.1 done
(7),cvs update
更新本地工作拷貝。可以縮寫成cvs up
cvs update 文件名(目錄名)
或
cvs up 文件名(目錄名)
或
cvs up (不加參數,則會更新當前目錄所有的文件以及遞歸的子目錄文件)
如果要連源代碼庫中新增的目錄也下載,則要加上 -d 選項。
cvs up -d
舉例如下:
[kai@kai cnix <198>]$ cvs up cvs server: Updating . U test <---把源代碼庫中的test文件更新到本地。 cvs server: Updating boot <---依序檢查各個子目錄 cvs server: Updating driver cvs server: Updating fs cvs server: Updating include cvs server: Updating include/asm cvs server: Updating include/cnix cvs server: Updating init cvs server: Updating kernel cvs server: Updating lib cvs server: Updating mm cvs server: Updating shell cvs server: Updating shell/app cvs server: Updating tools
(8),cvs diff
比較版本差異。當發現本地工作拷貝和源代碼庫中的文件不一致時,可以了解差異之處。
cvs diff 文件名
或
cvs diff (不加文件名參數,表示遞歸的比對當前目錄所有的文件和文件夾)
(具體的例子我不舉了)
(9),cvs log
查詢記錄,可以縮寫成cvs lo
比如,查詢一下Makefile文件的記錄:
[kai@kai cnix <200>]$ cvs log Makefile RCS file: /cvs/cnix/cnix/Makefile,v Working file: Makefile head: 1.2 branch: locks: strict access list: symbolic names: start: 1.1.1.1 asmcos: 1.1.1 keyword substitution: kv total revisions: 3; selected revisions: 3 description: ---------------------------- revision 1.2 date: 2003/07/22 13:35:45; author: arfankai; state: Exp; lines: +1 -0 test by arfankai ---------------------------- revision 1.1 date: 2003/03/25 09:51:27; author: asmcos; state: Exp; branches: 1.1.1; Initial revision ---------------------------- revision 1.1.1.1 date: 2003/03/25 09:51:27; author: asmcos; state: Exp; lines: +0 -0 ======================================================
五,如何使用CVS進行cnix的開發。
(本來已經寫了好多了,突然停電。。。55)
在前一份文檔中,我已經講了一些本地ssh的設置的問題。請參考這里:
http://cnix.gro.clinux.org/doc/dev_cnix.htm 按照那份文檔設置好之后,我們可以使用cvs checkout出一份cnix的本地working copy.
[kai@kai / ]$ cd ~/ [kai@kai kai ]$ export CVS_RSH=ssh [kai@kai kai ]$ cvs -z3 -d arfankai@cvs.cnix.gro.clinux.org:/cvs/cnix co cnix cvs server: Updating cnix U cnix/Makefile U cnix/define.mk cvs server: Updating cnix/boot U cnix/boot/boot.S U cnix/boot/head.S cvs server: Updating cnix/driver U cnix/driver/Makefile U cnix/driver/ide.c ... ...之后,在你的家目錄里將會看到一個cnix的目錄。以后的所有修改都可以在這個目錄中進行。
現在假設一種情況:我把cnix checkout到本地之后,由于忙畢業設計的事情,一個禮拜沒有怎么看代碼。今天想起Makefile有一個bug,于是先在本地改正之~
準備提交之前,先用cvs update更新一下本地的working copy.
[kai@kai cnix <11>]$ cvs up cvs server: Updating . M Makefile <---本地修改了Makefile文件 cvs server: Updating boot cvs server: Updating driver cvs server: Updating fs cvs server: Updating include cvs server: Updating include/asm cvs server: Updating include/cnix cvs server: Updating init cvs server: Updating kernel cvs server: Updating lib cvs server: Updating mm U mm/page_alloc.c <---合作者新添加了page_alloc.c文件 P mm/memory.c <---合作者修改了memory.c文件 cvs server: Updating shell cvs server: Updating shell/app cvs server: Updating tools [kai@kai cnix <12>]$
可以看到,cvs會提示一些文件的狀態。比如Makefile文件前面有一個"M"標志,表示本地有修改,還沒有提交到源代碼庫。mm目錄下的page_alloc.c文件是新增加的("U"標志),可能是在這一個禮拜中的某個時候,xiexiecn或者是ps/2或者是asmcos或者是別的開發者添加了這個文件,所以現在運行cvs update 會update到本地。mm目錄下的memory.c 可能被其他的合作者進行了修改("P"標志),所以也會由cvs更新到本地。
運行cvs update之后,就能把源代碼庫中的更新的文件更新到本地。現在需要做的是把我自己的修改Makefile提交到源代碼庫。
[kai@kai cnix <13>]$ cvs ci -m "test" Makefile Checking in Makefile; /cvs/cnix/cnix/Makefile,v <-- Makefile new revision: 1.3; previous revision: 1.2 done [kai@kai cnix <14>]$
我們來考查幾種比較簡單的情況下,CVS的處理方式:
ps/2和xiexiecn各自都從gro的cvs源代碼庫中取了一份工作拷貝。ps/2隨意編輯自己的工作拷貝, xiexiecn也編輯自己的工作拷貝。
(1),ps/2修改的是mm/memory.c; xiexiecn修改的是kernel/sched.c。
ps/2修改好了memory.c,進行提交(cvs commit),xiexiecn也修改好了sched.c,進行提交(倆人提交的時間是獨立的,沒有聯系)。由于倆人修改的是不同的文件,所以互不相干。都能很好的將這些修改提交到repository,并可附上修改記錄(可多可少,不明確規定)。
假設ps/2先進行了提交,然后看電視去了:p,xiexiecn提交了sched.c之后,還想再工作一會,于是他先使用cvs status看一下,會發現memory.c已經被ps/2修改,可以使用
cvs update mm/memory.c
更新memory.c文件,使得自己的工作拷貝在最update的狀態。
(一般來說,一個開發者在決定修改之前,最好是先使用cvs status看一下想要修改的文件,如果別人已經有了修改,就可以先update一下,然后再在這個新版本的基礎上進行修改,這樣可以省卻很多麻煩:)
(2),某一時刻,ps/2和xiexiecn都對mm/memory.c產生興趣,各自在自己的工作拷貝上對這個文件進行了修改,但是ps/2修改的是文件開頭的幾行,比如加入了一些版權信息; xiexiecn修改的是文件的最后,比如添加了一個函數。一句話:他們修改的是一個文件的不同文本區。
然后他們分別進行提交,CVS也能很好的處理這些修改,并且把他們倆個人的修改自動的整合到repository中的memor.c,v中。(Great!!)
下次當ps/2又想對memory.c進行修改的時候,記得我剛才的建議嗎?:) 可以先使用
cvs status mm/memory.c
看一下這個文件的狀態,會發現需要update,于是使用
cvs update mm/memory.c
update之~,再在這個基礎上進行修改,以后會愉快得多:) 。。。
(3),某一時刻,ps/2和xiexiecn都對mm/memory.c產生興趣,各自在自己的工作拷貝上對這個文件進行了修改,并且他們修改的是同一個部分。比如在同一行各自加了條不一樣的注釋。 ps/2 加了一條:
//This is test.
xiexiecn加的是另外一條:
/* This is a test */
之后他們提交各自不同的修改,這樣將會產生沖突。我們假設ps/2先進行了這項提交,他會很順利的提交成功。然后當xiexiecn也試圖提交memory.c的修改時,CVS將會告訴他發現一個沖突(conflicts),并且提交不會成功。CVS會要求你先解決掉這個沖突(conflicts)。于是 xiexiecn可以先使用
cvs status mm/memory.c
看一下本地的memory.c和repository的memory.c的狀態報告。如下:
~/Projects/cnix/mm $ cvs status memory.c ======================================================= File: memory.c Status: Needs Merge(需要合并) Working revision: 1.14 Repository revision: 1.14 /cvs/cnix/cnix/mm/memory.c,v Sticky Tag: (none) Sticky Date: (none) Sticky Options: (none) ~/Projects/cnix/mm $
然后使用cvs update 命令先update到本地:
~/Projects/cnix/mm $ cvs update memory.c RCS file: /cvs/cnix/cnix/mm/memory.c,v retrieving revision 1.14 retrieving revision 1.15 Merging differences between 1.14 and 1.15 into memory.c (合并到本地的memory.c中) rcsmerge: warning: conflicts during merge (合并的過程中發現有沖突) cvs server: conflicts found in memory.c C memory.c ~/Projects/cnix/mm $
可以發現,cvs 提示合并的過程中發現沖突(conflicts)。
我們看一下memory.c文件的相應部分,就可以發現,memory.c文件對應部分變成這樣:
<<<<<<< memory.c /* This is a test */ ======= //This is test. >>>>>>> 1.15
這就是沖突標記。
xiexiecn需要在整理這個沖突之后(比如只留下這一行: /* This is a test */) 再提交一個已解決沖突的新版本。也許xiexiecn和ps/2需要彼此交流一下以解決分歧。
六,總結
綜上,CVS的基本使用是非常簡單的,而CVS在自由軟件中的作用卻是巨大的。可以這么說,如果不學會CVS的基本使用,不會理解到合作開發的樂趣。
希望各位能從cnix中得到自己想要的東西。
參考資料:
1,CVS開源軟件開發技術,Karl Fogel 著,肖虎勤 陳軍等翻譯,機械工業出版社 2,http://linux.tnc.edu.tw/techdoc/cvs/book1.html (簡單參考手冊,很不錯) 3,http://www.soforge.com/cvsdoc/zh_CN/book1.html (詳細手冊) 4,http://www.uml.net.cn/sjms/sjms051612.htm 5,http://www.igenus.org/resource/CVS/cvs.txt 還有很多很多,google一下吧,別偷懶:)