首页 > Python基础教程 >
-
Git应用详解第四讲:版本回退的三种方式与stash(2)
git revert HEAD~n
该指令的作用为重做所在分支的倒数第n+1
次提交,会发生冲突,需要手动合并,完成重做操作。过程与上述一致,这里就不再赘述了。
总结:常用
git revert commit_id
这种方式。
3.撤销revert
操作
思路很简单,再次通过revert
操作取消上一次的revert
操作(即所谓"负负得正")。
操作前,dev
分支上的提交记录和test.txt
文件内容如下:
通过:git revert --no-edit f4a95
重做提交dev2
(--no-edit
表示不修改新增提交的注释):
重做后,多了一次提交,并且test.txt
文件中删除了dev2
这一行内容。此时,可以通过:
git revert --no-edit 582d127
重做上一次重做操作,以此达到取消上一次重做操作的目的:
如上图所示,虽然多出了一次提交,但是test.txt
文件中被删除的dev2
内容被恢复了,这样就撤销了revert
操作。
Ⅲ.git checkout
1.git checkout commit_id
使用checkout
可以进行版本回退,如直接使用:
git checkout cb214
回退到提交3rd
,此时会出现如下提示:
注意到,切换后HEAD
指向的不再是master
分支,而是cb214...
即第三次提交,查看历史提交记录:
可看到只有3
次提交,什么意思呢?如下图所示:
通过git checkout
让HEAD
指针指向了第3
次提交,可以将它想象为一个新的分支。但是却没有实际创建分支,即此时head
指向的由提交1~3
组成的commit
对象链条处于游离状态;
接着,在HEAD
还指向游离的提交节点3
的基础上对文件做出新的修改:
-
此时如果我们切换回
master
分支,会出现下列错误
提示显示:如果没有保存就从游离的提交上切换到master
分支,这一修改就会被checkout
命令覆盖。我们可以在切换前进行一次提交操作:
此时的状态为:
-
在游离的
Commit
对象链中进行了一次提交之后,再次通过:git checkout master
切换到master
分支:
提示大意为:如果没有任何分支指向刚才在游离的Commit
对象链中进行的提交,那么该提交就会被忽略。此时的状态如下图所示:
如果想要创建一个分支保存(指向)这条游离的Commit
对象链,现在就是很好的时机。根据上述提示的命令:
git branch mycommit c4d5cc3
创建指向commit_id
为c4d5cc3
的提交(即上述的提交节点5
)的分支mycommit
:
由此游离的commit
对象链得以被新分支所指向,并得到了保存,此时的状态如下图所示:
总结:
通过
checkout
进行版本回退会造成游离的提交对象链,需要额外创建一个分支进行保存;因此,使用
checkout
进行版本回退的思路为,先切换到想要回退的提交版本,再删除进行版本回退的分支dev
。最后,创建一个新的dev
分支指向游离的提交对象链,完成分支dev
的版本回退,简称"偷天换日";只要有分支指向,提交就不会被丢弃。
Ⅳ.revert
与reset
的选择
由于checkout
会造成游离的提交对象链,所以,一般不使用checkout
而是使用reset
和revert
进行版本回退:
-
revert
通过创建一个新提交的方式来撤销某次操作,该操作之前和之后的提交记录都会被保留,并且会将该撤销操作作为最新的提交; -
reset
是通过改变HEAD
和分支指针指向的方式,进行版本回退,该操作之后的提交记录不会被保留,并且不会创建新的提交;
在个人开发上,建议使用reset
;但是在团队开发中建议使用revert
,特别是公共的分支(比如master
),这样能够完整保留提交历史,方便回溯。
Ⅴ.回退方法汇总
版本回退主要有三大方式:reset
、revert
和checkout
,各方式的比较如下:
方法 | 效果 | 向前回退 | 向后回退 |
同步修改HEAD 与分支指向 |
---|---|---|---|---|
git reset --hard HEAD^ |
往前回退1 次提交 |
能 | 否 | 是 |
git reset --hard HEAD^^ |
往前回退2 次提交 |
能 | 否 | 是 |
git reset --hard HEAD~n |
往前回退n 次提交 |
能 | 否 | 是 |
git reset --hard <commit_id> |
回退到指定commit id 的提交 |
能 | 能 | 是 |
git revert HEAD |
重做最新一次提交 | 能 | 否 | 是 |
git revert HEAD^ |
重做倒数第二次提交 | 能 | 否 | 是 |
git revert HEAD^^ |
重做倒数第三次提交 | 能 | 否 | 是 |
git revert HEAD~n |
重做倒数第n+1 次提交 |
能 | 否 | 是 |
git revert commit_id |
重做指定commit_id 的提交 |
能 | 能 | 是 |
git checkout commit_id |
回退到指定commit id 的提交 |
能 | 能 | 否 |
从上表可知,只有下列三种方式可以自由地向前向后回退:
git reset --hard commit_id
git revert commit_id
git checkout commit_id
但是,使用checkout
进行回退会出现游离的提交,需要创建一个新分支进行保存,所以不常用。
二、git stash
1.git stash
的作用
git stash
指令的作用为:对没有提交到版本库的,位于工作区或暂存区中游离的修改进行保存,在需要时可进行恢复。具体应用场景如下:
在master
分支进行两次提交:1st
和2nd
,随后创建并切换到dev
分支。在dev
分支上进行一次提交(dev1
),此时两分支的状态为:
随后在dev
分支上给文件test.txt
添加一行dev2
,但是不提交到暂存区,直接切换到master
分支,会出现如下错误:
图中显示的错误大意为:在dev
分支上的修改会被checkout
操作覆盖。下面我们来看看,将dev
分支上的这一修改操作添加到暂存区后,再切换分支,是否还会出现同样的问题:
可见还是会出现该错误,这初步验证了位于工作区和暂存区中的修改都会被checkout
操作覆盖的结论。原因如下图所示:
虽然在dev
分支上修改了文件,但是没有将这一修改操作进行提交。这样就不会产生提交节点,就如上图所示,修改dev2
是游离的,在切换分支的时候会被丢弃。
这种情况在日常开发中很常见,当在develop
分支上开发新功能的时候,master
分支出现紧急情况需要切换回去进行修复。但是,当前分支的新功能还没开发完全,贸然切换分支,原来开发的内容就会因被覆盖而丢失,怎么办呢?
有人可能会说进行一次commit
不就可以了吗?确实可以。但是,这样不符合提交的代码就是正确代码的原则。更好的解决方法为使用git stash
,如下图所示:
可见git stash
可以将当前dev
分支上,位于在工作区或暂存区中的修改,在未提交的情况下进行了保存;并且将分支回退到修改前的状态,保存过后,就可以很顺畅地切换回master
分支了。
图中的
WIP
(working in progress
)表示的是正在进行的工作;
当我们在master
分支上完成了工作,再次切换回dev
分支时,查看test.txt
文件:
发现切换分支前所做的修改dev2
消失了,这是为什么呢?
-
其实,上面通过
git stash
将dev
分支上工作区或暂存区中的修改,提交到了stash
区域进行保存,并将dev
分支回退到修改前的状态。如下图所示: -
切换到
master
分支时test
分支上的修改依旧会被覆盖。所以,再次回到dev
分支时需要从stash
区域中恢复切换分支前保存的修改;
怎样恢复通过git stash
保存到stash
中的修改呢?可以通过:
git stash list
查看该分支上被stash
保存的修改:
继续给test.txt
文件添加内容:dev3
,并通过以下指令保存修改的同时添加注释:
git stash save '注释'
-
首先,通过上述命令可以修改
stash
中存储修改的备注信息; -
其次,虽然在
test
分支上进行了两次修改,但是使用git stash
保存修改后,文件test.txt
并没有实际被修改;
2.恢复stash
存储的修改
方法有很多,主要有以下三种:
git stash pop
如图所示,通过上述命令将stash
中存储的最新一次修改恢复了。相信你已经发现了,stash
与栈非常类似:先保存的修改,排在最后,序号最大;后保存的修改,排在最前,序号最小;
恢复了最新一次修改后,再次查看stash
:
可以看到存储的修改只剩下一条了,由此可推断出git stash pop
作用为:
-
第一:恢复
stash
中存储的最新一次修改; -
第二:将该修改从
stash
中删除;
git stash apply
如上图所示,使用该指令时发生了合并冲突。这是因为,stash
中保存的每一次修改代表的都是一个版本。
-
如上图所示,在
test
分支上,进行第一次修改后,通过git stash
将该修改作为修改0
保存到stash
中,此时分支中的文件并没有发生改变; -
进行第二次修改后,通过
git stash
将修改作为修改1
保存到stash