为什么Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件,比如你修改了一个文件,然后用git add把它加到暂存区,然后再一次修改这个文件,然后用git commit命令提交到某个分支,此时你再用git status命令能看到第二次修改并没有提交,甚至都还没到暂存区,因为git add的时候只是把第一次修改加到缓存区,git commit只负责把缓存区的内容提交。
撤销修改有下面三种情况:
- 仅修改工作区还没有加到缓存区(git add 之前)
- 加到缓存区但还没提交(git commit之前)
- 已提交
撤销工作区的修改(git add之前)
通过git status命令,Git会告诉你,git checkout -- file
可以丢弃工作区的修改,该命令作用是把缓存区的内容恢复到工作区,如果缓存区没有内容,则恢复到版本库中的内容,也就下面两种情况:
一种是文件自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是文件已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit
或git add
时的状态。
撤销工作区、缓存区的修改(git commit之前)
假如修改已经add到缓存区,此时怎么撤销工作区回到版本库的内容,上面提到的git checkout -- file命令只能把缓存的数据恢复到工作区,当工作区没有修改的内容时才会恢复到版本库内容。所以我们首先要把缓存区的内容恢复到版本库内容,同样的当我们用git status命令时,Git会告诉我们,用命令git reset HEAD <file>
可以把暂存区的修改撤销掉。
git reset
命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD
时,表示最新的版本,版本回退一章中可以看到,HEAD^ 代表上一个版本,HEAD^^代表上上一个版本。
下图中readme.txt文件add到缓存区后又修改了一次
现在执行git reset HEAD readme.txt命令
可以看到,该文件第一次修改的内容已经不在缓存区中了,但此时工作区的内容是没有任何改变的,还是第二次修改后的内容。我们再执行git checkout -- readme.txt命令时,工作区便被恢复到版本库的最新版本内容,也就是说第一、二两次的修改都被撤销了,假如你只要撤销第二次修改,则直接用git checkout -- readme.txt命令即可,假如add第一次修改到缓存区之后,没有再做第二次修改,则还是需要上面两步,因为不管什么情况下,git reset HEAD readme.txt命令都不会改变工作区。
撤销已提交的内容(git commit之后)
由于已提交到指定分支,首先执行git reset HEAD^ readme.txt命令,把缓存区恢复到版本库的前一个版本,如下图:
可见缓存区有一份缓存数据(上一个版本,即HEAD^),工作区也有一份修改,此时可以执行git checkout -- readme.txt把缓存区的上个版本的内容恢复到工作区:
此时可以看到工作区的文件内容被恢复成HEAD^版本的内容,但缓存区还有一份数据,缓存区的内容和工作区的内容是相同的,因为工作区就是从缓存区恢复回来的,从Git提示可以看到,可以通过git reset HEAD命令把缓存区数据清空,但最好不要这样做,因为该命令是把版本库分支上的HEAD内容恢复到缓存区(之所以提示缓存区有待提交的内容,就是因为缓存区的内容和版本库分支上的HEAD内容不同),如果我们不小心执行了该命令,则可以看到下图:
虽然缓存区数据和分支上的最新版本是一致的,但工作区内容与最新版本不同,此时,需要重新add到缓存区,然后再提交到分支上,明显这样多了很多步,其实我们可以在前面缓存区有数据的时候,直接commit,这样分支上的内容就和工作区一致了。
总结步骤就是:
- git reset HEAD^ readme.txt
- git checkout -- readme.txt
- git commit -m 恢复readme.txt文件到上一版本
第2和第3步位置可以调换,因为可以先提交缓存区,再从分支上恢复工作区。