Index 与暂存区¶
很多初学者最容易误解 Git 的地方,就是 add。
他们往往把 add 理解成“把文件提交到历史里”,其实不是。
add 做的事情更准确地说是:
把当前文件状态写入对象库,并把它登记到暂存区。
而 Git 的暂存区,最重要的实现载体就是 index 文件。
先建立一个最重要的脑内模型¶
先记住这张图:
flowchart LR
WT[工作区\n你正在编辑的文件]
IDX[暂存区 / Index\n下一次提交候选快照]
HEAD[HEAD 提交\n上一次已提交快照]
WT -->|git add| IDX
IDX -->|git commit| HEAD
HEAD -.->|git checkout / git switch / git reset| WT
WT -.->|git status 比较| IDX
IDX -.->|git status 比较| HEAD
这张图的意思很简单:
- 你改文件,是在工作区改;
git add是把当前文件状态放进暂存区;git commit是把暂存区内容变成新的提交;git status会帮你看清这几层状态之间的差异。
为什么 Git 需要暂存区¶
如果没有暂存区,Git 只能在“当前工作区”和“上一次提交”之间做简单比较。
这样会失去一个非常重要的能力:
你不能明确决定“这次提交到底要提交哪些变化”。
有了暂存区之后,Git 才拥有三层状态:
HEAD:上一次提交对应的快照;index:准备提交的快照;- working tree:你正在编辑的文件。
这三层分开以后,Git 才能表达:
- 已修改但未暂存;
- 已暂存待提交;
- 已提交;
- 有冲突待解决。
Index 不只是“一个列表”¶
很多教学材料会把暂存区说成“一个待提交列表”。这个说法入门时可以用,但从实现上看太弱了。
Git 的 index 是一个二进制文件,并且在这个项目里要求按 Index V2 规范读写。
它里面不仅有路径和对象 ID,还会记录:
- 时间戳;
- 设备和 inode 信息;
- mode;
- uid / gid;
- 文件大小;
- stage 位;
- 路径长度;
- checksum。
这就是为什么 index 更像“一个二进制状态机”,而不是“待办列表”。
git add 到底发生了什么¶
当你执行 add 时,大体发生的是:
- 读取工作区文件内容;
- 把文件内容编码成 blob 对象;
- 计算对象 ID;
- 把 blob 写入对象库;
- 在 index 中新增或更新这个路径对应的条目。
所以 add 不是“创建 commit”,而是“把工作区当前状态登记到下一次提交候选快照里”。
git add 平时到底怎么用¶
对初学者来说,最实用的理解方式是:
git add 的意思不是“提交文件”,而是:
“把这个文件当前的内容,放进下一次提交候选集合里。”
常见用法有三种:
暂存单个文件¶
git add hello.txt
当你只想把某一个文件的当前版本放进暂存区时,用这个。
暂存多个指定文件¶
git add file1.txt file2.txt
当你只想把一部分文件放进去,而不是全部一起提交时,用这个。
暂存当前目录下的全部变化¶
git add .
这个很方便,但也最容易把你没准备好的内容一起放进去。
所以新手最好在 git add . 之后立刻跑一次 git status,确认暂存结果是不是自己想要的。
暂存区 / 缓存区 在日常使用里意味着什么¶
很多人第一次听“暂存区”会觉得抽象。
其实它解决的是一个很现实的问题:
你改了很多文件,但你不一定想把它们一次全提交。
暂存区就是 Git 给你的“提交前缓冲层”。
它让你可以表达:
- 这个文件已经准备好进下一次提交;
- 这个文件我还在改,先别提交;
- 这个冲突还没解决,不能提交。
所以 git add 和 git commit 之间,故意隔着一个状态层。
一个最小使用例子¶
你可以按这个最小流程理解:
echo "hello" > hello.txt
git status
git add hello.txt
git status
git commit -m "add hello"
git status
这个流程里,状态变化应当是:
- 你刚改完文件时,它还是“未暂存”;
- 你执行
git add后,它变成“已暂存待提交”; - 你执行
git commit后,工作区、暂存区和HEAD又重新对齐。
这就是为什么很多时候,学 Git 最好的方法不是只看命令,而是反复观察 git status 的变化。
为什么建议新手反复看 git status¶
只要你对 git add 有点不确定,就按这个习惯来:
- 改完文件先看一次
git status git add之后再看一次git commit之前再看一次
这样你就不会把暂存区当成黑盒,而能一步步看见 Git 到底记住了什么。
为什么 status 需要三方比较¶
理解 index 后,你就会知道为什么 status 不是简单的“文件变了没”。
它其实至少要比较三件事:
HEADvsindex- 看哪些变化已经暂存但还没提交;
indexvs working tree- 看哪些文件改了但还没暂存;
- working tree vs index 之外
- 找出未追踪文件。
这就是为什么 Git 的状态显示能分得那么细。
merge 冲突为什么也要改 index¶
index 不只是为 add 和 commit 服务。
在 merge 冲突时,index 还会进入多阶段状态:
- Stage 1:base
- Stage 2:ours
- Stage 3:theirs
这意味着:
- 冲突不是只靠工作区里的
<<<<<<<文本标记; - index 也在元数据层保留了冲突三方信息;
- 只有用户解决冲突并重新
add,这些 stage 才会回到正常状态。
在 python-git-reproduction 里的实现意义¶
这个项目明确要求实现 Git Index V2,而不是自己发明一个简化文本格式。
原因是:
- 这样才更接近 Git 真正的工作方式;
status、write-tree、read-tree、merge conflict stage 才能讲清楚;- 项目也更有教学价值。
如果你真正理解了 index,Git 就不再只是“会存历史的文件夹”,而是一个拥有清晰状态层次的版本控制系统。