關于分支模式
并行軟件開發是企業級環境下軟件開發的一種不可避免的模式,這種開發模式可以說是任何大中型軟件產品和項目所必需的。然而,并行開發在為我們的開發效率提高保證的同時,也會給我們的開發管理帶來諸多問題:
* 什么時候進行分支?
* 什么時候進行合并?
* 如何選擇有效的分支策略?
* 如何保證不同分支上的代碼同步問題?
* 如果建立對分支訪問控制的授權機制?
* 如何避免頻繁的合并沖突?
* 如何處理被復用的代碼?
* ……
可以說并行開發中的分支與合并是一個涉及到環境、方法和技術平臺等諸多因素的綜合性難題,在這種場合下,“模式(Pattern)”可能是解決上述
問題的一個很好的工具。所謂模式,其實就是解決某一類問題的方法論。你把解決某類問題的方法總結歸納到理論高度,那就是模式。Alexander給出的經
典定義是:每個模式都描述了一個在我們的環境中不斷出現的問題,然后描述了該問題的解決方案的核心。通過這種方式,你可以無數次地使用那些已有的解決方
案,無需在重復相同的工作。
在不同的領域有不同的模式,具體到并行開發領域,“分支模式”是專門針對并行開發環境下分支及合并作業中的各種不同的操作方法抽象出來的一套方法論。其主要由以下幾部分組成:
結構模式——通過約束和指導分支/代碼線的整體結構,實現并行開發的組織結構、開發模式及開發過程的約束和指導。
規則模式——通過對特定分支/代碼線實施的約束,實現對該分支/代碼線相關的操作進行約束,如訪問控制及合并等操作的約束。
創建模式——提供對分支/代碼線創建的約束
反模式——以反例的方式展示并行開發中常見的行為誤區和陷阱,并提供有效的解決方案。
分支模式在并行開發中的應用難點有兩個:一是如何根據企業的實際情況選擇適合的分支模式,二是如何構建一個技術平臺來將這些分支模式的理論和方法有
效的應用于實踐。為此,我們專門在此開辟專欄和大家分享并行開發中分支模式相關的理論、方法以及如何將這些理論和方法付諸實現的相關實踐。
主線(結構模式)
一、分支模式的相關定義
模式 主線
別名 主干、主錨線、本線、地線(Main Trunk, Main Anchor Line, Home Line, Ground Line )
場景
在開發和維護周期中,因為各種原因需要創建多條代碼線,典型的代碼線是發布線、維護線和集成線。這在采用每發布代碼線、并行維護/開發線和重疊發布線 (或者其任何變形模式)的情況下尤為如此。隨著項目的進行,會創建出越來越多的代碼線,從而導致項目的版本樹越來越寬。

連續的瀑布式分支(應該避免的情況)
問題
怎樣確保當前活動代碼線的數量在可控范圍內,以及避免項目的版本樹過寬過深?
動機
- 通常,每條代碼線在某個時間點需要將變更集合并至其父分支。所以越多的代碼線就意味著更多的合并,而越多的合并意味著越多的同步工作。
- 當后續的版本發布時,看起來只有在當前版本的代碼線上開創一個新的分支才算是合理的。
解決方案
在每一個分支樹中保持一個“主”分支或代碼線作為主干,而不是持續的瀑布式從分支創建分支從而使分支樹變得寬廣而笨重(在每一對父子分支之間需要大量的同步工作)

擁有主線的瀑布式分支
當為主發布創建代碼線的時刻來到時,我們不會從前一個發布線創建新的發布線,而是將先前的發布線合并回主線,然后再從主線創建新的發布線。
為特定代碼線或分支合并回來的過程被稱為“主線化”、“主干化”、“歸位”、“錨定”或“接地”("mainlining," "trunking," "homing," "anchoring," or "grounding.")。
變種 穩定接收線(也稱為穩定主線、主集成線和基礎集成線(Stable Mainline, Main Integration Line, Base Integration Line))
保持一個穩定的,可靠的主要開發主干可以用來從其他代碼線導入(接收)穩定基礎。在代碼線上不會直接發生開發工作,所有集成工作必須來自其他代碼線(不是一個單獨的離散的活動分支)。這個規則唯一的例外是為了保證代碼線構建和功能一致性的集成變更。

(用以接收穩定基線的穩定主線——實際上就是通常所說的基線)
LAG開發線(也稱為主開發線、中央線和主流)
把主干作為最新的最好的(LAG)會一直進展的開發線,所有前面的發布的代碼線都會在其退休后被合并入其中。主干作為下一步/最后的開發發布(不是
維護,而是開發,包括顯著的改進和新特性)的開發線使用,因此當B2發布的工作已經準備好開始,而A1發布已經完成或逐漸停止,則建立一個新的分支來結束
A1的工作(見延遲分支),而LAG線則用來針對B2(最新的和最好的開發工作)發布的工作。

(LAG開發主線)
對于特定代碼線或分支合并到滯后(LAG)開發線的過程也可以稱為"LAGging," "mainlining," "LAG-lining," or "mainstreaming."。
盡管作為一個主線使用,LAG線也可以作為主線與穩定接收線結合:

(有一條穩定主線用于接收發布版本的LAG開發主線)
二、模式的分析
主線模式及其變種主要試圖從分支的結構上約束其不合理的擴展和延伸,而盡量使主線成為其他代碼線創建的來源及合并的目標。穩定接收線(也就是通常所
說的基線)用于接受并保存相對穩定的版本,通常情況下其只接受版本(通常情況下為直接保存版本的鏡像而非合并操作)而不進行其他任何操作。
三、主線模式在Subversion環境下的實現

如上圖所示實際上就是有一條基線伴隨的LAG開發線模式,實現該模式相關的約束包括:
1、所有的代碼線(不包括分支)都從主線創建
2、主線以外的代碼線的合并操作都以主線為目標
3、基線只接受版本(直接保存版本的鏡像而非合并操作)而不進行其他任何操作。
寬松訪問線(規則模式)
一、分支模式的相關定義
模式 寬松訪問線(Relaxed-Access Line)
問題 如何確定代碼線訪問控制規則的限制或排他程度?
動機
- 如果許多開發者在代碼線上工作,或某一些人缺乏經驗,那嚴格的治理是必要的。
- 如果代碼線上發生的工作具有顯著的風險級別或難度,則檢入和合并需要更緊密的監控和/或驗證。
- 保證代碼線一直處于完整的狀態非常重要,這樣就不會影響其它工作在代碼線上的人們。
- 如果代碼線對應了特定提升級別(見階段集成線)或生命周期階段,這也許指明了對特定代碼線驗證一致性的必要性級別。
解決方案
如果代碼線是用來開發或維護(而不是排它或集成),并且工作在代碼線上的團隊規模相對較小,人員富于經驗并可靠,那么給開發者有相對寬松自由的區
域,讓他們做他們已經知道如何去做的事情:在一起以及時的方式工作。使用最小檢查和控制,但是要確保代碼線所有者可以認真對待他的工作,并可以一直知道代
碼線的狀態以及確認完整性是否被特定風險/復雜開發任務危害。
相關模式
MYOC(合并你自己的代碼)及其變種PYOC(傳遞你自己的代碼)是使用寬松訪問線最常見的副產品(或原因),如果在你的環境中,風險較小,而且開發者合并自己的變更到代碼線的交流通暢,那么我們有充足的理由使用寬松訪問。
二、對模式的分析
這種訪問模式通常適用于沖突不嚴重(如開發初期或彼此按模塊獨立開發)的情況,要求相關人員具備一定的水準以保證開發的質量,而在開發的后期通常不適用這種模式。
三、寬松訪問線(Relaxed-Access Line)在Subversion環境下的實現

如上圖所示(相關內容只是相關模式實現的一個實例,實際使用時可根據實際需求對角色及授權進行調整):
1、代碼線的所有者對代碼線擁有完全的操作權限
2、主管及EPG成員僅對代碼線擁有有限的操作權限(讀)
3、除測試人員以外的項目成員都對代碼線擁有完全的讀寫權限
4、代碼線的所有者以及項目經理和配置經理擁有對代碼線鎖的權限
大爆炸式集成(反模式)
一、分支模式的相關定義
陷入的誤區 大爆炸集成
別名 大怪獸集成
癥狀
由于某種原因,一直不選擇集成,直到(軟件)要發布的時候
,才把所有的分支一下子全部交給倒霉的集成者進行集成。經常性的增量集成看起來是流行的常識性規則(亦稱為:盡早且經常合并),大爆炸(的方式)顯然在隔
離和避免風險方面達到了極致,這是大怪物地結束合并,結果不是一個“大爆炸”,而是以“哭泣”告終。

大爆炸式的集成(需要避免的反模式)
原因
一個原因是我們未能成功找到盡早集成和經常集成的地正確“節奏”和“脈搏”。有時‘大怪獸式集成’是“整合恐懼癥”帶來的附加品,在盡力縮小合并數
量的時候,結果會是一個延期而至的可怕的復雜合并的結尾。因而,在這種情況下,“集成是魔鬼”
變成了自我實現的一種預言,并且在失敗的惡性循環中不斷加強。
影響
太多人都很熟悉所造成的后果,直到一切已經太晚,如系統不能正確的構建,無法通過測試,或部分代碼與其它代碼工作不能協同工作,我們才能發現這問
題。直到項目生命周期的下一個階段,這些信息才得到交流,相關的風險和返工將會非常巨大,會導致“合并是魔鬼”或“合并恐懼”的心理。
修復和預防
使用盡早經常性的集成或使用一個或更多的變種來確保以一定頻率和間隔執行集成,以在時間充裕,額外工作較少時盡早分解風險和盡快交流問題區域。你可
以現在或者將來,或者由你自己決定何時付出,有規律的經常的集成可以迫使你在每次迭代和集成計劃中只需要付出很少的努力,通過分解減少隨時間積累的合并負
擔,從而定義了健康項目的“脈搏”。
二、模式的分析
這種大爆炸式的集成在缺乏有效管理的團隊中是經常發生的,我們經常可以看到這樣的場景:明天就要發版本了(尤其是非計劃的發布),今天所有的相關人員都一起合版本,于是大家驚訝的發現系統出現無數的合并沖突和缺陷,而在這種情況下,延遲發布幾乎是唯一的選項了。
要避免這種最好的方式就是通過某種機制約束分支的周期和合并。
三、大爆炸式集成在Subversion環境下的規避方式

如上圖所示,在創建分支時添加如下約束:
1、創建分支時定義分支的周期(通常任務分支都是需要及時終結的),并強制終結
2、創建分支時定義分支的合并周期和約束方式(包括提醒和強制合并兩種可選方式)
通過上述的約束,可以使分支/代碼線及時的合并和終結,從而避免大爆炸式集成的發生
代碼所有權(規則模式)
一、分支模式的相關定義
模式 代碼線所有權(Codeline Ownership)
別名 分支所有權(Branch Ownership )
場景
作為一名程序員,在一組多代碼線的環境下,并至少在一條代碼線上開發。代碼線規則已經為該代碼線定義好檢入/檢出的規則。有些人要在代碼線上進行某些工作,但是該規則并沒有允許這樣的操作,或者就是規則對一些特定事務的描述含糊不清。
問題
能影響代碼線的動作能否執行?在保證代碼線的完整性和連續性的同時,怎樣做上面的決策?
動機
- “理論上,實踐和理論是一致的;但在實踐中,其兩者卻有極大的不同。”沒有一個規則能夠涵蓋所有的情況。 代碼線規則從理論上滿足了需求,但是在實踐中,則還有必要其他一些東西補充理論和實踐之間的缺失。
- 如果代碼線規則不是很清楚,則開發人員需要將其定義清楚。
- 代碼線規則是會被違背的,不管是有意還是無意。
- 代碼線必須保持正確且連續的狀態,以避免與正在進行的開發任務起相反的作用。.
解決方案
為每個代碼線分配一名所有者(owner),其相應的職責如下
- 如果代碼線的規則定義不清晰,則定義清楚;
- 如果檢入的配置項與代碼線的規則相抵觸,則決定讓其保留在代碼線中,還是回退到上一個版本
- 制定相應的規則,防止代碼線處于含糊不清狀態或不適用實際情況
- 在該代碼線上,輔助或執行變更的集成
- 決定何時對代碼線進行凍結,解凍;何時代碼線必須結束生命周期并且合并到主線(Mainline)中
所有權(Ownership)并不一定意味著排他式的訪問,但卻表示用戶認證訪問控制。也許只有代碼線的所有者才能檢入文件(受限訪問線);或者,
其他人只要在檢入前獲得代碼線所有者的同意,即可檢入代碼線,又或者在檢入后立刻通知代碼線所有者(寬松訪問線)。代碼線規則必須清楚地定義訪問控制類型
相對應的所有權類型。通常來講,在代碼線上工作的開發人員越多,相應的所有權策略也越嚴格。同樣地,限制程度與代碼線包含活動的風險性或是復雜性,或是對
穩定性的要求成正比。在較小的項目和團隊中的代碼線中所包含較少的關鍵任務,在保證代碼線完整性和連續性的前提下,提供比較隨意,限制較少的訪問控制策
略。
變種 代碼線專屬(Codeline Dictatorship )
代碼線的所有權中極其嚴格的一種形式,其配置項的檢出和分支都是嚴格受限的,當然更包括檢入。專屬者可能是一個人,或是一個小組。一個常見的例子就
是“遠程開發線”。遠程開發人員可能被禁止從非遠程分支中創建新的版本或是分支,因此,本地分支僅僅是“主人(master)”開發的地方。這實質上就是
ClearCase Multisite定義的“分支主人身份(branch mastership)”的概念。
導致的場景
- 只有一個人對代碼線的連續性和完整性負責。這樣,代碼線比較可能處于一種穩定的狀態。
- 保持所有者對代碼線的狀態負責,降低了代碼線規則被踐踏或代碼線被用于錯誤目的的可能。
- 代碼線的概念完整性由一個頭腦,也就是所有者維護,作為解決代碼線問題的單一權威。
二、模式的分析
代碼線所有權/代碼線專屬模式強調的是代碼線所有者對代碼線的控制權,適用于由專人(或角色)對代碼線內容進行全權負責的情況下使用
三、代碼線專屬(Codeline Dictatorship )在Subversion環境下的實現

如上圖所示(相關內容只是相關模式實現的一個實例,實際使用時可根據實際需求對角色及授權進行調整):
1、代碼線的所有者對代碼線擁有完全的操作權限
2、主管、項目經理及配置經理僅對代碼線擁有有限的操作權限(讀)
3、代碼線的所有者擁有再授權的權限
代碼線規則(規則模式)
一、分支模式的相關定義
模式名稱 代碼線規則
別名 每代碼線規則
適用環境 使用多條代碼線開發軟件的情況下。
問題 開發人員如何知道需要將他們的代碼存入哪條代碼線中,并且何時保存?
動機
- 每條代碼線都有不同的目的 ;
- 代碼線的名稱通常能暗示其目的;
- 代碼線的名稱通常不能全部表達代碼線的使用要點;
- 如果代碼寫入到錯誤的代碼線,而這錯誤的變更必須要回退,導致生產率的降低;
- 使用正規文檔描述代碼線的用法會很有幫助,但是需要額外的記錄和維護;
- 這個文檔太過拘謹就會有一點過度規劃和專橫了;
解決方案
除了給分支/代碼線起一個有意義的名稱之外,要給每條代碼線明確目的,并使用簡捷明了的策略描述其目的。其中應該包括以下一些要點:
- 代碼線包含何種工作,例如:開發、維護、一種特定的版本、功能或是子系統;
- 配置項在怎樣的條件下才能被檢入,檢出,分支,合并;
- 對于不同的個人,角色,組,代碼線該設置怎樣的讀寫權限的限制;
- 導入/導出關系:代碼應該從其它哪些代碼線中接受變更,同時應該將變更應用于其它哪些代碼線;
- 代碼線的生命周期或結束條件;
- 預期的工作負載以及集成頻率。
讓規則簡短扼要:一個簡單的經驗方法是1-3段(各自25行25個字符,一頁絕對是上限)。
請切記不是所有的代碼線策略都需要上面所有的信息,只需要制定自己所需要的。一些版本控制工具允許在每個分
支、代碼線的名稱上附加詳細的注解,這是存放合適簡短代碼線規則描述的理想地方。開發者可以通過包含代碼線名稱的命令來查看代碼線規則,而無需在別的地方
找文檔。否則,將代碼線規則放在大家都知道的隨手可得的地方(或許提供簡單的命令或宏,對于給定代碼線名稱可以快速顯示規則)。
二、對模式的分析
代碼線規則這種模式實際上就是一種最基本的分支/代碼線使用規范,它強調每條分支/代碼線都應該以快捷而有效的方式記錄其相關的信息,并且這些信息可以隨時被方便的訪問。
作為更進一步的要求,除了將相關信息記錄在案,在某些情況下對其中部分內容(如分支的周期及合并的頻率等)進行提醒甚至約束也是有其必要性的。
三、寬松訪問線(Relaxed-Access Line)在Subversion環境下的實現

如上圖所示:
1、每條分支/代碼線代碼線創建時都有效的記錄相關信息
2、對分支的生命周期和合并周期提供約束控制
注:上述功能的實現是基于在系統底層屏蔽了所有不受控的分支創建操作,而只能在特定應用系統內進行分支/代碼線的創建,從而使所有分支/代碼線相關操作都處于受控狀態