这两个问题的答案都是肯定的:将旧(清理前)存储库与新(清理后)存储库混合会导致两个存储库合并。然而,对于问题 2,git push
没有做git fetch
第一个的顺子(或任何类型的拉,它运行 fetch 作为它的第一步),无论是谁做git push
都会看到失败,接收存储库抱怨推送不是快进。他们将不得不使用+
or--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/
命名空间找到。如果您使用 a --tag-name-filter cat
,则所有标签都会更新以使用新提交,因此删除所有refs/original/
引用会消除原始提交。
BFG 通过重写原始引用而不保留备份来避免所有这些refs/original/
(当然比 更快、更方便git filter-branch
)。尽管如此,它仍然有效地将所有原始提交复制到新提交。实际上,您复制的存储库是一个几乎全新的存储库,永远不应与旧存储库混合。
当然,如果有人想要从基于旧版本的自己的存储库中带来一些提交,那么该人将不得不以某种方式混合新旧存储库。 进行这种混合的人要小心,并确保不会重新引入所有旧的提交。
对于许多用户来说,在大多数情况下,将过滤后的存储库视为一个全新的项目就足够了,重新克隆它并丢弃他们以前的存储库。只有那些承诺移植的人需要了解以上所有内容。