git学习------gt;Git 分支管理最佳实践
ps:本文轉(zhuǎn)載于 : https://www.ibm.com/developerworks/cn/java/j-lo-git-mange/index.html
Git 是目前最流行的源代碼管理工具。大量的軟件項(xiàng)目由 GitHub、Bitbucket 和 GitLab 這樣的云服務(wù)平臺(tái)或是私有的 Git 倉(cāng)庫(kù)來(lái)管理。在使用 Git 時(shí)通常會(huì)遇到的一個(gè)問(wèn)題是采用何種分支管理實(shí)踐,即如何管理倉(cāng)庫(kù)中作用不同的各類分支。和軟件開(kāi)發(fā)中的其他實(shí)踐一樣,Git 分支管理并沒(méi)有普遍適用的最佳做法,而只有對(duì)每個(gè)團(tuán)隊(duì)和項(xiàng)目而言最適合的做法。簡(jiǎn)單來(lái)說(shuō),在項(xiàng)目開(kāi)發(fā)中使用多個(gè)分支會(huì)帶來(lái)額外的管理和維護(hù)開(kāi)銷,但是多個(gè)分支對(duì)于項(xiàng)目的團(tuán)隊(duì)合作、新功能開(kāi)發(fā)和發(fā)布管理都是有一定好處的。不同的團(tuán)隊(duì)可以根據(jù)團(tuán)隊(duì)人員組成和意愿、項(xiàng)目的發(fā)布周期等因素選擇最適合的策略,找到最適合團(tuán)隊(duì)的管理方式。本文將介紹三種常見(jiàn)的 Git 分支管理方式。
單主干
單主干的分支實(shí)踐(Trunk-based development,TBD)在 SVN 中比較流行。Google 和 Facebook 都使用這種方式。trunk 是 SVN 中主干分支的名稱,對(duì)應(yīng)到 Git 中則是 master 分支。TBD 的特點(diǎn)是所有團(tuán)隊(duì)成員都在單個(gè)主干分支上進(jìn)行開(kāi)發(fā)。當(dāng)需要發(fā)布時(shí),先考慮使用標(biāo)簽(tag),即 tag 某個(gè) commit 來(lái)作為發(fā)布的版本。如果僅靠 tag 不能滿足要求,則從主干分支創(chuàng)建發(fā)布分支。bug 修復(fù)在主干分支中進(jìn)行,再 cherry-pick 到發(fā)布分支。圖 1 是 TBD 中分支流程的示意圖。
圖 1. TBD 中的分支流程的示意圖
由于所有開(kāi)發(fā)人員都在同一個(gè)分支上工作,團(tuán)隊(duì)需要合理的分工和充分的溝通來(lái)保證不同開(kāi)發(fā)人員的代碼盡可能少的發(fā)生沖突。持續(xù)集成和自動(dòng)化測(cè)試是必要的,用來(lái)及時(shí)發(fā)現(xiàn)主干分支中的 bug。因?yàn)橹鞲煞种撬虚_(kāi)發(fā)人員公用的,一個(gè)開(kāi)發(fā)人員引入的 bug 可能對(duì)其他很多人造成影響。不過(guò)好處是由于分支所帶來(lái)的額外開(kāi)銷非常小。開(kāi)發(fā)人員不需要頻繁在不同的分支之間切換。
GitHub flow
GitHub flow 是 GitHub 所使用的一種簡(jiǎn)單的流程。該流程只使用兩類分支,并依托于 GitHub 的 pull request 功能。在 GitHub flow 中,master 分支中包含穩(wěn)定的代碼。該分支已經(jīng)或即將被部署到生產(chǎn)環(huán)境。master 分支的作用是提供一個(gè)穩(wěn)定可靠的代碼基礎(chǔ)。任何開(kāi)發(fā)人員都不允許把未測(cè)試或未審查的代碼直接提交到 master 分支。
對(duì)代碼的任何修改,包括 bug 修復(fù)、hotfix、新功能開(kāi)發(fā)等都在單獨(dú)的分支中進(jìn)行。不管是一行代碼的小改動(dòng),還是需要幾個(gè)星期開(kāi)發(fā)的新功能,都采用同樣的方式來(lái)管理。當(dāng)需要進(jìn)行修改時(shí),從 master 分支創(chuàng)建一個(gè)新的分支。新分支的名稱應(yīng)該簡(jiǎn)單清晰地描述該分支的作用。所有相關(guān)的代碼修改都在新分支中進(jìn)行。開(kāi)發(fā)人員可以自由地提交代碼和 push 到遠(yuǎn)程倉(cāng)庫(kù)。
當(dāng)新分支中的代碼全部完成之后,通過(guò) GitHub 提交一個(gè)新的 pull request。團(tuán)隊(duì)中的其他人員會(huì)對(duì)代碼進(jìn)行審查,提出相關(guān)的修改意見(jiàn)。由持續(xù)集成服務(wù)器(如 Jenkins)對(duì)新分支進(jìn)行自動(dòng)化測(cè)試。當(dāng)代碼通過(guò)自動(dòng)化測(cè)試和代碼審查之后,該分支的代碼被合并到 master 分支。再?gòu)?master 分支部署到生產(chǎn)環(huán)境。圖 2 是 GitHub flow 分支流程的示意圖。
圖 2. Github flow 中的分支流程的示意圖
GitHub flow 的好處在于非常簡(jiǎn)單實(shí)用。開(kāi)發(fā)人員需要注意的事項(xiàng)非常少,很容易形成習(xí)慣。當(dāng)需要進(jìn)行任何修改時(shí),總是從 master 分支創(chuàng)建新分支。完成之后通過(guò) pull request 和相關(guān)的代碼審查來(lái)合并回 master 分支。GitHub flow 要求項(xiàng)目有完善的自動(dòng)化測(cè)試、持續(xù)集成和部署等相關(guān)的基礎(chǔ)設(shè)施。每個(gè)新分支都需要測(cè)試和部署,如果這些不能自動(dòng)化進(jìn)行,會(huì)增加開(kāi)發(fā)人員的工作量,導(dǎo)致無(wú)法有效地實(shí)施該流程。這種分支實(shí)踐也要求團(tuán)隊(duì)有代碼審查的相應(yīng)流程。
git-flow
git-flow 應(yīng)該是目前流傳最廣的 Git 分支管理實(shí)踐。git-flow 圍繞的核心概念是版本發(fā)布(release)。因此 git-flow 適用于有較長(zhǎng)版本發(fā)布周期的項(xiàng)目。雖然目前推崇的做法是持續(xù)集成和隨時(shí)發(fā)布。有的項(xiàng)目甚至可以一天發(fā)布很多次。隨時(shí)發(fā)布對(duì)于 SaaS 服務(wù)類的項(xiàng)目來(lái)說(shuō)是很適合的。不過(guò)仍然有很大數(shù)量的項(xiàng)目的發(fā)布周期是幾個(gè)星期甚至幾個(gè)月。較長(zhǎng)的發(fā)布周期可能是由于非技術(shù)相關(guān)的因素造成的,比如人員限制、管理層決策和市場(chǎng)營(yíng)銷策略等。
git-flow 流程中包含 5 類分支,分別是 master、develop、新功能分支(feature)、發(fā)布分支(release)和 hotfix。這些分支的作用和生命周期各不相同。master 分支中包含的是可以部署到生產(chǎn)環(huán)境中的代碼,這一點(diǎn)和 GitHub flow 是相同的。develop 分支中包含的是下個(gè)版本需要發(fā)布的內(nèi)容。從某種意義上來(lái)說(shuō),develop 是一個(gè)進(jìn)行代碼集成的分支。當(dāng) develop 分支集成了足夠的新功能和 bug 修復(fù)代碼之后,通過(guò)一個(gè)發(fā)布流程來(lái)完成新版本的發(fā)布。發(fā)布完成之后,develop 分支的代碼會(huì)被合并到 master 分支中。
其余三類分支的描述如表 1所示。這三類分支只在需要時(shí)從 develop 或 master 分支創(chuàng)建。在完成之后合并到 develop 或 master 分支。合并完成之后該分支被刪除。這幾類分支的名稱應(yīng)該遵循一定的命名規(guī)范,以方便開(kāi)發(fā)人員識(shí)別。
表 1. git-flow 分支類型
| feature | feature/* | develop | develop | 新功能 |
| release | release/* | develop | develop 和 master | 一次新版本的發(fā)布 |
| hotfix | hotfix/* | master | develop 和 master | 生產(chǎn)環(huán)境中發(fā)現(xiàn)的緊急 bug 的修復(fù) |
對(duì)于開(kāi)發(fā)過(guò)程中的不同任務(wù),需要在對(duì)應(yīng)的分支上進(jìn)行工作并正確地進(jìn)行合并。每個(gè)任務(wù)開(kāi)始前需要按照指定的步驟完成分支的創(chuàng)建。例如當(dāng)需要開(kāi)發(fā)一個(gè)新的功能時(shí),基本的流程如下:
- 從 develop 分支創(chuàng)建一個(gè)新的 feature 分支,如 feature/my-awesome-feature。
- 在該 feature 分支上進(jìn)行開(kāi)發(fā),提交代碼,push 到遠(yuǎn)端倉(cāng)庫(kù)。
- 當(dāng)代碼完成之后,合并到 develop 分支并刪除當(dāng)前 feature 分支。
在進(jìn)行版本發(fā)布和 hotfix 時(shí)也有類似的流程。當(dāng)需要發(fā)布新版本時(shí),采用的是如下的流程:
- 從 develop 分支創(chuàng)建一個(gè)新的 release 分支,如 release/1.4。
- 把 release 分支部署到持續(xù)集成服務(wù)器上進(jìn)行測(cè)試。測(cè)試包括自動(dòng)化集成測(cè)試和手動(dòng)的用戶接受測(cè)試。
- 對(duì)于測(cè)試中發(fā)現(xiàn)的問(wèn)題,直接在 release 分支上提交修改。完成修改之后再次部署和測(cè)試。
- 當(dāng) release 分支中的代碼通過(guò)測(cè)試之后,把 release 分支合并到 develop 和 master 分支,并在 master 分支上添加相應(yīng)的 tag。
因?yàn)?git-flow 相關(guān)的流程比較繁瑣和難以記憶,在實(shí)踐中一般使用輔助腳本來(lái)完成相關(guān)的工作。比如同樣的開(kāi)發(fā)新功能的任務(wù),可以使用 git flow feature start my-awesome-feature 來(lái)完成新分支的創(chuàng)建,使用 git flow feature finish my-awesome-feature 來(lái)結(jié)束 feature 分支。輔助腳本會(huì)完成正確的分支創(chuàng)建、切換和合并等工作。
Maven JGit-Flow
對(duì)于使用 Apache Maven 的項(xiàng)目來(lái)說(shuō),Atlassian 的 JGit-Flow 是一個(gè)更好的 git-flow 實(shí)現(xiàn)。JGit-Flow 是一個(gè)基于 JGit 的純 Java 實(shí)現(xiàn)的 git-flow,并不需要安裝額外的腳本,只需要作為 Maven 的插件添加到 Maven 項(xiàng)目中即可。JGit-Flow 同時(shí)可以替代 Maven release 插件來(lái)進(jìn)行發(fā)布管理。JGit-Flow 會(huì)負(fù)責(zé)正確的設(shè)置不同分支中的 Maven 項(xiàng)目的 POM 文件中的版本,這對(duì)于 Maven 項(xiàng)目的構(gòu)建和發(fā)布是很重要的。
在 Maven 項(xiàng)目的 pom.xml 文件中添加代碼清單1中的插件聲明就可以使用 JGit-Flow。<configuration>中包含的是 JGit-Flow 不同任務(wù)的配置。
清單 1. JGit-Flow 的 Maven 設(shè)置
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <build> ???<plugins> ???????<plugin> ???????????<groupId>external.atlassian.jgitflow</groupId> ???????????<artifactId>jgitflow-maven-plugin</artifactId> ???????????<version>1.0-m5.1</version> ???????????<configuration> ??????????????<flowInitContext> ????????????????<versionTagPrefix>release-</versionTagPrefix> ??????????????</flowInitContext> ???????????????<releaseBranchVersionSuffix>RC</releaseBranchVersionSuffix> ???????????????<noDeploy>true</noDeploy> ???????????????<allowSnapshots>true</allowSnapshots> ???????????????<allowUntracked>true</allowUntracked> ???????????</configuration> ???????</plugin> ???</plugins> </build> |
JGit-Flow 提供了很多配置選項(xiàng),可以在 POM 文件中聲明。這些配置項(xiàng)可以對(duì)不同的任務(wù)生效。常用的配置項(xiàng)如表2 所示。
表 2. JGit-Flow 的配置項(xiàng)
| flowInitContext | 配置不同類型的分支的名稱 | 全局 |
| allowSnapshots | 是否允許存在 SNAPSHOT 類型的依賴 | ? |
| allowUntracked | 是否允許本地 Git 中存在未提交的內(nèi)容 | ? |
| scmCommentPrefix 和 scmCommentSuffix | JGit-Flow 會(huì)進(jìn)行代碼合并工作。通過(guò)這兩個(gè)配置項(xiàng)來(lái)設(shè)置 JGit-Flow 進(jìn)行代碼提交時(shí)的消息的前綴和后綴 | 全局 |
| username 和 password | 進(jìn)行 Git 認(rèn)證時(shí)的用戶名和密碼,適用于 HTTPS 倉(cāng)庫(kù) | 全局 |
| releaseBranchVersionSuffix | release 分支中項(xiàng)目的 POM 文件版本號(hào)的后綴 | ? |
| developmentVersion | 下一個(gè)開(kāi)發(fā)版本的版本號(hào) | ? |
| releaseVersion | 發(fā)布版本的版本號(hào) | ? |
| pushReleases | 是否把 release 分支 push 到遠(yuǎn)程倉(cāng)庫(kù) | ? |
| goals | 當(dāng)部署發(fā)布版本到 Maven 倉(cāng)庫(kù)時(shí)執(zhí)行的目標(biāo) | release-finish,hotfix-finish |
| keepBranch | 當(dāng)發(fā)布完成之后是否保留相應(yīng)的分支 | release-finish,hotfix-finish |
| noDeploy | 是否啟用部署到 Maven 倉(cāng)庫(kù)的功能 | release-finish,hotfix-finish |
| noReleaseBuild | 在完成發(fā)布時(shí)是否禁用 Maven 構(gòu)建 | release-finish |
| noReleaseMerge | 在完成發(fā)布時(shí)是否把 release 分支合并回 develop 和 master 分支 | release-finish |
| noTag | 在完成發(fā)布時(shí)是否添加標(biāo)簽 | release-finish,hotfix-finish |
| squash | 在進(jìn)行分支合并時(shí),是否把多個(gè) commit 合并成一個(gè) | release-finish,hotfix-finish |
| featureName | 新特性分支的名稱 | feature-start,feature-finish,feature-deploy |
| pushFeatures | 是否把特性分支 push 到遠(yuǎn)程倉(cāng)庫(kù) | feature-start,feature-finish |
| noFeatureBuild | 在完成特性時(shí)是否禁用 Maven 構(gòu)建 | feature-finish |
| noFeatureMerge | 在完成特性時(shí)是否把特性分支合并回 develop | feature-finish |
| pushHotfixes | 在完成 hotfix 時(shí)是否把分支合并回 master | hotfix-finish |
| noHotfixBuild | 在完成 hotfix 時(shí)是否禁用 Maven 構(gòu)建 | hotfix-finish |
其余的配置項(xiàng)可以參考插件不同任務(wù)的文檔。
在啟用了 JGit-Flow 之后,可以通過(guò) mvn 運(yùn)行 jgitflow:feature-start 和 jgitflow:feature-finish 來(lái)開(kāi)始和結(jié)束新特性的開(kāi)發(fā)。與版本發(fā)布和 hotfix 相關(guān)的命令分別是 jgitflow:release-start 和 jgitflow:release-finish 以及 jgitflow:hotfix-start 和 jgitflow:hotfix-finish。在運(yùn)行命令之后,JGit-Flow 會(huì)完成相關(guān)的 Git 分支創(chuàng)建、合并、刪除、添加 tag 等操作,不需要開(kāi)發(fā)人員手動(dòng)完成。
每個(gè)分支的 pom.xml 中的版本號(hào)的格式并不相同。如 master 分支的版本號(hào)是標(biāo)準(zhǔn)的發(fā)布版本號(hào),如 1.2.3;develop 分支中則是 SNAPSHOT 版本,比 master 分支的版本號(hào)要高,如 1.2.4-SNAPSHOT;release 分支可以通過(guò)<releaseBranchVersionSuffix>配置來(lái)指定版本號(hào)的后綴,如 1.2.3-RC-SNAPSHOT;feature 分支可以通過(guò)<enableFeatureVersions>配置來(lái)把 feature 分支名稱作為后綴添加到版本號(hào)中,如 1.2.3-my-awesome-feature-SNAPSHOT;hotfix 分支的版本號(hào)基于 master 分支的版本號(hào),如 1.2.3.1 是對(duì)于 1.2.3 版本的第一個(gè) hotfix。當(dāng)使用 jgitflow:release-finish 完成一個(gè) release 分支時(shí),develop 分支的版本號(hào)會(huì)被自動(dòng)更新成下一個(gè)小版本,如從 1.2.3 到 1.2.4。當(dāng)需要手動(dòng)修改版本號(hào)時(shí),可以使用 Versions 插件,如 mvn versions:set -DnewVersion=2.0.0-SNAPSHOT。
持續(xù)集成
由于 JGit-Flow 是純 Java 的 Maven 插件實(shí)現(xiàn),可以很容易的與常用的持續(xù)集成服務(wù)器進(jìn)行集成。不過(guò)在與 Atlassian 的 Bamboo 集成時(shí),有幾個(gè)細(xì)節(jié)需要注意。首先是 Bamboo 在進(jìn)行構(gòu)建的時(shí)候,使用的是一個(gè)虛擬的 Git 倉(cāng)庫(kù),其倉(cāng)庫(kù)地址是一個(gè)不存在的文件系統(tǒng)路徑。因此需要在 JGit-Flow 的配置中手動(dòng)設(shè)置 Git 倉(cāng)庫(kù)的地址,保證 Git 操作可以正確執(zhí)行,如代碼清單2所示。
清單 2. 手動(dòng)設(shè)置 JGit-Flow 的 Git 倉(cāng)庫(kù)地址
| 1 2 3 4 | <configuration> ???<defaultOriginUrl>[Git url]</defaultOriginUrl> ???<alwaysUpdateOrigin>true</alwaysUpdateOrigin> </configuration> |
另外在開(kāi)始新的 release 之前,需要確保前一個(gè)發(fā)布分支已經(jīng)被刪除。JGit-Flow 在默認(rèn)情況下會(huì)自動(dòng)在發(fā)布完成之后,刪除對(duì)應(yīng)的 Git 分支。但是可能本地倉(cāng)庫(kù)中還保留有之前的發(fā)布分支,這會(huì)導(dǎo)致新的 release-start 任務(wù)執(zhí)行失敗。一種解決方式是每次都重新 checkout 新的倉(cāng)庫(kù),這樣可以保證不會(huì)出現(xiàn)已經(jīng)在遠(yuǎn)程被刪除的分支。不過(guò)可能會(huì)增加構(gòu)建的時(shí)間。另外一種解決方式是通過(guò) Git 命令來(lái)刪除本地分支,如代碼清單3所示。
清單 3. 刪除 Git 本地分支
| 1 2 3 4 5 6 7 8 | ${bamboo.capability.system.git.executable} fetch --prune --verbose ${bamboo.capability.system.git.executable} branch -vv | awk '/: gone]/{print $1}' | ??????????????????????xargs ${bamboo.capability.system.git.executable} branch -d 2> /dev/null echo 'stale branches deleted' Git 分支合并沖突處理 |
當(dāng)把發(fā)布分支合并到 develop 時(shí),可能會(huì)出現(xiàn)沖突。因?yàn)樵诎l(fā)布分支中有與 bug fix 相關(guān)的改動(dòng),在 develop 分支中有可能修改相同的文件。當(dāng)有沖突時(shí),直接運(yùn)行 JGit-Flow 的 release-finish 任務(wù)會(huì)出錯(cuò)。這個(gè)時(shí)候需要開(kāi)發(fā)人員手動(dòng)把發(fā)布分支合并到 develop 分支,并解決相應(yīng)的沖突。然后再次運(yùn)行 release-finish 任務(wù)即可。
選擇合適的實(shí)踐
每個(gè)開(kāi)發(fā)團(tuán)隊(duì)都應(yīng)該根據(jù)團(tuán)隊(duì)自身和項(xiàng)目的特點(diǎn)來(lái)選擇最適合的分支實(shí)踐。首先是項(xiàng)目的版本發(fā)布周期。如果發(fā)布周期較長(zhǎng),則 git-flow 是最好的選擇。git-flow 可以很好地解決新功能開(kāi)發(fā)、版本發(fā)布、生產(chǎn)系統(tǒng)維護(hù)等問(wèn)題;如果發(fā)布周期較短,則 TBD 和 GitHub flow 都是不錯(cuò)的選擇。GitHub flow 的特色在于集成了 pull request 和代碼審查。如果項(xiàng)目已經(jīng)使用 GitHub,則 GitHub flow 是最佳的選擇。GitHub flow 和 TBD 對(duì)持續(xù)集成和自動(dòng)化測(cè)試等基礎(chǔ)設(shè)施有比較高的要求。如果相關(guān)的基礎(chǔ)設(shè)施不完善,則不建議使用。
小結(jié)
Git 作為目前最流行的源代碼管理工具,已經(jīng)被很多開(kāi)發(fā)人員所熟悉和使用。在基于 Git 的團(tuán)隊(duì)開(kāi)發(fā)中,Git 分支的作用非常重要,可以讓團(tuán)隊(duì)的不同成員同時(shí)在多個(gè)相對(duì)獨(dú)立的特性上工作。本文對(duì)目前流行的 3 種 Git 分支管理實(shí)踐做了介紹,并著重介紹了 git-flow 以及與之相關(guān)的 Maven JGit-Flow 插件。
相關(guān)主題
- 了解 Google 和 Facebook 的單主干實(shí)踐。
- 了解 GitHub flow 的更多內(nèi)容。
- 了解 git-flow 的更多內(nèi)容。
- 了解 Maven JGit-Flow 插件的更多內(nèi)容。
- developerWorks Java 技術(shù)專區(qū):這里有數(shù)百篇關(guān)于 Java 編程各個(gè)方面的文章。
總結(jié)
以上是生活随笔為你收集整理的git学习------gt;Git 分支管理最佳实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: iOS - 设置导航栏之标题栏居中、标题
- 下一篇: 2.let和const命令