重写Git历史时保持提交哈希的肮脏技巧?

重写Git历史时保持提交哈希的肮脏技巧?,git,Git,注意:有一个类似的问题,但答案集中在Git如何不能做到这一点上。在这个问题中,我想探讨在理论上是否有可能编写一个自定义脚本来保持提交散列 Gitfilter branch和是从回购历史记录中删除大型文件和其他内容的两种流行工具。它们导致不同的提交sha/散列,这就是Git在“指纹”提交内容、其父级等时的工作方式 然而,我们所处的情况是,不幸的大文件提交发生在不久前,我们对新提交有各种各样的引用,例如在GitHub问题(“请参阅提交f0ec467”)和其他外部系统中。如果我们使用过滤器分支或BFG

注意:有一个类似的问题,但答案集中在Git如何不能做到这一点上。在这个问题中,我想探讨在理论上是否有可能编写一个自定义脚本来保持提交散列

Git
filter branch
和是从回购历史记录中删除大型文件和其他内容的两种流行工具。它们导致不同的提交sha/散列,这就是Git在“指纹”提交内容、其父级等时的工作方式

然而,我们所处的情况是,不幸的大文件提交发生在不久前,我们对新提交有各种各样的引用,例如在GitHub问题(“请参阅提交f0ec467”)和其他外部系统中。如果我们使用过滤器分支或BFG,很多东西都会破裂

所以我来这里是想问是否有一些卑鄙的低级把戏如何保留提交ID/SHA-1,即使对于重写的提交。我设想,对于我们要重写的错误提交,自定义脚本将创建一个新的Git对象,但“硬编码”相同/旧的SHA-1,从而跳过它的计算。我认为较新的提交(其子/子代)应该继续工作(?)

如果这不起作用,我想知道为什么。Git是否定期检查哈希值是否与实际内容一致?它是否仅在某些操作期间执行此操作,如
gc
或推拉操作

(我知道这是一个非常薄弱的环节,我只是在技术上探索我们的选择,然后才接受我们的回购协议将永远有一个大的二进制文件,这意味着永远有更大的备份,完整克隆需要更长的时间等等。)


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

我添加了一个注释,但事实上,破坏SHA-1并没有多大帮助

问题是Git通过比较对象哈希ID来交换对象。这些是目前的SHA-1(有关未来的一些可能性,请参见其他问题及其答案)。如果您设法中断SHA-1,并生成生成相同哈希ID的新输入对象,则可以:

  • 从Git的对象数据库中删除旧对象,然后
  • 将新对象插入Git的数据库
从那时起,Git将只看到新对象,而不是旧对象。但是当你把你的Git连接到另一个Git时,你的Git对另一个Git说:我有object
a123456…
,你想要它吗?另一个Git可能会回答:不,谢谢,我已经有了。当然,他们有旧的。因此,您已经使您的Git与他们的Git不兼容,但没有从中获得任何好处

如果另一个Git没有问题的对象,那么你就没事了!他们会要你的复印件,你可以交给他们

Commit和tag对象中有空间容纳一些任意(而不是完全任意)的用户数据。在这里,你可以把你的扰动数据放在破坏SHA-1的地方。树对象不太友好,但只要您可以对提交和标记对象执行所需的操作,您就可以绕过它

至于从哪里获得计算能力,那么,一大组Raspberry Pi计算机的价格正在下降

编辑:我忘了回答这个问题:

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


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

提交ID包含其父级的提交ID。这意味着,如果两个提交具有相同的ID,Git不仅知道这两个提交是相等的,还知道它们的整个历史记录是相等的这是Git工作原理的基础,尤其是推拉。弄乱它,后果自负

你可以用git replace做一些聪明的事情,但我没有这方面的经验

如果这不起作用,我想知道为什么。Git是否定期检查哈希值是否与实际内容一致?它是否仅在某些操作(如gc或推或拉)期间才这样做

git-gc
可能有问题,但是
git-fsck
会失去理智。您永远无法修复损坏的存储库。随着时间的推移,新旧存储库之间的推拉会变得非常混乱


我建议保留一份原始存储库的副本以供参考。当您找到对旧ID的引用时,仍然可以查找它。如果您明智地重写它们以引用新存储库中的等效提交,最终您将不再需要旧的repo

您可以通过搜索十六进制字符串、检查它们是否匹配提交ID并将其替换为新提交ID来加快此过程。可以通过在两个存储库上运行
git log--pretty='format:%H'
并将它们一一比较来获得新旧映射


更新
如果你真的,真的需要那些Github链接来工作,你可以编写一个http代理来重定向
https://github.com/your-org/your-repo/commit/oldcommitid
https://github.com/your-org/your-repo/commit/newcommitid

唯一的方法是打破SHA-1算法。你看,也许可以通过破解Git来实现你想要的,但我不推荐这样做。Git强制为用于审计目的的重写提交创建一个新的SHA-1,因此很明显,有些工作有b