TL;DR:除非你混合 VCS,否则你是安全的。
您问题中的问题陈述首先不太正确:
... git 中的两个不同提交似乎不太可能具有相同的哈希值。
这(间接)导致错误的进一步假设:
但是不只是 git 的所有提交呢?我的应用程序使用 git,svn,hg - 我可以假设不会有相同哈希的不同提交吗?
即使所有的 VCS 都是完美的,你也不能真正做出这样的假设。即使所有的 VCS 都是完美的并且使用相同的散列算法,你仍然不能做出这样的假设。但是对于您的特定问题,有一个更简单(尽管不完美)的答案。
现在我正在尝试决定如何阻止我的应用程序从 db 中一个 repo 的不同分支创建相同的提交...
这里要考虑的主要问题是“一个 repo 的分叉”的概念,以及您将如何识别特定的提交。
如果我们在 Git 或 Mercurial 中查看提交的身份,我们会发现它是一个哈希 ID。
Git 中具有相同对象 ID 的两个对象是根据定义,是同一个对象,因为 Git 只会存储任何对象一次。这是因为 Git 的底层存储模型是一个简单的键值存储,键是一个哈希 ID。任何一个键下都只存储一个值。
为了允许 Git 中的四种对象类型——提交、注释标签、树和 blob——Git 将对象的类型存储在所有对象前面的标头中。它假设将字符串 commit <size>\0 附加到某些数据会导致与将字符串 blob <size>\0 附加到相同数据不同的哈希值。这个假设在很大程度上是正确的,尽管鸽巢原理告诉我们一定有一些数据是错误的。 (在 SHA-1 好的范围内,找到产生冲突的数据对的机会是 2160 中的 1 个。Stevens 等人的工作表明 SHA-1 不是那么好.)
不过,无论如何,Git 的底层存储模型意味着一旦一个键有一个关联的值,该键/值对现在就被占用了,并且没有与 相同的对 em> 密钥可以再次存储。因此,如果某个现有的键 k 存在,并且具有类型 commit 并表示某个提交,则没有具有键 的 any 类型的新对象k 可以添加到存储库数据库中(至少在没有首先使用键 k 删除现有对象的情况下不能)。
这意味着如果你假设提交没有被删除,并且如果你已经看到密钥 k 之前存在于这个存储库的任何克隆中,那么任何 other em> 带有键 k 的克隆具有 same 对象。散列,换句话说,是对象,在非常真实的意义上。
在 Mercurial 中不一定是这种情况。 Mercurial 的数据库可以存储具有重复键的新提交(与每个对象关联的简单本地序列号可以消除它们的歧义)。但是,此类提交永远不能从一个存储库转移到另一个存储库(并且可能会导致其他问题),因此如果存储库将被分发,您当然可以假设问题已解决。
目前,Git 和 Mercurial 都使用 SHA-1,但它们以不同的方式使用。也就是说,在 Git 和 Mercurial 中计算哈希的输入消息是不同的。 this 的意思是,如果你有代表相同 repository,G 中的键 kG (在数字上)与键无关 kM 在 M.
因此,如果您允许两个不同的分叉使用两个不同的底层 VCS,则不能假设两个不同的键代表两个不同的对象,也不能假设两个相同的键代表同一个对象。但是,如果您将它们限制在 same VCS,您可能会做出这样的假设。
(SVN 根本不通过哈希来识别提交。由于 SVN 存储库是中心化的,它们可以并且确实使用一个简单的唯一整数来表示每个提交。但是,通过将 SVN 存储库转换为 Git 存储库,您将 Git限制:您现在拥有一个满足 both VCSes 施加的任何限制的存储库。如果有人向 SVN 存储库添加了一个无法在 Git 存储库中正确表示的新提交,它根本不会进入 Git存储库。)