Git:恢复连续但不是最近的多个提交

Git: Reverting multiple commits that are contiguous but not most recent

我读了 How I reverted several git commits in a single commit,这正是我想要的结果。但是,我确实想在一次提交中还原所有内容,因为 1) 有很多提交要还原 2) 很可能我需要稍后还原这些还原。

我比 link 中的示例幸运,因为在要还原的提交中间没有要保留的提交。这就是我的历史:

A–C1–C2–C3–C4–O1–O2–O3

其中四个 C 提交是我要还原的,O 提交是其他作者后来做出的。

如何在一个提交 R 中还原四个 C,稍后可以还原以使所有 C 再次生效?

像许多 git 命令一样,git revert uses git rev-list (or the moral equivalent thereof) to parse its arguments: see in particular the description of OPTIONS. This means, among other things, that you can simply pass a range of commits, as spelled out in the gitrevisions documentation。请记住,C1..C4 排除了 C1 本身 1,因此如果您使用这种形式,请写 C1^..C4A0..C4.

这负责指定要还原的提交,但仍然会留下一个进行四个新提交的还原:

$ git revert 22b38d1^..676699a

没有做你想做的事情。2你想要的也列在那些OPTIONS中,而且只是-n(或--no-commit,同样的事情):

$ git revert -n 22b38d1^..676699a
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
You are currently reverting commit 22b38d1.
  (all conflicts fixed: run "git revert --continue")
  (use "git revert --abort" to cancel the revert operation)
[remainder snipped]

所有四个提交现在都已还原,但尚未完成任何提交,所以现在我必须手动提交结果。如果我 运行 git revert --continue git 会为我做一个提交,但只会提到 first 回退(22b38d1,在这个case——恢复是反向完成的,676699a 首先完成,以防需要恢复之前的提交,依此类推。

$ git commit

这会向编辑器显示 "reverts (the first commit)" 消息,我现在可以将其修改为 "revert 22b38d1^..676699a in one swoop"。 (构造一个好的恢复消息可能很困难;git 的默认自动提交就足够了,但在这种情况下,-n 的默认值就不够了。)


Git 是 git,当然,您可以使用相同的技术 without the -n and with 您链接的帖子中提到的 git rebase -i:将四个还原合并为一个还原,但使用挤压操作合并提交文本。在这里你可能 运行:

$ git revert --no-edit 22b38d1^..676699a
[messages omitted]
$ git rebase -i HEAD~4
[editor runs: change last three to "squash", write, exit editor]
[editor runs again: edit commit message to describe 4-in-1 reversion]

您可能更喜欢第二种方法(我想我自己更喜欢它,只是不完全符合您的要求)。


关于此的旁注:git rebase 使用临时的匿名 b运行ch 简单地 复制 (就像通过挑选樱桃一样)提交。要使用您的符号,您从:

开始
A – C1 – C2 – C3 – C4 – O1 – O2 – O3   <-- branch

并获取,在四次恢复之后(没有 -n,进行四次新提交),这个:

                                     R4 - R3 - R2 - R1   <-- branch
                                    /
A – C1 – C2 – C3 – C4 – O1 – O2 – O3

其中 R 是 C 的反转。然后,rebase 复制原始 R,在 rebase 开始时添加副本(在这种情况下,最终只是一个提交,我将其称为 R),并移动 b运行ch 标签,以便它指向最后一个新提交(使临时 b运行ch 再次永久化):

                                     R4 - R3 - R2 - R1   [abandoned]
                                    /
A – C1 – C2 – C3 – C4 – O1 – O2 – O3 - R   <-- branch

R1R4 会怎样?它们已被遗弃,最终被3 垃圾收集。


1除非你使用 --boundary,但在 git rev-list 输出中用 - 标记边界提交。我没有用 git revert 测试过这个:帽子后缀在图中移回一次提交就足够了,这也是我通常在这些情况下使用的。 (如果您到达根提交,它会失败,但这不会发生在这里。)

2不仅仅是因为那些特定的提交哈希对于我 运行 这些命令所在的存储库是唯一的。您的哈希值会有所不同;或者您可以使用相对名称,例如 HEAD~6^..HEAD~3 或等效的 HEAD~7..HEAD~3。这假设您绘制的提交图片段是准确的,并且从那时起您没有做任何移动 HEAD.

3仍然可以通过 HEAD 和您的 b运行ch 的引用日志找到旧的提交。默认情况下,这会使它们保留 30 天,之后旧的 reflog 条目将过期并被删除,然后 then a git gc(包括任何自动的 git需要)将删除这些提交。