编辑 git 历史的影响

Impact of editing git history

我们的一个回购协议的状态非常糟糕。有人不小心将 4 GB 的二进制文件提交到 repo 并将其推送到远程主机。然后,他们说 "Oops!" 并恢复了该提交。

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

我知道我可以使用 rebase 和 filter-branch 等工具来删除这些提交或从 git 历史记录中删除这些文件。但是,关于这些工具的每一个 post 或文档都说 "If the commits you want to edit have already been pushed to remote, then DON'T DO IT! Rewriting history is a BAD IDEA!!!"

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

所以,我希望有帮助的 Whosebug 用户可以解释使用 filter-branch 删除这些大文件可能会产生什么影响,或者是否有一些我们不知道的更好的解决方案。

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

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

虽然要回答你的核心问题。

Git 将数据存储为相互引用的对象集合。 (参见 Merkle Trees)因为树和历史都是由引用其他对象的对象构建的,所以很难真正从 git 存储库中消除共享数据。

"Rewriting history" 甚至有点用词不当,因为 git 从不重写历史,它只是返回并创建一个新的历史,然后指向那个新的历史。在垃圾收集之前,旧的东西可能会闲置几个月。一旦你开始分享,在 git 的逻辑模型中,你重写的历史只是 repo 另一个实例上的另一个分支。

通常情况下,分支会推动代码库向前发展,并且可以合并以将历史整合在一起。如果您有一个名为 feature1 的功能分支并将其合并到您的 master 分支中,那么不仅仅是代码成为 master 的一部分,feature1 上的所有提交都成为 master 的一部分以及。当每个分支都是一段离散的代码时,这不是问题。

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

所以这就是危险,如果即使一个人在他们的任何分支机构的某个地方仍然拥有历史记录中旧提交的副本,您最终不仅会拥有您要尝试的文件干掉,但是整个历史的第二个版本也要处理。

如果您必须删除它,可以这样做,但是您必须非常仔细地协调该过程以确保存储库的每个实例都已清除。对于一个非常小的团队,这并不可怕,但是你的团队越大越分散,就越难。

*它在打包对象进行存储时确实做了一些聪明的增量压缩,但总是以一种保证完美重建的方式。 Git 甚至会将整个历史记录中的一点不当检测为损坏的存储库。