我們組要使用SVN共同維護(hù),我寫的一個說明文檔,大家多指正:)
前言:
我是SVN初級玩家,只是結(jié)合SVN官方的文檔以及網(wǎng)上的一點(diǎn)資料寫的這個文檔,目的是通過實(shí)際例子方便大家迅速掌握SVN,SVN官方文檔很厚,而常用命令并不多,沒有必要查看全部官方文檔,水平有限,請多指正。
一、SVN基礎(chǔ)介紹:
1.1 SVN是什么?
Subversion 是一個自由/開源的版本控制系統(tǒng)。也就是說,在 Subversion 管理下,文件和目錄可以超越時空。也就是 Subversion 允許你數(shù)據(jù)恢復(fù)到早期版本,或者是檢查數(shù)據(jù)修改的歷史。正因?yàn)槿绱?/span>,許多人將版本控制系統(tǒng)當(dāng)作一種神奇的“時間機(jī)器”。
SVN確實(shí)可以像一個時間機(jī)器一樣,回到任意時刻的版本,查看任意兩個時刻的版本變動,不止在協(xié)同開發(fā)中,即使在個人開發(fā)過程中,這種特性都是非常非常有用的,我曾經(jīng)有過這種經(jīng)歷,對代碼進(jìn)行很多的修改,發(fā)現(xiàn)修改的想法根本是錯誤的,而這時我已經(jīng)修改了多個文件,要想回退是非常糾結(jié)的事情,而現(xiàn)在可以使用SVN輕松做到這一點(diǎn)。
1.2 SVN是“正確的工具嗎”?
如果你是一個考慮如何使用 Subversion 的用戶或管理員,你要問自己的第一件事就是: "這是這項(xiàng)工作的正確工具嗎?",Subversion 是一個夢幻般的錘子,但要小心不要把任何問題當(dāng)作釘子。如果你希望歸檔文件和目錄舊版本,有可能要恢復(fù)或需要查看日志獲得其修改的歷史,那么Subversion 是你需要的工具。如果你需要和別人協(xié)作文檔(通常通過網(wǎng)絡(luò))并跟蹤所做的修改,那么Subversion 也適合。這是 Subversion 為什么使用在軟件開發(fā)環(huán)境—編程是天生的社會活動,Subversion 使得與其他程序員的交互變得簡單。當(dāng)然,使用 Subversion 也有代價:管理負(fù)擔(dān)。你會需要管理一個存放所有歷史的數(shù)據(jù)版本庫,并需要經(jīng)常的備份。而在日常的工作中,你不能像往常一樣復(fù)制, 移動, 重命名或刪除文件,相反,你需要通過 Subversion 完成這些工作。
1.3 SVN的架構(gòu)
SVN是一個典型的C/S架構(gòu),服務(wù)端維護(hù)全部數(shù)據(jù)以及更改的記錄,客戶端維護(hù)一個本地的工作副本。
1.4 SVN的“拷貝-修改-合并”方案(圖片順序先橫向后縱向)
1.3 推薦的SVN布局安排
SVN服務(wù)器對布局沒有任何限制,但是一種推薦的目錄組織形式(我個人感覺比較好)是:
/
calc/
trunk/
tags/
branches/
calendar/
trunk/
tags/
branches/
…
其中/是主文件系統(tǒng),cal和calendar是兩個項(xiàng)目,在每個項(xiàng)目下有trunk、tags、branches文件夾其中:
trunk保存的是主開發(fā)版
tags的是一些快照,比如發(fā)布1.0版本的時候,把trunk中的內(nèi)容復(fù)制到tags/release-1.0下,然后可以對trunk內(nèi)代碼進(jìn)行繼續(xù)開發(fā),不耽誤用戶使用1.0的穩(wěn)定版
branches保存分支版本,比如一個項(xiàng)目的開發(fā)過程中,我想加入一些新特性或者
功能,這些功能又不是短時間能夠完成的,但我又不想耽誤到其他成員對主版本的開發(fā),這是我需要在branches里建立一個分支,這個分支可以隨時融合主版本的最新更改,保持同步,這樣的優(yōu)勢是,比如其他成員對主版本一些bug進(jìn)行了修復(fù),我可以及時融入到我的分支版本中,最后在我的分支版本完成后,把分支版本融入到trunk版本中,并刪除分支版本。
二、SVN實(shí)踐
2.1 準(zhǔn)備工作
首先安裝SVN客戶端、服務(wù)端軟件??蛻舳塑浖脕砭S護(hù)和管理SVN本地代碼,并與服務(wù)端軟件交互,服務(wù)端軟件用來建立一個SVN服務(wù)器,通常來說我們實(shí)驗(yàn)室的SVN服務(wù)器由實(shí)驗(yàn)室網(wǎng)管管理,我們不用負(fù)責(zé)服務(wù)端,但是這里為了練習(xí)需要,需要在本地建立一個測試服務(wù)端。另外這里只討論基于命令行的應(yīng)用。
2.2 建立一個服務(wù)端
首先在本地建立一個測試項(xiàng)目proj1
然后建立一個服務(wù)端(這個報告里面唯一需要使用服務(wù)端的地方),使用命令:
svnadmin create repos
2.3 把本地項(xiàng)目導(dǎo)入服務(wù)端
注意-m后面加入一句話作為你的注釋,如果你覺得一句話不夠,可以不加-m,svn會引導(dǎo)你進(jìn)入默認(rèn)的編輯器中編輯你的注釋。SVN中的注釋非常非常重要,他可以讓其他程序員(包括你自己)在不閱讀很多代碼的情況下了解改動,任何優(yōu)秀的版本管理軟件也比不上程序員的注釋!
這里把proj1導(dǎo)入了服務(wù)器之后,proj1目錄并不能和svn服務(wù)器溝通,因?yàn)樗皇且粋€工作副本。仍然需要導(dǎo)出一個工作副本和服務(wù)器進(jìn)行通信:
并進(jìn)入到導(dǎo)出的目錄中,開始進(jìn)一步的練習(xí)。
2.4 SVN基本操作
這里插兩個SVN操作的原則先:
所有update(更新本地副本,從服務(wù)器端“拉”數(shù)據(jù))和commit(提交本地修改到服務(wù)器,向服務(wù)器端“推”數(shù)據(jù))都是原子的,要么所有不同文件的變更結(jié)束,要么所有變更都不發(fā)生。
“推”動作不會導(dǎo)致“拉”,反之亦然,因?yàn)槟銣?zhǔn)備好了提交你的修改并不意味著你已經(jīng)準(zhǔn)備好了從其他人那里接受修改.
下面開始熟悉SVN的基本操作
1)為了達(dá)到測試協(xié)同開發(fā)的效果,我在2.3步驟中導(dǎo)入一個本地副本myprj后,我再次導(dǎo)入一個本地副本myprj2,使用命令:
svn checkout file:///tmp/repos/trunk myproj2
2)我首先在myprj目錄下更改文件a.txt,然后查看了本地的文件情況,最后向服務(wù)器提交我的更改:
運(yùn)行語句svn status,看到在a.txt前標(biāo)示了一個M,M表示這個文件被修改了。
3)去myprj2目錄下,因?yàn)檫@個目錄的文件已經(jīng)舊了我需要跟服務(wù)器同步一下,以保持我本地副本是最新的
這里請注意版本號的變化,第一次導(dǎo)出的時候是revsion1,現(xiàn)在經(jīng)過修改后最新的版本號是2,以后會用到基于版本號恢復(fù)文件的操作。
1)增加刪除文件以及目錄管理
首先我創(chuàng)建了一個aa.txt文件,然后我把aa.txt加入到svn版本管理中來,并把以前的a.txt刪除了,又把c.txt復(fù)制了一份到cc.txt,建立了一個自文件夾subdir,你也可以直接用操作系統(tǒng)命令建立文件夾,再使用svn add把文件夾加入到版本管理中來。最后別忘了commit。
2)查看變更
首先我修改了cc.txt文件,然后使用了svn diff命令查看我做了哪些修改,可以看到我在cc.txt文件中加入了一句“hello cc”,這是以補(bǔ)丁的形式給出的所以我可以把它重定向?yàn)橐粋€補(bǔ)丁文件,我還是用了svn status查看哪些文件被修改了。
3) 解決沖突
我首先提交了我之前的所有變更,然后進(jìn)入到myprj目錄進(jìn)行了update,然后修改了myprj中的aa.txt文件并commit,之后回到myprj2目錄修改aa.txt,并直接commit,這時commit報錯了,提醒我我的版本號太低了,這種情況必須先update到最新的版本,然后才能提交新的變更(見SVN的“拷貝-修改-合并”方案圖)。所以我執(zhí)行了svn update命令,然后發(fā)現(xiàn)了aa.txt和我的本地經(jīng)過修改的aa.txt沖突了。這時SVN給了我?guī)讉€選擇:
p,稍后處理,我個人認(rèn)為這個是最理想的方案,你可以通過svn log aa.txt命令發(fā)現(xiàn)到底是誰修改了aa.txt這個文件,找到那個人溝通一下然后手動合并你們的內(nèi)容,再提交。
df可以通過這個命令顯示你的版本和服務(wù)器版本到底有什么不同(以補(bǔ)丁的形式)。
e可以進(jìn)行一些編輯,svn會幫助你保存到本地文件
mc是以我的當(dāng)前副本的為準(zhǔn),無視覆蓋別人的改動
tc是以服務(wù)器上的版本為準(zhǔn),放棄我的改動。
假設(shè)我之前選擇了延后處理沖突,并和其他程序員協(xié)商之后,由我手動修改aa.txt:
我修改aa.txt之后,告訴svn我已經(jīng)解決了沖突,然后commit
6)查看日志
就像前面提到的,日志是非常重要的一環(huán)??梢允褂?/span>svn log來查看所有的log記錄,然后可以通過-r 選項(xiàng)指定一個區(qū)間的日志內(nèi)容查看HEAD是指最新的log,也可以加一個文件名,只看和這個文件相關(guān)的log
7)使用diff比較不同
前面已經(jīng)介紹過使用svn diff查看本地的修改(以補(bǔ)丁形式)。svn diff還可以比較工作副本和版本庫中某一特定版本的不同,也可以比較版本庫中不同版本的異同。
8)查看svn版本庫
Svn cat filename命令可以查看當(dāng)前版本的filename中的內(nèi)容,加上-r之后可以查看在指定版本中,filename文件的內(nèi)容。
svn list http://svn.collab.net/repos/svn可以查看svn目錄的內(nèi)容,但是現(xiàn)在我們通過http通信配置的svn都是可以在瀏覽器里直接訪問的,所以這個命令在我們的應(yīng)用中就有點(diǎn)多余了。
9)獲得舊版本的快照:
svn checkout -r 1729 # Checks out a new working copy at r1729
svn update -r 1729 # Updates an existing working copy to r1729
第一個命令是導(dǎo)出給定版本的快照,第二個是把本地副本變成給定版本的工作副本。
10)創(chuàng)建并導(dǎo)出分支
使用前面介紹過的svn copy命令創(chuàng)建一個分支,然后使用checkout導(dǎo)出一個分支的副本,這樣你就可以在這個分支的副本上工作了。
11)和trunk保持同步
如果只在分支上開發(fā),而分支又另外一些人在繼續(xù)維護(hù)和開發(fā),你們的差別肯定是越走越遠(yuǎn),而且你的分支版本畢竟是從主版本走出去的,后續(xù)在主版本上的進(jìn)一步開發(fā)和維護(hù)很可能你是非常需要的(比如主版本上bug的修復(fù)),因此經(jīng)常和主版本保持一致是很重要的,而在和主版本的同步過程中,你的分支文件和主版本有沖突是非常正常的(很可能的情況是不同的人為不同的目的開發(fā),沖突是必然的)。這是你只要自己修改就行,你的修改不會更改trunk版本,僅僅影響你自己的分支版本。
這里是一個和主版本同步的例子,myprj2是主版本的副本,在這里修改了b.txt并提交到svn服務(wù)器中。然后在branch-new(分支版本的副本)中進(jìn)行了merge,注意這里沒有發(fā)生任何沖突,如果發(fā)生沖突,解決方案和前面5)介紹的解決方案相同。
12)分支融合入主版本
我在分支節(jié)點(diǎn)修改了c.txt(假設(shè)我完成了分支節(jié)點(diǎn)的開發(fā)),并最終commit了。在主版本的副本里,我使用命令trunk:svn merge --reintegrate ^/branches/branch-new進(jìn)行了融合。注意這里多使用了—reintegrate參數(shù),因?yàn)榘?/span>trunk融合入branch和把branch融入trunk是不同的,把trunk融入branch和正常的svn協(xié)同開發(fā)情況類似,在branch分支中添加別人的修改記錄,可以使用svn proget svn:mergeinfo .來查看本地branch副本和那些版本號的trunk進(jìn)行了融合,這里是保持了trunk版本號11和12的同步;而把branch融入trunk則不同,因?yàn)?/span>branch中之前已經(jīng)加入了trunk的修改情況,所以再次融合回trunk時就會讓svn服務(wù)器很困惑,所以必須加上—reintegrate參數(shù),讓svn服務(wù)器比對兩個版本代碼的不同(而不是使用補(bǔ)丁的方式)然后進(jìn)行合并。另外這里使用了一個—dry-run參數(shù),這個參數(shù)的意思是不進(jìn)行實(shí)際的融合操作,只是看看如果運(yùn)行融合可能會發(fā)生什么事情。
最后不要忘記把融合入trunk的branch版本刪除掉
三、更多SVN
請參見SVN官方文檔,另外可以多多使用svn help,如果對某一具體命令不清楚,比如add,可以使用svn help add進(jìn)行查看。
四、來自互聯(lián)網(wǎng)的一個SVN使用規(guī)范
在互聯(lián)網(wǎng)上搜的,說的挺好,可以作為參考
先更新,再提交
SVN更新的原則是要隨時更新,隨時提交。當(dāng)完成了一個小功能,能夠通過編譯并且自己測試之后,謹(jǐn)慎地提交。 如果在修改的期間別人也更改了svn的對應(yīng)文件,那么commit就可能會失敗。如果別人和自 己更改的是同一個文件,那么update時會自動進(jìn)行合并,如果修改的是同一行,那么合并時會產(chǎn)生沖突,這種情況就需要同之前的開發(fā)人員聯(lián)系,兩個人一起 協(xié)商解決沖突,解決沖突之后,需要兩人一起測試保證解決沖突之后,程序不會影響其他功能。 在更新時注意所更新文件的列表,如果提交過程中產(chǎn)生了更新,則也是需要重新編譯并且完成自己的一些必要測試,再進(jìn)行提交。這樣既能了解別人修改了哪些文件,同時也能避免SVN合并錯誤導(dǎo)致代碼有錯。
多提交
每次提交的間歇盡可能地短,以幾個小時的開發(fā)工作為宜。例如在更改UI界面的時候,可以每完成一個 UI界面的修改或者設(shè)計,就提交一次。在開發(fā)功能模塊的時候,可以每完成一個小細(xì)節(jié)功能的測試,就提交一次,在修改 bug的時候,每修改掉一個bug并且確認(rèn)修改了這個bug,也就提交一次。我們提倡多提交,也就能多為代碼添加上保險。
我個人感覺鼓勵多提交的同時,也不應(yīng)理解為在修改代碼的時候要不斷的提交,應(yīng)該保證修改的代碼可以編譯運(yùn)行,保證你提交之后,別人update之后別人的副本不能運(yùn)行,確保你修改作為一個小的整體提交,個人觀點(diǎn)
不要提交不能通過編譯的代碼
代碼在提交之前,首先要確認(rèn)自己能夠在本地編譯。如果在代碼中使用了第三方類庫,要考慮到項(xiàng)目組成員中有些成員可能沒有 安裝相應(yīng)的第三方類庫。項(xiàng)目經(jīng)理在準(zhǔn)備項(xiàng)目工作區(qū)域的時候,需要考慮到這樣的情況,確保開發(fā)小組成員在簽出代碼之后能夠 在統(tǒng)一的環(huán)境中進(jìn)行編譯。
每次提交必須書寫明晰的標(biāo)注
在一個項(xiàng)目組中使用SVN,如果提交空的標(biāo)注或者不確切的標(biāo)注將會讓項(xiàng)目組中其他的成員感到很無奈,項(xiàng)目經(jīng)理無法很清晰 的掌握工作進(jìn)度,無法清晰的把握此次提交的概要信息。在發(fā)現(xiàn)錯誤后也無法準(zhǔn)確的定位引起錯誤的文件。所以,在提交工作時,要填寫明晰的標(biāo)注,能夠概要的描述所提交文件的信息,讓項(xiàng)目組其他成員在看到標(biāo)注后不用詳細(xì)看代碼就能了解你所做的修改。
提交時注意不要提交本地自動生成的文件
例如eclipse中的.classpath文件,Windows生成的縮略圖Thumbs.db,項(xiàng)目編譯生成的臨時 文件.obj, .class等等。如果項(xiàng)目中沒有進(jìn)行這方面的配置來強(qiáng)行禁止提交這樣的文件,請自覺不要提交這樣的文件。提交了這樣的文件后,別人在更新后就可能與本地 的環(huán)境沖突從而影響大家的工作。
不要提交自己不明白的代碼
代碼在提交入SVN之后,你的代碼將被項(xiàng)目成員所分享。如果提交了你不明白的代碼,你看不懂,別人也看不懂,如果在以后 出現(xiàn)了問題將會成為項(xiàng)目質(zhì)量的隱患。因此在引入任何第三方代碼之前,確保你對這個代碼有一個很清晰的了解。