第 5 篇:status、diff 和 ignore 其实在比较什么
上一篇我们把 index 讲清楚了:
HEAD tree = 上一次提交的完整快照 index = 下一次准备提交的清单 worktree = 当前真实文件目录这一篇讲status和diff。
它们不是魔法,本质就是比较这三份状态。
先讲人话
如果你只记一句话:
status = 同时看 HEAD、index、worktree 三份东西有没有不一样具体比较两次:
HEAD tree vs index -> Changes to be committed index vs worktree -> Changes not staged for commit所以:
已经 add 的修改 = HEAD 和 index 不一样 还没 add 的修改 = index 和 worktree 不一样这也是很多人刚学 Git 时最困惑的点:
为什么我改了文件,diff 有东西; add 之后,diff 反而没东西了?答案不是修改消失了,而是默认diff换了比较对象。
真实运行:没 add 前
创建第一次提交:
mkdir-p/tmp/mgit-article-05cd/tmp/mgit-article-05 /mnt/e/mini-git/build/mgit initprintf"one\n">a /mnt/e/mini-git/build/mgitadda /mnt/e/mini-git/build/mgit commit-m"v1"此时三份状态一样:
HEAD: a = one index: a = one worktree: a = one现在修改工作区:
printf"two\n">a这只改了普通文件,没有改 index,也没有创建 commit。
现在三份状态是:
HEAD: a = one index: a = one worktree: a = two执行:
/mnt/e/mini-git/build/mgit status你会看到a在Changes not staged for commit下面。
原因是:
index 和 worktree 不一样。执行默认 diff:
/mnt/e/mini-git/build/mgitdiff它比较的是:
index vs worktree所以没 add 前,默认 diff 能看到a的修改。
add 后状态怎么变
执行:
/mnt/e/mini-git/build/mgitadda现在三份状态变成:
HEAD: a = one index: a = two worktree: a = two执行 status,a会进入:
Changes to be committed因为:
HEAD 和 index 不一样。此时默认 diff 看不到东西,因为:
index 和 worktree 一样。但 cached diff 能看到:
/mnt/e/mini-git/build/mgitdiff--cached因为--cached比较的是:
HEAD tree vs index所以这不是 Git 把你的修改吃掉了,而是你已经把工作区修改登记到了 index,默认 diff 不再把它当成“未暂存修改”。
untracked 是什么
如果你新建一个文件:
printf"new\n">b但没有 add。
那它不在 HEAD,不在 index,只在 worktree。
所以 status 会把它归类为:
Untracked files人话:
Git 在工作区看见了 b,但 index 里还没有登记它。这个分类很重要。它和“文件被修改了”不是一回事。
modified = 这个路径 Git 已经认识,只是内容版本变了 untracked = 这个路径 Git 还不认识ignore 在哪一层生效
ignore 主要影响工作区扫描。
比如.gitignore里写:
*.log意思是:
扫描 worktree 时,遇到 .log 文件可以忽略,不把它当成 untracked 报出来。它不改变 commit,也不改变 objects。
所以 ignore 不是“删除文件”,也不是“禁止 Git 存这个文件”。它只是告诉 Git:
默认不要把这些工作区文件纳入状态检查和 add 扫描。如果一个文件已经被追踪,ignore 通常不会让它从历史里消失。
这是另一个常见误解:
已经 add/commit 过的文件,再写进 .gitignore,status 仍然可能看到它的修改。因为它已经在 index 或历史快照里登记过了。ignore 主要处理的是工作区里“还没被 Git 认识”的路径。
diff 的两个方向
总结一下:
| 命令 | 比较对象 | 人话 |
|---|---|---|
mgit diff | index vs worktree | 我工作区还有哪些没 add 的改动 |
mgit diff --cached | HEAD vs index | 我下一次 commit 准备提交哪些改动 |
mgit status | 两组都看 | staged、unstaged、untracked 都列出来 |
所以判断一个改动在哪里,不要只问“文件改没改”,而要问:
这个改动在 worktree? 在 index? 还是已经进 HEAD 了?这三个位置搞清楚,很多 Git 命令都会变得顺。
源码入口
主要文件:
src/commands/cmd_status.c src/commands/cmd_diff.c src/core/ignore.c src/core/tree.c src/core/index.cstatus 主线:
读取 HEAD commit 的 tree 读取 index 扫描 worktree 应用 ignore 规则 比较 HEAD/index/worktree 输出 staged、unstaged、untrackeddiff 主线:
默认 diff:读取 index,再读 worktree 文件,比较两者 --cached:读取 HEAD tree,再读 index,比较两者ignore 主线:
读取 ignore 规则 扫描工作区时判断路径是否应该跳过面试怎么说
Git status 不是简单看文件是否改过,而是比较 HEAD、index 和 working tree。 HEAD 到 index 的差异是 staged changes,index 到 working tree 的差异是 unstaged changes。 diff 默认看 index 和 working tree,diff --cached 看 HEAD 和 index。 ignore 主要影响工作区扫描时哪些 untracked 文件要被忽略。这一篇先记住
- status 的核心是三份状态比较。
- staged = HEAD 和 index 不一样。
- unstaged = index 和 worktree 不一样。
- untracked = 文件只在 worktree,没进 index。
- 默认 diff 是 index vs worktree。
- cached diff 是 HEAD vs index。
- ignore 主要影响 worktree 扫描。