主流的分支管理有以下幾種:
Git Flow
簡(jiǎn)介

首先,項(xiàng)目存在兩個(gè)長(zhǎng)期分支。
主分支master
開發(fā)分支develop
前者用于存放對(duì)外發(fā)布的版本,任何時(shí)候在這個(gè)分支拿到的,都是穩(wěn)定的分布版;后者用于日常開發(fā),存放最新的開發(fā)版。
其次,項(xiàng)目存在三種短期分支。
功能分支(feature branch)
補(bǔ)丁分支(hotfix branch)
預(yù)發(fā)分支(release branch)
一旦完成開發(fā),它們就會(huì)被合并進(jìn)develop或master,然后被刪除。
Git Flow 偏向于控制管理,使用了較多的分支,流程頗為復(fù)雜。大量的團(tuán)隊(duì)在實(shí)踐過程中也遇到了頗多問題,其中大部分來自長(zhǎng)期存在的分支。
問題
合并沖突,合并沖突在使用 Git Flow 是非常常見的。原因很簡(jiǎn)單:如果你有多個(gè)并行功能分支,他們長(zhǎng)時(shí)間存在,那么很可能代碼庫的相同部分在兩個(gè)功能分支中被分別更改。合并沖突不僅對(duì)于需要手動(dòng)解決的開發(fā)人員來說是令人沮喪的,也增加了在代碼中破壞某些功能的風(fēng)險(xiǎn),因?yàn)楫?dāng)你不得不決定使用哪個(gè)版本代碼時(shí),很容易犯錯(cuò)。
功能分離,在合并到同一個(gè)分支之前,你不能測(cè)試兩個(gè)功能的組合。當(dāng)你在單獨(dú)的分支中開發(fā)幾天甚至幾周的功能時(shí),當(dāng)合并回主分支后,可能也會(huì)發(fā)生兩個(gè)功能的相互作用影響了你的代碼。
并沒有做到持續(xù)交付,在 Git Flow 分支模型下,發(fā)布是非常有計(jì)劃的,一個(gè) feature 必須要經(jīng)過一系列步驟才能到達(dá)生產(chǎn)環(huán)境,在時(shí)間上平均一個(gè) feature 都要等待 兩周時(shí)間才能長(zhǎng)線,這樣的等待并非是需求上的“按計(jì)劃發(fā)布”,而是從技術(shù)上就造成了發(fā)布瓶頸,顯然難以達(dá)到持續(xù)交付的要求。
與持續(xù)集成相悖,你會(huì)發(fā)現(xiàn),在堅(jiān)持持續(xù)集成實(shí)踐的情況下,feature 分支是一件非常矛盾的事情。持續(xù)集成鼓勵(lì)更加頻繁的代碼集成和交互,讓沖突越早解決越好。feature 分支的代碼隔離策略卻在盡可能推遲代碼的集成。
GitHub Flow
簡(jiǎn)介

它只有一個(gè)長(zhǎng)期分支,就是
master,因此用起來非常簡(jiǎn)單。
官方推薦的流程如下:
第一步:根據(jù)需求,從
master拉出新分支,不區(qū)分功能分支或補(bǔ)丁分支。第二步:新分支開發(fā)完成后,或者需要討論的時(shí)候,就向
master發(fā)起一個(gè)pull request(簡(jiǎn)稱PR)。第三步:Pull Request既是一個(gè)通知,讓別人注意到你的請(qǐng)求,又是一種對(duì)話機(jī)制,大家一起評(píng)審和討論你的代碼。對(duì)話過程中,你還可以不斷提交代碼。
第四步:你的Pull Request被接受,合并進(jìn)
master,重新部署后,原來你拉出來的那個(gè)分支就被刪除。(先部署再合并也可。)
GitHub Flow是一個(gè)更輕量級(jí)的軟件開發(fā)模型。它摒棄了 Git Flow 中繁雜的分支, 只保留一個(gè)主分支 master 。開發(fā)新功能時(shí)從 master 分支上拉取 feature 分支,開發(fā)完成后發(fā)起 Pull-Request ,小組內(nèi)進(jìn)行評(píng)審和反饋,此時(shí)也進(jìn)行 Code Review 。測(cè)試通過后合并回主分支。
相比于 Git Flow,這種方式因?yàn)槭∪チ艘恍┓种Ф档土藦?fù)雜度,同時(shí)也更符合持續(xù)集成的思想,以一張故事卡為集成的最小單位,相對(duì)來說集成的周期短,反饋的速度也快,能夠及早的遇到問題并及早解決。
GitLab Flow
簡(jiǎn)介
Gitlab flow 是 Git flow 與 Github flow 的綜合。它吸取了兩者的優(yōu)點(diǎn),既有適應(yīng)不同開發(fā)環(huán)境的彈性,又有單一主分支的簡(jiǎn)單和便利。它是 Gitlab.com 推薦的做法。
上游優(yōu)先
Gitlab flow 的最大原則叫做"上游優(yōu)先"(upsteam first),即只存在一個(gè)主分支master,它是所有其他分支的"上游"。只有上游分支采納的代碼變化,才能應(yīng)用到其他分支。
Chromium項(xiàng)目就是一個(gè)例子,它明確規(guī)定,上游分支依次為:
- Linus Torvalds的分支
- 子系統(tǒng)(比如netdev)的分支
- 設(shè)備廠商(比如三星)的分支
持續(xù)發(fā)布
Gitlab flow 分成兩種情況,適應(yīng)不同的開發(fā)流程。

對(duì)于"持續(xù)發(fā)布"的項(xiàng)目,它建議在master分支以外,再建立不同的環(huán)境分支。比如,"開發(fā)環(huán)境"的分支是master,"預(yù)發(fā)環(huán)境"的分支是pre-production,"生產(chǎn)環(huán)境"的分支是production。
開發(fā)分支是預(yù)發(fā)分支的"上游",預(yù)發(fā)分支又是生產(chǎn)分支的"上游"。代碼的變化,必須由"上游"向"下游"發(fā)展。比如,生產(chǎn)環(huán)境出現(xiàn)了bug,這時(shí)就要新建一個(gè)功能分支,先把它合并到master,確認(rèn)沒有問題,再cherry-pick到pre-production,這一步也沒有問題,才進(jìn)入production。
只有緊急情況,才允許跳過上游,直接合并到下游分支。
版本發(fā)布

對(duì)于"版本發(fā)布"的項(xiàng)目,建議的做法是每一個(gè)穩(wěn)定版本,都要從master分支拉出一個(gè)分支,比如2-3-stable、2-4-stable等等。
以后,只有修補(bǔ)bug,才允許將代碼合并到這些分支,并且此時(shí)要更新小版本號(hào)。
從以上三種介紹可以看出復(fù)雜性從高到低:Git Flow > GitLab flow > GitHub Flow
Trunk Based Development

順著持續(xù)集成的思想,如果我們把上一種分支模型做得再極致一點(diǎn),我們不要 Feature 分支,或者把 Feature 分支只留在本地;不需要使用 Pull-Request 而是直接 Push 到遠(yuǎn)程 Master 分支,我們就做到了 Trunk based Development。
使用主干開發(fā)后,我們的代碼庫原則上就只能有一個(gè) Master 分支了,所有新功能的提交也都提交到 Master 分支上,沒有了分支的代碼隔離,測(cè)試和解決沖突都變得簡(jiǎn)單,持續(xù)集成也變得穩(wěn)定了許多,問題也接踵而至,主要有以下三個(gè):
- 如何避免發(fā)布的時(shí)候引入未完成的 Feature
- 如何進(jìn)行線上 Bug Fix
- 如何重構(gòu)
如何避免發(fā)布引入未完成 Feature
答案是: Feature Toggle。
既然代碼要隨時(shí)保持可發(fā)布,而我們又需要只有一份代碼來支持持續(xù)集成,在代碼庫里加一個(gè)特性開關(guān)來隨時(shí)打開和關(guān)閉新特性是最容易想到的也是最容易被質(zhì)疑的解決方案。
Feature Toggle 是有成本的,不管是在加 Toggle 的時(shí)候的代碼設(shè)計(jì),還是在移除 Toggle 時(shí)的人力成本和風(fēng)險(xiǎn),都是需要和它帶來的價(jià)值進(jìn)行衡量的。事實(shí)上,在我們做一個(gè)前端的大特性變更的時(shí)候,我們確實(shí)沒有因?yàn)闆]辦法 Toggle 而采用了一個(gè)獨(dú)立的 Feature 分支,我們認(rèn)為即使為了這個(gè)分支單獨(dú)做一套 Pipeline,也比在前端的各種樣式間添加移除 Toggle 來得簡(jiǎn)單。但同時(shí),團(tuán)隊(duì)商議決定在每次提交前都要先將 Master 分支 Merge 到 Feature 分支,以此避免分支隔離久以后合并時(shí)的痛苦。
如何進(jìn)行線上 Bug Fix
在發(fā)布時(shí)打上 Release Tag,一旦發(fā)現(xiàn)這個(gè)版本有問題,如果這個(gè)時(shí)候Master分支上沒有其他提交,可以直接在 Master 分支上 Hot Fix,如果 Master 分支已經(jīng)有了提交就要做以下三件事:
- 從 Release Tag 創(chuàng)建發(fā)布分支。
- 在 Master 上做 Fix Bug 提交。
- 將 Fix Bug 提交 Cherry Pick 到 Release 分支。
- 在Release 分支再做一次發(fā)布。
線上 Fix 通常都比較緊急??赐赀@個(gè)略顯繁瑣 Bug Fix 流程,你可能會(huì)問為什么不在 Release 分支直接 Fix,再合并到 Master 分支?
這樣做確實(shí)比較符合直覺,但事實(shí)是,如果在 Release 分支做 Fix,很可能會(huì)忘了 Merge 回 Master,試想深夜兩點(diǎn)你做完 Bug Fix 眼看終于上線成功,這時(shí)的第一反應(yīng)就是“終于可以下班了。什么,Merge 回 Master? 明天再來吧“ 等到第二天你早已把這個(gè)事忘得一干二凈。而問題要等到下一次上線才會(huì)被暴露出來,一旦發(fā)現(xiàn),而這個(gè)時(shí)候上一次 Release 的人又不在,無疑增加了很多工作量。
如何重構(gòu)
這里指的是比較大規(guī)模的重構(gòu),無法在一次提交完成,TBD 要求每一次提交都是一個(gè)可上線的版本,所以這同時(shí)還意味著這個(gè)重構(gòu)無法再一個(gè)上線周期內(nèi)完成。
這種情況,需要在代碼設(shè)計(jì)中增加一個(gè)抽象層,保證在重構(gòu)過程中先不動(dòng)原來的代碼,也不破壞既有功能,類似于藍(lán)綠部署中的負(fù)載均衡器的作用,這樣的流程就是:

在將要被重構(gòu)的代碼邏輯附近引入抽象層然后提交,對(duì)所有人可見。如果有需要可以是多個(gè)提交,這些提交都不能破壞 build,然后依次 push 到共享代碼庫。
為將要被引入的代碼寫抽象層的第二次實(shí)現(xiàn),然后提交。但在主干上由于關(guān)閉狀態(tài)所以其他開發(fā)人員暫時(shí)不依賴于它。如果需要的話,這可能像上面那樣需要多次提交。第一步的抽象層也可能偶然被調(diào)整,但必須遵循同樣的原則:不能破壞build。
切換使用重構(gòu)后的代碼,然后 Push。
刪除原有的舊實(shí)現(xiàn)(被重構(gòu)代碼)
刪除抽象層
以上過程也叫:抽象模擬分支。
參考資料:
http://www.ruanyifeng.com/blog/2015/12/git-workflow.html
https://www.codercto.com/a/38021.html