天天看點

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

使用svn幾年了,一直對分支和合并敬而遠之,一來是因為分支的管理不該我操心,二來即使涉及到分支的管理,也不敢貿然使用合并功能,生怕合并出了問題對團隊造成不良影響,最主要的原因是,自己對分支的目的和合并的方法不甚了解,這才是硬傷。

最近由于适配機型的需要(本人從事手機用戶端的開發),需要經常接觸分支和合并兩項工作,突然發現這玩意整不明白很難開展工作,遂這兩天着重研究了一下,有點收獲,怕以後忘了,故趁着餘溫尚在趕緊寫下來,好記性不如爛筆頭嘛。下文的實踐主要是參考了TortoiseSVN的幫助文檔和Subversion的線上文檔,Subversion的線上文檔:http://svnbook.red-bean.com/en/1.5/svn-book.html

話說我公司現在的源代碼管理挺亂的,svn目錄并沒有采取标準的source/branches、source/trunk結構,主線和分支放得到處都是,release版本也并沒有當成tag處理,而是當成branch來管理,經常還要在release版本上改來改去。。。

先說說什麼是branch。按照Subversion的說法,一個branch是某個development line(通常是主線也即trunk)的一個拷貝,見下圖:

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

branch存在的意義在于,在不幹擾trunk的情況下,和trunk并行開發,待開發結束後合并回trunk中,在branch和trunk各自開發的過程中,他們都可以不斷地送出自己的修改,進而使得每次修改在repository中都有記錄。

設想以下場景,如果你的項目需要開發一個新功能,而該功能可能會修改項目中的絕大多數檔案,而與此同時,你的另一位同僚正在進行bug fix,如果你的新功能不在branch中開發而直接在trunk中開發,那麼你極有可能影響另一位同僚的bug fix,他/她在bug修複中可能會遇到各種各樣的問題,因為你的頻繁送出代碼引入了過多的不穩定因素。你可能會說,那我在開發的過程中不送出不就行了,等到我全部開發結束我再送出,是,你可以這麼做,那還要版本控制幹什麼呢?也許等到你最後送出代碼的時候(也許一周,也許兩周?),你會發現有一大堆conflict等着你resolve。。。

那麼,正确的做法是什麼?使用branch,從trunk建立branch,然後在你的branch上開發,開發完成後再合并到trunk中。

關于branch先講到這裡,下面說說什麼叫做合并。很好了解,當branch開發完成後(包括必要的測試),将branch中的修改同步到trunk中,這個過程有可能包括修改檔案、增加檔案、删除檔案等等。

說到這裡,貌似本文差不多可以結束了,不就是分支和合并麼?隻要再簡單地說說如何建立分支和如何合并就可以收尾了,可能隻需兩個指令,也可能隻需滑鼠點幾下然後鍵盤敲兩下即可。其實事情遠非這麼簡單,愛動腦筋的同學可能會問了,将branch的改動merge到trunk的時候,和上文說的直接在trunk中全部開發完然後送出有何差別?你最後還不是要處理一大堆conflict?

這個問題問得非常好,其實這正是本文的重點:branch和trunk在并行開發的過程中如何感覺對方,branch如何才能在開發過程中不會和trunk越走越遠,導緻最後無法合并?試想一下,如果在你開發branch的過程中,trunk中的某個類檔案已經被删除了(這可能是另外一個家夥在另一個branch上開發了兩周後才合并到trunk的),而你竟然在這個類檔案上做了大量修改,試問你到最後合并回trunk的時候該有多蛋疼?解決這一問題的唯一手段是,branch要不停地和trunk保持同步,你要及時地知道trunk都做了什麼修改,這些修改是否會影響你正在開發的新功能,如果需要,你必須及時調整branch的代碼,使之能與trunk“相容”。

那麼如何讓branch和trunk保持同步?合并,從trunk合并到branch,你沒聽錯,是從trunk合并到branch。關于TortoiseSVN的合并,有幾點需要注意:

  • TortoiseSVN的合并發生在本地,也即你的working copy中,你無需過多擔心會對repository中的代碼造成影響
  • 不管是從trunk合并到branch還是最終從branch合并回trunk,在每次合并前最好先update,然後将本地的修改先全部commit,保護好現場,萬一合并不理想随時都可以revert
  • 合并完成後看是否能正确編譯,然後測試驗證,最後将合并後的改動送出到repository

下面我将step by step地示範如何一次完整的branching和merging,包括建立分支、分支開發、分支和主線同步,分支合并到主線的全過程,甚至包括如何在本地建立一個測試用的repository。

首先需要安裝TortoiseSVN,我安裝的版本是:TortoiseSVN 1.6.15, Build 21041 - 32 Bit , 2011/03/23 18:00:27

1、本地Repository的建立

repository的建立很簡單,假設我要在D:\TortoiseSVN\TestRepository目錄中建立repository,隻需右鍵TestRepository目錄,依次選擇"TortoiseSVN" -> "Create repository here"便完成了repository的建立。

2、Check out

假設要check out到D:\TortoiseSVN\TestSVN,同樣很簡單,在D:\TortoiseSVN目錄下建立TestSVN目錄,然後在該目錄上右鍵,選擇"SVN Check out...",在彈出的視窗中的"URL of repository"中填入"file:///D:/TortoiseSVN/TestRepository",其他預設即可,最後點選ok。

3、trunk建立新項目MyProject

相當簡單就不贅述了,隻列出本次操作所作出的修改:

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

4、建立branch

在/trunk/MyProject目錄上右鍵,依次選擇"TortoiseSVN" -> "Branch/tag...",在彈出視窗的"To URL"中填入分支的位址,在這裡目标revision選擇HEAD revision,如下圖所示,添加log後點選ok分支便建立了。這個操作速度非常快,建立的branch在repository中其實隻是一個指向trunk某個revision的軟連接配接而已,并沒有真的複制檔案。

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

5、Check out分支

右鍵TestSVN目錄選擇"TortoiseSVN Update"即可将剛剛建立的分支下載下傳回本地。進入/branches/MyProject目錄下你會發現其檔案結構和/trunk/MyProject一模一樣。

6、branch送出一個新檔案

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

7、trunk緊接着送出一個修改

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

8、branch再次送出一個修改

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

9、将trunk中的修改同步到branch

6-8示範的是branch和trunk在獨立、并行地開發。為了防止在“錯誤”的道路上越走越遠,現在branch意識到是時候和trunk來一次同步了(将trunk合并到branch)。

首先,在本地trunk中先update一下,有沖突的解決沖突,保證trunk和repository已經完全同步,然後在/branches/MyProject上右鍵,依次選擇"TortoiseSVN" -> “Merge...”,在彈出的視窗中選擇第一項"Merge a range of revision",這個類型的Merge已經介紹得很清楚,适用于将某個分支或主線上送出的多個revision間的變化合并到另外一個分支上。

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

點選next後,出現如下視窗:

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

由于是要從trunk合并到branch,理所當然這裡的"URL to merge from"應該填trunk的路徑,"Revision range to merge"很好了解,就是你要将trunk的哪些revision所對應的變化合并到branch中,可以是某一連串的revision,比如4-7,15-HEAD,也可以是某個單獨的revision号。由于在r4中,trunk修改了Person.Java中的talk()方法,是以這裡的revision隻需填4即可。點選next後出現下圖:

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

在這裡隻需保留預設設定即可。在點選Merge按鈕前你可以先Test merge一把,看成功與否,以及merge的詳細資訊。點選Merge按鈕後trunk所做的修改将同步到branch中。

10、送出合并後的branch

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

至此,branch已經完全和trunk同步,branch和trunk的代碼相處很融洽,沒有任何沖突,如果branch已經開發結束,那是時候将branch合并回trunk了,當然,如果branch還要繼續開發,那你将不斷地重複6-10這幾個步驟。

11、将branch合并回trunk

在/trunk/MyProject上右鍵(注意是在主線的目錄上右鍵),依次選擇"TortoiseSVN" -> "Merge...",在彈出的視窗中,Merge type選擇第二項"Reintegrate a branch",這種類型的合并适合在分支開發結束後将所有的改動合并回主線。

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

點選next後出現如下視窗:

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

在這裡,"From URL"選擇/branches/MyProject,無需選擇revision号,Reintegrate會将branch上所有修改合并到trunk。後面的步驟和上文第9步中的一樣,不再啰嗦了。如無意外,branch将成功合并到trunk,你需要做的隻是将合并後的trunk趕緊commit!

12、送出合并後的trunk

so easy...

13、删除branch

如果你認為你新加的功能已經開發完成了,你可以删除你的分支

到這裡,我已經給你示範完了整個過程,我一身的汗也下來了,我想罷工了,不過最後我們還是看看所有的log資訊吧,通過log能發現我們幹的所有事情:

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

r1-r7正是我上文在幹的事情,從Message中你能發現我對trunk和branch都幹了什麼,另外,在Log Messages視窗的左下角勾選了"Include merged revisions"你還能看到額外的Merge information:

TortoiseSVN中分支和合并實踐 SVN中Branch和tag優劣大比拼

圖中灰色的是和merge相關的log,共發生了兩次merge,第一次是在r6,在r6中,branch合并了trunk在r4時送出的變化;第二次是在r7,在r7中,trunk合并了branch從r2到r6的所有變化。

終于可以寫寫總結了:

  • branch主要用于新功能的開發
  • 合并發生在本地working copy,隻要你不送出就不會影響到repository
  • 合并前一定要先update、commit,保證不會out of day,并将本地的修改儲存到repository
  • branch和trunk并行開發的過程中,要經常同步,将trunk的修改合并到branch,合并時選擇"Merge a range of revision"
  • branch最後合并回trunk時,merge type選擇"Reintegrate a branch"

##########################################################################################

合并的工作是把主幹或者分支上合并範圍内的所有改動列出,并對比目前工作副本的内容,由合并者手工修改沖突,然後送出到伺服器的相應目錄裡。如果目前工作副本是主幹,則合并的範圍是分支上的改動,如果工作副本是分支的,則合并範圍是主幹上的改動,并且一定要注意,合并的起始位置URL一定要和目前的工作副本的URL是相同的。

一、合并一個範圍的版本(Merge a range of versions)

       此類型應用最為廣泛,主要是把分支中的修改合并到主幹上來。在主幹上點選右鍵選擇合并,然後選擇合并類型:合并一個範圍的版本。合并的源URL填寫的是要合并的分支的URL,待合并的版本範圍如果為空,則指的是合并分支上所有的版本,即自從分支建立以來到分支目前最新版本的所有演變。如果隻是選擇其中一個版本,或者幾個版本,那麼就表示隻是将制定的n個版本的變化合并到主幹上。如果隻是選擇其中一個版本,那麼表示隻是選擇那個版本的修改,之前或之後的修改将不被采納。

二、複興合并(Reintegrate a branch)

       複興合并可以了解為是第一種合并類型的一種特例,在複興合并中,主幹可以了解為是自從開創分支之後沒有任何修改,而分支是經過修改的,而且合并中分支是沒有版本選擇的。經過複興合并,分支中所有的修改都會合并到主幹中,合并的結果将使得分支和主幹一模一樣,進而可以删除分支。

三、合并兩個不同的樹(Merge two different tress)

       此類型與前兩種類型不同,第一種類型可以選擇分支合并的版本,主幹不能選擇版本;第二種類型是主幹和分支都不能選擇合并的版本;而這種類型則是無論是主幹還是分支都可以選擇合并的版本,即可以選擇過去的一個主幹版本與分支的某個版本進行合并。合并的時候以選擇的分支版本為主,如果選擇的主幹版本與分支版本有不同的地方,合并時主幹部分将被放棄。

起始URL:選擇主幹目錄的URL(應當和目前工作副本的URL一緻,這個是所謂的合并點)

結束URL:選擇要合并的分支的URL。

起始和結束的版本:一般起始版本應當找到最後一次同步時的版本,如果從沒有同步過(第一次合并),則選擇建立分支時的版本,結束版本一般是最新版本,如果你不想将某些内容合并進主幹的話,也可以選擇一個合并點。

#################################################################

SVN中Branch和tag優劣大比拼

本節主要講解一下在SVN中Branch和tag的比較,SVN中Branch和tag在一個功能選項中,在使用中也往往産生混淆。在實作上,branch和tag,對于svn都是使用copy實作的,是以他們在預設的權限上和一般的目錄沒有差別。至于何時用tag,何時用branch,完全由人主觀的根據規範和需要來選擇,而不是強制的(比如cvs),下面我們就來看一下SVN中Branch和tag的具體介紹。

SVN中Branch和tag的比較

在SVN中Branch和tag在一個功能選項中,在使用中也往往産生混淆。在實作上,branch和tag,對于svn都是使用copy實作的,是以他們在預設的權限上和一般的目錄沒有差別。至于何時用tag,何時用branch,完全由人主觀的根據規範和需要來選擇,而不是強制的(比如cvs)。

一般情況下,tag,是用來做一個milestone的,不管是不是release,都是一個可用的版本。這裡,應該是隻讀的。更多的是一個顯示用的,給人一個可讀(readable)的标記。

branch,是用來做并行開發的,這裡的并行是指和trunk進行比較。比如,3.0開發完成,這個時候要做一個tag,tag_release_3_0,然後基于這個tag做release,比如安裝程式等。trunk進入3.1的開發,但是3.0發現了bug,那麼就需要基于tag_release_3_0做一個branch,branch_bugfix_3_0,基于這個branch進行bugfix,等到bugfix結束,做一個tag,tag_release_3_0_1,然後,根據需要決定branch_bugfix_3_0是否并入trunk。對于svn還要注意的一點,就是它是全局版本号,其實這個就是一個tag的标記,是以我們經常可以看到,什麼什麼release,基于xxx項目的2xxxx版本。就是這個意思了。但是,它還明确的給出一個tag的概念,就是因為這個更加的可讀,畢竟記住tag_release_1_0要比記住一個很大的版本号容易的多。

SVN中Branch和tag建立的方法比較簡單,totoiseSVN中的操作是:

1.選擇Branch和tag..

2.在出來的界面中的ToURL中填上URL,一般是svn://IP/Project/branches/branch-1,這樣就建立了一個branch-1的branch.建立tag是一樣的操作,隻不過URL一般是svn://IP/Project/tags/tag-1

3.後面的Createcopyfrom是用于選擇從你目前的workingbase中的哪個版本中建立Branch和tag,可以根據自己的選擇來訂制,一般選擇HeadRevision

subclipse中幾乎是一樣的操作。

Merge分為很多種:

1.多個branch之間merge

2.branchmerge到trunk

3.trunkmerge到branch

第2種用的比較多,比如在otfs接口中netamount的需求提出後就得建立一個netamount的branch,trunk繼續在非netamount的情況下繼續開發,netamount單獨開發,當netamount功能測試通過後,将netamountbranchmerge到trunk下,然後将trunkrelease。第3種情況用的也不少,如上的例子,當使用者進行netamount測試時,如果使用者不想隻測試netamount的功能,則需要将trunk中的修改merge到netamountbranch,然後從netamountbranch中釋出一個版本供使用者測試。

branch merge to trunk在tortoiseSVN操作如下:

1.選擇TortoiseSVN->Merge

2.選擇Reintegrateabranch

3.選擇FromURL,URL填好之後可以點選ShowLog,可以看看這個branch是否是你要merge的内容,下面的Workingcopy中也可以ShowLog,可以确認一下你的工作目錄是否是trunk。确認後點選Next

4.MergeOptions裡面有些選項,根據需要來選擇,TestMerge按鈕會告訴你這次Merge會做哪些操作,最好先TestMerge一下!如果是預期的Merge操作,點選Merge則可以将branchMerge到本地工作目錄下

5.有沖突的檔案需要解決好沖突,解決之後點選svncommit則完成了merge

SVN中Branch和tag比較介紹時從多個revision中merge到本地工作目錄在tortoiseSVN的操作如下:

1.選擇TortoiseSVN->Merge

2.選擇Mergearangeofrevisions

3.URLtomergefrom填上merge的來源,merge來源一般和你的workingcopy是不同的branch或者workingcopy是trunk而mergefrom是branch

4.Revisionrangetomerge填上需要merge的revision,格式是1,3,5或者1-10

5.後續操作同Reintegrateabranch

subclipse進行Merge操作同tortoiseSVN的操作方式有所差別,原理基本同Mergearangeofrevisions。

點選Team->Merge後,隻有一個界面,這個界面提供了兩種Merge操作方式:

1.Merge某個版本範圍内的修改到本地工作目錄上

2.Mergefeaturebranch到trunk(也需要先merge到本地)

第1種的操作方法是:

1.在fromurl中填上branch的url

2.fromrevision中選擇建立這個branch的revision号,不是最新的那個版本号!

3.tourl框中勾上"Use'From:'URL"這個checkbox,ToRevision中選擇需要需要Merge到的revision号,一般勾headrevision

4.Dryrun讓你預覽一下merge效果,UnifiedDiff将Merge的兩邊進行Diff并将Diff結果儲存到檔案中。(在我機器中Dryrun沒有視窗出來,diff結果的檔案除非隻有很小的變化,不然看得頭大)

5.點選Merge将merge到本地,這時候與版本庫進行一下同步應該和上一步的dryrun有同樣的效果,如果merge過來的東西不是你預期的更改可以選擇revert,但是新增的檔案需要手工删除!如果是預期的merge效果,那commit,記得在comments中寫上merge來的branch、fromrevision、torevision(不要寫head,寫數字)總的來看subclipse的merge操作并不友善,不如tortoiseSVN。SVN中Branch和tag的比較介紹完畢。 

參考文獻: SVN版本管理規範1.4

SVN版本庫管理

繼續閱讀