🔬 第十三章:Git 内部原理 —— 幕后的秘密
Git 的三种对象
Git 的核心其实非常简单——它本质上是一个键值对数据库。所有的数据都存储为三种对象:
text
Git 的三种核心对象:
blob(数据对象):存储文件内容
tree(树对象): 存储目录结构(文件名 + blob 的映射)
commit(提交对象):存储提交信息(作者、时间、说明 + tree 的指针)
它们的关系:
commit → tree → blob
│ │ │
│ │ └── 文件的实际内容
│ └── 目录结构和文件名
└── 提交元数据 + 指向上一个 commit 的指针bash
# 查看 Git 对象
git cat-file -t HEAD
commit
# 查看 commit 对象的内容
git cat-file -p HEAD
tree a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7
parent b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1
author 小明 <xiaoming@example.com> 1624182600 +0800
committer 小明 <xiaoming@example.com> 1624182600 +0800
feat: 添加欢迎语
# 查看 tree 对象
git cat-file -p a8b9c0d1
100644 blob f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6 index.html
100644 blob e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5 style.css
# 查看 blob 对象
git cat-file -p f7a8b9c0
<!DOCTYPE html>
<html>
<head>
<title>我的第一个网站</title>
</head>
<body>
<h1>Hello, Git!</h1>
<p>欢迎来到我的网站!</p>
</body>
</html>SHA-1 哈希 —— Git 的身份证系统
Git 用 SHA-1 哈希值来标识每个对象。同样的内容永远产生同样的哈希值。
bash
# Git 用下面的方式计算 blob 的哈希值
# 格式:blob <内容长度>\0<实际内容>
echo -n "Hello, Git!" | git hash-object --stdin
a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0(示例,实际值因环境而异)
# 每次计算结果都一样(确定性)
echo -n "Hello, Git!" | git hash-object --stdin
a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0(示例,实际值因环境而异)
# 改一个字符,哈希值完全不同(雪崩效应)
echo -n "Hello, git!" | git hash-object --stdin
b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1(示例,实际值因环境而异)Refs —— 可读的指针
SHA-1 哈希值对人类来说太难记了,所以 Git 用"引用"(refs)来给它们起名字。
bash
# 分支就是指向某个 commit 的指针
cat .git/refs/heads/main
f4e5d6c7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3
# HEAD 指向当前分支
cat .git/HEAD
ref: refs/heads/main
# 远程分支的引用
cat .git/refs/remotes/origin/main
a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6
# 整个引用关系链:
# HEAD → refs/heads/main → commit hash → tree → blobsPackfiles —— Git 的压缩术
Git 虽然保存的是快照,但它并不是每次都完整复制所有文件。当文件很多时,Git 会自动打包压缩。
bash
# 手动触发垃圾回收和打包
git gc
Enumerating objects: 42, done.
Counting objects: 100% (42/42), done.
Delta compression using up to 8 threads
Compressing objects: 100% (30/30), done.
Writing objects: 100% (42/42), done.
Total 42 (delta 12), reused 42 (delta 12), pack-reused 0
# 查看仓库大小
git count-objects -vH
count: 0
size: 0 bytes
in-pack: 42
packs: 1
size-pack: 8.50 KiB
prune-packable: 0
garbage: 0
size-garbage: 0 bytes