來源:阮一峰的網(wǎng)絡(luò)日志
原文地址:http://www.ruanyifeng.com/blog/2015/12/git-workflow.html
Git 作為一個源碼管理系統(tǒng),不可避免涉及到多人協(xié)作。
協(xié)作必須有一個規(guī)范的流程,讓大家有效地合作,使得項目井井有條地發(fā)展下去。"協(xié)作流程"在英語里,叫做"workflow"或者"flow",原意是水流,比喻項目像水流那樣,順暢、自然地向前流動,不會發(fā)生沖擊、對撞、甚至漩渦。

本文介紹三種廣泛使用的協(xié)作流程:
Git flow
Github flow
Gitlab flow
如果你對Git還不是很熟悉,可以先閱讀下面的文章。
《Git 使用規(guī)范流程》http://www.ruanyifeng.com/blog/2015/08/git-use-process.html
《常用 Git 命令清單》http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html
《Git 遠(yuǎn)程操作詳解》http://www.ruanyifeng.com/blog/2014/06/git_remote.html
本文的三種協(xié)作流程,有一個共同點(diǎn):都采用"功能驅(qū)動式開發(fā):https://en.wikipedia.org/wiki/Feature-driven_development"(Feature-driven development,簡稱FDD)。
它指的是,需求是開發(fā)的起點(diǎn),先有需求再有功能分支(feature branch)或者補(bǔ)丁分支(hotfix branch)。完成開發(fā)后,該分支就合并到主分支,然后被刪除。
最早誕生、并得到廣泛采用的一種協(xié)作流程,就是Git flow:http://nvie.com/posts/a-successful-git-branching-model/ 。
特點(diǎn)
它最主要的特點(diǎn)有兩個。

首先,項目存在兩個長期分支。
前者用于存放對外發(fā)布的版本,任何時候在這個分支拿到的,都是穩(wěn)定的分布版;后者用于日常開發(fā),存放最新的開發(fā)版。
其次,項目存在三種短期分支。
一旦完成開發(fā),它們就會被合并進(jìn)develop或master,然后被刪除。
Git flow 的詳細(xì)介紹,請閱讀我翻譯的中文版《Git 分支管理策略》http://www.ruanyifeng.com/blog/2012/07/git.html。
評價
Git flow的優(yōu)點(diǎn)是清晰可控,缺點(diǎn)是相對復(fù)雜,需要同時維護(hù)兩個長期分支。大多數(shù)工具都將master當(dāng)作默認(rèn)分支,可是開發(fā)是在develop分支進(jìn)行的,這導(dǎo)致經(jīng)常要切換分支,非常煩人。
更大問題在于,這個模式是基于"版本發(fā)布"的,目標(biāo)是一段時間以后產(chǎn)出一個新版本。但是,很多網(wǎng)站項目是"持續(xù)發(fā)布",代碼一有變動,就部署一次。這時,master分支和develop分支的差別不大,沒必要維護(hù)兩個長期分支。
Github flow:http://scottchacon.com/2011/08/31/github-flow.html 是Git flow的簡化版,專門配合"持續(xù)發(fā)布"。它是 Github.com 使用的協(xié)作流程。
流程
它只有一個長期分支,就是master,因此用起來非常簡單。
官方推薦的流程:https://guides.github.com/introduction/flow/index.html如下。

第一步:根據(jù)需求,從master拉出新分支,不區(qū)分功能分支或補(bǔ)丁分支。
第二步:新分支開發(fā)完成后,或者需要討論的時候,就向master發(fā)起一個pull reqest(簡稱PR)。
第三步:Pull Request既是一個通知,讓別人注意到你的請求,又是一種對話機(jī)制,大家一起評審和討論你的代碼。對話過程中,你還可以不斷提交代碼。
第四步:你的Pull Request被接受,合并進(jìn)master,重新部署后,原來你拉出來的那個分支就被刪除。(先部署再合并也可。)
評價
Github flow 的最大優(yōu)點(diǎn)就是簡單,對于"持續(xù)發(fā)布"的產(chǎn)品,可以說是最合適的流程。
問題在于它的假設(shè):master分支的更新與產(chǎn)品的發(fā)布是一致的。也就是說,master分支的最新代碼,默認(rèn)就是當(dāng)前的線上代碼。
可是,有些時候并非如此,代碼合并進(jìn)入master分支,并不代表它就能立刻發(fā)布。比如,蘋果商店的APP提交審核以后,等一段時間才能上架。這時,如果還有新的代碼提交,master分支就會與剛發(fā)布的版本不一致。另一個例子是,有些公司有發(fā)布窗口,只有指定時間才能發(fā)布,這也會導(dǎo)致線上版本落后于master分支。
上面這種情況,只有master一個主分支就不夠用了。通常,你不得不在master分支以外,另外新建一個production分支跟蹤線上版本。
Gitlab flow:http://doc.gitlab.com/ee/workflow/gitlab_flow.html 是 Git flow 與 Github flow 的綜合。它吸取了兩者的優(yōu)點(diǎn),既有適應(yīng)不同開發(fā)環(huán)境的彈性,又有單一主分支的簡單和便利。它是 Gitlab.com 推薦的做法。
上游優(yōu)先
Gitlab flow 的最大原則叫做"上游優(yōu)先"(upsteam first),即只存在一個主分支master,它是所有其他分支的"上游"。只有上游分支采納的代碼變化,才能應(yīng)用到其他分支。
Chromium項目:https://www.chromium.org/chromium-os/chromiumos-design-docs/upstream-first就是一個例子,它明確規(guī)定,上游分支依次為:
Linus Torvalds的分支
子系統(tǒng)(比如netdev)的分支
設(shè)備廠商(比如三星)的分支
持續(xù)發(fā)布
Gitlab flow 分成兩種情況,適應(yīng)不同的開發(fā)流程。

對于"持續(xù)發(fā)布"的項目,它建議在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,這時就要新建一個功能分支,先把它合并到master,確認(rèn)沒有問題,再cherry-pick到pre-production,這一步也沒有問題,才進(jìn)入production。
只有緊急情況,才允許跳過上游,直接合并到下游分支。
版本發(fā)布

對于"版本發(fā)布"的項目,建議的做法是每一個穩(wěn)定版本,都要從master分支拉出一個分支,比如2-3-stable、2-4-stable等等。
以后,只有修補(bǔ)bug,才允許將代碼合并到這些分支,并且此時要更新小版本號。
Pull Request

功能分支合并進(jìn)master分支,必須通過Pull Request(Gitlab里面叫做 Merge Request)。

前面說過,Pull Request本質(zhì)是一種對話機(jī)制,你可以在提交的時候,@相關(guān)人員:https://github.com/blog/1004-mention-autocompletion或團(tuán)隊:https://github.com/blog/1121-introducing-team-mentions,引起他們的注意。
Protected branch
master分支應(yīng)該受到保護(hù),不是每個人都可以修改這個分支,以及擁有審批 Pull Request 的權(quán)力。
Github 和 Gitlab 都提供"保護(hù)分支"(Protected branch)這個功能。
Issue
Issue 用于 Bug追蹤和需求管理。建議先新建 Issue,再新建對應(yīng)的功能分支。功能分支總是為了解決一個或多個 Issue。
功能分支的名稱,可以與issue的名字保持一致,并且以issue的編號起首,比如"15-require-a-password-to-change-it"。

開發(fā)完成后,在提交說明里面,可以寫上"fixes #14"或者"closes #67"。Github規(guī)定,只要commit message里面有下面這些動詞 + 編號,就會關(guān)閉對應(yīng)的issue。
close
closes
closed
fix
fixes
fixed
resolve
resolves
resolved
這種方式還可以一次關(guān)閉多個issue,或者關(guān)閉其他代碼庫的issue,格式是username/repository#issue_number。
Pull Request被接受以后,issue關(guān)閉,原始分支就應(yīng)該刪除。如果以后該issue重新打開,新分支可以復(fù)用原來的名字。
Merge節(jié)點(diǎn)
Git有兩種合并:一種是"直進(jìn)式合并"(fast forward),不生成單獨(dú)的合并節(jié)點(diǎn);另一種是"非直進(jìn)式合并"(none fast-forword),會生成單獨(dú)節(jié)點(diǎn)。
前者不利于保持commit信息的清晰,也不利于以后的回滾,建議總是采用后者(即使用--no-ff參數(shù))。只要發(fā)生合并,就要有一個單獨(dú)的合并節(jié)點(diǎn)。
Squash多個commit
為了便于他人閱讀你的提交,也便于cherry-pick或撤銷代碼變化,在發(fā)起Pull Request之前,應(yīng)該把多個commit合并成一個。(前提是,該分支只有你一個人開發(fā),且沒有跟master合并過。)

這可以采用rebase命令附帶的squash操作,具體方法請參考我寫的《Git 使用規(guī)范流程》:http://www.ruanyifeng.com/blog/2015/08/git-use-process.html。
Vanilla社區(qū)發(fā)起?晨讀計劃?,每天堅持積累一點(diǎn),今天的努力至少讓我們比昨天更進(jìn)一步。
?晨讀計劃? 期待你的加入... ...
Vanilla:基于OpenResty的高性能Web應(yīng)用開發(fā)框架我們的微信號:Vanilla-OpenResty
我們的QQ群:205773855、481213820、34782325