Skip to content

🔬 第十三章: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 → blobs

Packfiles —— 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