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 reset
、git checkout
和 git revert
的最常见配置。希望您能够放心地使用这些命令中的任何一个在存储库中导航。
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
引用指针移动到指定提交的操作。要演示这一点,请看下方示例。
相关资料
如何移动完整的 Git 存储库
查看解决方案
了解 Bitbucket Cloud 的 Git
此示例演示了 main
分支上的一系列提交。HEAD
引用和 main
分支引用当前指向提交 d。现在我们来执行 git checkout b
这是对“提交历史记录”树的更新。git checkout
命令可以在提交或文件级范围中使用。文件级签出会将文件的内容变更为特定提交内容。
还原是一种操作,它接受指定的提交并创建一个新的提交,该提交与指定提交相反。git revert
只能在提交级别范围运行,没有文件级功能。
重置是一种操作,它接受指定的提交,并将“三棵树”重置为与该指定提交时存储库的状态相匹配。重置可以在与三棵树相对应的三种不同模式下调用。
签出和重置通常用于在本地或私人“撤销”。它们修改了存储库的历史记录,推送到远程共享存储库时可能会导致冲突。还原被认为是“公共撤销”的安全操作,因为它会创建新的历史记录,可以远程共享,并且不会覆盖远程团队成员可能依赖的历史记录。
Git reset vs revert vs checkout reference
下表总结了所有这些命令的最常见用例。一定要随身携带这份参考资料,因为在您的 Git 职业生涯中,您肯定需要用到其中一些信息。
命令 | 范围 | 常见用例 |
---|---|---|
| 范围 提交级别 | 常见用例 Discard commits in a private branch or throw away uncommitted changes |
| 范围 文件级 | 常见用例 取消暂存文件 |
| 范围 提交级别 | 常见用例 在分支之间切换或检查旧快照 |
| 范围 文件级 | 常见用例 丢弃工作目录中的变更 |
| 范围 提交级别 | 常见用例 撤销公共分支中的提交 |
| 范围 文件级 | 常见用例 (不适用) |
Commit level operations
您传递给 git reset
和 git checkout
的参数决定了它们的范围。当您没有将文件路径作为参数时,它们会对整个提交进行操作。这就是我们将在本节探讨的内容。请注意,git revert
没有文件级对应物。
Reset a specific commit
在提交级别上,重置是一种将分支尖端移动到另一个提交的方法。这可以用来从当前分支中移除提交。例如,以下命令将 hotfix
分支向后移动两次提交。
git checkout hotfix git reset HEAD~2
hotfix
末尾的两次提交现在处于未决状态,或者是孤立的提交。这意味着它们将在下次 Git 执行垃圾回收时被删除。换句话说,您在表明您想丢掉这些提交。可以将其可视化为以下内容:
使用 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
不会移动任何分支。
您还可以通过传递提交引用而不是分支来签出任意提交。这与签出分支的作用完全相同:它将 HEAD
引用移动到指定提交中。例如,以下命令将签出当前提交的祖父级:
git checkout HEAD~2
这对于快速检查项目的旧版本很有用。但是,由于没有对当前 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 reset
和 git 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 的持续更新。