Git commit ID是如何生成的以唯一标识提交记录?例如:521747298a3790fde1710f3aa2d03b55020575aa。它是如何工作的?它们仅对每个项目是唯一的吗?还是在全局范围内的Git存储库中也是唯一的?
commit 238tree 0de83a78334c64250b18b5191f6cbd6b97e77f84
parent 6270c56bec8b3cf7468b5dd94168ac410eca1e98
author Michael G. Schwern <schwern@pobox.com> 1659644787 -0700
committer Michael G. Schwern <schwern@pobox.com> 1659644787 -0700
feature: I did something cool
$ openssl zlib -d < .git/objects/81/2e8c33de3f934cb70dfe711a5354edfd4e8172 | sha1sum
812e8c33de3f934cb70dfe711a5354edfd4e8172 -
(作者是最初编写提交的人,提交者是执行提交的人。通常情况下它们相同,但也可能不同。例如,当您重新设置基线或修改提交时。或者如果您提交了其他人通过电子邮件发送给您的补丁,并希望归属于该作者。)
更改其中任何一个属性都将更改提交ID。是的,具有相同属性的相同提交在不同机器上将具有相同的ID。这有三个目的。首先,它意味着系统可以检测到提交是否被篡改。它已经融入了架构中。
其次,通过查看提交ID,可以快速比较提交。这使得Git的网络协议非常高效。想要比较两个提交是否相同?不需要发送整个差异,只需发送ID。origin
A - B - C - D - E [master]
A - B [origin/master]
git fetch origin
的网络对话大致如下:
local
嘿,origin,你有哪些分支?origin
我有E上的主分支。local
我没有E,我有你在B上的主分支。origin
你说B?我有B,并且它是E的祖先。没问题。让我给你发送C、D和E。这也是为什么当您使用rebase重写提交时,之后的所有内容都必须更改的原因。以下是一个示例。
A - B - C - D - E - F - G [master]
假设您重写了D,只是为了稍微更改日志信息。现在D就不能再是D了,它必须复制到一个新的提交中,我们称之为D1。
A - B - C - D - E - F - G [master]
\
D1
A - B - C - D - E - F - G [master]
\
D1 - E1
继续进行F到F1和G到G1的操作。
A - B - C - D - E - F - G
\
D1 - E1 - F1 - G1 [master]
它们都有相同的代码,只是父级不同(或在D1的情况下,提交信息不同)。
通过运行以下命令,您可以准确地查看提交ID所包含的内容
git cat-file commit HEAD
它会给你一个类似于这样的东西
tree 07e239f2f3d8adc12566eaf66e0ad670f36202b5
parent 543a4849f7201da7bed297b279b7b1e9a086a255
author Justin Howard <justin.howard@example.com> 1426631449 -0700
committer Justin Howard <justin.howard@example.com> 1426631471 -0700
My commit message
它为您提供:
Git将所有这些内容进行sha1哈希处理。您可以通过运行以下命令来重新生成提交ID:
(printf "commit %s\0" $(git cat-file commit HEAD | wc -c); git cat-file commit HEAD) | sha1sum
这首先打印字符串 commit
,然后是一个空格和 cat-file
文本 blob 的字节数。然后它将 cat-file
blob添加到其中,再加上一个空字节。所有内容都会通过 sha1sum
运行。
正如您所看到的那样,这些信息中没有任何标识项目或存储库的内容。 这不会引起问题的原因是两个不同的提交哈希碰撞的可能性极微不足道。