Git 初学者如何撤销工作区修改并保留暂存内容

内容总结生成中
你看见的,不仅是一个总结

你正在处理一个已经被 Git 跟踪的文件:之前已经把一部分修改用 git add 放进了暂存区,后来又在工作区继续改,结果改乱了。现在你想只丢弃工作区里那些未暂存的改动,让文件回到暂存区里的版本,同时让暂存内容原样保留。下面以单个文件为例,在本地仓库中按“确认差异—备份—撤销—核对”四步完成。准备一个终端、目标文件的路径,并先记住一条:撤销工作区修改会丢弃未暂存内容,Git 不会自动留副本;不要用 git reset --hard 或 git checkout HEAD -- 文件 代替。

Git 工作区、暂存区与本地仓库关系示意图

第一步:确认工作区和暂存区差在哪里

先在仓库根目录打开终端,执行:

git status

重点看两栏输出。Changes to be committed 下面列出的是暂存区内容,也就是你准备提交的修改;Changes not staged for commit 下面列出的是工作区里尚未暂存的修改。如果目标文件同时出现在这两栏,说明暂存区里有一个版本,工作区里又叠加了新修改。你要撤销的是后一栏里的内容,而不是前一栏。

接着查看具体差异:

git diff -- "src/app.js"

git diff 默认比较工作区和暂存区,输出的是“还没有被 git add 的那部分修改”。确认这些差异确实是你想丢弃的。再看暂存区:

git diff --cached -- "src/app.js"

git diff --cached 比较暂存区和最新提交,显示的是你准备保留的暂存内容。核对时不要把两者的输出弄反:前者是准备丢弃的,后者是准备保留的。

如果文件路径不确定,可以直接复制 git status 里显示的路径。git status 通常显示相对于仓库根目录的路径;如果你在子目录里执行命令,路径要按当前目录来写。路径中有空格时,用引号包起来。

第二步:备份你还要保留的未暂存修改

如果 git diff 里有一部分内容你其实还想留着,只是不想现在提交,先备份。最直观的方法是把当前工作区文件复制到项目外:

cp -- "src/app.js" "../app.js.worktree.bak"

Windows PowerShell 可以用:

Copy-Item "src/app.js" "..app.js.worktree.bak"

另一种方法是只保存未暂存差异,生成一个补丁文件:

git diff --output="../app.js.worktree.patch" -- "src/app.js"

这个补丁只包含工作区里尚未暂存的修改,不包含暂存区内容。之后可以用 git apply --check "../app.js.worktree.patch" 检查能否应用,再用 git apply "../app.js.worktree.patch" 把修改加回来。备份文件建议放在项目目录外,避免它出现在 git status 里干扰判断。

如果你已经确认这些未暂存修改完全不要,可以跳过备份。但撤销后 Git 不会替你保留这部分内容,编辑器的本地历史或系统备份也不一定可靠。这个风险需要先明确。

终端中备份工作区修改的示意图

第三步:撤销工作区修改,但不要动暂存区

Git 2.23 及以上版本推荐使用 git restore:

git restore -- "src/app.js"

这条命令默认把工作区文件恢复到暂存区里的版本。因为不加 --staged,它不会改动暂存区。执行成功时通常没有输出。此时工作区里那些未暂存的新修改被丢弃,暂存内容仍然保留。

如果你的 Git 版本低于 2.23,可以使用旧写法:

git checkout -- "src/app.js"

git checkout -- 路径 同样是从暂存区恢复工作区文件,不会取消暂存。命令中的 -- 用来明确后面是文件路径,建议保留。

需要特别避开几种看似相近、实际会影响暂存区的命令:

命令 影响范围 是否适合当前任务
git restore -- "src/app.js" 只恢复工作区,暂存区不变 适合
git checkout -- "src/app.js" 旧版本等效,只恢复工作区 适合
git restore --staged -- "src/app.js" 只改暂存区,工作区不变 不适合,会取消暂存
git checkout HEAD -- "src/app.js" 工作区和暂存区都覆盖为最新提交版本 不适合,会丢失暂存内容
git reset --hard 工作区和暂存区一起重置 不适合,范围过大

如果你只想丢弃文件中的部分修改,不要直接整文件恢复。可以先备份,再手动编辑,或者使用 git restore -p -- "src/app.js" 按块选择要恢复的内容;这个交互式选项同样需要 Git 2.23 及以上版本。

第四步:核对暂存内容是否还在

撤销后先看简洁状态:

git status --short

目标文件如果只显示为 M src/app.js,表示修改在暂存区;如果显示为 M src/app.js,表示工作区还有未暂存修改;如果显示为 MM src/app.js,表示两边都有修改。你希望看到的是目标文件不再出现在未暂存那一侧,但暂存状态仍然存在。

再分别核对差异:

git diff -- "src/app.js"
git diff --cached -- "src/app.js"

第一条命令应该没有输出,说明工作区已经和暂存区一致;第二条命令应该仍然显示暂存内容,说明准备提交的修改还在。也可以看暂存统计:

git diff --cached --stat

如果这里还能看到目标文件,说明暂存内容没有被误清掉。

撤销后核对暂存区与工作区状态的示意图

路径不对或修改还在时怎么查

如果执行 git restore 时出现类似下面的报错:

error: pathspec 'src/app.js' did not match any file(s) known to git

先检查路径。可以在仓库根目录执行 git status,直接复制其中的文件路径;如果文件在子目录里,确认当前终端所在位置。也可以用下面的命令确认文件是否已被 Git 跟踪:

git ls-files -- "src/app.js"

没有输出通常说明路径不对,或者该文件本来就没有被跟踪。未跟踪文件的清理不属于本文场景,不要用本文的恢复命令处理它。

如果命令执行后修改仍然存在,按顺序排查:

  1. 确认执行的不是 git restore --staged -- "src/app.js"。只加 --staged 只会取消暂存,不会改工作区。
  2. 确认路径没有写错。用 git diff --name-only 查看当前还有哪些未暂存文件,复制准确路径后重试。
  3. 确认终端在正确的仓库和目录中。路径是相对于当前目录解析的,不是永远相对于仓库根目录。
  4. 确认文件名大小写与实际文件一致,尤其是在大小写敏感的系统上。

想恢复刚撤销的修改时,如果之前生成过补丁,先检查再应用:

git apply --check "../app.js.worktree.patch"
git apply "../app.js.worktree.patch"

如果之前复制过 .bak 文件,也可以把它复制回原路径。反过来,如果暂存内容也被覆盖了,通常需要依赖你事先的备份;git checkout HEAD -- 文件 和 git reset --hard 之所以危险,就是因为它们会同时影响暂存区和工作区。遇到不确定的情况,先停下并备份当前文件,再继续操作。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布或转载收集发布,发布内容都是作者本人发布,与本站无关。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

给TA打赏
共{{data.count}}人
人已打赏
6 条回复 A文章作者 M管理员
  1. 鹿灵

    路径大小写真的坑

  2. 月影魔导师

    备份补丁这步救过我的代码

  3. 鳄鱼嘟嘟

    改乱了还舍不得暂存,太真实

  4. 凤凰涅槃

    diff 和 diff –cached 老看反

❯
个人中心
购物车
优惠劵
有新私信 私信列表
搜索