通常*需要多少个git sha来唯一标识给定代码库中的更改?


212

例如,如果要构建一个目录结构,该目录结构为Git存储库中的提交命名了一个目录,并且您希望它足够短以至于不让您流血,但又要足够长以使它碰撞的机会可以忽略不计,通常需要多少SHA子字符串?

假设我要唯一标识此更改:https : //github.com/wycats/handlebars.js/commit/e62999f9ece7d9218b9768a908f8df9c11d7e920

我最多只能使用前四个字符:https : //github.com/wycats/handlebars.js/commit/e629

但是我觉得那会很冒险。但是,假设某个代码库可能在几年内(例如)更改了3万个代码,那么如果我使用8个字符,发生冲突的机会是多少?12吗 是否存在通常被认为可以接受的数字?


Answers:


230

Pro Git手册的第7章实际上回答了这个问题:

通常,八个到十个字符足以在一个项目中唯一。最大的Git项目之一Linux内核开始需要在40个字符中保持12个字符以保持唯一性。

短SHA的Git默认值为7位数,因此对于大多数项目来说都可以。如前所述,内核团队增加了几次,因为有数十万次提交。因此,对于您的约3万次提交,8或10位数字应该是完美的。


38
还要注意,这git很聪明。您可以将缩写简称设置为4,并且git将使用4位数字表示尽可能多的哈希值,但是如果知道缩写不是唯一的,则切换为5或更多...
twalberg

31
但也请注意,这当然仅适用于Git打印SHA的那一刻。如果“保存”缩写的SHA(例如,在日志,电子邮件,即时消息等中),以后再使用它们来引用提交,则它们可能不再是唯一的!虽然对于7-12个字符这样的正常长度当然不太可能,但是如果您减小到4或5,并且又得到了几万个新对象(或提交,具体取决于上下文),这确实可能会再次咬住您。
Nevik Rehnel

140

注意:您可以要求git rev-parse --short最短但唯一的SHA1。
请参阅“ git从常规哈希中获取短哈希

git rev-parse --short=4 921103db8259eb9de72f42db8b939895f5651489
92110

如您在示例中所见,即使我指定的长度为4,SHA1的长度也为5。


对于大型存储库,自2010年以来7个还不够,因此请 Linus Torvalds本人提交dce9648(git 1.7.4.4,2010年10月):

默认值7来自git开发的相当早的时候,当时7个十六进制数字很多(它涵盖了大约250+百万个哈希值)。
那时我以为65k修订版本很多(这是我们在BK中要达到的修订版本),每个修订版本通常大约有5-10个新对象,所以一百万个对象是一个很大的数目。

(BK = BitKeeper)

这些天来,内核甚至不是最大的Git项目,甚至内核约220K版本(比BK树曾是大),我们正在接近200万级的对象。
那时,对于许多字符来说,七个十六进制数字仍然是唯一的,但是当我们谈论的是对象数量和散列大小之间仅两个数量级的差异时,截断的散列值存在冲突。
它甚至不再接近于不现实-它一直在发生。

我们应该增加一个不切实际的默认缩写,增加人们在git config文件中设置自己的默认每个项目的方法

core.abbrev

设置长度对象名称的缩写。
如果未指定,则许多命令缩写为7个十六进制数字,这可能不足以使缩写对象名称在足够长的时间内保持唯一。

environment.c

int minimum_abbrev = 4, default_abbrev = 7;

注意:如下面marco.m评论,在commit a71f09f中的同一Git 1.7.4.4中core.abbrevLength被重命名。core.abbrev

重命名core.abbrevlengthcore.abbrev

--abbrev=$n毕竟它对应于命令行选项。


最近,Linus在提交提交了e6c587c(适用于Git 2.11,2016年第4季度):(
Matthieu Moy回答中所述

在相当早的日子里,我们以某种方式决定将对象名称缩写为7个十六进制数字,但是随着项目的发展,越来越早地看到这样简短的对象名称并记录在日志消息中就不再唯一了。

当前,Linux内核项目需要11到12个十六进制数,而Git本身需要10个十六进制数来唯一地标识它们拥有的对象,而许多较小的项目可能仍然可以使用原始的7十六进制数。单一尺寸并不适合所有项目。

引入一种机制,在该机制下,我们会在第一个请求时估计存储库中的对象数量,以使用默认设置来缩写对象名称,并为存储库提出一个合理的默认值。基于预期,2^(2N)当使用对象名称缩短到前N位时,在存储库中会与对象发生冲突,请使用足够数量的十六进制数来覆盖存储库中的对象数量。
我们在缩写名称中添加的每个十六进制数字(4位)使我们在存储库中的对象数可以达到其四倍(2位)。

参见Linus Torvalds()的commit e6c587c(2016年10月1日。 参见Junio C Hamano()提交的commit 7b5b772commit 65acfea(2016年10月1日(通过合并JUNIOÇ滨野- -提交bb188d0,2016年10月3日)torvalds
gitster
gitster

这个新属性(猜测SHA1缩写值的合理默认值)直接影响Git如何计算自己的版本号以进行发布


3
此答案提供了一种检查单个存储库中最长的“缩短的”哈希值的方法:stackoverflow.com/a/32406103/1858225
Kyle Strand 2015年

1
请注意,该名称core.abbrevLength已重命名为core.abbrev
marco.m

@ marco.m谢谢。我已经相应地修改了答案。而且我已链接到Git提交,该提交记录了该新名称core.abbrev
VonC

我将添加到它,您可以运行git rev-parse --short=10 --verify HEAD以生成10个字符。我们使用git log -1 --format=%h,但是仅生成了7个字符,因此发生了碰撞。
grayaii

感谢您的解释,文档(git-scm.com/docs/git-rev-parse)是陈旧的。
安德烈·韦朗(AndréWerlang)'17

36

这被称为生日问题。

对于小于1/2的概率,可以将碰撞概率近似为

p〜=(n 2)/(2m)

其中n是项目数,m是每个项目的可能性数。

十六进制字符串的可能数目为16 c,其中c为字符数。

因此对于8个字符和3万次提交

30K〜= 2 15

P〜=(N 2)/(2M)〜=((2 152)/(2 * 16 8)= 2 30 /2 33 =⅛

增加到12个字符

P〜=(N 2)/(2M)〜=((2 152)/(2 * 16 12)= 2 30 /2 49 = 2 -19


正是我要解决的问题,谢谢!@Messa答案中链接的概率表也很有帮助。
凯尔·查达


13

这个问题已经得到了解答,但是对于任何在背后寻找数学原理的人来说,这就是生日问题Wikipedia)。

大约有N个人中有2个人(或更多)在一年中的同一天过生日的可能性。这类似于来自总共有N个提交且长度为X的相同哈希前缀的存储库中的2个(或更多)git提交。

查看“ 概率”表。例如,对于长度为8的哈希十六进制字符串,当存储库中只有大约9300个项目(git commit)时,发生冲突的可能性达到1%。对于11万次提交,概率为75%。但是,如果您使用长度为12的哈希十六进制字符串,则100 000次提交中发生冲突的可能性低于0.1%。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.