# Git 的 commit 提交对象包含哪些信息?
6 min read
Table of Contents
Git 的 commit 提交对象包含哪些信息?
【核心定义】 Git 的 commit 提交对象是一个包含树对象引用、父提交引用、作者/提交者信息以及提交说明的不可变快照,它是版本历史的基本单元。
【关键要点】
- 树对象引用 (tree): 指向一个树对象(tree object),该树对象代表了本次提交时工作区根目录的完整目录结构和文件快照。这是提交内容的核心。
- 父提交引用 (parent): 指向一个或多个父提交对象的哈希值。首次提交没有父提交,普通提交有一个父提交,而合并提交则有两个或更多父提交。这构成了版本历史的链表/图结构。
- 作者信息 (author): 包含创建该次修改的人员姓名、邮箱和时间戳。
- 提交者信息 (committer): 包含实际执行提交操作的人员姓名、邮箱和时间戳。在大多数情况下,作者与提交者是同一人;但在应用补丁或进行
cherry-pick等操作时,两者可能不同。 - 提交说明 (message): 用于描述本次提交目的和内容的文本信息。
【深度推导/细节】
- 不可变性与哈希:Commit 对象的内容(包括上述所有信息)一旦生成,其 SHA-1 哈希值就唯一确定。任何对提交信息的修改(如
git commit --amend)都会产生一个全新的、哈希值不同的提交对象,原对象依然存在于对象库中。这是 Git 历史不可篡改性的基础。 - 对象关联逻辑:一个 commit 对象通过
tree字段关联到文件系统快照,通过parent字段关联到历史。这种设计使得:- 快速比较:比较两个 commit 的差异,实质是比较它们所指向的 tree 对象的差异。
- 历史追溯:沿着
parent指针可以遍历整个项目历史。 - 空间高效:未修改的文件在不同 commit 中通过 tree 和 blob 对象的复用共享存储,不会重复占用空间。
【关联/对比】
- 与工作区/暂存区对比:Commit 对象是 Git 对象库(.git/objects)中的永久存储,而工作区是用户可见的当前文件,暂存区(Index)是准备构建下一个 tree 对象的中间状态。
git commit命令就是将当前暂存区的内容固化为一个 tree 对象,并创建一个指向它的 commit 对象。 - 与其他 Git 对象对比:
- Blob 对象:存储文件内容。Commit 通过 tree 对象间接引用 blob。
- Tree 对象:存储目录结构和文件名。Commit 直接引用根 tree。
- Tag 对象:一种特殊的对象,可以指向一个 commit 对象并为其提供一个永久的、可读的名字(如版本号)。
『面试官追问』
- 如何查看一个 commit 对象的原始内容?
- 使用
git cat-file -p <commit-hash>命令。这会漂亮地(pretty-print)打印出该 commit 对象的全部原始信息,包括 tree、parent(s)、author、committer 和 message。
- 使用
git commit --amend操作后,原来的 commit 对象去哪了?- 原来的 commit 对象仍然存在于 Git 的对象数据库中,只是不再被当前分支的引用(如
HEAD、分支名)所直接指向。它可能会在一段时间后被 Git 的垃圾回收机制清理,但只要知道其哈希值,仍然可以访问到它。
- 原来的 commit 对象仍然存在于 Git 的对象数据库中,只是不再被当前分支的引用(如
- 为什么需要区分 author 和 committer?
- 这体现了对贡献来源的尊重和审计的精确性。例如,当你将别人邮件发来的补丁应用并提交到仓库时,
author是原始补丁创作者,而committer是你自己。这能更清晰地记录代码的真实来源和流转过程。
- 这体现了对贡献来源的尊重和审计的精确性。例如,当你将别人邮件发来的补丁应用并提交到仓库时,
【版本差异】
- 在 Git 的早期设计中,commit 对象格式相对固定。随着发展,其格式保持高度稳定,但围绕 commit 的工具链和扩展功能(如 GPG 签名、提交图)在不断丰富。例如,commit 对象可以包含 GPG 签名信息,这通常以特殊格式嵌入在提交说明之后,但这属于元数据的扩展,而非核心格式的改变。