git 提交时怎么只选部分修改?把精准暂存这件事一次讲清楚

Dylan
Dylan
发布时间:2026-08-22 | 95人浏览过
原创
展示混合修改下如何通过暂存区分离代码提交与文档提交的白底步骤图
混合修改下的精准暂存流程把功能代码和文档拆成两次提交,关键不在命令多少,而在暂存区是否真的只放入一个意图。
展示 .gitignore、生效范围与 git rm --cached 处理方式的白底边界说明图
.gitignore 生效范围与边界忽略规则能拦住未跟踪文件,但拦不住已经进历史的内容;这正是很多误解的来源。
展示 git status、git diff、git diff --cached、git log 与 git show 各自作用的白底流程信息图
Git 提交检查窗口总览先看状态、再看差异、最后核对历史,这四个检查窗口决定了提交边界是否清晰。

前言

日常开发里,最容易把提交搞乱的不是命令本身,而是多个改动同时堆在工作区时,分不清哪些该进这次提交。本文用一个可复现的小仓库,把忽略无关文件、检查差异、选择暂存、提交后复核这条链路拆开讲清楚,读完你就能判断什么时候该直接 `git add`,什么时候该用 `git add --patch` 精准切分。

真实开发里,很少有人始终保持“只改一个文件、改完立刻提交”的理想节奏。更常见的情况是:功能代码、顺手补的文档、本地产生的日志、只属于个人环境的配置,一起出现在工作区里。

这时难点已经不是会不会用 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
相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表