Git 分支管理:成功的Git分支模型[A successful Git branching model]

譯自:A successful Git branching model ? nvie.com
本文將展示我一年前在自己的項目中成功運用的開發(fā)模型荆忍。我一直打算把這些東西寫出來导而,但總是沒有抽出時間辕录,現(xiàn)在終于寫好了玫鸟。這里介紹的不是任何項目的細節(jié)退渗,而是有關(guān)分支的策略以及對發(fā)布的管理脆炎。


在我的演示中,所有的操作都是通過 git 完成的氓辣。

為什么選擇 git 秒裕?

為了了斷 git 和中心源代碼控制系統(tǒng)的比較和爭論,請移步這里看看 鏈接1 鏈接2钞啸。作為一個開發(fā)者几蜻,我喜歡 git 超過其它任何現(xiàn)有的工具。Git 真正改變了開發(fā)者對于合并和分支的認識体斩。在傳統(tǒng)的 CVS/SVN 里梭稚,合并/分支總是有點令人害怕的(“注意合并沖突,它們會搞死你的”)絮吵。

但是 git 中的這些操作是如此的簡單有效弧烤,它們真正作為你每天工作流程的一部分。比如蹬敲,在 CVS/SVN 的書籍里暇昂,分支和合并總是最后一個章節(jié)的討論重點(對于高級用戶),而在每一本 git 的書里 鏈接1 鏈接2 鏈接3伴嗡,這些內(nèi)容已經(jīng)被包含在第三章(基礎)里了急波。

因為它的簡單直接和重復性,分支和合并不再令人害怕瘪校。版本控制工具比其它任何東西都支持分支/合并澄暮。

有關(guān)工具就介紹到這里名段,我們現(xiàn)在進入開發(fā)模型這個正題。我要展現(xiàn)的模型本質(zhì)上無外乎是一個流程的集合泣懊,每個團隊成員都有必要遵守這些流程伸辟,來達到管理軟件開發(fā)流程的目的。

分散但也集中

我們的分支模型中使用良好的代碼庫的設置方式馍刮,是圍繞一個真實的中心代碼庫的信夫。注意,這里的代碼庫僅僅被看做是一個中心代碼庫(因為 git 是 DVCS渠退,即分散版本控制系統(tǒng)忙迁,從技術(shù)層面看脐彩,是沒有所謂的中心代碼庫的)碎乃。我們習慣于把這個中心代碼庫命名為 origin
,這同時也是所有 git 用戶的習慣惠奸。



每一位開發(fā)者都向 origin這個中心結(jié)點 pull 和 push梅誓。但是除此之外,每一位開發(fā)者也可以向其它結(jié)點 pull 改變形成子團隊佛南。比如梗掰,對于兩個以上開發(fā)者同時開發(fā)一項大的新Feature來說,為了不必過早向 origin推送開發(fā)進度嗅回,這就非常有用及穗。在上面的這個例子中,Alice 和 Bob绵载、Alice 和 David埂陆、Clair 和 David 都是這樣的子團隊。

從技術(shù)角度娃豹,這無非意味著 Alice 定義一個名為 Bob的 git remote焚虱,指向 Bob 的代碼庫,反之亦然懂版。

主分支


該開發(fā)模型的核心基本和現(xiàn)有的模型是一樣的鹃栽。中心代碼庫永遠維持著兩個主要的分支:

  • master
  • develop

在 origin上的 master分支和每個 git 用戶的保持一致。而和 master分支并行的另一個分支叫做 develop躯畴。

我們認為 origin/master是其 HEAD源代碼總是代表了生產(chǎn)環(huán)境準備就緒的狀態(tài)的主分支民鼓。

我們認為 origin/develop是其 HEAD源代碼總是代表了最后一次交付的可以趕上下一次發(fā)布的狀態(tài)的主分支。有人也把它叫做“集成分支”蓬抄。該源代碼還被作為了 nightly build 自動化任務的來源摹察。

每當 develop分支到達一個穩(wěn)定的階段,可以對外發(fā)布時倡鲸,所有的改變都會被合并到 master分支供嚎,并打一個發(fā)布版本的 tag。具體操作方法我們稍后討論。

因此克滴,每次改動被合并到 master的時候逼争,這就是一個真正的新的發(fā)布產(chǎn)品。我們建議對此進行嚴格的控制劝赔,因此理論上我們可以為每次 master分支的提交都掛一個鉤子腳本誓焦,向生產(chǎn)環(huán)境自動化構(gòu)建并發(fā)布我們的軟件。

支持型分支

我們的開發(fā)模型里着帽,緊接著 master和 develop主分支的杂伟,是多種多樣的支持型分支。它們的目的是幫助團隊成員并行處理每次追蹤Feature仍翰、準備發(fā)布赫粥、快速修復線上問題等開發(fā)任務。和之前的主分支不同予借,這些分支的生命周期都是有限的越平,它們最終都會被刪除掉。
我們可能會用到的不同類型的分支有:

  • feature 分支
  • release 分支
  • hotfix 分支

每一種分支都有一個特別的目的灵迫,并且有嚴格的規(guī)則秦叛,諸如哪些分支是它們的起始分支、哪些分支必須是它們合并的目標等瀑粥。我們快速把它們過一遍挣跋。
這些“特殊”的分支在技術(shù)上是沒有任何特殊的。分支的類型取決于我們?nèi)绾?em>運用它們狞换。它們完完全全都是普通而又平凡的 git 分支避咆。

feature 分支

  • 可能派發(fā)自:develop
  • 必須合并回:develop
  • 分支命名規(guī)范:除了 master、develop哀澈、release-或 hotfix-的任何名字

Feature 分支(有時也被稱作 topic 分支)用來開發(fā)包括即將發(fā)布或遠期發(fā)布的新的Feature牌借。當我們開始開發(fā)一個Feature的時候,發(fā)布合并的目標可能還不太確定割按。Feature 分支的生命周期會和新Feature的開發(fā)周期保持同步膨报,但是最終會合并回 develop (恩,下次發(fā)布的時候把這個新Feature帶上)或被拋棄(真是一次杯具的嘗試啊)适荣。

Feature 分支通常僅存在于開發(fā)者的代碼庫中现柠,并不出現(xiàn)在 origin里。

創(chuàng)建一個 feature 分支

當開始一個新feature的時候弛矛,從 develop分支派發(fā)出一個分支

$ git checkout -b myfeature develop
Switched to a new branch "myfeature"

把完成的feature合并回 develop

完成的feature可以合并回 develop
分支并趕上下一次發(fā)布:

$ git checkout developSwitched to a new branch "develop"
$ git merge --no-ff myfeatureUpdating ea1b82a..05e9557(Summary of changes)
$ git branch -d myfeatureDeleted branch myfeature (was 05e9557)$ git push origin develop

-no-ff 標記使得合并操作總是產(chǎn)生一次新的提交够吩,哪怕合并操作可以快速完成。這個標記避免將 feature 分支和團隊協(xié)作的所有提交的歷史信息混在主分支的其它提交之后丈氓。比較一下:



在右邊的例子里周循,我們不可能從 git 的歷史記錄中看出來哪些提交實現(xiàn)了這一Feature——你可能不得不查看每一筆提交日志强法。恢復一個完整的Feature(比如通過一組提交)在右邊變成了一個頭疼事情湾笛,而如果使用了 --no-ff之后饮怯,就變得簡單了。

是的嚎研,這會創(chuàng)造一些沒有必要的(空的)提交記錄蓖墅,但是得到的是大量的好處。

不幸的是临扮,我還沒有找到一個在 git merge 時默認就把 --no-ff 標記打上的辦法论矾,但這很重要。

release 分支

可能派發(fā)自:develop

必須合并回:develop和 master

分支命名規(guī)范:release-*

Release 分支用來支持新的生產(chǎn)環(huán)境發(fā)布的準備工作杆勇。允許在最后階段產(chǎn)生提交點(dotting i's)和交匯點(crossing t's)贪壳。而且允許小幅度的問題修復以及準備發(fā)布時的meta數(shù)據(jù)(比如版本號、發(fā)布日期等)靶橱。

在 release分支做了上述這些工作之后寥袭,develop分支會被“翻篇兒”路捧,開始接收下一次發(fā)布的新Feature关霸。

我們選擇(幾近)完成所有預期的開發(fā)的時候,作為從 develop
派發(fā)出 release分支的時機杰扫。最起碼所有準備構(gòu)建發(fā)布的功能都已經(jīng)及時合并到了 develop分支队寇。而往后才會發(fā)布的功能則不應該合并到 develop分支——他們必須等到 release分支派發(fā)出去之后再做合并。

在一個 release分支的開始章姓,我們就賦予其一個明確的版本號佳遣。直到該分支創(chuàng)建之前,develop分支上的描述都是“下一次”release 的改動凡伊,但這個“下一次”release 其實也沒說清楚是 0.3 release 還是 1.0 release零渐。而在一個 release 分支的開始時這一點就會確定。這將成為有關(guān)項目版本號晉升的一個守則系忙。

創(chuàng)建一個 release 分支

Release分支派發(fā)自 develop分支诵盼。比如,我們當前的生產(chǎn)環(huán)境發(fā)布的版本是 1.1.5银还,馬上有一個 release 要發(fā)布了风宁。develop分支已經(jīng)為“下一次”release 做好了準備,并且我們已經(jīng)決定把新的版本號定為 1.2 (而不是 1.1.6 或 2.0)蛹疯。所以我們派發(fā)一個 release 分支并以新的版本號為其命名:

$ git checkout -b release-1.2 developSwitched to a new branch "release-1.2"
$ ./bump-version.sh 1.2Files 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.21 files changed, 1 insertions(+), 1 deletions(-)

創(chuàng)建好并切換到新的分支之后戒财,我們完成對版本號的晉升。這里的 bump-version.sh是一個虛構(gòu)的用來改變代碼庫中某些文件以反映新版本的 shell 腳本捺弦。(當然你也可以手動完成這些改變——重點是有些文件發(fā)生了改變)然后饮寞,晉升了的版本號會被提交孝扛。

這個新的分支會存在一段時間,直到它確實發(fā)布出去了為止幽崩。期間可能會有 bug 修復(這比在 develop做更合理)疗琉。但我們嚴格禁止在此開發(fā)龐大的新Feature,它們應該合并到 develop分支歉铝,并放入下次發(fā)布盈简。

完成一個 release 分支

當 release 分支真正發(fā)布成功之后,還有些事情需要收尾太示。首先柠贤,release 分支會被合并到 master(別忘了,master上的每一次提交都代表一個真正的新的發(fā)布)类缤;然后臼勉,為 master上的這次提交打一個 tag,以便作為版本歷史的重要參考餐弱;最后宴霸,還要把 release 分支產(chǎn)生的改動合并回 develop,以便后續(xù)的發(fā)布同樣包含對這些 bug 的修復膏蚓。

前兩部在 git 下是這樣操作的:

$ git checkout masterSwitched to branch 'master'
$ git merge --no-ff release-1.2Merge made by recursive(Summary of changes)
$ git tag -a 1.2

現(xiàn)在發(fā)布工作已經(jīng)完成了瓢谢,同時 tag 也打好了,用在未來做參考驮瞧。

補充:你也可以通過 -s或 -u <key>標記打 tag氓扛。

為了保留 release 分支里的改動記錄,我們需要把這些改動合并回 develop论笔。git 操作如下:

$ git checkout developSwitched to branch 'develop'
$ git merge --no-ff release-1.2Merge made by recursive.(Summary of changes)

這一步有可能導致沖突的發(fā)生(只是有理論上的可能性采郎,因為我們已經(jīng)改變了版本號),一旦發(fā)現(xiàn)狂魔,解決沖突然后提交就好了蒜埋。
現(xiàn)在我們真正完成了一個 release 分支,該把它刪掉了最楷,因為它的使命已經(jīng)完成了:

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

hotfix 分支

  • 可能派發(fā)自:master

  • 必須合并回:develop 和 master

  • 分支命名規(guī)范:hotfix-*

Hotfix 分支和 release 分支非常類似整份,因為他們都意味著會產(chǎn)生一個新的生產(chǎn)環(huán)境的發(fā)布,盡管 hotfix 分支不是先前就計劃好的管嬉。他們在實時的生產(chǎn)環(huán)境版本出現(xiàn)意外需要快速響應時皂林,從 master分支相應的 tag 被派發(fā)琳轿。

我們這樣做的根本原因倍试,是為了讓團隊其中一個人來快速修復生產(chǎn)環(huán)境的問題,其他成員可以按工作計劃繼續(xù)工作下去而不受太大影響徒蟆。

創(chuàng)建一個 hotfix 分支

Hotfix 分支創(chuàng)建自 master分支胎挎。例如沟启,假設 1.2 版本是目前的生產(chǎn)環(huán)境且出現(xiàn)了一個嚴重的 bug忆家,但是目前的 develop并不足夠穩(wěn)定。那么我們可以派發(fā)出一個 hotfix 分支來開始我們的修復工作:

$ git checkout -b hotfix-1.2.1 masterSwitched to a new branch "hotfix-1.2.1"
$ ./bump-version.sh 1.2.1Files 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.11 files changed, 1 insertions(+), 1 deletions(-)

別忘了在派發(fā)出分支之后晉升版本號德迹!
然后芽卿,修復 bug,提交改動胳搞。通過一個或多個提交都可以卸例。

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

完成一個 hotfix 分支

當我們完成之后,對 bug 的修復需要合并回 master肌毅,同時也需要合并回 develop筷转,以保證接下來的發(fā)布也都已經(jīng)解決了這個 bug。這和 release 分支的完成方式是完全一樣的悬而。

首先呜舒,更新 master并為本次發(fā)布打一個 tag:

$ git checkout masterSwitched to branch 'master'
$ git merge --no-ff hotfix-1.2.1Merge made by recursive(Summary of changes)
$ git tag -a 1.2.1

補充:你也可以通過 -s或 -u <key> 標記打 tag。
然后笨奠,把已修復的 bug 合并到 develop:

$ git checkout developSwitched to branch 'develop'
$ git merge --no-ff hotfix-1.2.1Merge made by recursive(Summary of changes)

這個規(guī)矩的一個額外之處是:如果此時已經(jīng)存在了一個 release 分支袭蝗,那么 hotfix 的改變需要合并到這個 release 分支,而不是 develop 分支般婆。因為把對 bug 的修復合并回 release 分支之后到腥,release 分支最終還是會合并回 develop分支的。(如果在 develop分支中立刻需要對這個 bug 的修復腺兴,且等不及 release 分支合并回來左电,則你還是可以直接合并回 develop分支的廉侧,這是絕對沒問題的)

最后页响,刪掉這個臨時的分支:

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

摘要

其實這個分支模型里沒有什么新奇的東西。文章開頭的那張大圖對我們的項目來說非常有用段誊。它非常易于團隊成員理解這個優(yōu)雅有效的模型闰蚕,并在團隊內(nèi)部達成共識。

這里還有一份那張大圖的 高清PDF版本连舍,你可以把它當做手冊放在手邊快速瀏覽没陡。

補充:還有,如果你們需要的話索赏,這里還有一份 Keynote 版本

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盼玄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子潜腻,更是在濱河造成了極大的恐慌埃儿,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件融涣,死亡現(xiàn)場離奇詭異童番,居然都是意外死亡精钮,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門剃斧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轨香,“玉大人,你說我怎么就攤上這事幼东”廴荩” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵根蟹,是天一觀的道長策橘。 經(jīng)常有香客問我,道長娜亿,這世上最難降的妖魔是什么丽已? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮买决,結(jié)果婚禮上沛婴,老公的妹妹穿的比我還像新娘。我一直安慰自己督赤,他們只是感情好嘁灯,可當我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著躲舌,像睡著了一般丑婿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上没卸,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天羹奉,我揣著相機與錄音,去河邊找鬼约计。 笑死诀拭,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的煤蚌。 我是一名探鬼主播耕挨,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼尉桩!你這毒婦竟也來了筒占?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蜘犁,失蹤者是張志新(化名)和其女友劉穎翰苫,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沽瘦,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡革骨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年农尖,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片良哲。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡盛卡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出筑凫,到底是詐尸還是另有隱情滑沧,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布巍实,位于F島的核電站滓技,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏棚潦。R本人自食惡果不足惜令漂,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丸边。 院中可真熱鬧叠必,春花似錦、人聲如沸妹窖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骄呼。三九已至共苛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蜓萄,已是汗流浹背隅茎。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留绕德,地道東北人患膛。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像耻蛇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子胞此,可洞房花燭夜當晚...
    茶點故事閱讀 45,500評論 2 359

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