☰
status、diff 和 ignore 其实在比较什么:HEAD、index、worktree 三份状态
2026/10/1 6:37:36 网站建设 项目流程

第 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 diffindex vs worktree我工作区还有哪些没 add 的改动
mgit diff --cachedHEAD 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.c

status 主线:

读取 HEAD commit 的 tree 读取 index 扫描 worktree 应用 ignore 规则 比较 HEAD/index/worktree 输出 staged、unstaged、untracked

diff 主线:

默认 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 文件要被忽略。

这一篇先记住

  1. status 的核心是三份状态比较。
  2. staged = HEAD 和 index 不一样。
  3. unstaged = index 和 worktree 不一样。
  4. untracked = 文件只在 worktree,没进 index。
  5. 默认 diff 是 index vs worktree。
  6. cached diff 是 HEAD vs index。
  7. ignore 主要影响 worktree 扫描。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询