8

我很久以前有一个错误的提交,我想将它从 git 历史记录中完全删除,就好像它从未发生过一样。我知道提交 ID,比如说 1f020。我已经尝试过 git rebase 并删除它,但是在 rebase 时有很多冲突,所以不可能那样做。该提交是一个简单的 1 行代码更改并推送一些与项目无关的文件。所以我想编写那 1 行代码更改并提交它,然后以某种方式替换这个新提交,用很久以前的那个。

4

4 回答 4

8

如果有问题的提交位于私有存储库中,那么您想要做的并不是什么大问题。重写已发布的 git 历史会让你的合作者感到恼火,所以一定要确保删除这条线是值得的。

git-rebase文档有一个有用的段落。

git rebase [...] [<--onto newbase>] [<upstream>] [<branch>]

还可以使用 rebase 删除一系列提交。如果我们有以下情况:

E---F---G---H---I---J  topicA

然后命令

git rebase --onto topicA~5 topicA~3 topicA

将导致删除提交FG

E---H'---I'---J'  topicA

如果FG在某些方面存在缺陷,或者不应该是topicA. 请注意,参数 to--onto上游参数可以是任何有效的 commit-ish。

假设您的历史是线性的并且有问题的提交在您的主分支中,您可以通过运行来调整上面的示例

git rebase --onto 1f020~ 1f020 master

对于更复杂的情况,使用交互式变基。您可能会发现跟随一个合并两个提交的示例很有帮助,但是不要将提交标记s为壁球,而是删除整行以从您的历史记录中删除该提交。

于 2012-11-09T23:10:58.067 回答
5

这有点复杂,但无论如何,它是这样的:

分离头并在错误提交之后移动以提交。使用 git log 找出 1f020 之后的下一次提交。

git checkout <SHA1-for-commit-just-after-bad-commit-1f020>

移动 HEAD 以在错误提交之前提交,但保留索引和工作树原样

git reset --soft <SHA1-for-just-previous-to-bad-commit-1f020>

在错误提交之后重新使用提交消息重做提交,但现在在错误提交之前的提交之上。因此,删除那个错误的提交

git commit -C <SHA1-for-commit-just-after-bad-commit-1f020>

将错误提交之后的所有内容重新应用到这个新地方

git rebase --onto HEAD <SHA1-for-commit-just-after-bad-commit-1f020> master
于 2012-11-09T22:43:30.263 回答
1

你永远不应该从 git 的历史中删除提交。你以后会遇到问题。

但是,如果您知道自己在做什么,您可以在本地倒回您的历史记录,用当前提交替换该提交,然后再次重播所有提交 - 然后对您的存储库执行强制推送。

但我强烈建议不要这样做。只需做一个新的提交,并忍受历史上有一个错误的提交。

于 2012-11-09T22:42:28.220 回答
1

所以,你想要的是:

  1. 从历史记录中删除错误提交,并且

  2. 不必重做删除提交后的所有合并。即,从删除的提交派生的每个提交都应保持原样,仅进行最小限度的更改,以不包括从历史记录中删除的更改。

基于第二点的解决方案git rebase --onto并失败,因为它们需要重做以后发生的所有合并。git rebase -i但是,理论上可以简单地重新创建所有这些其他提交,就好像有问题的提交从未发生过一样,只要错误提交足够小,以至于从其后续提交中恢复它本身就会产生冲突。

正如迈克尔和其他人所指出的,这样做是非常不可取的。这也是一项几乎可以肯定不值得付出努力的重大事业。但是,对于教育价值,这里有一个实现目标的综合解决方案的大纲:

  • 备份存储库。

  • 用于git rev-list生成一个提交列表,该列表以错误提交开始,并通向所有可以访问该提交的分支头。

  • 初始化一个空映射以将旧提交映射到新提交。

  • 对于列表中的每个提交,请执行以下操作:

    • 检查提交并从错误提交中恢复更改
    • 使用从当前树中创建一个树对象git write-tree
    • 用于git commit-tree创建一个新的提交,新树、旧提交消息和使用上述映射从旧父级翻译而来的父级(如果某些父级不在映射中,则使用旧父级)
    • 在提交映射中注册新提交。
  • 浏览标签和标签对象,并重新创建它们以指向新的提交,咨询地图。

  • 通过分支头并调用git update-ref以将它们指向新的提交,咨询地图。

如果提交引入的一行更改(以及不必要的文件)与后续提交的更改不冲突,则此过程可以完全自动化,并且不需要您手动重做以后的所有合并和冲突提交。

缺点是它仍然需要重写所有后来的提交,如果它们已在其他地方共享则使它们无效,并使提交消息中的提交引用无效,例如由git revert.

于 2012-11-10T09:02:03.283 回答