跳到主要内容

Git Reflog:误操作后的后悔药

git reset --hard、错误的 rebase、删掉的分支——这些操作看起来都把提交"弄丢"了。

大多数时候没丢。只要提交曾经存在于本地仓库,git reflog 都能翻出来。

一、reflog 是什么​

它记录 HEAD 和分支引用在本地每一次移动:

git reflog
# 3f2a1b0 HEAD@{0}: reset: moving to HEAD~3
# 9c8d7e6 HEAD@{1}: commit: feat: add validation
# 5b4a3c2 HEAD@{2}: rebase (finish): returning to refs/heads/main

HEAD@{n} 就是第 n 步之前的快照。注意它只存在于本地,不会推送,默认保留 90 天。

二、reset --hard 之后​

git reset --hard HEAD~3 # 手滑,三个提交没了
git reflog # 找到 reset 之前那条
git reset --hard HEAD@{1} # 回去

也可以只捞不挪:

git cherry-pick 3f2a1b0

三、rebase 搞砸了​

rebase 会留下一串 rebase (start) / rebase (finish) 记录,找到 rebase (start) 之前那一条:

git reflog | grep rebase
git reset --hard HEAD@{5}

rebase 中途想直接放弃,其实不用 reflog:

git rebase --abort

四、删掉的分支​

git branch -D feature-x # 删错了
git reflog # 找到该分支最后一次提交
git branch feature-x 9c8d7e6

如果连 reflog 都记不清,直接看所有分支引用里剩下的提交:

git log -g --oneline --all

五、提交没进任何分支​

在 detached HEAD 上直接 git checkout main,那个提交就没人引用了。这时用:

git fsck --lost-found
ls .git/lost-found/commit/
git show <id>

git fsck --unreachable 列出的就是所有不可达对象,理论上都能捞。

六、reflog 会失效的情况​

  • git gc --prune=now 清理过,或超过保留期
  • 从没在本地存在过(比如别人 force push 覆盖了远程)
  • clone 之后本地根本没拉过那些提交

所以救命的顺序是:先 reflog,再 fsck,最后看远程或同事的仓库。

七、把它变成习惯​

手滑之后的正确姿势:

  1. 千万别慌着继续操作,每次 commit / reset / merge 都会往 reflog 再压一条
  2. git reflog 定位目标
  3. 用 git reset --hard 回去,或用 git branch rescue <id> 先建个保险分支

第三条尤其推荐:先建分支把提交钉住,再慢慢折腾,怎么都不会丢。