git revert 可以讓開發者快速把已經 merge 到 develop branch 的 feature revert 掉。比如在發生緊急 bug 又沒辦法立刻解決時,透過退掉該 feature 來達成 work around,此時 git revert 將會是你最好的工具!立刻來看看如何使用!
目錄
準備練習用 repository
為了方便練習,可以 clone 這個事先準備好的 repo 來測試
https://github.com/Jim-Chang/git-revert-demo
git clone https://github.com/Jim-Chang/git-revert-demo.git
repo 目前有兩個 branch,分別是 master 和 feat,用來模擬當 feature branch merge 到 master 後要 revert 的過程(一般流程會是 develop,這邊方便起見以 master 為代表)。
一開始在 repo 裡面新增一個檔案 demo.py 來做實驗,commit: add demo.py 如下
print('old 1')
print('old 2')
print('old 3')
print('old 4')
print('old 5')
print('old 6')
print('old 7')
後續在 master 上每一個 commit 會改一行 print,將字串 old 改成 master,如 master: commit 1
在 feat 上也是一樣方法,只是把 old 改成 feat 來區分,如 feat: commit 1
最後 demo.py 如下,可以用 print 中的文字來區別是在哪一個 branch 中做的修改或增加
print('master 1')
print('master 2')
print('feat 1')
print('feat 2')
print('feat 3')
print('old 6')
print('old 7')
print('master 3')
print('master 4')
如何 revert 掉某一個 commit?
先從簡單的做起,如果今天我們要退掉某一個在 master branch 的 commit 該如何做?
假設我們今天要退掉 master: commit 4 這個 commit,其修改是在最後面增加一行 print('master 4')
輸入以下指令做 revert
$ git revert 495a61c
[master a2e4445] Revert "master: commit 4"
1 file changed, 1 insertion(+), 2 deletions(-)
git 會自動幫你提交一個 commit,把 master: commit 4 中的修改 revert 掉
此時線圖長這樣
實驗完畢,我們讓 repo 回到 revert 前的狀態,使用 git reset 往前 reset 一個 commit
$ git reset --hard @~1
HEAD is now at 495a61c master: commit 4
如何 revert 掉 feature branch 的修改?
今天因為 feat 裡面修改的地方壞了,需要整個 revert 掉,該怎麼處理?
首先找到 feat merge 到 master 的 commit: Merge branch ‘feat’ 78dfcc5,並輸入以下指令做 revert
$ git revert 78dfcc5
error: commit 78dfcc5f25971a9a8de95203451ee6800f786858 is a merge but no -m option was given.
fatal: revert failed
發現報錯了!怎麼會這樣呢?剛剛做 revert 不是也是這麼做嗎?
原來我們這次 revert 的 commit 是 merge commit,他往前有兩個 parent,分別是在 master 上的 master: commit 3 和 feat 上的 feat: commit 3
git 不知道要用哪一個 commit 來和 merge commit 做 diff 因此報錯
此時只需要帶 -m
參數給 git,告訴他要用哪一個 parent commit 做 diff 即可。其中
-
-m 1
表示用 parent 1 (master branch) -m 2
表示用 parent 2 (feat branch)。
現在需求是 revert 掉 feat branch 的修改,因此使用 parent 1 (master branch) 為基準做 diff
$ git revert 78dfcc5 -m 1
Auto-merging demo.py
[master 748343c] Revert "Merge branch 'feat'"
1 file changed, 3 insertions(+), 3 deletions(-)
git 也自動幫你提交一個 commit,並把 feat branch 中做的修改都 revert 掉
線圖如下
實驗完畢,我們讓 repo 回到 revert 前的狀態,使用 git reset 往前 reset 一個 commit
$ git reset --hard @~1
HEAD is now at 495a61c master: commit 4
如果 revert 時對 feat branch 做 diff 會發生什麼事情?
剛剛是使用 -m 1
對 master branch 做 diff 做 revert,如果改用 -m 2
對 feat branch 做會發生什麼事情?
立刻來試試!
$ git revert 78dfcc55 -m 2
Auto-merging demo.py
CONFLICT (content): Merge conflict in demo.py
error: could not revert 78dfcc5... Merge branch 'feat'
回報發生 conflict!看一下目前 demo.py 檔案狀態
看起來衝突是在 master 上修改的最後幾行,為什麼呢?
我們把 merge commit 和 feat: commit 3 的修改抓出來比較一下
因為我們這次選的 parent 是 feat branch,從他的角度來看,master branch 在 merge commit 前增加了第八行 print('master 3')
,所以要把他 revert 掉,但沒想到 merge commit 後面又有 commit 在第八行接續增加了第九行 print('master 4')
,導致和 revert 衝突,只好報錯人工處理。
假設今天 merge commit 後面沒有 master: commit 4 的修改,是否還會有衝突呢?
實驗看看!先 hard reset 到 merge commit
$ git reset --hard 78dfcc5
HEAD is now at 78dfcc5 Merge branch 'feat'
此時線圖如下
緊接著再做 revert
$ git revert 78dfcc5 -m 2
[master 4a53b4d] Revert "Merge branch 'feat'"
1 file changed, 1 deletion(-)
果然安然無恙的 revert 了!revert 的修改如下,符合預期
線圖如下
最後重點複習
- 如果是一般 commit ( 1 parent) 時,使用
git revert <commit_id>
- 如果是 merge commit(2 parent),要帶
-m
參數,通常會帶 1 代表 merge 進去的 branch,使用指令git revert <commit_id> -m 1
如果覺得我文章內容對你有幫助的話,請在文章後面幫我按 5 個讚!讓我知道大家都喜歡什麼內容哦!
延伸閱讀:
git 綜合技, 坐時光機解決 CI pre-commit 報錯問題
被新創公司裁員後,我學到的五件事