在 Git 中进行时间旅行以撤销破坏性错误的 3 种方法
如果你有同感,请举手:
🙋🙋🙋🙋🙋
我们都经历过这种情况!很容易出于好意git rebase
,但git reset
事后却发现一切都乱套了。由于你的更改会重写历史记录,唯一的办法就是删除所有内容,然后重新克隆代码库,从头再来。我以前经常这样做!
但是如果我告诉你有更好的方法呢?如果你可以回到进行那些破坏性更改之前的某个时间点,而无需删除任何内容,那会怎么样?事实证明,Git 内置了用于实现这一点的工具,例如reflog
、ORIG_HEAD
和gitrevisions
,而且它们出奇地好用!
在本文中,我将向您展示如何使用这些巧妙的快捷方式来撤消一些改变历史的举动并回到正确的时间线!
设置
如果您只想阅读示例,可以直接跳到下一部分。但如果您想在自己的终端上继续操作,请在终端中将以下每一行作为命令运行,以设置演示存储库:
mkdir reflog-demo && cd reflog-demo && git init
echo "file 1 content" > file1.txt && git add . && git commit -m "add file1"
echo "file 2 content" > file2.txt && git add . && git commit -m "add file2"
git checkout -b my-new-branch
echo "new branch file content" > newBranchFile.txt && git add . && git commit -m "add newBranchFile"
git checkout master
echo "file 3 content" > file3.txt && git add . && git commit -m "add file3"
git checkout my-new-branch
示例
以下是起始 git 历史记录的概述(使用命令git log --oneline --graph --all
):
有一个master
分支包含 3 个文件,每个文件都通过单独的提交添加。此外还有一个单独的my-new-branch
分支。该分支也包含前两个文件,但之后从主分支分离出来,并拥有自己的第三个文件。
这是理想状态。当我们在下面的例子中搞乱了它时,我们的目标是将存储库恢复到这个状态。
reflog
1. 使用参考重置
假设我们已开启my-new-branch
并重新定位到 master 上:
但是啊!我们意识到这是一个错误!也许代码在 rebase 之后就无法正常工作了。也许在 rebase 过程中发生了一些奇怪的合并冲突,导致一切都变得混乱。无论如何,我们都希望一切恢复原状,但新的 git log 显示的内容如下:
...并且我们的所有my-new-branch
文件都与以下文件合并master
:
显然,我们原来的提交(6cde040
)不见了。该如何恢复?我们可以删除所有内容并重新克隆,但我们可能没有最近更改的远程副本可供克隆。我们可以手动从旧提交创建一个新分支,然后重新添加/删除那里的文件,但这太麻烦了!
相反,尝试运行git reflog
来查看历史上的一些有用点:
Git reflog会显示我们当前工作HEAD
所在的所有位置,包括git reset
使用和 等破坏性命令删除的提交git rebase
。只需选择进行破坏性更改之前的提交,将其硬重置为gitrevision编号(带有花括号的编号),所有内容就会重置为 rebase 之前的状态!
git reset --hard HEAD@{3}
运行此命令后,我们git log
将看到我们已恢复到原始状态,包括原始提交和原始文件:

2. 重置为ORIG_HEAD
“但是 Zak, ”您可能会说,“ reflog 输出有点令人困惑,而且很难确切地说出要返回到哪一行 reflog。 ”
同意!还好,还有更简单的方法!
git reset --hard ORIG_HEAD
ORIG_HEAD
自动指向最近一次破坏性更改之前的状态,因此我们可以轻松地撤消最近的更改rebase
,或者reset
只需一个命令!git reset --hard ORIG_HEAD
在这里运行与上面示例中的运行完全相同,git reset --hard HEAD@{3}
而无需查找特定的修订版本!
3. 重置为相对时间
🌟🌟🌟警告:这真的很酷
重置ORIG_HEAD
固然很好,但如果我们真的陷入了破坏性改变的泥潭怎么办?让我们以上一个示例中的 rebase 为例,并添加一些步骤:
git rebase master
git reset --hard HEAD~2
git commit --amend -m 'shablagoo!'
我们为什么要这么做?我不知道¯\_(ツ)_/¯。Stack Overflow 上可能有人觉得这个主意不错,所以我们一时兴起就尝试了一下,结果,事情变得很奇怪:
所有提交的顺序都乱了,而且文件丢失了。该如何撤销?我们做了几个破坏性的更改,所以ORIG_HEAD
无法正常工作。我们可以检查git reflog
,但更改太多,可能很难找到正确的更改。我们只想让分支看起来和更改前 5 分钟的样子一样。
尝试一下:
git reset --hard HEAD@{5.minutes.ago}
...之后,您将看到分支处于 5 分钟前的精确状态:

是的,您没看错:这个命令实际上是用简单的英语告诉 Git 返回过去的某个时间,Git 就这么做了!
这里用到了 git 中一个强大的概念,叫做gitrevisions。其他一些例子包括:HEAD@{1.day.2.hours.ago}
、HEAD@{yesterday}
、HEAD@{2020-01-01 18:30:00}
、HEAD@{12:43}
。换句话说:在 GIT 中,时间旅行是可能的!!!
注意事项
上述技术非常强大,但也有一些限制需要牢记:
- 仅适用于您的本地终端- 您的
reflog
gitrevisions 存储在本地,但在推送到远程存储库时不会共享 - 仅适用于已提交的文件——如果你删除了未提交的文件,这些文件将不会存储在 Git 的任何位置。尽早提交并经常提交是有充分理由的。
回顾
因此,总而言之,当您reset
或rebase
您的代码处于无法使用的状态后,这里有一些修复 repo 的方法:
- 用于
git reflog
选择历史记录中的某个点,然后撤消最近的更改git reset --hard HEAD@{<number>}
- 作为快捷方式,用于
git reset --hard ORIG_HEAD
撤消最近的破坏性操作 - 重置
<refname>@<relative time>
时间旅行回到更快乐的状态(例如git reset --hard HEAD@{10.minutes.ago}
)
希望以上内容能帮到大家!如果你有任何问题,或者有其他关于 git 修复错误的方法,欢迎在评论区留言!
文章来源:https://dev.to/zaklaughton/3-ways-to-time-travel-in-git-to-undo-destroy-mistakes-inm