Git 工作流程及分支策略

?Git 是目前世界上最先進(jìn)的分布式版本控制系統(tǒng)。它使我們更方便的跟蹤,管理和組織代碼。也幫助了我們更好的與其他開發(fā)者進(jìn)行協(xié)作開發(fā)。但是沒有規(guī)矩不成方圓。協(xié)作開發(fā)必須有一個(gè)規(guī)范來(lái)約束各個(gè)貢獻(xiàn)者的行為。這個(gè)規(guī)范就是 Git 工作流程(Git Workflow),也為我們規(guī)范各個(gè)分支管理策略等行為。
?
Git 工作流程是有關(guān)如何使用 Git 以高效協(xié)作開發(fā)的規(guī)則或策略建議,下文這些常見工作流程只是作為指導(dǎo)參考,這些工作流不是一成不變的,我們應(yīng)該根據(jù)自己的項(xiàng)目選擇或制定合適自己的工作流。
集中式工作流
首先我們來(lái)介紹最簡(jiǎn)單粗暴的集中式工作流,如果你用過(guò)SVN[1],那么你可以無(wú)痛切換到 Git 集中式工作流。該工作流只用到 master 這一個(gè)主分支來(lái)維護(hù)我們的代碼,這也是該工作流的主要工作方式。
工作流程示例
1、首先 clone 遠(yuǎn)程倉(cāng)庫(kù)到本地
git clone ssh://user@host/path/to/repo.git2、然后在本地的 master 分支做出修改完成開發(fā)后,創(chuàng)建一個(gè)提交
git status # 查看本地倉(cāng)庫(kù)的修改狀態(tài)git add # 暫存文件git commit # 提交文件
3、然后推送到遠(yuǎn)程倉(cāng)庫(kù)
git push origin master4、 如果此時(shí)本地倉(cāng)庫(kù)與遠(yuǎn)程倉(cāng)庫(kù)有了分歧,Git 將拒絕操作并報(bào)錯(cuò)。這時(shí)我們就應(yīng)該先 pull 遠(yuǎn)程倉(cāng)庫(kù)的修改到本地倉(cāng)庫(kù)后再解決重新推送
git pull --rebase origin master5、如果有沖突,Git 會(huì)在合并有沖突的提交處暫停 rebase 過(guò)程,并報(bào)出錯(cuò)誤信息,我們?cè)诮鉀Q掉沖突后可以使用一下命令將更改加入暫存區(qū)后繼續(xù)只能執(zhí)行 rebase:
git add <some-file>git rebase --continue
如果遇到一個(gè)搞不定的沖突,這可以使用一下命令來(lái)終止 rebase
git rebase --abort主要遵循原則
只用到 master這一個(gè)分支,所有的修改都推送到 master 分支master分支代表了項(xiàng)目的正式發(fā)布版本,所以提交歷史應(yīng)該被尊重且是穩(wěn)定不變的pull 代碼時(shí), 最好使用 rebase 而不是生成一個(gè) merge commit
優(yōu)點(diǎn)
集中式工作流主要優(yōu)點(diǎn)是簡(jiǎn)單,非常適合小型團(tuán)隊(duì) 對(duì)于要從 SVN遷移過(guò)來(lái)的團(tuán)隊(duì)來(lái)說(shuō)很友好
缺點(diǎn)
當(dāng)您的團(tuán)隊(duì)規(guī)模擴(kuò)大時(shí),上面詳述的沖突解決過(guò)程可能會(huì)成為瓶頸
完全沒有發(fā)揮 Git 的優(yōu)勢(shì),在實(shí)際使用 Git 協(xié)作的項(xiàng)目開發(fā)中很少使用集中式工作流
如果在稍大的團(tuán)隊(duì)中使用前面的集中式工作流,那么可能會(huì)在解決沖突中浪費(fèi)很多時(shí)間。在實(shí)際開發(fā)中我們基本上都是都 功能驅(qū)動(dòng)式開發(fā)[2],基于功能需求,創(chuàng)建相應(yīng)的分支進(jìn)行開發(fā),完成開發(fā)合并到主分支后再被刪除掉。接下來(lái)我們介紹的三個(gè)工作流:GitHub Flow,Git Flow 及 Gitlab Flow 都是基于不同的分支管理策略運(yùn)作的。
GitHub Flow 工作流
GitHub Flow 是一個(gè)輕量級(jí)的基于分支的工作流程。它由 GitHub[3] 在 2011 年創(chuàng)建。分支是 Git 中的核心概念,并且 GitHub 工作流程中的一切都以此為基礎(chǔ)。
最重要的的一點(diǎn)就是:「main / master分支中的任何內(nèi)容始終都是可部署的」。其他分支(功能分支)創(chuàng)建用于新功能和錯(cuò)誤修復(fù)的工作,并且在工作完成并經(jīng)過(guò)檢查后,這些分支將合并回到主分支中后刪除。這些分支名應(yīng)該是具有描述性的如 refactor-authentication, user-content-cache-key, make-retina-avatars。
工作流程示例
1、首先 clone 遠(yuǎn)程倉(cāng)庫(kù)到本地
git clone ssh://user@host/path/to/repo.git2、根據(jù)要實(shí)現(xiàn)的功能創(chuàng)建一個(gè)新的分支,分支名應(yīng)該具有描述性,如實(shí)現(xiàn)鑒權(quán)功能:feature-auth
git checkout -b feature-auth3、然后將新工作提交到該分支下,并定期將工作推送到遠(yuǎn)程倉(cāng)庫(kù)
# 創(chuàng)建一個(gè)commitgit status # 查看本地倉(cāng)庫(kù)的修改狀態(tài)git add # 暫存文件git commit # 提交文件# 將分支推送到遠(yuǎn)程倉(cāng)庫(kù)git push --set-upstream origin feature-auth
4、當(dāng)你需要幫助或者反饋,或是你覺得你已經(jīng)完成該功能的工作準(zhǔn)備合進(jìn)主分支的時(shí)候創(chuàng)建 pull request[4]
5、當(dāng)你的這部分工作在 pull request 中,被 review 以及 approved 后,則可以合并到主分支。
git checkout mastergit pullgit pull origin feature-authgit push
6、合并到主分支后,應(yīng)立即對(duì)其進(jìn)行部署
主要遵循原則
master分支中的任何內(nèi)容都是可部署的要進(jìn)行新的工作,需要從主分支 master創(chuàng)建出一個(gè)分支,并給出描述性的名稱(如:new-oauth2-scopes)本地的修改提交到該分支,并定期將你的工作推送到服務(wù)器上的同一命名分支 當(dāng)你需要反饋或幫助時(shí),或者你認(rèn)為分支已準(zhǔn)備好進(jìn)行合并時(shí),請(qǐng)打開一個(gè)Pull Request[5] 在其他人 Review 并 Approved 該功能后,你可以將其合并到 master合并并推送到后主分支 master,你可以并且應(yīng)該立即進(jìn)行部署
優(yōu)點(diǎn)
相比于前文介紹的集中式流程更為合理實(shí)用,可以減少工作中的沖突 由于工作流程的簡(jiǎn)單性,此 Git 分支策略對(duì)進(jìn)行持續(xù)交付和持續(xù)集成很友好。 當(dāng)需要在生產(chǎn)中維護(hù)單個(gè)版本時(shí),它是理想的選擇 這個(gè) Git 分支策略非常適合小型團(tuán)隊(duì)和 Web 應(yīng)用程序
缺點(diǎn)
這種 Git 分支策略無(wú)法同時(shí)支持多個(gè)版本的代碼的部署 沒有解決部署、環(huán)境區(qū)分、 releases、Bug 修復(fù)相關(guān)的問(wèn)題 生產(chǎn)代碼容易變得不穩(wěn)定
Git Flow 工作流
基于功能分支的工作流是一中相當(dāng)靈活的方式。但是問(wèn)題也是有時(shí)候過(guò)于靈活。對(duì)于大型團(tuán)隊(duì),常常需要給不同分支分配一個(gè)更具體的角色。 Git Flow 工作流是管理功能開發(fā)、預(yù)發(fā)布和維護(hù)的常用模式。
Git Flow 是最早誕生并且相當(dāng)流行的工作流程。它由 Vincent Driessen[6] 于 2010 年創(chuàng)建。Git Flow 工作流定義了一個(gè)圍繞項(xiàng)目發(fā)布的嚴(yán)格分支模型。雖然比 GitHub Flow 的功能分支工作流復(fù)雜一點(diǎn),但提供了用于一個(gè)健壯的用于管理大型項(xiàng)目的框架。
Git Flow 工作流沒有用超出功能分支工作流的概念和命令,Git Flow 為不同的分支分配一個(gè)很明確的角色,并定義分支之間如何和什么時(shí)候進(jìn)行交互。
工作流程示例
假設(shè)你已經(jīng)創(chuàng)建好了中央倉(cāng)庫(kù),并且已將其 clone 到本地
1、首先我們來(lái)從主分支創(chuàng)建一個(gè)開發(fā)分支 develop, 并推送到服務(wù)器
git branch developgit push -u origin develop
以后這個(gè)分支將會(huì)包含了項(xiàng)目的全部歷史,而 master 分支將只包含了部分歷史
2、接下來(lái)我們開始開發(fā)新功能,基于 develop 分支創(chuàng)建新的功能分支
git checkout -b some-feature develop并在自己的功能分支上進(jìn)行開發(fā)、創(chuàng)建提交:
git status # 查看本地倉(cāng)庫(kù)的修改狀態(tài)git add # 暫存文件git commit # 提交文件
3、當(dāng)你在你的分支上完成工作,準(zhǔn)備好進(jìn)行合并時(shí),請(qǐng)打開一個(gè) Pull Request,用于合并到develop分支。如果你的團(tuán)隊(duì)沒使用 Pull Request 則可以直接合并到本地的 develop 分支然后推送到中央倉(cāng)庫(kù)。
git pull origin developgit checkout developgit merge --no-ff some-featuregit push# 刪除本地分支git branch -d some-feature
并且在功能分支合并后應(yīng)該立即刪除
4、當(dāng)我們的 develop 分支進(jìn)行到需要發(fā)布時(shí),需要從develop分支創(chuàng)建一個(gè)新的發(fā)布分支,命名為release-*或release/*。這一步也確定了發(fā)布的版本號(hào):
git checkout -b release-0.1.0 develop這個(gè)分支是一個(gè)預(yù)發(fā)布的版本,只做 bug 修復(fù)、文檔生成等面向發(fā)布的任務(wù)。新功能不再添加到這個(gè)分支上
5、經(jīng)過(guò)一系列測(cè)試確認(rèn)沒有問(wèn)題,準(zhǔn)備好了對(duì)外發(fā)布后,我們需要將發(fā)布分支合并到 master 分支,并打下 tag
git checkout mastergit merge --no-ff release-0.1git push
git tag -a 0.1 -m "release 0.1 publish" mastergit push --tags
6、同時(shí)我們還需要將這個(gè)發(fā)布分支合并回去 master 分支
git checkout developgit merge --no-ff release-0.1.0git push
最后我們還需要?jiǎng)h除掉這個(gè)發(fā)布分支 release-0.1.0
7、如果我們的線上版本出現(xiàn)問(wèn)題時(shí),就需要?jiǎng)?chuàng)建一個(gè)維護(hù)分支,用于快速給產(chǎn)品打補(bǔ)丁。這個(gè)維護(hù)分支需要從master分支上創(chuàng)建,提交修改以解決問(wèn)題,然后再直接合并回master分支:
git checkout -b hotfix-auth master修復(fù)完成后將其合并到 master 分支
git checkout mastergit merge --no-ff hotfix-authgit push
同時(shí)打下新的 tag
git tag -a 0.1.1 -m "release 0.1.1 publish" mastergit push --tags
同樣也需要將修復(fù)分支合并到 develop 分支
git checkout developgit merge --no-ff hotfix-authgit push
最后刪除掉這個(gè)熱修復(fù)分支
git branch -d hotfix-auth主要遵循原則
它基于兩個(gè)長(zhǎng)期的主要分支:
主分支 master該分支包含生產(chǎn)代碼。 該分支是穩(wěn)定的發(fā)布版開發(fā)分支 develop此分支包含預(yù)生產(chǎn)代碼。所有的功能分支完成后需要合并到該分支
其次在開發(fā)中還有三種短期分支
功能分支(feature branch)功能分支用于為即將發(fā)布的版本開發(fā)新功能。從 develop分支中檢出也必須合并回develop。補(bǔ)丁分支(hotfix branch)補(bǔ)丁分支 用于修復(fù)線上 bug, 從 master 分支上面檢出。修復(fù)結(jié)束以后,再合并進(jìn) master 和 develop 分支 預(yù)發(fā)分支(release branch)預(yù)發(fā)布分支用于生成一個(gè)預(yù)發(fā)布版本進(jìn)行測(cè)試 預(yù)發(fā)布分支是從 develop 分支上面分出來(lái)的,預(yù)發(fā)布結(jié)束以后,必須合并進(jìn) Develop 和 Master 分支。它的命名,可以采用 release-*的形式。
優(yōu)點(diǎn)
整個(gè)項(xiàng)目生命周期內(nèi),分支狀態(tài)十分干凈,各個(gè)分支各司其職 分支的命名遵循系統(tǒng)的模式,使其更易于理解 master和develop分支分別記錄發(fā)布和功能開發(fā)的歷史由于有發(fā)布分支,其他暫不發(fā)布的功能的開發(fā)不受發(fā)布的影響,可以繼續(xù)提交 維護(hù)分支能快速打補(bǔ)丁,不影響正在開發(fā)的功能 當(dāng)生產(chǎn)中需要多個(gè)版本時(shí),它是理想的選擇
缺點(diǎn)
較為復(fù)雜 Git 歷史記錄變得不可讀 需要維護(hù)兩個(gè)長(zhǎng)期分支 master和develop不方便持續(xù)交付和持續(xù)集成
Gitlab Flow 工作流
GitLab Flow 是通過(guò)創(chuàng)建工作流 GitLab[7]在 2014 年。它將功能驅(qū)動(dòng)的開發(fā)[8]和功能分支以及問(wèn)題跟蹤結(jié)合在一起。它是 Git Flow 與 GithubFflow 的綜合。它吸取了兩者的優(yōu)點(diǎn),既有適應(yīng)不同開發(fā)環(huán)境的彈性,又有單一主分支的簡(jiǎn)單和便利。GitLab Flow 和 GitHub Flow 之間的最大區(qū)別是 GitLab Flow 中的環(huán)境分支支持(例如staging和production),
GitLab Flow 的最大原則叫做"上游優(yōu)先"(upsteam first),即只存在一個(gè)主分支master,它是所有其他分支的"上游"。只有上游分支采納的代碼變化,才能應(yīng)用到其他分支。
GitLab Flow 分成兩種情形來(lái)應(yīng)付不同的開發(fā)流程
持續(xù)發(fā)布
對(duì)于持續(xù)發(fā)布的項(xiàng)目,它建議在master分支以外,再建立不同的環(huán)境分支,每個(gè)環(huán)境都會(huì)有對(duì)應(yīng)的分支。比如,開發(fā)環(huán)境的分支是master,預(yù)發(fā)環(huán)境的分支是pre-production,生產(chǎn)環(huán)境的分支是production。
開發(fā)分支 master用于發(fā)布到測(cè)試環(huán)境,該分支為受保護(hù)的分支預(yù)發(fā)分支 pre-production用于發(fā)布到預(yù)發(fā)環(huán)境,上游分支為master正式分支 production用于發(fā)布到正式環(huán)境,上游分支為pre-production
如果生產(chǎn)環(huán)境(production)發(fā)生錯(cuò)誤,則要建一個(gè)新分支修改完后合并到最上游的開發(fā)分支(master)此時(shí)就是 Upstream first),且經(jīng)過(guò)測(cè)試,再繼續(xù)往 pre-production branch,要經(jīng)過(guò)測(cè)試沒有問(wèn)題了才能夠再往下合并到生產(chǎn)環(huán)境。
版本發(fā)布
對(duì)于"版本發(fā)布"的項(xiàng)目,建議的做法是每一個(gè)穩(wěn)定版本,都要從master分支拉出一個(gè)分支,比如2-3-stable、2-4-stable等等。
再出現(xiàn) bug 后,根據(jù)對(duì)應(yīng)的 release branch 創(chuàng)建一個(gè)修復(fù)分支,修復(fù)工作萬(wàn)和城呢個(gè)后,一樣要按著上游優(yōu)選的原則,先合并到 master 分支,經(jīng)過(guò)測(cè)試才能到才能夠合并到 release 分支,并且此時(shí)要更新小版本號(hào)。
工作流程示例
和之前一樣。首先 clone 項(xiàng)目到本地
1、當(dāng)我們要實(shí)現(xiàn)新功能或是修復(fù) bug 時(shí)。從 master 檢出新的分支如:feature-auth
git checkout -b feature-auth2、然后在該分支下進(jìn)行工作,完成后創(chuàng)建提交
git status # 查看本地倉(cāng)庫(kù)的修改狀態(tài)git add # 暫存文件git commit # 提交文件
推送代碼到遠(yuǎn)程倉(cāng)庫(kù),
git push origin feature-auth3、代碼推送到倉(cāng)庫(kù)后,自定運(yùn)行 GitLab CI
4、當(dāng)我們開發(fā)完成準(zhǔn)備合并進(jìn) master 時(shí),在 GitLab 上創(chuàng)建一個(gè) Merge Request
5、項(xiàng)目管理者進(jìn)行代碼審查,通過(guò)后,合并到master
6、運(yùn)行第二次 GitLab CI
7、通過(guò)相應(yīng)的測(cè)試后,將master分支合并到stable,如果是新版本則創(chuàng)建一個(gè)新的stable分支
8、為stable打上 tag,并進(jìn)行發(fā)布
主要遵循原則
使用功能分支,不直接提交(commit)到 master 分支 測(cè)試所有的提交,而不僅僅只在 master 分支上 在所有的提交上,運(yùn)行所有的測(cè)試(如果你的測(cè)試時(shí)間長(zhǎng)于 5 分鐘則讓它們并行) 在合并到 master 之前執(zhí)行代碼審查,而不是事后審查 部署是自動(dòng)的,并基于分支或標(biāo)簽(tag) 標(biāo)簽(tag)是由用戶設(shè)置的,而不是由 CI 創(chuàng)建 發(fā)布(release)是基于標(biāo)簽(tag)的 永遠(yuǎn)不對(duì)已推送的提交(pushed commits)進(jìn)行變基(rebase) 每個(gè)人都從 master 分支開始工作,目標(biāo)也是 master 分支 在 master 分支中修正錯(cuò)誤,其次再到發(fā)布分支 提交信息(commit message)應(yīng)體現(xiàn)意圖
優(yōu)點(diǎn)
它定義了如何進(jìn)行持續(xù)集成和持續(xù)交付 Git 歷史記錄干凈、易讀
缺點(diǎn)
相比較于 GitHub Flow 更復(fù)雜
當(dāng)需要在生產(chǎn)中維護(hù)多個(gè)版本時(shí),它可能會(huì)像 Git Flow 一樣變得復(fù)雜
總結(jié)及建議
沒有一個(gè)萬(wàn)能的適合所有項(xiàng)目的 Git 工作流程及分支策略,無(wú)論最終選擇哪種策略,你都可以通過(guò)進(jìn)一步的修改來(lái)優(yōu)化它。就像前文所說(shuō)的,Git 工作流程對(duì)我們提升團(tuán)隊(duì)生產(chǎn)力非常重要,在制定我們的工作流程時(shí),應(yīng)該盡量符合我們的項(xiàng)目具體業(yè)務(wù)需求及開發(fā)環(huán)境。但是也有如下幾點(diǎn)小建議:
臨時(shí)分支不應(yīng)該存在太久,每個(gè)分支應(yīng)盡量保持精簡(jiǎn),用完即刪
工作流應(yīng)該盡量簡(jiǎn)單,同時(shí)方便回滾
工作流程應(yīng)該符合我們的項(xiàng)目發(fā)布計(jì)劃
參考鏈接
atlassian comparing workflows[9] What are GitLab Flow best practices?[10] A successful Git branching model[11] Understanding the GitHub flow[12] Introduction to GitLab Flow[13]
Reference
SVN: https://subversion.apache.org/
[2]功能驅(qū)動(dòng)式開發(fā): https://en.wikipedia.org/wiki/Feature-driven_development
[3]GitHub: https://guides.github.com/introduction/flow/
[4]pull request: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests
[5]Pull Request: http://help.github.com/send-pull-requests/
[6]Vincent Driessen: http://nvie.com/posts/a-successful-git-branching-model/
[7]GitLab: https://about.gitlab.com/2014/09/29/gitlab-flow/
[8]功能驅(qū)動(dòng)的開發(fā): https://en.wikipedia.org/wiki/Feature-driven_development
[9]atlassian comparing workflows: https://www.atlassian.com/git/tutorials/comparing-workflows
[10]What are GitLab Flow best practices?: https://about.gitlab.com/topics/version-control/what-are-gitlab-flow-best-practices/
[11]A successful Git branching model: https://nvie.com/posts/a-successful-git-branching-model/
[12]Understanding the GitHub flow: https://guides.github.com/introduction/flow/
[13]Introduction to GitLab Flow: https://docs.gitlab.com/ee/topics/gitlab_flow.html
相關(guān)推薦:
Jenkins打造強(qiáng)大自動(dòng)化工作流

左手代碼右手磚,拋磚引玉
給點(diǎn)個(gè)贊,好不好啊
