【git revert】使用以及理解(詳解)

allanGold發表於2020-12-18

一、

假如git commit 鏈是

A -> B -> C -> D

如果想把B,C,D都給revert,除了一個一個revert之外,還可以使用range revert

git revert B^…D

這樣就把B,C,D都給revert了,變成:

A-> B ->C -> D -> D’-> C’ -> B’

用法就是:

git revert OLDER_COMMIT^…NEWER_COMMIT

如果我們想把這三個revert不自動生成三個新的commit,而是用一個commit完成,可以這樣:

git revert -n OLDER_COMMIT^…NEWER_COMMIT
git commit -m “revert OLDER_COMMIT to NEWER_COMMIT”

二、

git revert 撤銷 某次操作,此次操作之前和之後的commit和history都會保留,並且把這次撤銷,作為一次最新的提交。

  • git revert HEAD 撤銷前一次 commit
  • git revert HEAD^ 撤銷前前一次 commit
  • git revert commit (比如:fa042ce57ebbe5bb9c8db709f719cec2c58ee7ff)撤銷指定的版本,撤銷也會作為一次提交進行儲存。
    git revert是提交一個新的版本,將需要revert的版本的內容再反向修改回去,
    版本會遞增,不影響之前提交的內容

git revert 和 git reset的區別:

git revert是用一次新的commit來回滾之前的commit,git reset是直接刪除指定的commit。
1、在回滾這一操作上看,效果差不多。但是在日後繼續merge以前的老版本時有區別。因為git revert是用一次逆向的commit“中和”之前的提交,因此日後合併老的branch時,導致這部分改變不會再次出現,但是git reset是之間把某些commit在某個branch上刪除,因而和老的branch再次merge時,這些被回滾的commit應該還會被引入。
2、git reset 是把HEAD向後移動了一下,而git revert是HEAD繼續前進,只是新的commit的內容和要revert的內容正好相反,能夠抵消要被revert的內容。

revert理解

revert 可以取消指定的提交內容。

當討論 revert 時,需要分兩種情況,因為 commit 分為兩種:一種是常規的 commit,也就是使用 git commit 提交的 commit;另一種是 merge commit,在使用 git merge 合併兩個分支之後,你將會得到一個新的 merge commit

merge commit 和普通 commit 的不同之處在於 merge commit 包含兩個 parent commit,代表該 merge commit 是從哪兩個 commit 合併過來的。

clipboard.png

在上圖所示的紅框中有一個 merge commit,使用 git show 命令可以檢視 commit 的詳細資訊

➜  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> 即可,git 會生成一個新的 commit,將指定的 commit 內容從當前分支上撤除。

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 行裡面列出來的第一個還是第二個。

保留主分支,應該設定主分支為主線,操作如下:

➜ git revert -m 1 bd86846

img

revert 之後重新上線

假設tom在自己分支 tom/feature 上開發了一個功能,併合併到了 master 上,之後 master 上又提交了一個修改 h,這時提交歷史如下:

a -> b -> c -> f -- g -> h (master)
           \      /
            d -> e   (tom/feature)

突然,大家發現tom的分支存在嚴重的 bug,需要 revert 掉,於是大家把 g 這個 merge commit revert 掉了,記為 G,如下:

a -> b -> c -> f -- g -> h -> G (master)
           \      /
            d -> e   (tom/feature)

然後tom回到自己的分支進行 bugfix,修好之後想重新合併到 master,直覺上只需要再 merge 到 master 即可,像這樣:

a -> b -> c -> f -- g -> h -> G -> i (master)
           \      /               /
            d -> e -> j -> k ----    (tom/feature)

i 是新的 merge commit。但需要注意的是,這 不能 得到我們期望的結果。因為 d 和 e 兩個提交曾經被丟棄過,如此合併到 master 的程式碼,並不會重新包含 d 和 e 兩個提交的內容,相當於只有 goudan/a-cool-feature 上的新 commit 被合併了進來,而 goudan/a-cool-feature 分支之前的內容,依然是被 revert 掉了。

所以,如果想恢復整個 tom/feature 所做的修改,應該先把 G revert 掉:

a -> b -> c -> f -- g -> h -> G -> G' -> i (master)
           \      /                     /
            d -> e -> j -> k ----------    (tom/feature)

其中 G’ 是對 G 的 revert 操作生成的 commit,把之前撤銷合併時丟棄的程式碼恢復了回來,然後再把tom的分支merge到master分支上,把解決 bug 寫的新程式碼合併到 master 分支。

參考

https://stackoverflow.com/questions/9059335/get-parents-of-a-merge-commit-in-git

相關文章