Close

Resetting, checking out & reverting


The git reset, git checkout, and git revert commands are some of the most useful tools in your Git toolbox. They all let you undo some kind of change in your repository, and the first two commands can be used to manipulate either commits or individual files.

因为它们非常相似,所以很容易混淆在任何给定的开发场景中应该使用哪个命令。在本文中,我们将比较 git resetgit checkoutgit revert 的最常见配置。希望您能够放心地使用这些命令中的任何一个在存储库中导航。

Git 的三棵树

It helps to think about each command in terms of their effect on the three state management mechanisms of a Git repository: the working directory, the staged snapshot, and the commit history. These components are sometimes known as "The three trees" of Git. We explore the three trees in depth on the git reset page. Keep these mechanisms in mind as you read through this article.

签出是将 HEAD 引用指针移动到指定提交的操作。要演示这一点,请看下方示例。

将 HEAD 引用指针移至指定提交
数据库
相关资料

如何移动完整的 Git 存储库

Bitbucket 徽标
查看解决方案

了解 Bitbucket Cloud 的 Git

此示例演示了 main 分支上的一系列提交。HEAD 引用和 main 分支引用当前指向提交 d。现在我们来执行 git checkout b

Sequence of commits on the main branch

这是对“提交历史记录”树的更新。git checkout 命令可以在提交或文件级范围中使用。文件级签出会将文件的内容变更为特定提交内容。

还原是一种操作,它接受指定的提交并创建一个新的提交,该提交与指定提交相反。git revert 只能在提交级别范围运行,没有文件级功能。

重置是一种操作,它接受指定的提交,并将“三棵树”重置为与该指定提交时存储库的状态相匹配。重置可以在与三棵树相对应的三种不同模式下调用。

签出和重置通常用于在本地或私人“撤销”。它们修改了存储库的历史记录,推送到远程共享存储库时可能会导致冲突。还原被认为是“公共撤销”的安全操作,因为它会创建新的历史记录,可以远程共享,并且不会覆盖远程团队成员可能依赖的历史记录。

Git reset vs revert vs checkout reference


下表总结了所有这些命令的最常见用例。一定要随身携带这份参考资料,因为在您的 Git 职业生涯中,您肯定需要用到其中一些信息。

命令

范围

常见用例

git reset

范围

提交级别

常见用例

Discard commits in a private branch or throw away uncommitted changes

git reset

范围

文件级

常见用例

取消暂存文件

git checkout

范围

提交级别

常见用例

在分支之间切换或检查旧快照

git checkout

范围

文件级

常见用例

丢弃工作目录中的变更

git revert

范围

提交级别

常见用例

撤销公共分支中的提交

git revert

范围

文件级

常见用例

(不适用)

Commit level operations


您传递给 git resetgit checkout 的参数决定了它们的范围。当您没有将文件路径作为参数时,它们会对整个提交进行操作。这就是我们将在本节探讨的内容。请注意,git revert 没有文件级对应物。

Reset a specific commit

在提交级别上,重置是一种将分支尖端移动到另一个提交的方法。这可以用来从当前分支中移除提交。例如,以下命令将 hotfix 分支向后移动两次提交。

git checkout hotfix git reset HEAD~2

hotfix 末尾的两次提交现在处于未决状态,或者是孤立的提交。这意味着它们将在下次 Git 执行垃圾回收时被删除。换句话说,您在表明您想丢掉这些提交。可以将其可视化为以下内容:

将 hotfix 分支重置为 HEAD-2

使用 git reset 是一种撤销未与他人共享的变更的简单方法。当您开始开发一个功能时发现自己在想:“哦该死,我在干什么?我应该从头开始。”

除了移动当前分支外,您还可以通过向 git reset 传递以下标志之一来更改暂存快照和/或工作目录:

  • --soft – 暂存快照和工作目录不会以任何方式更改。
  • --mixed – 更新暂存的快照以匹配指定的提交,但工作目录不受影响。这是默认选项。
  • --hard – 暂存的快照和工作目录都已更新以匹配指定的提交。

It’s easier to think of these modes as defining the scope of a git reset operation. For further detailed information visit the git reset page.

签出旧提交

git checkout 命令用于将存储库状态更新到项目历史记录中的特定点。使用分支名称传递时,它允许您在分支之间切换。

git checkout hotfix

在内部,以上命令所做的就是将 HEAD 移到不同的分支并更新工作目录以进行匹配。由于这有可能覆盖本地变更,因此 Git 会强制您在工作目录中提交或存储任何将在签出操作期间丢失的变更。与 git reset 不同,git checkout 不会移动任何分支。

Moving HEAD from main to hotfix

您还可以通过传递提交引用而不是分支来签出任意提交。这与签出分支的作用完全相同:它将 HEAD 引用移动到指定提交中。例如,以下命令将签出当前提交的祖父级:

git checkout HEAD~2
将 `HEAD` 移至任意提交

这对于快速检查项目的旧版本很有用。但是,由于没有对当前 HEAD 的分支引用,这会使您处于游离的 HEAD 状态。如果您开始添加新的提交,这可能很危险,因为在您切换到另一个分支之后将无法返回它们。因此,在向游离的 HEAD 添加提交之前,应始终创建一个新分支。

Undo public commits with revert

还原通过创建新的提交来撤销提交。这是撤销变更的安全方法,因为它没有机会重写提交历史记录。例如,以下命令将找出倒数第二次提交中包含的变更,创建一个新提交来撤销这些变更,然后将新提交粘贴到现有项目上。

git checkout hotfix git revert HEAD~2

可以将其可视化为以下内容:

将第二次提交还原到上一次提交

与此相比,git reset 确实会改变现有的提交历史记录。因此,应使用 git revert 来撤销公共分支上的变更,而 git reset 应保留用于撤销私有分支上的变更。

您也可以将 git revert 视为撤销已提交变更的工具,而 git reset HEAD 用于撤销未提交的变更。

git checkout 一样,git revert 有可能覆盖工作目录中的文件,因此它会要求您提交或存储在还原操作期间会丢失的变更

File-level operations


git resetgit checkout 命令也接受可选的文件路径作为参数。这极大地改变了他们的行为。这不是对整个快照进行操作,而是迫使他们将其操作限制在单个文件中。

Git reset a specific file

使用文件路径调用时,git reset 会更新暂存的快照以匹配指定提交的版本。例如,此命令将在倒数第二个提交中获取 foo.py 的版本,并将其存放到下一次提交中:

git reset HEAD~2 foo.py

git reset 的提交级别版本一样,它更常用于 HEAD,而不是任意提交。运行 git reset HEAD foo.py 将取消暂存 foo.py。它包含的变更仍将存在于工作目录中。

将文件从提交历史记录移到暂存快照中

--soft--mixed--hard 标记对 git reset 的文件级版本没有任何影响,因为暂存快照始终更新,工作目录从不更新。

Git checkout file

签出文件与使用带有文件路径的 git reset 类似,不同之处在于它更新工作目录而不是暂存区域。与这个命令的提交级别版本不同,它不会移动 HEAD 引用,这意味着您不会切换分支。

将文件从提交历史记录移到工作目录中

例如,以下命令使工作目录中的 foo.py 与倒数第二次提交中的相匹配:

git checkout HEAD~2 foo.py

就像 git checkout 的提交级别调用一样,它可以用来检查项目的旧版本,但范围仅限于指定的文件。

如果您暂存并提交已签出的文件,则会“复原”到该文件的旧版本。请注意,这会删除文件的所有后续变更,而 git revert 命令仅撤销指定提交引入的变更。

git reset 一样,它通常使用 HEAD 作为提交引用。例如,git checkout HEAD foo.py 的效果是丢弃对 foo.py 的未暂存变更。这与 git reset HEAD --hard 的行为类似,但它只能在指定的文件上运行。

摘要


You should now have all the tools you could ever need to undo changes in a Git repository. The git reset, git checkout, and git revert commands can be confusing, but when you think about their effects on the working directory, staged snapshot, and commit history, it should be easier to discern which command fits the development task at hand.


分享此文章
下一主题

推荐阅读

将这些资源加入书签,以了解 DevOps 团队的类型,或获取 Atlassian 关于 DevOps 的持续更新。

人们通过满是工具的墙进行协作

Bitbucket 博客

Devops 示意图

DevOps 学习路径

与 Atlassian 专家一起进行 Den 功能演示

Bitbucket Cloud 与 Atlassian Open DevOps 如何协同工作

注册以获取我们的 DevOps 新闻资讯

Thank you for signing up