1. 为什么“管理修改”是Git最核心也最容易被忽略的概念
很多人学Git,第一步就是照着教程敲 git init、git add、git commit,三条命令走下去,感觉也没多难。但真正开始写项目、改代码之后,第一个让人抓狂的问题就来了:为什么我明明改了一个文件,git status 里却显示两个状态?为什么我 git add 过了,git diff 还能看到内容?为什么我 commit 完了,还有人告诉我“你还有修改没提交”?
这些问题全都指向同一个根节点——Git 管理的不是文件,而是修改(changes)。
文件只是修改的载体。Git 从头到尾记录的是“这个文件从某个版本到另一个版本之间,发生了什么变化”。你改了一行、删了一行、移动了一段,这些都是修改。新增文件和删除文件,本质上也是修改的一种。理解了这一点,再看 Git 的一切操作,都会变得非常顺。
我们先抛开命令,用一个生活的例子来建立直觉。想象你在一张纸上写文章,每写一个版本就复印一份存档。但你不会重新抄写整篇文件,你只需要记录“第二版相比第一版,第三段第二句话被替换了”。Git 也是这样:它保存的是每个提交之间的差异,而不是每个文件的全量快照(虽然底层存储会优化,但逻辑上就是如此)。
这个视角一旦建立起来,“管理修改”这个词就不再是一个空泛的概念,而是你每天执行的那些操作的总称:查看修改、暂存修改、提交修改、撤销修改、合并修改。下面我会按照这个逻辑,把 Git 中所有跟修改相关的操作拆开来讲,每一步都讲清楚为什么这样做。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 看清你的修改:git status 和 git diff 三兄弟
2.1 git status 是你理解一切的起点
几乎所有跟修改打交道的时刻,第一件事都是跑 git status。它会告诉你工作区、暂存区和当前 HEAD 版本之间的差异状态。但很多新人只看到“红色、绿色”就结束了,根本没读懂 status 真正想表达的信息。
我以实际输出为例:
code复制$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: README.md
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: src/main.py
你看,这里有两条关键信息:
Changes to be committed:这些修改已经放进暂存区,下一次 commit 会带上它们。也就是“待提交的修改”。Changes not staged for commit:这些修改还只存在于工作区,没有被暂存。也就是“未暂存的修改”。
注意,README.md 和 src/main.py 都叫 modified,但一个在暂存区里,一个在暂存区外。同一个文件完全可能同时出现在两个区域——因为你改了一部分,add 了一部分,然后又改了一部分。这就是 Git 管理修改的第一个经典困惑:一个文件的“修改”不是铁板一块,而是可以拆分成多段的。
2.2 git diff 三个变体的使用场景
理解了 status 之后,你自然会问:那这些修改具体改了什么?这时候就要用 git diff。很多人不知道,git diff 有多个变体,每个变体回答的问题完全不同。
| 命令 | 比较的对象 | 回答的问题 |
|---|---|---|
git diff |
工作区 vs 暂存区 | 我从上次 add 之后,又改了哪些内容? |
git diff --staged(等价于 --cached) |
暂存区 vs 当前版本库 | 我准备提交的内容有哪些? |
git diff HEAD |
工作区 vs 当前版本库 | 自上次提交以来,我所有未提交的改动(含暂存和未暂存)有哪些? |
这三个命令的区别是很多人的盲区。我见过不少同事,跑 git diff 发现没有输出,就以为自己的改动丢了,实际上是因为改动早已 git add,该用 git diff --staged 才能看到。
具体来说:
- 你刚改完文件但还没 add,
git diff能看到变化。 - 你执行了
git add,此时git diff就变成空白,但git diff --staged能看到已经在暂存区里的改动。 - 你继续改同一个文件,
git diff又能看到了——它显示的是新一次修改相对暂存快照的差异。
这三者的关系用一句话概括:git diff 永远指向“更近的那个参照物”之差。工作区对比暂存区,暂存区对比版本库,工作区对比版本库。
2.3 一个真实场景,完整追踪一次修改
假设你有一个 app.py,当前已经提交过一版,内容是:
python复制print("hello")
现在你做了两次修改:先加了 import os,执行了 git add app.py,随后又加了一行 print("world")。
此时整条链路是这样的:
code复制
