编辑过去提交的补丁

Editing patch of a past commit

我有一个过去的提交,其补丁中包含一行,这应该是较新提交的一部分。有什么办法可以将这条线从一个补丁移到另一个补丁吗?我正在尝试使用 git rebase --interactiveedit 选项,但我无法更改补丁。这甚至可能吗?

你可以得到你想要的,使用交互式变基。但请注意一个关键点:提交不是补丁!提交是 快照 。即使 git log -p 显示 每个提交作为补丁,每个提交都有一个 所有源代码的完整副本

git log -p 将提交显示为补丁的原因,即使它们是快照,也是显而易见的:有用commit #12 与提交 #11 略有不同,区别 比起说:这是提交 #12,哈!你是靠你自己的,笨蛋! 所以 git log -p 通过提取提交 #11 提交 #12 来工作,比较两者,然后向你展示什么改变了。

您应该始终牢记这一点:提交是快照;但是 Git 喜欢 比较 两个快照以向您展示它们之间的变化。您可以选择任意两个快照进行比较,但是 git log -p 和其他 Git 命令将自动使用两个相邻的 parent/child 提交。 Rebase 的复制也是这样工作的:它比较 parent 和 child,看看要做什么。 (要比较您选择的两个提交,您可以使用 git diff 并简单地命名要比较的两个提交。)

您可能还想知道更改任何现有提交在技术上是不可能的。 Rebase 不能那样做,也不会尝试。 git rebase 所做的是 复制 旧的(有缺陷的)提交到 new-and-improved 的。旧的提交至少会保留一段时间,如果你的新的和改进的提交实际上是新的和更糟的,你可以找回它们。


综上所述,以下是使用交互式变基执行此操作的方法。首先选择一个提交 before 你需要改进的:

9abcdef  Subject of last commit
00afde4  Second to last commit that should have more stuff
badc0de  The commit that you dislike
ba5eba5  A commit you're OK with

在这里,提交 badc0de 是您想要替换为 new-and-improved 提交的那个。您想要将 badc0de 中所做的一些更改改为进入 00afde4,因此您真的想用一个不同的提交替换 badc0de , 和 00afde4 与另一个不同的提交做更多。然后,因为 9abcdefmy parent 是 00afde4 你将不得不复制 9abcdef 到一个新的和改进的提交其唯一的改进是 my parent 是 _______,其中空白通过制作 [=23= 的新改进版本来填补].

所以,现在您将 运行 git rebase -i ba5eba5 并在您的编辑器中得到一条指令 sheet,内容为:

pick badc0de The commit that you dislike
pick 00afde4 Second to last commit that should have more stuff
pick 9abcdef Subject of last commit

(请注意,虽然 git log 结束 开始并向后工作,但 git rebase -i 的指令 sheet 从第一次提交开始你会copy-but-change-while-copying并向前移动。)

因为我们在这里假设您要将 badc0de 中的行放入 00afde4,您现在应该将三个 pick 命令中的这两个更改为 edit,将 9abcdef(您希望原封不动地复制)作为 pick 命令。将指令 sheet 写回到它所在的任何文件,然后退出编辑器以按照指令开始 Git。

Git 现在将复制提交 badc0de,但随后停止以便您可以 更改 副本。从技术上讲,它实际上还没有 copied badc0de — 它只是设置了一些东西,以便您接下来的操作 复制它 —但在我们即将看到的更复杂的情况下,它会创建一个临时副本。您现在处于 Git 所谓的 detached HEAD 模式——根本不在任何分支上——您所做的是 运行 您的编辑器编辑任何文件( s) 应该不同,更改它们,写出它们,然后退出你的编辑器。然后你 git add 每个这样的文件和 运行:

git commit --amend

这将 badc0de 推到 detached-HEAD 分支上,使 badc0de 提交的副本现在是 good犯罪。我们不知道它 有什么哈希 ID,但我们称它为 1111111 以供参考。

现在您已经 1111111 和 parent ba5eba5 以及您想要的快照,运行:

git rebase --continue

恢复交互式变基。 Git 现在继续将 00afde4 复制到新的临时提交。这次它确实必须制作临时副本,因为 00afde4 我的 parent 是 badc0de,它需要一个说 我的 parent 是 1111111。 Git 通过 比较 00afde4badc0de 来创建这个临时副本,看看有什么变化,并对 1111111 进行相同的更改。我们不知道也不关心这个新的临时提交得到了什么哈希 ID,因为现在是时候再次启动你的编辑器并恢复你上次删除的缺失行了。

如果您忘记了缺失的行是什么,您可以轻松找到它们:使用 [=55= 将仍在您的存储库中的提交 badc0de 与其 parent 进行比较] 例如。使用编辑器将这些行放入 work-tree 文件中。 W找出文件,退出编辑器。 运行 git add 在这些文件上,然后再次 运行 git commit --amend.

Git 现在将临时提交推开,进行类似于 00afde4 的新提交,除了:

  • 它将 1111111 列为其 parent,并且
  • 它恢复了 badc0de 中的行。

同样,我们不知道这个 有什么散列,但我们在这里称它为 2222222 以便我们讨论它。

现在您可以再次 运行 git rebase --continue。此时唯一剩下的指令是pick 9abcdef。因此 Git 将 9abcdef 与其 parent 00afde4 进行比较以查看发生了什么变化,对 2222222 中的快照进行相同的更改,并从那。让我们调用新副本 3333333.

Git 现在有:

3333333  (copy of) Subject of last commit
2222222  (copy of) Second to last commit (now with more stuff)
1111111  (copy of) The commit that you disliked (but now improved)
ba5eba5  A commit you're OK with

提交 3333333 确实 9abcdef 相同, 除了 3333333我的 parent 是 2222222.

由于复制现已完成,git rebase 执行最后一个技巧:它使您的分支名称(无论名称是什么)标识副本 3333333 而不是原始 9abcdef,并且通过检查 re-adjusted 分支退出分离的 HEAD 模式。所以现在如果你 Git 查看从当前开始的提交并向后工作,你会看到 3333333,然后是 2222222,然后是 1111111,然后是 ba5eba5 , 等等。原始提交(您已用新的和改进的副本替换的提交)是不可见的。它们仍在 您的存储库中(大约一个月),但现在很难找到。

@karlosss git rebase -i commit-id-test ,在commit-id测试后会显示所有commit-id和commit-msg,然后你可以编辑commit-msg避免ctrl-c/v.

例如

pick 90b83db Replace QtMultimedia usage with 4A + gstreamer
pick f55e504 autobuild: introduce autobuild scripts