git 存储差异是一个常见的误解。它实际上存储了每个版本的全部内容*。事实上,整个 git 模型都是围绕源代码的保证位完美检索而构建的,这是基于差异的 VCS 无法实现的。
您可能已经对二进制文件进行了两次提交,或者您正在计算数据库中的副本和工作目录中的副本。
不过要回答你的核心问题。
Git 将数据存储为相互引用的对象集合。(请参阅Merkle 树)因为树和历史都是由引用其他对象的对象构建的,所以很难从 git 存储库中真正消除共享数据。
“重写历史”甚至有点用词不当,因为 git 从不重写历史,它只是返回并创建一个新的历史,然后指向那个新的历史。在垃圾收集之前,旧的东西可能会挂起几个月。一旦你开始分享它,在 git 的逻辑模型中,你重写的历史只是另一个 repo 实例上的另一个分支。
通常,分支会向前移动代码库,并且可以合并以将历史记录在一起。如果您有一个功能分支被调用feature1
并将其合并到您的master
分支中,那么不仅仅是代码成为 master 的一部分,所有提交也feature1
成为 master 的一部分。当每个分支都是一段离散的代码时,这不是问题。
当您尝试重写历史时,它确实会成为一个问题。假设您按照您的建议进行操作,并使用 filter-branch 从历史记录中删除代码(尽管变基会更容易并且可能更安全,因为它是相当新的)。团队中的每个成员都会删除该分支的本地副本并签出新的副本。一切都很好,除了你正在处理 featureX,并且在错误发生后已经将 master 分支合并到其中,所以旧的 master 是你的 featureX 分支的一部分。featureX
在and之间进行差异master
将显示与旧 master 之间的差异相同的结果featureX
,但所有这些提交仍然是featureX
. 在 git 的大脑中,featureX
在添加大文件时分支,当你将它合并到 master 时,featureX
把一切都带回来。
所以这就是危险,即使一个人,在他们的任何分支的某个地方,仍然拥有历史中旧提交的副本,你最终不仅仍然拥有你想要摆脱的文件,而且还要处理完整的第二个历史版本。
如果您必须删除它,可以这样做,但您必须非常仔细地协调该过程以确保已清理存储库的每个实例。对于一个非常小的团队,这并不可怕,但是你的团队越大越分散,就越难。
*当它打包对象进行存储时,它确实做了一些巧妙的增量压缩工作,但总是以一种保证位完美重建的方式。Git 甚至会将整个历史记录中的一点不合适的地方检测为损坏的存储库。