什么是 Git 提交 ID?

What is a Git commit ID?

如何生成 Git 提交 ID 以唯一标识提交?

示例:521747298a3790fde1710f3aa2d03b55020575aa

它是如何工作的?它们是否仅对每个项目都是唯一的?还是全球 Git 个存储库?

Git 提交 ID 是关于提交的每件重要事情的 SHA-1 hash。我不打算将它们全部列出,但这是重要的...

  • 内容,所有内容,而不仅仅是差异。
  • 提交日期。
  • 提交者的姓名和电子邮件地址。
  • 记录消息。
  • 先前提交的 ID。

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

其次,只需查看提交的 ID 即可快速比较提交。这使得 Git 的网络协议非常高效。想要比较两个提交以查看它们是否相同?不必发送整个差异,只需发送 ID。

第三,这是天才,具有相同 ID 的两个提交 具有相同的历史记录 。这就是为什么之前提交的 ID 是哈希的一部分。如果一个提交的内容相同,但parent不同,则提交ID必须不同。这意味着当比较存储库时(比如在推或拉中),一旦 Git 发现两个存储库之间有共同的提交,它就可以停止检查。这使得推拉非常有效。例如...

origin
A - B - C - D - E [master]

A - B [origin/master]

git fetch origin 的网络对话是这样的...

  • local嘿源,你有什么分支?
  • origin我在E有master
  • 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 作为其 parent(C 不受影响,提交不知道它们的 children)它与 E、F 和 G 断开连接。如果我们更改 E 的 parent到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]

它们都有相同的代码,只是 parent 不同(或者在 D1 的情况下,不同的提交消息)。

您可以通过 运行ning

准确了解创建提交 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 哈希。您可以通过 运行ning

重现提交 ID
(printf "commit %s[=12=]" $(git cat-file commit HEAD | wc -c); git cat-file commit HEAD) | sha1sum

首先打印字符串 commit,后跟 space 和 cat-file 文本 blob 的字节数。然后它将 cat-file blob 添加到后跟空字节的那个。然后所有这些都通过 sha1sum.

获得 运行

如您所见,此信息中没有任何标识项目或存储库的信息。这不会导致问题的原因是因为两个不同的提交散列在天文学上不太可能发生冲突。