Git的revert命令

git的revert命令用来取消某次特定的提交内容。

当我们使用revert命令时,需要考虑两种情况。因为commit就分两种类型:一种是常规的commit,也就是使用git commit命令得到的commit;另一种是merge commit,在使用git merge合并两个分支后,我们会得到一个新的merge commit。

merge commit和普通commit的不同之处在于,merge commit包含两个parent commit,代表merge commit是从哪两个commit合并而来。

Snipaste_2024-04-07_21-58-15

在上图所示的红框中有一个 merge commit,使用 git show 命令可以查看 commit 的详细信息

1
2
3
$ git show 422ecbc
commit 422ecbc384c9e091a4990789f890661055ae1c9e (HEAD -> master)
Merge: 26407b4 3d0cd1e

这代表该 merge commit 是从 26407b4和 3d0cd1e两个 commit 合并过来的。

而常规的commit则没有“Merge”行

1
2
$ git show b086f02
commit b086f02120d257965a0d340d5442ad83df4c591f

revert常规commit

使用git revert <commit id>即可,git 会生成一个新的 commit,将指定的 commit 内容从当前分支上撤销掉。

revert merge commit

revert merge commit 有一些不同,这时需要添加 -m 选项,用来表示这次 revert 的是一个merge commit

但如果直接使用 git revert,git 也不知道到底要撤除哪一条分支上的内容,这时需要指定一个 parent number 标识出”主线”,主线的内容将会保留,而另一条分支的内容将被 revert。

如上面的例子中,从 git show 命令的结果中可以看到,merge commit 的 parent 分别为 26407b4和 3d0cd1e,其中“26407b4”代表 master 分支(从图中可以看出),“3d0cd1e”代表DEV 分支。需要注意的是 -m 选项接收的参数是一个数字,数字取值为 1 和 2,也就是 Merge 行里面列出来的第一个还是第二个。

我们要 revert DEV 分支上的内容,即 保留主分支,应该设置主分支为主线,操作如下:

1
git revert -m 1 422ecbc

revert之后重新上线

假设我们在自己的分支 david/a-cool-feature 上开发了一个功能,并合并到了 master 上,之后 master 上又提交了一个修改 h,这时提交历史如下:

1
2
3
a -> b -> c -> f -- g -> h (master)
\ /
d -> e (david/a-cool-feature)

突然,大家发现David的分支存在严重的 bug,需要 revert 掉,于是大家把 g 这个 merge commit revert 掉了,记为 G,如下:

1
2
3
a -> b -> c -> f -- g -> h -> G (master)
\ /
d -> e (david/a-cool-feature)

然后David回到自己的分支进行 bugfix,修好之后想重新合并到 master,直觉上只需要再 merge 到 master 即可,像这样:

1
2
3
a -> b -> c -> f -- g -> h -> G -> i (master)
\ / /
d -> e -> j -> k ---- (david/a-cool-feature)

i 是新的 merge commit。但需要注意的是,这 不能 得到我们期望的结果。因为现在的merge是基于G和k进行merge,又因为“d”和“e”两个提交曾经被“G”丢弃过,所以如此合并到 master 的代码,并不会重新包含“d”和“e”两个提交的内容,相当于只有 david/a-cool-feature 上的新 commit “j”和“k”被合并了进来,而 david/a-cool-feature 分支之前的内容,依然是被 revert 掉了。

所以,如果想恢复整个 david/a-cool-feature 所做的修改,应该先把“G” revert 掉:

1
2
3
a -> b -> c -> f -- g -> h -> G -> G' -> i (master)
\ / /
d -> e -> j -> k ---------- (david/a-cool-feature)

其中 G’ 是对 G 的 revert 操作生成的 commit,把之前撤销合并时丢弃的代码恢复了回来,然后再 merge david的分支,把解决 bug 写的新代码合并到 master 分支。