在這篇文章中,我提出一個開發模型。我已經將這個開發模型引入 到我全部的項目裏(不管在工做仍是私人)已經一年有餘,而且它被證實是很是成功的。我打算寫 這些已經好久了,但我一直找不到時間來作,如今終於有時間了。我不會講任何項目的具體細節,僅是關於分支策略和釋放管理相關內容。php
它主要體現了Git對咱們源代碼版本的管理。git
對於Git與其餘集中式代碼管理工具相比的優缺點的全面討論,請參見這裏。 這樣的爭論老是喋喋不休。做爲一個開發者,與現今的其餘開發工具相比較,我更喜歡Git。Git真得改變了開發者對於合併和分支的思考。我曾經使用經典的 CVS/Subversion,然而每次的合併/分支和其餘行爲總讓人擔驚受怕(「當心合併裏的衝突,簡直要命!」)。github
可是對於Git來講,這些行爲很是簡單和搞笑,它們被認爲是平常工做中的核心部分。例如,在不少CVS/Subversion書裏,分支與合併老是在後面的章節中被討論(對於高級用戶使用),然而在每一個Git書中,在第3章就已經徹底涵蓋了(做爲基礎)。shell
簡單和重複的特性帶來的結果是:分支與合併再也不是什麼能夠懼怕的東西。分支/合併被認爲對於版本管理工具比其餘功能更重要。服務器
關於工具,再也不多說,讓咱們直接看開發模型吧。這個模型並非以下模型:在管理軟件開發進度方面,面對每一個開發過程,每一個隊員必須按必定次序開發。分佈式
對於這種分支模型,咱們設置了一個版本庫,它運轉良好,這是一個"事實上" 版本庫。不過請注意,這個版本庫只是被認爲是中心版本庫(由於Git是一個分佈式版本管理系統,從技術上來說,並無一箇中心版本庫)。咱們將把這個版本庫稱爲原始庫,這個名字對全部的Git用戶來講都很容易理解。svn
每一個開發者都對origin庫拉代碼和提交代碼。可是除了集中 式的存取代碼關係,每一個開發者也能夠從子團隊的其餘隊友那裏得到代碼版本變動。例如,對於2個或多個開發者一塊兒完成的大版本變動,爲了防止過早地向 origin庫提交工做內容,這種機制就變得很是有用。在上述途中,有以下子團隊:Alice和Bob,Alice和David,Clair和 David。工具
從技術上將,這意味着,Alice建立了一個Git的遠程節點,而對於Bob,該節點指向了Bob的版本庫,反之亦然。post
在覈心部分,研發模型很大程度上靠其餘現有模型支撐的。中心庫有2個可一直延續的分支:開發工具
每一個Git用戶都要熟悉原始的master分支。與master分支並行的另外一個分支,咱們稱之爲develop分支。
咱們把原始庫/master庫認做爲主分支,HEAD的源代碼存在於此版本中,而且隨時都是一個預備生產狀態。
咱們把origin/develop庫認爲是主分支,該分支HEAD源碼始終體現下個發佈版的最新軟件變動。有人稱這個爲「集成分支」,而這是每晚自動構建得來的。
當develop分支的源碼到達了一個穩定狀態待發布,全部的代碼變動須要以某種方式合併到master分支,而後標記一個版本號。如何操做將在稍後詳細介紹。
因此,每次變動都合併到了master,這就是新產品的定義。在這一點,咱們傾向於嚴格執行這一點,從而,理論上,每當對master有一個提交操做,咱們就可使用Git鉤子腳原本自動構建而且發佈軟件到生產服務器。
咱們的開發模型使用了各類輔助性分支,這些分支與關鍵分支(master和develop)一塊兒,用來支持團隊成員們並行開發,使得易於追蹤功能,協助生產發佈環境準備,以及快速修復實時在線問題。與關鍵分支不一樣,這些分支老是有一個有限的生命期,由於他們最終會被移除。
咱們用到的分支類型包括:
每一種分支有一個特定目的,而且受限於嚴格到規則,好比:能夠用哪些分支做爲源分支,哪些分支能做爲合併目標。咱們立刻將進行演練。
從技術角度來看,這些分支毫不是特殊分支。分支的類型基於咱們使用的方法來進行分類。它們理所固然是普通的Git分支。
多是develop分支的分支版本,最終必須合併到develop分支中。
分支命名規則:除了master、develop、release-*、orhotfix-*以外,其餘命名都可。
功能分支(有時被稱爲topic分支)一般爲即將發佈或者將來 發佈版開發新的功能。當新功能開始研發,包含該功能的發佈版本在這個仍是沒法肯定發佈時間 的。功能版本的實質是隻要這個功能處於開發狀態它就會存在,可是最終會或合併到develop分支(肯定將新功能添加到不久的發佈版中)或取消(譬如一次 使人失望的測試)。
功能分支一般存在於開發者的軟件庫,而不是在源代碼庫中。
開始一項功能的開發工做時,基於develop建立分支。
1 $ git checkout -b myfeature develop 2 Switched to a new branch "myfeature"
完成的功能能夠合併進develop分支,以明確加入到將來的發佈:
1 $ git checkout develop 2 Switched to branch 'develop' 3 $ git merge --no-ff myfeature 4 Updating ea1b82a..05e9557 5 (Summary of changes) 6 $ git branch -d myfeature 7 Deleted branch myfeature (was 05e9557). 8 $ git push origin develop
--no-ff標誌致使合併操做建立一個新commit對象,即便該合併操做能夠fast-forward。這避免了丟失這個功能分支存在的歷史信息,將該功能的全部提交組合在一塊兒。 比較:
後一種狀況,不可能從Git歷史中看到哪些提交一塊兒實現了一個功能——你必須手工閱讀所有的日誌信息。若是對整個功能進行回退 (好比一組提交),後一種方式會是一種真正頭痛的問題,而使用--no-ffflag的狀況則很容易.
是的,它會建立一個新的(空)提交對象,可是收益遠大於開銷。
不幸的是,我還沒找到一種方法,讓--no-ff時做爲合併操做的默認選項,但它應該是可行的。
Release分支可能從develop分支分離而來,可是必定要合併到develop和master分支上,它的習慣命名方式爲:release-*。
Release分支是爲新產品的發佈作準備的。它容許咱們在最 後時刻作一些細小的修改。他們容許小bugs的修改和準備發佈元數據(版本號,開發時間等等)。當在Release分支完成這些全部工做之後,對於下一次 打的發佈,develop分支接收features會更加明確。
從develop分支建立新的Release分支的關鍵時刻是develop分支達到了發佈的理想狀態。至少全部此次要發佈的features必須在這個點及時合併到develop分支。對於全部將來準備發佈的features必須等到Release分支建立之後再合併。
在Release分支建立的時候要爲即將發行版本分配一個版本 號,一點都不早。直到那時,develop分支反映的變化都是爲了下一個發行版,可是在Release分支建立以前,下一個發行版到底叫0.3仍是1.0 是不明確的。這個決定是在Release分支建立時根據項目在版本號上的規則制定的。
Release分支是從develop分支建立的。例如,當前 產品的發行版本號爲1.1.5,同事咱們有一個大的版本即將發行。develop 分支已經爲下次發行作好了準備,咱們得決定下一個版本是1.2(而不是1.1.6或者2.0)。因此咱們將Release分支分離出來,給一個可以反映新 版本號的分支名。
1 $ git checkout -b release-1.2 develop 2 Switched to a new branch "release-1.2" 3 $ ./bump-version.sh 1.2 4 Files modified successfully, version bumped to 1.2. 5 $ git commit -a -m "Bumped version number to 1.2" 6 [release-1.2 74d9424] Bumped version number to 1.2 7 1 files changed, 1 insertions(+), 1 deletions(-)
建立新分支之後,切換到該分支,添加版本號。這裏,bump-version.sh 是一個虛構的shell腳本,它能夠複製一些文件來反映新的版本(這固然能夠手動改變--目的就是修改一些文件)。而後版本號被提交。
這個新分支可能會存在一段時間,直到該發行版到達它的預約目標。在此期間,bug的修復可能被提交到該分支上(而不是提交到develop分支上)。在這裏嚴格禁止增長大的新features。他們必須合併到develop分支上,而後等待下一次大的發行版。
當一個release分支準備好成爲一個真正的發行版的時候, 有一些工做必須完成。首先,release分支要合併到master上(由於每一次提交到master上的都是一個新定義的發行版,記住)。而後,提交到 master上必須打一個標籤,以便之後更加方便的引用這個歷史版本。最後,在release分支上的修改必須合併到develop分支上,以便將來發行 版也包含這些bugs的修復。
在Git中的前兩步是:
1 $ git checkout master 2 Switched to branch 'master' 3 $ git merge --no-ff release-1.2 4 Merge made by recursive. 5 (Summary of changes) 6 $ git tag -a 1.2
發行版如今已經完成,爲之後引用打上標籤。
編輯:你可能也想使用the-sor-u <key>flags來標記你的標籤。
爲了是修改保持在release分支上,咱們須要合併這些到develop分支上去,在Git上:
1 $ git checkout develop 2 Switched to branch 'develop' 3 $ git merge --no-ff release-1.2 4 Merge made by recursive. 5 (Summary of changes)
這個步驟可能會致使合併衝突(可能因爲改變版本號更是如此)。若是是這樣,修復它而後提交。
如今咱們真正的完成了,這個release分支將被刪除,由於咱們再也不須要它了。
1 $ git branch -d release-1.2 2 Deleted branch release-1.2 (was ff452fe).
能夠基於master分支,必須合併回develop和master分支。
分支名約定:hotfix-*
熱修復分支與發佈分支很類似,他們都爲新的生成環境發佈作準備,儘管這是未經計劃的。他們來自生產環境的處於異常狀態壓力。當生成環境驗證缺陷必須立刻修復是,熱修復分支能夠基於master分支上對應與線上版本的tag建立。
其本質是團隊成員(在develop分支上)的工做能夠繼續,而另外一我的準備生產環境的快速修復。
hotfix branch(修補bug分支)是從Master分支上面分出來的。例如,1.2版本是當前生產環境的版本而且有bug。可是開發分支(develop)變化還不穩定。咱們須要分出來一個修補bug分支(hotfix branch)來解決這種狀況。
1 $ git checkout -b hotfix-1.2.1 master 2 Switched to a new branch "hotfix-1.2.1" 3 $ ./bump-version.sh 1.2.1 4 Files modified successfully, version bumped to 1.2.1. 5 $ git commit -a -m "Bumped version number to 1.2.1" 6 [hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1 7 1 files changed, 1 insertions(+), 1 deletions(-)
分支關閉的時侯不要忘了更新版本號(bump the version)
而後,修復bug,一次提交或者屢次分開提交。
1 $ git commit -m "Fixed severe production problem" 2 [hotfix-1.2.1 abbe5d6] Fixed severe production problem 3 5 files changed, 32 insertions(+), 17 deletions(-)
完成一個bugfix以後,須要把butfix合併到master和develop分支去,這樣就能夠保證修復的這個bug也包含到下一個發行版中。這一點和完成release分支很類似。
首先,更新master並對release打上tag:
1 $ git checkout master 2 Switched to branch 'master' 3 $ git merge --no-ff hotfix-1.2.1 4 Merge made by recursive. 5 (Summary of changes) 6 $ git tag -a 1.2.1
編輯:你可能也會想使用 -sor-u <key>參數來對你的tag進行加密
下一步,把bugfix添加到develop分支中:
1 $ git checkout develop 2 Switched to branch 'develop' 3 $ git merge --no-ff hotfix-1.2.1 4 Merge made by recursive. 5 (Summary of changes)
規則的一個例外是: 若是一個release分支已經存在,那麼應該把hotfix合併到這個release分支,而不是合併到develop分支。當 release分支完成後, 將bugfix分支合併回release分支也會使得bugfix被合併到develop分支。(若是在develop分支的工做急需這個bugfix, 等不到release分支的完成,那你也能夠把bugfix合併到develop分支)
最後,刪除臨時分支:
1 $ git branch -d hotfix-1.2.1 2 Deleted branch hotfix-1.2.1 (was abbe5d6).
儘管這個分支模型沒有任何震撼的新東西, 文章開頭的圖表在咱們的項目中表現出驚人的實用性。它造成了一個優雅的思惟模型,易於領悟並使團隊成員發展出對分支和發佈過程的共同理解。
這裏提供一份高質量PDF格式圖表。去吧,把它掛載牆上以便能隨時快速參考。
更新: 若是有人須要: 這是主圖表的gitflow-model.src.key (Apple Keynote格式).