Recovery
reflog 恢复手册
当 reset、rebase、pull 或误删分支后找不到原来位置时,优先用 reflog 重新定位并建立恢复路径。
- 正在处理 Git 误操作的人
- 想提前建立保守恢复习惯的协作者
- 先停手,不继续乱试命令
- 能执行 `git reflog`、`git status`、`git log --graph`
- 还没保住旧位置就继续 reset / rebase
- 在没判断影响面时直接改共享历史
数据 / 性能事实
- 90 daysreachable reflog entries survive for 90 days by default — a wide recovery window before gc prunes them出处: git-reflog(1) / git-gc(1) official manual
关键引语
Every move of HEAD is recorded in the reflog, so commits that seem 'lost' after a reset or rebase remain reachable until the reflog entry expires.
引用与延伸阅读
学完这篇你会掌握什么
- 理解 reflog 记的是“引用怎么移动”,而不是帮你直接撤销一切
- 掌握“先看 reflog → 建救援分支 → 再决定 reset/cherry-pick”的稳妥恢复顺序
- 知道 reflog 有本地 90 天窗口等边界,哪些场景该立刻建救援分支而不是继续试命令
reflog 是很多 Git 恢复动作里最值得先看的东西。误操作之后,你最先丢掉的通常不是提交对象,而是“当前分支原来指向哪里”的名字和上下文。
reflog 不是直接帮你“撤销一切”。它先告诉你 HEAD 和分支曾经去过哪里。真正的恢复动作,通常是从这些旧位置里选一个最可信的落点,再决定是 reset、建救援分支,还是 cherry-pick 部分提交。
先想一个问题
你刚执行 git reset --hard HEAD~1 想回退一步,结果发现自己把还没推送的重要提交一起弄“丢”了。终端里历史变短了,心跳加速——但其实提交对象还在,你只是把“当前分支指向哪”这个名字弄丢了。reflog 就是帮你把那个名字重新找回来的工具。
哪些场景最适合先看 reflog
reset --hard之后才发现回退过头了rebase过程中把原来的提交顺序改乱了pull之后历史变得和预期不一样- 分支被删了,但怀疑提交对象仍然存在
commit --amend或rebase -i之后想找回旧版本
它的价值在于:你不用先想清楚最终怎么恢复,只要先把“旧位置在哪”找出来,很多事情就立刻可控了。
一个最稳的恢复顺序
1. 先看 reflog,不要急着继续试命令
git reflog --date=local
重点不是把每条都看懂,而是找到“误操作之前的那个状态”。常见线索包括:
reset: moving to ...rebase (start)/rebase (finish)checkout: moving from ... to ...commit (amend): ...
2. 找到目标位置后,先建救援分支
git switch -c rescue/recover HEAD@{1}
如果你对 HEAD@{1} 没把握,也可以先用提交 SHA 建分支。
这一步的核心不是“立刻修复”,而是先把旧位置固定下来。这样后面就算你继续尝试 reset、merge、cherry-pick,也不会把这个落点再丢掉。
3. 再根据目标选择真正的恢复动作
- 想让当前分支回到旧位置:
git reset --hard <target> - 想保留当前状态并另开一条线整理:继续用
rescue/*分支 - 想只取回部分提交:
git cherry-pick <sha> - 想比较两边差异后再决定:
git log --oneline --graph --decorate --all
为什么“先建分支”比“直接 reset 回去”更稳
很多人知道 reflog 有用,但还是容易在找到旧位置后马上执行一次新的 reset --hard。问题在于,恢复动作本身也会继续移动引用。如果你判断错了目标位置,很容易在混乱中继续叠加混乱。
保守做法是:
git refloggit switch -c rescue/... <old-position>- 在救援分支上检查内容、跑测试、比对差异
- 最后再决定要不要改当前分支
这套顺序会慢一点,但事故处理中“慢一点但稳一点”通常更值。
三种常见恢复场景怎么判断
reset 后想找回
重点是找到 reset 之前的 HEAD,然后先接住旧提交。
如果只是想恢复到旧位置,后续可以在确认无误后把主分支 reset 回去。
rebase 后历史乱了
重点不是先纠结哪些 SHA 变了,而是找到 rebase 前的分支头。
很多时候 HEAD@{n} 或 branch@{n} 就能直接定位到 rebase 开始前。
pull 后想撤回
先判断这次 pull 实际做了什么:
- 是 fast-forward
- 是 merge
- 还是 rebase
然后再从 reflog 里定位 pull 之前的状态。不要在没搞清 pull 结果之前直接硬 reset。
reflog 不是万能恢复器,它也有边界
- reflog 默认是本地记录,不会替你恢复别人机器上的状态
- 记录不是永久保留的,长期未引用对象可能被清理
- 如果仓库做过 aggressive gc,恢复窗口可能缩短
- 你能否恢复成功,也取决于相关对象是否仍然可达或尚未被清掉
所以更稳的习惯是:一旦进入事故排查,就尽早建救援分支,而不是把恢复机会押在“以后总能再找回来”。
reflog 很强,但不是永远存在、永远完整。它更像一个恢复窗口,而不是永久备份系统。看到可能有价值的旧位置后,先建分支接住,才是真正保守的动作。
常见误区
只要看到旧 SHA,就立刻 reset
这样做太快了。正确顺序通常是先建 rescue/* 分支,再决定是否真的要重置当前分支。
以为 reflog 只在 reset 后才有用
只要引用移动过,很多操作都会留下记录,包括 checkout、merge、rebase、amend、pull。
以为分支删了,提交就一定没了
很多时候只是名字没了,对象还在,只是需要靠 reflog 或其他引用记录重新定位。
先故意做一次可控的小误操作,再用 reflog 把旧位置接住。练过一次,真实事故里你会冷静很多。
mkdir reflog-lab && cd reflog-lab git init echo one > notes.txt git add notes.txt git commit -m "init" echo two >> notes.txt git commit -am "second" echo three >> notes.txt git commit -am "third"步骤
- 执行 `git reset --hard HEAD~1`
- 立刻运行 `git reflog --date=local` 找到 reset 前的记录
- 使用 `git switch -c rescue/recover HEAD@{1}` 建立救援分支
- 用 `git log --oneline --graph --decorate --all` 对比当前分支和救援分支
- 确认后再决定是否让原分支回到旧位置
- 旧提交其实没有立刻消失
- rescue 分支会把旧位置重新接住
- 当前分支和救援分支会出现清晰的分叉关系
- 你可以在不破坏现状的前提下继续判断下一步
- 看到 `HEAD@{1}` 就直接再做一次 reset
- 没建救援分支就继续试别的命令
- 找到旧 SHA 后不做任何记录,过一会儿又忘了目标是哪条
一条最值得长期记住的恢复习惯
看到历史不对时,优先做这三件事:
git reflog- 建救援分支
- 再做 reset、merge、cherry-pick 等整理动作
给你的练习
- 按本文的 PracticeLab 搭一个临时仓库,故意
git reset --hard HEAD~1,再用git reflog --date=local找到 reset 前的记录并git switch -c rescue/recover HEAD@{1}接住它。 - 再模拟一次误删分支:
git branch -D掉一个本地分支后,用git reflog或git fsck --lost-found把它指向的提交重新定位,建一条新分支接住。 - 故意跑一次
git commit --amend改掉最近一条 message,然后用 reflog 找到 amend 前的 SHA,用救援分支或git cherry-pick把旧版本拿回来。
延伸阅读
沿着同一主题继续深入: