KernelRanch
返回文章列表

Git 提交突然消失:用 reflog 找回改写前的分支

2026-11-13 #git#recovery#debugging

执行 git commit --amend 后突然发现原提交还有一份需要保留的内容。当前分支只显示新提交,但本地 reflog 可能保存分支移动前的提交 ID。先确认旧提交的内容,然后为它创建救援分支。

1. 在临时仓库模拟一次 amend

保存为 reflog_lab.py,运行 python reflog_lab.py。需要 Git 在 PATH 中。脚本只改动临时仓库。

from pathlib import Path
import subprocess
from tempfile import TemporaryDirectory

with TemporaryDirectory() as tmp:
    def git(*args):
        return subprocess.check_output(["git", *args], cwd=tmp, text=True).strip()

    git("init", "-q")
    git("config", "core.logAllRefUpdates", "true")
    git("config", "user.name", "Reflog Lab")
    git("config", "user.email", "lab@example.com")
    git("config", "commit.gpgsign", "false")
    git("config", "core.hooksPath", str(Path(tmp) / "empty-hooks"))
    path = Path(tmp) / "notes.txt"
    path.write_text("first draft", encoding="utf-8")
    git("add", "notes.txt")
    git("commit", "-qm", "first draft")
    original = git("rev-parse", "HEAD")

    path.write_text("revised draft", encoding="utf-8")
    git("add", "notes.txt")
    git("commit", "--amend", "-qm", "revise draft")
    revised = git("rev-parse", "HEAD")
    assert original != revised
    candidate = git("rev-parse", "HEAD@{1}")
    assert candidate == original
    assert git("show", f"{candidate}:notes.txt") == "first draft"

    git("branch", "rescue/draft", candidate)
    assert git("show", "rescue/draft:notes.txt") == "first draft"
    assert git("show", "HEAD:notes.txt") == "revised draft"
    print("reflog: old commit found")
    print("rescue/draft: first draft")
    print("current HEAD: revised draft")

本地实测输出如下。

reflog: old commit found
rescue/draft: first draft
current HEAD: revised draft

2. 在实际仓库恢复已有提交

先运行 git reflog --date=iso,根据操作时间与 amend、reset 或 rebase 记录找候选。查看特定分支的移动记录时,使用 git reflog show <分支>。演练中的 HEAD@{1} 刚好指向上一次提交,平时的 checkout、reset 等操作会不断推进编号,因此找到候选后应抄下完整 ID。

操作命令
检查候选提交及改动git show <提交ID>
建立可以长期引用的分支git branch rescue/draft <提交ID>
比较救援分支和当前版本git diff HEAD rescue/draft

救援分支建立后,可以选择从旧版本恢复特定文件,或把需要的改动合入当前分支。reflog 属于本地仓库,重新克隆通常无法提供旧机器上的操作记录。记录可能过期,已经被清理的对象无法用此法找回。未进入提交的编辑应先查看编辑器历史和备份。

小结

先从 reflog 找提交 ID,再从该 ID 校验内容并创建救援引用。恢复过程中可以保留当前分支,等 diff 确认后再决定如何合入。

数据来源与检索时间

检索时间 2026-11-13。

来源URL对应内容
Git reflog 手册https://git-scm.com/docs/git-reflog引用历史、时间选择器与过期行为

核验说明

  • Git 2.54.0.windows.1 实测 amend 后按完整 ID 恢复旧提交。案例覆盖已提交的文件。