这两个问题的答案都是肯定的:将旧(清理前)存储库与新(清理后)存储库混合会导致两个存储库合并。但是,对于问题 2,直接 git push 而不首先执行 git fetch (或任何形式的拉动,作为其第一步运行 fetch),执行 git push 的人将看到失败并收到来自推送不是快进的接收存储库。他们将不得不使用+ 或--force 标志覆盖此故障。
git pull 可能会或可能不会因抱怨不相关的历史而失败,这取决于哪些复制的提交(参见下面的描述)最终会重新使用原始提交。这也取决于特定的 Git 版本,因为旧版 Git 会尝试 git merge 不相关的历史记录,而不需要 --allow-unrelated-histories 选项。
(我是否必须只比较该提交的父级的提交哈希?我的理解是,无论是通过 BFG 还是 git filter-branch 清理主仓库都会更改仓库中所有提交的所有哈希)
这有点正确,但在一些重要细节上是错误的。
过滤(通过任何方式)实际上是复制提交的过程。我们获取所有原始提交,以及它们的父哈希 ID 和树以及存储的 blob,并将每个提交复制到一个新的提交。新提交将应用过滤器:我们删除任何我们想要删除的 blob,或对树和/或提交元数据(用户名、时间戳、消息等)进行我们希望的任何其他更改.第一个结果是另一棵树,如果我们没有对树进行任何更改,则重用一些现有的树哈希 ID,或者如果新树与每个现有树不同,则重用新树 ID。我们使用更新的父哈希将这个旧的或新的树 ID 放入我们的旧的或新的提交元数据中。如果任何先前提交发生任何更改,则更新的父哈希是新的,否则相同。然后我们进行新的提交:如果它与原始提交 100% 相同,我们将取回原始哈希 ID,否则我们将获得一个新的哈希 ID。
这意味着只要新副本与原始副本 100% 逐位相同,您实际上只是在重复使用原始副本。但是一旦某个提交发生了变化,哪怕是一点点,新副本就是一个不同的提交,并且它的所有子节点现在都有一个不同的父哈希并且它们本身也是不同的提交。
最终效果是,在使用 git filter-branch 过滤存储库后,您通常会拥有一个 加倍 的存储库,减去 Git 能够重用现有提交的任何数量。原始分支头现在只能通过 refs/original/ 命名空间找到。如果您使用了--tag-name-filter cat,则所有标签都会更新为使用新提交,因此删除所有refs/original/ 引用会消除原始提交。
BFG 通过重写原始引用避免了这一切,而不在refs/original/ 中保留备份(当然比git filter-branch 更快、更方便)。尽管如此,它仍然有效地将所有原始提交复制到新提交。实际上,您复制的存储库是一个几乎是新的存储库,绝不应该与旧存储库混合。
当然,如果某人有一些他们想从他们自己的存储库中带来的提交,这些提交是基于旧的,那么这个人将必须混合旧的和以某种方式创建新的存储库。 进行这种混合的人要小心,并确保不会重新引入所有旧的提交。
对于许多用户来说,在大多数情况下,他们只需将过滤后的存储库视为一个全新的项目,重新克隆它并丢弃之前的存储库即可。只有那些承诺移植的人才需要了解以上所有内容。