Git 重置 --Hard git reset --explain

2025-06-07

Git 重置 --Hard git reset --explain

Reset 可能是最难理解的 git 命令之一,而且它还因危险而声名狼藉。这两种说法都有其合理性:是的,reset 命令确实比较难理解,在某些情况下甚至可能很危险。但其实它并没有那么难。因此,在这篇文章中,我将尽力为您呈现一个清晰精炼的 reset 命令教程。为了简洁明了,避免内容过于冗长,我提取了一些非必要的细节并进行了简化。如果您想进一步了解 git 的内部工作原理,也可以查看我的“理解 Git”系列文章,了解更多此处介绍的内容。

Git 树

在深入研究reset命令之前,我们需要先了解一下 git 的树:工作目录、暂存区和存储库。

git thress

从 git 的角度来看,你可以将它们视为可以存储更改的区域:

  • 工作目录 - 文件系统上的项目文件
  • 暂存区——下一次提交的预览
  • 存储库——git 保存所有(过去)提交的数据存储。

命令reset作用于这三个/区域,但是首先,让我们看看我们日常使用的命令是如何影响这些区域的addcommit


假设我们有一个 Web 应用,并对index.php文件进行了一些重构。我们所做的更改会反映在工作目录中:

工作流程-1

我们可以通过运行以下命令来确认这一点git status

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
modified:   index.php
Enter fullscreen mode Exit fullscreen mode

现在我们使用以下add命令将这些更改移动到暂存区:

工作流程-2

现在运行status命令将告诉我们:

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
modified:   index.php
Enter fullscreen mode Exit fullscreen mode

因为status命令发现我们index.php在工作目录和暂存区中都有相同版本的文件,但在存储库中却没有。

要将其添加到那里,我们使用以下commit命令:

工作流程-3

现在工作目录、暂存区和存储库都包含相同版本的index.php,运行git status会告诉我们:

nothing to commit, working tree clean
Enter fullscreen mode Exit fullscreen mode

因此,该命令的工作方式status是比较工作目录、暂存区和存储库中的文件版本,如果存在不同,则有文件需要暂存/提交。


假设我们现在index.php进一步重构文件,并再次执行整个添加/提交循环。
现在我们的工作目录、暂存区和存储库都包含了文件的第二个版本index.php

工作流程-4

但是第一个版本怎么办呢?如果你还记得的话,我们说过 Repository 会保留所有之前的提交,所以index.php文件的第一个版本仍然存在:

工作流程-5

为了跟踪我们index.php文件的当前版本,存储库有一个特殊的指针,称为HEAD指向当前版本(并且该命令仅在将其与暂存区的版本进行比较时status查看指向的当前版本)。HEAD


现在我们已经了解了这些,我们终于可以转到我们的reset命令并通过操纵这些区域的内容来查看它是如何工作的。

重置--软

第一种命令模式reset只会做一件事:

  • 移动HEAD指针。

在我们的例子中,我们将通过执行以下操作将其移动到上一个提交(的第一个版本index.php):git reset --soft HEAD~1

重置软-1

git 的树现在看起来像这样:

重置软件-2

如果我们运行,git status我们会看到一条熟悉的消息:

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)
modified:   index.php
Enter fullscreen mode Exit fullscreen mode

因此,运行git reset --soft HEAD~1基本上撤消了我们的最后一次提交,但该提交中包含的更改并没有丢失 - 它们位于我们的暂存区和工作目录中。

重置 - 混合

该命令的第二种模式reset将执行两件事:

  • 移动HEAD指针
  • HEAD更新暂存区(使用指向的内容)

所以,第一步和模式一样--soft。第二步获取HEAD指向的内容(在本例中是index.php文件的第一个版本)并将其放入暂存区。

重置混合-1

因此,运行后git reset --mixed HEAD~1我们的区域如下所示:

重置混合-2

如果我们现在运行,git status我们会再次看到一条熟悉的消息:

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
modified:   index.php
Enter fullscreen mode Exit fullscreen mode

因此,运行git reset - mixed HEAD~1撤消了我们的最后一次提交,但这次来自该提交的更改(仅)在我们的工作目录中。

重置--hard

现在来看看臭名昭著的困难模式。跑步reset -- hard会做三件事:

  • 移动HEAD指针
  • HEAD更新暂存区(使用指向的内容)
  • 更新工作目录以匹配暂存区

因此,前两个步骤与第三步相同--mixed,使工作目录看起来像暂存区(已经填充了HEAD指向的内容)。

重置硬-1

因此,运行后git reset --hard HEAD~1我们的区域如下所示:

重置硬-2

跑步git status会给我们带来:

nothing to commit, working tree clean
Enter fullscreen mode Exit fullscreen mode

因此,运行git reset - hard HEAD~1撤销了我们上一次提交,并且该提交中包含的更改现在既不在工作目录也不在暂存区了。但它们并没有完全丢失。Git 不会从仓库中删除提交(实际上,它有时会删除,但很少),所以这意味着我们的第二个版本的提交仍然在仓库中,只是有点难以找到(您可以通过查看reflog来跟踪它)。

那么,为什么说 reset 很危险呢?嗯,有一种情况可能会导致一些更改永久丢失。考虑这样一种情况:在第二次提交后,你对index.php文件做了一些更改,但没有将它们暂存并提交:

重置硬-3

现在运行git reset --hard HEAD~1

重置硬-4

由于该reset命令将覆盖您的工作目录的内容以匹配暂存区(即匹配HEAD),并且您从未暂存和提交您的更改(存储库中没有对这些更改的提交),因此所有这些更改现在都会随着时间而丢失...就像雨中的泪水一样。

硬重置的危险之处在于它不安全于工作目录——这意味着如果你在工作目录中更改了文件,而这些文件在运行硬重置时会被覆盖(并丢失),它不会发出任何警告。所以,硬重置时要格外小心。


好了,这就是reset命令。希望我解释得很清楚,并且你会同意它其实并不难。当然,它可能很危险,但前提是必须与--hard选项一起使用。 

正如开头所说,如果您想了解更多有关 git 内部工作原理的信息,您可以查看我的《理解 Git》系列,如果您想要更深入地解释该reset命令,您可以查看git pro 书中的《重置揭秘》一章。

附录:

  • 在示例中,我们使用了HEAD~1checksum 作为命令的参数。你可能已经知道,git 中的每个提交都有一个唯一的标识符,称为 checksum,我们reset可以将其用作命令的参数。reset

校验和

  • 为了使示例更简单,我们只编辑并提交了一个文件,实际上,我们经常提交多个文件,因此特定的提交包含多个文件的不同版本。

提交

  • 特殊HEAD指针通常不直接指向提交(如示例中所示),而是指向一个分支指针,该分支指针指向特定的提交

指针

文章来源:https://dev.to/konrad_126/git-reset---explain-49bl
PREV
文本转 GIF 动画 — React Pet 项目开发日志
NEXT
前端捆绑器头脑风暴