227

假设您的 git 历史记录如下所示:

1 2 3 4 5

1-5 是单独的修订版。您需要在保留 1、2、4 和 5 的同时删除 3。如何做到这一点?

当要删除的修订后有数百个修订时,有没有一种有效的方法?

4

9 回答 9

137

根据这个评论(我检查了这是真的),rado 的答案非常接近,但让 git 处于分离的头部状态。相反,删除HEAD并使用它<commit-id>从您所在的分支中删除:

git rebase --onto <commit-id>^ <commit-id>
于 2012-11-15T00:50:55.973 回答
126

这是一种以非交互方式删除特定的方法<commit-id>,只知道<commit-id>您要删除的内容:

git rebase --onto <commit-id>^ <commit-id> HEAD
于 2010-09-14T00:12:35.417 回答
79

要将版本 3 和 4 合并为一个版本,您可以使用 git rebase。如果要删除修订版 3 中的更改,则需要在交互式变基模式下使用编辑命令。如果要将更改合并到一个修订版中,请使用 squash。

我已经成功地使用了这种 squash 技术,但以前从未需要删除修订。“拆分提交”下的 git-rebase 文档有望为您提供足够的想法来弄清楚。(或者其他人可能知道)。

git 文档

从您希望按原样保留的最旧提交开始:

git rebase -i <after-this-commit>

一个编辑器会被你当前分支中的所有提交触发(忽略合并提交),这些提交在给定提交之后。您可以将此列表中的提交重新排序为您心中的内容,您可以删除它们。该列表或多或少看起来像这样:

选择 deadbee 本次提交的单行
选择 fa1afe1 下一次提交的单行
...

在线描述纯粹是为了您的乐趣;git-rebase 不会查看它们,而是查看提交名称(在此示例中为“deadbee”和“fa1afe1”),因此不要删除或编辑名称。

通过用命令“edit”替换命令“pick”,您可以告诉 git-rebase 在应用该提交后停止,以便您可以编辑文件和/或提交消息,修改提交,并继续变基。

如果要将两个或多个提交合并为一个,请将命令“pick”替换为“squash”以进行第二次和后续提交。如果提交有不同的作者,它会将压扁的提交归于第一次提交的作者。

于 2008-08-31T23:46:33.880 回答
77

如前所述git-rebase(1)是你的朋友。假设提交在您的master分支中,您将执行以下操作:

git rebase --onto master~3 master~2 master

前:

1---2---3---4---5  master

后:

1---2---4'---5' master

来自 git-rebase(1):

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

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

然后命令

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

将导致删除提交 F 和 G:

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

如果 F 和 G 在某些方面存在缺陷,或者不应该是 topicA 的一部分,这很有用。请注意, --onto 的参数和参数可以是任何有效的 commit-ish。

于 2010-11-05T23:34:04.550 回答
23

如果您只想删除修订版 3 中所做的更改,您可能需要使用 git revert。

Git revert 只是创建一个新的修订版本,其中的更改会撤消您正在恢复的修订版本中的所有更改。

这意味着您保留了有关不需要的提交和删除这些更改的提交的信息。

如果有人可能同时从您的存储库中拉出,这可能会更加友好,因为恢复基本上只是一个标准提交。

于 2008-09-05T16:16:32.763 回答
19

到目前为止,所有答案都没有解决尾随问题:

当要删除的修订后有数百个修订时,有没有一种有效的方法?

步骤如下,但作为参考,让我们假设以下历史:

[master] -> [hundreds-of-commits-including-merges] -> [C] -> [R] -> [B]

C:在要删除的提交之后提交(干净)

R:要删除的提交

B:在要删除的提交之前提交(基础)

由于“数百次修订”的限制,我假设以下先决条件:

  1. 有一些令人尴尬的承诺,你希望永远不存在
  2. 实际上依赖于该令人尴尬的提交的后续提交为零(还原时冲突为零)
  3. 你不在乎你会被列为数百个介入提交的“提交者”(“作者”将被保留)
  4. 您从未共享存储库
    • 或者你实际上对所有曾经克隆历史的人有足够的影响力,以说服他们使用你的新历史
    • 而且你不在乎改写历史

这是一组非常严格的约束,但有一个有趣的答案实际上适用于这种极端情况。

以下是步骤:

  1. git branch base B
  2. git branch remove-me R
  3. git branch save
  4. git rebase --preserve-merges --onto base remove-me

如果真的没有冲突,那么这应该继续进行,不会有进一步的中断。如果有冲突,你可以解决它们,rebase --continue或者决定只是忍受尴尬和rebase --abort

现在你应该知道master它不再有提交R了。save分支指向你之前所在的位置,以防你想调和。

您希望如何安排其他人转移到您的新历史记录取决于您。您需要熟悉stashreset --hardcherry-pick。您可以删除base,remove-mesave分支

于 2012-09-24T15:11:16.060 回答
3

我也遇到了类似的情况。使用下面的命令使用交互式变基,并在选择时删除第 3 次提交。

git rebase -i remote/branch
于 2016-09-06T22:54:51.667 回答
2

所以这是我面临的场景,以及我是如何解决的。

[branch-a]

[Hundreds of commits] -> [R] -> [I]

R是我需要删除的提交,并且I是之后的单个提交R

我做了一个还原提交并将它们压在一起

git revert [commit id of R]
git rebase -i HEAD~3

在交互式 rebase squash 期间,最后 2 次提交。

于 2014-11-05T09:09:03.293 回答
0

rado 和 kareem 的答案对我没有任何作用(只有消息“当前分支是最新的。”出现)。这可能是因为 '^' 符号在 Windows 控制台中不起作用。但是,根据评论,将 '^' 替换为 '~1' 可以解决问题。

git rebase --onto <commit-id>^ <commit-id>
于 2017-09-14T07:51:57.550 回答