上篇文章介绍了关于本地库的一些基本操作,如:初始化、添加、提交等等,本篇文章将介绍如何使用Git切换项目版本。
在进行版本切换之前,我们需要了解一个指令:
git log
该指令能够查看提交历史,执行该指令,结果如下:
前面我们进行了两次提交,通过该指令就都显示出来了,包括提交的作者标识,提交时间,提交描述等。仔细观察,会发现这两次提交的信息展示不太一样:
首先是commit后面的字符串不一样,这是通过一系列hash算法计算出来的一个值,作为每次提交的索引;其次是在第二次提交中,有这么一个信息:(HEAD -> master)
,这里的HEAD其实是一个指针,它指向的是当前版本,所以要想实现版本的切换,我们就需要改变HEAD指针的指向。
为了后续测试,这里我再进行一次修改,然后提交:
使用git log指令虽然可以显示提交历史,但是显示得过于详细有时候也并不方便,当提交次数逐渐增多时,这样显然会加重我们查找某些重要信息的负担,所以我们还需要掌握几个关于查看提交历史的指令。
比如:
git log --pretty=oneline
意思是以只显示一行的方式来输出提交历史,结果如下:
还可以使用:
git log --oneline
运行结果:
该方式显示的内容将会更加简洁,哈希值只显示部分。
最后一条指令:
git reflog
运行结果:
这种方式显示提交历史的区别在于,它多了一个信息:HEAD@{0}
。
比如第二次提交信息中的HEAD@{1}
,意思是说,从当前版本切换到第二次提交的版本,需要将HEAD指针移动一次;
同理,HEAD@{2}
表示切换到该版本需要将HEAD指针移动两次。
需要注意的是,只有git reflog能够显示所有版本的提交历史,什么意思呢?
假设有一个项目提交了很多次,此时想要回退到之前的版本,当你回退之后,其它查看提交历史的指令是无法看到当前版本之前的历史的,而git reflog可以。
经过前面的铺垫,相信大家已经对版本切换的实现有了一个大体的认识,接下来就是掌握具体的指令了。 对于版本切换,Git提供了三种方式:
这是比较方便的一种切换版本的方式,既然是基于索引值,我们就得先得到索引值,执行指令:
git reflog
此时我若是想切换到第一次提交的版本,执行指令:
git reset --hard 43dc5b0
运行结果:
HEAD指针目前处于43dc5b0,版本切换成功。
切换版本时哈希值不必全部写出来,只需要写出部分能够唯一标识当前版本即可。
现在去看看工作区里的test.txt文件,打开一看:
文件是空的,此时证明版本已经回退到最初始的时候了。
该指令不光能回退,还能前进,比如我想前进到第二次提交时的版本,执行指令:
git reset --hard 47ee58a
运行结果:
再去看看test.txt文件:
没错,第二次提交的内容就是这个。
综上所述,通过索引值,我们能够切换到任意版本。
该符号也能实现版本切换,但它只能后退版本,无法前进。 我们先用索引值切换到最新的版本,执行指令:
git reset --hard 05f2f17
然后执行指令:
git reset --hard HEAD^
运行结果:
版本回退到了第二次提交的时候,查看test.txt文件内容:
指令中包含几个^
符号则代表回退几个版本。
使用^
符号虽然也能够实现版本切换,但我若是想回退几十个版本,那指令后面就得跟着几十个^
符号,这显然不是一个好的选择,这时候就可以使用~
符号。
假设我需要回退十五个版本,就可以执行如下指令:
git reset --hard HEAD~15
~
符号后面的数字是多少则表示回退多少版本,所以该方式同样只能回退版本而无法前进。
学习了版本切换之后,有同学可能会疑惑,git reset指令中的hard参数是什么意思?它是否有别的参数呢?
这里介绍reset指令的三个参数:
先看soft,这是官方文档对其的解释:
Does not touch the index file or the working tree at all (but resets the head to , just like all modes do). This leaves all your changed files “Changes to be committed”, as git status would put it.
意思是说,仅仅在本地库移动HEAD指针,完全不触及索引文件或工作树。
再看mixed:
Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit) and reports what has not been updated. This is the default action.
该方式会在本地库移动HEAD指针,并重置索引,但不重置工作树。
最后是head:
Resets the index and working tree. Any changes to tracked files in the working tree since are discarded.
该方式会在本地库移动HEAD指针,并重置索引和工作树。
举个例子,目前我所处的是第二次提交的版本,执行如下指令:
git reset --soft 05f2f17
我们通过该指令切换到最新的版本,查看一下提交历史,执行指令:
gt reflog
运行结果:
目前HEAD指针确实指向了最新版本,但是我们去查看一下test.txt文件:
可以看到,test.txt文件并没有恢复到最新版本的内容,而且我们执行如下指令查看一下状态:
git status
看到这里,有些同学可能懵了,前面说了soft仅仅是改变了本地库中的HEAD指针,完全不触及索引和工作树,照这样说,工作区的内容应该完全没有变化,可通过git status指令,终端却提示我们有文件被修改了。这是为什么呢?
这里注意理解,事实上工作区里的文件确实没有被修改,只是因为版本区的HEAD指针被修改了,此时暂存区里的内容无法与版本区对应,相对来说,暂存区的内容被修改了。也就是说,版本区的版本往回退,相当于暂存区的内容往前了,所以终端才会提示我们有文件被修改了。事实上并不是真正意义上的被修改,可能思维会有点绕。
现在我们版本是处于最新版本,我们通过mixed参数回退到最初始的版本试一试:
git reset --mixed 43dc5b0
运行结果:
此时成功回退到最初始版本,我们看看test.txt文件:
文件依然没有变化,我们再执行git status指令查看一下状态:
这里又变成红色了, 红色表示未被暂存区追踪,这又该如何理解呢?
还是一样的道理,mixed参数会改变版本区的指针并重置索引,此时本地库和暂存区处于同一版本,它们都回退了,此时相对来说,工作区的版本就往前了,这并不是真正意义上的,而是相对而言的。
该参数使得版本区、暂存区和工作区的版本同时变化,所以是没有前面的问题的。
新建文件夹X
大自然用数百亿年创造出我们现实世界,而程序员用几百年创造出一个完全不同的虚拟世界。我们用键盘敲出一砖一瓦,用大脑构建一切。人们把1000视为权威,我们反其道行之,捍卫1024的地位。我们不是键盘侠,我们只是平凡世界中不凡的缔造者 。