改错代码、提交错分支、工作区被搞乱——Git 误操作几乎是每个开发者的必经之路。本文按三种典型场景给出急救步骤,最后再用 reflog 兜底,看完这篇基本可以告别「代码找不回来了」的恐慌。

场景一:刚 commit 了不该提交的内容(还没 push)

这是最安全的情况,本地还没有同步出去,直接用 reset 回退即可。关键是分清三档模式:

# 软重置:撤销 commit,改动保留在暂存区(最常用,想重新组织提交时用)
git reset --soft HEAD~1

# 混合重置(默认):撤销 commit 和暂存,改动保留在工作区
git reset HEAD~1

# 硬重置:撤销 commit、暂存和工作区,改动直接丢弃(危险,想清楚再用)
git reset --hard HEAD~1

选择口诀:改动还想留就 soft,想重新改就 mixed(默认),确定不要了才 hard。

回退多个提交时,HEAD~n 中的 n 换成数量即可,也可以直接指定 commit id:git reset --soft <commit-id>,回退到该提交之后的状态。

场景二:错误提交已经 push 到远端

远端已经有人可能拉取了代码,这时不要用 reset 改写历史,用 revert 生成一个反向提交:

# 撤销某次提交的改动,生成一个新的「反向提交」
git revert <commit-id>

# 撤销最近一次提交
git revert HEAD

# 批量撤销一段连续提交(不含 oldest,含 newest)
git revert oldest-commit-id..newest-commit-id

revert 的好处是历史不会被改写——错误提交和撤销操作都完整保留,别人拉取不会产生冲突。多人协作分支(main / develop)上的误操作,revert 永远是首选。

如果误操作只是把不该提交的文件(如配置文件、日志)commit 了进去,也可以用 git rm --cached <file> 移出跟踪后 amend:

git rm --cached application-prod.yml
git commit --amend --no-edit   # 修改本次提交而不新建提交(仅限未 push 或确认无他人拉取时)

场景三:本地改了一半,需要临时切分支

手头改动还没法提交,但必须切到别的分支修线上问题——这是 stash 的主场:

git stash                       # 暂存全部未提交改动(含暂存区)
git stash -u                    # 连未跟踪的新文件一起暂存
git stash pop                   # 恢复最近一次暂存并删除记录
git stash apply                 # 恢复但保留记录(想应用到多个分支时用)
git stash list                  # 查看所有暂存
git stash pop stash@{2}         # 恢复指定某一次暂存
git stash drop stash@{0}        # 删除某条暂存记录

一个实用习惯:暂存时加备注,方便半天后想起来这是哪次改动:

git stash push -m 「修复订单金额精度-进行到一半」

注意:stash 只作用于当前分支的工作区,pop 恢复时如果目标分支同文件有冲突,Git 会提示冲突而不是覆盖,此时解决冲突后需要手动 git stash drop。

兜底:reflog 找回一切

reset --hard 之后发现误删了有用的提交?别慌,只要提交过的内容,reflog 里都有记录:

git reflog                      # 查看本地所有 HEAD 移动记录
# 输出示例:
# a1b2c3d HEAD@{0}: reset: moving to HEAD~2
# e4f5g6h HEAD@{1}: commit: 完成支付回调逻辑

git reset --hard e4f5g6h        # 跳回任意历史状态,被删的提交就回来了

reflog 默认保留 90 天,这就是 Git 说的「几乎不会真正丢失提交」。唯一救不回来的是从未 commit 过的工作区改动——所以重要改动勤 commit,反正可以 squash。

更多分支与撤销操作可以对照速查表使用:Git 命令速查、Git 撤销与回退速查。