從一點到另外一點,最快的方式就是直線過去。但有時視情況因為管理的需求,必須要迂迴。因此人們訂定並遵守規範,但對軟體開發來說,不對的開發流程,就只會造成不必要的時間和成本的浪費而已。
兩年多以前,寫了這篇 了解 Git Flow。在此之後到現在,我開發並維護了一個產品,在遵循 gitflow 來進行多人同時開發之後,發現 gitflow 其實存在不必要的步驟,會造成團隊運作產生額外的浪費。
這些問題在網路上很少被提到,至少 google 'gitflow' 不會出現相關的負面文章,但在將 the flaw of gitflow 餵狗之後,終於發現有人附議了我的想法,文章標題是:GitFlow considered harmful(gitflow 其實是有害的)。
接下來,我要開始指出 gitflow 會帶來的麻煩,以及背後的原因。
Branch 存在的意義
一個 repository 最重要的就是 master 分支,在 GitHub Flow Guide 中有提到
Branching is a core concept in Git, and the entire GitHub Flow is based upon it. There's only one rule: anything in the master branch is always deployable.
分支是 Git 中最核心的概念,整個 GitHub Flow 也是建立在這個觀念之上。這邊只有唯一一個原則:任何一個 master 分支上的 commit 都是可以被部屬的。
可以被部屬就表示功能完整(feature)以及沒有 bug(應該啦)。
因此如果該 repo 是個 library,就會有一個正式的版號;如果是 app,就會是真正給用戶來使用的版本。
接著
Because of this, it's extremely important that your new branch is created off of master when working on a feature or a fix.
所以當有新的功能或修正要作,記得一定要從 master 開新的分支出來
因此 gitflow 定義了一些分支命名的常規:
- hotfix: 已經出版的舊有功能需要修正
- feature: 要新增新的功能
- release: 集結下一個版本要的功能,在這裡修正 bug 後準備出版,進到 master
這樣看起來整個開發流程就相當完整了。那麼各位有沒有發現少了什麼?有一個分支沒有被提到,甚至是圖表中字體被加粗的分支
是 develop 分支!為什麼?這個開發者最常提到的分支竟然看起來不是必要的。大家有沒有想過為什麼要定義 develop 分支?
開發環境
理由很簡單,其實就是對應到相對的開發環境而已 — 在整合 CI (continuous integration) 之後,通常會定義 hook,在發現是 develop 分支的話會把程式碼部屬到測試環境。
所以反過來說,如果你開發的產品沒有環境因素的話,develop 分支根本是不必要的!!去看 React 的 repository,上面根本沒有 develop branch。
所以 gitflow 裡提到:「最重要的兩個分支會永久存在:master 以及 develop」是有待商榷的。
gitflow 中不必要的 flow
假設現在有開發環境的差別,是需要 develop branch 的。這樣和 gitflow 的原則就一樣了(gitflow 強調 develop 分支的必要性)。
而 gitflow 中,有 release 或 hotfix 的話,該 branch 是要分別 merge 進去到 develop 和 master 的。我這邊要問:為何要這樣作?有人會解釋說這樣看起來 master 比較乾淨,很乾脆的跟 develop 切開。
但這個分別 merge 的原則,有潛在的不必要問題,在解釋原因之前,我們先談一下這兩個分支。
細談 master 和 develop 分支
master 分支不會有異議,就是穩定版本的分支,而 develop 分支,因為它被命名為 「develop」,大家也很順理成章的認為:一切跟「開發」相關的行為都應建立在它之上。gitflow 中也強調了兩者的差異,以及彼此間的獨立性。
但如果仔細再看一次 gitflow 的圖表,你可以發現它卻背叛了上述的原則,那個地方指出了 master 和 develop 的橋樑。
這樣子是表示說 develop 從 master 產生的,我想這點這點大家都知道。而這條線是由下面這個 git 指令作出來的 —
# 在 develop branch
git merge master
也就是說從圖表來看 master 是會 merge 回 develop 的。既然如此,為何你還要從 hotfix / release 分支作一次同樣的動作呢?
另外一個問題是,gitflow 圖表要從它的最底部接回上面的 flow 是不行的,缺了一條 master 回去 develop 的線。如果今天把它加上去,就更能夠看出從 release 回去 develop 的多餘。
....
...
..
.
另外從這裡還可以發現一個開發產品時很常出現的謬誤:
很多人認為:因為環境的關係,master 不應該 merge 回去 develop,但上面提到 develop 是從 master 長出來的也就是說 master 應該要 merge 回 develop 的。
所以用環境來當作理由其實是本末倒置了,這跟前面解釋 develop 非必要的原因一樣,環境是幫助你開發的,並不能拿來當作訂定開發流程的藉口。
在 develop 分支上開發的問題
除了造成上面多餘的步驟之外,從 develop 建立分支有一個很大的問題,就是缺乏彈性,看一下下圖實色的部分:
這裡表示:F2
和 F1
是不同的 feature,因為如果是相關的,會是像 F0
的作法。
接著,F2
是從 A
也就是 develop 中的某個 commit 做出來的,也因此它擁有到 F1
為止的所有 commit。
因為這時候還沒有完成出版(release 還沒進到 master),在現實中很常發生:突然有需求下來說不要出版 F1
了,這時就要花功夫去處理 F2
和 develop。
處理的作法有很多種,可以 rebase、cherry-pick 或 revert 之後 merge,再加上 A
還會有 release 中的修正,我可以很肯定的告訴你,這件事會是 a pain in the ass。
從 develop 開分支出來作事就表示 — 這個分支的功能是建構在 develop 中已經有的功能之上,即使兩個功能完全沒有關係,因此這個作法造成分支間被建立起了完全不必要的相依性。
所以問題不在該怎麼處理這種情況,而是如何避免這種情況發生。
如何解決 gitflow 的問題
gitflow 最大的問題,就是強調了 develop 分支,所以整個 repo 出現兩條主線(master, develop),導致後續許多繁瑣的多餘步驟。
如果能把 develop 純粹視為對應到 CI 用的環境,而把整個開發重心移回 master,問題就會迎刃而解。
因此我比較推薦可以參考 GitHub Flow ,它原則比較單純,只強調一條建構在 master 上的主要線路 —
再看一次上面所引述的話:
Because of this, it's extremely important that your new branch is created off of master when working on a feature or a fix.
所以當有新的功能或修正要作,記得一定要從 master 開新的分支出來
小結
這邊總結一下 gitflow 的問題所在
- gitflow 將流程建立在 develop 之上是不必要的,也是所有問題的根源,應單純以 master 為主。
- gitflow 要求 hotfix 及 release 需要分別 merge 進去 master 及 develop。這個想法製造了相當多不必要的 merge 動作。這邊正確的原則應跟前一點一樣,全部透過 master 來同步比較簡潔。
- 從尚未出版的 develop 建立分支,有可能會造成在每次出版週期中應該各自獨立的分支間產生不必要的相依性。
另外
- develop 分支是由 master 長出來的,所以不要再有 master 不能 merge 進去 develop 這種想法。
在釐清 gitflow 帶來的麻煩,再加上實際參與開發產品之後,我思考出了一套個人認為比較有彈性及效率的作法,可以參考 git flow 實戰經驗談 part2。
感謝 Vincent Driessen 提出 gitflow,上面的圖表皆由連結中的 keynote 檔案修改而成。