別人的 svn 筆記 ~
Posted on 2010-09-25 20:36 幻海藍(lán)夢(mèng) 閱讀(2217) 評(píng)論(0) 編輯 收藏 所屬分類(lèi): Linux 、版本管理------------------ 參考文檔 --------------------
官方文檔: http://www.subversion.org.cn/svnbook/ 包括可下載的PDF 和一頁(yè)HTML。
配置文檔: 去ubuntu的wiki吧~~ 如果用 svn + ssh 的話(huà),基本不用配置,按下面“快速工作”即可。
------------------ 快速工作: -------------------
1. 建立版本庫(kù):
我的方式,是用svn+ssh來(lái)訪問(wèn)版本庫(kù),這意味著:
不用啟動(dòng)svnserve守護(hù)進(jìn)程,而是由每次ssh連接到版本庫(kù)時(shí),自動(dòng)啟動(dòng)svnserve,工作完再退出svnserve進(jìn)程。這些東西,都是本地的svn調(diào)用ssh讓它那樣做的。而啟動(dòng)的svnserve進(jìn)程的身份就是ssh用戶(hù)的身份,而ssh用戶(hù)來(lái)自于系統(tǒng)用戶(hù),這就是這種方法的優(yōu)點(diǎn):就用系統(tǒng)用戶(hù)了。
正因?yàn)槿绱耍總€(gè)用戶(hù)是否對(duì)版本庫(kù)能讀寫(xiě),就依賴(lài)于版本庫(kù)的權(quán)限設(shè)置,就是你用svnadmin create生成的文件的權(quán)限設(shè)置。
我的策略是,為了讓某些用戶(hù)讀寫(xiě)版本庫(kù),我創(chuàng)建了一個(gè)svn用戶(hù),和一個(gè)svn組,然后在/home/svn下建立版本庫(kù)。然后把所有需要訪問(wèn)svn的用戶(hù),加到svn組中,并設(shè)置版本庫(kù)的所有文件,svn組的人都能夠讀寫(xiě)。
在版本庫(kù)機(jī)器上,具體如下:
adduser svn # 創(chuàng)建svn用戶(hù)和svn組
gpasswd -a fengyi svn # 把fengyi用戶(hù)加到svn組
svnadmin create /home/svn/buptnic # 創(chuàng)建版本庫(kù)buptnic
chmod -R g+w /home/svn/buptnic # 讓所有的文件為組可寫(xiě)(因?yàn)槟J(rèn)一般是組可讀了)
以上兩步,也可以改為,先用umask 0002,再用svnadmin 一樣的效果。因?yàn)槟J(rèn)的umask一般是0022,這會(huì)去掉組的寫(xiě)權(quán)限。
注意:一旦svnadmin create之后,其他的事情,就應(yīng)該用svn在工作拷貝機(jī)器上做了?!?要點(diǎn)是,大部分svn的子命令,最終效果都是操作版本庫(kù),但是分兩種途徑。如果子命令帶URL的,一般對(duì)版本庫(kù)直接操作,如果不帶URL的,一般是先對(duì)工作拷貝進(jìn)行操作,然后用commit使版本庫(kù)生效。當(dāng)然,好多命令,即可以帶URL,也可以不帶,一般都是先update,再更改工作拷貝,再commit。
在工作拷貝機(jī)器上,具體如下:
svn mkdir svn+ssh://svn@210.25.137.252/home/svn/buptnic/flow # 建立flow項(xiàng)目目錄
svn mkdir svn+ssh://svn@210.25.137.252/home/svn/buptnic/flow/trunk # 建立flow/trunk目錄
svn mkdir svn+ssh://svn@210.25.137.252/home/svn/buptnic/flow/branches # 建立flow/branches目錄
svn mkdir svn+ssh://svn@210.25.137.252/home/svn/buptnic/flow/tags # 建立flow/tags目錄
svn import /home/echo/src svn+ssh://svn@210.25.137.252/home/svn/buptnic/flow/trunk # 將本地的src下的源文件導(dǎo)入到版本庫(kù)的trunk目錄下。注意,trunk是主分支,一般習(xí)慣是放任何東西,包括文檔,第三方庫(kù)。
然后,就OK了,你可以找個(gè)地方,用fengyi用戶(hù)做checkout了。
svn checkout svn+ssh://fengyi@210.25.137.252/home/svn/buptnic/flow
注意的是:版本庫(kù)中,雖然你通過(guò)mkdir建立了那些目錄,上傳了那些文件,但是,你是在/home/svn/buptnic/flow中直接看不到的,它們都放在flow 下的db目錄中,是虛擬文件系統(tǒng)里。
2. 用戶(hù)基本操作原則:
(1) 不管是 版本庫(kù) 還是 工作拷貝,他們的維護(hù)文件,都在他們的文件夾內(nèi),所以,如果要?jiǎng)h除他們,直接用文件系統(tǒng)的rm即可徹底刪除。
(2) 用svn子命令,對(duì)版本庫(kù)的直接操作,必須用一種形式的URL,而不能是普通文件路徑,包括在版本庫(kù)機(jī)器上?。。。?!但是svnadmin等命令,就是直接用普通文件路徑來(lái)操作版本庫(kù),并要求在版本庫(kù)機(jī)器上執(zhí)行。
(3) 在工作拷貝的哪個(gè)目錄執(zhí)行 svn 子命令,該命令的最后一個(gè)參數(shù),默認(rèn)就是當(dāng)前目錄,這使得命令的效果從當(dāng)前目錄開(kāi)始作用。
這從每個(gè)目錄里有一個(gè).svn看得出來(lái),每個(gè)目錄都有維護(hù)文件,都可以單獨(dú)工作。這也是工作拷貝的不同部分允許不同版本號(hào)的基礎(chǔ)。
(4) commit只有當(dāng)當(dāng)前目錄下所有文件的版本號(hào) 等于 版本庫(kù)當(dāng)前版本號(hào)時(shí),才能成功。
工作拷貝的不同部分允許不同版本號(hào),所以,當(dāng)你commit時(shí),可能當(dāng)前目錄下,比如各個(gè)子目錄里 的版本號(hào)并不同,如果有比版本庫(kù)低的版本號(hào),commit就不能成功,提示信息是 ... out of date ...,這時(shí),你需要update一次,然后,再commit。當(dāng)然,其他很多情形,也可能導(dǎo)致這種情況,最典型的就是(5)中文件沖突的情況。
(5) 先在工作拷貝上操作,然后commit到版本庫(kù)。
除了修改文件以外的任何操作,都用svn子命令完成,比如添加一個(gè)文件,創(chuàng)建之后,用svn add添加到本地拷貝。當(dāng)你覺(jué)得本次的工作拷貝工作結(jié)束時(shí),可以將其commit到版本庫(kù),使得版本庫(kù)更新到新的revision。
(6) 解決沖突
解決沖突最好的辦法,就是兩個(gè)人不要編輯同一個(gè)文件。否則,見(jiàn)下面或資料,你就需要update一次,來(lái)合并兩人的文件(因?yàn)閯e人先commit了,版本庫(kù)里的版本號(hào)比自己的高,自己不會(huì)commit提交成功,需要先update)。如果需要手工解決沖突,見(jiàn)下面的解決辦法。
(7) 是的,如果兩個(gè)分支獨(dú)立發(fā)展太久,則必然改動(dòng)較大,那merge是會(huì)有沖突的,你還是要手動(dòng)解決。
merge和update沖突時(shí),生成的沖突臨時(shí)文件名不同,但是源文件,都是被沖突標(biāo)志標(biāo)注了的,你只需要改源文件即可。里面<<<到>>>部分,是沖突區(qū)域,即svn認(rèn)為,在這個(gè)區(qū)域上,兩個(gè)文件各自發(fā)展了自己的修改,所以第一個(gè)文件的修改,放到<<<和===中,第二文件的修改,放到===和>>>中,你自己選擇,保留哪個(gè)修改。如果某個(gè)修改為空,即如===和>>>中沒(méi)有代碼,那說(shuō)明“修改”就是“刪除”操作。
所以,當(dāng)我們把branches的文件merge到trunk時(shí),得考慮清楚了,因?yàn)閎ranches在修改bug,而trunk在增加新特性,為了把branches的bug修改通過(guò)merge引入trunk,是很可能導(dǎo)致沖突的。
(8) revert 和 merge
revert是在版本內(nèi)恢復(fù),即:當(dāng)commit后,就是一個(gè)版本了,之后,如果更改了任何東西,但是還沒(méi)有commit的話(huà),就可以恢復(fù)到上次commit后的狀態(tài)。revert不依靠版本庫(kù),僅靠.svn目錄來(lái)維護(hù)信息。
而merge,則是依賴(lài)版本庫(kù),做的則是版本間的差異比較,從而作用于某個(gè)目錄。使得內(nèi)容恢復(fù)到某個(gè)版本狀態(tài)。
(9) 忘了的命令,用svn help subcmd 來(lái)查閱。
(10) diff, status, log 怎么用阿?
下面是trunk目錄下,svn status -v 的輸出:
28 28 fengyi .
28 26 fengyi sheep
28 26 fengyi sheep/sheep.c
28 20 svn main.c
28 28 fengyi cow
28 28 fengyi cow/main2.c
注意到:第一列是當(dāng)前工作拷貝的版本號(hào),是因?yàn)樵趖runk update后導(dǎo)致所有文件的版本號(hào)變?yōu)樽钚?。但是,后面的一列?shù)字,表明自那個(gè)版本號(hào)以來(lái),文件沒(méi)有改過(guò)(不保證版本庫(kù)沒(méi)有更新)。你用svn log看那個(gè)文件,也只能看到那個(gè)號(hào)版本,看不到當(dāng)前工作拷貝的版本號(hào)(沒(méi)改,沒(méi)commit,怎么可能有l(wèi)og!)
-------------------- 筆記 ---------------------
第一章 基本概念
svn就是能夠跟蹤數(shù)據(jù)變化的文件服務(wù)器
“拷貝-修改-合并”模型而不是“鎖定-修改-解鎖”(見(jiàn)保存的圖)
每當(dāng)版本庫(kù)由用戶(hù)commit了一個(gè)提交,文件系統(tǒng)進(jìn)入了一個(gè)新的狀態(tài),叫做一次修訂(revision),每一個(gè)修訂版本被賦予一個(gè)獨(dú)一無(wú)二的自然數(shù),commit顯然是原子操作。同時(shí),可以看出,版本庫(kù)沒(méi)有回退的概念,只有工作拷貝有這個(gè)概念,版本庫(kù)只不過(guò)一直在往前走。
(許多其它版本控制系統(tǒng)使用每文件一個(gè)修訂號(hào)的策略,所以會(huì)感覺(jué)這些概念有點(diǎn)不一樣。)
SVN概念中,版本庫(kù)的修訂版本是統(tǒng)一的,就是說(shuō),任何時(shí)候,每個(gè)文件的“修訂版本”都是一樣的。
但是工作拷貝中經(jīng)常是“混合修訂版本”,因?yàn)槟阒惶峤灰粋€(gè)文件,導(dǎo)致版本庫(kù)中修訂版本加1,這時(shí),你的工作拷貝中這個(gè)文件的修訂版本也加1,但是工作拷貝中其他的文件,只能維持原版本號(hào),除非你update一次。
這是很有用的一個(gè)特性,它允許工作拷貝的任何一個(gè)部分在歷史中前進(jìn)或后退。
svn有一個(gè)基本原則就是“推”動(dòng)作不會(huì)導(dǎo)致“拉”,反之亦然。
svn commit & svn update 和 四種狀態(tài) (是否修改+是否當(dāng)前)
svn status --verbose 檢查“混合修訂版本”
svn log 顯示一個(gè)文件或目錄的歷史修改信息。
$ svn checkout http://svn.example.com:9834/repos
$ svn checkout file:///path/to/repos
$ svn checkout file://localhost/path/to/repos
C:\> svn checkout file:///X:/path/to/repos
$ svn checkout http://svn.example.com/repos/calc
$ svn commit button.c -m "Fixed a typo in button.c."
$ svn update
Subversion可以通過(guò)多種方式訪問(wèn)版本庫(kù)!!
———— 一個(gè)重要意思是說(shuō):各種svn的子命令,都可以直接訪問(wèn)版本庫(kù)。如果你在版本庫(kù)機(jī)器上,那么就用file:///,如果你在自己機(jī)器上,就用下面四種方式。
———— 但是,你怎么只操作本地的工作拷貝呢,以為你想在本地改,改完后再commit,那就不要加這些東西了,直接是文件名,或者不加就好。
模式 訪問(wèn)方法
file:/// 直接版本庫(kù)訪問(wèn)(本地磁盤(pán))
http:// 通過(guò)配置Subversion的Apache服務(wù)器的WebDAV協(xié)議
https:// 與http://相似,但是包括SSL加密。
svn:// 通過(guò)svnserve服務(wù)自定義的協(xié)議
svn+ssh:// 與svn://相似,但通過(guò)SSH封裝。
但是注意:一般的做法,還是先update,merge,copy等命令把版本庫(kù)數(shù)據(jù)弄到工作拷貝,修改好工作拷貝后,再commit,這會(huì)導(dǎo)致版本庫(kù)的進(jìn)入下一個(gè)revision。
如果直接在版本庫(kù)中操作的,比如copy,delete等,也會(huì)使得版本庫(kù)進(jìn)入下一個(gè)reversion.
第二章 基本使用
svn help SUBCOMMAND
1. 創(chuàng)建版本庫(kù)
創(chuàng)建新的版本庫(kù)名 newrepos
$ svnadmin create /usr/local/svn/newrepos
將mytree下未版本化的文件,導(dǎo)入該版本庫(kù),文件放在some/project下,沒(méi)有mytree文件夾了。
$ svn import mytree file:///usr/local/svn/newrepos/some/project -m "Initial import"
注意:import后的路徑,可以在版本庫(kù)上,也可以在自己機(jī)器上。一般,可以在自己機(jī)器上的代碼,直接這樣導(dǎo)入服務(wù)器上的版本庫(kù)。
列出newrepos版本庫(kù)中某文件夾下的文件。
$ svn list file:///usr/local/svn/newrepos/some/project
推薦的版本庫(kù)布局: 這里有paint和calc兩個(gè)項(xiàng)目
$ svn list file:///usr/local/svn/repos
/paint/trunk
/paint/branches
/paint/tags
/calc/trunk
/calc/branches
/calc/tags
/vender/vendorlib1/current #供方最新版本
/vender/vendorlib1/1.0 #上次最新版本的快照(svn copy)
注意:文件夾用 svn mkdir 創(chuàng)建。看你在版本庫(kù)主機(jī)上還是工作拷貝主機(jī)上,選用上面某一個(gè)URL可以對(duì)版本庫(kù)進(jìn)行操作。
2. 初始化檢出
在本地創(chuàng)建一個(gè)工作拷貝,這個(gè)拷貝可能僅僅是一個(gè)版本庫(kù)中的一個(gè)文件夾。
$ svn checkout http://svn.collab.net/repos/svn/trunk
同上,只不過(guò),在本地創(chuàng)建的是subv目錄,而不是trunk目錄。
$ svn checkout http://svn.collab.net/repos/svn/trunk subv
3. 基本的工作周期
更新你的工作拷貝
svn update
做出修改: 只有修改一個(gè)文件,可以直接修改,其他任何對(duì)文件和目錄的操作,均是先通過(guò)以下命令在工作拷貝生效,然后,你下次update的時(shí)候,才在版本庫(kù)生效。這種狀態(tài),常見(jiàn)英文為: sth is scheduled for addition.(比如這個(gè)文件是add處理的)
svn add foo (先vim foo,然后用add命令添加)
svn delete foo (但是只有在update時(shí),版本庫(kù)和工作拷貝內(nèi)的刪除才會(huì)生效)
svn copy foo bar
svn move foo bar
svn mkdir foo (等于mkdir foo; snv add foo)
檢驗(yàn)修改: 在你提交你的工作拷貝時(shí),檢查一下修改了哪些東西是個(gè)好習(xí)慣,這不需要網(wǎng)絡(luò)(不需要和版本庫(kù)比對(duì))
svn status (瀏覽所做的修改,傳說(shuō)中用的最多的命令)
-v 顯示所有文件
-u 這個(gè)文件常用于預(yù)測(cè)update前的沖突。會(huì)聯(lián)系版本庫(kù),看是否工作拷貝里,有過(guò)期的文件,以 * 標(biāo)注。
可能的狀態(tài)碼為:
A # file is scheduled for addition
C # file has textual conflicts from an update
D # file is scheduled for deletion
M # the content in bar.c has local modifications
? # 這個(gè)文件還沒(méi)有版本化,你需要用svn add
svn diff (檢查修改的詳細(xì)信息,通過(guò) svn diff > patchfile 可以生成補(bǔ)丁文件)
可能會(huì)取消一些修改
svn revert (任何修改,然后用revert就可以撤銷(xiāo)任何schedule 的操作,即上次update以來(lái) schedule的操作)
解決沖突(合并別人的修改)
svn update (只要版本庫(kù)被別人修改,你鐵定不能commit,必須update一次,而update會(huì)嘗試合并被人修改過(guò)的版本庫(kù)和工作拷貝,生成新的工作拷貝,下面U表示更新(你沒(méi)有修改,但是版本庫(kù)被修改),G表示合并(你修改了,版本庫(kù)也修改了,兩者可以合并),C表示沖突(你修改了,版本庫(kù)修改了,兩者不能按照一定規(guī)則合并)。如果是C存在,那么你必須解決沖突,svn才能允許你下次commit。如果有沖突,那么對(duì)應(yīng)bar.c 會(huì)有三個(gè)臨時(shí)文件被生成,它們是bar.c.mine, bar.c.rR1(你上次update時(shí)的文件), bar.c.rR2(版本庫(kù)中的最新版本號(hào)的文件)。這個(gè)三個(gè)臨時(shí)文件,會(huì)阻止你commit。此時(shí)的svn只是要求你考慮下更改沖突的文件,如果考慮好了,用svn resolved bar.c來(lái)告訴svn,你改好了,然后resolved會(huì)自動(dòng)刪除這三個(gè)臨時(shí)文件,然后,你再commit,就行了!所以,svn只是要求你考慮清楚后,刪除這三個(gè)文件,它就可以commit了,不管你到底是不是真的改恰當(dāng)了,這還是你的事,所以,如果不用svn resolved,而是直接刪除這三個(gè)文件,也是可以commit的。另外,這三個(gè)文件,可以用于你查閱,如果愿意的話(huà)。
更改沖突文件,通??梢裕褐苯泳庉嫑_突文件;用bar.c.rR2(R2 > R1)來(lái)替換bar.c;或者直接用svn revert來(lái)恢復(fù)文件到上次update時(shí)的狀態(tài)。如果是前面兩種方法,要接上svn resolved來(lái)告訴svn,沖突已解決好了,可以允許自己commit了。
$ svn update
U INSTALL
G README
C bar.c
Updated to revision 46.
svn resolved
提交你的修改
只有當(dāng)你的工作拷貝的版本號(hào)=版本庫(kù)版本號(hào)時(shí),才能提交成功?。。。ㄐ∮诰偷孟萿pdate,大于是不可能的)
svn commit -m "message"/ -F logfile
4. 檢驗(yàn)歷史:
(1) svn log (從下面就看出每次 commit時(shí) -m 的重要性了,對(duì)于追溯很重要)
$ svn log
------------------------------------------------------------------------
r3 | sally | Mon, 15 Jul 2002 18:03:46 -0500 | 1 line
Added include lines and corrected # of cheese slices.
------------------------------------------------------------------------
r2 | harry | Mon, 15 Jul 2002 17:47:57 -0500 | 1 line
Added main() methods.
------------------------------------------------------------------------
r1 | sally | Mon, 15 Jul 2002 17:40:08 -0500 | 1 line
Initial import
------------------------------------------------------------------------
$ svn log -r 5:19 # shows logs 5 through 19 in chronological order
$ svn log -r 19:5 # shows logs 5 through 19 in reverse order
$ svn log -r 8 # shows log for revision 8
$ svn log foo.c
$ svn log http://foo.com/svn/trunk/code/foo.c
注意:
a. 加上 -v 能顯示文件和目錄的移動(dòng)和復(fù)制 等 “路徑修改” 信息,這可以用于找出刪除的項(xiàng)目。
b. 如果 svn log -r 8 沒(méi)有輸出信息,那請(qǐng)?jiān)诤竺娓霭姹編?kù)的路徑。
(2) svn diff
$ svn diff #比較工作拷貝和.svn中存的上次update時(shí)的版本。
$ svn diff -r 3 rules.txt #比較工作拷貝和版本庫(kù)3版
$ svn diff -r 2:3 rules.txt #比較版本庫(kù)的2版和3版。
(3) svn cat
$ svn cat -r 2 rules.txt #查看版本庫(kù)2版
(4) svn list
缺省列出的是當(dāng)前工作拷貝對(duì)應(yīng)的版本庫(kù)的URL下的東西,當(dāng)然,你可以直接寫(xiě)出版本庫(kù)。
$ svn list -v http://svn.collab.net/repos/svn
5. 時(shí)間機(jī): ———— 注意:版本庫(kù)沒(méi)有回退的概念,只有你的工作拷貝有回退的概念!!
(1) 將工作拷貝,回退到以往某個(gè)版本: ———— 但注意,根據(jù)commit原理,這種回退的工作拷貝,你是無(wú)法commit的了。如果,你原意是想回退后,以后還能繼續(xù)提交,那應(yīng)該用svn merge!?。?!
$ svn checkout -r 1729 # Checks out a new working copy at r1729
$ svn update -r 1729 # Updates an existing working copy to r1729
(2) 將版本庫(kù)的1729版本打包。
$ svn export http://svn.example.com/svn/repos1 -r 1729
6. 清理:
status發(fā)現(xiàn)有L的,表明是鎖住了,這通常是由于svn上次要修改,所以鎖住,但是由于某種原因,進(jìn)程退出了,所以鎖還在,這是用cleanup命令清除即可。
$ svn status
L somedir
M somedir/foo.c
$ svn cleanup
$ svn status
M somedir/foo.c
第三章 高級(jí)主題
1. 去掉未版本化的文件:
你當(dāng)然不想讓臨時(shí)文件擾亂版本庫(kù),也讓svn方法有svn自身提供的兩種方法,見(jiàn)這一章相應(yīng)部分。我還是覺(jué)得復(fù)雜。
那就當(dāng)svn add 和 import時(shí),后面顯示指定出文件名,而不是默認(rèn)讓它遞歸查找所有的文件和目錄。
2. 鎖定:
如果你想當(dāng)你update后,就能鎖住某些版本庫(kù)的話(huà)。這樣使得一些非文本的對(duì)象,不會(huì)由于交替操作發(fā)生錯(cuò)誤,如圖像處理。
3. 其他很多高級(jí)主題。
見(jiàn)章節(jié)中。
第四章 分支與合并
當(dāng)你創(chuàng)建了一個(gè)版本庫(kù)時(shí)(svnadmin create),里面的目錄和文件可以用 svn copy 或者 svn move來(lái)移動(dòng)。但不能在不同版本庫(kù)間移動(dòng)。
1. 分支:
“分支”,沒(méi)有被SVN賦予特殊的意義,我們稱(chēng)用 svn copy 后的新的目錄,為一個(gè)分支,但對(duì)于svn來(lái)講,只是一個(gè)普通目錄。
(1) 創(chuàng)建分支:
由于分支是對(duì)版本庫(kù)的操作,所以copy后的兩個(gè)參數(shù)都是版本庫(kù)URL,如果你在版本庫(kù)機(jī)器上,用file:///一樣。
$ svn copy http://svn.example.com/repos/calc/trunk \
http://svn.example.com/repos/calc/branches/my-calc-branch \
-m "Creating a private branch of /calc/trunk."
上面,也說(shuō)明了分支放置的地方,在branches里,新建一個(gè)my-calc-branch。以后,也可以在branches里建立其他新的分支。
(2) 版本號(hào)還是往前走!
由于svn的一個(gè)版本庫(kù)內(nèi)部沒(méi)有“分支”的內(nèi)建概念,所以不管哪個(gè)“分支”被commit了,都會(huì)導(dǎo)致版本庫(kù)的版本號(hào)往前走。
2. 合并:
“合并”,即 svn merge 做的事,就是比較版本庫(kù)中兩個(gè)版本的區(qū)別,并把區(qū)別應(yīng)用于你的工作拷貝。這沒(méi)有什么限制,兩個(gè)版本可以是任何兩個(gè)版本,你的工作拷貝,也可以是你本地的任何一個(gè)“分支”。但是,這個(gè)命令拿來(lái)干嘛呢?? 就是為了使得兩個(gè)“分支”能夠合并一些共同的東西,比如一個(gè)分支做了一些錯(cuò)誤修改,另一個(gè)分支也應(yīng)該跟著改。但是,這些修改是通過(guò)比較哪兩個(gè)版本,這就要靠你自己判斷了,一般是看別人用commit -m參數(shù),提交的說(shuō)明,比如它說(shuō),這次提交,修改了xxx,那你就可以比較這次提交和上次版本的差別,然后應(yīng)用于自己的工作拷貝,這就是所謂的“合并”。
如果不能合并,不能合并的文件前,會(huì)打印C,就同update一樣。這往往表明,你選了錯(cuò)誤的兩個(gè)版本作比較,使得差別不能應(yīng)用于拷貝。這時(shí),可用svn revert --recursive恢復(fù)修改。
另外,merge必須經(jīng)常做,如果你打算維護(hù)兩個(gè)相關(guān)的分支的話(huà)。否則,長(zhǎng)時(shí)間不做,merge起來(lái),會(huì)沖突很多,不好解決。
下面是些用法:
$ svn merge -c 344 http://svn.example.com/repos/calc/trunk #默認(rèn)合并到當(dāng)前工作目錄
$ svn merge -c 344 http://svn.example.com/repos/calc/trunk my-calc-branch
$ svn merge http://svn.example.com/repos/branch1@150 \ #@后面接版本號(hào)
http://svn.example.com/repos/branch2@212 \
my-working-copy
$ svn merge -r 100:200 http://svn.example.com/repos/trunk my-working-copy
$ svn merge -r 100:200 http://svn.example.com/repos/trunk
不太懂 --ignore-ancestry 的用途,這個(gè)用于比較兩個(gè)不相關(guān)的目錄樹(shù)
3. 典型例子:
(1) 把某個(gè)branch合并到trunk
先進(jìn)入工作拷貝里的trunk目錄,使得后面svn merge是針對(duì)這個(gè)工作拷貝。
$ cd calc/trunk
$ svn update
At revision 405.
用于merge的兩個(gè)版本庫(kù)版本,是my-calc-branch誕生時(shí)的版本341和目前的版本405。注意,這僅僅是對(duì)第一次merge,但是如果你后來(lái)又修改了分支,你還想merge,那顯然,從406到目前的版本即可。最新版本,可用HEAD代替。不知道,就看上面svn update,告訴你最新是405?;蛘哂胹vn log查詢(xún)。
$ svn merge -r 341:405 http://svn.example.com/repos/calc/branches/my-calc-branch
U integer.c
U button.c
U Makefile
檢查一下?tīng)顟B(tài),不要有沖突的
$ svn status
M integer.c
M button.c
M Makefile
# ...examine the diffs, compile, test, etc...
然后提交,就可以完成合并到trunk。
$ svn commit -m "Merged my-calc-branch changes r341:405 into the trunk."
Sending integer.c
Sending button.c
Sending Makefile
Transmitting file data ...
Committed revision 406.
(2) 恢復(fù)工作拷貝到以前版本:
注意是303后面跟302,后面的小,這樣就能夠使得工作拷貝,回到以前的版本??! 這樣之后,是可以commit的,這不同于用checkout和update得到的以前版本,這種情況下無(wú)法commit.
$ svn merge -r 303:302 http://svn.example.com/repos/calc/trunk
U integer.c
$ svn status
M integer.c
$ svn diff
…
# verify that the change is removed
…
$ svn commit -m "Undoing change committed in r303."
Sending integer.c
Transmitting file data .
Committed revision 350.
(3) 恢復(fù)單個(gè)文件到以前版本:
如果你想對(duì)單個(gè)文件回溯? 那還是用svn copy把,它能夠直接把某個(gè)版本的文件拷貝到你當(dāng)前的工作拷貝。如:
拷貝807版本的real.c到工作拷貝的當(dāng)前目錄
$ svn copy -r 807 \
http://svn.example.com/repos/calc/trunk/real.c ./real.c
查看狀態(tài)
$ svn status
A + real.c
提交修改
$ svn commit -m "Resurrected real.c from revision 807, /calc/trunk/real.c."
Adding real.c
Transmitting file data .
Committed revision 1390.
(4) 當(dāng)然,你可以選擇直接在版本庫(kù)中恢復(fù)文件和文件夾:
這所謂恢復(fù),其實(shí)就是進(jìn)入下一個(gè)revision,注意下面例子中revision號(hào)的變化。
比如你用這個(gè)刪除了:
$ svn delete http://svn.example.com/repos/calc/branches/my-calc-branch \
-m "Removing obsolete branch of calc project."
Committed revision 375.
你可以用這個(gè)來(lái)恢復(fù):
$ svn copy -r 374 http://svn.example.com/repos/calc/branches/my-calc-branch \
http://svn.example.com/repos/calc/branches/my-calc-branch
Committed revision 376.
4. 常用分支模式:
大多數(shù)軟件存在這樣一個(gè)生命周期:編碼、測(cè)試、發(fā)布,然后重復(fù)。這樣有兩個(gè)問(wèn)題,第一,開(kāi)發(fā)者需要在質(zhì)量保證小組測(cè)試假定穩(wěn)定版本時(shí)繼續(xù)開(kāi)發(fā)新特性,新工作在軟件測(cè)試時(shí)不可以中斷,第二,小組必須一直支持老的發(fā)布版本和軟件;如果一個(gè)bug在最新的代碼中發(fā)現(xiàn),它一定也存在已發(fā)布的版本中,客戶(hù)希望立刻得到錯(cuò)誤修正而不必等到新版本發(fā)布。
(1) 永久分支:
一般trunk做主分支(給開(kāi)發(fā)者),branches做發(fā)布分支(給測(cè)試員測(cè)試,不允許添加特性,僅做bug修復(fù)),tag做打包分支(給用戶(hù)):
這是版本控制可以做的幫助,典型的過(guò)程如下:
* 開(kāi)發(fā)者提交所有的新特性到主干。 每日的修改提交到/trunk:新特性,bug修正和其他。
* 這個(gè)主干被拷貝到“發(fā)布”分支。 當(dāng)小組認(rèn)為軟件已經(jīng)做好發(fā)布的準(zhǔn)備(如,版本1.0)然后/trunk會(huì)被拷貝到/branches/1.0。
* 項(xiàng)目組繼續(xù)并行工作,一個(gè)小組開(kāi)始對(duì)分支進(jìn)行嚴(yán)酷的測(cè)試(一般只允許做bug修復(fù)性的提交),同時(shí)另一個(gè)小組在/trunk繼續(xù)新的工作(如,添加新特性,準(zhǔn)備2.0)。兩邊發(fā)現(xiàn)的bug,可以相互merge。注意,這種分支,也就是“維護(hù)某個(gè)版本”的由來(lái)。即不會(huì)添加新特性了,如果OK了,就放到tags里,然后以后有bug,也繼續(xù)在分支改。
* 分支已經(jīng)作了標(biāo)簽并且發(fā)布,當(dāng)測(cè)試結(jié)束,/branches/1.0作為引用快照已經(jīng)拷貝到/tags/1.0.0,這個(gè)標(biāo)簽被打包發(fā)布給客戶(hù)。
* 分支多次維護(hù)。當(dāng)繼續(xù)在/trunk上為版本2.0工作,bug修正繼續(xù)從/trunk運(yùn)送到/branches/1.0,如果積累了足夠的bug修正,管理部門(mén)決定發(fā)布1.0.1版本:拷貝/branches/1.0到/tags/1.0.1,標(biāo)簽被打包發(fā)布。
整個(gè)過(guò)程隨著軟件的成熟不斷重復(fù):當(dāng)2.0完成,一個(gè)新的2.0分支被創(chuàng)建,測(cè)試、打標(biāo)簽和最終發(fā)布,經(jīng)過(guò)許多年,版本庫(kù)結(jié)束了許多版本發(fā)布,進(jìn)入了“維護(hù)”模式,許多標(biāo)簽代表了最終的發(fā)布版本。
(2) 臨時(shí)分支:
有時(shí),可能做些trunk的臨時(shí)分支,以開(kāi)發(fā)某種特性,或做較大更改。
這些分支,一旦開(kāi)發(fā)結(jié)束,就merge入主分支,并刪除自己。
(3) 供方分支:
顯然,我們需要用到別人提供的庫(kù)。但是,我們?cè)趺从媚??一般是,放到我們的版本?kù)中,準(zhǔn)備一個(gè)current目錄,將其放入,然后做快照到如1.0。然后拷貝到brunch中做使用和定制。然后,一旦有新版本發(fā)布,我們想用的話(huà),把最新版本替換current,然后和1.0做比較,用merge將修改應(yīng)用于brunch中的版本。
目錄結(jié)構(gòu)為:
在repos根目錄下,再創(chuàng)建一個(gè)vender目錄,庫(kù)名叫l(wèi)ibcomplex比如,就創(chuàng)建libcomplex目錄,在里面再創(chuàng)建current放最新版本,用1.0放上次版本的快照(svn copy)
-------- 從文檔上摘抄的例子:---------
也許一個(gè)例子有助于我們闡述這個(gè)算法,我們會(huì)使用這樣一個(gè)場(chǎng)景,我們的開(kāi)發(fā)團(tuán)隊(duì)正在開(kāi)發(fā)一個(gè)計(jì)算器程序,與一個(gè)第三方的復(fù)雜數(shù)字運(yùn)算庫(kù)libcomplex關(guān)聯(lián)。我們從供方分支的初始創(chuàng)建開(kāi)始,并且導(dǎo)入供方drop,我們會(huì)把每株分支目錄叫做libcomplex,我們的代碼drop會(huì)進(jìn)入到供方分支的子目錄current,并且因?yàn)閟vn import創(chuàng)建所有的需要的中間父目錄,我們可以使用一個(gè)命令完成這一步。
$ svn import /path/to/libcomplex-1.0 \
http://svn.example.com/repos/vendor/libcomplex/current \
-m 'importing initial 1.0 vendor drop'
…
我們現(xiàn)在在/vendor/libcomplex/current有了libcomplex當(dāng)前版本的代碼,現(xiàn)在我們?yōu)槟莻€(gè)版本作標(biāo)簽(見(jiàn)“標(biāo)簽”一節(jié)),然后拷貝它到主要開(kāi)發(fā)分支,我們的拷貝會(huì)在calc項(xiàng)目目錄創(chuàng)建一個(gè)新的目錄libcomplex,它是這個(gè)我們將要進(jìn)行自定義的供方數(shù)據(jù)的拷貝版本。
$ svn copy http://svn.example.com/repos/vendor/libcomplex/current \
http://svn.example.com/repos/vendor/libcomplex/1.0 \
-m 'tagging libcomplex-1.0'
…
$ svn copy http://svn.example.com/repos/vendor/libcomplex/1.0 \
http://svn.example.com/repos/calc/libcomplex \
-m 'bringing libcomplex-1.0 into the main branch'
…
我們?nèi)〕鑫覀冺?xiàng)目的主分支—現(xiàn)在包括了第一個(gè)供方釋放的拷貝—我們開(kāi)始自定義libcomplex的代碼,在我們知道之前,我們的libcomplex修改版本是已經(jīng)與我們的計(jì)算器程序完全集成了。 [23]
幾周之后,libcomplex得開(kāi)發(fā)者發(fā)布了一個(gè)新的版本—版本1.1—包括了我們很需要的一些特性和功能。我們很希望升級(jí)到這個(gè)版本,但不希望失去在當(dāng)前版本所作的修改。我們本質(zhì)上會(huì)希望把我們當(dāng)前基線(xiàn)版本是的libcomplex1.0的拷貝替換為libcomplex 1.1,然后把前面自定義的修改應(yīng)用到新的版本。但是實(shí)際上我們通過(guò)一個(gè)相反的方向解決這個(gè)問(wèn)題,應(yīng)用libcomplex從版本1.0到1.1的修改到我們修改的拷貝。
為了執(zhí)行這個(gè)升級(jí),我們?nèi)〕鲆粋€(gè)我們供方分支的拷貝,替換current目錄為新的libcomplex 1.1的代碼,我們只是拷貝新文件到存在的文件上,或者是解壓縮libcomplex 1.1的打包文件到我們存在的文件和目錄。此時(shí)的目標(biāo)是讓我們的current目錄只保留libcomplex 1.1的代碼,并且保證所有的代碼在版本控制之下,哦,我們希望在最小的版本控制歷史擾動(dòng)下完成這件事。
完成了這個(gè)從1.0到1.1的代碼替換,svn status會(huì)顯示文件的本地修改,或許也包括了一些未版本化或者丟失的文件,如果我們做了我們應(yīng)該做的事情,未版本化的文件應(yīng)該都是libcomplex在1.1新引入的文件—我們運(yùn)行svn add來(lái)將它們加入到版本控制。丟失的文件是存在于1.1但是不是在1.1,在這些路徑我們運(yùn)行svn delete。最終一旦我們的current工作拷貝只是包括了libcomplex1.1的代碼,我們可以提交這些改變目錄和文件的修改。
我們的current分支現(xiàn)在保存了新的供方drop,我們?yōu)檫@個(gè)新的版本創(chuàng)建一個(gè)新的標(biāo)簽(就像我們?yōu)?.0版本drop所作的),然后合并這從個(gè)標(biāo)簽前一個(gè)版本的區(qū)別到主要開(kāi)發(fā)分支。
$ cd working-copies/calc
$ svn merge http://svn.example.com/repos/vendor/libcomplex/1.0 \
http://svn.example.com/repos/vendor/libcomplex/current \
libcomplex
… # resolve all the conflicts between their changes and our changes
$ svn commit -m 'merging libcomplex-1.1 into the main branch'
…
5. 聰明的switch
svn switch可以把工作拷貝的全部或部分,邏輯上轉(zhuǎn)換為 其他分支 的工作拷貝! 這是繼前面,一個(gè)工作拷貝中可以混合不同版本后,又一種混合方式 ———— 混合不同的分支的部分。 它表現(xiàn)得像update!!
舉個(gè)例子,你的工作拷貝目錄是/calc/trunk,你已經(jīng)做了很多修改,然后你突然發(fā)現(xiàn)應(yīng)該在分支上修改更好,沒(méi)問(wèn)題!你可以使用svn switch使得工作拷貝邏輯上變?yōu)榉种У墓ぷ骺截悾惚镜匦薷倪€會(huì)保留,你可以測(cè)試并提交它們到分支。
如果服務(wù)器做了svn move造成文件移動(dòng),那么 svn update會(huì)?? 必須用svn switch??
6. 建立標(biāo)簽:
標(biāo)簽就是一次正式發(fā)行打包。這里再次說(shuō)明了:標(biāo)簽和分支一樣,都是人為的概念,對(duì)于svn來(lái)說(shuō),都是用svn copy制造的“快照”。比如標(biāo)簽就是從trunk拷貝到tags,或者從測(cè)試的branch拷貝到tags。
但是,需要用一種方法,來(lái)禁止用戶(hù)提交commit,因?yàn)闃?biāo)簽概念上,不允許修改了,只是歷史沉淀。
怎么做呢??? svn+ssh的話(huà),用基本文件系統(tǒng)的權(quán)限來(lái)解決吧。
http://hi.baidu.com/00%C6%F3%B6%EC/blog/item/c2bb0245d00dd03786947321.html