回滚的经常使用手法

传统VCS的回滚操做git

对于版本控制系统VCS来讲,回滚这个操做应该是个很普通也是很重要的需求。网络

若是你是传统VCS,好比SVN或者P4来讲,revert是个最直观,也是最直接的手段,固然前提是你的修改尚未被提交到远程的中央仓库。分布式

若是你已经ci了你的code到了远程中央仓库,那revert恐怕也无能为力,只能借助其余命令workaroud这个问题,好比:你用SVN的话,就得来个逆向merge操做,把全部的修改都merge回去。工具

但这样作也有一些弊端:3d

此次merge会做为一次全新的commit记录记录下来,也就是说它不能真正从你的历史记录里面抹掉你那次不想要的修改。一般状况下其实也没啥大不了的,除非你我的洁癖就是不想看到之前的那次commit记录或者你真的干了啥不想让别人知道的事情。版本控制

Git时代的回滚操做指针

但当发展到git时代,这种回滚操做的复杂度,已经随着git模型自己的特色,变得不那么简单了。code

熟悉git的人都知道,为了分布式的需求,git将每个网络节点做为了一个完整的VCS,也就是每一个单台的host在没有网络的前提下,都是一个不受任何影响能够知足除了和其余节点同步(好比:git pull/push这类)以外的几乎全部操做。对象

为了达到这种效果,git不只在本地有一个完整的local repository,并且将本来简单的working tree(或者叫working directory)也切成了两块区域——working tree和index(也叫stage)。blog

这样,光从本地修改的角度来看,你的修改就可能存在三块区域中,working tree、index或者commit以后的历史对象区域。下面咱们一个一个各个区域通常都怎么回滚。

working tree内的回滚

这个属于最简单一种情形,本质上说也是和传统VCS中revert直接对应的一种场景,只是这里不叫revert了,而是git checkout,这种情形很简单,这里就不作截图展现了。列出依稀经常使用的命令形式以下:

  • git checkout file1 (回滚单个文件)
  • git checkout file1 file2 ... fileN (一次回滚多个文件,中间用空格隔开便可)
  • git checkout . (直接回滚当前目录一下的全部working tree内的修改,会递归扫描当前目录下的全部子目录)

index内的回滚

这部分回滚也不复杂,由于这部分的回滚,只要你勤快点使用git status命令,命令的输出上都会给你提示你须要干啥。只是这个过程通常被分为了两步:

  1. 将index区域中修改过的文件移除index,也就是恢复到working tree中。这部用git reset来解决。
  2. 一旦文件从新回到working tree中,回滚操做就是上面提到的git checkout喽。

这个看个截图直观点:

我working tree下的原始文件信息以下

 我修改了a.txt和my_dir/b.txt,并将将他们加入了index区域,当前运行git status获得以下输出


 这里再执行git reset . 将当前目录及子目录内的全部修改移出index区域,再次运行git status命令


 到这一步以后,就用上面提到git checkout就能够解决问题了。

 

commit以后的回滚

这种情形是git本地回滚里面最复杂,也是最容易让人迷糊的了,由于针对不一样的状况,方法比较多,因此不是很好记。

  • 修改最后一次commit的记录:不少时候先要回滚仅仅是由于本身对最后一次的commit的漏掉(注意,这里说的漏掉不只仅是你少提交了文件的修改,也包括你多提交了一下你不想要提交的东西)了一些东西,想要回滚此次commit以后再从新commit。若是是这样的话,没有必要真的非要先回滚再从新commit。只要在在本身已经满意了本身全部的修改以后,直接执行git commit --amend,就能够开启上次提交的“补救”提交模式,而后把你对上次全部漏掉的东西加上去就行了。下面看个例子:我进行了一次错误的提交,修改的内容以下:

     目前commit 记录以下:

     如今我想补救此次commit,至关于取消此次新加入的文件b.txt、取消对a.txt第三行的修改,而后加入我真正想要的修改:在my_dir下增长一个c.txt,而且修改a.txt的第三行为另一句话。

     

     再次经过git log查看commit记录

     请注意比较最新的一次commit的修改,其实已经被修改成另外一个SHA1的值了。这里请注意,从某种意义上说(实际上这种替换在reflog中很容易追踪到痕迹,只是在全部的commit逆向引用链条中,咱们已经找不到以前的那个fad4...),这种操做已经作到了无痕修改最后一次提交。这和SVN的逆向merge是本质不一样的。
  • 回滚中间的某次提交(固然也包括最后一次):好比我想要回滚上图中倒数第二次提交,就是HEAD^那次,咱们先经过git show HEAD^看看那次提交都干了啥?

     而后再经过git revert HEAD^ 来回滚此次操做,而后咱们获得了下面的提示:

     杯具,冲突了。。。其实,只要你熟悉任何一种VCS工具,想一想这个场景,其实也是挺正常的。那就git status看看哪些个文件冲突了吧。

     其实你只要仔细看看上面的说明信息,应该已经知道该怎么解决这个冲突了。明显,a.txt是冲突发生的文件:

     打开这个文件,能够看到标准的冲突标识文件。这里正是以前咱们采用补救式提交方式修改的那句话。至于冲突怎么解决很容易,看你究竟想要啥了,本身去编辑,去掉冲突范围标识符号,保存文件便可。而后按照git正常的流程再次提交,编辑提交的信息便可。再次提交以后的log信息以下:

     上面咱们基本上演示了一个标准的revert场景(包括了冲突解决),从这个过程能够看出,git revert和SVN的逆向merge几乎一模一样,就是将你须要回滚的那次commit所作的全部操做,反向操做一次,而后从新作一一个单独的commit对象进行提交。这个过程是否发生冲突,就取决于你的修改了。请注意,这个过程你虽然回滚了你不想要的修改内容,可是你无法抹掉那次commit在history中的信息,请注意上图的第三行,他依旧坚挺的躺在那里。这个也是git revert的特色。固然,git revert实际上也提供-n(--no-commit)参数,用来表示仅将revert的修改体如今当前的working tree,不自动进行提交。可是若是你真的想回滚那些修改的话,再次commit这个环节是逃不掉的。
  • 回滚最后的N次提交(永远从commit的history中抹掉这些记录):这种场景就轮到git reset登场了。git reset的帮助文档写的很是清楚,在回滚commit的场景中,他的做用就是将当前的HEAD reset到你指定的那个分支。但这个过程当中最值得注意的就是你使用的参数,最经常使用的主要是--soft(我的推荐使用这个,他不会修改你目前index或者working tree中所作的任何修改)/--mixed(你在reset时不加任何参数时的默认行为,会默默把你在index中的修改给灭了!)/--hard(这个是我绝的最危险的参数,会把你index和working tree中的全部修改毁灭的毛都不剩,使用以前请三思,这确实是你要的行为!)这三种。由于我推荐使用--soft参数,下面主要演示回滚到3f412...那次的记录(git reset --soft HEAD~2):

     从上面能够看出来,你的index区域突然多了不少未提交的修改,这些就是回滚回来的记录,要怎么处理他们,就看你的了。这时我再来看看log的记录信息:

     最新的提交已经变成咱们但愿的那次了。其实从git reset的解释中,咱们就能够看出,git reset是一个“斩断”式的回滚操做,由于你把当前的HEAD指针直接移动到了你须要回滚到的那次记录。而git自己的commit链条是逆向回溯的,因此你在提交历史里面再也找不到当前HEAD指向的commit以后的记录了。(不过若是你是git文艺青年的话,你固然知道,想找到那些表面上找不到的commit,经过reflog也是易如反掌)。

好了,到这里,经常使用的git回滚操做和场景都介绍完了。但愿对不熟悉git的TX能有所帮助。

转载自:https://hittyt.iteye.com/blog/1961386

相关文章
相关标签/搜索