如何恢复被误删且未推送的Git分支?

文章导读
遇到分支误删,我一般会先停掉手头的 rebase、reset 和清理操作,不急着跑任何命令。未推送的分支被删后,提交对象通常还躺在对象库里,真正让恢复变难的,是删除后有没有触发 gc 或 prune。所以第一步是确认删除之后、发现之前,仓库里发生过哪些清理动作。
📋 目录
  1. 先从 reflog 里找最后一次引用
  2. fsck 兜底:reflog 没有记录时
  3. 恢复后的验证顺序
  4. 哪些操作会让恢复变难
  5. 常见误区:误用 checkout 和误判提交
A A

遇到分支误删,我一般会先停掉手头的 rebase、reset 和清理操作,不急着跑任何命令。未推送的分支被删后,提交对象通常还躺在对象库里,真正让恢复变难的,是删除后有没有触发 gc 或 prune。所以第一步是确认删除之后、发现之前,仓库里发生过哪些清理动作。

如果误删的分支从未推送到远程仓库,那么所有提交只存在于本地。删除分支实际只是移除分支引用,提交对象仍留在对象数据库中,直到被垃圾回收清理。能否恢复,取决于删除后是否有新的git gc或git prune执行,以及仓库是否启用了reflog。在多数默认配置下,reflog会保留90天,因此短时间内恢复的概率很高。

先从 reflog 里找最后一次引用

首先检查reflog:运行git reflog,从输出中查找形如“checkout: moving from feature-x to master”的记录,找到已删除分支最后一次指向的提交哈希。随后用该哈希重建分支:git branch feature-x 。如果reflog里没有记录,可以尝试git fsck --unreachable --no-reflogs,它会列出所有悬空提交,再根据提交时间与提交说明判断目标分支。

reflog 的输出里,一个分支名会出现多次。取最近一次 checkout 记录,再用 git show --stat 看那一次提交的内容,确认它就是离开分支前最后的提交状态。

fsck 兜底:reflog 没有记录时

如果 reflog 里找不到可用的 checkout 记录,比如分支创建后一直没有切换过,就不会留下离开分支的日志。这时用 git fsck --unreachable --no-reflogs 扫描全部悬空提交。输出可能很长,先按提交时间排序,再结合提交说明找目标分支的候选哈希。

建议把 fsck 输出保存到文件,然后用 git log --all --oneline 做对照。悬空提交里父链顶端的那几个才是候选分支头,不要只凭哈希前缀判断。

恢复后的验证顺序

恢复后务必立即验证:切换到恢复的分支,运行git log --oneline --graph确认提交历史完整,再用git diff 对比关键提交。同时建议马上执行git push origin feature-x,将分支推到远程备份。若仓库较大,恢复后先不要运行git gc或git prune,避免丢失其他悬空对象。

如何恢复被误删且未推送的Git分支?

验证时重点看两处:分支顶端提交是不是预期中的最后一次提交,提交的父链是否连续。发现不对就重新选哈希,不要留在半截历史上继续开发。推送这一步别跳过,给恢复结果加一份外部备份。

哪些操作会让恢复变难

最需要警惕的是显式清理命令。误删后一旦执行过 git gc --prune=now 或 git prune --expire now,对象会被直接清掉,reflog 也可能被截断。gc.reflogExpire 配置默认通常是 90 天,但这个期限不保证,有些清理操作会强制缩短。如果仓库还有其他副本,优先从副本恢复更安全。

常见误区:误用 checkout 和误判提交

常见的坑是直接用git checkout 进入分离HEAD状态,然后以为分支恢复了,实际上没有分支名。正确做法是先用git branch新建分支。另一个坑是误将reflog中的其他提交当作目标,例如checkout记录可能来自临时提交。恢复后应检查提交的父链,确认它是否是预期分支的拓扑位置,而不仅仅是看哈希前缀。

看到 detached HEAD 时不要继续提交。先用 git branch feature-x 重建引用,再 git switch feature-x 切过去。

最后给个维护建议:批量清理本地分支前,先跑 git branch -vv 看每个分支的最新提交和远程跟踪状态。对没推送过的分支,临时打个 tag,比如 backup/feature-x,后续就算误删,也有个明确的恢复锚点。