Git 是现代软件开发中不可或缺的工具。然而,强大的命令如 git reset –hard 或不小心删除分支,可能会导致本地提交记录“丢失”。幸运的是,Git 提供了一个本地的安全网:git reflog。本文将详细解释 reflog 的工作原理,并提供两个实操示例,教你如何找回丢失的提交。
1. 什么是 Git Reflog?
很多人熟悉 git log,它展示了提交历史(Commit Tree)。但 reflog(Reference Log,引用日志)记录的不是提交之间的关系,而是本地 HEAD 指针及其他引用(如分支)的移动历史。
想象一下,git log 记录了你房子的结构图,而 git reflog 记录了你在房子里走过的每一步路。
reflog 记录了所有本地操作,包括克隆、拉取、提交、重置、合并、变基等,通常默认保留 90 天的历史记录。
2. 如何查看 Reflog
在任何 Git 仓库中执行以下命令:
git reflog
# 或者使用 'git log -g' 查看更详细的日志
输出示例:
8d29c4f HEAD@{0}: commit: C3: The current commit
7a1b3e5 HEAD@{1}: reset: moving to HEAD^ # HEAD 曾移动到这里
b2c9a0d HEAD@{2}: commit: C2: Commit to be lost
a4f6d89 HEAD@{3}: initial commit
这里的关键是 HEAD@{index},它指向了特定时间点的 HEAD 状态。HEAD@{0} 永远是当前状态。
3. 场景一:找回因 git reset –hard 丢失的提交
git reset –hard 是一个强大的命令,它可以将 HEAD 和工作区恢复到指定状态,并丢弃所有后续提交。如果操作失误,这些被丢弃的提交看似消失了,但实际上它们仍保存在 reflog 中。
3.1 模拟丢失提交
假设我们有 C1 -> C2 -> C3 三个提交。现在我们强制重置回 C1,导致 C2 和 C3 丢失。
# 假设当前在 C3 (e0e98c7)
git log --oneline
# e0e98c7 (HEAD -> main) C3: Latest feature
# 5f9a1b2 C2: Another change
# 1c3d4e5 C1: Initial commit
# 强制重置到 C1 (即 HEAD~2)
git reset --hard HEAD~2
# 检查日志,C2 和 C3 确实丢失了
git log --oneline
# 1c3d4e5 (HEAD -> main) C1: Initial commit
3.2 使用 Reflog 恢复
- 查看 Reflog 找到丢失的提交哈希:
git reflog
你会在日志中看到重置操作,并在重置前找到 C3 的哈希值(例如 e0e98c7)。它可能位于 HEAD@{1} 或 HEAD@{2}。
e0e98c7 HEAD@{1}: reset: moving to HEAD~2
... # 找到这个哈希!
- 恢复提交或分支:
现在我们找到了丢失的提交哈希 e0e98c7,我们可以通过创建新的分支来指向它,从而将提交带回工作区。
# 将丢失的提交恢复到一个名为 'recovered-feature' 的新分支上
git branch recovered-feature e0e98c7
git checkout recovered-feature
# 切换回主分支并合并(如果需要)
git checkout main
git merge recovered-feature
4. 场景二:恢复被删除的分支
如果一个本地分支被删除(例如使用 git branch -D old-feature),虽然分支指针消失了,但该分支上的提交仍然存在于仓库中,并且 reflog 记录了 HEAD 最后一次指向该分支时的状态。
4.1 模拟删除分支
# 确保有一个独立分支并提交
git checkout -b delete-me
echo "temp content" > temp.txt
git add .
git commit -m "C_Delete: Last commit on delete-me branch"
git checkout main
# 删除分支
git branch -D delete-me
# Deleted branch delete-me (was <LAST_COMMIT_HASH>).
4.2 使用 Reflog 恢复
- 查看 Reflog 找到分支最后的哈希:
执行 git reflog,找到在切换回 main 之前,HEAD 最后一次指向 delete-me 分支的记录。查找包含 checkout 或 commit 操作的记录,它会带有该分支的最新提交哈希。
git reflog
# ...
5c3e0d2 HEAD@{2}: checkout: moving from delete-me to main
6f2a7b1 HEAD@{3}: commit: C_Delete: Last commit on delete-me branch
# 6f2a7b1 就是我们需要的哈希。
- 重新创建分支:
使用找到的哈希值 6f2a7b1 重新创建同名的分支。
# 重新创建分支 delete-me,指向该提交
git branch delete-me 6f2a7b1
# 切换回该分支,所有提交记录都回来了
git checkout delete-me
总结
git reflog 是本地 Git 仓库的“时间机器”,它是你在执行破坏性操作(如 reset –hard 或删除分支)后恢复提交记录的最后一道防线。只要提交存在于你的本地仓库中且未过期,reflog 几乎总能帮你找回它们。
汤不热吧