天天看點

Git協作流程

  Git 作為一個源碼管理系統,不可避免涉及到多人協作。

  協作必須有一個規範的流程,讓大家有效地合作,使得項目井井有條地發展下去。"協作流程"在英語裡,叫做"workflow"或者"flow",原意是水流,比喻項目像水流那樣,順暢、自然地向前流動,不會發生沖擊、對撞、甚至漩渦。

Git協作流程

  藍色可以叫主幹送出記錄線,紫色和綠色可以叫合并分支記錄線

經典 branch 工作模式

如下是一個不錯的代碼管理模式,詳細的可以參考 ​​​A successful Git branching model​​​,也可以檢視 ​​本地文檔​​ ,如下是一個簡單介紹。

Git協作流程

從上圖可以看到主要包含下面幾個分支,簡單介紹如下:

master: 主分支,用來版本釋出,通常

develop:日常開發分支,儲存了開發的最新代碼,用于每天的回歸測試;

feature:新的特性或者功能開發分支,隻與 develop 分支互動;

release:預釋出分支,在特性開發基本測試完成後,準備釋出前,用于釋出前最後測試;

hotfix:線上 bug 修複分支。

Main Branches

含兩個主分支 master 和 develop ,其中 origin/master 表示主分支, HEAD 始終是用于生産環境的代碼。而 origin/develop 表示主開發分支,其中的 HEAD 表示最新送出的代碼,這也就是每天用來編譯測試的代碼。

Git協作流程

當開發分支的代碼趨于穩定之後,且準備釋出時,隻需要打個釋出的版本号标簽 (tag) 即可。

是以,每次送出到主幹時,也就意味着這是一個新的生産版本的釋出,那麼可以通過一個 hook 自動編譯,生成生産環境的安裝包。

----- 建立一個倉庫,預設會有一個master分支
$ git init
$ echo 'Hello World!' > README
$ git add README
$ git commit -m 'Initial Commit'

----- 建立一個develop分支
$ git branch develop      

上述的 main 和 develop 都可以稱之為主幹分支。

除了主幹的分支外,還包括了一些其它分支,差別是這些分支有固定的生命周期,包括了 Feature Branches、Release Branches、Hotfix Branches 三種針對不同的場景,也對應了如何擷取分支以及如何合并分支。

如上介紹的三種分支也可以被稱為 Support Branches,接下來詳細介紹這三種分支。

Feature Branches

特性分支用來開發一個新的特性,這一分支從 develop 建立,而且最終會合并到 develop 分支;當然,也有可能最終取消掉,這取決于最終産品的決策。

Git協作流程

接下來看看如何使用。

----- 0. 建立新的特性分支
$ git checkout -b feature-foobar develop

----- 1. 執行一些操作,多次送出
$ echo "FOOBAR Feature 1" >> README
$ git commit -a -m "foobar feature 1"
$ echo "FOOBAR Feature 2" >> README
$ git commit -a -m "foobar feature 2"

----- 2.1 開發完成,接下來準備合并,先切換到develop分支
$ git checkout develop

----- 2.2 合并到develop主分支
$ git merge --no-ff feature-foobar

----- 2.3 删除原來的分支
$ git branch -d feature-foobar

----- 2.4 送出到遠端倉庫
$ git push origin develop      

另外,需要注意的是,上述合并到主分支的時候,采用的是 --no-ff 模式。

Git協作流程

該參數的作用是強行關閉 fast-forward 方式,該方式就是當條件允許的時候,git 直接把 HEAD 指針指向合并分支的頭,完成合并。不過如果删除分支,由于這個過程中沒有建立 commit,則會丢失分支資訊,使用該參數将保留分支 commit 曆史。

實際上,通過 git log 檢視時,使用 --on-ff 會強制添加 commit ,否則看不到合并的資訊。

另外,還有個參數 --squash,會把多次分支 commit 曆史壓縮為一次。

Release Branches

用于正式釋出前的最後測試,盡量減少 bug 數目,添加元資訊 (版本号以及釋出日期等)。該分支從 develop 建立,可以合并到 develop 或者 master 分支,其命名為 release-* 。

合并到 master 之後就可以用于釋出了,而之是以合并到 develop ,是為了将最新修改合并到新的開發分支。

----- 1. 建立分支,并更新檔案一些頭部資訊等,然後送出
$ git checkout -b release-1.2 develop
$ ./bump-version.sh 1.2
$ git commit -a -m "Bumped version number to 1.2"

----- 2. 測試沒有問題後,準備正式釋出
$ git checkout master
$ git merge --no-ff release-1.2
$ git tag -a 1.2

----- 3. 删除release分支
$ git checkout develop
$ git merge --no-ff release-1.2
$ git branch -d release-1.2      

Hotfix Branches

該分支由于修複線上 bug,當線上代碼出現 bug 時,從 master 開一個 hotfix 分支,修複 bug 之後再将 hotfix 分支合并到 master 分支并進行釋出,同時也需要合并到 develop 分支上去。

Git協作流程

可以發現,hotfix 和 release 分支功能類似,兩者好處是不會打斷 develop 分支正常功能開發。

----- 1. 從master擷取分支
$ git checkout -b hotfix-1.2.1 master
$ ./bump-version.sh 1.2.1
$ git commit -a -m "Bumped version number to 1.2.1"

----- 2. 然後修改代碼修複bug,并送出
$ git commit -m "Fixed severe production problem"

----- 3. bug修複後,将該分支合并到master
$ git checkout master
$ git merge --no-ff hotfix-1.2.1
$ git tag -a 1.2.1

----- 4. 然後将hotfix合并到develop分支
$ git checkout develop
$ git merge --no-ff hotfix-1.2.1

----- 5. 删除hotfix分支
$ git branch -d hotfix-1.2.1      

​​https://jin-yang.github.io/post/git-branch-model.html​​

本文介紹三種廣泛使用的協作流程:

  • Git flow
  • Github flow
  • Gitlab flow

  如果你對Git還不是很熟悉,可以先閱讀下面的文章。

  • ​​《Git 使用規範流程》​​
  • ​​《常用 Git 指令清單》​​
  • ​​《Git 遠端操作詳解》​​

  一、功能驅動

  本文的三種協作流程,有一個共同點:都采用​​"功能驅動式開發"​​(Feature-driven development,簡稱FDD)。

  它指的是,需求是開發的起點,先有需求再有功能分支(feature branch)或者更新檔分支(hotfix branch)。完成開發後,該分支就合并到主分支,然後被删除。

  二、Git flow

  最早誕生、并得到廣泛采用的一種協作流程,就是​​Git flow​​ 。

  2. 1 特點

  它最主要的特點有兩個。

Git協作流程

  首先,項目存在兩個長期分支。

  • 主分支​

    ​master​

  • 開發分支​

    ​develop​

  前者用于存放對外釋出的版本,任何時候在這個分支拿到的,都是穩定的釋出版;後者用于日常開發,存放最新的開發版。

  其次,項目存在三種短期分支。

  • 功能分支(feature branch)
  • 更新檔分支(hotfix branch)
  • 預發分支(release branch)

  一旦完成開發,它們就會被合并進​

​develop​

​​或​

​master​

​,然後被删除。

  Git flow 的詳細介紹,請閱讀我翻譯的中文版​​《Git 分支管理政策》​​。

  2. 2 評價

  Git flow的優點是清晰可控,缺點是相對複雜,需要同時維護兩個長期分支。大多數工具都将​

​master​

​​當作預設分支,可是開發是在​

​develop​

​分支進行的,這導緻經常要切換分支,非常煩人。

  更大問題在于,這個模式是基于"版本釋出"的,目标是一段時間以後産出一個新版本。但是,很多網站項目是"持續釋出",代碼一有變動,就部署一次。這時,​

​master​

​​分支和​

​develop​

​分支的差别不大,沒必要維護兩個長期分支。

  三、Github flow

  ​​Github flow​​ 是Git flow的簡化版,專門配合"持續釋出"。它是 Github.com 使用的協作流程。

  3. 1 流程

  它隻有一個長期分支,就是​

​master​

​,是以用起來非常簡單。

  官方推薦的​​流程​​如下。

Git協作流程
  第一步:根據需求,從​

​master​

​拉出新分支,不區分功能分支或更新檔分支。

  第二步:新分支開發完成後,或者需要讨論的時候,就向​

​master​

​​發起一個​​pull reqest​​(簡稱PR)。

  第三步:Pull Request既是一個通知,讓别人注意到你的請求,又是一種對話機制,大家一起評審和讨論你的代碼。對話過程中,你還可以不斷送出代碼。

  第四步:你的Pull Request被接受,合并進​

​master​

​,重新部署後,原來你拉出來的那個分支就被删除。(先部署再合并也可。)

  3. 2 評價

  Github flow 的最大優點就是簡單,對于"持續釋出"的産品,可以說是最合适的流程。

  問題在于它的假設:​

​master​

​​分支的更新與産品的釋出是一緻的。也就是說,​

​master​

​分支的最新代碼,預設就是目前的線上代碼。

  可是,有些時候并非如此,代碼合并進入​

​master​

​​分支,并不代表它就能立刻釋出。比如,蘋果商店的APP送出稽核以後,等一段時間才能上架。這時,如果還有新的代碼送出,​

​master​

​​分支就會與剛釋出的版本不一緻。另一個例子是,有些公司有釋出視窗,隻有指定時間才能釋出,這也會導緻線上版本落後于​

​master​

​分支。

  上面這種情況,隻有​

​master​

​​一個主分支就不夠用了。通常,你不得不在​

​master​

​​分支以外,另外建立一個​

​production​

​分支跟蹤線上版本。

  四、Gitlab flow

  ​​Gitlab flow​​ 是 Git flow 與 Github flow 的綜合。它吸取了兩者的優點,既有适應不同開發環境的彈性,又有單一主分支的簡單和便利。它是 Gitlab.com 推薦的做法。

  4. 1 上遊優先

  Gitlab flow 的最大原則叫做"上遊優先"(upsteam first),即隻存在一個主分支​

​master​

​,它是所有其他分支的"上遊"。隻有上遊分支采納的代碼變化,才能應用到其他分支。

  ​​Chromium項目​​就是一個例子,它明确規定,上遊分支依次為:

  1. Linus Torvalds的分支
  2. 子系統(比如netdev)的分支
  3. 裝置廠商(比如三星)的分支

  4. 2 持續釋出

  Gitlab flow 分成兩種情況,适應不同的開發流程。

Git協作流程

  對于"持續釋出"的項目,它建議在​

​master​

​​分支以外,再建立不同的環境分支。比如,"開發環境"的分支是​

​master​

​​,"預發環境"的分支是​

​pre-production​

​​,"生産環境"的分支是​

​production​

​。

  開發分支是預發分支的"上遊",預發分支又是生産分支的"上遊"。代碼的變化,必須由"上遊"向"下遊"發展。比如,生産環境出現了bug,這時就要建立一個功能分支,先把它合并到​

​master​

​​,确認沒有問題,再​

​cherry-pick​

​​到​

​pre-production​

​​,這一步也沒有問題,才進入​

​production​

​。

  隻有緊急情況,才允許跳過上遊,直接合并到下遊分支。

  4. 3 版本釋出

Git協作流程

  對于"版本釋出"的項目,建議的做法是每一個穩定版本,都要從​

​master​

​​分支拉出一個分支,比如​

​2-3-stable​

​​、​

​2-4-stable​

​等等。

  以後,隻有修補bug,才允許将代碼合并到這些分支,并且此時要更新小版本号。

  五、一些小技巧

  5. 1 Pull Request

Git協作流程

  功能分支合并進​

​master​

​分支,必須通過Pull Request(Gitlab裡面叫做 Merge Request)。

Git協作流程

  前面說過,Pull Request本質是一種對話機制,你可以在送出的時候,​

​@​

​​相關​​人員​​​或​​團隊​​,引起他們的注意。

  5. 2 Protected branch

  ​

​master​

​分支應該受到保護,不是每個人都可以修改這個分支,以及擁有審批 Pull Request 的權力。

  ​​Github​​​ 和 ​​Gitlab​​ 都提供"保護分支"(Protected branch)這個功能。

  5. 3 Issue

  Issue 用于 Bug追蹤和需求管理。建議先建立 Issue,再建立對應的功能分支。功能分支總是為了解決一個或多個 Issue。

  功能分支的名稱,可以與issue的名字保持一緻,并且以issue的編号起首,比如"15-require-a-password-to-change-it"。

Git協作流程

  開發完成後,在送出說明裡面,可以寫上"fixes #14"或者"closes #67"。Github規定,隻要commit message裡面有下面這些​​動詞​​ + 編号,就會關閉對應的issue。

  • close
  • closes
  • closed
  • fix
  • fixes
  • fixed
  • resolve
  • resolves
  • resolved

  這種方式還可以一次關閉多個issue,或者關閉其他代碼庫的issue,格式是 ​

​username/repository#issue_number​

​。

  Pull Request被接受以後,issue關閉,原始分支就應該删除。如果以後該issue重新打開,新分支可以複用原來的名字。

  5. 4 Merge節點

  Git有兩種合并:一種是"直進式合并"(fast forward),不生成單獨的合并節點;另一種是"非直進式合并"(none fast-forword),會生成單獨節點。

  前者不利于保持commit資訊的清晰,也不利于以後的復原,建議總是采用後者(即使用​

​--no-ff​

​參數)。隻要發生合并,就要有一個單獨的合并節點。

  5. 5 Squash 多個commit

  為了便于他人閱讀你的送出,也便于​

​cherry-pick​

​​或撤銷代碼變化,在發起Pull Request之前,應該把多個commit合并成一個。(前提是,該分支隻有你一個人開發,且沒有跟​

​master​

​合并過。)

Git協作流程

  這可以采用​

​rebase​

​​指令附帶的​

​squash​

​​操作,具體方法請參考我寫的​​《Git 使用規範流程》​​。