最近專案開始做大起來了,需要團隊協作。由於之前一直是在 master 分支上開發,現在專案新增人手,就需要換個方案了。找了挺多資料的,發現有篇講的比較全。就轉發翻譯了一下。
原文:A successful Git branching model
反思筆記(2020年3月5日)
這個模型是在 2010 年構思出來的,而現在距今已有 10 多年的歷史,而 Git 本身才誕生不久。在那 10 年中,git-flow「本文介紹的分支模型」在許多軟體團隊中非常流行,以至於人們開始將其視為某種標準,但不幸的是,它也被當作教條或靈丹妙藥。
在那 10 年中,Git 本身就席捲了整個世界,並且與 Git 一起開發的最流行的軟體型別正在越來越多地轉向 Web 應用程式-至少在我的過濾泡中。 Web 應用程式通常是連續交付的,不會回滾,並且您不必支援在野外執行的軟體的多個版本。
這不是我十年前寫部落格時想到的那種軟體。如果您的團隊正在持續交付軟體,我建議您採用更簡單的工作流程「例如 GitHub Flow」,而不是嘗試將 git-flow 引入您的團隊。
但是,如果您正在構建顯式版本的軟體,或者如果您需要狂野地支援軟體的多個版本,那麼git-flow 仍然可能像適合您團隊的人一樣適合您的團隊最近 10 年。在這種情況下,請繼續閱讀。
總而言之,請始終記住萬靈藥不存在。考慮您自己的情況。別討厭自己決定。
在這篇文章中,我介紹了大約一年前我為我的一些專案「在工作中和在私人專案中」引入的開發模型,事實證明該模型非常成功。我一直想寫一陣子,但是直到現在為止我還沒有真正找到時間這樣做。我不會談論任何專案的詳細資訊,而只會談論分支策略和釋出管理。
為什麼是 git?
有關 Git 與集中式原始碼控制系統相比的優缺點的詳盡討論,請參見 Web。那裡發生了許多火焰大戰。作為開發人員,我比今天所有其他工具都更喜歡Git。 Git 確實改變了開發人員對合並和分支的看法。在我來自經典的CVS / Subversion 的世界中,合併/分支一直被認為有點嚇人「當心合併衝突,它們會傷害你!」,而您卻偶爾會做一次。
但是,使用Git,這些操作非常容易且簡單,實際上,它們被視為日常工作流程的核心部分之一。例如,在CVS / Subversion 書籍中,分支和合並首先在後面的章節中(針對高階使用者)進行討論,而在每本 Git 書籍中,第3章(基礎知識)都已經進行了介紹。
由於其簡單性和重複性,分支和合並不再是令人害怕的事情。版本控制工具應該比其他任何工具都更有助於分支/合併。
有關這些工具的足夠多的知識,讓我們進入開發模型。我將在此處介紹的模型本質上不過是每個團隊成員必須遵循的一組程式才能進入托管軟體開發過程。
分散但集中
我們使用的儲存庫設定與該分支模型一起很好地使用,它是一箇中央「真實」儲存庫。請注意,此儲存庫僅被視為中央儲存庫「由於 Git 是 DVCS,因此在技術層面上沒有中央儲存庫之類的東西」。我們將此倉庫稱為起源,因為所有 Git 使用者都熟悉該名稱。
每個開發人員都拉起並推到原點。但是,除了集中的推拉關係之外,每個開發人員還可以從其他同伴那裡拉出變更以組成子團隊。例如,在將進行中的工作過早地推向市場之前,與兩個或多個開發人員一起使用一項重大的新功能可能會很有用。在上圖中,有 Alice 和 Bob,Alice 和David 以及 Clair 和 David 的子團隊。
從技術上講,這僅意味著 Alice 定義了一個名為 bob 的 Git 遙控器,該遙控器指向 Bob 的儲存庫,反之亦然。
主要分支
從根本上說,開發模型受到了現有模型的極大啟發。中央儲存庫包含兩個無限生命的主要分支: * master * develop
每個 Git 使用者都應該熟悉原始的 master 分支。與 master 分支平行,存在另一個分支稱為develop。
我們認為 origin/develop 是 HEAD 原始碼始終反映狀態的主要分支,該狀態具有下一發行版的最新交付開發更改。有人將其稱為「整合分支」。這是構建任何夜間自動構建的地方。
當 develop 分支中的原始碼達到穩定點並準備釋出時,所有更改應以某種方式合併回 master,然後用釋出號標記。如何進行詳細的討論將在後面進行討論。
因此,每次將更改合併回主版本時,根據定義,這是一個新的生產版本。我們通常對此非常嚴格,因此從理論上講,每當主伺服器上有提交時,我們就可以使用 Git 鉤子指令碼自動將軟體構建和推出到生產伺服器。
分支行
除了主要分支機構的掌握和開發之外,我們的開發模型還使用各種支援分支機構,以幫助團隊成員之間進行並行開發,簡化功能跟蹤,為生產釋出做準備並協助快速解決生產中的實際問題。與主要分支不同,這些分支的生命週期總是有限的,因為它們最終將被刪除。
我們可以使用的不同型別的分支機構是:
- 功能分支
- 釋出分支
- 修補程式分支
這些分支中的每一個都有特定的用途,並受嚴格的規則約束,即哪些分支可能是其原始分支,哪些分支必須是其合併目標。我們將在一分鐘內逐步解決它們。
從技術的角度來看,這些分支絕不是「特殊的」。分支型別是根據我們的使用方式進行分類的。它們當然是普通的舊 Git 分支。
功能分支
可能從以下分支:
develop
必須合併回:
develop
分支命名約定:
除了master,develop,release- * 或 hotfix- * 以外的任何東西
功能分支「或有時稱為主題分支」用於為即將釋出或遙遠的將來版本開發新功能。當開始開發功能時,此時可能不知道將合併該功能的目標版本。功能分支的本質是隻要功能在開發中就存在,但是最終會合並回到開發中「以確保將新功能新增到即將釋出的版本中」或丟棄「以防實驗令人失望」。
功能分支通常僅存在於開發人員儲存庫中,而不存在於源中。
建立功能分支
當開始使用新功能時,請從 develop 分支中分支出來。
$ git checkout -b myfeature develop
切換到新的分支 「myfeature」
在開發中加入完成的功能
可以將完成的功能合併到 developer 分支中,以確保將它們新增到即將釋出的版本中:
$ git checkout develop
切換到分支 「develop」
$ git merge --no-ff myfeature
更新 ea1b82a..05e9557 「變更摘要」
$ git branch -d myfeature
已刪除分支 myfeature「之前是05e9557」
$ git push origin develop
–no-ff 標誌使合併始終建立一個新的提交物件,即使合併可以通過快進來執行。這樣可以避免丟失有關要素分支歷史存在的資訊,並將所有新增了要素的提交分組在一起。比較:
在後一種情況下,無法從 Git 歷史記錄中看到哪些提交物件一起實現了功能—您將不得不手動讀取所有日誌訊息。在後一種情況下,還原整個功能「即一組提交」確實很頭疼,而如果使用 –no-ff 標誌則很容易做到。
是的,它將建立更多「空」提交物件,但收益遠大於成本。
釋出分支
可能從以下分支:
develop
必須合併回:
develop 和 master
分支命名約定:
release-*
釋出分支支援新產品版本的準備。他們允許最後一刻對細節一絲不苟。此外,它們允許進行較小的錯誤修復併為釋出準備後設資料「版本號,構建日期等」。通過在發行分支上完成所有這些工作,將清除 develop 分支以接收下一個大型發行版的功能。
從 develop 分支出新版本分支的關鍵時刻是 develop「幾乎」何時反映新版本的所需狀態。此時至少必須合併要構建的發行版的所有功能。面向將來發行版的所有功能可能都不會—它們必須等到發行分支分支出來之後。
正是在發行分支的開頭,為即將釋出的版本分配了版本號-較早的版本。直到那一刻,develop 分支都反映了「下一個發行版」的更改,但是尚不清楚該「下一個發行版」最終將變為 0.3 還是1.0,直到發行分支開始。該決定是在釋出分支的開始時做出的,並且由專案的版本號增加規則來執行。
建立一個釋出分支
釋出分支是從開發分支建立的。例如,說版本 1.1.5 是當前的生產版本,我們即將釋出一個大版本。開發狀態已準備就緒,可用於「下一個發行版」,我們已經決定將其變成 1.2 版「而不是1.1.6或2.0版」。因此,我們分支並給發行分支起一個反映新版本號的名稱:
$ git checkout -b release-1.2 develop
選擇一個新分支 「release-1.2」
$ ./bump-version.sh 1.2
檔案修改成功,版本升至1.2。
$ git commit -a -m "將版本提高至 1.2"
[release-1.2 74d9424] 將版本號推送到 1.2
1 files changed, 1 insertions(+), 1 deletions(-)
建立新分支並切換到該分支後,我們更改版本號。在這裡,bump-version.sh 是一個虛構的shell 指令碼,它會更改工作副本中的某些檔案以反映新版本。 「當然,這可以是手動更改-關鍵是某些檔案會更改。」然後,提交被修改的版本號。
這個新分支可能在那裡存在了一段時間,直到可以肯定地釋出該版本為止。在此期間,錯誤修復程式可能會應用於此分支「而不是develop分支」。嚴格禁止在此處新增大型新功能。必須將它們合併到開發中,因此,請等待下一個重要版本。
完成釋出分支
當釋出分支的狀態準備好成為真實發布時,需要執行一些操作。首先,將release分支合併到master中「記住,由於 master 上的每次提交都是新的發行,請記住」。接下來,必須標記對主提交的提交,以便將來輕鬆參考此歷史版本。最後,需要將對發行分支所做的更改重新合併到開發中,以便將來的發行版也包含這些錯誤修復程式。
Git 的前兩個步驟:
$ git checkout master
切換到 'master'
$ git merge --no-ff release-1.2
遞迴合併「更改摘要」
$ git tag -a 1.2
該版本現已完成,並已標記以供將來參考。
編輯:您也可能希望使用-s或-u 標誌以加密方式對標籤進行簽名。
為了保留在 release 分支中所做的更改,我們需要將這些更改重新合併到 develop 中。在Git中:
$ git checkout develop
切換 'develop'
$ git merge --no-ff release-1.2
遞迴合併「更改摘要」
此步驟很可能導致合併衝突「因為我們已經更改了版本號」。如果是這樣,請修復它並提交。
現在我們已經完成了,並且釋放分支可能已刪除,因為我們不再需要它了:
$ git branch -d release-1.2
刪除了分支版本1.2「為 ff452fe」
修補程式分支
可能從以下分支: master 必須合併回: develop 和 master 分支命名約定: hotfix-*
修補程式分支與釋出分支非常相似,儘管它們是計劃外的,但它們也旨在為新的生產版本做準備。它們源於對不期望的實時生產版本立即採取行動的必要性。當必須立即解決生產版本中的嚴重錯誤時,可以從標記生產版本的 master 分支上的相應標記中分支出一個修補程式分支。
實質是團隊成員的工作(在 develop 分支)可以繼續,而另一個人正在準備快速的生產修復。
建立修補程式分支
修補程式分支是從 master 分支建立的。例如,說 1.2 版是當前的生產版本,正在執行,並且由於嚴重的錯誤而引起麻煩。但是發展的變化還不穩定。然後,我們可能會分支出一個修補程式分支並開始解決問題:
$ git checkout -b hotfix-1.2.1 master
切換到新的分支 「hotfix-1.2.1」
$ ./bump-version.sh 1.2.1
檔案修改成功,版本升至1.2.1。
$ git commit -a -m "Bumped version number to 1.2.1"
[hotfix-1.2.1 41e61bb] 將版本號推送到1.2.1
1 files changed, 1 insertions(+), 1 deletions(-)
別忘了在分支後增加版本號!
然後,修復該錯誤,並在一個或多個單獨的提交中提交此修復程式。
$ git commit -m "Fixed severe production problem"
[hotfix-1.2.1 abbe5d6] 解決了嚴重的生產問題
5 files changed, 32 insertions(+), 17 deletions(-)
完成修補程式分支
完成後,該錯誤修復程式需要重新合併到母版中,但也需要重新合併到開發中,以保護該錯誤修復程式也包含在下一個版本中。這與釋放分支的完成方式完全相似。
首先,更新 master 並標記發行版。
$ git checkout master
切換到 'master'
$ git merge --no-ff hotfix-1.2.1
遞迴合併「更改摘要」
$ git tag -a 1.2.1
編輯:您也可能希望使用-s或-u 標誌以加密方式對標籤進行簽名。
接下來,在 develop 分支中也包含錯誤修正:
$ git checkout develop
切換到 'develop'
$ git merge --no-ff hotfix-1.2.1
遞迴合併「更改摘要」
該規則的一個例外是,噹噹前存在發行分支時,需要將修補程式更改合併到該發行分支中,而不是進行開發。在發行分支完成後,將錯誤修正回合併到發行分支中,最終也會導致該修正修正也被合併到開發中。 「如果開發人員中的工作立即需要此錯誤修復,而不能等待發布分支完成,則現在也可以安全地將錯誤修復合併到開發中。」
最後,刪除臨時分支:
$ git branch -d hotfix-1.2.1
刪除了分支修補程式 1.2.1「為 abbe5d6」
總結
儘管該分支模型沒有什麼真正令人震驚的新東西,但這篇文章開頭的「大圖」圖在我們的專案中被證明是非常有用的。它形成了一個易於理解的優雅思維模型,並允許團隊成員對分支和釋出過程形成共識。
本作品採用《CC 協議》,轉載必須註明作者和本文連結