Git存储机制
在理解Git分支的运行机制之前,要了解一下Git是如何存储数据的。
当使用 git commit
新建一个提交对象前,Git 会先计算每一个子目录的校验和,然后在 Git 仓库中将这些目录保存为树(tree)对象。之后 Git 创建的提交对象,除了包含相关提交信息以外,还包含着指向这个树对象(项目根目录)的指针,如此它就可以在将来需要的时候,重现此次快照的内容了。
单个提交对象在仓库中的数据结构如下图所示:
上图中,Git 仓库中有五个对象:三个表示文件快照内容的 blob 对象;一个记录着目录树内容及其中各个文件对应 blob 对象索引的 tree 对象;以及一个包含指向 tree 对象(根目录)的索引和其他提交信息元数据的 commit 对象。
作些修改后再次提交,那么这次的提交对象会包含一个指向上次提交对象的指针,即下图中的 parent 对象。两次提交后,仓库历史会变成下图的样子:
上图展现了多个提交对象之间的链接关系。
什么是Git分支
Git 中的分支,其实本质上仅仅是个指向 commit 对象的可变指针。Git 会使用 master 作为分支的默认名字。在若干次提交后,你其实已经有了一个指向最后一次提交对象的 master 分支,它在每次提交的时候都会自动向前移动。
如下图所示:
Git 创建一个新的分支就等同于创建一个新的分支指针。
比如我们要创建一个名为testing的分支,我们可以使用git branch
命令:
$ git branch testing
这会在当前 commit 对象上新建一个分支指针,如下图所示:
Git会保存着一个名为 HEAD 的特别指针,用来记录当前当前你正在哪个分支上工作。
运行 git branch
命令,仅仅是建立了一个新的分支,但不会自动切换到这个分支中去,所以在上个例子中,我们依然还在 master 分支里工作,如图:
要切换到其他分支,可以执行 git checkout
命令。我们现在转换到新建的 testing 分支:
$ git checkout testing
这样 HEAD 就指向了 testing 分支:
此时,如果我们再提交一次:
$ vim test.rb
$ git commit -a -m 'made a change'
提交后的结果示意图如下:
我们会发现,每次提交后 HEAD 随着当前所在分支一起向前移动,testing 分支向前移动了一格,而 master 分支仍然指向原先 git checkout 时所在的 commit 对象。
现在我们回到 master 分支看看:
$ git checkout master
这条命令做了两件事。它把 HEAD 指针移回到 master 分支,并把工作目录中的文件换成了 master 分支所指向的快照内容。也就是说,现在开始所做的改动,将始于本项目中一个较老的版本。它的主要作用是将 testing 分支里作出的修改暂时取消,这样你就可以向另一个方向进行开发。
我们作些修改后再次提交:
$ vim test.rb
$ git commit -a -m 'made oth