首页 > 解决方案 > 重写 Git 历史记录时保持提交哈希的肮脏技巧?

问题描述

注意:有一个类似的问题How to keep commit hashs not change when using git filter-repo rewrite the history但答案集中在Git如何无法做到这一点。在这个问题中,我想探索理论上是否可以编写自定义脚本来保留提交哈希。

Gitfilter-branchBFG Repo-Cleaner是两个流行的工具,用于从回购历史中删除大文件和其他内容。它们导致不同的提交 SHA/哈希,这就是 Git 的工作方式,因为它“指纹”了提交的内容、其父级等。

但是,我们处于不幸的大文件提交发生在不久前的情况,并且我们有各种对较新提交的引用,例如在 GitHub 问题(“参见提交 f0ec467”)和其他外部系统中。如果我们使用 filter-branch 或 BFG,很多事情都会中断。

所以我来这里询问是否有一些肮脏的低级技巧,即使对于重写的提交,也可以保留提交 ID/SHA-1。我想,对于我们想要重写的错误提交,自定义脚本将创建一个新的 Git 对象,但“硬编码”相同/旧的 SHA-1,跳过它的计算。我认为较新的提交(它的孩子/后代)应该继续工作(?!)。

如果这不起作用,我想了解原因。Git 是否会定期检查哈希值是否与实际内容一致?它是否仅在某些操作期间这样做,例如gc推或拉?

(我知道这是一个非常薄的冰,我只是在技术上探索我们的选择,然后我们接受我们将永远在我们的回购中拥有一个大型二进制文件,所有的影响,比如永远拥有更大的备份,完整的克隆需要更长的时间, ETC。)


更新:现在有一个公认的答案,但同时,没有答案提到git replace这可能是解决方案?我做了一些基本的实验,但还不确定。

标签: git

解决方案


我在评论中包含了一个链接,但实际上,破坏 SHA-1 并没有多大帮助。

问题在于 Gits 通过比较对象哈希 ID 来交换对象。这些目前是 SHA-1(有关未来的一些可能性,请参阅另一个问题及其答案)。如果您设法破坏 SHA-1,并生成一个生成相同哈希 ID 的新输入对象,您可以:

  • 从 Git 的对象数据库中取出旧对象,然后
  • 将新对象插入到 Git 的数据库中

从那时起,您的Git 将只看到新对象,而不是旧对象。但是当你将你的 Git 连接到另一个 Git 时,你的 Git 对另一个 Git 说:我有 object a123456...,你想要吗?另一个 Git 可能只是回答:不,谢谢,我已经有了那个。 当然,他们有的。因此,您使您的 Git 与他们的 Git 不兼容,但没有从中获得任何收益。

如果另一个 Git没有相关对象,那么您就可以了!他们会要求您提供副本,您可以将其交出。

提交和标记对象在其中有一些空间来存储一些任意(不是完全任意)的用户数据。这是您放置扰动数据以破坏 SHA-1 的地方。树对象不太友好,但只要您可以使用提交和标记对象做您需要做的事情,您就可以绕过这个。

至于算力从哪里来,嗯,一大群树莓派电脑的价格要降下来了……

编辑:我忘了解决这个问题:

Git 是否会定期检查哈希值是否与实际内容一致?

是的。事实上,它每次通过哈希 ID 提取对象时都会执行此检查。请记住,大部分存储库是对象数据库,它是一个简单的键值存储。键是哈希 ID,存储在该键下的数据代表对象。Git 使用密钥进行查找,然后验证存储的数据哈希到该密钥,以确保存储的数据没有被磁盘或内存错误损坏。


推荐阅读