在git中撤销一系列提交

196

如何在Git中还原一系列提交?从查看gitrevisions文档来看,我无法看到如何指定我需要的范围。例如:

A -> B -> C -> D -> E -> HEAD

我想要做类似于这样的事情:

git revert B-D

结果将会是:

A -> B -> C -> D -> E -> F -> HEAD

F中包含B到D(包括B和D)的反转。


在gitrevisions(7)页面的末尾,有一个标题为“指定范围”的部分。您想要的与那里描述的有什么不同? - Gareth McCaughan
3
gitrevisions页面建议使用“git revert A..D”来实现我想要的功能。但是,当我尝试时,出现了“fatal: Cannot find 'A..D'”错误。 - Alex Spurling
3
11年过�了,但我�想说这是一个�常清晰和��得当的问题。💪 - Alan
参见这个概念的一种相反推论:如何挑选多个提交 - Gabriel Staples
4个回答

250

您正在使用哪个版本的Git?

只有在Git1.7.2+中才支持撤销多个提交: 更多详细信息请参见Rollback to an old commit using revert multiple times.
当前的git revert手册页仅适用于当前的Git版本(1.7.4+)。


正如OP Alex Spurling在评论中所述:

升级到1.7.4后就可以正常工作。
回答我自己的问题,这是我正在寻找的语法:

git revert B^..D 
B^表示“B的第一个父提交”,这样可以将B包括在还原中。
请查看“git rev-parse SPECIFYING REVISIONS section”中包含的<rev>^,例如 HEAD^语法:更多信息请参见“What does the caret (^) character mean?”。请注意,每个还原提交都是单独提交的。在评论中Henrik N进行了澄清。
git revert OLDER_COMMIT^..NEWER_COMMIT

如下所示,您可以在不立即提交的情况下还原:

git revert -n OLDER_COMMIT^..NEWER_COMMIT
git commit -m "revert OLDER_COMMIT to NEWER_COMMIT"

2
谢谢,那就是答案。升级到1.7.4没有问题。为了回答自己的问题,这就是我要找的语法:git revert B^..D - Alex Spurling
17
我经常参考这个答案,但总是需要一段时间才能弄清楚顺序。因此为了帮助未来的自己:git revert OLDER_COMMIT^..NEWER_COMMIT - Henrik N
2
"^" 的意思是什么? - Dustin Getz
1
@DustinGetz 的第一个父级:请参阅 http://git-scm.com/docs/gitrevisions:“修订参数后缀 ^ 表示该提交对象的第一个父级”。 - VonC
1
@GabrielStaples 确实:git revertgit cherry-pick中的<commit>使用git revisions - VonC
显示剩余12条评论

34

如何使用一个撤销提交来还原一系列的提交

如果你想在一个单独的提交中撤销提交范围从 B 到 D(至少在 git 版本 2 中),你可以执行以下操作:

git revert -n B^..D
git commit -m "revert the commit range B to D, inclusive"
-n(或--no-commit)参数告诉git从B的父提交(不包括)开始撤销更改,直到D提交(包括),但是不会创建任何包含撤销更改的提交。撤销只会修改工作树(您的活动文件系统)和索引(您的暂存文件部分)。
在运行git revert -n后不要忘记提交更改:
git commit -m "revert the commit range B to D, inclusive"

你还可以使用相同的方法在一个提交中撤销多个无关的提交。例如:要撤销 B 和 D,但不撤销 C:
git revert -n B D
git commit -m "Revert commits B and D"

参考资料:

  1. https://www.kernel.org/pub/software/scm/git/docs/git-revert.html

  2. 感谢 Honza Haering 提供的 更正

  3. 来自 man git revert

    -n--no-commit

    通常,此命令会自动创建一些提交,并在提交日志中声明已还原哪些提交。此标志将所需的更改应用于您的工作树和索引以还原指定的提交,但不进行提交。此外,在使用此选项时,您的索引无需与 HEAD 提交匹配。还原是针对索引的初始状态进行的。

    当连续还原多个提交的影响到您的索引时,这很有用。


8
git revert -n B..D 只撤销了 C 和 D 提交,而没有撤销 B 提交。 git revert -n B^..D 撤销了 B、C 和 D 提交。 - Honza Haering
根据 Git 文档的说明,它确实可以。帖子中有参考。 - Ramast
2
如果您参考了参考文献中这个有点令人困惑的例子:git revert -n master~5..master~2,它表示包括最近的第五次提交。但是,master~5实际上是第六次最新的提交。请参阅Git文档中的修订选择以获取有关..符号的详细信息 :-) - Honza Haering
如果您计划重新应用这些提交,通常情况下这是一个不好的想法。 - PatKilg
@PatKilg,我不明白如何操作。如果你愿意,在执行这个合并还原提交之后,你仍然可以逐个地重新应用每个提交,以单独的提交方式进行重新应用。 - Gabriel Staples
显示剩余2条评论

13

git revert OLDER_COMMIT^..NEWER_COMMIT 对我没有起作用。

我使用了 git revert -n OLDER_COMMIT^..NEWER_COMMIT,一切顺利。我使用的是 git 版本1.7.9.6


我曾经遇到过相同的问题,并使用“-n”来解决它,但你应该在 OLDER_COMMIT 后面保留 ^(即 git revert -n OLDER_COMMIT^..NEWER_COMMIT)。 - FeelGood
@FeelGood 为什么你应该离开 ^? - Orlando
我有这样的git历史记录 A -> B -> C,我的目标是还原B和C。当我使用'git revert -n B..C'命令时,只有C被还原了。但是,当我使用'git revert -n B^..C'命令时,git把这两个提交都还原了。也许我做错了什么。 - FeelGood
很酷,我们需要测试一下,但我认为在我的情况下效果不错(除非我正在还原一个1个提交范围的版本哈哈),我会修改答案以包括^。 - Orlando
3
-n--no-commit 选项将在一个提交中还原范围内的所有更改,而不是为范围内的每个提交创建还原提交。最终结果相同,也就是说,相同的更改将被还原。只是取决于您想要 git 历史记录看起来如何。 - Dennis
1
@FeelGood,范围B..D表示从D可达但从B不可达的提交。这就是为什么B不包括在内的原因——因为B可以从自身到达。使用B^..D表示从D可达但从B的父级不可达的提交,因此B也被包括在内。 - MawrCoffeePls

-2
使用 git rebase -i 将相关提交合并为一个。然后您只需撤销一个提交即可。

8
如果使用git rebase,您可以轻松删除提交记录。我认为不进行rebase的原因是想要保持提交记录F的SHA1值不变。 - Paŭlo Ebermann
7
可以将撤销的提交合并为一个提交。 - aeosynth
我猜你可以将它们压缩到一个单独的分支上,撤销该提交,并将撤销提交挑选到原始分支上。 - herman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接