如果我使用foo
创建文件touch foo
,然后运行shasum foo
,则会打印出来
da39a3ee5e6b4b0d3255bfef95601890afd80709
。
无论我运行shasum foo
的频率如何,或者如果我在另一台计算机上运行它,它将始终打印da39a3ee5e6b4b0d3255bfef95601890afd80709
因为,是的,它是完全相同内容的SHA1表示。在这种情况下空内容:)
但是,如果我执行以下步骤:
cd /some/where
mkdir demo
git init
touch foo
git add -A
git commit -m "adding foo"
..并记住提交的SHA键(例如959c363ed4cf147725360532454bc258c964c744
)。
现在,当我删除demo
并重复完全相同的步骤时,提交 SHA键仍然会有所不同。这很好,确保身份非常重要。
我想知道的是,git究竟做了什么来确保提交哈希值始终是唯一的,即使它们使用完全相同的内容执行完全相同的操作。 git是否只使用uuidgen
之类的东西为提交对象生成一个唯一的id,或者根据时间戳,你的mac地址,你的wifi信号等组合做一些不同的事情。
答案 0 :(得分:11)
我想知道的是,git确实做了什么确保提交哈希值始终是唯一的,即使它们使用完全相同的内容执行完全相同的操作。
无。如果您创建相同的内容,则会获得相同的SHA-1。
然而,首先,你需要意识到"相同的内容"提交意味着 - 如果您没有意外发生SHA-1冲突 1 或找到破解SHA-1的方法 - 您必须创建相同的完整存储库历史记录,直至和包括提交本身,包括所有相同的树,作者姓名,时间戳等。
这是因为如果您在提交时运行git cat-file -p <sha-1>
,那么提交的内容就是您所看到的内容(加上标记为&#34的标记和大小字段;此对象是类型为commit&#34;,因此通过创建与前一次提交具有相同内容的blob,没有简单的方法可以解决问题。以下是一个例子:
$ git cat-file -p 996b0fdbb4ff63bfd880b3901f054139c95611cf
tree e760f781f2c997fd1d26f2779ac00d42ca93f534
parent 6da748a7cebe3911448fabf9426f81c9df9ec54f
parent 740c281d21ef5b27f6f1b942a4f2fc20f51e8c7e
author Junio C Hamano <gitster@pobox.com> 1406140600 -0700
committer Junio C Hamano <gitster@pobox.com> 1406140600 -0700
Sync with v2.0.3
* maint:
Git 2.0.3
.mailmap: combine Stefan Beller's emails
git.1: switch homepage for stats
请注意,此字符串包括树及其SHA-1,这两个提交的父SHA-1,作者和时间戳,提交者和时间戳以及消息。如果您甚至更改了单个位 - 例如尝试更改基础树,或使用某些不同的父提交 - 您将获得一个新的,不同的SHA-1,而不是996b0fdbb4ff63bfd880b3901f054139c95611cf
。
所以答案是这样的:
所以从理论上讲,如果我和你在同一时间使用完全相同的配置作者,电子邮件等完成相同的步骤,我们实际上会得到相同的提交SHA密钥?
是&#34;是&#34;。但是......您必须从相同的暂存区域开始(这将成为tree
),并且同一个父提交。如果您然后配置您的作者,电子邮件等,与其他人完全相同,并且您在同一秒创建新提交(或使用git的环境变量 2 为了强制时间戳,你们都得到了同样的新提交。
这正是我们想要的。如果你创造了它,当你被命名为#34; me&#34;或者我创造了它,当我被命名为&#34; me&#34;时,这并不重要。所有其他内容都是一样的。因为无论谁创造了它,另一个是我的#34;可以克隆它,然后我们也有同样的事情。
(如果我想确定&#34; me&#34;创造的东西不会与真实的我混淆,我需要添加一些独特的东西,我知道,而另一个我不知道当然,如果我在某个地方发布这个东西,我知道的其他人就知道了。但这是签名的注释标签。它们可以包含GPG签名。)
1 意外哈希碰撞的可能性(对于任何一对物体;随着更多物体的上升而增加的机会)是2 <2> 160 中的1,这是......很小。 :-)上升实际上是非常迅速的,所以当你拥有一百万个物体时,它大约有2个 121 。我在这里使用的公式是:
1 - exp(( - ( n *( n -1)))/(2 * r ))
其中 r = 2 160 和 n 是对象的数量。如果没有从1减去,则等式计算&#34;安全边界&#34;,因为它是:我们赢得的机会有意外的哈希冲突。如果我们想将这个数字保持在与磁盘驱动器不会读回文件错误内容的安全范围相同的范围内 - 或者至少是磁盘制造商声称 - 我们需要将其保持在10左右 -18 ,这意味着我们需要避免在我们的git数据库中放置超过1.7千万亿(1.7E15)的对象。
2 您可以设置许多git环境变量来覆盖各种默认值。作者和提交者的内容包括日期和电子邮件:
答案 1 :(得分:2)
它没有,但您必须手动构造提交以使时间戳排成一行。您可以通过编辑.git/objects
文件手动构建与另一个相同的完整有效存储库,但由于较新的提交包含旧提交的哈希值,因此当然必须完全相同。< / p>
答案 2 :(得分:2)
{1}}显示了提供提交对象的唯一SHA-1的内容。
git show <commit>
那是:
其他答案中带有commit e6e53f5256c47b039ed19e95a073484dbb97cbf7
tree 543b9bebdc6bd5c4b22136034a95dd097a57d3dd
author Alex Balhatchet <kaoru@slackwise.net> 1406774132 -0700
committer Alex Balhatchet <kaoru@slackwise.net> 1406774132 -0700
foo
的示例无效的原因是因为您需要覆盖提交者时间戳和作者时间戳。
例如,以下内容是完全可重复的:
--date
如果你在你的机器上运行它,你应该得到完全相同的输出。
<强>更新强>
如果你得到不同的输出,它至少应该是可重复的。例如,我为不同版本的git获得不同的输出; 1.7.10.4将新的空README文件报告为alex@yuzu:~$ ( mkdir foo ; cd foo ; git init ; export GIT_AUTHOR_DATE='Wed Jul 30 19:35:32 2014 -0700'; export GIT_COMMITTER_DATE=$GIT_AUTHOR_DATE; touch README; git add README; git commit README --message 'foo' --author 'Foo Bar <foo@example.com>'; git show HEAD --format=raw ; cd .. ; rm -rf foo ) 2>&1 | grep '^commit '
commit 7438e0a18888854650e6a53a9a5d823d6382de45
,而1.9.1将其报告为0 files changed
,它会更改提交对象的内容。
答案 3 :(得分:0)
CarlMäsak的这个要点比以往更好地解释了它:
https://gist.github.com/masak/2415865
alex@yuzu:~/foo$ git show HEAD
commit 7438e0a18888854650e6a53a9a5d823d6382de45
Author: Foo Bar <foo@example.com>
Date: Wed Jul 30 19:35:32 2014 -0700
foo
diff --git README README
new file mode 100644
index 0000000..e69de29
“commit \ 0”的SHA-1校验和,后跟git cat-file commit HEAD
的字符数(长度)。
alex@yuzu:~/foo$ git cat-file commit HEAD
tree 543b9bebdc6bd5c4b22136034a95dd097a57d3dd
author Foo Bar <foo@example.com> 1406774132 -0700
committer Alex Balhatchet <kaoru@slackwise.net> 1406774132 -0700
foo
把它们放在一起......
alex@yuzu:~/foo$ (printf "commit %s\0" $(git cat-file commit HEAD | wc -c); git cat-file commit HEAD) | sha1sum
7438e0a18888854650e6a53a9a5d823d6382de45 -
sha1sum输出与提交SHA-1匹配!