如何将多个提交作为单个提交的一部分还原

78

这不是一个严重的问题,只是我想知道是否可能。

假设我们有两个提交记录abcd123wxyz789,它们在存储库历史记录中非相邻、独立的位置。现在我们想要撤销它们。执行以下操作:

git revert abcd123 wxyz789

如果我们要在两个提交中修复的错误在逻辑上是相关的,并且为了自我记录的目的,我们想要创建一个单一提交,其中包含一个单一的“我弄坏了某些东西,所以现在我正在还原文件x、y和z”的注释。是否有一个git命令可以做到这一点?

(当然,我知道可以创建一个提交,在提交中手动修复所有更改,然后推送。这对于所有明显的原因都是痛苦的。)

3个回答

97

你可以这样做:

git revert abcd123
git revert --no-commit wxyz789
git commit --amend

...然后编写一个适当的提交消息,描述撤销这两个提交所产生的综合影响。


49
更简洁的写法: git revert abcd123 wxyz789 --no-commit git commit - rmp251
2
更详细的答案在这里:https://dev59.com/8HM_5IYBdhLWcg3wPAjU。基本上,将“--no-commit”标志添加到您想要还原的所有提交中,然后提交结果并为整个批次添加消息。 - Todd

46

如果涉及彼此更改的复杂还原,使用 revert --no-commit 可能会有问题。

我的简单解决方案是执行真正的还原,并进行压缩:

git revert <all commits>
git rebase -i

然后将所有的回滚标记为squash,除了第一个以外,以创建一个单一的提交。


4
对于 Git 学习者:
  1. 了解一些提交(commit)的概念;
  2. 熟练掌握交互式变基(interactive rebase):合并(squash)、移除(remove)、编辑(edit)、变基(rebase)、重新排序(commits)。一旦你熟悉了它(提示:这就像是时光旅行),这个解决方案就比使用 --no-commit 这类东西更加自然。
(不要害怕“不要对已发布内容进行变基”的规则——只要在本地或私人分支上操作,你可以做任何事情。)
- Alois Mahdal
1
额外提示:第一个可以从 pick 更改为 reword(用于输入新的提交消息)或 edit(用于暂存更改而不进行提交)。对于其他还原操作,如果您不关心提交消息,可以使用 fixup 替代 squash - ADTC
@AloisMahdal 我实际上尝试了将已经推送到远程仓库的提交回滚并重新基于,结果很好。它没有修改提交历史记录,只是创建了一个新的单一提交。我想知道为什么他们说不要对已经推送到远程仓库的提交进行交互式变基,或者它会如何修改git历史记录。你有任何想法吗? - KarenAnne
@KarenAnne,这与您在rebase中要更改的内容有关。在我的例子(git revert + git rebase),您只需创建一些本地提交并将它们压缩为单个本地提交。 但是,如果您尝试重新定位现有提交,它将更改历史记录。 例如,请尝试运行git rebase -i origin/master~2并尝试更改已合并的提交。 - ElyashivLavi

25
git revert -n <commits>
git commit

第一个命令将执行所有还原操作,而不会创建任何提交,并且会将最终结果暂存(使用-n选项)。之后,commit命令将创建单个提交。


1
完美。简洁明了。而且它允许您在所有还原之后添加提交消息,而不是返回修改其他答案建议的提交。 - Paul Masri-Stone
1
除了它不起作用之外,我需要回滚最近的5个提交,其中3个是在拉取他人更改时发生的Git合并。所以这会出现“commit <###>是一个合并但没有给出-m选项”的错误。每个合并都有两个父级,显然我必须确定我需要保留这些合并的哪个父级。我甚至不知道如何解决这个问题,更不用说如何将其指定给还原命令了。 - Neutrino
@Neutrino 你应该可以使用 --no-merges 来避免获取合并提交。 - MattJenko
虽然这很有趣,但并不能解决问题。我想在我的提交流中合并提交,但我不想手动识别每个提交的父级应该遵循哪个还原选项,然后将其馈送到还原过程中。在我看来,还原选项应该足够智能,能够选择我正在还原的分支上的父级。 - Neutrino

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