git使用进阶——版本穿梭reset三种模式理解
Posted 涂宗勋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了git使用进阶——版本穿梭reset三种模式理解相关的知识,希望对你有一定的参考价值。
在日常工作中使用git,除了正常场景外,会有各种异常场景,比如误提交了代码需要回退就是其中一个常见的场景,这就需要用到reset
。
要理解reset
相关的内容,首先需要理解git工作区相关概念,可以参考上一篇:
https://tuzongxun.blog.csdn.net/article/details/120315735
上一篇关于工作区的图也先拿过来用一下:
首先,reset
翻译过来是重置,常用于回退操作,但是实际上它的作用不局限于回退,而是可以进行版本之间的切换。
reset操作有三种模式,分别是soft
、mixed
和hard
。
soft
例如我有一个被git管理的文件·test.txt·,经过了三次提交,使用git log --pretty=oneline --abbrev-commit
查看结果如下:
b6430be (HEAD -> master) update username
73c9b49 add username
b161811 init
可以看到当前最新的是b6430be
,提交信息为update username
。
上边三次提交的具体内容如下:
- 在
test.txt
中写入一行git init
,然后提交; - 在
test.txt
中写入一行add username tzx
,然后提交; - 在
test.txt
中修改add username tzx
为add username tzx123
,然后提交。
假如这时候发现最后一次的提交内容不对,上一次的才对,需要回退到和上一次提交的内容一样,我目前知道的有两种常用做法,一种是直接修改代码为和上一个版本的一样,然后重新提交。
这样做了之后使用git log
就会查到四个提交记录,但是第四次的内容和第二次的实际是一样的。
这种做的好处是,会直接留下修改记录,但是坏处也是会直接留下中间记录。
如果有的公司或者项目就是需要直接留下这种记录,那么就必须这样做,他们可能会在远程仓库设置不允许强制push
。
同时,也有公司或者项目不需要这种中间的记录,允许直接擦除,以使提交记录相对少一些,那么就可以使用reset
进行回退。
例如,这里可以先执行git status
看一下状态:
On branch master
nothing to commit, working tree clean
证明当前工作区、暂存区和本地版本库都是一致的。然后执行git reset --soft 73c9b49
回退到指定版本(这里其实还可以有其他的写法,但是我觉得这种直接指定id的方式个人更习惯),之后再执行git log --pretty=oneline --abbrev-commit
查看日志,结果如下:
73c9b49 (HEAD -> master) add username
b161811 init
可以看到,现在就只剩下了两次提交记录,然后再使用git status
查看状态,结果如下:
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: test.txt
理解这里的内容,就需要开头提到的上一篇的知识了。
这里的意思是,git当前状态为:test.txt
文件有修改,并且已经修改已经添加到了暂存区,但是还没有提交。
根据提示可以看到,这种状态下可以进行两个操作,要么commit
到本地版本库,要么restore
到工作区。
那么这里先使用commit
操作,例如git commit -m "reset"
,然后再查看日志:
ede0234 (HEAD -> master) reset
73c9b49 add username
b161811 init
然后再使用git status
会看到现在工作区、暂存区和本地版本库又都是一致的了。同时,在上边几个reset之后的任意时间查看test.txt
文件,会发现内容实际都是之前第三次提交后的内容。
那么这里重新执行git reset --soft 73c9b49
进行回退,再查看日志,结果如下:
73c9b49 (HEAD -> master) add username
b161811 init
执行git status
还是会看到之前一样的内容:
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: test.txt
这一次进行第二种操作,执行git restore --staged test.txt
,然后再执行git status
结果如下:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: test.txt
很显然,根据上一篇的知识可以知道,这里的意思是当前文件的修改已经只存在于工作区了,也有两种操作可以选择,要么add到暂存区提交,要么放弃修改。
add到暂存区,这个就是之前的普通操作,这里就直接看一下第二个选择,执行操作之前,先确认一下文件内容,如下:
git init
add username tzx123
确认完之后,执行放弃修改的操作git restore test.txt
,然后再使用git status
会看到结果如下:
On branch master
nothing to commit, working tree clean
上边内容证明工作区、暂存区和本地版本库的内容又一致了。然后再看一下test.txt
文件的内容,结果如下:
git init
add username tzx
显然,到这里才真正回到了之前想要的回退效果。总结一下步骤如下:
git reset --soft 73c9b49
回退到暂存区;git restore --staged test.txt
回退到工作区;git restore test.txt
回退工作区的内容,完成回退;
这里画一个简单的图来说明每个操作后文件内容的变化:
reflog和git log -g
上边说直接修改内容之后再提交会留下直接记录,使用reset
没有直接记录,这里重点是直接
两个字。
也就是说,没有直接记录,而不是没有记录。
这里的直接记录指的是使用git log
查看到的记录,那么实际上使用git reflog
或者git log -g
还是可以查看到记录的,例如这里执行git reflog --abbrev-commit
,结果就如下:
73c9b49 (HEAD -> master) HEAD@{0}: reset: moving to 73c9b49
ede0234 HEAD@{1}: commit: reset
73c9b49 (HEAD -> master) HEAD@{2}: reset: moving to 73c9b49
b6430be HEAD@{3}: reset: moving to HEAD
b6430be HEAD@{4}: commit: update username
73c9b49 (HEAD -> master) HEAD@{5}: commit: add username
b161811 HEAD@{6}: commit (initial): init
很显然,这里的提交记录、reset记录都是可以查到的。
hard
上边也说了,reset
常用来回退,但是并不是只能回退。或者说,这里通常所说的回退其实只是指git log
里看到的内容的回退。
那么借助于git reflog
或者git log -g
,实际还可以实现前进操作。
例如,在上边的基础上查看当前的log如下:
73c9b49 (HEAD -> master) add username
b161811 init
也就是当前显示有两次提交,最新的是73c9b49
,但是根据git reflog
我们实际可以看到在这个提交之后还有很多的操作,比如现在我们又想回到最初三次提交的那个样子,我这里就可以借助reset
的hard
模式直接跳到之前第三次提交的那个点,例如执行git reset --hard b6430be
,然后再查看日志,结果如下:
b6430be (HEAD -> master) update username
73c9b49 add username
b161811 init
这里可以翻到最开始的地方进行比较,就能看到和第一次查看log时的结果一样了。
同时,如果现在执行git status
会看到结果是这样:
On branch master
nothing to commit, working tree clean
也就是说当前也是工作区、暂存区、本地版本库状态都一致。那么这时候再查看一下test.txt
文件内容,可以看到也成了最开始回退之前的样子:
git init
add username tzx123
为了进一步说明问题,这里再回到第二次的提交,执行git reset --hard 73c9b49
,然后再查看日志会看到又只剩下了两次提交。
执行git status
也会看到是干净的不需要提交的、三区一致的状态,查看文件内容也是成了下边这样:
git init
add username tzx
显然,和soft
不一样的是,hard
模式是一步到位,相当于把上边soft
模式的三步都操作了。数据变化如图所示:
mixed
那么上边两种模式,都是必须要手动加对应模式的参数的,也就是说不是默认的模式,默认的是mixed
,可以不指定。
当前文件内容如上,没有末尾的123
,log结果如下:
73c9b49 (HEAD -> master) add username
b161811 init
为了保证三个示例看起来一致,这里先再执行git reset --hard b6430be
跳到第三次提交的位置,然后使得查看日志结果如下:
b6430be (HEAD -> master) update username
73c9b49 add username
b161811 init
然后再执行使用mixed
模式回到第二次提交的位置,使用git reset 73c9b49
或者git reset --mixed 73c9b49
后,再使用git status
查看状态,看到结果如下:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
这时候如果查看文件内容,会看到还是第二次提交的内容,并没有回到第三次提交的有123
结尾的。
根据对工作区相关概念的理解,可以知道上边的意思是文件test.txt
有修改,这个修改目前存在于工作区,可以有两个操作可以选择,一个是add
到暂存区,另一个是放弃当前工作区的修改。同时,add
操作也可以直接使用commit -a
直接提交。
那么add
操作实际也相当于是修改了新内容后要进行的普通操作,而放弃工作区修改,实际就是和soft
模式的第三步一样。
总结下操作步骤就是:
git reset --mixed 73c9b49
回退到工作区;git restore test.txt
回退工作区的内容,完成回退;
那么mixed
模式下每步操作的数据状态变化如下图:
总结
那么最后总结一下这三种模式的区别,大致如下:
- soft模式,会改变本地版本库的内容,保留暂存区和工作区之前的内容;
- mixed模式,会改变本地版本库和暂存区的内容,保留工作区之前的内容;
- hard模式,会把本地版本库、暂存区、工作区的内容全部改变。
以上是关于git使用进阶——版本穿梭reset三种模式理解的主要内容,如果未能解决你的问题,请参考以下文章