前提
使用SVN分支技術(shù),需要理解并掌握SVN的基本操作。
如果對(duì)SVN的基本操作不熟悉,或者存在理解上的問題,可能對(duì)分支的使用造成障礙。
一些基本概念:
1.版本庫(kù)
版本庫(kù)就是SVN服務(wù)器上,存放記錄了文件、目錄每一次變化的數(shù)據(jù)庫(kù)。
2.工作副本
工作副本(也叫工作拷貝,很多時(shí)候簡(jiǎn)寫做WC——即Work Copy)。它是版本庫(kù)代碼在本地的映射,開發(fā)人員在本地的工作副本中做修改再上傳到SVN版本庫(kù)中。
開發(fā)人員對(duì)于同一個(gè)版本庫(kù)路徑,比如https://KFBServer/svn/xykj_2013/trunk,可以創(chuàng)建無(wú)數(shù)個(gè)不同名稱的本地文件夾進(jìn)行下載,這就是“副本”這個(gè)名稱的意義。
3.TTB目錄
trunk、tags、branches
trunk:存放開發(fā)主線的文件夾,用于創(chuàng)建分支、合并分支、創(chuàng)建標(biāo)記。
branches:存放分支的文件夾。
tags:存放“快照”的文件夾。通常,tags目錄中的東西不再允許修改了,作為一個(gè)“定版”來測(cè)試、發(fā)布。簡(jiǎn)單的說,一個(gè)標(biāo)簽就是具有特定意義的一個(gè)版本。
“分支(branch)和標(biāo)記(tag)對(duì)于 SVN 來說其實(shí)都只是主干的拷貝,這些拷貝本身并不存在分支和標(biāo)記的區(qū)別,分支和標(biāo)記的意義是我們?nèi)藶榻o予的”。這句話在分支的使用中就會(huì)慢慢理解。
4.廉價(jià)拷貝
對(duì)于SVN來說,通常意義的創(chuàng)建分支,只不過是在版本庫(kù)內(nèi)創(chuàng)建了一個(gè)目錄,并復(fù)制了一些文件和子目錄過去。但你不需要擔(dān)心版本庫(kù)的體積會(huì)變得十分巨大,因?yàn)镾ubversion并不是拷貝真實(shí)的文件,而是通過拷貝“文件的快捷方式”來完成的。只有當(dāng)復(fù)制后的目錄、文件被程序員在本地做了修改并提交到版本庫(kù)時(shí),SVN才會(huì)用真實(shí)的文件來存儲(chǔ)它們。
基本概念
一、什么是分支。
分支是軟件開發(fā)中源代碼管理的一種技術(shù),而不是產(chǎn)品版本——比如精簡(jiǎn)版、普通版、豪華版,也決不是針對(duì)特定用戶的特殊修改。
所謂的分支就是一條開發(fā)線。
說明:上圖僅僅描述了分支的創(chuàng)建,并未談到分支的合并。
不合并的分支只會(huì)得到不同的產(chǎn)物,這與我們的初衷相違背,因此我們?cè)谑褂梅种У臅r(shí)候,一旦分支代碼通過了測(cè)試就必須合并,這樣才能形成統(tǒng)一的一套代碼。
二、為什么要使用分支
分支的作用:隔離。
1.避免未經(jīng)測(cè)試的修改提交到版本庫(kù)后對(duì)編譯出的產(chǎn)品造成影響。
2.允許多個(gè)開發(fā)人員同時(shí)修改某一產(chǎn)品的代碼時(shí)互不影響。
3.允許開發(fā)人員在自己的分支中提交半成品代碼,即使是無(wú)法編譯的代碼;
4.單個(gè)開發(fā)人員通過創(chuàng)建多個(gè)分支,從而可以同時(shí)修改多個(gè)問題,并隨意切換。相反,在單一開發(fā)線中修改多個(gè)問題,就很麻煩。
5.允許新入職員工隨意修改代碼。
三、分支和文件系統(tǒng)中簡(jiǎn)單的拷貝有什么不同
文件系統(tǒng)的拷貝:未處于SVN的管理中,人工比較、跟蹤其他分支線的變化非常痛苦,而且容易造成閉門造車的結(jié)果,導(dǎo)致合并困難。
分支:處于SVN的管理中,大大減少了人工比較、跟蹤其他開發(fā)線的工作量。分支既保持獨(dú)立,又可以隨時(shí)共享。
具體操作
使用分支前需要對(duì)版本庫(kù)做結(jié)構(gòu)調(diào)整,目前調(diào)整后的結(jié)構(gòu)如下(不排除在對(duì)SVN更加了解以后會(huì)進(jìn)行調(diào)整):
權(quán)限設(shè)置如下:
trunk:僅管理員可修改(用于新建項(xiàng)目),下面各文件夾的內(nèi)容開發(fā)人員可讀寫(因?yàn)樾枰獜姆种ё龊喜⒉僮鳎?/p>
tags:僅部門經(jīng)理可寫(用于創(chuàng)建特定版本,比如用于發(fā)布、通測(cè)等),其他用戶只能讀。
branches:管理員和部門經(jīng)理可讀寫(用于清理很久不合并、不刪除的分支),每個(gè)開發(fā)人員有一個(gè)固定的目錄,只能讀寫自己的分支目錄,看不到別人的目錄。
假設(shè)場(chǎng)景:需要修改缺陷管理平臺(tái)上的#77問題,那么首先從主干創(chuàng)建分支。
創(chuàng)建主干的分支
創(chuàng)建分支有多種操作方法。
1.使用本地工作副本的主干上創(chuàng)建分支。
在本地工作副本的主干目錄(trunk)上,右鍵點(diǎn)擊然后選擇“分支/標(biāo)記…”
在彈出的窗口中,默認(rèn)的新分支的目標(biāo)URL將會(huì)是主干trunk的URL。你需要把這個(gè)URL修改為新分支的路徑。
將“至路徑”指向branches目錄并輸入分支的具體目錄名,這里是“問題077”,我們即將創(chuàng)建的分支便存放于此處,點(diǎn)擊確定。
建議:分支目錄名遵循一定的規(guī)則,比如使用缺陷管理平臺(tái)的問題號(hào),或者寫明問題名稱,這樣便于查詢這個(gè)分支是為解決什么問題而建立的。而且最好是一個(gè)問題一個(gè)分支。
現(xiàn)在你需要選擇從哪里復(fù)制。你有三個(gè)選擇:
1.版本庫(kù)中的最新版本:新的分支從trunk的最新版本復(fù)制到版本庫(kù)中。
注意:此時(shí),數(shù)據(jù)的拷貝發(fā)生在服務(wù)器上版本庫(kù)的內(nèi)部,而不是從你的工作副本發(fā)送到版本庫(kù)的branches目錄下,因此創(chuàng)建分支會(huì)非常迅速。
☆講完分支再講標(biāo)簽☆
2.版本庫(kù)中指定的版本:新的分支從你選擇的trunk的歷史版本復(fù)制到指定的分支目錄。
這個(gè)功能主要用來創(chuàng)建標(biāo)簽。關(guān)于什么是標(biāo)簽,后面會(huì)講到。比如:當(dāng)你上一周發(fā)布了一個(gè)產(chǎn)品出去,但忘記了創(chuàng)建標(biāo)記,這就很有用了。
如果你不記得版本號(hào),可以點(diǎn)擊右邊的按鈕來顯示版本日志,然后從中選擇版本號(hào)。這種方式也不需要從你的工作副本傳送數(shù)據(jù)到版本庫(kù),分支非常迅速的被創(chuàng)建。
☆最后來講切換☆
3.工作副本:通過上傳你的本地工作副本來創(chuàng)建新的分支。
這個(gè)方法非常的特殊,它允許你制造出非常復(fù)雜的分支!而不是項(xiàng)目的某個(gè)修訂版本中所有文件、目錄的完整副本。
假設(shè)某個(gè)項(xiàng)目保存了一組子目錄和許多文件,你可以通過選擇性的回滾某些文件和目錄到特定修訂版本,或者切換某些文件和目錄到特定分支,甚至你可以做各種本地修改。
這樣,你的本地拷貝就變成了一個(gè)包括特定特性和Bug修正的工作副本,形成了你所需要的一種精確數(shù)據(jù)組合。然后,你可以使用這個(gè)本地拷貝來生成分支。
當(dāng)然,如果你的本地修改不存在于版本庫(kù)中,這些變動(dòng)就會(huì)從你的工作副本傳輸?shù)桨姹編?kù),而那些回滾或切換的歷史版本的文件、目錄,SVN會(huì)在分支目錄中做快捷鏈接。
創(chuàng)建臨時(shí)文件夾:這個(gè)功能的意義是如果你所輸入的路徑在SVN版本庫(kù)中不存在,那么SVN會(huì)幫你創(chuàng)建所需要的目錄。
比如你輸入的分支路徑是/branches/羅樂/為融資將/德潤(rùn)/…/問題99,因?yàn)椤盀槿谫Y將/德潤(rùn)/…/”這些目錄事先沒有建立,勾選此項(xiàng)后,SVN會(huì)幫你創(chuàng)建這些中間目錄。
☆最后來講切換☆
切換工作副本至新分支/標(biāo)記:(不選)
這里需要講一下什么叫切換。如果你擁有同一個(gè)項(xiàng)目的多個(gè)分支,比如BranchA、BranchB,他們都包含了很多相同的文件和目錄,差異僅僅是幾個(gè)文件有所不同。那么實(shí)際上,你不必同時(shí)擁有BranchA、BranchB的本地拷貝,你可以先取出BranchA的代碼到本地目錄AB中,修改后提交,當(dāng)你需要修改BranchB中的文件時(shí),在本地目錄AB上使用“切換”,就可以只下載BranchB中不同于BranchA的那幾個(gè)文件到本地,從而使得本地目錄AB中存放的是BranchB的代碼,就可以進(jìn)行BranchB分支的修改工作了。
需要注意的是:如果目錄AB中原來存放的是BranchA的代碼,你做了本地修改沒有提交到BranchA中就進(jìn)行了切換,那這些本地修改會(huì)被合并到BranchB的代碼中。
仍然回到選項(xiàng)本身,如果想要將當(dāng)前的工作副本(也就是選中的trunk)自動(dòng)切換到新創(chuàng)建分支,那么就選中“切換工作副本至新分支/標(biāo)記”復(fù)選框。這樣一來,svn會(huì)下載代碼,使得你的本地trunk目錄中保存的就是新創(chuàng)建分支的代碼。
還是那句老話,如果你要這樣做,首先確認(rèn)你的本地trunk工作副本中不包含修改。如果存在修改,這些修改將會(huì)在你切換時(shí)合并到分支的工作副本中。
我本人不喜歡使用切換,因?yàn)檫@可能造成混淆。如果你的記憶力非常強(qiáng)、思路非常清晰,那么你可以使用切換。
其實(shí)除了可以在本地拷貝上右鍵創(chuàng)建分支,還有幾種不需要本地拷貝就可以創(chuàng)建分支的方法:
1. 打開版本庫(kù)瀏覽器。按下 Ctrl 鍵并拖動(dòng)你要?jiǎng)?chuàng)建分支的項(xiàng)目的文件夾到branches/xxx目錄下從而創(chuàng)建副本。
注意:你必須在拖拽過程中按下 Ctrl 鍵,否則文件夾是被移動(dòng),而不是被復(fù)制。
2.你可以使用鼠標(biāo)右鍵拖拽某個(gè)項(xiàng)目文件夾。
當(dāng)松開鼠標(biāo)右鍵時(shí)會(huì)出現(xiàn)菜單,讓你選擇移動(dòng)還是復(fù)制文件夾。
☆講完分支再講標(biāo)簽☆
3.從日志對(duì)話框中創(chuàng)建分支,這個(gè)功能通常用于創(chuàng)建歷史版本的標(biāo)簽,比如某個(gè)歷史版本需要做標(biāo)簽,但你忘了做。
具體操作:你可以對(duì)本地拷貝,或者在“瀏覽版本庫(kù)”中,對(duì)某個(gè)文件夾做“顯示日志”的操作,在日志對(duì)話框,選擇一個(gè)版本(可以是位于頂端的最新版本,也可以是先前的版本),右鍵單擊并選擇從版本創(chuàng)建分支/標(biāo)記。
推薦在版本庫(kù)中做分支操作,這樣你可以多次拖動(dòng)你本次修改需要用到的項(xiàng)目文件夾到分支目錄中,從而減小分支在本地拷貝的體積。
但需要注意的是,以后在做合并操作時(shí),源、目的url都必須是子目錄或具體的文件,否則未拖動(dòng)過來的根目錄的文件、目錄就視為被刪除了,一旦提交,就導(dǎo)致主干trunk中的這些文件、目錄被刪掉。
比如
取出分支開始修改
分支建立之后,就可以取出分支的代碼進(jìn)行修改工作了。
取出分支的操作很簡(jiǎn)單,到branches目錄下做“svn更新”即可。
實(shí)際上,你并不一定要在本地拷貝下的branches目錄下做更新,你可以在本地硬盤上的任何目錄下做分支url的更新,取出的都是版本庫(kù)上新創(chuàng)建的分支的代碼,之所以放在本地branches目錄下,是為了便于管理。
另外說一下,目前的SVN版本庫(kù)規(guī)劃是不同開發(fā)部門的所有項(xiàng)目都在一個(gè)版本庫(kù)中,導(dǎo)致每次在根目錄做更新時(shí),就會(huì)下載到其他部門的不相干的代碼。實(shí)際上,可以通過權(quán)限控制使每個(gè)部門只下載自己的代碼,以及需要共享的代碼,從而減小本地拷貝的大小。
這里講一下“切換”。切換是在本地拷貝上執(zhí)行的操作,假設(shè)你同時(shí)擁有多個(gè)分支,分支A、分支B,在項(xiàng)目頂級(jí)目錄上執(zhí)行切換動(dòng)作,可以下載特定的分支的本地拷貝,而且切換動(dòng)作只會(huì)下載并覆蓋存在差異的文件,從而減少網(wǎng)絡(luò)傳輸。
合并分支
合并操作非常靈活,可能存在以下情形需要用到分支:
1.當(dāng)你某個(gè)分支修改完畢并通過測(cè)試的時(shí)候,你就需要合并到主干上;
2.你同時(shí)擁有分支A、分支B,你發(fā)現(xiàn)分支A中存在一個(gè)BUG,然后你解決了這個(gè)BUG,此時(shí)你并不打算立即把AB合并到主干上去,那么你可以把這種修改合并到分支B去,以免以后合并分支B到主干的時(shí)候忘記這里為什么存在差異。
3.你正在修改某個(gè)分支,發(fā)現(xiàn)主干代碼發(fā)生了變化(你可以在修改分支時(shí),隨時(shí)更新主干),那么你需要把主干的這種變化合并到分支中,以免分支和主干越來越遠(yuǎn)。
合并和創(chuàng)建分支不同,創(chuàng)建分支可以不需要工作副本,直接在版本庫(kù)中操作,但合并必須在本地工作副本中進(jìn)行操作,合并成功后用戶檢查代碼的正確性,再提交到版本庫(kù)上。所以你必須取出合并操作的源和目的開發(fā)線的代碼到本地來操作。
另外,在合并操作之前,應(yīng)該確保:
①源開發(fā)線中的修改都已經(jīng)提交到版本庫(kù)中了,這樣源開發(fā)線的工作不會(huì)遺漏;
②目標(biāo)開發(fā)線中不存在本地修改!因?yàn)楹喜⒉僮靼l(fā)生在本地,如果目標(biāo)開發(fā)線中存在本地修改,這些修改可能被丟失,另外也增加了合并操作的復(fù)雜性。
點(diǎn)擊右鍵之后,會(huì)出現(xiàn)如下的對(duì)話框,提供了兩種合并方法。這些合并方法其實(shí)都差不多,僅存在細(xì)微的差別,
1.合并一個(gè)版本范圍
如果你在某個(gè)開發(fā)線上(分支或主干)上做了多次修訂(即多次修改并提交),使用這個(gè)方法就可以把“某個(gè)開發(fā)線上從修訂版本n到修訂版本m的修改”應(yīng)用到其他的開發(fā)線。
意義在于:讓你可以選擇不同開發(fā)線上特定的修改進(jìn)行合并,因此,你可以繼續(xù)在分支上進(jìn)行開發(fā),而只合并分支上的某一部分歷史修改(這通常意味著在該分支上修改了多個(gè)問題,不推薦這樣做)
Subversion 會(huì)做如下事情: “計(jì)算[從]開發(fā)線 A 的版本 n [到] 版本 m 所需的修改,并將這些改變應(yīng)用到開發(fā)線B(主干或分支)的工作副本上?!?/p>
2.合并兩個(gè)不同的樹
此合并方法的意義我并不是很理解,按照我自己的推測(cè):它比較的是兩個(gè)開發(fā)線的整體差異,并進(jìn)行合并。
比較通用的用法是:當(dāng)你完成某個(gè)分支的所有修改之后,你希望將這個(gè)分支的修改合并到主干上,就是用此方法,此時(shí),Subversion會(huì)做的動(dòng)作是:“計(jì)算[從]主干的某個(gè)版本變化[到]分支的某個(gè)版本所需要做的修改,并將這些修改應(yīng)用到(主干的)工作副本。”最終,主干與分支一模一樣。
注意:在這種情況下,你需要從一個(gè)主干的工作副本內(nèi)啟動(dòng)合并向?qū)А?/p>
標(biāo)簽
其實(shí)分支和標(biāo)簽沒有區(qū)別,創(chuàng)建分支和創(chuàng)建標(biāo)簽都使用相同的技術(shù)——拷貝,僅僅是因?yàn)镾VN管理員在標(biāo)簽tags目錄上的權(quán)限控制更加嚴(yán)格,導(dǎo)致只有特定的用戶(比如部門經(jīng)理)可以做寫入操作,而大多數(shù)用戶只能讀取而已。
(回到前面,講標(biāo)簽)
建立簡(jiǎn)單標(biāo)簽
建立復(fù)雜標(biāo)簽
分支管理:常用的分支模式
一.特性分支
特性分支是一個(gè)臨時(shí)分支,用來增加新特性、bug修正等操作,而不會(huì)干擾/trunk的穩(wěn)定性,特性分支誕生后,使用了一段時(shí)間,合并到主干,最終被刪除。
二.發(fā)布分支
大多數(shù)軟件存在這樣一個(gè)生命周期:編碼、測(cè)試、發(fā)布,然后重復(fù)。
這樣有兩個(gè)問題:
第一,當(dāng)某個(gè)假定穩(wěn)定版本(比如1.0α)出爐時(shí),測(cè)試組需要對(duì)其進(jìn)行測(cè)試,而開發(fā)者需要繼續(xù)為下一個(gè)大規(guī)劃版本(比如2.0版本)開發(fā)新特性,新增特性等開發(fā)工作在測(cè)試1.0α?xí)r不可以中斷;
第二,對(duì)于歷史發(fā)布版本(比如1.0版本)中的bug,客戶希望立刻得到修正而不必等到下一個(gè)大規(guī)劃版本(比如2.0)的發(fā)布才得以解決;如果一個(gè)bug在最新的代碼中發(fā)現(xiàn),它一定也存在已發(fā)布的版本中。
要解決這些問題,可以使用SVN的發(fā)布分支來實(shí)現(xiàn),具體過程如下:
·不論是直接修改trunk還是通過特性分支的方式,開發(fā)者提交所有的新特性、bug修正等到主干。
·當(dāng)版本管理團(tuán)隊(duì)認(rèn)為軟件已經(jīng)做好發(fā)布的準(zhǔn)備(如,版本1.0),主干trunk代碼被拷貝到“測(cè)試”分支,比如/test/1.0。測(cè)試小組開始對(duì) /test/1.0 進(jìn)行測(cè)試,開發(fā)小組在主干/trunk繼續(xù)為實(shí)現(xiàn) 2.0 版本繼續(xù)新的工作。
·此時(shí)進(jìn)入1.0和2.0的并行工作階段。如果一個(gè)bug在 /test/1.0 被發(fā)現(xiàn),那么這個(gè)錯(cuò)誤需要在 /test/1.0 和 /trunk上同時(shí)被修正。如果 /trunk 中發(fā)現(xiàn)了一個(gè)bug,通常也需要在 /test/1.0 同時(shí)做修改,但如果版本管理團(tuán)隊(duì)認(rèn)為 /test/1.0已經(jīng)完成了最終測(cè)試,那么此bug可以不納入 /test/1.0 中。
·當(dāng) /test/1.0 完成最終測(cè)試后(可能還有一些bug,但不打算在1.0中解決了),就可以拷貝到 /tags/1.0 目錄下作為1.0版本發(fā)布。
·當(dāng)繼續(xù)在/trunk上為版本2.0工作,bug修正繼續(xù)從/trunk運(yùn)送到/branches/1.0,如果積累了足夠的bug修正但版本2.0的規(guī)劃尚未達(dá)成,管理部門則決定發(fā)布1.0.1版本,拷貝/test/1.0(注意:這個(gè)/test/1.0的代碼是處于不斷變化中的)到/tags/1.0.1,1.0.1版本被打包發(fā)布。
·整個(gè)過程隨著軟件的成熟不斷重復(fù):當(dāng)2.0完成,一個(gè)新的2.0分支被創(chuàng)建,測(cè)試、打標(biāo)簽和最終發(fā)布。
另外,使用特性分支的策略也是多種多樣:
一些項(xiàng)目永遠(yuǎn)不使用特性分支:大家都可以提交到/trunk,好處是系統(tǒng)簡(jiǎn)單——沒有人需要知道分支和合并,壞處是主干會(huì)經(jīng)常不穩(wěn)定或者不可用;
一些項(xiàng)目使用分支達(dá)到極限:任何修改都不能直接提交到主干,即使最細(xì)小的修改都要?jiǎng)?chuàng)建短暫的分支,然后小心的審核合并到主干,然后刪除分支,相對(duì)方式繁瑣一些,但保持了主干一直穩(wěn)定和可用。
許多項(xiàng)目采用折中的方式,堅(jiān)持每次編譯/trunk并進(jìn)行回歸測(cè)試,只有需要多次不穩(wěn)定提交時(shí)才需要一個(gè)特性分支,這個(gè)規(guī)則可以用這樣一個(gè)問題檢驗(yàn):如果開發(fā)者在好幾天里獨(dú)立工作,一次提交大量修改(這樣/trunk就不會(huì)不穩(wěn)定。),是否會(huì)有太多的修改要來回顧?如果答案是“是”,這些修改應(yīng)該在特性分支上進(jìn)行,因?yàn)殚_發(fā)者增量的提交修改,你可以容易的回頭檢查。
最終,有一個(gè)問題就是怎樣保持一個(gè)特性分支“同步”于工作中的主干,在前面提到過,在一個(gè)分支上工作數(shù)周或幾個(gè)月是很有風(fēng)險(xiǎn)的,主干的修改也許會(huì)持續(xù)涌入,因?yàn)檫@一點(diǎn),兩條線的開發(fā)會(huì)區(qū)別巨大,合并分支回到主干會(huì)成為一個(gè)噩夢(mèng)。
這種情況最好通過有規(guī)律的將主干合并到分支來避免,制定這樣一個(gè)政策:每周將上周的修改合并到分支,注意這樣做時(shí)需要小心,需要手工記錄合并的過程,以避免重復(fù)的合并(在“手工跟蹤合并”一節(jié)描述過),你需要小心的撰寫合并的日志信息,精確的描述合并包括的范圍(在“合并分支到另一分支”一節(jié)中描述過),這看起來像是脅迫,可是實(shí)際上是容易做到的。
聯(lián)系客服