我认为这个问题引起了一些混乱,因为您将其表述为想要“恢复”错误 - 而“恢复”意味着特定于 git 的东西。我知道你的意思不是 git 那个词的意思,因为 git 的意思是像“恢复历史”这样的短语。
因为您想完全撤消更改,所以第一步是重写历史记录。 AnoE 的回答显示了一种方法来做到这一点,假设只有一个 ref 可以到达错误的合并,并且在该 ref 和错误的合并“之间”没有合并提交。
如果有多个 refs,您需要做更多的事情。例如,如果你有
x -- x -- x --- ML -- x -- A -- x <--(master)
/ \
(linux history) o <--(branch_one)
完成rebase会给你
x' -- A' -- x' <--(master)
/
x -- x -- x --- ML -- x -- A -- o <--(branch_one)
/
(linux history)
然后你需要移植 o 提交,类似于
git rebase --onto A' A branch_one
(将 A 和 A' 替换为提交 ID 或其他命名适当提交的表达式)。
如果有需要重写的合并,那么你有一个更大的问题。 rebase 命令默认会尝试写入线性历史。您可以使用--preserve-merges 选项告诉它您想要保留合并拓扑,但它可能无法正常工作。如果合并提交有冲突,您将不得不重新解决它。更糟糕的是,如果合并提交没有有冲突,但最初没有使用默认合并结果完成,那么rebase 将无法正确重新创建合并(或其任何子级)。
因此,唯一安全的 rebase 方法是分段,在遇到合并时手动复制它们。
另一种选择可能是使用git filter-branch 而不是rebase;但这也很棘手。仅当您可以编写脚本删除合并引入的任何内容时,它才可行。例如,如果 linux 历史记录与您自己的工作位于不同的路径中,那么您可以通过 rming 某些路径来清理给定的内容实例,那么您可以使用 filter-branch。
(由于这对您来说可能可行也可能不可行,所以现在我不会详细说明步骤。filter-branch 文档可以填补空白。基本上你会使用@987654335 @ 绕过合并提交(通过将以下提交重新设置为第一个父提交),加上 index-filter 或 tree-filter 从后续提交中删除 linux 文件。)
不管怎样,一旦你清理了历史记录,你会仍然在你的回购数据库中拥有所有这些历史记录。至少您需要确保没有引用该历史记录。然后它最终会被gc 清除(或者你可以强制它更快发生)。
这通常意味着您必须找到任何可以访问 linux 历史记录的 refs。由于重写移动了“你的”引用,这可能包括任何与 linux 历史本身一起拉入的引用(分支或标签)。所以你只想删除那些。
还会有可以(间接)访问 linux 历史记录的 reflog,而 gc 无法删除以这种方式可访问的历史记录。老实说,此时最简单的做法可能是重新克隆 repo(因为新的克隆应该只获取当前 refs 及其历史记录)并用结果替换 origin。
如果您想修复现有的 repo 而不是出于任何原因重新克隆,下一步是清除 reflogs(我通常只是 rm -r .git/logs),然后运行积极的 gc(参见 @987654341 @文档)