一個成功的Git分支模型

本文翻譯至:nvie.com/posts/a-suc…php

譯者: TopJohngit

原文鏈接:www.xuanzhangjiong.top/2019/01/30/…github

版權歸做者全部,商業使用請聯繫做者shell

一個成功的Git分支模型

在這篇文章中,我將介紹一下在一年前很是成功的不只是工做也包括私人項目的開發模型。我一直想寫關於開發模型相關的內容,可是歷來沒有像如今這麼強烈。在這裏,我並不想將任何項目的細節,僅僅是想表達關於分支策略以及發佈管理相關的內容。 bash

git-model

爲何選擇git?

有關Git與集中式源代碼控制系統的優缺點討論,請參閱網站。在代碼的控制系統中,硝煙瀰漫。做爲一個開發者,在今天,相比於其餘工具我更喜歡Git。Git真的改變了開發者對合並和分支的想法。從我來看經典的CVS/Subversion世界,合併/分支一般被認爲是使人懼怕的(「當心合併衝突,它們會咬你!」),並且是每過一段時間都會作的事情。服務器

可是對於Git來講,這些操做是極其廉價和簡單的,它們真的被認爲是您平常工做流程中的核心部分之一。例如,在CVS/Subversion書籍中,分支和合並在後續的章節中首次討論(並且是對於高級用戶),可是在Git書籍中,它們在第三章中就出現了(基礎部分)。分佈式

因爲其簡單和重複的性質,分支和合並變得再也不那麼可怕。版本控制工具應該比其餘任何東西更有助於分支/合併。svn

當咱們有了足夠的瞭解,讓咱們進入開發模型中去。我將在這裏呈現的模型實質上只是一套每一個團隊成員都應該遵循的軟件開發模式。工具

分散但集中

咱們使用的倉庫和分支模型配合得不錯,是一個可信的中心化的倉庫,這裏指的是初始化的遠程倉庫。不過要注意,這是惟一的一個被認爲是中央庫的倉庫(由於Git是一個分佈式的版本管理系統,在技術層面上來說沒有一箇中心化的倉庫)。咱們會把這個倉庫稱爲origin(原始庫),由於它的名字對全部用戶來講都比較熟悉。post

centr-decentr

每個開發者將代碼拉取或者推送到origin(原始庫)中。可是除了集中式的推拉關係以外,每一個開發者也能夠從其餘節點獲取變動來造成子團隊。舉個例子,當兩個或者更多的開發者一塊兒開發一個新特性,這將變得很是有用,能夠避免過早地將代碼推送到原始庫中。在上圖中,Alice和Bob是一個子團隊,Alice和David是一個子團隊,Clair和David是一個子團隊。

在技術上,這意味着Alice定義了一個遠程分支,叫作bob,指向了Bob的倉庫,反之亦然。

主分支

在覈心部分,開發模式很大程度受到了傳統模型的影響。中央庫一直維護2個能夠無限延伸的分支:

  • master 分支
  • develop 分支

main-branches

在原始庫中,master分支應該被全部用戶所熟知。和master分支並行的還有另外一個分支叫作develop分支。

咱們把origin/master分支做爲主分支,這個分支代碼的HEAD指針老是指向生產版本代碼的一個準備狀態。

咱們把origin/develop分支做爲一個反映下一次須要交付的代碼變動的分支。有人會叫develop分支爲「集成分支」。這是全部夜間自動構建的出處。

當源代碼在develop分支中到達一個穩定的狀態以後,已經準備好發佈了,那麼全部的變動應該以某種方式被合併回master分支。同時打上版本號。這個操做會在後續進行詳細講解。

所以,每次當變動被合併到主分支的時候,將定義一個新的生產版本。咱們對此很是嚴格,所以從理論上來講,咱們可使用Git hook腳本在每次對master分支進行提交代碼的時候進行構建和在你的生產服務器上發佈你的軟件。

輔助分支

接下,來,除了master分支和develop分支你的開發模型上須要使用一系列的輔助分支來幫助團隊的成員平行地開發,輕鬆地追蹤一些特性,爲生產發佈作準備,並幫助快速修復生產版本代碼的問題。不像主分支,這些分支的生命週期是有限的,由於它們最終將會被移除。

咱們使用不一樣類型的分支:

  • Feature 分支
  • Release 分支
  • Hotfix 分支

這些分支每個都有特定的目的以及嚴格的規則,例如什麼分支能夠做爲它們的原始分支,什麼分支能夠做爲他們合併的目標。接下來咱們將展開討論。

從技術角度來說,這些分支絕對不是「特殊的」。這些分支的類型取決於咱們如何去使用它們。它們一樣是平凡的Git分支。

功能分支(feature branches)

功能分支可能從develop分支分離出來,可是必須合併到develop分支中去。

功能分支的命名規範:

除了master,develop,release-*,或者hotfix-*以外的名字均可以。

fb

功能分支(或者有時候叫作主題分支)是被用來開發即將發佈或者將來版本的新功能的分支。在開發新功能的時候,目標的須要合併的發佈版本可能在那個時候還不肯定。功能分支的本質是它存在於新功能開發的過程當中,最終將被合併到develop開發分支中去(爲一個發佈版本添加一個新特性)或者被丟棄(在實驗狀況使人失望的狀況下)。

功能分支一般僅存在於開發人員的倉庫中,而不是遠程的原始庫中。

建立一個功能分支

當要開發一個新功能的時候,通常都是從develop分支檢出一個新分支。

$ git checkout -b myfeature develop
Switched to a new branch "myfeature"
複製代碼

合併一個已經完成的分支到develop分支

完成的功能將會被合併到develop分支,以確保將它們添加到即將發佈的版本中;

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop
複製代碼

--no-ff標誌-將致使建立一個新的commit標誌,即便合併可使用fast-forward模式。這能夠避免丟失功能分支的歷史信息並將新特性和全部的提交合併到一塊兒。比較:

merge-without-ff

在後一種狀況,是不可能從Git歷史中看到哪些提交記錄實現了這個功能-你必須經過手動讀取全部的log記錄才能獲取到相關信息。恢復整個功能(即一組提交),在後一種狀況下是真的很頭疼,可是若是使用了--no-ff標誌就很容易完成。

雖然將多處幾個(空的)提交記錄,可是它的收益遠大於成本。

發佈分支(Release branches)

可能一個release分支是從develop分支切出來的,可是它必須被合併到develop或者master分支上去,分支的命名慣例通常是:release-*

Release分支用於支持新的生產版本的發佈。它容許在最後作一些點綴或者小修改。此外,它們容許修復小錯誤而且準備發佈的元數據(例如版本號,構建日期等)。經過在發佈分支上完成全部這些工做,develop分支將更明確地準備下一個大版本的發佈。

從develop分支建立release分支的關鍵時刻是develop分支達到了一個想要發佈的理想狀態。至少此次想要發佈的特性必須被合併到develop分支在切出release分支以前。全部打算在將來發布的版本中特性須要在release分支切出來以後在合併進去。

在release分支開始的時候爲即將發佈的版本分配一個版本號--而不是在release分支以前。直到那一刻,develop分支反映的變化都是爲了下一次發版,可是至於下一次發版是0.3仍是1.0是不清楚的,在release分支切出來以前。這個決定是在release分支開始的時候根據項目的規定定的,版本號也是根據項目的要求定的。

建立一個release分支

Release分支從develop分支建立。舉個例子,假設1.1。5版本是當前的生產分支,咱們即將推出一個大版本。目前develop分支的狀態是已經好爲下一個release作好了準備,而且決定下一個版本將是1.2版(而不是1.1.6或者2.0)。因此咱們切出分支,而且附上一個新的版本號:

$ git checkout -b release-1.2 develop
Switched to a new branch "release-1.2"
$ ./bump-version.sh 1.2
Files modified successfully, version bumped to 1.2.
$ git commit -a -m "Bumped version number to 1.2"
[release-1.2 74d9424] Bumped version number to 1.2
1 files changed, 1 insertions(+), 1 deletions(-)
複製代碼

在建立並切換到release分支以後,咱們設置了版本號。這裏,bump-version.sh虛構了一個shell腳本,改變一些文件來指向一個新的版本。(這裏固然能夠手動修改一些文件-來代表某些文件變了。)而後,設置的版本號就被提交了。

這個新分支會存在一段時間,直到發佈版本被正式推出。在這段時間,問題修復將在這個分支進行(而不是在develop分支)。在這裏進行一些大的新特性的添加是不被容許的。它們必須被合併到develop分支,等待下一次大版本的發佈。

完成release分支

當release分支的狀態到達了一個真正的發佈版本的時候,咱們須要進行一些操做,以便完成此次發佈。首先,release分支須要被合併到master分支中(由於根據定義master上的每一次提交都是一次新的發佈,記住)。接下來,在master上的這個提交必須被打上標記,以便未來參考此歷史版本。最終,在release分支上的變動須要合併到develop分支上去,以便後續的發佈可以包含這些問題修復。

Git中的前兩步是:

$ git checkout master
Switched to branch 'master'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2
複製代碼

此次發佈就算完成了,標記將用於將來的引用。

編輯:你可能還想以加密方式用-s或者-u 標誌來對標記進行簽名。

在Git中,爲了保持此次發佈中的變動,咱們須要將這些變動合併到develop分支中去:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)
複製代碼

這個步驟可能致使一些合併衝突(可能因爲咱們改變了版本號)。若是發生了,須要修復從新提交。

如今咱們已經完成,發佈分支或許須要被移除,由於咱們再也不須要這個分支:

$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).
複製代碼

熱修復分支(Hotfix branches)

熱修復分支可能來源於master分支,可是必須被合併到develop和master分支,分支命名慣例:hotfix-*

hotfix-branches

Hotfix分支很是像release分支,它們都是爲一個新的生產分支作準備的,儘管Hotfix是計劃以外的。它們源於生產版本一些不被指望的狀態。在生產版本出現了嚴重的錯誤須要馬上被修復,一個熱修復分支須要根據master分支上生產版本的標記進行切出。

本質是團隊的其餘成員能夠繼續在develop分支上開發,與此同時須要有一我的來在Hotfix分支上進行bugfix。

建立熱修復分支(hotfix branch)

熱修復分支從master分支上建立。舉個例子,1.2版本是當前運行的生產版本,因爲嚴重的錯誤致使了一些問題。可是如今develop分支是不穩定的。咱們須要切出一個hotfix分支開始修復問題:

$ git checkout -b hotfix-1.2.1 master
Switched to a new branch "hotfix-1.2.1"
$ ./bump-version.sh 1.2.1
Files modified successfully, version bumped to 1.2.1.
$ git commit -a -m "Bumped version number to 1.2.1"
[hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1
1 files changed, 1 insertions(+), 1 deletions(-)
複製代碼

在建立完分支以後不要忘記更新版本號!

接下來,修復錯誤,提交一個或者多個記錄。

$ git commit -m "Fixed severe production problem"
[hotfix-1.2.1 abbe5d6] Fixed severe production problem
5 files changed, 32 insertions(+), 17 deletions(-)
複製代碼

完成熱修復分支

當完成了,bugfix須要合併到master分支,可是爲了保障下次發佈版本可以包含bugfix,一樣須要被合併到develop分支。 這和release分支的實現是相似的。

首先,更新master分支並打上發佈標籤:

$ git checkout master
Switched to branch 'master'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2.1
複製代碼

編輯:你可能還想以加密方式用-s或者-u 標誌來對標記進行簽名。

接下來,將bugfix合併到開發分支,一樣:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)
複製代碼

規則的一個例外是:當一個release分支已經存在的狀況下,熱修復分支的變更須要合併到release分支中,而不是develop分支。 在release分支發佈完成的時候,若是將錯誤修復合併到了release分支一樣也會反映到develop分支中最終。(若是develop分支馬上須要這個問題修復並且等不及release分支修復,那麼你能夠安然地合併問題修復到develop分支中。)

最終,移除這個臨時的hotfix分支:

$ git branch -d hotfix-1.2.1
Deleted branch hotfix-1.2.1 (was abbe5d6).
複製代碼

總結

雖然這個分支模型沒有什麼使人震驚的地方,可是在文章開頭的這張大圖在你的項目中是異常有用的。這造成了一個優雅的思惟模型,並且是很是容易讓人理解的,容許團隊成員創建一個共同的分支和發佈的模型。

此處提供一個高質量的PDF版本。把它掛牆上以便隨時參考。

更新:若是有人須要的話:這裏是文章中Git圖片的源文件gitflow-model.src.key

我的公衆號
相關文章
相關標籤/搜索