翻譯自:https://nvie.com/posts/a-successful-git-branching-model/
在這篇文章中,主要介紹 Git 分支模型。不會談論任何項目的細節,只討論分支策略和發布管理。
Git分布式和集中式理解
我們配置了中央存儲庫可以很完美的配合該分支模型工作。這里需要注意下,這個倉庫只是被認為 是中央倉庫(因為Git是DVCS(分布式版本管理系統),在技術層面上沒有中央倉庫)。我們將這個中央倉庫稱為origin
,應該所有Git用戶都熟悉這個名稱。
每個開發人員都會從中央庫 pull 並 push origin。但除了集中式pull push 關系之外,每個開發人員還可以從其他開發人員的庫中獲取更改以形成子團隊。例如,在將正在開發的代碼push origin 之前,獲取到其他開發人員的代碼。這對於與一個大的新功能上的兩個或更多開發人員一起工作可能是有用的 。在上圖中,有alice和bob,alice和david以及clair和david的子團隊。
從技術上講,這意味着Alice已經定義了一個Git遙控器,名為bob
,指向Bob的存儲庫,反之亦然。
主要分支
在核心,開發模型受到現有模型的極大啟發。中央倉庫擁有兩個主要分支,具有無限的生命周期:
master
develop
該master
分支在origin
應該存在於每一個用戶的Git。另一個與master並行的
分支是develop
。
我們認為origin/master
是主要分支,這個分支HEAD源碼始終反映生產就緒狀態 ,簡單來說就是master分支上的代碼與生產使用的代碼始終保持一致。這樣有個好處就是,當生產代碼出現緊急bug的時候,可以快速從master上fork出一個hotfix分支用來修復bug並發布,而不會因為修復線上bug,影響正在開發過程中的下一個版本的代碼
我們認為origin/develop
是主要開發分支,其HEAD源碼始終反映了下一版本中最新交付的開發更改的狀態。有些人稱之為“整合分支”。這是可以用來建立夜間自動構建的分支。如果我們對此非常嚴格的執行,從理論上講,我們可以使用Git鈎子腳本在每次提交時自動構建和推出我們的項目到我們的測試服務器。
當develop
分支中的源代碼到達穩定點並准備好發布時,所有更改都應以某種方式合並到master
,然后使用版本號進行標記。如何執行后面將詳細討論。
因此,每次將更改合並回master時,根據我們的定義,這就是一個新的生產版本。
支持分支
接下來除了兩個主分支master
和develop
,我們的開發模型使用各種支持分支來幫助團隊成員之間的並行開發,輕松跟蹤功能,准備生產版本以及幫助快速修復實時生產問題。與主分支不同,這些分支的壽命有限,因為它們最終會被刪除。
我們使用的不同類型的分支分別是:
- 功能分支 命名方式:feature-*
- 發布分支 命名方式:release-*
- 修補bug分支 命名方式:hotfix-*
這些分支中每一個都有特定的目的,並且有着嚴格的規則:從哪些分支中fork出來,又合並到那些分支中。
分支類型根據我們如何使用它們進行分類。
功能分支
分支出自:develop
必須合並回:develop
分支命名約定:最好是 feature-[功能名],當然如果是想自己定義其他名字只要不是master
, develop
, release-*
, or hotfix-* 就都可以
功能分支主要用於為下一個版本開發新功能。在開始開發功能時,此功能的發布版本可能在此處未知。功能分支的本質是,只要功能處於開發階段,它就會存在,但最終會合並回develop
(以便將新功能添加到即將發布的版本中)或丟棄(在產品經理放棄這個功能的時候)。
功能分支通常僅存在於開發人員本地存儲庫中,而不存在於origin
。
創建功能分支
在開始處理新功能時,從develop
分支分支。
$ git checkout -b myfeature develop Switched to a new branch "myfeature"
在開發中加入完成的功能
完成的功能分支會合並到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,最新版中git merge 默認的就是--no-ff。這樣可以避免丟失功能分支的歷史信息,並將所有添加功能的 commit 組合到一個commit中。對比:
在后一種情況下,不可能從Git歷史中看到哪些 commit 實現了一個功能 - 您必須手動讀取所有日志消息。恢復整個功能(即一組提交)在后一種情況下也是比較頭痛的,而如果使用該--no-ff
標志則很容易完成 。
雖然它會創建一些(空的)commit,但增益遠遠大於成本。
發布分支
分支出自:develop
必須合並回:develop 和 master
分支命名約定:release-[版本號]
發布分支主要用來發布新的版本到生產。它可以用來修復最后一分鍾的bug,當在發布的過程中發現了新的bug,可以直接在release分支中修改。develop
分支將接收下一個大版本的功能。
需要注意的是在develop上創建一個新的發布分支的時候,develop分支的代碼應該是測試完畢后准備發布的代碼
,至少下一個版本所有的功能都已經合並到develop
分支 。
當新建了發布分支分配新的版本號,從這個時候開始develop分支反映的將應該是下一個版本的代碼。比如新建了release-1.6 后 1.6版本的代碼將不再允許提交到develop分支中。
創建發布分支
發布分支是從develop
分支創建的。例如,假設版本1.1.5是當前的生產版本,我們即將推出一個大版本。狀態develop
為“下一個版本”做好了准備,我們已經決定這將版本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(-)
創建新分支並切換到它后,我們會更新版本號。這 bump-version.sh
是一個虛構的shell腳本,它可以更改工作副本中的某些文件以反映新版本。(這當然可以是手動更改 - 關鍵是某些文件會發生變化。)然后提交了最新的版本號。
這個新分支可能存在一段時間,直到新版發布。在此期間,可以在此分支中修復bug(而不是在develop
分支上)。嚴禁在此處添加大型新功能,新功能必須合並到develop
等待下一個大版本。
完成發布分支
當release分支准備好真正發布的時候,需要執行一些操作。首先,release分支合並到 master
(因為每次提交master
都是按照定義的新版本)。接下來,master
必須標記 (tag) 該提交,以便將來參考此歷史版本。最后,需要將發布分支上的更改合並回來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 <key>
標記以加密方式對您的標記進行簽名。
為了保持release分支中所做的更改,我們需要將這些更改合並到develop
。在Git中:
$ 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).
修補程序分支
分支出自:master
必須合並回:develop 和 master
分支命名約定:hotfix-*
hotfix 分支主要用來修復生產的緊急bug,比如當開發人員正在 feature、develop 分支 開發下一個版本的功能,而生產出現了緊急bug 必須立刻修復並發布。而你又不想把當前未完成的版本發布到生產,這個時候我們可以在 master 分支上 fork 一個新的 hotfix 分支用來修復bug
,這樣的話就不會影響到下一個版本的開發。
創建修補 Bug 分支
從master
分支創建修復 bug 分支。例如,假設版本1.2是當前生產版本正在運行並且由於嚴重錯誤而影響生產正常使用。但是develop分支代碼
仍然不穩定。然后我們可以 fork 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(-)
新建分支后不要忘記標記小版本號!
然后,修復 bug 並提交一個或多個單獨 commit。
$ git commit -m "Fixed severe production problem" [hotfix-1.2.1 abbe5d6] Fixed severe production problem 5 files changed, 32 insertions(+), 17 deletions(-)
完成修補 Bug 分支
完成后,需要將hotfix分支合並回master
,同時也需要合並回develop
,以保證修復bug的代碼也包含在下一個版本中。這與發布分支的完成方式相似。
首先,更新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 <key>
標記以加密方式對您的標記進行簽名。
接下來,合並hotfix 到 develop
:
$ git checkout develop Switched to branch 'develop' $ git merge --no-ff hotfix-1.2.1 Merge made by recursive. (Summary of changes)
此處有一個例外就是, 在當前release分支存在時,只需要將hotfix分支合並到該release分支中即可,而不是develop
。將hotfix分支合並到release分支中,修復的代碼最終也會在release分支完成時被合並到develop
。(當然如果develop立刻
需要此修復bug代碼,不能等到release分支完成,您也可以直接地將hotfix合並到develop
。)
最后,刪除這個臨時分支:
$ git branch -d hotfix-1.2.1 Deleted branch hotfix-1.2.1 (was abbe5d6).