Git 是当今使用最广泛的版本控制系统,它以多种方式跟踪更改,以确保您永远不会丢失已提交的更改。此外,它为您提供的对开发工作流程的控制意味着您可以准确地确定项目历史的样子。 Git 有几种重写提交历史的机制来帮助解决这个问题,包括git commit --amend
、 git rebase
和git reflog
。
在本文中,您将学习如何利用git reflog
有效且轻松地重新组织和重写您的 Git 提交历史,同时降低重写提交历史经常带来的风险。
Git 使用一个称为参考日志的系统,或者简称为“ reflog ”,来跟踪对分支提示的修改。引用,通常称为“ ref ”,是指向许多 Git 命令接受作为参数的提交或分支的指针。 git checkout
、 git reset
和git merge
是一些常见的 git 命令的示例,它们接受 refs 作为参数。
默认情况下,reflogs 会在过去 90 天内跟踪每个HEAD
位置。此外,reflog 历史记录是存储库独有的,无法远程访问。除了分支提示 reflog,还有一个单独的Git stash reflog。
Reflogs 存储在本地存储库的.git
目录下的特定目录中。这些git reflog
目录可以在.git/logs/refs/heads/.
, .git/logs/HEAD
和.git/logs/refs/stash
如果git stash
已在存储库中使用。
基本的 reflog 命令如下:
# To see activity on HEAD git reflog show
上述命令的输出类似于以下内容:
0a2e358 HEAD@{0}: reset: moving to HEAD~2 0254ea7 HEAD@{1}: checkout: moving from 2.2 to main c10f740 HEAD@{2}: checkout: moving from main to 2.2
其他常见用法如下:
# To see activity on HEAD, including timestamp git reflog show --date=relative #or git reflog --relative-date # same, on some_branch git reflog show --date=relative some_branch
默认情况下, git reflog
输出HEAD
ref 的 reflog。符号HEAD
表示当前活动的分支。也有可用于其他 refs 的 reflogs。用于访问 git ref 的语法是name@{qualifier}
,例如 - otherbranch@{0}
。除了HEAD
refs 之外,还可以引用其他分支、标签、远程和 Git stash。
要查看所有 refs 的完整 reflog,可以执行:
git reflog show --all
除了有序索引之外,每个 reflog 条目都附加了一个时间戳,它也可以用作 Git ref 指针语法的限定符标记。这就是启用按时间过滤这些 reflog 条目的原因。常用时间限定符的示例包括:
@{0}
@{6.minutes.ago}
@{2.hour.ago}
@{3.day.ago}
@{5.weeks.ago}
@{8.years.ago}
@{today}
@{2022-01-23.08:30:00}
@{1.day.10.hours.ago}
这些时间限定符可以组合起来(例如1.day.3.hours.ago
)。这些时间限定词的复数形式也被接受(例如5.minutes.ago
)。它们可以与git reflog
命令一起使用,如下所示:
git reflog show develop@{3.days.ago}
git reflog
接受一些额外的参数,这些参数因此被视为子命令,例如show
、 expire
和delete
。让我们详细讨论这些子命令。
如前所述,默认情况下会隐式传递show
。执行git reflog show
将显示传递参数的日志。
例如:
git reflog develop@{0}
是相同的
git reflog show develop@{0}
此外, git reflog show
是git log -g --abbrev-commit --pretty=oneline
的别名。
expire
子命令有助于清理旧的或无法访问的 reflog 条目。
expire
子命令有可能导致数据丢失。
然而,这个子命令通常不是由最终用户使用,而是由 git 内部使用。
可以通过将-n
或--dry-run
选项传递给git reflog expire
来执行“试运行”,以输出哪些 reflog 条目被标记为被修剪,这样它们就不会被实际修剪。在清理过期的 reflog 条目时,这可以作为安全网提供帮助。
此外,可以通过将命令行参数--expire=time
传递给git reflog expire
或设置 git 配置名称gc.reflogExpire
来指定过期时间。
顾名思义,delete 子命令删除传递的 reflog 条目。删除,如过期,有可能导致数据丢失,最终用户不经常使用。
git reflog
和git log
是 Git 提供的两个类似名称的组件,它们让我们可以潜入存储库的提交历史、日志和 reflogs。这两个组件经常表现出相同的历史,尤其是当开发人员在没有获取或拉取的情况下完成许多本地提交时,这一事实是 Git reflog 与日志混淆的原因之一。
但是,它们本质上是不同的,并且具有不同的用例。
让我们了解上述两个命令的潜在差异和相似之处。
Git reflog 和 log 之间最显着的区别是 log 是存储库提交历史的公共记录,而 reflog 是存储库本地提交的私有的、特定于工作区的记录。
在推送、获取或拉取之后,Git 日志将作为 Git 存储库的一部分进行复制。另一方面,Git reflog 不包含在重复的 repo 中。如果没有对保存本地存储库的计算机的物理访问,开发人员无法检查 reflog。
reflog 是在.git\logs\refs\heads
中找到的一个文件,它跟踪特定分支的本地提交历史记录,并排除可能已被 Git 垃圾收集进程删除的任何提交。另一方面,Git 日志提供了一个分支的历史提交遍历,该分支从最近的提交开始,到分支历史中的第一个提交结束。
Git reflog 可以在开发过程中用作安全网,因为如果您正确理解了 reflog 的概念,一旦提交,您就不会从您的 repo 中丢失数据。如果您无意重置为较旧的提交、错误地重新设置基准或执行任何其他明显“删除”的操作,您可以使用 reflog 查看您之前的位置并使用git reset --hard
回到该 ref 以恢复您之前的状态提交。
请记住,refs 指的是提交的完整历史记录,而不仅仅是提交本身。
在本文中,我们讨论了git reflog
的扩展配置选项、常见用例和git reflog
的陷阱。
总而言之,Git 会在您工作时在后台保留一个 reflog,它是您的HEAD
和分支引用在过去几个月(90 天)中的位置的日志。每当您的分支提示因任何原因被修改时,Git 都会在此临时历史记录中保存信息。
reflog 命令也可用于从 reflog 中删除或过期太旧的条目。 expire
子命令用于删除过期的 reflog 条目,而delete
子命令用于删除并指定要从 reflog 中删除的特定条目。