撤销操作
在任何阶段,我们都可能希望撤消某些操作。在这里,我们将回顾一些基本工具,用于撤消之前所做的更改。操作要谨慎,因为这些撤销可能无法修复。这是 Git 中为数不多的几个领域之一由于操作不当,导致丢失一些工作。
- 一种情况是当提交的太早,可能忘记添加某些文件
- 另一种情况是弄乱了提交消息
如果想重新提交该提交,请执行以下操作:
补充忘记的额外更改,将它们暂存,然后使用 --amend 选项再次提交。
$ git commit --amend
- 只更改提交消息
该命令将使用暂存区域进行提交。如果自上次提交以来没有进行任何更改(例如,在上一次提交后立即运行此命令),那么快照将完全相同,要更改的只是提交消息。
同样的提交消息编辑器会启动,但它已经包含了上一次提交的消息。可以像往常一样编辑消息,但它会覆盖之前的提交。
- 追加新的更改文件
举例来说,如果提交后意识到忘记将一个文件中的更改暂存,而又希望将其添加到此次提交中,可以执行以下操作:
$ git commit -m 'Initial commit'
$ git add forgotten_file
$ git commit --amend
最终得到了一个单独的提交 — 第二次提交取代了第一次提交的结果。
重要的是要理解,当修改最后一次提交时,并不是在修复它,而是完全用一个新的、改进过的提交替换它,这样就将旧的提交推开并将新的提交放在了它的位置。实际上,就好像以前的提交从未发生过,并且不会出现在仓库历史记录中。
修改提交的明显价值在于对最后一次提交进行微小改进,而不会在您的仓库历史记录中添加形式为“哎呀,忘记添加文件”或“糟糕,修复上次提交中的拼写错误”等提交消息,从而使仓库历史记录变得混乱。
只修改仍然在本地且尚未推送到其他地方的提交。修改先前已经推送的提交并强制推送分支会给合作者带来问题。
取消已暂存文件的暂存
通过git reset撤销
针对git 2.23之前的版本,示例中代码块部分粘自git book,没有进行实际操作。
接下来的两个部分演示了如何处理暂存区和工作目录中的更改。好处是,用于确定这两个区域状态的命令还会提醒如何撤消对它们的更改。
例如,假设更改了两个文件,并希望将它们作为两个单独的更改进行提交,但不小心输入了 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 命令,需要知道的就是这个神奇的调用方式。
将已修改的文件还原
如果意识到不想保留对 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 分支》中介绍存储和分支;这些通常是更好的方法。
请记住,Git 中提交的任何内容几乎都可以恢复。即使是在已删除的分支上的提交,或者是在使用 --amend 命令覆盖的提交,都可以恢复(请参阅数据恢复以进行数据恢复)。但是,任何提交的丢失内容可能永远不会再见。
通过git restore撤销
Git 版本 2.23.0 引入了一个新的命令:git restore。它基本上是我们刚刚介绍过的 git reset 的替代品。从 Git 版本 2.23.0 开始,Git 将在许多撤消操作中使用 git restore 而不是 git reset。
让我们重新追溯我们的步骤,使用 git restore 而不是 git reset 来撤销操作。
在“Changes to be committed”文本的正下方,它说要使用 git restore --staged <file>… 来取消暂存。因此,让我们按照这个建议来取消暂存text.txt 文件:
Text.txt文件已被修改,但未暂存。
使用 git restore 取消对已修改文件的修改
重要的是要理解,git restore <file> 是一个危险的命令。对该文件所做的任何本地更改都将消失 —— Git 只是用最后暂存或提交的版本替换了该文件。除非绝对确定不需要那些未保存的本地更改,否则绝对不要使用此命令。