天天看點

《版本控制之道:使用subversion》讀書筆記

[align=center][img]http://images.china-pub.com/ebook30001-35000/34229/zcover.jpg?2010-4-1%2014:27:48[/img][/align]

svn也是平時開發中用到最多的一個工具. 一些基本的操作用起來也是沒有問題的, 看這本書算是對已有svn知識的一個擴充, 學到了很多以前不是很了解的知識, 以前我們都是通過小烏龜用戶端來用, 這裡知道了很多通過指令行方式來使用svn, 比較有價值的是如何實作多分支開發以及合并, 貌似跟我們目前的開發模式正好相反. 個人覺得書裡面的開發模式要比我們要好一些, 我們采用在分支開發, 合并到主幹釋出, 書裡面是主幹開發, 分支釋出, 這樣在釋出後期當機釋出分支代碼, 轉到主幹并行開發更有效. 不知道我們的scm有沒有看這本書呢?

要決定在版本倉庫中放什麼不放什麼有一個簡單的測試, 隻要問問"如果我們沒有這個東西的最新版本, 我們是不是可以建構, 測試, 并傳遞我們的程式?" 如果回答是不能, 那麼它就應該被放在項目倉庫中.

checkout和export的差別在于, 當你export的時候, 你得到的不是一個工作拷貝, 而是一個項目倉庫檔案的快照, 即沒有.svn這樣的版本控制檔案, 這個在某些情況下是比較有用的, 比如打包.

extrenals 就是把另外一個subversion項目倉庫的位置包含到你項目的任何目錄中.

subversion采用整體版本号方式, 整個項目倉庫從版本号0開始, 簽入一個改變之後, 項目倉庫的版本号增加1, 以此類推.

subversion的項目倉庫版本号有點像馬克筆, 在每次送出的時候給項目倉庫中的所有檔案畫一條線.

subversion的版本号并不是用來表明某個檔案或者某些檔案變動了多少. 不管版本号是怎麼配置設定的, 因為一個改動可能是一個修改了檔案每一行的改動, 如果你想知道到底有多少改動, 或許更好的方式是使用版本控制系統浏覽曆史的功能, 直接去觀察改動本身.

svn --version 檢視目前機器安裝svn的情況

svn import -m "xxx" . file:///home/svn-repos/proj/trunk 表示把一些檔案導入到項目倉庫中, -m 選項 使得你可以給本次導入操作關聯一條消息.

svn status Day.txt 獲得指定檔案的狀态, 如果現實為M, 表示Subversion認出這個檔案已經在本地被修改過了.

要簽出一個指定版本7的項目

svn co -r 7 svn://svn-repos/proj/trunk

使用svn update的時候, 有一些提示, 分别表示是意義:

A 代表倉庫中新檔案, 已經添加到你的工作拷貝中了

U 表示你的工作拷貝中檔案過期了, svn已經把你工作拷貝的檔案更新為最新版本.

D 代表該檔案已經從倉庫中删除了, svn已經把它從你的工作拷貝中删除了.

G 表示在你的工作拷貝中的檔案已經過期了, 而你本地還做了修改, svn已成功了将倉庫中的版本和你本地的修改合并到了一起.

C 表示你工作拷貝中的檔案已經過期了, 而你本地也做了修改, svn嘗試合并, 但是遇到了沖突

svn還可以給檔案關聯中繼資料, 也就是屬性, 比如java檔案可以有一個關聯的"審閱者"屬性, 告訴你誰最後審閱過這個檔案

svn propset checked-by "Mike" Number.txt 表示把Number.txt的checked-by屬性的值設定為Mike

關鍵字展開

如果你給檔案設定了svn:keywords 屬性 然後 你的檔案中使用了下面的關鍵字, 那麼在送出的時候, svn會做替換為相關的内容. 這個功能一般不建議使用

$Date$ 最後一次送出到項目倉庫的時間

$Rev$ 最後一次送出到倉庫的版本号

$Author$ 最後一個送出檔案的使用者名

$URL$ 檔案在倉庫中的完整路徑

$Id$ 展開Wie其他關鍵字的簡短摘要

svn propedit svn:ignore .

表示編輯目前目錄下的svn:ignore屬性, 但是這裡需要綁定一下editor, 否則會出現如下提示:

svn: 沒有設定 SVN_EDITOR,VISUAL 或 EDITOR 環境變量,運作時的配置參數中也沒有 “editor-cmd” 選項

svn會存儲所有的檔案, 無論他們是文本, 圖檔, 編譯過的代碼, 或者電影, 在項目倉庫中使用的都是二進制格式.

svn copy的用法

svn copy Number.txt Data.txt

svn commit -m "copy ...."

在你的本地工作目錄copy一個檔案或者目錄會建立拷貝, 并且把它們列到要添加到項目倉庫中的檔案清單中. 這裡因為svn記住了檔案的曆史, 當詢問Data.txt的曆史也會給出Number.txt的曆史.

svn move用來重命名檔案

svn move Time.java Clock.java

實際上是先添加然後删除, 現在已經提供了rename指令

如果要執行基于項目倉庫的重命名, 使用兩個類似最開始簽出時用到的那樣的URL, 比如:

svn move -m "rename ..." \

svn://repos/proj/trunk/util \

svn://repos/proj/trunk/common

比較兩個版本(19和20)的差異:

svn diff -r19:20 Clock.java

讓svn比較你的工作拷貝和項目倉庫中的最新版本, 使用HEAD關鍵字:

svn diff -r HEAD Clock.java

有時在你開始改動一個檔案之前希望能夠看到它最近的改動, 可以使用PREV符号版本做到這一點:

svn diff -r PREV:BASE Clock.java

下面的指令建立一個稱為mychanges.patch的檔案, 包含了對在mydir極其以下檔案的所有改動:

mydir> svn diff > machanges.patch

而把這個檔案發給維護者, 他隻要使用patch指令就能把這個更新檔應用到他的源碼中去.

mydir> patch -p0 -i mychanges.patch

這裡的參數-p0 表示patch在應用它之前, 從檔案名種略過零層目錄. 如果你包含這個選項, path會包含說它找不到正确的目錄

-i表示path使用mychanges.patch作為輸入.

解決沖突

如果你決定扔掉改動, 使用項目倉庫中的版本, 你要做的就是使用svn revert指令:

svn revert Number.txt

svn update Number.txt

如果決定保留你的改動并且丢掉項目倉庫中的那些東西, 拷貝你的那個以.mine結尾的版本檔案, 并且告訴svn你已經修正好沖突了

cp Number.txt.mine Number.txt

svn resolved Number.txt

在commit log中寫"把timeout改為42" 是沒有意義的, 因為隻要用一下diff就能看出來, 應該用日志消息來回答為什麼要這麼做?

如果改動是為了完成一個bug報告, 把跟蹤号寫到日志消息中. 問題的描述已經在bug資料庫中了, 無須在這裡再重複.

顯示某個版本到某個版本的log:

svn log -r 11920:11900

如果要檢視更詳細的資訊, 可以加上-v(verbose)選項獲得更多資訊:

svn log -r 24 -v Clock.java

svn blame指令顯示一個或者多個檔案的内容. 對于每個檔案中的每一行, 它會顯示改動了那行的最後的版本, 以及做這個改動的人.

丢棄掉對代碼的改動, 如果改動是在本地未簽入的工作代碼中, 那麼使用svn revert就能把改動扔掉. 如果已經送出, 要扔掉改動, svn也提供了多種辦法.

先使用svn update

然後找出要移除的确切版本, 然後使用svn log檢視要扔掉改動檔案的版本情況, 如果要移除對版本27的改動, 使用:

svn merge -r 27:26 Contacts.java

這樣我們可以讓svn計算Contacts.java版本27和26之間的差異, 并把改動應用到工作拷貝中

svn status可以得到你工作目錄中檔案的資訊, 如果指定--show-updates選項的話, svn會與伺服器通信顯示額外的資訊, 如果想避免RSI的話可以使用-u.

檔案加鎖

svn 1.2引入了可選的檔案加鎖, 它可以幫助避免由不可合并的檔案導入的問題.

任何檔案都可以被設定為加鎖模式, 這樣的話, 在編輯之前就需要先開鎖. 設定加鎖模式是通過它設定它的needs-lock屬性, 任何加鎖的檔案簽出到工作目錄中的時候都是隻讀的.

我們可以使用svn lock指令來獲得檔案的鎖. svn用戶端會與伺服器通信確定檔案不是已經被鎖住了, 在獲得一個"令牌鎖"之後在工作拷貝中把檔案标記為可讀可寫.

一般如果是二進制檔案的話, 最好采用加鎖的模式, 這個也是采用鎖的必要所在.

給一個電子表格檔案加鎖:

svn propset svn:needs-lock true myexcel.els

svn co -m "lock it"

當我開始編輯myexcel.xls檔案的時候, 必須先鎖住:

svn lock myexcel.xls -m "i lock it"

在鎖住檔案加注釋可以很好的告訴其他人增進交流溝通.

當你送出一個檔案或者目錄的時候, svn會自動解開任何你擁有的鎖.

svn unlock指令可以用來解開别人對檔案的鎖, 當不擁有這個鎖的時候, 還需要傳入--force選項.

這樣當原來擁有鎖的人嘗試送出改動的時候, svn會讓他不再擁有對應的鎖令牌, 如果要送出, 必須再次鎖定該檔案才能送出.

關于标簽

标簽是給一組檔案取的符号名稱, 每個都有一個特定的版本号, 你可以把标簽看成你項目倉庫的一個切片, 标記了其中的每一件東西.

标簽對于跟蹤項目生命周期中的重要事件是相當有用的. 你不在為了給客戶釋出一個版本記住需要使用版本16的Calendar.java, 版本23的Schedule.java以及版本12的Contacts.java, 相反你可以讓标簽來幫你記住這些東西, 你可以認為我們可以隻使用版本号或者日期就能簽出所有的代碼來建構一個釋出版本. 你可以通過标簽簽出項目倉庫中的不同版本号的檔案來獲得一個混合版本的工作拷貝.

标簽隻是你項目倉庫特定版本的一個拷貝, 是以沒有什麼可以阻止人們将改動簽入到标簽目錄, 但是大部分時間最好還是将标簽當作隻讀. 如果你實在要改動标簽中的内容, 那麼實際上标簽就變成分支了.

一種開發模式

在開發進行到快要釋出的階段, 把釋出的代碼移到移到釋出分支中, 釋出相關人員在該分支上開發, 項目其餘人員繼續在主幹開發, 當釋出本身完成, 用釋出号給釋出分支打标簽(标簽隻是釋出分支在特定時刻的一個簡單拷貝), 釋出分支可以随後合并回主幹.

建立釋出分支:

svn mdir -m "create branch" svn://repos/proj/branches

svn copy -m "create relaase branch for 1.0" svn://repos/proj/trunk svn://repos/proj/branch/RB-1.0

把工作拷貝切換到釋出分支

svn switch svn://repos/proj/branches/RB-1.0

經過釋出開之後, 準備釋出了, 給分支打上标簽

svn mdir -m "create tag" svn://repos/proj/tags

svn copy -m "tag release 1.0.0" svn://repos/proj/breanches/RB-1.0 svn://repos/proj/tags/REL-1.0.0

現在已經到1.0.4版本了, 要修複一個1.0.0版本上的bug(前提不想使用1.0.5版本), 先簽出1.0.0标簽

svn co svn://repos/proj/tags/REL-1.0.0 client-fix

然後用後續版本中修複該bug的代碼更新client-fix中的相應代碼, 這裡是common下的代碼需要更新

cd client-fix

svn switch svn://repos/proj/breanch/RB-1.0/common common

測試通過之後, 在client-fix處再打一個标簽

svn copy -m "tag client 1.0.0 fix" client-fix svn://repos/proj/tags/REL-1.0.0-clientfix

在釋出分支中修複簡單bug

簽出包含bug的代碼到本地工作拷貝

編寫測試重制bug, 并修正該bug

把改動送出到項目倉庫, 并記住新的版本号, 最好将版本号記錄到bug跟蹤系統中, 友善每個人以後都能找到他.

使用新的版本号把改動合并到其他受影響的分支(可能包括主幹)

for example:

rb1.0>svn commit -m "fix bug 3065"

Committed revision 38

這裡得到了修正代碼的版本38

切換到主幹代碼

rb1.0>cd ../proj

proj>svn update

proj>svn merge -r37:38 svn:/repos/proj/branches/RB-1.0

U ...

proj> svn commit -m "Merge r38 (fix bug 3065)"

修複複雜bug

為有bug的代碼分支建立新的分支

給分支打上标簽, 标記bug修複開始

編寫測試重制bug, 修改代碼讓新的測試通過

送出改動到項目倉庫

修複完成, 再打上一個标簽, 标記bug 結束

使用兩個标簽來吧修正代碼合并到所有其他受影響的分支

for example:

work> svn copy -m "create bugfix branch" svn://repos/proj/branch/RB-1.0 svn://repos/proj/branch/BUG-10512

work> svn copy -m "create bugfix tag start" svn://repos/proj/branch/BUG-10512 svn://repos/proj/branch/PRE-10512

checkout BUG-10512分支做修改

work> svn checkout svn://repos/proj/branch/BUG-10512

bug修複結束, 送出代碼, 打上新的标簽

work>svn copy -m "create fix bug finish" svn://repos/proj/branch/BUG-10512 svn://repos/proj/branch/POST-10512

将bug修複代碼合并到釋出分支

work> cd rb1.0

rb1.0>svn update

rb1.0>svn merge svn://repos/proj/tags/PRE-10512 svn://repos/proj/tags/POST-10512

測試一把, 沒有問題, 送出代碼

rb1.0> svn commit -m"marge fix bug 10512"

使用svn merge将修改代碼合并到其他分支或主幹

擷取分支從建立到目前修改的所有版本和修改

svn log --stop-on-copy

将目前分支的修改合并到其他分支

svn merge -r xx:HEAD svn://repos/proj/branches/my-branch

導入檔案到svn倉庫

svn import -m "xxx import" svn://repos/proj/trunck

一些有用的指令

在遇到沖突的時候廢棄你的改動

svn revert file..

svn update file..

在遇到沖突的時候廢棄别人的改動

cp file.mine file

svn resovled file

繼續閱讀