繁体   English   中英

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

[英]Dirty trick to keep commit hashes when rewriting Git history?

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

Git filter-branchBFG Repo-Cleaner是两个流行的工具,用于从存储库历史记录中删除大文件和其他内容。 它们导致不同的提交 SHA/哈希,这就是 Git 在“指纹”提交内容、其父项等时的工作方式。

然而,我们处于这样一种情况:不幸的大文件提交发生在不久前,我们有各种对较新提交的引用,例如在 GitHub 问题( “请参阅提交 f0ec467” )和其他外部系统中。 如果我们使用 filter-branch 或 BFG,很多事情都会崩溃。

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

如果这行不通,我想了解原因 Git 是否会定期检查哈希值是否与实际内容一致? 它是否仅在某些操作(例如gc或 push 或 pull)期间这样做?

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


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

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

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

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

从那时起,您的Git 将只看到新对象,而不是旧对象。 但是当你将你的 Git 连接到另一个 Git 时,你的 Git 对另一个 Git 说:我有对象a123456... ,你喜欢吗? 另一个 Git 可能只是回答:不,谢谢,我已经有了那个。 当然,他们有的。 所以你让你的 Git 与他们的 Git 不兼容,但没有从中得到任何好处。

如果另一个 Git没有有问题的对象,那么,你问题! 他们会要求您提供副本,您可以将其交出。

提交和标记对象在其中留有空间用于存储有点任意(并非完全任意)的用户数据。 您可以在此处放置扰动数据以破坏 SHA-1。 树对象不太友好,但是只要您可以对提交和标记对象执行您需要的操作,您就可以绕过它。

至于算力哪里来,嗯,一大群树莓派电脑的价格都在降....

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

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

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

提交 ID 包含其父项的提交 ID。 这意味着如果两个提交具有相同的 ID,Git 不仅知道这两个提交是相等的,而且它们的整个历史都是相等的。 这是 Git 工作原理的基础,尤其是推和拉。 弄乱它有你的危险。

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

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

git gc可能有问题,但git fsck会失去理智。 您永远无法修复损坏的存储库。 正如torek 所说,新旧存储库之间的推拉会变得非常混乱。


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

您可以通过搜索十六进制字符串、检查它们是否与提交 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM