什么是Git提交ID?

66
Git commit ID是如何生成的以唯一标识提交记录?例如:521747298a3790fde1710f3aa2d03b55020575aa。它是如何工作的?它们仅对每个项目是唯一的吗?还是在全局范围内的Git存储库中也是唯一的?

1
请参阅http://git-scm.com/book/en/v2/Getting-Started-Git-Basics#Git-Has-Integrity。 - jub0bs
2个回答

105
这是一个提交对象文件的示例,进行了解压。
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

提交 ID 是该的SHA-1哈希
$ openssl zlib -d <  .git/objects/81/2e8c33de3f934cb70dfe711a5354edfd4e8172 | sha1sum 
812e8c33de3f934cb70dfe711a5354edfd4e8172  -

这包括:
  • 完整的提交内容,不仅仅是差异,表示为tree object ID。
  • 之前提交的ID(如果是合并,则有多个)。
  • 提交和作者日期。
  • 提交者和作者的姓名和电子邮件地址。
  • 日志消息。

(作者是最初编写提交的人,提交者是执行提交的人。通常情况下它们相同,但也可能不同。例如,当您重新设置基线或修改提交时。或者如果您提交了其他人通过电子邮件发送给您的补丁,并希望归属于该作者。)

更改其中任何一个属性都将更改提交ID。是的,具有相同属性的相同提交在不同机器上将具有相同的ID。这有三个目的。首先,它意味着系统可以检测到提交是否被篡改。它已经融入了架构中。

其次,通过查看提交ID,可以快速比较提交。这使得Git的网络协议非常高效。想要比较两个提交是否相同?不需要发送整个差异,只需发送ID。
第三点,这是它的精髓,具有相同ID的两个提交具有相同的历史记录。这就是为什么先前提交的ID是哈希的一部分。如果提交的内容相同但父级不同,则提交ID必须不同。这意味着在比较存储库(如推送或拉取)时,一旦Git找到两个存储库之间共同的提交,它就可以停止检查。这使得推送和拉取极其高效。例如...
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

虽然 D1 可以有 C 作为其父级(C 不受影响,提交不知道他们的子级),但它与 E、F 和 G 断开连接。如果我们将 E 的父级更改为 D1,那么 E 就不能再是 E 了。它必须复制到一个新的提交 E1。
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的情况下,提交信息不同)。


5
最后一次提交的ID。Git就像区块链。 - g10guang
4
我喜欢这个图形化的解释,而@JustinHoward在下面给出的答案则是它的完美“另一半”,它展示了复制哈希值的确切步骤,现在你知道了所有的东西,包括目的和细节! - ProfDFrancis

37

通过运行以下命令,您可以准确地查看提交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

它为您提供:

  1. 树内容的校验和
  2. 父提交ID(如果这是合并,则会有更多父级)
  3. 作者及其时间戳
  4. 提交者及其时间戳
  5. 提交消息

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 运行。

正如您所看到的那样,这些信息中没有任何标识项目或存储库的内容。 这不会引起问题的原因是两个不同的提交哈希碰撞的可能性极微不足道。


3
这真的很好地补充了所选答案,我已经检查过了,shell脚本确实有效,我猜这是从实际代码中复制的,对吗? - Jean Vincent
2
@JeanVincent:这并不是直接从代码中提取的。我想那是用C语言编写的。但它准确地复制了该算法(目前如此,但贡献者正在努力替换SHA1)。 - Justin Howard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接