Docs Library
Git 三层模型:工作区、暂存区与对象库
解释 Git 最核心的三层架构——工作区(Working Tree)、暂存区(Index)与对象库(Objects Database)——以及它们如何协同工作。
- 想先理解历史图再看命令的人
- 知道提交不是文件快照列表那么简单
- 把概念页当命令说明页使用
一句话理解
Git 的核心不是"文件管理系统",而是一个三层快照引擎:你在工作区编辑文件,通过暂存区准备快照,最终提交到对象库形成永久历史。
理解这三层关系,是掌握所有 Git 命令的关键。很多"为什么我的提交没有包含最新修改"这类问题,本质上都源于对这三层边界的混淆。
第一层:工作区(Working Tree)
工作区是你看得见、摸得着的项目文件。就是你在编辑器里打开、修改、保存的那些文件。
工作区的特征
- 它是你磁盘上的实际文件
- 你可以用任何编辑器修改它们
- 这些修改 Git 不会自动记录
git status会显示哪些文件在工作区被修改过
示例
# 查看工作区有哪些修改
git status
# 查看具体修改内容
git diff
当你修改了一个文件但还没做任何 Git 操作时,这个修改只存在于工作区。
第二层:暂存区(Index / Staging Area)
暂存区是 Git 独有的概念——它是一个中间缓冲层,用来精确控制下一次提交包含哪些内容。
暂存区的特征
- 它不是一个你能直接看到的目录
- 它存储在当前项目的
.git/index文件中 - 它是下一次提交的精确蓝图
- 你可以反复修改暂存区的内容,而不影响工作区或历史
为什么需要暂存区
很多版本控制系统没有这个概念,它们只有"修改 → 提交"两步。Git 引入暂存区,让你可以在提交前做三件重要的事:
- 拆分一次混乱的开发为多个逻辑提交:你可以把 bug 修复和重构分开提交
- 审查即将进入历史的内容:用
git diff --staged最后确认 - 保留实验性改动:有些改动还在试验阶段,不想混进正式提交
示例
# 把修改加入暂存区
git add src/login.ts
# 按代码块交互式选择
git add --patch
# 查看暂存区里有什么
git diff --staged
# 从暂存区撤回
git restore --staged src/login.ts
第三层:对象库(Objects Database)
对象库是 Git 的永久历史存储,位于 .git/objects/ 目录下。一旦内容写入这里,除非执行垃圾回收,否则它会一直存在。
对象库的特征
- 它存储在
.git/objects/中 - 内容按 SHA 哈希值组织,相同内容只存一份
- 包含三种对象类型:blob(文件内容)、tree(目录结构)、commit(提交记录)
- 写入后不可变——你无法修改一个已有的对象
三种对象类型
| 类型 | 作用 | 类比 |
|---|---|---|
| blob | 存储文件内容(不含文件名) | 一个文件的"内容本身" |
| tree | 存储目录结构和文件名 | 一个文件夹的"目录清单" |
| commit | 存储提交信息、作者、父提交、指向 tree | 一次"提交记录" |
示例
# 查看对象库中的对象
git cat-file -p HEAD^{tree}
# 查看某个 blob 的内容
git cat-file -p <blob-hash>
# 查看对象类型
git cat-file -t <hash>
三层之间的数据流动
理解三层模型,最关键的是理解数据如何在它们之间流动:
工作区 ──git add──→ 暂存区 ──git commit──→ 对象库
↑ ↓ ↓
└──git restore──────┘ │
└──git checkout / git restore──────────────┘
从上到下(正向流程)
- 编辑文件 → 修改只存在于工作区
- git add → 选中的修改复制到暂存区
- git commit → 暂存区快照写入对象库
从下到上(恢复流程)
git restore <file>→ 用暂存区的内容覆盖工作区git checkout <commit> -- <file>→ 用对象库的某个版本覆盖工作区- git reset HEAD → 清空暂存区,但不改变工作区
用三层模型理解常见命令
git status 告诉你什么
git status 的输出实际上是在比较这三层之间的差异:
- "Changes not staged for commit" → 工作区 vs 暂存区 的差异
- "Changes to be committed" → 暂存区 vs 最新提交 的差异
- "Untracked files" → 工作区中有但完全不在 Git 管理中的文件
git diff 的三种模式
# 工作区 vs 暂存区(默认)
git diff
# 暂存区 vs 最新提交
git diff --staged
# 工作区 vs 最新提交(跳过暂存区)
git diff HEAD
git reset 的三层影响
| 模式 | HEAD | 暂存区 | 工作区 |
|---|---|---|---|
--soft | 移动 | 不变 | 不变 |
--mixed(默认) | 移动 | 重置 | 不变 |
--hard | 移动 | 重置 | 重置 |
常见误区
"我改了文件,为什么提交里没有?"
因为你只修改了工作区,还没有 git add 把修改放入暂存区。
"我 add 过了,为什么后续修改没包含?"
git add 只是把那一刻的快照放入暂存区。之后你对工作区的新修改需要再次 git add。
"git add . 有什么问题?"
它会把工作区所有修改都加入暂存区,包括你还没准备好的调试代码、临时日志等。更安全的做法是明确指定文件或使用 --patch。
一个实用心智模型
把 Git 想象成拍照:
- 工作区 = 你在布置场景
- git add = 你在取景器里选择构图
- git commit = 按下快门,照片存入相册
- git diff = 对比当前场景和上一张照片的差别
- git restore = 重新摆回上一张照片里的样子
这个模型帮你记住:暂存区是你提交前的"最后检查站",不是"自动保存"。
继续学习建议
理解三层模型后,建议继续学习:
git add—— 如何精确控制暂存内容git commit—— 如何把暂存快照变成永久历史git diff—— 如何比较不同层之间的差异Git 内部对象—— blob、tree、commit 的详细结构