# 撤销操作
撤销是 Git 提供的一个非常优秀的功能,它可以允许你撤消刚刚所做的操作。这就意味着你不必害怕搞砸你正在工作的项目: Git 一直会让你的项目处于一个安全的状态。
## 修改最后一次提交
无论你如何精心地推敲你的提交,你总是有可能出错的。比如忘记了把一个改动过的文件添加到提交中,或者是输入了错误的提交注释等等。当你认为提交有问题时,你都可以使用 “git commit” 命令,并附带上 “--amend” 参数,这个操作可以非常轻松地来修改你的_最后一次_提交。
如果你仅仅是想修改上一次的提交注释,你并不需要操作暂存区,简单地再次输入 “git commit --amend” 并附带上正确的注释就可以了:
```
$ git commit --amend -m "This is the correct message"
```
如果你想要添加更多的改动到上一次提交中,你可以像平常一样把这些新的改动添加到暂存区。然后再次使用 “--amend” 参数进行提交:
```
$ git add some/changed/file.ext
$ git commit --amend -m "commit message"
```
##### 黄金法则
#### #5: 不要修改已经被发布的提交
“amend” 操作是一个非常强大的小帮手,这点你会很快地领会到。但是在你使用它的同时,你一定要考虑到以下一些方面:
* (a) 你只能使用它来修正你的**上一次**提交。更早的提交是不能使用 “amend” 来进行操作的。
* (b) 你不要对一个已经在远程仓库上被发布,或者说已经被共享的提交进行 “amend” 操作!这是因为 “amend” 操作实际上在后台打包了一个全新的提交来替换旧的提交。如果在这个远程仓库里仅仅只有你一个人在工作,那么这种操作是没有问题的。但是在团队工作中,如果开发团队的其他人员已经得到了你所发布的改动,并且在此基础之上进行了他自己的改动,再次整合一个被修改过的(amended)提交就会出现很多麻烦。
## 撤销本地改动
当改动还没有被提交之前,它们仍然被称之为 “本地” 改动。这些在你的工作目录(working directory)的修改还仍然在本地,它们属于未被提交的改动(uncommitted changes)。
有时候你对代码进行了一些修改,但是发现这些改动带来的问题比之前还要多。在这种情况下,你可能想要放弃你刚刚的改动,让代码恢复到你改动之前的版本,也就是上次提交之后的状态。
恢复一个文件到上次提交之后的状态,你可以使用 “git checkout” 命令:
```
$ git checkout -- file/to/restore.ext
```
我们已经知道了 “checkout” 命令主要是用来切换分支用的。但是你同样可以给这个命令附带上 “--” 参数,并加上用一个空格来分隔的文件路径。这个操作将撤销在特定文件上所有的未提交的改动。
如果你想要放弃你在工作副本(working copy)中的所有本地改动,并让你的本地副本恢复到上次提交之后的版本,你可以使用 “git reset” 命令:
```
$ git reset --hard HEAD
```
上面这个操作会通知 Git 将你本地副本上的所有文件替换到和 “HEAD” 分支一致的版本(也就是上次提交之后的版本状态)上,并放弃所有的本地改动。
##### 注释
放弃本地未被提交的改动是不能被撤销的。这是因为这些改动还没有保存在你的仓库中。因此,Git 也就没有任何机会来挽回这种操作带来的改动。
请在你撤销本地改动时始终牢记这一点。
## 撤销已提交的改动
有时你也许想撤销某一个之前的提交。例如当你发现你的改动存在错误,或是整个改动就是错误的,又或者你的客户决定不需要这个改动了等等。
使用 “git revert” 命令可以撤销某个之前的提交。但是这个命令并不是删除那个提交,相反的,它是恢复那个提交的改动,这只是看起来像是撤销而已。这个操作实际上会自动产生一个新的提交。在提交中包括了你想要撤销的那个提交的所有反向改动。例如在原始提交中,你在某一个位置添加一些字符,那么这个恢复提交(reverting commit)就会把这些字符删除掉。
![revert-concept](https://box.kancloud.cn/2016-05-04_572967dedd267.png)
如果想要撤销已提交的改动,你只需要简单地给出这个提交的 commit hash:
```
$ git revert 2b504be
[master 364d412] Revert "Change headlines for about and imprint"
2 files changed, 2 insertions(+), 2 deletions (-)
$ git log
commit 364d412a25ddce997ce76230598aaa7b9759f434
Author: Tobias Günther <support@learn-git.com>
Date: Tue Aug 6 10:23:57 2013 +0200
Revert "Change headlines for about and imprint"
This reverts commit 2b504bee4083a20e0ef1e037eea0bd913a4d56b6.
```
另外一种撤销提交的方法是使用 “git reset” 命令。这个操作不会自动产生一个新的提交,或是删除你要撤销的提交,它会重置你当前的 HEAD 分支到一个特定旧的版本,也被称作 “回滚(rolling back)” 到旧的版本:
```
$ git reset --hard 2be18d9
```
在执行了这个操作之后,你当前签出的分支将被重置为版本 2be18d9。在这个版本之后的一个或者多个版本将被真正的放弃,它们也不会显示在分支的历史记录中。
![reset-concept](https://box.kancloud.cn/2016-05-04_572967df05bb3.png)
如果在这个命令上使用 “--hard” 参数则一定要小心,Git 将会丢弃所有你当前可能拥有的本地改动。整个项目将会被恢复成一个之前的旧版本。
如果你使用 “--keep” 参数来替代 “--hard” 参数,那么在 “回滚” 到的版本之后的所有改动将会转换成本地改动,并保留在你的工作目录中。
##### 注释
和 “revert” 命令一样, “reset” 命令也不会删除任何已存在的提交。这些操作仅仅是做得好像这个提交不存在似的,并从历史记录中删除它们。无论如何,提交会被保存在 Git 的数据库中至少30天。因此,如果你发现你曾经不小心删除了一个仍然有用的提交,任何一个精通 Git 的同事都能为你恢复它们。
这两个命令(revert and reset)只是工作于当前 HEAD 分支上。因此在你执行这些操作之前,你必须要切换到正确的分支上去。
- Learn Version Control with Git 中文版
- 前言
- Part 1 - 基础知识
- 什么是版本控制?
- 为什么要使用版本控制系统?
- 准备工作
- 版本控制的基本工作流程
- 从一个未被纳入版本控制的项目开始
- 从一个已被纳入版本控制的项目开始
- 工作在你的项目上
- Part 2 - 分支与合并
- 分支可以改变你的生命
- 在分支上工作
- 暂时保存更改
- 切换一个本地分支
- 合并改动
- 分支的工作流程
- Part 3 - 远程仓库
- 关于远程仓库
- 连接一个远程仓库
- 查看远程数据
- 整合远程的改动
- 发布一个本地分支
- 删除分支
- Part 4 - 高级应用
- 撤销操作
- 用 diff 来检查改动
- 处理合并冲突
- Rebase 代替合并
- 子模块
- git-flow 的工作流程
- 使用 SSH 公钥验证
- Part 5 - 工具与服务
- 桌面应用程序
- 比较和整合工具
- 代码托管服务
- 更多学习资源
- 附录
- 版本控制的最佳实践
- 命令 101
- 从 Subversion 过渡到 Git
- 为什么选择 Git