jasmine214--love

          只有當(dāng)你的內(nèi)心總是充滿(mǎn)快樂(lè)、美好的愿望和寧?kù)o時(shí),你才能擁有強(qiáng)壯的體魄和明朗、快樂(lè)或者寧?kù)o的面容。
          posts - 731, comments - 60, trackbacks - 0, articles - 0

          一個(gè)成功的Git分支模型

          Posted on 2011-02-22 11:22 幻海藍(lán)夢(mèng) 閱讀(1265) 評(píng)論(1)  編輯  收藏 所屬分類(lèi): 配置管理

          本文中我會(huì)展示一種開(kāi)發(fā)模型,一年前該模型就已經(jīng)被我用在所有的項(xiàng)目中(包括工作中的項(xiàng)目和私有項(xiàng)目),結(jié)果是非常成功的。我早就想為此寫(xiě)點(diǎn)東西,可直到現(xiàn)在才有時(shí)間。本文不會(huì)講述任何項(xiàng)目的細(xì)節(jié),只會(huì)涉及到分支策略和發(fā)布管理。

          本文使用Git作為所有源碼的版本控制工具。

          為什么是Git?

          要全面了解Git與其它集中式版本控制系統(tǒng)相比的優(yōu)劣,可以參考這個(gè)頁(yè)面。 這方面的爭(zhēng)論可謂是硝煙彌漫。作為一個(gè)開(kāi)發(fā)者,所有這些工具中我最鐘情于Git。Git的的確確改變了人們考慮合并及分支的方式。在我之前所處的經(jīng)典 CVS/Subversion世界中,合并/分支總是被認(rèn)為是有點(diǎn)可怕的事情(“小心合并沖突,丫會(huì)惡心到你”),因此你只應(yīng)偶爾干這種事情。

          但有了Git,這類(lèi)事情就變得非常簡(jiǎn)單,分支及合并甚至被認(rèn)為是你日常版本控制操作的核心之一。例如,在CVS/Subversion的書(shū)中,分支及合并往往在后面的章節(jié)才被介紹(針對(duì)高級(jí)用戶(hù)),但在每一本Git的書(shū)中,該內(nèi)容已經(jīng)在前3章中介紹(基礎(chǔ))。

          簡(jiǎn)單及易重復(fù)性帶來(lái)的好處就是,分支及合并變得不再可怕。版本控制工具本該幫助我們方便的進(jìn)行和分支及合并操作。

          簡(jiǎn)單介紹下工具后,讓我們來(lái)看開(kāi)發(fā)模型。我講介紹的模型本質(zhì)上只是一組步驟,每個(gè)團(tuán)隊(duì)成員都必須遵循這些步驟以形成一個(gè)可靠管理的軟件開(kāi)發(fā)過(guò)程。

          去中心化但仍保持中心化

          在這個(gè)分支模型中我們使用的,且被證實(shí)工作得很好的倉(cāng)庫(kù)配置,其核心是一個(gè)中心“真理”倉(cāng)庫(kù)。注意只有該倉(cāng)庫(kù)才被認(rèn)為是中心庫(kù)(由于Git是 DVCS [分布式版本控制系統(tǒng)],在技術(shù)層面沒(méi)有中心庫(kù)這一東西)。之后我們用origin指代該倉(cāng)庫(kù),因?yàn)榇蠖鄶?shù)Git用戶(hù)都熟悉這個(gè)名稱(chēng)。

          每個(gè)開(kāi)發(fā)者都對(duì)origin做push和pull操作。不過(guò)除了這種中心化的push-pull關(guān)系外,每個(gè)開(kāi)發(fā)者還可以從其他開(kāi)發(fā)者或者小組處 pull變更。例如,可能兩個(gè)或更多的開(kāi)發(fā)者一起開(kāi)發(fā)一個(gè)大的特性,在往origin永久性的push工作代碼之前,他們之間可以執(zhí)行一些去中心化的操 作。在上圖中,分別有Alice和Bob、Alice和David、Clair和David這些小組。

          從技術(shù)上來(lái)說(shuō),這僅僅是Alice定義一個(gè)Git remote,名字為bob,指向Bob的倉(cāng)庫(kù),反過(guò)來(lái)也一樣。

          主要分支

          此開(kāi)發(fā)模型的核心主要受現(xiàn)有的模型啟發(fā)。中心倉(cāng)庫(kù)包含了兩個(gè)主要分支,這兩個(gè)分支的壽命是無(wú)限的:

          • master
          • develop

          每個(gè)Git用于都應(yīng)該熟悉origin上的master分支。與master分支平行存在的,是另外一個(gè)名為develop的分支。

          我們認(rèn)為origin/develop分支上的HEAD源碼反映了開(kāi)發(fā)過(guò)程中最新的提交變更。有人會(huì)稱(chēng)之為“集成分支”。該分支是自動(dòng)化每日構(gòu)建的代碼源。

          當(dāng)develop分支上的源碼到達(dá)一個(gè)穩(wěn)定的狀態(tài)時(shí),就可以發(fā)布版本。所有develop上的變更都應(yīng)該以某種方式合并回master分支,并且使用發(fā)布版本號(hào)打上標(biāo)簽。稍后我們會(huì)討論具體操作細(xì)節(jié)。

          因此,每次有變化被合并到master分支時(shí),根據(jù)定義這就是一次新的產(chǎn)品版本發(fā)布。我們趨向于嚴(yán)格遵守該規(guī)范,所以理論上來(lái)說(shuō),每次master有提交時(shí),我們都可以使用一個(gè)Git鉤子(hook)腳本來(lái)自動(dòng)構(gòu)建并部署軟件至產(chǎn)品環(huán)境服務(wù)器。

          支持性分支

          緊接著主要分支master和develop,我們的開(kāi)發(fā)模型使用多種支持性分支來(lái)幫助團(tuán)隊(duì)成員間實(shí)現(xiàn)并行開(kāi)發(fā)、追蹤產(chǎn)品特性、準(zhǔn)備產(chǎn)品版本發(fā)布、以及快速修復(fù)產(chǎn)品問(wèn)題。與主要分支不同的是,這些分支的壽命是有限的,它們最終都會(huì)被刪除。

          我們會(huì)用到的分支有這幾類(lèi):

          • 特性分支(feature branch)
          • 發(fā)布分支(release branch)
          • 熱補(bǔ)丁分支(hotfix branch)

          上述每種分支都有特定的用途,它們各自關(guān)于源自什么分支、合并回什么分支,都有嚴(yán)格的規(guī)定。稍后我們逐個(gè)進(jìn)行介紹。

          從技術(shù)角度來(lái)說(shuō),這些分支一點(diǎn)都不“特殊”。分支按照我們對(duì)其的使用方式進(jìn)行分類(lèi)。技術(shù)角度它們都一樣是平常的Git分支。

          特性分支

          可能的分支來(lái)源:develop
          必須合并回:develop
          分支命令約定:任何除master, develop, release-*, 或 hotfix-*以外的名稱(chēng)

          特性分支(有時(shí)也被稱(chēng)作topic分支)是用來(lái)為下一發(fā)布版本開(kāi)發(fā)新特性。當(dāng)開(kāi)始開(kāi)發(fā)一個(gè)特性的時(shí)候,該特性會(huì)成為哪個(gè)發(fā)布版本的一部分,往往還不 知道。特性分支的重點(diǎn)是,只要特性還在開(kāi)發(fā),該分支就會(huì)一直存在,不過(guò)它最終會(huì)被合并回develop分支(將該特性加入到發(fā)布版本中),或者被丟棄(如 果試驗(yàn)的結(jié)果令人失望)。

          特性分支往往只存在于開(kāi)發(fā)者的倉(cāng)庫(kù)中,而不會(huì)出現(xiàn)在origin。

          創(chuàng)建一個(gè)特性分支

          開(kāi)始開(kāi)發(fā)新特性的時(shí)候,從develop分支創(chuàng)建特性分支。

          $ git checkout -b myfeature develop
          Switch to a new branch “myfeature”

          合并完成的特性回develop

          完成的特性應(yīng)該被合并回develop分支以將特性加入到下一個(gè)發(fā)布版本中:

          $ git checkout develop
          Switch to branch ‘develop’
          $ git merge –no-ff myfeature
          Updating ea1b82a..05e9557
          (Summary of changes)
          $ git branch -d myfeature
          Deleted branch myfeature (was 05e9557).
          $ git push origin develop

          上述代碼中的–no-ff標(biāo)記會(huì)使合并永遠(yuǎn)創(chuàng)建一個(gè)新的commit對(duì)象,即使該合并能以fast-forward的方式進(jìn)行。這么做可以避免丟失特性分支存在的歷史信息,同時(shí)也能清晰的展現(xiàn)一組commit一起構(gòu)成一個(gè)特性。比較下面的圖:

          在第2張圖中,已經(jīng)無(wú)法一眼從Git歷史中看到哪些commit對(duì)象構(gòu)成了一個(gè)特性——你需要閱讀日志以獲得該信息。在這種情況下,回退(revert)整個(gè)特性(一組commit)就會(huì)比較麻煩,而如果使用了–no-diff就會(huì)簡(jiǎn)單很多。

          是的,這么做會(huì)造成一些(空的)commit對(duì)象,但這么做是利大于弊的。

          可惜的是,我沒(méi)能找到方法讓–no-diff成為默認(rèn)的git merge行為參數(shù),但其實(shí)應(yīng)該這么做。

          發(fā)布分支

          可能的分支來(lái)源:develop
          必須合并回:develop和master
          分支命名約定:release-*

          發(fā)布分支為準(zhǔn)備新的產(chǎn)品版本發(fā)布做支持。它允許你在最后時(shí)刻檢查所有的細(xì)節(jié)。此外,它還允許你修復(fù)小bug以及準(zhǔn)備版本發(fā)布的元數(shù)據(jù)(例如版本號(hào),構(gòu)建日期等等)。在發(fā)布分支做這些事情之后,develop分支就會(huì)顯得比較干凈,也方便為下一大版本發(fā)布接受特性。

          從develop分支創(chuàng)建發(fā)布分支的時(shí)間通常是develop分支(差不多)能反映新版本所期望狀態(tài)的時(shí)候。至少說(shuō),這是時(shí)候版本發(fā)布所計(jì)劃的特性 都已經(jīng)合并回了develop分支。而未來(lái)其它版本發(fā)布計(jì)劃的特性則不應(yīng)該合并,它們必須等到當(dāng)前的版本分支創(chuàng)建好之后才能合并。

          正是在發(fā)布分支創(chuàng)建的時(shí)候,對(duì)應(yīng)的版本發(fā)布才獲得一個(gè)版本號(hào)——不能更早。在該時(shí)刻之前,develop分支反映的是“下一版本”的相關(guān)變更,但不知道這“下一版本”到底會(huì)成為0.3還是1.0,直到發(fā)布分支被創(chuàng)建。版本號(hào)是在發(fā)布分支創(chuàng)建時(shí),基于項(xiàng)目版本號(hào)規(guī)則確定的。

          創(chuàng)建一個(gè)發(fā)布分支

          發(fā)布分支從develop分支創(chuàng)建。例如,假設(shè)1.1.5是當(dāng)前的產(chǎn)品版本,同時(shí)我們即將發(fā)布下個(gè)版本。develop分支的狀態(tài)已經(jīng)是準(zhǔn)備好“下 一版本”發(fā)布了,我們也決定下個(gè)版本是1.2(而不是1.1.6或者2.0)。因此我們創(chuàng)建發(fā)布分支,并且為其賦予一個(gè)能體現(xiàn)新版本號(hào)的名稱(chēng):

          $ git checkout -b releases-1.2 develop
          Switched to a new branch “release-1.2”
          $ ./bump-version.sh 1.2
          Files modified successfully. version bumped to 1.2.
          $ git commit -a -m “Bumped version number to 1.2”
          [release-1.2 74d9424] Bumped version number to 1.2
          1 files changed. 1 insertions(+). 1 deletions(-)

          創(chuàng)建新分支并轉(zhuǎn)到該分支之后,我們?cè)O(shè)定版本號(hào)。這里的bump-version.sh是一個(gè)虛構(gòu)的shell腳本,它修改一些本地工作區(qū)的文件以體現(xiàn)新的版本號(hào)。(當(dāng)然這也可以手動(dòng)完成——這里只是說(shuō)要有一些文件變更)接著,提交新版本號(hào)。

          新的發(fā)布分支可能存在一段時(shí)間,直到該版本明確對(duì)外交付。這段時(shí)間內(nèi),該分支上可能會(huì)有一些bug的修復(fù)(而不是在develop分支上)。在該分支上添加新特性是嚴(yán)格禁止的。新特性必須合并到develop分支,然后等待下一個(gè)版本發(fā)布。

          結(jié)束一個(gè)特性分支

          當(dāng)特性分支達(dá)到一個(gè)可以正式發(fā)布的狀態(tài)時(shí),我們就需要執(zhí)行一些操作。首先,將發(fā)布分支合并至master(記住,我們之前定義master分支上的 每一個(gè)commit都對(duì)應(yīng)一個(gè)新版本)。接著,master分支上的commit必須被打上標(biāo)簽(tag),以方便將來(lái)尋找歷史版本。最后,發(fā)布分支上的 變更需要合并回develop,這樣將來(lái)的版本也能包含相關(guān)的bug修復(fù)。

          前兩步在Git中的操作:

          $ git checkout master
          Switched to branch ‘master’
          $ git merge –no-ff release-1.2
          Merge made by recursive.
          (Summary of changes)
          $ git tag -a 1.2

          現(xiàn)在版本發(fā)布完成了,而且為未來(lái)的查閱提供了標(biāo)簽。

          提醒:你可能同時(shí)也會(huì)想要用 -s 或者 -u <key> 來(lái)對(duì)標(biāo)簽進(jìn)行簽名。

          為了能保留發(fā)布分支上的變更,我們還需要將分支合并回develop。在Git中:

          $ git checkout develop
          Switched to branch ‘develop’
          $ git merge –no-ff release-1.2
          Merge made by recursive.
          (Summary of changes)

          這一操作可能會(huì)導(dǎo)致合并沖突(可能性還很大,因?yàn)槲覀兏淖兞税姹咎?hào))。如果發(fā)現(xiàn),則修復(fù)之并提交。

          現(xiàn)在工作才算真正完成了,最后一步是刪除發(fā)布分支,因?yàn)槲覀円巡辉傩枰?/p>

          $ git branch -d release-1.2
          Deleted branch release-1.2 (was ff452fe).

          熱補(bǔ)丁分支

          可能的分支來(lái)源:master
          必須合并回:develop和master
          分支命名約定:hotfix-*

          熱補(bǔ)丁分支和發(fā)布分支十分類(lèi)似,它的目的也是發(fā)布一個(gè)新的產(chǎn)品版本,盡管是不在計(jì)劃中的版本發(fā)布。當(dāng)產(chǎn)品版本發(fā)現(xiàn)未預(yù)期的問(wèn)題的時(shí)候,就需要理解著 手處理,這個(gè)時(shí)候就要用到熱補(bǔ)丁分支。當(dāng)產(chǎn)品版本的重大bug需要立即解決的時(shí)候,我們從對(duì)應(yīng)版本的標(biāo)簽創(chuàng)建出一個(gè)熱補(bǔ)丁分支。

          使用熱補(bǔ)丁分支的主要作用是(develop分支上的)團(tuán)隊(duì)成員可以繼續(xù)工作,而另外的人可以在熱補(bǔ)丁分支上進(jìn)行快速的產(chǎn)品bug修復(fù)。

          創(chuàng)建一個(gè)熱補(bǔ)丁分支

          熱補(bǔ)丁分支從master分支創(chuàng)建。例如,假設(shè)1.2是當(dāng)前正在被使用的產(chǎn)品版本,由于一個(gè)嚴(yán)重的bug,產(chǎn)品引起了很多問(wèn)題。同時(shí),develop分支還處于不穩(wěn)定狀態(tài),無(wú)法發(fā)布新的版本。這時(shí)我們可以創(chuàng)建一個(gè)熱補(bǔ)丁分支,并在該分支上修復(fù)問(wèn)題:

          $ git checkout -b hotfix-1.2.1 master
          Switched to a new branch “hotfix-1.2.1″
          $ ./bump-version.sh 1.2.1
          Files modified successfully, version bumped to 1.2.1.
          $ git commit -a -m “Bumped version number to 1.2.1″
          [hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1
          1 files changed, 1 insertions(+), 1 deletions(-)

          不要忘了在創(chuàng)建熱補(bǔ)丁分之后設(shè)定一個(gè)新的版本號(hào)!

          然后,修復(fù)bug并使用一個(gè)或者多個(gè)單獨(dú)的commit提交。

          $ git commit -m “Fixed severe production problem”
          [hotfix-1.2.1 abbe5d6] Fixed severe production problem
          5 files changed, 32 insertions(+), 17 deletions(-)

          結(jié)束一個(gè)熱補(bǔ)丁分支

          修復(fù)完成后,熱補(bǔ)丁分支需要合并回master,但同時(shí)它還需要被合并回develop,這樣相關(guān)的修復(fù)代碼才會(huì)同時(shí)被包含在下個(gè)版本中。這與我們完成發(fā)布分支很類(lèi)似。

          首先,更新master分支并打上標(biāo)簽。

          $ git checkout master
          Switched to branch ‘master’
          $ git merge –no-ff hotfix-1.2.1
          Merge made by recursive.
          (Summary of changes)
          $ git tag -a 1.2.1

          提醒:你可能同時(shí)也會(huì)想要用 -s 或者 -u <key> 來(lái)對(duì)標(biāo)簽進(jìn)行簽名。

          接著,將修復(fù)代碼合并到develop:

          $ git checkout develop
          Switched to branch ‘develop’
          $ git merge –no-ff hotfix-1.2.1
          Merge made by recursive.
          (Summary of changes)

          這里還有個(gè)例外情況,如果這個(gè)時(shí)候有發(fā)布分支存在,熱補(bǔ)丁分支的變更則應(yīng)該合并至發(fā)布分支,而不是develop。將熱補(bǔ)丁合并到發(fā)布分支,也意味著當(dāng)發(fā)布分支結(jié)束的時(shí)候,變更最終會(huì)被合并到develop。(如果develop上的開(kāi)發(fā)工作急需熱補(bǔ)丁并無(wú)法等待發(fā)布分支完成,這時(shí)你也已經(jīng)可以安全地將熱補(bǔ)丁合并到develop分支。)

          最后,刪除臨時(shí)的熱補(bǔ)丁分支:

          $ git branch -d hotfix-1.2.1
          Deleted branch hotfix-1.2.1 (was abbe5d6).

          小結(jié)

          雖然這個(gè)分支模型中沒(méi)有什么特別新鮮的東西,但本文起始處的“全景圖”事實(shí)上在我們的項(xiàng)目中起到了非常大的作用。它幫助建立了優(yōu)雅的,易理解的概念模型,使得團(tuán)隊(duì)成員能夠快速建立并理解一個(gè)公用的分支和發(fā)布過(guò)程。

          我同時(shí)也提供了一個(gè)該圖對(duì)應(yīng)的高質(zhì)量PDF版本。你可以打印出來(lái)并掛在墻上,隨時(shí)參考。

          原文:http://www.juvenxu.com/tag/%E5%88%86%E6%94%AF/

          Feedback

          # re: 一個(gè)成功的Git分支模型  回復(fù)  更多評(píng)論   

          2014-09-29 16:13 by 4332weizi
          我想知道博文里面的圖是用什么工具畫(huà)出來(lái)的,知道的同志們告知一下,謝謝
          主站蜘蛛池模板: 蒲城县| 瑞金市| 清徐县| 临湘市| 陇南市| 古蔺县| 南宁市| 法库县| 宝鸡市| 池州市| 咸阳市| 乌鲁木齐市| 噶尔县| 大新县| 龙泉市| 大邑县| 黄陵县| 五指山市| 洪湖市| 通山县| 新干县| 章丘市| 江西省| 和田县| 邛崃市| 洞头县| 界首市| 五常市| 白玉县| 泰顺县| 汶川县| 平阳县| 洪雅县| 石狮市| 崇文区| 巩义市| 双桥区| 民乐县| 曲麻莱县| 宁安市| 桓台县|