有几种不同的方法可以回答这个问题。一个人使用数学来告诉你在各种提议的条件下会是什么的机会。另一个是问 Git 实际上做了什么,但是当问 那个 问题时,答案取决于你的特定 Git 版本。
数学答案
机会取决于缩写的长度和存储库中对象的数量。 (在某些情况下,如果您知道所需的对象类型,如果潜在匹配描述 不同 对象类型,则可以消除冲突的歧义。在这种情况下,您可以简单地减少值 n在下面的公式中。)
由于 StackOverflow 不格式化 LaTeX,我在这里有一张来自我自己(进行中)书的第 77 页的屏幕截图。我把它弄得太大了——抱歉:
要找到你想要的数字,用正确的值替换 n 和 r 并计算 p-bar,然后从 1 中减去它。N 是对象:
$ git count-objects -v
count: 49
size: 568
in-pack: 307916
packs: 40
size-pack: 176024
prune-packable: 0
garbage: 0
size-garbage: 0
这个存储库有大约 300,000 个对象(大部分是打包的;只有 49 个松散的对象),所以 n 大约是 300k。您的存储库当然会有所不同。
然后,为 r 插入正确的值。如果您使用 完整 散列,则 r 的值为 2160,或 1461501637330902918203684832716283019655932542976。如果您将散列缩写为四个字符 - 这是Git 将接受的最小输入为 216 或 65536,因为每个字符提供 4 位。完整哈希的长度为 40 个字符,因此完整哈希公式中的长度为 160。
Git 实际做了什么
如果您使用git rev-parse --short=<em>number</em> 或git log --abbrev=<em>number</em> --abbrev-commit,则由您自己选择长度。如果您没有提供数字,Git 会使用不合适的公式选择一个数字。1 但它不仅仅使用该数字!
现代 Git 检查缩写哈希在当前数据库中是否唯一。这不是一个概率猜测,它只是一个字面测试,循环执行:
length = <whatever>
loop {
generate short hash using <length> characters
is short hash unambiguous? if so, we're done - exit the loop
increment length
}
这样就不会与您现在拥有的对象发生碰撞。
不幸的是,如果您再添加一个对象,新对象可能会与基于旧对象生成的缩写哈希冲突。使用上面的公式计算这个概率,知道所有现有的键都没有碰撞,加上缩写哈希长度隐含的 r 的值。它可能仍然相当不错,因为即使是 4 个字符也可以让您获得 1-out-of-65536。但请注意,随着您添加更多对象,它会迅速恶化。
当 Linus Torvald 的第一段代码进入 Git 2.11 时,这个循环检查代码就已经存在。我不确定一个人必须回到它没有发生的地方多远,但在一些非常旧的 Git 版本中肯定没有发生。
1从 Git 2.11 开始,Git 使用的事实是,对于大量 n 个键,50% 的冲突率发生在n = sqrt(r)。 Linus Torvalds 添加了这段代码:
+ if (len < 16 && !status && (flags & GET_SHA1_AUTOMATIC)) {
+ unsigned int expect_collision = 1 << (len * 2);
+ if (ds.nrobjects > expect_collision) {
+ default_automatic_abbrev = len+1;
+ return SHORT_NAME_AMBIGUOUS;
+ }
+ }
在 commit e6c587c733 中用于 Git 2.11。随后在commit 8e3f52d778 中对其进行了改进。但是 50% 的概率太高了。