Git 撤销操作全景:reset、revert、restore 到底选哪个
SilverLens
5 min read
Git 的撤销命令之所以让人困惑,是因为它们分属两条不同的时间线:一条改的是"历史记录",一条改的是"工作区文件"。选错命令,要么丢提交,要么推不上去。先记住一句判断:已经推送到共享仓库的提交,只能用 revert;本地还没推的混乱,才考虑 reset。
三个命令的职责边界
git reset 动的是分支指针,把 HEAD 向后挪,相当于改写历史。git revert 不动旧提交,而是新建一个"反向"提交抵消它的修改,历史只增不减。git restore 只动工作区或暂存区里的文件内容,跟提交历史无关,属于"文件级"撤销。三者不是同一层面的工具,混用就会出事:用 reset 去修已经发布的提交,等于把别人的基线也改了。
reset 的三种模式
git reset --soft HEAD~1 # 只挪指针,改动留在暂存区
git reset --mixed HEAD~1 # 默认模式,改动退回工作区(未暂存)
git reset --hard HEAD~1 # 连工作区一起清空,改动直接消失
--soft、--mixed、--hard 的区别只在"改动最终落在哪一层":soft 留在暂存区,mixed 退回工作区,hard 直接丢弃。--hard 最危险:它不进回收站,执行后工作区未提交的修改一起没。经验上,只要还有任何未提交改动,--hard 之前先 git stash 或 git status 确认。已经 push 过的提交再用 reset 往前挪,会导致他人拉取时出现"非快进"冲突,需要强制推送才能覆盖,这在协作仓库里是事故源头,能避则避。
revert 的安全写法
git revert <commit-hash> # 撤销单个提交
git revert HEAD~2..HEAD # 撤销最近两个提交(依次新建反向提交)
revert 之所以安全,是因为它不删任何历史。多人协作的分支上发现某个提交引入问题,revert 是默认选择。它的代价是历史里会多一条"撤销"记录,但可读性比改写历史好得多。注意一个陷阱:连续 revert 一段提交后再想合并原分支,可能遇到补丁已应用的错误,提示该变更已存在。此时不是 revert 失效,而是那批改动已经在另一条线上被反向抵消过,需要 revert 掉那个 revert 提交本身,才能把原改动重新请回来。这条回环在团队里第一次遇到时很绕,提前讲清楚能省掉一次排查。
restore 解决"文件级"误伤
git restore src/app.js # 丢弃工作区改动,回到最近提交状态
git restore --staged config.ini # 把暂存的文件撤出暂存区,不动工作区内容
restore 是后来拆出来的命令,原本由 git checkout 和 git reset 兼任,职责混乱。现在文件级的"丢弃修改"优先用 restore,语义清晰:加 --staged 只动暂存区,不加就动工作区。它不会碰提交历史,所以没有协作风险,适合"我刚改错了一个文件,想回到干净状态"这类高频场景。
一个决策表
| 场景 |
推荐命令 |
| 撤销刚 commit 还没 push 的提交 |
reset --soft 保留改动 |
| 撤销已 push 的提交 |
revert |
| 丢掉某个文件未提交的修改 |
restore 文件名 |
| 把暂存的文件移出暂存区 |
restore --staged 文件名 |
| 彻底重来、丢弃最近 N 个本地提交 |
reset --hard(确认无共享) |
边界很清晰:涉及"别人已经看见的提交"走 revert,涉及"我自己机器上的文件或本地提交"才考虑 reset/restore。把这条规则刻进肌肉记忆,撤销操作基本不会再出错。还有一个常被忽略的点:reset 到某次提交后如果还想找回被跳过的提交,提交并没有真的消失,git reflog 里能查到旧 HEAD 的指向,在误操作后的黄金抢救期里它是最可靠的退路。
1 view
0 likes
0 comments