研究 Git:工作区、暂存区与 HEAD 三份状态

文件改完后直接提交,有时会漏掉刚写的内容。

edyang · 2015-01-05

文件改完后直接提交,有时会漏掉刚写的内容。原因是 Git 提交的是暂存区快照,而不是此刻整个工作目录。

本文使用Git 2.x 与独立演示仓库。Git(分布式版本管理)保存提交对象和引用,工作区、暂存区与提交历史不是同一份状态。每次操作先判断它改变哪个区域,再决定是否适合当前协作情境。

1. 从现象推到机制

HEAD 通常指向当前分支的最新提交,暂存区描述下一次提交内容,工作区允许继续编辑。同一个文件可以同时有已暂存和未暂存修改。理解这点,就能主动组织一组完整变化,而不是被文件保存时间牵着走。

研究 Git:工作区、暂存区与 HEAD 三份状态的处理、反馈与观察关系

图中的四个节点分别表示提交历史、暂存区域、工作目录、差异比较。顺着实线检查输入如何变成输出,再沿虚线核对反馈条件。图示表达的是本文讨论的关系,不是实测曲线或现场截图。

2. 最小实现与输入条件

把同一个文件修改两次,在第一次修改后 add,再修改第二次。分别比较工作区差异与暂存差异,最后观察提交包含哪一部分。

1
2
3
4
5
git status
git diff
git add demo.txt
git diff --cached
# 再次编辑后重复比较两种 diff

示例在没有私人资料的临时仓库执行。先看 status、diff 与 log,再运行改变状态的命令;共享历史优先新增提交表达修复,不用改写历史掩盖已经发生的变化。

3. 用对照区分原因

下表列出需要主动改变的条件。预期行为来自机制分析,复现时还需记录运行环境和原始输出,不能将预期当作已经测得的结果。

改变的条件 需要验证的行为
暂存后继续编辑 新编辑不会自动进入已暂存快照
只提交暂存内容 未暂存部分仍留在工作区
一次修改包含两类目的 可拆为两个语义清楚的提交

对照 1:暂存后继续编辑

新编辑不会自动进入已暂存快照。观察时记录cached 与普通 diff 的差异。把这个条件与基准情形分开,有助于判断变化来自哪一层;若得到相反结果,应先核对前提,再修改实现。

对照 2:只提交暂存内容

未暂存部分仍留在工作区。观察时记录提交树与当前文件。把这个条件与基准情形分开,有助于判断变化来自哪一层;若得到相反结果,应先核对前提,再修改实现。

对照 3:一次修改包含两类目的

可拆为两个语义清楚的提交。观察时记录每个提交是否能独立解释。把这个条件与基准情形分开,有助于判断变化来自哪一层;若得到相反结果,应先核对前提,再修改实现。

4. 适用条件与取舍

暂存区不是备份服务,未提交内容仍可能丢失。组织提交之前也要保留必要工作成果,避免用清理命令代替理解当前状态。

一次正常运行只覆盖了这组输入。继续扩展前,应把上面的异常对照保留下来,尤其关注“一次修改包含两类目的”这一分支。后续方案若改变了图中任意一处依赖,原来的判断就要重新验证,不能仅凭界面相似或返回值相同沿用结论。

同月延伸

参考资料

  1. Pro Git 第二版
  2. Git 2.2.0 版本说明