替代的分支模型
分支:僅在必要時使用,當提交政策不相容時使用,盡可能推遲建立,以及作為凍結版本的替代方案。
— Laura Wingerd & Christopher Seiwald1998 年 Perforce 使用者大會《High-Level SCM Best Practices》白皮書
現代主張的高吞吐量分支模型
GitHub Flow
這與以 PR 為中心的主幹開發非常接近。為什麼呢?因為這是一種分支模型,其中個別開發者或者是配對開發者同時在多個(短暫存在的)分支(或 fork)中活動。
關鍵的區別在於從哪裡進行發布。
GitHub Flow 顯示的是在合併回主幹之前從分支進行發布的步驟:
(分支圖示的說明)
這裡存在一個問題,那就是如果新版本已經推出,但相應的分支沒有及時合併回主幹,那麼下一個版本可能會小概率出現錯誤重新出現的問題。還有一點是,分支中可能缺少主幹上原本應有的某些東西,這些東西是之前版本中的一部分(這種情況實際上也會造成錯誤重新出現)。
就像 GitHub 文件中所說的,審查評論是流程的一部分。評論在時間線上像對話氣泡一樣出現,之後會有另一個提交,這個提交通常是用來回應審查意見的。
主幹開發如何修改 GitHub Flow 模型:
(分支圖示的說明)
當一切塵埃落定,短期功能分支被刪除後,提交的記錄不會像在 Subversion 和 Perforce 中那樣被合併成一個更大的提交。相反,它們會各自排列在提交歷史中的相應位置,這樣的提交歷史並不像我們在這裡展示的那樣線性:
(分支圖示的說明)
當然,如果你將一系列提交進行 rebase 或合併(squash),它們可以作為一個單一的提交進入主幹。而且,即使分支被刪除,審查評論也會保留下來。
GitFlow 及類似流程
GitFlow 是與主幹開發相互矛盾的。
在現代,有許多人推崇這種模型,認為它有很大的擴展空間,並且幾乎沒有缺點。這是一種分支模型,能讓一組開發者可以同時在多個分支(或分叉)上進行活動
(分支圖示的說明)
看起來你無法使用這種分支模型進行連續發布的並行開發,或者利用功能標誌和抽象分支來降低風險。
傳統分支模型
多個主幹
這表面上看起來沒問題,但有很多陷阱。建議不要使用這種模型。
有些人會使用單一版本庫,但包含多個主幹(和許多分支——不論是否為發布分支)。這在沒有大小限制(包括歷史記錄)的版本控制系統中並不少見。這樣做至少允許在多個主幹之間進行原子提交,這在進行大規模重構時會發生,而原子提交始終是可行的。
root/
module_one/
branches/
rel_1.0.0/
rel_1.1.0/
trunk/
build_file.xml
src/
# 產品程式碼的目錄結構
# 測試程式碼的目錄結構
module_two/
branches/
rel_1.0.0/
rel_1.1.0/
trunk/
build_file.xml
src/
# 產品程式碼的目錄結構
# 測試程式碼的目錄結構
module_three/
branches/
rel_1.0.0/
rel_1.1.0/
trunk/
build_file.xml
src/
# 產品程式碼的目錄結構
# 測試程式碼的目錄結構
如果你在程式碼版本庫中,對所有單獨的主幹採取同步發布(相同節奏),擁有多個主幹會變得不理想,而且你會需要為這些主幹進行「分支發布」。這會讓從頭建置整個專案變得更難。更好的做法是只有一個主幹,裡面具有三個模組,並使用遞迴建置系統,或現代的有向圖建置系統,比如 Buck 或 Bazel。
root/
branches/
rel_1.0.0/
rel_1.1.0/
trunk/
module_one/
build_file.xml
src/
# 產品程式碼的目錄結構
# 測試程式碼的目錄結構
module_two/
build_file.xml
src/
# 產品程式碼的目錄結構
# 測試程式碼的目錄結構
module_three/
build_file.xml
src/
# 產品程式碼的目錄結構
# 測試程式碼的目錄結構
這樣至少你可以建立一個代表發布的分支(隨著錯誤被修復,進行 cherry-pick)。 即使有不同的發布節奏,你也可以這樣設計主幹。只需要一個建置系統,能夠減少正在測試和部署的模組,並且跳過那些不需要的模組。參見單一版本庫和擴大與收縮單一版本庫。
主線
主線(Mainline)與主幹開發的概念完全相反——不要這樣做。
主線是一個在 ClearCase 實現中推廣的分支模型,它是主幹開發所反對的主要分支模型。主線是一個將永遠存在的分支✱。 基於此,團隊會建立分支來進行開發工作。當工作完成後,可以從該分支進行發布,然後進行一次大規模的合併回主幹。在發布過程中,分支可能會被凍結。
這就是主線模型的示意圖:
(分支圖示的說明)
當錯誤不可避免的發生:
(分支圖示的說明)
每次修復錯誤後,必須將其合併到主幹。這個修改後的分支圖沒有錯誤,但你應該能猜到最糟糕的分支/合併情況。如果你猜不到的話:
(分支圖示的說明)
針對上述情況的合併
- 1.1 版本團隊說服 1.0 版本團隊在他們分支之前,將一些東西提前(且不完整)合併回主線。
- 1.1 版本團隊在 1.0 版本工作看似完成後進行合併。
- 1.0 版本團隊將發布後的錯誤修復合併回主線,並祈禱 1.0 分支現在可以真正結束。
- 1.2 版本團隊說服 1.1 版本團隊在他們分支之前,將一些東西提前(且不完整)合併回主線。
- 1.1 版本團隊從主線進行合併,接收第 3 步的內容。
- 1.2 版本團隊在 1.1 版本工作看似完成後進行合併。
- 1.1 版本團隊將發布後的錯誤修復合併回主線,並祈禱 1.0 分支現在可以真正結束。
- 1.2 版本團隊從主線進行合併,接收第 7 步的內容。
所有這些妥協與計劃中的「接連開發連續版本」相違背,在很多情況下會更糟糕,特別是當開發者數量增加時。
有一點需要注意,相比於主幹開發,使用主線分支模型的團隊幾乎從不進行 cherry pick。不管什麼原因,他們都是進行「合併所有尚未合併的內容」這種合併。
最基本的是,他們使用的版本控制系統應該具有「合併點追蹤」功能。在更複雜情況下,還應該包括「僅記錄」合併,以及之後的正常合併。
✱ 我們認為,選擇「主線」模型的公司會逐漸衰退甚至消失,所以並不會一直存在。
合併
發布之後,程式碼會大量合併回主線。這些合併可能會很困難且耗時。可能團隊在項目進行過程中從主線進行合併,也可能團隊將合併推送到主線。
有多少分支?
我們剛剛描述了兩個分支模型——主線和專案分支。可能這個應用在任何時候都有多個正在進行中的專案,這意味著會有多個專案分支,這會帶來更多的中間合併壓力,從而導致合併難度增加。
隨時準備發布?
根本不可能!計劃的工作需要完成,並且需要有估算來指導何時完成。缺陷需要被消除,正式的測試階段需要啟動。在這裡,我們在第一個分支圖上新增紅色、橙色和綠色來顯示已知的建置中斷、建置通過但缺少自動化測試的情況,這些可能會隱藏缺陷,綠色表示可以上線。至少對於那些缺少或無效的自動化測試在 CI 流水線中運行的最差情況:
(分支圖示的說明)
瀑布模型
瀑布模型與主幹開發不兼容——不要這麼做。
這種方法的想法是每個版本都有自己的分支,每個版本團隊每天從「上游(upstream)」分支進行合併。他們只有在 CI 伺服器顯示上游建置通過時才會這樣做。
(分支圖示的說明)
這種模型存在的問題會隨著同時處理的發布版本數量增加而加劇。上游的變化可能會導致下游難以合併的巨大變動。下游的合併開始被跳過或放棄。或者合併成功,但程式碼錯誤,因此需要在分支內進行修復,而這些修復對上游並不適用。現實情況如下(再次疊加顯示的中斷情況):
(分支圖示的說明)
請記住,在這個模型中,合併從來不是 cherry-pick ——而是合併所有尚未合併的內容(或者合併到選定的提交號碼,以便更易於處理)。
當然,只有較大的組織才需要擔心連續發布的並行開發,而且許多人可能會認為應用程式本身過於龐大(而微服務是解決方案)。
持續整合對你的分支模型的證明或反駁
這裡有個想法。配置你的 CI 伺服器,無論使用的是什麼分支模型都讓它關注每個分支。具體來說,對每個提交進行建置,並進行變革推動者中描述的推測可合併性分析。
如果所有地方都是綠色的,那麼你就可以隨時準備好發布。但是,很少有團隊能在 CI 伺服器的積極建置和分析下看到綠燈,而不是紅燈。
其他參考資料
31 Aug 2011, Blog Entry |
GitHub Flow |
04 Dec 2013, Blog Entry |
What is Your Branching Model? |
05 Apr 2013, Blog Entry |
What is Trunk-Based Development? |
19 Mar 2013, Blog Entry |
The Cost of Unmerge |
15 Oct 2015, InfoQ Interview |
More Feature Branching Means Less Continuous Integration |
03 May 2015, Blog Entry |
GitFlow considered harmful |
08 Jan 2016, Blog Entry |
GITFLOW HMMMM |