天天看點

如何選擇 Git 分支模式?

如何選擇 Git 分支模式?

分支的目的是隔離,但多一個分支也意味着維護成本的增加。我們可以分别從開發和釋出分支的多寡,做個簡單組合,即:

  • 主幹開發,主幹釋出。
  • 分支開發,主幹釋出。
  • 主幹開發,分支釋出。
  • 分支開發,分支釋出。

設想兩個不同的場景:

  • 如果一個軟體,隻有一個開發者,隻需要一個釋出版本,那他需要什麼樣的分支模式?
  • 如果一個軟體,有 10 位開發者,需要支援多個版本,那他們又需要什麼樣的分支模式?

一個好的分支模式,可以大大提高軟體的開發、內建和釋出效率。選擇什麼樣的分支政策,是每一個開發團隊開始工作時面臨的第一個問題。那麼,選擇什麼樣的分支模式才适合我們呢?在回答這個題之前,我們先了解一下幾種常見的分支模式。

主流的分支模式

常見的分支模式有 TBD(即主幹開發模式)、Git-Flow 模式、Github-Flow 模式及 Gitlab-Flow 模式。

TBD(主幹開發模式)

即所有開發者,僅在一個開發分支(即主幹)上進行協作開發的模式,在這種模式下,不允許建立任何長期存在的開發分支,有且僅保留主幹分支進行開發協作。

因為沒有長期分離的其他開發分支,任何代碼變更持續地更新到主幹上,在一定程度上避免了 merge 代碼帶來的困擾。同時,在這種開發模式下,建議采用釋出分支的政策,根據軟體版本的釋出節奏拉出釋出分支。在 TBD 模式下,所有的修改都是在主幹上,哪怕是缺陷的修改也是,修改完缺陷後,再 cherry pick 到釋出分支上。

其特點總結一下就是:

  • 有且僅有一個開發分支,即主幹分支。
  • 所有改動都發生在主幹分支。
  • 釋出可以從主幹拉釋出分支。
  • 主幹上進行的修複需要根據缺陷的修複政策,确定是否 cherry pick 到對應版本的釋出分支。
如何選擇 Git 分支模式?

因為團隊共享一個開發分支,并且在開發分支上進行內建驗證,而每次代碼送出都會觸發內建驗證,這就要求每次代碼的變更在主幹上都能快速地驗證,以确定是否接受下一次代碼變更(每次代碼變更都應該基于前一個穩定的版本進行),為了保證主幹一直處在可工作狀态,這就需要:

  • 每一次的變更要小,這樣在驗證的過程中才能控制範圍。
  • 快速完成驗證,這就要求有相對完善的自動化檢查驗證機制。

是以,主幹開發模式可以說是持續內建的關鍵推動者。主幹開發模式非常利于持續內建,并且根據穩定和主幹基線,做到随時釋出,以達到持續傳遞。但這些是建立在團隊成熟的協作能力和相對成熟的工程配套的基礎上,快速地對主幹的變更送出完成編譯、檢查及驗證;同時,因為采取釋出分支的實踐方式,在産品版本、分支、部署場景的對應關系需要梳理清楚,避免釋出分支混亂,及缺陷修改在各分支上的修複政策。

因為主幹開發要求每次變更送出都要小,并且要快速驗證完,保證主幹是處在可釋出狀态。對于一些處在開發過程中的特性,如每次變更送出,并非意味着完整特性的完成,為了隔離“特性半成品”對主幹的影響,一般會采用特性開關(Feature Toggle)的方式進行隔離。即頻繁的代碼變更送出,可以先做內建及驗證,但是在釋出的角度,通過(Feature Toggle)先隐藏相關特性,隻有當特性都完成之後,才打開開關,特性完全透出。

但是,特性開關的引入也并不是沒有成本,因為特性開關是配置,本質上跟我們常常用到的宏定義(#if #else)沒啥差別,從本質上,它也是一種代碼的分支。特性開關的使用,在一定程度上讓你的代碼變得更脆弱。是以,特性開關的使用,是建立在良好的代碼設計基礎上。

為了彌補諸如特性開關這樣針對某個特性開發的需要,而且現在軟體開發中,越來越多的團隊共同協作在一起完成某一個特性這樣的場景,一種針對特性開發的分支模式就應運而生,這就是特性分支開發模式,最有代表性的就是 Git-Flow。

Git-Flow

Git-Flow 是為了解決多個不同特性之間并行開發需要的一種工作方式。當開始一個特性的開發工作的時候,從主幹上拉出一個特性分支,所有的關于該特性的開發工作都發生在這個特性分支上,當完成該特性的工作之後,再把特性分支合并回代碼主路徑上,并準備釋出。

Git-Flow 有以下幾種分支:

  • feature 分支:開發者進行功能開發的分支。
  • develop 分支:對開發的功能進行內建的分支。
  • release 分支:負責版本釋出的分支。
  • hotfix 分支:對線上缺陷進行修改工作的分支。
  • master:儲存最新已釋出版本基線的分支。

每個特性都有屬于自己的開發分支,即 feature 分支,當一個開發者需要在兩個特性上進行工作的時候,他需要做的是通過 check out 指令在兩個分支之間進行切換。這樣做的目的是防止開發過程中,兩個特性開發工作的互相幹擾。

特性開發過程中,需要針對該特性進行單獨驗證,當該特性并驗證通過之後,merge 到一個叫做 develop 分支(大部分時間與 master 分支相近)的內建分支中,對整個軟體進行驗證。develop 分支永遠儲存都是最近的未釋出版本,當 develop 分支的代碼被驗證可釋出之後,單獨從 develop 分支拉出 release 分支進行釋出。

當拉出 release 分支進行釋出過程中,如果發現缺陷,缺陷的修複發生在 release 分支上,所做的缺陷修改再持續同步到 develop 分支上。當 release 分支被釋出完,其代碼的最終版本會再次分别同步給 develop 分支和 master 主幹上。我們可以發現, master 上永遠儲存的是可工作版本的基線。develop 分支保證的是開發內建中最新的版本。

Git-Flow 引入了一種叫做 hotfix 的分支,專門用于線上缺陷的修複。當缺陷修複完,再內建到 develop 分支,及同步到 master。其實,我們可以了解 hotfix 是一種特殊的 feature 分支,隻是它的變更送出在內建到 develop 分支的同時需要同步到 master。

如何選擇 Git 分支模式?

是不是覺得這個模式很專(fu)業(zha)的樣子,那我們通過開發者做一個特性開發,按 happy path 捋捋:

  • 開發者接到一個開發需求,從 develop 分支拉一個 feature 分支。
  • 大家完成自己本地的開發工作,完成本地驗證,送出代碼到 feature 分支。
  • 基于 feature 分支進行驗證,并持續合并新的開發代碼。
  • 完成特性的開發,并且 feature 分支驗證無誤,将 feature 分支的代碼合并到 develop 分支。
  • 在 develop 分支進行內建驗證(此處,可能和其他合并進來的特性分支一起進行驗證),內建驗證完畢,feature 分支會被删除。
  • 當 develop 分支是一個成熟的釋出版本時,如完成了徹底的測試及問題的修複,拉出 release 分支進行釋出。
  • 完成釋出之後,将 release 分支合并入 develop 和 master(master 儲存的永遠都是已釋出的最新代碼),并删除 release 分支。

Hotfix 的流程如下:

  • 如果釋出之後,發現了缺陷,基于 master 拉出一個 hotfix 分支。
  • 在 hotfix 對問題進行修改及驗證。
  • 問題的修複合并到 develop 和 master 上。
  • 删除 hotfix 分支。

Git-Flow 的分支模式,提供了相對完備的各種分支,以覆寫軟體開發過程中的大部分場景,以緻于在相當長的一段時間内,人們認為這就是标準的 Git 的分支模式(因為從它的名字上看,也很容易産生這樣的幻覺)。但是,Git-Flow 也存在着明顯的一些問題,如:

  • 分支特别多,而且每類分支都有特定限定的用法,開發者很難記住什麼分支是幹什麼的。
  • 整個分支模式過于複雜,大大超出大部分團隊和項目的需求。
  • feature 分支的生命周期過長導緻的合并沖突。如果一個特性所在 feature 分支生命周期過長,它跟 develop 分支的差異就越大,這樣,在該特性內建到 develop 的過程中,潛在的代碼沖突将是內建的噩夢。
  • 像 develop 分支,感覺其實存在的意義不是太大,完全通過 master 就可以替代內建的作用,額外為變更送出的內建引入 develop 分支,對分支模式來說,變得更加的複雜;同樣,如果取消 develop 分支,那麼,hotfix 分支存在的意義也就沒有必要了,因為這個時候,hotfix 分支與 feature 分支就沒有任何的差别。

那麼,有沒有一種分支模式,既包含開發任務對于主線的隔離,又相對 Git-Flow 輕量一點?我認為,真正的解決方案,應該是本質極簡單的。這裡,我們就不得不給大家介紹另一種分支模式,叫做 GitHub-Flow。

GitHub-Flow

在 GitHub-Flow 上,第一步就是沒有 Git-Flow 中所介紹的 release 分支。對于 GitHub-Flow 來說,釋出應該是持續地,當一個版本準備好,它就可以被部署。同樣,在 hotfix 上的處理,GitHub-Flow 認為,hotfix 與那些小的特性修改沒有任何差別,它的處理方式也應該與之相似。

在 GitHub-Flow 的整體流程是:

  • 在 master 分支上的所有代碼都應該是最新可部署、可工作的版本。
  • 如果要進行新的工作,從 master 分支上拉出一個新的分支,并以工作任務清晰命名,如 “new-scheduling-strategy”。
  • 盡可能頻繁地送出代碼變更到本地分支,與此同時,盡可能頻繁地同步到服務端相同分支名的分支。
  • 當準備合并代碼到 master 主幹分支上,通過發起 Pull Request,提請代碼評審。
  • 通過代碼評審後,或與此同時,需要将該分支部署到測試環境,進行驗證。
  • 如果評審通過及驗證通過,代碼則合并到 master 主幹分支上,應該立即部署到生産環境。
如何選擇 Git 分支模式?

GitHub-Flow 相比 Git-Flow 來說,有個顯而易見的好處——簡單。另一個好處就是持續部署的要求,盡可能快速地發現 master 分支的問題,并能通過 rollback 等機制,快速恢複。将所有内容合到 master 分支中,并經常部署,意味着你可以最小化未釋出的代碼量,這也是精益開發和持續傳遞所倡導的最佳實踐。部署原本是一件很繁瑣的事情,但是因為要頻繁做,我們就容易把這樣一件事情做簡單,以達到持續傳遞的目的。

雖然 GitHub-Flow 簡化了 Git-Flow 的分支模式,但是對于部署、環境、以及釋出,該分支模式仍然存在許多未回答的問題,是以,我們希望通過 GitLab-Flow 來為這些問題提供更多的參考。

GitLab-Flow

GitLab-Flow 相比于 GitHub-Flow 來說,在開發側的差別不大,隻是将 pull request 改成了 merge request,而 merge request 的用法與 pull request 類似,都可以做為代碼評審、擷取回報意見的一種溝通方式。

最大的差別展現在釋出側,即引入了對應生産環境的 production 分支和對應預發環境的 pre-production 分支(如果有預發環境的話)。這樣,master 分支反映的是部署在內建環境上的代碼,pre-production 分支反映的是部署在預發環境的代碼,production 分支反映的最新部署在生産環境的代碼。

當一個特性開發完成,送出 merge request,将特性開發的代碼合并到 master,并部署到內建環境進行驗證;當驗證通過之後,送出 merge reqeust,合并 master 到 pre-production 分支,并部署到預發環境,進行預發環境上驗證;當預發環境驗證成功之後,再送出 merge request,将 pre-production 分支上的代碼合并到 production 分支上。

如何選擇 Git 分支模式?

除了以上這種按環境,将主幹釋出向下遊合并,并依次部署釋出的過程。GitLab-Flow 同樣支援不同版本的釋出分支,即不同的版本會從 master 上拉出釋出分支,不同的釋出分支再走 pre-production 分支和 production 分支的方式進行釋出。

從上面的介紹中,我們發現,GitLab-Flow 更多是在釋出側做了更多的工作。同樣 GitLab-Flow 因為跟 GitLab 工具強依賴,是以 GitLab-Flow 與 GitLab 中的 Issue 系統也有很好的內建,在其推薦的工作模式中,每次建立一個新的 feature 分支,都是從一個 issue 上發起的,即建立 issue 與 feature 開發分支之間的映射。

pros vs cons

是以,如果我們還是按照開發和釋出的分支多寡來分類的話,以上這些分支模式分别屬于:

  • TBD 應該是主幹開發,可以是分支釋出,也可以是主幹釋出。
  • Git-Flow 應該是分支開發,分支釋出。
  • GitHub-Flow 應該是分支開發,主幹釋出。
  • GitLab-Flow 支援分支開發,但支援主幹釋出,也支援分支釋出。
如何選擇 Git 分支模式?

了解了常見的這些分支模式之後,我們在工作中就可以根據自身的業務特點和團隊規模來選擇适合的實踐,沒有絕對好的模式,隻有适合的模式。

根據團隊自身和項目的特點來選擇最适合的分支實踐應該從哪些地方考量呢?

選擇合适的實踐

首先是項目的版本釋出周期。如果釋出周期較長,則 Git-Flow 是最好的選擇。Git-Flow 可以很好地解決新功能開發、版本釋出、生産系統維護等問題;如果釋出周期較短,則 TBD 和 GitHub-Flow 都是不錯的選擇。GitHub-flow 的特色在于內建了 pull request 和代碼審查。如果項目已經使用 GitHub,則 GitHub-Flow 是最佳的選擇。GitHub-Flow 和 TBD 對持續內建和自動化測試等基礎設施有比較高的要求。如果相關的基礎設施不完善,則不建議使用。

如何選擇 Git 分支模式?

另外,從需要釋出版本的多寡的角度來看:

  • 支援一個産品多個釋出版本,用 Git-Flow。
  • 支援一個簡單産品單個釋出版本,用 GitHub-Flow 或 TBD。
  • 支援一個複雜産品單個釋出版本,用 GitLab-Flow。

如果發現,現有主流的分支模式都無法滿足你的要求,那麼,我們可以定義自己的分支模式,如我們有個團隊是基于主幹開發的,于是定義了春夏秋冬分支模式,春天用“春分支”,夏天用“夏分支”......,我個人就蠻喜歡的,原因有二,一是做到了主幹開發,持續釋出,二是名字起得很有意思,開發工作一定要有樂趣,不是麼?

參考

[1]TBD:

https://trunkbaseddevelopment.com/

[2]A successful Git branching model:

https://nvie.com/posts/a-successful-git-branching-model/

[3]Learn Version Control with Git:

https://www.git-tower.com/learn/git/ebook/cn/command-line/advanced-topics/git-flow

[4]Branching Models and Best Practices for Abstract - Design Version Control:

https://projekt202.com/blog/2018/branching-models-and-abstract

[5]Understand the GitHub-Flow:

https://guides.github.com/introduction/flow/

[6]GitHub Flow:

http://scottchacon.com/2011/08/31/github-flow.html

[7]Introduction to GitLabFlow:

https://docs.gitlab.com/ee/workflow/gitlab_flow.html

繼續閱讀