Git 之 revert
revert 可以取消指定的提交內容。
當討論 revert 時,需要分兩種情況,因為 commit 分為兩種:一種是常規的 commit,也就是使用 git commit
提交的 commit;另一種是 merge commit,在使用 git merge
合併兩個分支之後,你將會得到一個新的 merge commit
merge commit 和普通 commit 的不同之處在於 merge commit 包含兩個 parent commit,代表該 merge commit 是從哪兩個 commit 合併過來的。
在上圖所示的紅框中有一個 merge commit,使用 git show
➜ git show bd86846
commit bd868465569400a6b9408050643e5949e8f2b8f5
Merge: ba25a9d 1c7036f
這代表該 merge commit 是從 ba25a9d 和 1c7036f 兩個 commit 合併過來的。
而常規的 commit 則沒有 Merge 行
➜ git show 3e853bd
commit 3e853bdcb2d8ce45be87d4f902c0ff6ad00f240a
revert 常規 commit
使用 git revert <commit id>
revert merge commit
revert merge commit 有一些不同,這時需要新增 -m
選項以代表這次 revert 的是一個 merge commit
但如果直接使用 git revert <commit id>
,git 也不知道到底要撤除哪一條分支上的內容,這時需要指定一個 parent number 標識出"主線",主線的內容將會保留,而另一條分支的內容將被 revert。
如上面的例子中,從 git show 命令的結果中可以看到,merge commit 的 parent 分別為 ba25a9d 和 1c7036f,其中 ba25a9d 代表 master 分支(從圖中可以看出),1c7036f 代表 will-be-revert 分支。需要注意的是 -m 選項接收的引數是一個數字,數字取值為 1 和 2,也就是 Merge 行裡面列出來的第一個還是第二個。
我們要 revert will-be-revert 分支上的內容,即 保留主分支,應該設定主分支為主線,操作如下:
➜ git revert -m 1 bd86846
revert 之後重新上線
假設狗蛋在自己分支 goudan/a-cool-feature 上開發了一個功能,併合併到了 master 上,之後 master 上又提交了一個修改 h,這時提交歷史如下:
a -> b -> c -> f -- g -> h (master)
\ /
d -> e (goudan/a-cool-feature)
突然,大家發現狗蛋的分支存在嚴重的 bug,需要 revert 掉,於是大家把 g 這個 merge commit revert 掉了,記為 G,如下:
a -> b -> c -> f -- g -> h -> G (master)
\ /
d -> e (goudan/a-cool-feature)
然後狗蛋回到自己的分支進行 bugfix,修好之後想重新合併到 master,直覺上只需要再 merge 到 master 即可(或者使用 cherry-pick),像這樣:
a -> b -> c -> f -- g -> h -> G -> i (master)
\ / /
d -> e -> j -> k ---- (goudan/a-cool-feature)
i 是新的 merge commit。但需要注意的是,這 不能 得到我們期望的結果。因為 d 和 e 兩個提交曾經被丟棄過,如此合併到 master 的程式碼,並不會重新包含 d 和 e 兩個提交的內容,相當於只有 goudan/a-cool-feature 上的新 commit 被合併了進來,而 goudan/a-cool-feature 分支之前的內容,依然是被 revert 掉了。
所以,如果想恢復整個 goudan/a-cool-feature 所做的修改,應該先把 G revert 掉:
a -> b -> c -> f -- g -> h -> G -> G' -> i (master)
\ / /
d -> e -> j -> k ---------- (goudan/a-cool-feature)
其中 G' 是對 G 的 revert 操作生成的 commit,把之前撤銷合併時丟棄的程式碼恢復了回來,然後再 merge 狗蛋的分支,把解決 bug 寫的新程式碼合