走进 .git —— 对象与哈希

Dev.to AI 2026-06-22T15:46:25.084715

走进 .git —— 对象与哈希

理解隐藏的 .git 目录内部发生的情况,是掌握 Git 最重要的步骤之一。它有助于解释为什么 Git 快速、可靠,并且能够跟踪大型软件项目中数百万次变更。

当你使用 git init 初始化一个仓库时,Git 会创建一个名为 .git/ 的隐藏目录。它包含了 Git 跟踪变更、管理版本、存储提交历史以及维护项目整体状态所需的一切。

Git 对象是构成仓库的基本构建块。它们共同使 Git 能够高效地存储和跟踪项目历史。对象是 Git 内部使用的基本存储单元。仓库中的每条信息都存储为以下四种对象类型之一:

哈希是将数据转换为唯一的、固定长度的标识符(称为哈希值)的过程。Git 使用 SHA-1 哈希算法生成这些标识符。

这个哈希值就像内容的唯一指纹。如果内容发生变化,哪怕只是一个字符,Git 也会生成一个完全不同的哈希值。

现在让我们通过一个小型项目来帮助理解这些概念……

初始化并探索 .git 目录

初始化一个新的 Git 仓库,并检查隐藏的 .git 目录,以了解 Git 如何在幕后存储和管理提交、分支、配置文件及其他版本控制数据。

1. 初始化一个新仓库

这一步会创建一个新的工作目录,使用 git init 初始化一个 Git 仓库,并通过 git status 验证仓库的状态。在初始化过程中,Git 会生成一个隐藏的 .git 目录,其中包含版本控制所需的内部数据库和配置文件。

git init 命令是每个 Git 工作流的起点。它创建了 Git 用于存储对象、跟踪变更、管理分支和维护提交历史的基础仓库结构。没有 .git 目录,Git 就无法记录版本、存储元数据或执行任何版本控制操作。

理解 git init 在幕后所做的事情,有助于为学习 Git 打下坚实基础,因为每一次提交、分支、标签和仓库操作都依赖于它所创建的数据库结构。

每个软件项目都应该从一个正确初始化的仓库开始。从一开始就建立版本控制,有助于在软件开发生命周期中保持一致性、可追溯性和有效的协作。

# 创建一个新目录(文件夹)。
mkdir git-internals-lab
# 切换当前目录。
cd git-internals-lab
# 在当前目录中初始化一个新的 Git 仓库。
git init
# 显示当前工作状态下哪些文件已被暂存、修改或未被跟踪。
git status

2. 探索 .git 目录

这一步检查隐藏的 .git 目录的内容,包括关键组件,如 HEAD、config、objects、refs、hooks 和 info。同时检查诸如 HEAD(标识当前检出的分支)和 config(存储仓库特定设置)等文件。

.git 目录是每个 Git 仓库的核心。它包含 Git 用于管理版本控制的所有信息,包括提交历史、分支、标签、配置设置和对象存储。像 HEAD 这样的文件充当指向当前分支的指针,而 objects 目录存储 Git 的内部数据库,refs 则维护对分支和标签的引用。

理解 .git 目录的结构,可以让我们深入了解 Git 在幕后是如何运作的。这些知识有助于揭开诸如分支、变基(rebase)、引用日志检查(reflog inspection)、仓库恢复以及排查版本控制问题等高级概念的神秘面纱。

# 列出文件和目录。-l 显示详细信息(权限、大小、日期)。
ls .git
# 显示 Git 的 HEAD 文件内容
cat .git/HEAD
# 显示仓库的本地配置文件
cat .git/config
# 列出 Git refs 目录的内容
ls .git/refs

哈希与 Blob 对象

生成你的第一个 SHA 哈希值,并观察 Git 如何将文件转换为一个存储在对象数据库中的内容寻址 blob 对象。SHA 哈希值基于文件内容充当唯一标识符,使 Git 能够高效地跟踪和检索文件,而不依赖于其文件名或位置。

1. 创建一个文件并对其哈希

将 "Hello Git" 写入 hello.txt,并让 Git 计算该文件的 SHA-1 哈希值,但不将其存储为对象。生成的哈希值(例如,9f4d96d5b00d98959ea9960f069585ce42b1349a)作为一个独特的内容指纹,仅根据其内容来标识文件。

Git 通过哈希值而不是文件名来跟踪数据。相同的内容总是产生相同的哈希值,而即使是一个字符的修改也会生成完全不同的哈希值。这种内容可寻址的设计使得 Git 历史具有防篡改的特性,因为修改文件会改变其哈希值,并使所有依赖该哈希值的引用失效。

密码学哈希有助于确保仓库数据的完整性和真实性。

# 将文本输出到终端——也可通过 > 或 >> 写入文件。
echo 'Hello Git' > hello.txt
# 运行一个 Git 版本控制命令。
git hash-object hello.txt

2. 将 Blob 写入对象存储

-w 标志告诉 Git 将该文件作为一个 blob 对象存储在 .git/objects 目录中。使用 find 命令可以揭示 Git 将对象存储在 .git/objects/前两个字符/剩余38个字符,将哈希值拆分为目录和文件名,以防止单个目录包含过多文件。

对象的哈希值决定了其存储位置。相同的文件产生相同的哈希值,并且只存储一次,从而减少重复,同时使 Git 能够高效地管理数百万个对象。

基于哈希值的目录分片可以实现快速的对象查找,并在仓库增长时保持文件系统的性能。

# 运行一个 Git 版本控制命令。
git hash-object -w hello.txt
# 搜索符合条件(名称、类型、大小、日期等)的文件和目录。
find .git/objects -type f
# 列出文件和目录。-l 显示详细信息(权限、大小、日期)。
ls .git/objects

3. 使用 cat-file 检查 Blob

git cat-file -t 显示对象的类型(如 blob),而 git cat-file -p 以人类可读的格式打印对象的内容。将示例哈希值替换为你环境中生成的哈希值;如果文件内容相同,输出也将相同。

blob 对象只存储文件的内容。它不包含文件名、目录路径或提交历史。这就是为什么多个具有相同内容的文件可以引用同一个 blob 对象。文件名和目录结构分别存储在 tree 对象中。

cat-file 等底层 Git 命令可以让我们深入了解 Git 的内部数据模型,并有助于故障排除、数据恢复和高级仓库管理。

# 运行一个 Git 版本控制命令。
git cat-file -t 9f4d96d5b00d98959ea9960f069585ce42b1349a
# 运行一个 Git 版本控制命令。
git cat-file -p 9f4d96d5b00d98959ea9960f069585ce42b1349a

提交、树与快照

暂存并提交你的文件,然后观察 Git 如何使用三种核心对象类型(blob、tree 和 commit)来表示项目快照。

在这种结构中,blob 存储文件内容,tree 表示目录层次结构,commit 则通过作者、时间戳以及对根 tree 对象的引用等元数据将所有内容联系在一起。正是这种对象模型使得 Git 能够高效地进行版本跟踪和历史重建。

1. 暂存并创建第一个提交

配置你的 Git

查看原文