Git 分支管理最佳實踐

[轉(zhuǎn)載]https://www.ibm.com/developerworks/cn/java/j-lo-git-mange/

WeiboGoogle+用電子郵件發(fā)送本頁面

2

Git 是目前最流行的源代碼管理工具。大量的軟件項目由 GitHub、Bitbucket 和 GitLab 這樣的云服務(wù)平臺或是私有的 Git 倉庫來管理仙粱。在使用 Git 時通常會遇到的一個問題是采用何種分支管理實踐孝常,即如何管理倉庫中作用不同的各類分支遣铝。和軟件開發(fā)中的其他實踐一樣外潜,Git 分支管理并沒有普遍適用的最佳做法玖媚,而只有對每個團(tuán)隊和項目而言最適合的做法精续。簡單來說坝锰,在項目開發(fā)中使用多個分支會帶來額外的管理和維護(hù)開銷,但是多個分支對于項目的團(tuán)隊合作重付、新功能開發(fā)和發(fā)布管理都是有一定好處的顷级。不同的團(tuán)隊可以根據(jù)團(tuán)隊人員組成和意愿、項目的發(fā)布周期等因素選擇最適合的策略确垫,找到最適合團(tuán)隊的管理方式弓颈。本文將介紹三種常見的 Git 分支管理方式。

單主干

單主干的分支實踐(Trunk-based development删掀,TBD)在 SVN 中比較流行翔冀。GoogleFacebook 都使用這種方式。trunk 是 SVN 中主干分支的名稱披泪,對應(yīng)到 Git 中則是 master 分支纤子。TBD 的特點是所有團(tuán)隊成員都在單個主干分支上進(jìn)行開發(fā)。當(dāng)需要發(fā)布時款票,先考慮使用標(biāo)簽(tag)控硼,即 tag 某個 commit 來作為發(fā)布的版本。如果僅靠 tag 不能滿足要求艾少,則從主干分支創(chuàng)建發(fā)布分支象颖。bug 修復(fù)在主干分支中進(jìn)行,再 cherry-pick 到發(fā)布分支姆钉。圖 1 是 TBD 中分支流程的示意圖说订。

圖 1. TBD 中的分支流程的示意圖
圖 1. TBD 中的分支流程的示意圖

由于所有開發(fā)人員都在同一個分支上工作,團(tuán)隊需要合理的分工和充分的溝通來保證不同開發(fā)人員的代碼盡可能少的發(fā)生沖突潮瓶。持續(xù)集成和自動化測試是必要的陶冷,用來及時發(fā)現(xiàn)主干分支中的 bug。因為主干分支是所有開發(fā)人員公用的毯辅,一個開發(fā)人員引入的 bug 可能對其他很多人造成影響埂伦。不過好處是由于分支所帶來的額外開銷非常小。開發(fā)人員不需要頻繁在不同的分支之間切換思恐。

GitHub flow

GitHub flow 是 GitHub 所使用的一種簡單的流程沾谜。該流程只使用兩類分支膊毁,并依托于 GitHub 的 pull request 功能。在 GitHub flow 中基跑,master 分支中包含穩(wěn)定的代碼婚温。該分支已經(jīng)或即將被部署到生產(chǎn)環(huán)境。master 分支的作用是提供一個穩(wěn)定可靠的代碼基礎(chǔ)媳否。任何開發(fā)人員都不允許把未測試或未審查的代碼直接提交到 master 分支栅螟。

對代碼的任何修改,包括 bug 修復(fù)篱竭、hotfix力图、新功能開發(fā)等都在單獨的分支中進(jìn)行。不管是一行代碼的小改動掺逼,還是需要幾個星期開發(fā)的新功能吃媒,都采用同樣的方式來管理。當(dāng)需要進(jìn)行修改時吕喘,從 master 分支創(chuàng)建一個新的分支赘那。新分支的名稱應(yīng)該簡單清晰地描述該分支的作用。所有相關(guān)的代碼修改都在新分支中進(jìn)行兽泄。開發(fā)人員可以自由地提交代碼和 push 到遠(yuǎn)程倉庫漓概。

當(dāng)新分支中的代碼全部完成之后漾月,通過 GitHub 提交一個新的 pull request病梢。團(tuán)隊中的其他人員會對代碼進(jìn)行審查,提出相關(guān)的修改意見梁肿。由持續(xù)集成服務(wù)器(如 Jenkins)對新分支進(jìn)行自動化測試蜓陌。當(dāng)代碼通過自動化測試和代碼審查之后,該分支的代碼被合并到 master 分支吩蔑。再從 master 分支部署到生產(chǎn)環(huán)境钮热。圖 2 是 GitHub flow 分支流程的示意圖。

圖 2. Github flow 中的分支流程的示意圖
圖 2. Github flow 中的分支流程的示意圖

GitHub flow 的好處在于非常簡單實用烛芬。開發(fā)人員需要注意的事項非常少隧期,很容易形成習(xí)慣。當(dāng)需要進(jìn)行任何修改時赘娄,總是從 master 分支創(chuàng)建新分支仆潮。完成之后通過 pull request 和相關(guān)的代碼審查來合并回 master 分支。GitHub flow 要求項目有完善的自動化測試遣臼、持續(xù)集成和部署等相關(guān)的基礎(chǔ)設(shè)施性置。每個新分支都需要測試和部署店诗,如果這些不能自動化進(jìn)行繁疤,會增加開發(fā)人員的工作量樟蠕,導(dǎo)致無法有效地實施該流程钳宪。這種分支實踐也要求團(tuán)隊有代碼審查的相應(yīng)流程。

git-flow

git-flow 應(yīng)該是目前流傳最廣的 Git 分支管理實踐隐砸。git-flow 圍繞的核心概念是版本發(fā)布(release)之碗。因此 git-flow 適用于有較長版本發(fā)布周期的項目。雖然目前推崇的做法是持續(xù)集成和隨時發(fā)布凰萨。有的項目甚至可以一天發(fā)布很多次继控。隨時發(fā)布對于 SaaS 服務(wù)類的項目來說是很適合的。不過仍然有很大數(shù)量的項目的發(fā)布周期是幾個星期甚至幾個月胖眷。較長的發(fā)布周期可能是由于非技術(shù)相關(guān)的因素造成的武通,比如人員限制、管理層決策和市場營銷策略等珊搀。

git-flow 流程中包含 5 類分支冶忱,分別是 master、develop境析、新功能分支(feature)囚枪、發(fā)布分支(release)和 hotfix。這些分支的作用和生命周期各不相同劳淆。master 分支中包含的是可以部署到生產(chǎn)環(huán)境中的代碼链沼,這一點和 GitHub flow 是相同的。develop 分支中包含的是下個版本需要發(fā)布的內(nèi)容沛鸵。從某種意義上來說括勺,develop 是一個進(jìn)行代碼集成的分支。當(dāng) develop 分支集成了足夠的新功能和 bug 修復(fù)代碼之后曲掰,通過一個發(fā)布流程來完成新版本的發(fā)布疾捍。發(fā)布完成之后,develop 分支的代碼會被合并到 master 分支中栏妖。

其余三類分支的描述如表 1所示乱豆。這三類分支只在需要時從 develop 或 master 分支創(chuàng)建。在完成之后合并到 develop 或 master 分支吊趾。合并完成之后該分支被刪除宛裕。這幾類分支的名稱應(yīng)該遵循一定的命名規(guī)范,以方便開發(fā)人員識別论泛。

表 1. git-flow 分支類型
分支類型 命名規(guī)范 創(chuàng)建自 合并到 說明
feature feature/* develop develop 新功能
release release/* develop develop 和 master 一次新版本的發(fā)布
hotfix hotfix/* master develop 和 master 生產(chǎn)環(huán)境中發(fā)現(xiàn)的緊急 bug 的修復(fù)

對于開發(fā)過程中的不同任務(wù)揩尸,需要在對應(yīng)的分支上進(jìn)行工作并正確地進(jìn)行合并。每個任務(wù)開始前需要按照指定的步驟完成分支的創(chuàng)建孵奶。例如當(dāng)需要開發(fā)一個新的功能時疲酌,基本的流程如下:

  • 從 develop 分支創(chuàng)建一個新的 feature 分支,如 feature/my-awesome-feature。
  • 在該 feature 分支上進(jìn)行開發(fā)朗恳,提交代碼湿颅,push 到遠(yuǎn)端倉庫。
  • 當(dāng)代碼完成之后粥诫,合并到 develop 分支并刪除當(dāng)前 feature 分支油航。

在進(jìn)行版本發(fā)布和 hotfix 時也有類似的流程。當(dāng)需要發(fā)布新版本時怀浆,采用的是如下的流程:

  • 從 develop 分支創(chuàng)建一個新的 release 分支谊囚,如 release/1.4。
  • 把 release 分支部署到持續(xù)集成服務(wù)器上進(jìn)行測試执赡。測試包括自動化集成測試和手動的用戶接受測試镰踏。
  • 對于測試中發(fā)現(xiàn)的問題,直接在 release 分支上提交修改沙合。完成修改之后再次部署和測試奠伪。
  • 當(dāng) release 分支中的代碼通過測試之后,把 release 分支合并到 develop 和 master 分支首懈,并在 master 分支上添加相應(yīng)的 tag绊率。

因為 git-flow 相關(guān)的流程比較繁瑣和難以記憶,在實踐中一般使用輔助腳本來完成相關(guān)的工作究履。比如同樣的開發(fā)新功能的任務(wù)滤否,可以使用 git flow feature start my-awesome-feature 來完成新分支的創(chuàng)建,使用 git flow feature finish my-awesome-feature 來結(jié)束 feature 分支最仑。輔助腳本會完成正確的分支創(chuàng)建藐俺、切換和合并等工作。

Maven JGit-Flow

對于使用 Apache Maven 的項目來說盯仪,Atlassian 的 JGit-Flow 是一個更好的 git-flow 實現(xiàn)紊搪。JGit-Flow 是一個基于 JGit 的純 Java 實現(xiàn)的 git-flow蜜葱,并不需要安裝額外的腳本全景,只需要作為 Maven 的插件添加到 Maven 項目中即可。JGit-Flow 同時可以替代 Maven release 插件來進(jìn)行發(fā)布管理牵囤。JGit-Flow 會負(fù)責(zé)正確的設(shè)置不同分支中的 Maven 項目的 POM 文件中的版本爸黄,這對于 Maven 項目的構(gòu)建和發(fā)布是很重要的。

在 Maven 項目的 pom.xml 文件中添加代碼清單1中的插件聲明就可以使用 JGit-Flow揭鳞。<configuration>中包含的是 JGit-Flow 不同任務(wù)的配置炕贵。

清單 1. JGit-Flow 的 Maven 設(shè)置

|

<``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 提供了很多配置選項,可以在 POM 文件中聲明野崇。這些配置項可以對不同的任務(wù)生效称开。常用的配置項如2 所示。

表 2. JGit-Flow 的配置項
名稱 描述 適用任務(wù)
flowInitContext 配置不同類型的分支的名稱 全局
allowSnapshots 是否允許存在 SNAPSHOT 類型的依賴
allowUntracked 是否允許本地 Git 中存在未提交的內(nèi)容
scmCommentPrefix 和 scmCommentSuffix JGit-Flow 會進(jìn)行代碼合并工作。通過這兩個配置項來設(shè)置 JGit-Flow 進(jìn)行代碼提交時的消息的前綴和后綴 全局
username 和 password 進(jìn)行 Git 認(rèn)證時的用戶名和密碼鳖轰,適用于 HTTPS 倉庫 全局
releaseBranchVersionSuffix release 分支中項目的 POM 文件版本號的后綴
developmentVersion 下一個開發(fā)版本的版本號
releaseVersion 發(fā)布版本的版本號
pushReleases 是否把 release 分支 push 到遠(yuǎn)程倉庫
goals 當(dāng)部署發(fā)布版本到 Maven 倉庫時執(zhí)行的目標(biāo) release-finish清酥,hotfix-finish
keepBranch 當(dāng)發(fā)布完成之后是否保留相應(yīng)的分支 release-finish,hotfix-finish
noDeploy 是否啟用部署到 Maven 倉庫的功能 release-finish蕴侣,hotfix-finish
noReleaseBuild 在完成發(fā)布時是否禁用 Maven 構(gòu)建 release-finish
noReleaseMerge 在完成發(fā)布時是否把 release 分支合并回 develop 和 master 分支 release-finish
noTag 在完成發(fā)布時是否添加標(biāo)簽 release-finish焰轻,hotfix-finish
squash 在進(jìn)行分支合并時,是否把多個 commit 合并成一個 release-finish昆雀,hotfix-finish
featureName 新特性分支的名稱 feature-start辱志,feature-finish,feature-deploy
pushFeatures 是否把特性分支 push 到遠(yuǎn)程倉庫 feature-start狞膘,feature-finish
noFeatureBuild 在完成特性時是否禁用 Maven 構(gòu)建 feature-finish
noFeatureMerge 在完成特性時是否把特性分支合并回 develop feature-finish
pushHotfixes 在完成 hotfix 時是否把分支合并回 master hotfix-finish
noHotfixBuild 在完成 hotfix 時是否禁用 Maven 構(gòu)建 hotfix-finish

其余的配置項可以參考插件不同任務(wù)的文檔揩懒。

在啟用了 JGit-Flow 之后,可以通過 mvn 運行 jgitflow:feature-start 和 jgitflow:feature-finish 來開始和結(jié)束新特性的開發(fā)挽封。與版本發(fā)布和 hotfix 相關(guān)的命令分別是 jgitflow:release-start 和 jgitflow:release-finish 以及 jgitflow:hotfix-start 和 jgitflow:hotfix-finish旭从。在運行命令之后,JGit-Flow 會完成相關(guān)的 Git 分支創(chuàng)建场仲、合并和悦、刪除、添加 tag 等操作渠缕,不需要開發(fā)人員手動完成鸽素。

每個分支的 pom.xml 中的版本號的格式并不相同。如 master 分支的版本號是標(biāo)準(zhǔn)的發(fā)布版本號亦鳞,如 1.2.3馍忽;develop 分支中則是 SNAPSHOT 版本,比 master 分支的版本號要高燕差,如 1.2.4-SNAPSHOT遭笋;release 分支可以通過<releaseBranchVersionSuffix>配置來指定版本號的后綴,如 1.2.3-RC-SNAPSHOT徒探;feature 分支可以通過<enableFeatureVersions>配置來把 feature 分支名稱作為后綴添加到版本號中瓦呼,如 1.2.3-my-awesome-feature-SNAPSHOT;hotfix 分支的版本號基于 master 分支的版本號测暗,如 1.2.3.1 是對于 1.2.3 版本的第一個 hotfix央串。當(dāng)使用 jgitflow:release-finish 完成一個 release 分支時,develop 分支的版本號會被自動更新成下一個小版本碗啄,如從 1.2.3 到 1.2.4质和。當(dāng)需要手動修改版本號時,可以使用 Versions 插件稚字,如 mvn versions:set -DnewVersion=2.0.0-SNAPSHOT饲宿。

持續(xù)集成

由于 JGit-Flow 是純 Java 的 Maven 插件實現(xiàn)厦酬,可以很容易的與常用的持續(xù)集成服務(wù)器進(jìn)行集成。不過在與 Atlassian 的 Bamboo 集成時瘫想,有幾個細(xì)節(jié)需要注意弃锐。首先是 Bamboo 在進(jìn)行構(gòu)建的時候,使用的是一個虛擬的 Git 倉庫殿托,其倉庫地址是一個不存在的文件系統(tǒng)路徑霹菊。因此需要在 JGit-Flow 的配置中手動設(shè)置 Git 倉庫的地址,保證 Git 操作可以正確執(zhí)行支竹,如代碼清單2所示旋廷。

清單 2. 手動設(shè)置 JGit-Flow 的 Git 倉庫地址

|

<``configuration``>

<``defaultOriginUrl``>[Git url]</``defaultOriginUrl``>

<``alwaysUpdateOrigin``>true</``alwaysUpdateOrigin``>

</``configuration``>

|

另外在開始新的 release 之前,需要確保前一個發(fā)布分支已經(jīng)被刪除礼搁。JGit-Flow 在默認(rèn)情況下會自動在發(fā)布完成之后饶碘,刪除對應(yīng)的 Git 分支。但是可能本地倉庫中還保留有之前的發(fā)布分支馒吴,這會導(dǎo)致新的 release-start 任務(wù)執(zhí)行失敗扎运。一種解決方式是每次都重新 checkout 新的倉庫,這樣可以保證不會出現(xiàn)已經(jīng)在遠(yuǎn)程被刪除的分支饮戳。不過可能會增加構(gòu)建的時間豪治。另外一種解決方式是通過 Git 命令來刪除本地分支,如代碼清單3所示扯罐。

清單 3. 刪除 Git 本地分支

|

${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 時负拟,可能會出現(xiàn)沖突。因為在發(fā)布分支中有與 bug fix 相關(guān)的改動歹河,在 develop 分支中有可能修改相同的文件掩浙。當(dāng)有沖突時,直接運行 JGit-Flow 的 release-finish 任務(wù)會出錯秸歧。這個時候需要開發(fā)人員手動把發(fā)布分支合并到 develop 分支厨姚,并解決相應(yīng)的沖突。然后再次運行 release-finish 任務(wù)即可键菱。

選擇合適的實踐

每個開發(fā)團(tuán)隊都應(yīng)該根據(jù)團(tuán)隊自身和項目的特點來選擇最適合的分支實踐谬墙。首先是項目的版本發(fā)布周期。如果發(fā)布周期較長纱耻,則 git-flow 是最好的選擇芭梯。git-flow 可以很好地解決新功能開發(fā)险耀、版本發(fā)布弄喘、生產(chǎn)系統(tǒng)維護(hù)等問題;如果發(fā)布周期較短甩牺,則 TBD 和 GitHub flow 都是不錯的選擇蘑志。GitHub flow 的特色在于集成了 pull request 和代碼審查。如果項目已經(jīng)使用 GitHub,則 GitHub flow 是最佳的選擇急但。GitHub flow 和 TBD 對持續(xù)集成和自動化測試等基礎(chǔ)設(shè)施有比較高的要求澎媒。如果相關(guān)的基礎(chǔ)設(shè)施不完善,則不建議使用波桩。

小結(jié)

Git 作為目前最流行的源代碼管理工具戒努,已經(jīng)被很多開發(fā)人員所熟悉和使用。在基于 Git 的團(tuán)隊開發(fā)中镐躲,Git 分支的作用非常重要储玫,可以讓團(tuán)隊的不同成員同時在多個相對獨立的特性上工作。本文對目前流行的 3 種 Git 分支管理實踐做了介紹萤皂,并著重介紹了 git-flow 以及與之相關(guān)的 Maven JGit-Flow 插件撒穷。

相關(guān)主題

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蛤奥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子僚稿,更是在濱河造成了極大的恐慌喻括,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贫奠,死亡現(xiàn)場離奇詭異唬血,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)唤崭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門拷恨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谢肾,你說我怎么就攤上這事腕侄。” “怎么了芦疏?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵冕杠,是天一觀的道長。 經(jīng)常有香客問我酸茴,道長分预,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任薪捍,我火速辦了婚禮笼痹,結(jié)果婚禮上配喳,老公的妹妹穿的比我還像新娘。我一直安慰自己凳干,他們只是感情好晴裹,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著救赐,像睡著了一般涧团。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上经磅,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天少欺,我揣著相機(jī)與錄音,去河邊找鬼馋贤。 笑死赞别,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的配乓。 我是一名探鬼主播仿滔,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼犹芹!你這毒婦竟也來了崎页?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤腰埂,失蹤者是張志新(化名)和其女友劉穎飒焦,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屿笼,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡牺荠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了驴一。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片休雌。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖肝断,靈堂內(nèi)的尸體忽然破棺而出杈曲,到底是詐尸還是另有隱情,我是刑警寧澤胸懈,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布担扑,位于F島的核電站,受9級特大地震影響趣钱,放射性物質(zhì)發(fā)生泄漏涌献。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一羔挡、第九天 我趴在偏房一處隱蔽的房頂上張望洁奈。 院中可真熱鬧间唉,春花似錦绞灼、人聲如沸利术。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽印叁。三九已至,卻和暖如春军掂,著一層夾襖步出監(jiān)牢的瞬間轮蜕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工蝗锥, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留跃洛,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓终议,卻偏偏與公主長得像汇竭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子穴张,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容