真实开发里,很少有人始终保持“只改一个文件、改完立刻提交”的理想节奏。更常见的情况是:功能代码、顺手补的文档、本地产生的日志、只属于个人环境的配置,一起出现在工作区里。
这时难点已经不是会不会用 git add 和 git commit,而是要先判断清楚:哪些变化应该进入这次提交,哪些应该暂时留在外面。下面按一个完整实验过程展开,带你把这件事看清楚、做稳妥。
提交前后先看哪几个检查窗口
一次提交前后,建议固定从四个角度观察仓库状态:
| 想确认的问题 | 命令 |
|---|---|
| 哪些路径发生了变化,分别位于哪一层 | git status --short |
| 工作区相对暂存区还有哪些具体变化 | git diff |
| 暂存区相对当前提交准备记录什么 | git diff --cached,也可以使用 git diff --staged |
| 提交完成后形成了什么历史 | git log --oneline、git show |
status 负责先定位“哪里变了”,diff 负责解释“具体改了什么”,而 log 与 show 用来核对最终写进历史的结果。把这四个窗口串起来看,提交边界就不会只靠感觉判断。
先搭一个可复现的实验仓库
下面的初始化过程只是为了构造一个可重复操作的现场。如果本地已经有同名目录,建议换一个新名字。
mkdir commit-check-lab cd commit-check-lab git init git config user.name "Git Blog Lab" git config user.email "git-blog@example.invalid" printf "login validationn" > app.txt printf "old documentationn" > README.md git add app.txt README.md git commit -m "chore: initialize commit check lab"
执行完成后,仓库里会有一个初始提交,工作区处于干净状态。这样后面的每一步都更容易观察。
先排除不该进入仓库的文件
很多误提交都不是因为不会提交,而是没有先把“不该跟踪的东西”排出去。先制造两个典型的未跟踪文件:
printf "debug outputn" > debug.log printf "local development configurationn" > .env git status --short
输出会包含:
?? .env ?? debug.log
它们都是工作区中的未跟踪文件。如果这时直接执行 git add .,就很容易把本地日志和环境配置一起放进暂存区。
更稳妥的做法,是先在仓库根目录写入 .gitignore:
printf "*.logn.envn" > .gitignore git status --short
这时 debug.log 和 .env 不再出现在普通状态输出中,只剩下新的 .gitignore:
?? .gitignore
忽略规则本身属于项目约定,应该进入版本历史:
git add .gitignore git commit -m "chore: ignore logs and local environment files"
.gitignore 的两个边界
第一,.gitignore 主要影响尚未被跟踪的路径。一个文件如果已经进过提交,后来再补写忽略规则,并不会自动停止跟踪它。
如果误跟踪的是日志或本地生成文件,可以这样处理:
git rm --cached <文件> git add .gitignore git commit -m "chore: stop tracking generated file"
--cached 会把“从版本控制中删除”的记录放进暂存区,但保留本地工作区文件。目录场景需要配合 -r 使用。执行后最好先用 git status 确认暂存内容,再提交规则调整。
第二,忽略规则并不是凭据泄露后的补救手段。密钥、Token 或密码一旦进入历史,应该立即轮换或作废,再按团队流程处理仓库历史。只删除文件或补写 .gitignore,并不能让已经泄露的值失效。
制造一个混合修改现场
现在构造一个更接近真实开发的状态:代码、文档和日志同时变化。
printf "check empty usernamen" >> app.txt printf "new usage examplen" >> README.md printf "another debug linen" >> debug.log git status --short
输出如下:
M README.md M app.txt
debug.log 仍然存在于本地,只是因为命中了 .gitignore,所以不会出现在普通状态结果中。
git status --short 的文件名前会显示两个位置的状态,也就是常见的 XY 文件名:
XY 文件名
- 第一个位置
X:表示已经进入暂存区的变化; - 第二个位置
Y:表示仍留在工作区、尚未暂存的变化。
当前两个文件显示为 M,说明左侧暂存区为空,右侧工作区有修改,也就是“都改了,但都还没选进下次提交”。
用 diff 看清每个文件到底改了什么
确认路径之后,不要急着 add,先逐个看内容:
git diff -- app.txt
这里会看到新增的 check empty username,它对应的是登录校验逻辑。
再看文档文件:
git diff -- README.md
这里则会看到另一种意图:补充使用示例。
要特别注意,普通的 git diff 比较的是工作区和暂存区,它不会展示普通未跟踪文件的内容。因此检查现场时,不能只盯着 diff,而要先看 status;否则可能漏掉尚未跟踪、也没有被忽略的文件。
只把本次提交需要的变化放进暂存区
这次假设你只想先提交登录校验,不把文档改动混进去,那么就按路径选择真正相关的文件:
git add app.txt git status --short
输出会变成:
M README.md M app.txt
这时 app.txt 的 M 进入左列,说明它已经被放入暂存区;README.md 仍然只存在于工作区,没有被选入这次提交。
提交之前,再检查一次暂存区里的实际内容:
git diff --cached -- app.txt
这里应该只出现与登录校验有关的改动。确认边界正确后,再执行提交:
git commit -m "feat: validate empty username"
一次提交当然可以同时包含代码、测试和必要文档,关键从来不是“只能改一个文件”,而是这些变化能否共同表达一个意图,并且适合一起评审、一起回滚。
提交之后别立刻走,继续核对结果
提交完成后,先回到状态视图:
git status --short
输出应该是:
M README.md
这说明功能改动已经进入历史,而文档修改仍安全地留在工作区。提交不会自动带走那些没有进入暂存区的内容。
然后检查最近几条提交记录:
git log --oneline -3
这条命令适合快速确认最近提交的顺序和说明是否符合预期。
如果想看刚才那次提交到底碰了哪些文件:
git show --stat HEAD
如果想看提交的完整内容:
git show HEAD
HEAD 表示当前检出位置对应的提交;在常见分支场景里,它通常指向当前分支的最新提交。
确认功能提交里没有混入 README.md 之后,再把文档单独提交:
git add README.md git diff --cached -- README.md git commit -m "docs: add usage example" git status
最后的状态应该回到干净状态。
同一个文件里混了两个意图怎么办
按路径执行 git add app.txt,会把这个文件当前的全部变化一次性选入暂存区。但真实开发里,同一个文件也可能同时塞进两个不同目的的修改。
这时就该使用交互式暂存:
git add --patch app.txt
Git 会把变化拆成若干差异块,让你逐块决定是否暂存。没有被选中的部分仍然保留在工作区,不会丢失。
操作结束后,可以分别检查两边的内容:
git diff --cached -- app.txt git diff -- app.txt
第一条查看已经选入下次提交的部分,第二条查看仍留在工作区的部分。如果一个差异块内部仍然混着两个意图,可以继续拆分差异块,或者先回到编辑器把内容整理开,再重新选择。
为什么不把 commit -am 当成默认动作
git commit -am "message"
这个写法会在提交前自动暂存所有已跟踪文件的修改和删除,并提交它们在命令执行当下的内容,但不会纳入普通未跟踪文件。
它的问题也很直接:会跳过“选择路径”和“检查暂存差异”这两个关键动作。如果某个已跟踪文件在你原本准备提交之后又继续被修改,-a 会把这些后续工作区变化一并带进提交,打乱原本已经划好的边界。
只有在所有已跟踪变化都明确属于同一意图,而且你已经确认过现场时,这个快捷方式才算合适。
一次提交前后的稳定检查顺序
git status --short ↓ git diff ↓ 排除不应跟踪的路径,选择本次相关变化 ↓ git diff --cached ↓ git commit ↓ git status --short ↓ git log --oneline / git show
提交前先检查选择,提交后再核对结果。暂存区并不只是机械流程中的中转站,它本质上是在帮你编辑“下一条历史记录”应该长成什么样。
结语
清晰的提交边界,不是靠提交说明临时补救出来的,而是从检查现场、排除无关文件、选择同一意图的变化开始。用 git status --short 看路径,用 git diff 和 git diff --cached 看内容,再用 git log 和 git show 验证结果,基本就能把“只提交该提交的东西”变成稳定习惯。
参考资料:
- Git 官方资料:Recording Changes to the Repository
- Git 官方资料:Viewing the Commit History
- Git 官方文档:git-status
- Git 官方文档:git-diff
- Git 官方文档:git-add
- Git 官方文档:git-commit
- Git 官方文档:git-log
- Git 官方文档:git-show
- Git 官方文档:gitignore
















