書到用時方恨少,前幾天遇到程式換版,想將 master 累積的幾次 Commit 內容合併到 release 分支準備上線。因觀念薄弱經驗不足技巧生疏,一時手忙腳亂。

痛定思痛,特別回家再演練一次 Git 合併分支上線情境,筆記備忘。

準備版本歷程實驗環境如下:

  • Commit 1 (Init) - 建立 ver.txt,C1.txt,此時從 master 建立 release 分支,模擬首次上線的 1.0 版
  • Commit 2 - 開發人員持續開發,修改 ver.txt,新增 C2.txt
  • Commit 3 - 開發人員持續開發,修改 ver.txt,新增 C3.txt
  • Commit 4 - 開發人員持續開發,修改 ver.txt,新增 C4.txt,master 分支最新進度在此

Commit 4 時想安排一次上線,故需將 master 累積的修改內容合併到 release 分支。做法是先 git checkout release 切換到 release 分支,再下指令或操作 GUI 將 master 合併至 release,常見的合併做法有三種:

簡單合併

git merge master

release 從 master 分支出來後並未提交其他新內容,故最快的合併方式是直接將 release 指向 master 的最新位置(術語叫 Fast-Forward 快轉),二者一起指向 Commit 4 就算完成合併。

這麼做有個小缺點是線圖上不會有明顯合併軌跡,講到合併,大家通常會期待在線圖看到「兩條平行線,在某個時間點出現交集」(依龍哥的術語叫小耳朵),但採用快轉合併時 master 與 release 在線圖的同一條線上,master 在前,release 在後追趕,每次合併就是 release 追上 master 的時候,再隨即被甩開。採行這種做法時,通常需要另外放個 Tag 以標注每次合併上線的位置。

--no-ff 非快轉式合併(做出小耳朵)

git merge master --no-ff

加上 --no-ff 參數會迫使 Git 產生一個合併專用的 Commit,如下圖,線圖可看到 master 與 release 兩條線,release 多了一個合併專用 Commit [Merge branch 'master' into release], 此一 Commit 來自於 master 的 Commit 4:

合併 Commit 的訊息"Merge branch 'master' into release" 是系統自動產生的,若要自行指定,可用-m "合併Commit的說明文字"設定,或加上--no-commit,請 Git 先不要提交 Commit, 允許檢視修改 Commit 內容後再自行手動提交,提交時再想一個貼切的名稱。

合併後檔案的完整修改歷程都在,以 ver.txt 為例,從 release 分支可以追溯回 Init, C2, C3, C4 四次修改:

--squash 壓縮式合併

git merge master --squash

會將 C2、C3、C4 的三次更改融合成一大包整批放進 release,由於檔案被重新混合打包過,要輸入訊息手動 Commit。 而從線圖來看,relese 上的合併 Commit 點並沒有線連回 master 的 Commit 4,master 與 release 是兩條沒有交集的線:

squash 合併會切斷檔案的詳細修改歷史,可以想像成異動檔案是透過 Email 或 USB 行動碟複製過來的沒兩樣,只看得出它們與 releaes 前一次版本的差異,至於中間歷經過幾次修改,無從得知。

以 ver.txt 為例,squash 合併後,從 release 查看 ver.txt,看不到 C2, C3, C4 的修改記錄,只能看到它在 Init 建立後,從 Release V1.1 Commit 時的樣子被改成與 master C4 時的內容。

由於 release 與 master 的歷史失去關聯性,日後合併時自動解決衝突常會失效,需要人工排除。但優點是 release 的歷史歷程很清楚單純,只忠實反應每次上版時的檔案異動,不會摻差開發期間改了又改、 加上又拿掉再加回去的雜亂軌跡。

Visual Stuiod 合併操作

觀察完 Git 合併行為,來看看在 Visual Studio 應如何操作。

因為是要將 master 合併到 release,記得要先將目前的分支切換到 release (可觀察下圖黃框處,忘記怎麼切換分支的同學可參考 Visual Studio Git 筆記 - Git 的 Branch 魔法),然後在 master 上按右鍵選「Merge From...」:

如果你接受快轉式合併,沒有刻意要產生合併專用 Commit,可勾選下圖中的「Commit changes after merging」:

其合併效果跟簡單合併相同:

另一種做法是不要勾選「Commit changes after merging」,合併後不自動 Commit:

此時需切到 Changes 輸入訊息按下「Commit Staged」將結果送入版控:

這種做法會產生一個代表合併動作的 Commit,而其與 master 間有連線,以 ver.txt 為例可以追溯 C4、C3、C2 修改,由此判斷 Visual Studio 採用的做法相當於--no-ff --no-commit(非快轉式合併加自行手動提交)。

經過評估,我希望有合併小耳朵清楚對應每次上線合併動作,合併操作要力求簡單(同事還是習慣 GUI,聽到敲指令會怕 XD),不介意從 release 分支看到程式的詳細修改歷程,未來併進 release 的操作應會使用 Visual Studio 的合併功能,但不要勾選「Commit changes after merging」以產生自訂訊息文字的非快轉式合併。

[2019-07-07 補充] 有不少朋友提到 Git Flow,在此補充說明。先前做過評估,Git Flow 對我來說太複雜太沈重了,One Flow 比較接近我理想的做法。我們採行的策略是以 master 做為開發主軸,重大修改時另開 feature 分支做完再併回來(之後 feature 分支刪除),另外再開 release 分支對應線上版。

Explaining the difference between git merge, --no-ff and --squash and how to merge with VS2019.


Comments

# by max533

這是依照gitflow去開發嗎? 如果是的話,release不是應該是要從develop開分支,而不是從master吧?

# by Jeffrey

to max533, Git Flow 對我來說太複雜太沈重了,One Flow 比較接近我的理想做法。參考: https://blog.darkthread.net/blog/git-branching-strategies/

# by Harry Hung

我在 funds 分公司的折衷做法是分成 origin (AP可讀寫) 和 origin_production (AP唯讀) 兩個 repository。 AP 依提案單號開 branch,要上線前自行先 rebase master。然後再切換到 master 把要上線的 branch 單純的 merge 進來。 SP 依上線單執行自動化 push 到 production 與編譯過版。

# by Jeffrey

to Harry Hung, 感謝寶貴經驗分享

Post a comment