Git
章节 ▾ 第二版

2.4 Git 基础 - 撤销操作

撤销操作

在任何阶段,你可能都需要撤销某些操作。在这里,我们将回顾一些用于撤销已做更改的基本工具。请谨慎操作,因为你无法始终撤销某些撤销操作。这是 Git 中为数不多的几个可能因操作错误而丢失某些工作内容的领域之一。

常见的撤销操作之一发生在你过早提交时,可能忘记添加某些文件,或者提交信息出现错误。如果你想重做该提交,请进行你忘记的额外更改,暂存它们,然后使用 --amend 选项再次提交

$ git commit --amend

此命令获取你的暂存区并将其用于提交。如果你自上次提交以来没有进行任何更改(例如,你在上次提交后立即运行此命令),则你的快照看起来完全相同,你唯一更改的是提交信息。

相同的提交信息编辑器会启动,但它已包含上次提交的信息。你可以像往常一样编辑信息,但它会覆盖你之前的提交。

例如,如果你提交后意识到忘记暂存想要添加到此提交中的某个文件中的更改,可以执行以下操作

$ git commit -m 'Initial commit'
$ git add forgotten_file
$ git commit --amend

最终你会得到一个提交——第二个提交替换了第一个提交的结果。

注意

理解这一点很重要,当你修改上次提交时,与其说是修复它,不如说是完全替换它,用一个新的、改进的提交来替换它,将旧的提交移出,并将新的提交放在其位置。实际上,就好像之前的提交从未发生过,它不会显示在你的仓库历史记录中。

修改提交的明显价值在于对上次提交进行细微改进,而不会使你的仓库历史记录充斥着诸如“哎呀,忘记添加文件”或“哎呀,修复上次提交中的错别字”之类的提交信息。

注意

仅修改仍在本地且尚未推送到任何位置的提交。修改之前已推送的提交并强制推送分支会导致与你的协作者发生冲突。有关执行此操作时会发生什么以及如果你是接收方如何恢复的更多信息,请阅读 变基的风险

取消暂存已暂存的文件

接下来的两节将演示如何处理暂存区和工作目录的更改。 好的方面是,用于确定这两个区域状态的命令也会提醒您如何撤消对它们的更改。 例如,假设您更改了两个文件并希望将它们作为两个单独的更改提交,但您意外地键入了git add *并将它们都暂存了。 如何取消暂存其中一个?git status 命令会提醒您

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
    modified:   CONTRIBUTING.md

在“Changes to be committed”文本的正下方,它说使用git reset HEAD <file>…​来取消暂存。 因此,让我们使用此建议来取消暂存CONTRIBUTING.md文件

$ git reset HEAD CONTRIBUTING.md
Unstaged changes after reset:
M	CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

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:   CONTRIBUTING.md

该命令有点奇怪,但它确实有效。CONTRIBUTING.md文件已修改,但再次取消暂存。

注意

确实,git reset可能是一个危险的命令,特别是如果您提供了--hard标志。 但是,在上面描述的场景中,工作目录中的文件不会被触碰,因此相对安全。

目前,您只需要了解有关git reset命令的这个神奇调用即可。 我们将在Reset Demystified中详细介绍reset的功能以及如何掌握它来执行真正有趣的事情。

撤消已修改的文件

如果您意识到不想保留对CONTRIBUTING.md文件的更改怎么办? 如何轻松地撤消修改——将其恢复到上次提交(或最初克隆,或您将其放入工作目录的方式)时的样子? 幸运的是,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:   CONTRIBUTING.md

它非常明确地告诉您如何丢弃所做的更改。 让我们按照它说的去做

$ git checkout -- CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README

您可以看到更改已恢复。

重要

必须理解,git checkout -- <file>是一个危险的命令。 您对该文件所做的任何本地更改都将消失——Git 只是用上次暂存或提交的版本替换了该文件。 除非您绝对知道不需要这些未保存的本地更改,否则请勿使用此命令。

如果您想保留对该文件所做的更改,但现在仍需要将其移开,我们将介绍Git Branching中的暂存和分支;这些通常是更好的方法。

请记住,Git 中提交的任何内容几乎总是可以恢复的。 即使是在已删除的分支上的提交或使用--amend提交覆盖的提交也可以恢复(有关数据恢复,请参阅Data Recovery)。 但是,任何您丢失的从未提交过内容可能永远无法再看到。

使用 git restore 撤消操作

Git 版本 2.23.0 引入了一个新命令:git restore。 它基本上是刚刚介绍的git reset的替代方案。 从 Git 版本 2.23.0 开始,Git 将使用git restore代替git reset来执行许多撤消操作。

让我们回溯我们的步骤,并使用git restore而不是git reset来撤消操作。

使用 git restore 取消暂存已暂存的文件

接下来的两节将演示如何使用git restore处理暂存区和工作目录的更改。 好的方面是,用于确定这两个区域状态的命令也会提醒您如何撤消对它们的更改。 例如,假设您更改了两个文件并希望将它们作为两个单独的更改提交,但您意外地键入了git add *并将它们都暂存了。 如何取消暂存其中一个?git status 命令会提醒您

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   CONTRIBUTING.md
	renamed:    README.md -> README

在“Changes to be committed”文本的正下方,它说使用git restore --staged <file>…​来取消暂存。 因此,让我们使用此建议来取消暂存CONTRIBUTING.md文件

$ git restore --staged CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    README.md -> README

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   CONTRIBUTING.md

CONTRIBUTING.md文件已修改,但再次取消暂存。

使用 git restore 撤消已修改的文件

如果您意识到不想保留对CONTRIBUTING.md文件的更改怎么办? 如何轻松地撤消修改——将其恢复到上次提交(或最初克隆,或您将其放入工作目录的方式)时的样子? 幸运的是,git status也会告诉您如何做到这一点。 在最后一个示例输出中,未暂存区域如下所示

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   CONTRIBUTING.md

它非常明确地告诉您如何丢弃所做的更改。 让我们按照它说的去做

$ git restore CONTRIBUTING.md
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	renamed:    README.md -> README
重要

必须理解,git restore <file>是一个危险的命令。 您对该文件所做的任何本地更改都将消失——Git 只是用上次暂存或提交的版本替换了该文件。 除非您绝对知道不需要这些未保存的本地更改,否则请勿使用此命令。

scroll-to-top