如何删除git历史记录中的特定修订版?


213

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

1 2 3 4 5

1–5是单独的修订。您需要删除3,同时仍然保留1、2、4和5。如何完成?

如果要删除的修订有数百个,是否有一种有效的方法?


这个问题定义不明确。它并没有明确地说作者想要的是1-2-(3 + 4)-5或1-2-4-5
RandyTek '16

14
好了8年后,我无法确切告诉您我要解决的问题。但是在git中,总是有很多方法可以做某事,并且有很多人喜欢的答案,所以我想这种模棱两可并不会给太多人带来很多麻烦
1800 Information

1
git rebase --onto 2 3 HEAD几乎意味着要重新设置为2,并在3和HEAD之间提交(HEAD是可选的,在这种情况下为5)
neaumusic

Answers:


76

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

我已经成功使用了这种壁球技巧,但之前从未需要删除修订版。希望“分裂提交”下的git-rebase文档应该给您足够的想法来解决它。(或者其他人可能知道)。

git文档

从您想保持原样的最早提交开始:

git rebase -i <after-this-commit>

将使用当前分支中的所有提交(忽略合并提交)来激发编辑器,这些提交将在给定提交之后进行。您可以按照自己的喜好对列表中的提交进行重新排序,也可以将其删除。该列表大致如下所示:

选择Deadbee此提交的单行代码
选择fa1afe1下一次提交的单行
...

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

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

如果要将两个或多个提交折叠为一个,请在第二次和后续提交中将命令“ pick”替换为“ squash”。如果提交的作者不同,它将被压缩的提交归因于第一提交的作者。


42
-1问题定义明确,但这个答案不太清楚。作者没有说确切的解决方案是什么。
2011年

1
这是错误的线索。拆分提交部分不正确。您想在手册中阅读更多内容-请参阅@Rares Vernica的答案。
2011年

1
@AleksandrLevchuk问题的定义不明确:从陈述问题的方式尚不清楚是保留还是丢弃3中的变更集。我同意,如果要放弃更改,则其他答案将提供一种更简单的方法。但是,如果要保留更改,那将是纯粹的修饰操作。两种方法都以危险的方式重写历史记录,如果其他人可能在有缺陷的历史记录之上进行基础工作,则会很危险。如果是这种情况,则不应该进行外观清理,而更改删除最好通过git revert来完成。
西奥多·默多克(

124

根据此评论(我检查这是对的),rado的回答非常接近,但git处于分离状态。而是删除HEAD并使用它<commit-id>从您所在的分支中删除:

git rebase --onto <commit-id>^ <commit-id>

1
如果您能告诉我如何仅基于commit-id从历史记录中删除该commit-id,您将是我的英雄。
kayleeFrye_onDeck

您能否在答案中包含解释魔术命令的作用?即每个参数表示什么?
alexandroid

这很棒,但是如果我想删除历史记录中的第一个提交怎么办?(这就是为什么我来这里:P的原因)
Sterling Camden

2
由于某种原因,运行此程序时什么也没有发生。但是,进行更改^~1使其起作用。

太晚了,但是我要补充一点,如果遇到合并冲突,请中止重新设置基准,然后最后重新运行--strategy-option theirs
LastStar007 '19

122

这是一种非交互方式删除特定对象的方法<commit-id>,仅知道<commit-id>您要删除的对象:

git rebase --onto <commit-id>^ <commit-id> HEAD

2
也为我工作。顺便说一句,^运算符的作用是什么?这是否意味着在指定的提交之后的下一个提交?
Hopia 2012年

3
@hopia表示指定提交的(第一个)父级。参见“ git帮助修订版”
Emil Styrke,2013年

12
请参阅@kareem的建议,HEAD以免遗漏,以免造成头部过头。
mklement0

1
这比必须弄清楚有多少提交返回搜索要容易得多。
达纳·伍德曼

1
这很棒,但是如果我想删除历史记录中的第一个提交怎么办?(这就是为什么我来这里:P的原因)
Sterling Camden

76

如前所述,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):

一定范围的提交也可以通过重新设置基准来删除。如果我们有以下情况:

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。


3
这不是--onto master~3 master~1吗?
马赛厄斯

3
如果您只想删除最后一个提交,则是--onto master〜1 master
MikeHoss

我想带这个溶胶。但我得到了MERGE CONFLICT错误。我使用了stackoverflow.com/questions/2938301/remove-specific-commit中提到的方案,并且在该示例中无法删除第二次提交。
maan81

22

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

Git revert只需创建具有更改的新修订,即可撤消您要还原的修订中的所有更改。

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

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


4
不幸的是,这对我来说不是一个好的解决方案,因为有人不小心将100MB的废话提交给了仓库,从而炸毁了大小,并使Web界面变得缓慢。
史蒂芬·史密斯

18

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

如果要删除的修订有数百个,是否有一种有效的方法?

遵循以下步骤,但作为参考,我们假设以下历史记录:

[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。你可以删除baseremove-mesave分支机构


我怎么会喜欢这150000次?
Gena Moroz

为我工作,从某人提交了一堆150mb文件的分支历史的中间顺序删除了3个提交。
马科斯(Marcos)

3

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

git rebase -i remote/branch

2

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

[branch-a]

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

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

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

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

在交互式变基压缩期间,最后2次提交。


0

rado和kareem的答案对我无济于事(仅显示消息“当前分支是最新的。”)。可能是因为“ ^”符号在Windows控制台中不起作用。但是,根据注释,将“ ^”替换为“〜1”解决了该问题。

git rebase --onto <commit-id>^ <commit-id>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.