1. 这篇文章解决什么问题?
日常写代码时,Git 最常见的压力不是“怎么提交”,而是:
1. 文件改错了,怎么撤回? 2. 已经 git add 了,怎么从暂存区拿出来? 3. commit 写错了,怎么修改? 4. 已经提交了好几次,怎么回到之前的版本? 5. reset、restore、revert 到底有什么区别?这篇只讲本地回退和撤销。
远程分支、push被拒绝、pull --rebase这类问题,统一放在02和05里理解。
2. 回退前先看清楚当前状态
任何回退操作之前,先执行:
gitstatusgitlog--oneline--graph--decorate--all前者看工作区和暂存区:
哪些文件被改了 哪些文件已经 add 哪些文件还没有被 Git 跟踪后者看提交历史:
当前 HEAD 在哪里 分支指向哪个 commit 想回到哪个 commit不要在没看清状态时直接reset --hard。
3. 三类“撤销”要分清楚
Git 里的撤销大致分三层:
工作区撤销:文件还没 add,只想丢掉当前修改 暂存区撤销:文件已经 add,但不想让它进入下一次 commit 提交历史撤销:commit 已经生成,需要改历史或新增一个反向提交对应常见命令:
git restore git restore --staged git reset git commit --amend git revert这些命令解决的问题不一样,不能只靠“撤销”两个字混着用。
4. 撤销工作区修改
如果文件还没有git add,只是工作区改错了,可以执行:
gitrestore README.md这表示:
把 README.md 恢复到当前 HEAD 中记录的版本 丢弃工作区里这个文件的未暂存修改如果要撤销当前目录下所有已跟踪文件的工作区修改:
gitrestore.注意:
git restore 不会恢复没有被 Git 跟踪的新文件也就是说,Untracked files里的文件不会因为git restore .自动消失。
5. 取消暂存区内容
如果已经执行过:
gitaddREADME.md但后来发现暂时不想提交它,可以执行:
gitrestore--stagedREADME.md它的意思是:
把 README.md 从暂存区拿出来 但保留工作区里的修改也就是说,文件内容不会丢,只是下一次 commit 不再包含它。
常见流程:
gitstatusgitrestore--stagedREADME.mdgitstatus这样能清楚看到文件从:
Changes to be committed回到:
Changes not staged for commit6. 同时撤销暂存区和工作区
如果一个文件已经git add,并且你确定这些修改都不要了,可以分两步:
gitrestore--stagedREADME.mdgitrestore README.md第一步:
从暂存区拿出来第二步:
丢掉工作区修改更推荐新手这样写,因为每一步都能通过git status看清楚变化。
7. 修改最近一次 commit
如果刚刚提交完,发现 commit message 写错了:
gitcommit--amend-m"docs(git): update rollback note"如果发现漏了一个文件:
gitaddREADME.mdgitcommit--amend这会把暂存区里的内容合并进最近一次 commit。
需要注意:
amend 会生成一个新的 commit 它不是在原地修改旧 commit如果这次 commit 还没有 push,通常没什么问题。
如果已经 push 到远程,并且别人可能已经基于它继续开发,就不要随便 amend。
8. git reset 是什么?
reset的核心动作是:
移动当前分支指针。
假设当前历史是:
A -- B -- C -- D | main HEAD执行:
gitreset--softC或者:
gitreset--mixedCgitreset--hardC本质上都是让main从D移回C:
A -- B -- C | main HEAD区别在于:
commit 指针移动后,工作区和暂存区怎么处理9. reset --soft
执行:
gitreset--softHEAD~1表示:
撤销最近一次 commit 但把这次 commit 的内容保留在暂存区适合场景:
commit message 写错了 几个 commit 想重新整理成一个 刚提交完发现还想补一点内容执行后通常会看到:
Changes to be committed说明修改还在暂存区,下一次可以重新 commit。
10. reset --mixed
reset默认就是--mixed:
gitreset HEAD~1等价于:
gitreset--mixedHEAD~1它表示:
撤销最近一次 commit 把这次 commit 的内容放回工作区 暂存区清空适合场景:
刚才提交太急了 想重新选择哪些文件进入下一次提交执行后通常会看到:
Changes not staged for commit11. reset --hard
执行:
gitreset--hardHEAD~1表示:
撤销最近一次 commit 丢掉对应的工作区和暂存区内容 让文件状态直接回到目标 commit这是最危险的一种。
适合场景很少:
你非常确定最近的提交和修改都不要了 当前修改没有任何保留价值 你已经确认没有未保存的重要内容执行前至少先看一眼:
gitstatusgitlog--oneline如果只是想取消暂存或撤销某个文件,通常不需要reset --hard。
12. HEAD~1 是什么意思?
常见写法:
gitreset--softHEAD~1这里的HEAD~1表示:
当前提交的上一个提交类似地:
HEAD~2 当前提交往前数两个提交 HEAD~3 当前提交往前数三个提交也可以直接写 commit hash:
gitreset--mixeda8c912e意思是回到a8c912e这个提交。
13. git revert 是什么?
revert和reset很不一样。
reset是移动分支指针,可能改写历史。
revert是新增一个提交,用来抵消之前某个提交的修改。
例如历史是:
A -- B -- C -- D | main执行:
gitrevert C可能得到:
A -- B -- C -- D -- E | main其中E是一个新的提交,它的内容用来撤销C带来的修改。
14. reset 和 revert 怎么选?
简单判断:
本地提交,还没 push,想整理历史:可以 reset 已经 push 到远程,别人可能看到了:优先 revert 多人共享分支上要撤销某次提交:优先 revert原因是:
reset 会改变分支历史 revert 不改旧历史,只追加新历史在团队协作里,revert往往更稳。
15. 删除未跟踪文件
git restore不会删除未跟踪文件。
如果想查看哪些未跟踪文件会被删除,可以先执行:
gitclean-n真正删除:
gitclean-f删除未跟踪目录:
gitclean-fd注意:
clean 删除的是 Git 还没有跟踪的文件 删除后通常不能靠 Git 找回来所以新手更推荐先用git clean -n预览。
16. 一个比较稳的回退流程
当你不知道该用哪个命令时,按这个顺序判断:
1. 先 git status 看文件状态 2. 如果只是没 add 的文件改错了,用 git restore <file> 3. 如果已经 add 但不想提交,用 git restore --staged <file> 4. 如果最近一次 commit 写错了,并且还没 push,用 git commit --amend 5. 如果本地几个 commit 想重来,用 git reset --soft 或 --mixed 6. 如果已经 push 到共享分支,优先 git revert不要一上来就:
gitreset--hard这个命令不是“万能撤销键”,它更像是“我确认这些修改都不要了”。
17. 总结
restore主要处理工作区和暂存区,reset主要移动分支指针并可能改写历史,revert通过新增提交来撤销旧提交。
回退前先git status,再决定要动的是文件、暂存区,还是提交历史。