【问题标题】:Git rewrite history, mitigating regressionGit 重写历史,减少回归
【发布时间】:2018-04-16 17:45:59
【问题描述】:

我想从 git 存储库中删除大量单元测试文件,并将它们从提交历史记录中擦除,以节省空间。我知道这样做的两种主要方法是使用 git filter-branch 或使用 BFG repo 清洁器(由 Roberto 编写。)

1) 假设主 repo 已被清理,并且团队成员尚未删除旧版本的 repo。如果他们执行 git pull --rebase,然后推送到主 repo,历史还会变脏吗?

2) 如上所述,假设主 repo 已被清理,并且团队成员尚未删除旧版本的 repo。假设团队成员推送到主仓库。我怎么能知道团队成员使用了他们的脏版本的 repo 进行了推送? (我是否必须只比较该提交的父级的提交哈希?我的理解是,无论是通过 BFG 还是 git filter-branch 清理主仓库都会更改仓库中所有提交的所有哈希)

【问题讨论】:

    标签: git rebase git-filter-branch bfg-repo-cleaner


    【解决方案1】:

    1) 别担心,他们的拉动会失败。因为历史不同,他们需要修复远程存储库和本地存储库之间的差异。正如您所提到的,删除本地存储库并进行新的克隆是执行此操作的一种方法,但它可能会丢失有价值的数据 - 例如,在其他分支或 .gitignored 文件中。

    更好的方法是Git pull after forced update

    2) 同样,别担心 - git 不会让他们推送具有不同历史记录的副本。他们需要使用--force 来执行此操作,因此他们会非常清楚自己正在覆盖其他人的工作。

    如果他们使用--force git 不会阻止他们(就像它并没有阻止你首先使用--force 来改变历史一样)。但是,您可以将服务器配置为拒绝某些人的强制推送。如果您使用网络服务; GitHub、GitLab、BitBucket 等都有拒绝强制推送的选项 - 转到存储库的设置进行配置。

    【讨论】:

      【解决方案2】:

      这两个问题的答案都是肯定的:将旧(清理前)存储库与新(清理后)存储库混合会导致两个存储库合并。但是,对于问题 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 更快、更方便)。尽管如此,它仍然有效地将所有原始提交复制到新提交。实际上,您复制的存储库是一个几乎是的存储库,绝不应该与旧存储库混合。

      当然,如果某人有一些他们想从他们自己的存储库中带来的提交,这些提交是基于旧的,那么这个人将必须混合旧的和以某种方式创建新的存储库。 进行这种混合的人要小心,并确保不会重新引入所有旧的提交。

      对于许多用户来说,在大多数情况下,他们只需将过滤后的存储库视为一个全新的项目,重新克隆它并丢弃之前的存储库即可。只有那些承诺移植的人才需要了解以上所有内容。

      【讨论】:

      • 感谢您的详细解答。让我知道我在这里是否正确:假设我们有一个提交 c1
      • @user55206:是的。这反过来意味着重新加入存储库的人有一个对此非常满意的 Git,因为新(过滤/固定)存储库中的历史记录以哈希 c1 开头,而原始(未过滤/错误)存储库中的历史记录 也以哈希c1开头,所以很明显旧的c2-onward和新的replacement-for-c2-onward在c1处加入,无论谁这样做都意味着加入他们! :-)
      猜你喜欢
      • 2021-12-09
      • 2014-09-25
      • 1970-01-01
      • 2020-05-30
      • 2018-03-20
      • 2013-04-15
      • 1970-01-01
      • 2013-10-28
      • 1970-01-01
      相关资源
      最近更新 更多