Git 撤销指南
Git 撤销有三个场景
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout -- file。
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,用命令:git reset --hard commit_id不过前提是没有推送到远程库。
场景一
在熬夜加班时,你正在赶一份代码,你在readme.txt中添加了一行:
$ cat readme.txt Git is a distributed version control system. Git is free software distributed under the GPL. Git has a mutable index called stage. Git tracks changes of files. Git is not a good tool
在你准备提交前,突然发现发现了“Git is not a good tool”这句话是不对的!
如果用git status查看一下:
$ git status # On branch master # 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: readme.txt # no changes added to commit (use "git add" and/or "git commit -a")
Git会提示你,git checkout -- file可以丢弃工作区的修改:
$ git checkout -- readme.txt
命令git checkout -- readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,就是让这个文件回到最近一次git commit或git add时的状态。
现在,看看readme.txt的文件内容:
$ cat readme.txt Git is a distributed version control system. Git is free software distributed under the GPL. Git has a mutable index called stage. Git tracks changes of files.
文件内容果然复原了。
git checkout -- file命令中的--很重要,没有--,就变成了“创建一个新分支”的命令,我们在后面的分支管理中会再次遇到git checkout命令。
场景二
加班愈发困了,你不但写错了,还git add到暂存区了:
$ cat readme.txt Git is a distributed version control system. Git is free software distributed under the GPL. Git has a mutable index called stage. Git tracks changes of files. My stupid boss still prefers SVN.
$ git add readme.txt
在commit之前,聪明的你发现了这个问题。用git status查看一下,修改只是添加到了暂存区,还没有提交:
$ git status
# On branch master# Changes to be committed:# (use "git reset HEAD <file>..." to unstage)## modified: readme.txt#
Git同样告诉我们,用命令git reset HEAD file可以把暂存区的修改撤销掉(unstage),重新放回工作区:
$ git reset HEAD readme.txt Unstaged changes after reset: M readme.txt
git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。
再用git status查看一下,现在暂存区是干净的,工作区有修改:
$ git status # On branch master # 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: readme.txt # no changes added to commit (use "git add" and/or "git commit -a")
回顾下场景一如何丢弃工作区的修改吗?
$ git checkout -- readme.txt
$ git status
# On branch master
nothing to commit (working directory clean)
场景三
readme.txt文件一共有几个版本被提交到Git仓库里了:
版本1:wrote a readme file
Git is a version control system.
Git is free software.
版本2:add distributed
Git is a distributed version control system.
Git is free software.
版本3:append GPL
Git is a distributed version control system.
Git is free software distributed under the GPL.
在Git中,我们用git log命令查看:
$ git log commit 3628164fb26d48395383f8f31179f24e0882e1e0 Author: f_zhou <[email protected]> Date: Tue Aug 20 15:11:49 2013 +0800 append GPL commit ea34578d5496d7dd233c827ed32a8cd576c5ee85 Author: f_zhou <[email protected]> Date: Tue Aug 20 14:53:12 2013 +0800 add distributed commit cb926e7ea50ad11b8f9e909c05226233bf755030 Author: f_zhou <[email protected]> Date: Mon Aug 19 17:51:55 2013 +0800 wrote a readme file
git log命令显示从最近到最远的提交日志,我们可以看到3次提交,最近的一次是“append GPL”,上一次是“add distributed”,最早的一次是“wrote a readme file”。
如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline参数:
$ git log --pretty=oneline
3628164fb26d48395383f8f31179f24e0882e1e0 append GPL ea34578d5496d7dd233c827ed32a8cd576c5ee85 add distributed cb926e7ea50ad11b8f9e909c05226233bf755030 wrote a readme file
我们现在回滚版本咯
首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交3628164...882e1e0(注意我的提交ID和你的肯定不一样),
上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100。
现在,我们要把当前版本“append GPL”回退到上一个版本“add distributed”,
就可以使用git reset命令:
$ git reset --hard HEAD^
HEAD is now at ea34578 add distributed
看看readme.txt的内容是不是版本“add distributed”:
$ cat readme.txt Git is a distributed version control system. Git is free software.
还可以继续回退到上一个版本“wrote a readme file”,不过且慢,然我们用git log再看看现在版本库的状态:
$ git log commit ea34578d5496d7dd233c827ed32a8cd576c5ee85 Author: f_zhou <[email protected]> Date: Tue Aug 20 14:53:12 2013 +0800 add distributed commit cb926e7ea50ad11b8f9e909c05226233bf755030 Author: f_zhou <[email protected]> Date: Mon Aug 19 17:51:55 2013 +0800 wrote a readme file
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向“append GPL”:
然后顺便把工作区的文件更新了。所以你让HEAD指向哪个版本号,你就把当前版本定位在哪。
现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的commit id怎么办?
在Git中,总有后悔药可以吃的。当你用$ git reset --hard HEAD^回退到“add distributed”版本时,再想恢复到“append GPL”,就必须找到“append GPL”的commit id。
Git提供了一个命令git reflog用来记录你的每一次命令:
$ git reflog ea34578 [email protected]{0}: reset : moving to HEAD^ 3628164 [email protected]{ 1 }: commit : append GPL ea34578 [email protected]{ 2 }: commit : add distributed cb926e7 [email protected]{ 3 }: commit (initial): wrote a readme file
第二行显示“append GPL”的commit id是3628164,现在,你又可以乘坐时光机回到未来了。
要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪个版本
http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/0013744142037508cf42e51debf49668810645e02887691000