在 Git 中进行时间旅行以撤销破坏性错误的 3 种方法

2025-06-07

在 Git 中进行时间旅行以撤销破坏性错误的 3 种方法

如果你有同感,请举手:

🙋🙋🙋🙋🙋

我们都经历过这种情况!很容易出于好意git rebase,但git reset事后却发现一切都乱套了。由于你的更改会重写历史记录,唯一的办法就是删除所有内容,然后重新克隆代码库,从头再来。我以前经常这样做!

但是如果我告诉你有更好的方法呢?如果你可以回到进行那些破坏性更改之前的某个时间点,而无需删除任何内容,那会怎么样事实证明,Git 内置了用于实现这一点的工具,例如reflogORIG_HEADgitrevisions,而且它们出奇地好用!

回到未来截图及标题

在本文中,我将向您展示如何使用这些巧妙的快捷方式来撤消一些改变历史的举动并回到正确的时间线!

设置

如果您只想阅读示例,可以直接跳到下一部分。但如果您想在自己的终端上继续操作,请在终端中将以下每一行作为命令运行,以设置演示存储库:

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
Enter fullscreen mode Exit fullscreen mode

示例

以下是起始 git 历史记录的概述(使用命令git log --oneline --graph --all):

git 日志截图

有一个master分支包含 3 个文件,每个文件都通过单独的提交添加。此外还有一个单独的my-new-branch分支。该分支也包含前两个文件,但之后从主分支分离出来,并拥有自己的第三个文件。

以下是所有文件my-new-branch
ls -1 命令的屏幕截图

这是理想状态。当我们在下面的例子中搞乱了它时,我们的目标是将存储库恢复到这个状态。

reflog1. 使用参考重置

假设我们已开启my-new-branch重新定位到 master 上:

运行 git rebase 的终端截图

但是啊!我们意识到这是一个错误!也许代码在 rebase 之后就无法正常工作了。也许在 rebase 过程中发生了一些奇怪的合并冲突,导致一切都变得混乱。无论如何,我们都希望一切恢复原状,但新的 git log 显示的内容如下:

重新定基的 git 日志的屏幕截图

...并且我们的所有my-new-branch文件都与以下文件合并master
截图

显然,我们原来的提交(6cde040不见了。该如何恢复?我们可以删除所有内容并重新克隆,但我们可能没有最近更改的远程副本可供克隆。我们可以手动从旧提交创建一个新分支,然后重新添加/删除那里的文件,但这太麻烦了!

相反,尝试运行git reflog来查看历史上的一些有用点:
git reflog 输出的屏幕截图

Git reflog会显示我们当前工作HEAD所在的所有位置,包括git reset使用和 等破坏性命令删除的提交git rebase。只需选择进行破坏性更改之前的提交,将其硬重置为gitrevision编号(带有花括号的编号),所有内容就会重置为 rebase 之前的状态!

git reset --hard HEAD@{3}
Enter fullscreen mode Exit fullscreen mode

运行此命令后,我们git log将看到我们已恢复到原始状态,包括原始提交和原始文件:

原始树
啊啊啊,好久没碰过的树枝!

2. 重置为ORIG_HEAD

但是 Zak, ”您可能会说,“ reflog 输出有点令人困惑,而且很难确切地说出要返回到哪一行 reflog。

同意!还好,还有更简单的方法!

git reset --hard ORIG_HEAD
Enter fullscreen mode Exit fullscreen mode

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!'
Enter fullscreen mode Exit fullscreen mode

我们为什么要这么做?我不知道¯\_(ツ)_/¯。Stack Overflow 上可能有人觉得这个主意不错,所以我们一时兴起就尝试了一下,结果,事情变得很奇怪:
修改后日志截图
修改后文件的截图

所有提交的顺序都乱了,而且文件丢失了。该如何撤销?我们做了几个破坏性的更改,所以ORIG_HEAD无法正常工作。我们可以检查git reflog,但更改太多,可能很难找到正确的更改。我们只想让分支看起来和更改前 5 分钟的样子一样。

尝试一下:

git reset --hard HEAD@{5.minutes.ago}
Enter fullscreen mode Exit fullscreen mode

...之后,您将看到分支处于 5 分钟前的精确状态:

原始树
啊啊啊,好久没碰过的树枝!

是的,您没看错:这个命令实际上是用简单的英语告诉 Git 返回过去的某个时间,Git 就这么做了!

这里用到了 git 中一个强大的概念,叫做gitrevisions。其他一些例子包括:HEAD@{1.day.2.hours.ago}HEAD@{yesterday}HEAD@{2020-01-01 18:30:00}HEAD@{12:43}。换句话说:在 GIT 中,时间旅行是可能的!!!

注意事项

上述技术非常强大,但也有一些限制需要牢记:

  • 仅适用于您的本地终端- 您的refloggitrevisions 存储在本地,但在推送到远程存储库时不会共享
  • 仅适用于已提交的文件——如果你删除了未提交的文件,这些文件将不会存储在 Git 的任何位置。尽早提交并经常提交是有充分理由的。

回顾

因此,总而言之,当您resetrebase您的代码处于无法使用的状态后,这里有一些修复 repo 的方法:

  • 用于git reflog选择历史记录中的某个点,然后撤消最近的更改git reset --hard HEAD@{<number>}
  • 作为快捷方式,用于git reset --hard ORIG_HEAD撤消最近的破坏性操作
  • 重置<refname>@<relative time>时间旅行回到更快乐的状态(例如git reset --hard HEAD@{10.minutes.ago}

希望以上内容能帮到大家!如果你有任何问题,或者有其他关于 git 修复错误的方法,欢迎在评论区留言!


你觉得这篇文章有用吗?欢迎关注我的个人主页或关注我的 Twitter 账号,获取更多开发者技巧和文章公告!

文章来源:https://dev.to/zaklaughton/3-ways-to-time-travel-in-git-to-undo-destroy-mistakes-inm
PREV
捍卫 Angular 框架
NEXT
2020 年我最喜欢的隐私工具:互联网上更安全!