编辑git历史记录的影响

时间:2018-12-06 22:33:13

标签: git git-rebase git-filter-branch git-history

我们的一个回购协议的状态非常糟糕。有人不小心将4 GB的二进制文件提交给了存储库,并将其推送到了远程主服务器上。然后,他们说:“糟糕!”并还原了该提交。

不幸的是,git只存储差异文件,并且由于它不能真正存储二进制文件的差异文件,因此它将整个文件存储在历史记录中。而且由于它在历史记录中包含两次(一次添加,一次删除),因此现在回购的大小为8 GB。这给我们带来了巨大的问题,并使我们的构建比他们所需的时间长了一个小时。

我知道我可以使用rebase和filter-branch之类的工具删除这些提交或从git历史记录中删除这些文件。但是,这些工具上的每一篇帖子或文档都说:“如果您要编辑的提交已被推送到远程,那么不要这样做!重写历史记录是一个糟糕的想法!”

但实际上没有地方解释重写历史的影响。我们在这里真的别无选择-我们已经从历史记录中删除了这些文件。但是,由于所有有关重写git历史记录的危险的 dire 警告,我们非常害怕实际尝试删除这些文件。

因此,我希望有一个有用的StackOverflow用户可以解释使用filter-branch删除这些大文件可能带来的影响,或者是否有我们不知道的更好的解决方案。

1 个答案:

答案 0 :(得分:3)

一个常见的误解是git存储差异。实际上,它存储每个版本的全部内容*。实际上,整个git模型都是基于保证对源代码的位完美检索而构建的,而基于差异的VCS根本无法实现。

您可能对二进制文件进行了两次提交,或者您正在计算数据库中的副本和工作目录中的一个副本。

不过要回答您的核心问题。

Git将数据存储为互相引用的对象的集合。 (请参阅Merkle Trees。由于树和历史都是由引用其他对象的对象构成的,因此很难从git repo中真正删除共享数据。

“重写历史记录”甚至有点用词不当,因为git从不重写历史记录,它只是返回并创建一个新历史记录,然后指向该新历史记录。这些旧东西可能会在垃圾收集之前徘徊数月。一旦开始共享它,在git的逻辑模型中,您的重写历史记录就是该存储库另一个实例上的另一个分支。

通常,分支将代码库向前移动,并且可以合并以将历史记录汇总在一起。如果您有一个名为feature1的功能分支并将其合并到您的master分支中,则不仅代码成为master的一部分,feature1上的所有提交也都成为master的一部分也一样当每个分支都是离散的代码位时,这不是问题。

当您尝试重写历史记录时,这确实成为一个问题。假设您按照您的建议进行操作,并使用filter-branch从历史记录中删除了代码(尽管变基会更容易,而且可能更安全,因为它是最近的)。团队中的每个成员都会删除该分支的本地副本,并签出新的分支。一切都很好,除了您正在FeatureX上工作,并且在发生错误后已将master分支合并到其中,因此旧的master是您的featureX分支的一部分。在featureXmaster之间进行比较将显示与featureX和旧主文件之间进行比较的结果相同,但是所有这些提交仍是featureX的一部分。在git的大脑中,featureX在添加大文件时分叉出来,当您将其合并到master文件时,featureX将所有内容都带回了。

这就是危险,如果即使是某个人,在其任何分支的某个位置上,仍然保留了历史记录中的旧提交副本,您将不仅拥有仍然试图摆脱的文件,而且最终,但也要处理整个历史版本。

如果必须删除它,可以完成,但是您必须非常仔细地协调过程,以确保已清理存储库的每个实例。对于一个很小的团队来说,这并不可怕,但是您的团队规模越大,分布越分散,就越难。

*当打包对象进行存储时,它确实做了一些巧妙的增量压缩操作,但始终以保证位完美重建的方式进行。 Git甚至会在整个历史记录中发现一点点错位的回购。