Git 误删文件怎么恢复:reset --soft 和 --mixed 差在哪

结论先行:进过一次提交的东西都能靠 reflog 捞回来,只在工作区、从未 add 过的改动 Git 没有任何记录。本文按「误改工作区 / 误 add / 误 commit / 已 push」四种场景给出命令对照、reflog 抢救步骤,以及 reset --soft、--mixed、--hard 分别动到哪一层。

约 7 分钟读完 · 更新于 2026-10-21 · 文中每条都能在下面的配套工具里动手验证

先分清:哪些还能救,哪些已经没了

判定标准只有一条:这份内容有没有变成 Git 对象。进过一次 commit 的一切(文件内容、整棵目录、临时分支)都留在对象库里,reflog 记着每个 commit 的位置,reset、rebase、误删分支都只是移动了指针。差别很大:从没 git add 过的本地改动,Git 一个字节都没存,救不回来——只能去翻编辑器的 Local History、IDE 的撤销栈或备份软件。这条边界先认清,后面所有命令都只是选哪条路回到那个对象。

误操作 → 命令 → 什么时候别用

排错时先看 git status:改动停在哪一层(工作区 / 暂存区 / 提交),就用对应的 undo。往下层级的 undo 永远比上一层级安全,代价是更啰嗦。

你干了什么用什么救什么时候别用
改了文件,想整个丢掉git restore 文件改动里混了要保留的内容(先 git diff 看一眼)
add 错了,想退回工作区git restore --staged 文件你本来就准备提交它
commit 错了,想重做这次提交git reset --soft HEAD~1这次提交已经推到远端共享分支
commit 错了,想把文件退回未暂存git reset HEAD~1同一个提交里还有别的正确改动要留
想彻底扔掉工作区和暂存区git reset --hard任何未提交且没备份的改动——不可逆
已 push 的提交要撤销git revert <sha>你需要的是「改写」而不是「追加一个反向提交」
rebase 之后提交不见了git reflog 找 sha,再 git reset --hard <sha>你还没确认哪个 sha 才是目标
删了文件想找回git restore 文件,或 git checkout <commit> -- 文件这个文件从没被提交过
误删了分支git branch 分支名 <sha>reflog 记录已被清理(默认 90 天过期)

reset --soft / --mixed / --hard 到底差在哪

reset 做的是三件事,区别只在做几件:① 把 HEAD(以及当前分支指针)移到另一个提交;② 把暂存区重置成那个提交的状态;③ 把工作区重置成那个提交的状态。--soft 只做 ①,--mixed 做 ① ②(不加参数时的默认值),--hard 做 ① ② ③。你要的几乎总是「只回退一层」:想重做提交用 --soft,想重新挑选文件进暂存区用 --mixed,想连未提交的改动一起扔掉才用 --hard。

reflog 抢救:把丢掉的提交按顺序捞回来

  1. git reflog:输出形如「sha HEAD@{n}: 操作描述」,n=0 是当前位置,n 越大越久远。看到「reset: moving to HEAD~3」这类记录,就知道是哪次操作把提交从指针上摘了下来。
  2. 按描述和哈希挑出目标提交,不确定就先看内容:git show <sha> --stat,或 git diff <sha>^ <sha>。
  3. 先开新分支验证:git switch -c rescue <sha>。这一步不动任何已有指针,确认内容无误再落到原分支,比直接 reset --hard 稳。
  4. 确认后 git reset --hard <sha>(或 git merge <sha> 把改动并回当前分支,两条路各有用处)。
reflog:把 HEAD 走过的每一步列出来
git reflog
# 8f2a1c9 HEAD@{0}: commit: 补了分页的边界条件
# 4b70de1 HEAD@{1}: reset: moving to HEAD~2   ← 提交就是被这一步弄丢的
# a91b7d5 HEAD@{2}: commit: 重构了订单校验

git show a91b7d5 --stat           # 先确认内容
git switch -c rescue a91b7d5     # 在新分支上打开它,零风险
动手前先留退路
git branch backup-before-reset           # 一个分支指针就是一份备份
git stash push -u -m "before reset"   # 未提交 + 未跟踪的改动一起收进 stash
git stash list                          # 后悔时:git stash pop

误删文件:git rm 和 rm 的差别,以及文件到底去哪了

git rm 文件只删工作区和暂存区两处,HEAD 里的那份还在,git restore 文件就能拿回原内容;直接 rm 文件则只删工作区,同样能恢复。真正删掉内容的是 git rm 加 git commit——提交之后文件进入历史,git restore 拿的是当前分支的版本,要旧版本就得从历史里取:git checkout <旧提交> -- 文件(注意这会把文件放回暂存区)。两条保险值得知道:文件从未被 git add 过,Git 里没有副本,只能去翻编辑器的本地历史;文件被提交过又删除,删除这个动作本身也可以通过 reflog 反向恢复。

已 push 的提交怎么改:改写还是追加

push 之后 Git 不允许你直接「改」历史,这是特性不是限制。两条路:想抹掉痕迹(改写历史)用 rebase 或 reset 后强推,风险是所有拉过这个分支的人都会不一致,非要推就用 --force-with-lease 而不是 --force,它会在远端被别人动过时拒绝;想留痕(追加提交)用 git revert 生成一个反向提交,历史变长但任何人都安全,PR 里也更适合表达意图。团队共享分支默认选 revert,只有自己独占的特性分支才考虑改写。

场景做法代价
刚 push 自己就发现错了reset --soft 后重推远端历史不一致;用 --force-with-lease 兜底
共享分支上撤一个旧提交git revert <sha> 后正常 push历史多一条;需在 PR 里说明原因
特性分支还没合并,想整理提交git rebase -i 改写后强推改写历史;同事拉过就得重新对齐
分支已合并到 main 之后git revert -m 1 <merge-sha>主干的线性历史被反向提交打断

reflog 不是备份:90 天和 gc 之后就没了

reflog 记录的是 HEAD 这个引用移动的历史,条目默认 90 天后过期(可用 git reflog expire --expire=30.d --all 调整),而悬空对象会在 git gc --prune=1d 之后被真正清理。也就是说:一个被 reset 掉的提交,你有三到四个月的窗口去捞,之后就只能靠备份。重要分支定期推到远端、或用 git bundle 打包一份,是比 reflog 可靠得多的保险。

把误操作概率降下来的四个动作

  • 改代码前保证工作区干净:git status 输出为空再动手。这一条能把绝大多数误操作从「不可逆」降级为「一次 reset」。
  • 大改动前先切分支:git switch -c 试验性改动。分支是可以随手删的,删错了也还有 reflog。
  • 本地提交别只留在本地:每天至少推一次,PR 走远端才有人看到。
  • 装 pre-commit 钩子挡住低级错误:跑一次格式化和测试,比事后用 reflog 抢救便宜得多。

常见问题

git reset --hard 之后文件还能找回来吗?

只要进过提交就能,reflog 记着 HEAD 的每一步:git reflog 找到 sha,再 git reset --hard 回去。真正找不回的是从未 git add 过的改动——Git 没存过它们,去翻编辑器的本地历史。动手前先 git branch backup-before-reset 留个指针,比事后抢救便宜。

reset --soft 和 --mixed 的区别到底在哪?

只差暂存区:--soft 把 HEAD 移回上一提交,暂存区和工作区原样保留(改动还在暂存区,适合重新提交);--mixed(默认)在 --soft 基础上再把暂存区清空,改动退回工作区,适合重新挑选文件。两者都不动工作区文件。相关命令可在 Git 命令速查里按场景查。

已经 push 的提交改错了,还能删掉吗?

能,但先问一句:这个分支有人拉过吗。没人拉过就 reset --soft 后用 --force-with-lease 重推;有人拉过就用 git revert 生成一个反向提交,历史保留痕迹、任何人都安全。团队共享分支默认选后者。

误删了本地分支还能恢复吗?

能,只要 reflog 记录还没过期(默认 90 天)。先 git reflog 找到分支最后一个提交的 sha,再 git branch 分支名 重建;连改名都用 git branch -m 旧名 新名。如果提示找不到记录,多半是 gc 已经清掉了悬空对象。

📂

松开以自动格式化

支持 .json / .sql / .csv / .html / .css / .xml / .yaml 文件