(2017/12/22 更新)如果你第一次看到這篇文章,你可以繼續看,但之後可以再看一下這一篇,裡面說明了一些 git flow 不好的地方。
(2018/1/31 更新)如果你想要找的是一個好的 git 開發流程,推薦你可以直接看這篇
gitflow這張圖相信用 git 的人都會看過。

當初剛接觸 git 的時候看到這張,只覺得版本控管本來就該這樣作嘛,好像也沒什麼大不了的。真的開始多人開發,加上要出版本的時候,就開始發生問題。最明顯的就是:啊怎麼我的 git source tree 長得這麼亂...如圖這樣簡潔的線條是到底是怎麼長得...。

在釐清比三千煩惱絲還亂的原始碼樹狀歷史之後,有了一些小心得。其實寶藏就在那裡了,只是你沒發現而已。上面這張圖,有一些細節是需要被提出來的。

首先最重要的只有兩個分支會永久存在:master 以及 develop,也就是圖上粗體字的那兩條。其餘的支援型分支在完成任務後應刪除,包括 feature,release,hotfix。

feature 分支:當準備開發新的功能時會建立,一定是從develop 分支拉出來。命名的原則為 前綴feature + / +功能名,i.e

git checkout -b feature/myFeature develop

feature前綴代表是一個 feature,myFeature題點 feature 的內容。

Feature 分支

feature 分支只會被 merge 回 develop 分支。

git checkout develop
git merge --no-ff feature/myFeature

--no-ff 照字面上的意思來看,就表示不要作 fast-forward 的動作。fast-forward 的意思是,當被 merge 對象(此例是 feature/myFeature)完全是目前分支的後代時,merge 完後會看不出來有 merge 過,直接變成一條線。

使用--no-ff這個設定的話,即使 feature 和 develop 分支沒有任何 conflict,可以直接被 merge 回去,仍然會產生一個 commit 來紀錄。commit 標題會是:Merge feature/myFeature into develop

這個 commit 的好處是可以清楚的看出有哪些 feature 分支被開發及 merge 過,不知道為何 git 沒有把這個行為設為預設動作
(2015/5/24更新,使用一個月之後,了解到 --no-ff 沒有設為預設是因為 fast forward 在免錢開 branch 的 git 是很方便的,有時候,你就是會覺得沒有必要顯示 merge branch 的訊息)

上面步驟其實等同於你發一個 Merge Request。

git push origin feature/myFeature
// 發出 Merge Request

merge / merge request 完記得刪除 feature 分支

git branch -d feature/myFeature

好,現在會有幾個問題。

  • 狀況一:這個 feature 會有很多人共同開發該怎麼辦?

首先,這個分支會被推上 remote,基本上只要是在 remote 存在的分支,都不可以被任意複寫,也就是不要使用 --force這個指令。

假設現在有兩位開發者,John 和 Doe,John 會從 myFeature 分支開一個名為 feature/myFeature/john 的分支,同樣道理,Doe 的則命名為 feature/myFeature/doe

而這兩個分支只會 merge 回該 feature 分支

git checkout feature/myFeature
git merge --no-ff feature/myFeature/john

一樣記得刪除

git branch -d feature/myFeature/john
  • 狀況二:遠端的 feature 分支有新的 commit

這個情況就需要用到 rebase,執行

git pull --rebase origin feature/myFeature

rebase 的邏輯是,把對象分支可以 fast forward 的 commits 都先 merge 進目前分支,再把目前分支有的 commit 一個一個加回去。
如果是開發同一個 feature,一般來說都會遇到 conflict(因為很可能會改動同一行的東西)。

rebase 的好處是,你可以清楚看到本地每個 commit 會遇到 conflict 的地方,相較於用 merge:一次解掉本地所有 commits 的 conflict,比較不容易出錯。

如果有先把 myFeature 分支拉回本地,下列指令等同 pull --rebase

git rebase feature/myFeature

Release 分支

當新的功能開發的差不多之後,可以開一出一個 release 分支。這條分支只會修現在功能的 bug,而不應該有新功能的 commit。

git checkout -b release/1.0.0 develop

Release 分支可以 merge 回 develop,也可以 merge develop 分支的新 commit 進來。等到版本內容確定無誤後,最後 merge 進 master,並發版本。

git checkout master
git merge release/1.0.0
git tag -a v1.0.0

在 merge 回 develop 分支後刪除 release 分支

git checkout develop
git merge release/1.0.0
git branch -d release/1.0.0

Hotfix 分支

當出版的版本如果有緊急的 bug 需要修復,就會用到 hotfix 分支。它必須從出版本的分支拉出來

// at master branch
git checkout -b hotfix/1.0.1

該 hotfix 分支在 merge 進 master 後,記得也 merge 回 develop 分支。

git checkout master
git merge hotfix/1.0.1
git checkout develop
git merge hotfix/1.0.1
git branch -d hotfix/1.0.1

延伸問題

環境設定

在開發 client 常常會遇到這個情況:在 develop 和 master 分支會是不同的設定(例如 server 的 host name),這樣會導致 master 分支無法 merge 回 develop。這樣子該怎麼實作 gitflow 呢?方法有二:

  • 從 task manager(e.g grunt, gulp)設定相對應環境的設定值。以 angular 的 web app 舉例,可以用 grunt-ng-constant 來產生一個 config module 在你的 web app module 引入。

  • 有時候 deploy 的流程會不允許在不同環境執行不同的 task,原因是預期同個 code base compile 出來的 code 應該要一樣,不應該隨著環境改變。這種情況,你就必須把環境設定拉出現有的 code base,做成一個單純的 json 檔案,再由你的 web app 導入。