你先暂存了一次改动,又继续编辑同一个文件。此时提交会带走哪一版?为什么 git diff 只显示后半段,而 git diff --cached 显示另一段?

答案取决于三个不同位置:工作区是正在编辑的文件,暂存区是下一次提交准备采用的快照,HEAD 在本文的普通分支场景中指向当前提交。本文用一个文件把它们分开,练习只读检查、按明确路径暂存和提交前审查。不涉及删除、清理、强推或历史重写。

先建立模型

三个位置不是三份备份

编辑不会自动更新暂存区;暂存也没有产生历史提交。

再审查差异

每条 diff 有两个端点

先说清比较什么,再解释空输出代表什么。

最后提交

审查下一次快照

提交前检查暂存区内容、相关测试与敏感信息,不只看编辑器。

HEAD 当前提交暂存区 下一次快照工作区 正在编辑

三个位置怎样改变

Pro Git 的基础章节说明,修改后的文件可以被选择性暂存,然后写入提交。git add 保存的是执行那一刻的内容;之后再编辑,暂存区不会跟着变化。

以下假设仓库已经有一次提交,文件 note.txt 已被跟踪,使用普通分支,没有合并冲突或子模块。HEAD 是当前提交的引用,不是一个普通文件目录;暂存区是 Git 的索引,不应手动编辑其内部文件。

一个文件的三版内容

通过编辑器创建和修改练习文件,不在命令中覆盖已有项目文件。初始提交的两行是 title=demo 和 mode=basic。

步骤 HEAD 中的 mode 暂存区中的 mode 工作区中的 mode 短状态
初始提交后 basic basic basic 无输出
编辑为 staged basic basic staged 空格 M
对 note.txt 执行 add basic staged staged M 空格
再编辑为 working basic staged working MM
普通 commit 后 staged staged working 空格 M

最后一步的提交内容是 mode=staged,不是编辑器里当前的 mode=working。普通 git commit -m ... 使用暂存区;本文不使用会改变暂存行为的 -a 或路径限定提交形式。

独立Git实验中状态为MM;HEAD保存basic,暂存区保存staged,工作区保存working
同一时刻的三版内容。 在独立教学仓库实际执行命令后,将输出排版为截图;仅含 note.txt 的合成内容,不是 Visual Studio 或线上项目界面。查看原图

三种 diff 的比较端点

在表中 MM 的那一刻运行下面三条命令。-- 分隔选项与路径;练习只涉及这个明确的文件名。

1
2
3
git diff -- note.txt
git diff --cached -- note.txt
git diff HEAD -- note.txt
命令 左侧旧内容 右侧新内容 本例中的变化
git diff 暂存区 工作区 staged → working
git diff –cached HEAD 暂存区 basic → staged
git diff HEAD HEAD 工作区 basic → working

这些语义见Git diff 官方文档。--staged 是 --cached 的同义选项。普通 git diff 为空,只说明工作区与索引的这一类差异为空,并不表示暂存区没有改动;尚未跟踪的新文件通常也不会出现在普通 diff 中。

三份真实diff输出分别显示staged到working、basic到staged、basic到working
端点不同,差异就不同。 三条命令在同一个 MM 状态下执行;可放大核对每组减号旧行和加号新行,再与上方比较表对应。查看原图

差异中的减号和加号表示旧行与新行,不表示程序运行时会“先删文件再建文件”。但删除路径确实可能被暂存,所以在真实项目中仍要检查状态和变更类型。

短状态的两列不要交换

1
2
git status --short
git status --porcelain=v1 --untracked-files=all

在本文无冲突的跟踪文件场景,第一列表示索引相对 HEAD 的变化,第二列表示工作区相对索引的变化。因此 MM note.txt 的意思是已经暂存了一版修改,工作区又有尚未暂存的修改。

状态记号 本文场景下的意思 接下来先检查
空格 M 工作区修改,索引未修改 git diff
M 空格 已暂存修改,工作区与索引一致 git diff –cached
MM 两个位置都有修改 分别看两份 diff
?? 未跟踪文件 文件正文及是否应纳入版本控制

Git status 文档还定义了未合并、重命名、子模块和忽略文件等状态,不能把这张简表扩展成所有场景的解释。--porcelain=v1 适合稳定的机器处理;若程序要解析任意文件名,应进一步使用 -z 的 NUL 分隔输出,而不是按空格切割。

提交前的安全检查顺序

先使用只读命令确认当前仓库、分支和差异。将 note.txt 换成经过确认的目标路径;这组命令不是让你在未知仓库直接照抄提交。

1
2
3
4
5
6
7
8
9
10
git rev-parse --show-toplevel
git branch --show-current
git status --short
git diff --stat
git diff --check
git diff -- note.txt
git add -- note.txt
git diff --cached --stat
git diff --cached --check
git diff --cached -- note.txt

git diff --check 可以检查某些空白错误和冲突标记,但不是语法检查、测试或敏感信息扫描。还要执行项目约定的测试,核查所有已暂存路径的正文;不要因为本次只 add 一个文件,就忽略原先已经暂存的其他文件。

通过检查后才执行普通提交,并核对历史快照与剩余工作区改动:

1
2
3
4
5
git commit -m "Explain staging snapshot"
git log -1 --oneline
git show --stat --oneline HEAD
git show HEAD:note.txt
git status --short

如果刚才是在 MM 状态提交,最后仍会看到 note.txt 的未暂存改动。这不是提交失败,而是提交保存了你审查过的暂存快照。

测试通过也要看测试的是哪一版

大多数本地测试读取工作区,不直接读取索引。若文件处于 MM,测试可能验证的是 working 版,提交却包含 staged 版,不能直接说“提交内容已测试通过”。

一个易执行的习惯是完成目标修改后重新暂存明确路径,再检查相关路径没有未暂存差异,运行相关测试并复核暂存 diff。它不是让你把其他人的改动一并暂存。部分暂存、多文件依赖或测试会修改文件时,应采用团队认可的独立验证环境,验证实际提交快照。

本地提交不等于远端发布

本文的实验不访问远端,不推送,也不修改凭据。git commit 只产生本地提交,git log 看到的本地记录不能证明远端已更新。真实发布应遵循项目规定的构建、扫描、远端分歧检查、推送和线上验证顺序。

不要为了让状态“变干净”而清理目录、批量删除文件或强制改写历史。遇到不属于本次任务的改动,先保留并确认归属。状态是需要解释的信息,不是必须消灭的提示。

复盘练习 预测提交会保存哪一版

在独立练习仓库中走完三版内容的步骤。提交前用三种 diff 预测结果,提交后用 git show HEAD:note.txt 检查。再解释:为什么普通 diff 为空和工作区完全干净不是同一句话?为什么 MM 时运行工作区测试不能证明暂存快照通过?

继续阅读

配合Visual Studio 编辑与调试快捷键,把编辑、验证与审查连成习惯;若在维护评测题集,接着阅读RAG 回归评测。更多入口见开发工具分类和资源阅读路径。