跳至主要内容

Git Reflog:理解并使用 Git 中的引用日志

了解 git reflog 如何帮助跟踪与找回丢失提交、撤销失误并管理分支历史。探索实用用例与命令,掌握 git reflog。
· 10分钟 阅读

使用 AI 探索

对于开发者或数据工程师来说,没有什么比不小心删除 Git 分支或在不想要的时候重置提交更让人头疼了。这也是我很高兴分享自己踩坑后学到、并希望更早就掌握的一项技能:如何使用 git reflog。git reflog 是非常值得学习的技巧之一;现在投入一点时间,日后就能少掉很多麻烦。 

本文将向您展示我在排错与从失误中恢复时非常好用的 git reflog。同时,我也推荐我们的 Foundations of Git 和 Introduction to GitHub Concepts 课程,全面学习版本控制相关知识。

什么是 git reflog?

Git reflog(引用日志)是一种本地跟踪机制,用于记录 Git 仓库中分支尖端与 HEAD 引用的更新。(在 Git 语境中,HEAD 指当前工作目录与暂存区所基于的提交。)

与根据祖先关系展示提交历史的 git log 不同(即显示分支中各提交如何相连),git reflog 会捕获 HEAD 的所有移动,包括切换分支、变基、重置与提交等。这使得 reflog 在找回丢失提交和调试最近操作时非常有用。

Reflog 条目何时创建?

凡是会改变 HEAD 或分支引用状态的操作,都会创建 Reflog 条目。常见情形包括:

  • 使用 git commit 提交更改。

  • 使用 git checkout branch_name 切换到其他分支。

  • 使用 git branch new_branch 创建新分支。

  • 变基 git rebase

  • 重置到之前的提交 git reset --hard。

  • 使用 git merge 合并分支。

下面的代码用于跟踪本地仓库中的更新:

git reflog

使用 git reflog 跟踪本地仓库中的更新。

使用 git reflog 跟踪本地仓库中的更新。图片来源:作者。

如何解读 git reflog 的输出?

您可以按如下方式解读输出:

  • HEAD@{0}:最近一次操作是切换到 HEAD 分支。

  • HEAD@{1}:在此之前,我将文件类型从 .xlxs 更改为 .csv 格式。

  • HEAD@{2}:我在将文件推送到仓库时进行了第一次提交。

每条记录都会显示:

  • 提交哈希(fa82776)

  • Reflog 索引(HEAD@{0}、HEAD@{1} 等)

  • 所执行操作的描述(commit、checkout、rebase)

如何使用 git reflog

Git reflog 提供了一种跟踪引用更新并还原仓库先前状态的方式。理解如何在 reflog 条目间导航,您就能找回丢失的提交、撤销更改,并比较以往版本。 

基础 git reflog 命令

如下是基本的 reflog 命令:

git reflog

上述命令会显示近期所有更新了 HEAD 或分支引用的操作列表,包括提交、切换分支、重置、变基等。每个条目都有索引,如 HEAD@{0}、HEAD@{1},用于表示其在 reflog 历史中的位置。

引用过去的状态

Git reflog 记录了过去的引用更新,使我们能够定位并恢复仓库历史中的早前节点。没有它,这些引用将不存在,我们就需要准确的提交哈希才能回到特定的过去状态。现在,让我们看看 Git 如何通过 git checkout 来帮助我们在这些过去状态之间导航。

HEAD@{n}:指向特定的 reflog 条目,其中 n 为索引。例如,HEAD@{2} 指向 HEAD 的第三近状态。

git checkout HEAD@{2}

使用 git checkout 跟踪过去的更改。

使用 git checkout 跟踪过去的更改。图片来源:作者。

branch@{time}:指向某个分支在特定时间点的状态。例如,main@{1.week.ago} 指代主分支一周前的状态,而 feature@{yesterday} 指代功能分支在昨天的状态。

git checkout main@{1.week.ago}

使用 git checkout 跟踪过去的更改。

使用 git checkout 跟踪过去的更改。图片来源:作者。

基于时间的限定符

git reflog 不仅帮助我们还原过去状态,还能用来比较它们。由于 reflog 会跟踪引用更新,我们可以借此查看仓库随时间的变化。现在,让我们看看 git diff 如何利用 reflog 条目来比较过去与现在的状态。

以下是一些时间限定符示例,使您无需仅依赖 reflog 索引号,也能轻松将仓库恢复到特定时间点。

git checkout HEAD@{1.minute.ago}     # 一分钟前的状态
git checkout HEAD@{1.hour.ago}       # 一小时前的状态
git checkout HEAD@{1.week.ago}       # 一周前的状态
git checkout HEAD@{yesterday}        # 昨天的状态
git checkout HEAD@{2024-01-01.12:00:00} # 指定时间戳的状态

使用 git diff 比较过去状态

您可以用 git diff 等命令比较过去的状态。下面的命令将当前主分支状态 main@{0} 与其一天前的状态 main@{1.day.ago} 进行比较。输出会显示这两个快照之间的差异。

git diff main@{0} main@{1.day.ago}

使用 git diff 比较过去状态。

使用 git diff 比较过去状态。图片来源:作者。

Git Reflog 的常见用例

Git reflog 是找回丢失更改、撤销失误与修复常见 Git 问题的利器。以下是一些 git reflog 能派上用场的实用场景。

撤销错误的重置

若您不小心使用 git reset --hard 重置了分支,可以通过 reflog 恢复之前的状态。

git reset --hard HEAD@{3}

找回丢失的提交

如果您因删除分支、重置或变基而丢失了提交,可以使用 git reflog 找到丢失的提交。

git reflog

从 reflog 输出中定位提交哈希并检出:

git checkout <commit-hash>

确认丢失的提交后,您可以创建新分支来保存它:

git branch recovered-branch <commit-hash>

修复失败的变基

若变基出现问题,您可以使用 git reflog 找到变基前的提交并重置分支。先确认变基之前的提交,再进行重置。

git reset --hard HEAD@{3}  # 根据 reflog 输出调整数字

恢复被删除的分支

如果您误删了分支,可以借助 git reflog 找回。找到被删分支的最后一次提交并重新创建它:

git branch restored-branch <commit-hash>

跟踪 stash 历史

Git reflog 也可用于查看 stash 历史。下面的命令会列出 stash 操作,使您在需要时能找回较早的 stash。

git reflog stash

要应用先前的某个 stash 条目,请使用以下命令:

git stash apply stash@{2}

查看我们的教程 Git Pull Force: How to Overwrite a Local Branch With Remote,了解覆盖本地更改的最佳实践。

Git Reflog 的子命令与选项

Git 提供了多种用于管理与交互 reflog 的子命令与选项。 

Git reflog 子命令

下面是关键 git reflog 子命令及其用法的结构化概览。

git reflog show:默认显示 HEAD 的 reflog 条目,或显示指定引用(如某个分支)的条目。

git reflog show

使用 git reflog show 显示指定引用的条目。

使用 git reflog show 显示指定引用的条目。图片来源:作者。

git reflog list:显示所有带有 reflog 的引用。用于识别哪些分支与 HEAD 引用存有 reflog 条目。

git reflog list

git reflog delete <ref>@{<specifier>}:清理超过指定时间限制的旧 reflog 条目。例如,下面的命令会移除早于 30 天的条目。

git reflog expire --expire=30.days.ago

git reflog delete <ref>@{<specifier>}:根据引用与位置删除某个特定的 reflog 条目。下面的命令会移除 HEAD 的索引为 2 的条目。

git reflog delete HEAD@{2}

git reflog exists <ref>:验证某个引用是否存在 reflog。例如,下面的命令若主分支存在 reflog 则返回成功。

git reflog exists main

git reflog 子命令的选项

以下是 git reflog 子命令可用选项及其用法。

--expire-unreachable=<time>:仅修剪从任何引用都不可达的 reflog 条目。例如,下面的命令会移除 7 天前的不可达条目。

git reflog expire --expire-unreachable=7.days.ago

--all:处理所有引用的 reflog,而不只是 HEAD。下面的命令会清理所有分支上早于 60 天的 reflog。

git reflog expire --expire=60.days.ago --all

--dry-run:模拟执行命令,显示会被修剪的条目而不实际删除。如下命令将显示哪些条目会被移除。

git reflog expire --expire=30.days.ago --dry-run

--verbose:提供命令执行的详细输出。如下命令在过期旧的 reflog 条目时会显示详细信息。

git reflog expire --expire=90.days.ago --verbose

Git Reflog 与 Git Log:关键差异

git log 与 git reflog 都能帮助了解仓库历史,但用途不同。让我们看看它们的差异,以便理解如何将其用于版本控制与恢复策略。

  • git log 通过跟随分支中提交的祖先关系来展示提交历史,提供仓库内容如何演进的时间顺序视图。

  • git reflog 记录对 HEAD、分支与 stash 等引用的更新,包括切换分支、重置、变基等操作。它会跟踪不一定属于提交祖先链的变化。

  • git reflog 严格来说只存在于您的本机,不会与远程仓库共享。

  • 当 git log 无法恢复已不在分支祖先链中的提交时,git reflog 可通过跟踪引用更新帮助找回这些“丢失”的提交,即便它们已不再能从任何分支到达。

下表总结了这些关键差异。

Featuregit loggit reflog
Tracks commitsYesNo
Track reference updatesNoYes
Shared in remoteYesNo
Can recover lost commitsNoYes

使用 Git Reflog 的最佳实践

Git reflog 是找回丢失提交与修复历史问题的强大工具,但要高效使用也需谨慎。以下是在使用 reflog 时应遵循的一些最佳实践。

  • 将 Reflog 用于恢复与调试: 如果您不小心错误地 reset 或 rebase 了分支,请查看 git reflog 找到先前的引用并将其恢复。

  • 谨慎使用 git reset --hard:git reset --hard 会永久移除未提交的更改。操作前务必先查看 reflog,确保在出错时可恢复。

  • 在执行破坏性命令前保留备份: 为防止数据丢失,请对 Git 仓库实施自动化备份。始终将备份安全地存放在异地,以便在硬件故障或其他灾害发生时仍可恢复。

  • 不要将 Reflog 作为长期恢复的唯一依赖: 默认情况下,reflog 条目会保留 90 天。之后可能被垃圾回收而无法找回。请定期将提交推送到远程仓库,确保其在本地 reflog 之外也能被保存。

  • 使用 git reflog expire 管理旧条目:如果仓库的 reflog 变得臃肿,请使用 git reflog expire 修剪更旧或不可达的条目。

结语

要高效管理 Git 中的项目历史,光靠基础命令还不够。深入了解跟踪引用更新的高级工具,能为找回丢失提交、恢复被删分支与纠正失误提供宝贵的安全网。结合 git reset、git checkout 与 git revert 等命令进行上手实践,能显著提升您在版本控制方面的熟练度。

参加我们的课程不仅是学习的好方式,也能向雇主表明您对软件开发的重视。为此,我建议阅读我们的博文 Top 20 Git Interview Questions and Answers for All Levels,并学习全新的 Git Fundamentals 技能路径,全面精通 Git。

Allan Ouko

作者

Allan Ouko

数据科学技术写作者,具备数据分析、商业智能和数据科学的一线实践经验。我撰写以行业为导向的实用内容,涵盖 SQL、Python、Power BI、Databricks 和数据工程,并以真实的分析工作为基础。我的写作兼顾技术深度与业务影响,帮助专业人士将数据转化为自信的决策。

git reflog 常见问答

什么是 Git reflog?

Git reflog 是一种引用日志,用于跟踪分支尖端与 HEAD 的更新,帮助用户找回丢失提交并撤销失误。

Git reflog 与 Git log 有何不同?

git log 跟踪提交历史,而 git reflog 记录对 HEAD 与分支等引用的变更。

Git reflog 会与远程仓库共享吗?

不会,git reflog 是本地的,在推送到远程仓库时不会被共享。

reflog 会跟踪未提交的更改吗?

不会,reflog 只跟踪引用的变更,不记录未提交的工作。

哪些操作会创建 reflog 条目?

提交、切换分支、重置、变基、合并以及 stash 操作都会生成 reflog 条目。

reflog 条目会保存多久?

默认情况下,reflog 条目会在 90 天后过期,但可进行配置。

主题