Appearance
对象存储原理
Git 的底层是一个内容寻址文件系统(content-addressable filesystem)。理解对象存储的原理,有助于深入理解 Git 的数据模型。
内容寻址文件系统
Git 根据内容来存储和查找数据,而不是文件名或路径。相同内容的数据只存储一份。
bash
# 演示内容寻址
echo "Hello, Git!" | git hash-object --stdin
# 8ab686eafeb1f44702738c8b0f24f2567c36da6d
# 无论存储多少次,相同内容的哈希永远相同
echo "Hello, Git!" | git hash-object --stdin
# 8ab686eafeb1f44702738c8b0f24f2567c36da6d(完全一样)
# 写入对象存储(-w 参数)
echo "Hello, Git!" | git hash-object --stdin -w
# 8ab686eafeb1f44702738c8b0f24f2567c36da6d松散对象与打包对象
松散对象(Loose Objects)
每个对象单独存储为一个文件,路径由 SHA-1 的前 2 位(目录)和后 38 位(文件名)组成:
.git/objects/8a/b686eafeb1f44702738c8b0f24f2567c36da6d存储格式:
原始内容 = "<类型> <长度>\0<实际内容>"
存储内容 = zlib 压缩(原始内容)例如,Blob 对象 "Hello, Git!\n":
原始内容 = "blob 13\0Hello, Git!\n"
文件内容 = zlib压缩("blob 13\0Hello, Git!\n")松散对象的问题:
- 文件数量多,文件系统性能下降
- 没有利用对象间的相似性(全量存储,而非差异)
- 网络传输效率低
打包对象(Packed Objects)
当松散对象过多,或执行 git gc、git push 时,Git 会将多个对象打包成 Pack 文件:
.git/objects/pack/
├── pack-abc123.idx ← 索引文件(快速查找)
└── pack-abc123.pack ← 数据文件(存储压缩数据)打包的优化:
- 利用对象间的相似性,存储差异而非全量
- 减少文件数量,提升文件系统性能
- 更小的存储空间和传输体积
git cat-file -t / -p 查看对象
bash
# 查看对象类型
git cat-file -t abc123
# blob(或 tree、commit、tag)
# 查看对象内容(格式化显示)
git cat-file -p abc123
# Blob 对象:显示文件内容
git cat-file -p HEAD:README.md
# # My Project...
# Tree 对象:显示目录结构
git cat-file -p HEAD^{tree}
# 100644 blob abc123 README.md
# 040000 tree def456 src
# Commit 对象:显示提交信息
git cat-file -p HEAD
# tree abc...
# parent def...
# author ...
# committer ...
#
# feat: 添加功能
# 查看文件大小
git cat-file -s abc123
# 13(字节)git hash-object 计算哈希
bash
# 计算文件的 SHA-1(不写入存储)
git hash-object README.md
# 计算并写入对象存储(-w)
git hash-object -w README.md
# 从 stdin 读取内容
echo "test content" | git hash-object --stdin
# 指定对象类型
echo "test" | git hash-object --stdin -t blob
echo "test" | git hash-object --stdin -t taggit ls-tree 查看树对象
bash
# 查看 HEAD 指向的根树对象
git ls-tree HEAD
# 100644 blob abc123 README.md
# 040000 tree def456 src
# 100755 blob ghi789 run.sh
# 递归显示所有文件
git ls-tree -r HEAD
# 只显示文件名
git ls-tree -r --name-only HEAD
# 查看特定目录
git ls-tree HEAD src/
# 查看某个提交的特定路径
git ls-tree abc123 -- src/app.jsgit ls-files --stage 查看暂存区
bash
# 查看暂存区中的所有文件
git ls-files --stage
# 100644 abc123... 0 README.md
# 100644 def456... 0 src/app.js
# 格式:<模式> <SHA-1> <冲突阶段> <路径>
# 冲突阶段:0=正常,1=共同祖先,2=当前,3=被合并
# 只显示文件名
git ls-files
# 显示修改的文件
git ls-files -m
# 显示未跟踪的文件
git ls-files -o对象统计和验证
bash
# 统计对象数量和存储大小
git count-objects
# 12 objects, 56 kilobytes
# 详细统计
git count-objects -v
# count: 12(松散对象数)
# size: 56(松散对象大小,KiB)
# in-pack: 1024(打包对象数)
# packs: 2(pack 文件数)
# size-pack: 512(pack 文件大小,KiB)
# prune-packable: 0(可以清除的冗余对象)
# garbage: 0(垃圾对象数)
# size-garbage: 0(垃圾对象大小)
# 验证对象存储的完整性
git fsck
git fsck --full # 更彻底的检查实践:手动创建对象
bash
# 手动创建一个 Blob 对象
echo "Hello, World!" | git hash-object --stdin -w
# abc123...
# 手动创建 Tree 对象(通常由 Git 自动管理)
git mktree << 'EOF'
100644 blob abc123 hello.txt
EOF
# 返回 tree 的 SHA-1
# 手动创建 Commit 对象
git commit-tree <tree-sha> -m "手动创建的提交"
# 返回 commit 的 SHA-1总结
| 操作 | 命令 |
|---|---|
| 计算对象哈希 | git hash-object <file> |
| 写入对象存储 | git hash-object -w <file> |
| 查看对象类型 | git cat-file -t <sha> |
| 查看对象内容 | git cat-file -p <sha> |
| 查看树对象 | git ls-tree HEAD |
| 查看暂存区 | git ls-files --stage |
| 统计对象 | git count-objects -v |
深入理解 Git 的对象存储原理,会让你对 Git 的各种操作(如 reset、rebase、cherry-pick)有更清晰的认识——它们本质上都是在操纵这些对象和引用。