前言
在團隊協作中,經常會遇到這種情境:你的功能分支已經 merge 到正式環境的 release branch,但上線前一天被通知要抽單退版。這時候如果直接在 release branch 上 git revert 那筆 merge commit,後續想把同一個功能分支重新 merge 回去時,Git 會認為「這些 commit 已經被 merge 過了」而跳過所有變更——導致程式碼根本沒有被帶回來。
本文記錄這個問題的成因,以及在不遺漏檔案的前提下,如何正確退版並繼續推進功能。
問題場景
假設流程如下:
- 你在
CC1-XXX功能分支上完成修改 - 將
CC1-XXXmerge 到正式環境前的 release branch(例如release/prod) - 上線前被通知要抽單,需要從
release/prod退掉你的變更 - 但功能本身還需要繼續修改,修完後要重新上線
如果此時直接在 release/prod 上 revert 你的 merge commit,之後再次從 CC1-XXX merge 回去,Git 會判定這些 commit 已經存在於歷史中,不會帶入任何變更。
為什麼會這樣?
Git 的 merge 判斷基於 commit history,而非檔案差異。當你 revert 一筆 merge commit 時:
- Revert 只是新增一筆「反向操作」的 commit,原本的 merge commit 仍然留在歷史中
- 下次再 merge 同一個分支時,Git 看到那些 commit 已經在歷史裡了,就不會再 merge 一次
- 結果就是:你以為 merge 成功了,但實際上什麼都沒帶進去
正確的退版與重新上線流程
Step 1:在 release branch 上 revert merge commit
先在 release/prod 上 revert 掉 CC1-XXX 的 merge commit,讓 release branch 回到乾淨的狀態。
1 | git checkout release/prod |
Step 2:將 release branch 同步回你的功能分支
把最新的 release/prod(包含 revert commit)sync 回你的 CC1-XXX 分支,確保兩邊歷史一致。
1 | git checkout CC1-XXX |
Step 3:在功能分支上 revert 那筆 revert
在 CC1-XXX 上,cherry-pick 或 revert 掉 Step 1 產生的 revert commit。這會讓你的變更重新出現在功能分支上。
1 | git revert <revert-commit-hash> |
Step 4:繼續修改並重新 merge
此時 CC1-XXX 上已經有了「revert of revert」,等於你的原始變更被還原回來了。繼續修改完畢後,正常 merge 回 release/prod 即可,Git 這次會正確帶入所有變更。
流程示意圖
重點整理
- 直接 revert merge commit 後,不能再次 merge 同一個分支——Git 會認為已經 merge 過而跳過
- 解法是「revert the revert」——在功能分支上反轉那筆退版 commit,讓變更重新生效
- 務必先將 release branch 同步回功能分支,確保歷史一致後再操作,避免遺漏檔案