雪飞海 2019-06-26
版本库,也就是git的分支库,其实是git最核心的部分。当我们提到git时,其实就是在说git分支。如果你是一个个人开发者,你的项目只有一个人在开发,永远只有一个master分支,那么你可能永远接触不到分支的内容,你对git的了解会永远停留在「了解」这一阶段。
版本库这个名称来源,就是它管理的是一个个「版本」。
版本库是由分支组成的,分支则是由commit组成的。commit就是版本库的基本单位。
在git中,每个都commit有一个独一无二的「commit id」,也就是说通过这个id你可以精确定位到某一个commit上。
所以,为什么git要求每次commit的时候必须为commit添加明确的commit message,并且要专门开辟出stage暂存区来管理每一次commit的内容(svn就没有暂存区),就是为了能够随时进行commit(代码版本)层面的操作,明确每一次commit,保证出现问题时能快速定位,以及完成版本回退等操作。
简单的说,分支就是由commit串成的一条时间线,就是这样子的:
上面的每个圈圈都是一次commit。
在新建git仓库时,git会自动给你创建一条分支,叫master,你之后所做的所有操作都是在这个master分支上操作。不过,并不要把一个分支就理解为是这样的一条时间线。这里可能有点绕,也是git中最抽象的一个点之一。就是:
master分支其实是一个指针,它指向一个commit,代表了在这条时间线上,master指针指向的那个commit时间点的代码状态。可能对于指针概念有基础的同学会更好理解一点。如下图:
当我们建立一个分支时,使用两种方式:
这样子建立的分支指针会指向目前你所在的那个分支(master),假如我们创建了一个分支叫dev,那么我们的时间线就变成了这个样子:
可以看到,时间线还是那条时间线,不过已经有两条分支同时存在了,只是这两条分支都指向同一个commit,所以两条分支内容完全相同,git也就只会指一下指针,不会对代码和时间线做任何操作。但是,抽象来看,你可以把这两条分支看作是两条线。
而我们怎么确定哪一条分支是我们当前的分支呢?也是指针,不过是一个指向指针的指针,叫做HEAD。HEAD指针在git中算是个关键字,HEAD就是当前指向当前分支的指针的意思。我们只用git branch dev
创建分支时,HEAD指针不会做改变,当前分支依然是master分支,当前状态如图所示:
当我们在当前这种状态下,将暂存区中的内容进行一次提交时,会将时间线向前推进,生成一个commit,然后把当前分支master指向最新的commit。如图所示:
当切换分支时,我们会使用git checkout <branch name>
,将HEAD指针指向切换过去的那个分支名。
我们是不是见到了老朋友checkout!
在前面的暂存区我讲过,checkout作为检出操作是将暂存区中的内容同步到工作区的方法。这里的checkout依然是检出的意思,只不过检出的对象变成了相应分支的文件。
其实我们可以把暂存区想象成一个分支,暂存区的内容就是commit的内容,这两种操作的意义就保持一致了,只不过检出分支时HEAD指针会移动。checkout文件会修改工作区。所以,执行git checkout dev
后,分支状态就变成了这样:
而上面说过,checkout会修改工作区,所以你磁盘上的文件就变成了dev分支上那次commit时的样子。而在创建时提到的git commit -b <branch name>
则同时完成了「创建分支」和「检出分支」两个操作,方便得很。
使用git branch -av
就可以查看所有分支的信息了(a是all,v是verbose)。
涉及操作:git branch <branch name>
, git checkout <branch name>
理解git结构与简单操作(一)git的本质
理解git结构与简单操作(二)工作区与暂存区
理解git结构与简单操作(三)认识版本库与分支
理解git结构与简单操作(四)合并分支的方法与策略