git命令提交代码add和commit有什么区别?分工与顺序
- 2026年11月10日
- 13 次
上周同事发来一张截图:git log 里连着四条提交都叫 update,diff 出来的内容完全一样。用git命令提交代码的时候他把顺序弄反了——每次 add 之后又动了文件,commit 进去的还是 add 那一刻的旧内容,后面那版一直留在工作区没进去;等下回再 add,几回的改动就一起并进了同一条提交。改法不复杂,加完先看一眼 status,两栏对得上再提交。
分工只有一句话:add 摆放,commit 落档
add 决定下一次提交包含哪些内容,commit 决定这些内容成为哪一条历史。前者写索引,不产生任何历史;后者读索引,不读工作区。

区别落到操作上就这两句:add 是准备,commit 是记录。新文件没 add,commit 不会带上它;只 add 不 commit,git log 里永远看不到。
暂存区到底存了什么
存的是一张路径到内容的对照表,不是一份完整的项目副本。git add 的那一刻,工作区里这个文件的内容被算成一个 blob 写进对象库,索引记下路径和那串哈希。之后继续改文件,索引里那条记录不会自己跟着变。
同一份文件于是能同时出现在两栏。改完 add 一次,接着又改,git status 会把它两边都列出来:
git add app/settings.py
git status
输出分两段。头一段是 On branch main 和 Changes to be committed:,下面缩进一行 modified: app/settings.py;第二段换成 Changes not staged for commit:,再缩进一行同样的 modified: app/settings.py。同一个路径,两栏各列了一遍。
上面那栏是索引里的版本,下面那栏是工作区现在的版本。这时候直接 commit,进历史的是上面那栏,也就是 add 那一刻的内容,后面那次修改留在工作区,没进去。同事那四条重复提交,根子在这儿。
想确认这次到底会提交什么,用 git diff --cached 看,它比对索引和上一次提交;不带 --cached 的 git diff 比对工作区和索引。两个输出不一样,就说明暂存区已经和工作区分叉。所以每次git命令提交代码之前,status 那两栏值得花两秒对一遍。
索引里的东西也能直接打出来,git ls-files --stage 一行四列:权限、blob 哈希、暂存号、路径。同一个内容被两个路径引用时哈希是同一个,对象库里只存一份;暂存号那一列正常是 0,出现别的数字说明这个路径有未解决的冲突。
一次提交里两步的先后顺序
顺序不能换。第一步改文件,第二步 git add,第三步 status 核对,第四步 git commit,第五步 git log 确认——git命令提交代码的完整链条就这五步,中间那步最容易被省掉。

status 不只是个查看命令,它是提交前最省事的一次核对。两栏对不上,说明索引和工作区已经分叉,这时候提交,写进去的是你以为的东西还是实际的东西,全看运气。
图上那条回退分支是真会走的:status 发现多了一个不该进的文件,restore 回来重新 add,不用硬着头皮把它提交上去。
三步连着敲,中间没有隐藏动作:
git diff --cached
git commit -m "fix: 配置缺省值在空字符串时失效"
git log --oneline -3
git log --oneline 一行一条,前面是缩写哈希,默认 7 位,仓库大了 Git 会自动加长到能区分开为止,后面跟提交信息。三条里最新那条在最上面,先看它是不是你要的那条。
提交信息的第一行是摘要,--oneline 只显示这一行;详细说明要空一行再写,否则也会被压进摘要里,列表页上看着就是一长串。
add 错了文件,git restore --staged 把路径从索引里撤出来,工作区的改动原样留着。这个命令成功时不打印任何东西,回车后没有输出是对的;不放心再用 status 看一次,那一栏空了才算撤干净。
add 能反复覆盖,commit 只能改写
add 的结果可以被下一次 add 覆盖,commit 的结果不能。commit 对象里带着树、提交信息、作者时间,还有一个指向上一条的父提交;它一旦写进对象库,改它只能 amend 或者 revert,代价完全不同。
判断标准落在一句话上:只想调整这次提交包含哪些文件,反复 add 就行,随便改;一旦 commit,历史的形状就定了,后面所有分支的起点都跟着变。
两个在真机上才看得见的现象
.gitignore 对已经跟踪的文件不起作用。文件早就 commit 过,后来才把路径写进 .gitignore,git status 里照样显示 modified,git add 也照样能加进去。要它真的退出跟踪,得先 git rm --cached,再提交一次删除记录。顺序反了,下一个人拉代码照样看到那个文件。

git commit 后面跟路径,会绕过暂存区,直接提交工作区里这个路径的当前内容。命令能过,但和你刚在 status 里核对的东西不是同一份。团队里有人这么写,评审时 diff 对不上是常事。
这两件事文档里写得不显眼,git命令提交代码时却经常撞上。
什么时候别按这个流程走
这套流程有个隐含前提,你对工作区有独占的控制权。前提不成立的时候,先别纠结git命令提交代码的顺序,把工作区清出来再说。
只改已跟踪的文件、又不想分两次敲,git commit -a 可以省掉 add,代价是新文件不会被带进去,忘了这个前提就会漏文件。
改动都挤在同一个文件里、只想提交其中几段,用 git add -p 一块一块挑,比整文件 add 精确;代价是每块都要回一次 y 或 n,文件一多就很慢。
多人共用一个工作目录,或者 CI 正在同一份检出上跑构建,提交前先 stash。你核对完 status 到 commit 之间,索引可能已经被别的进程改过,这时候的核对通过不算数。
Git 2.23 以前的版本没有 git restore,撤暂存区要写 git reset HEAD <file>,脚本里按实际装的版本选命令。
还有一种属于约定层面:仓库配了 pre-commit 钩子,并且钩子会自动改文件,那你 add 进去的内容和最终进提交的内容可能不是同一份,这种情况以钩子为准,别只信 status。
若非作者标注皆为李涛自留地原创文章,转载或复制请以超链接形式并注明出处。
- PHP如何在图片上添加文字?PHP在图片上添加文字实例代码 2022-11-22
- nginx配置负载均衡的几种写法与生产环境踩坑记录 2026-11-09
- excel函数xlookup用法,和vlookup区别在哪 2026-11-09
- PHP中数组是什么?PHP数组怎么创建及打印输出 2022-10-20
- PHP比较运算符==号和===号的用法和区别 2022-06-14
- Python列表排序中的sort()和sorted()有什么不同 2021-11-10
