git分支简介,理解HEAD,master

为了真正理解 Git 处理分支的方式,咱们须要回顾一下 Git 是如何保存数据的。git

或许你还记得 起步 的内容,Git 保存的不是文件的变化或者差别,而是一系列不一样时刻的文件快照。算法

在进行提交操做时,Git 会保存一个提交对象(commit object)。知道了 Git 保存数据的方式,咱们能够很天然的想到——该提交对象会包含一个指向暂存内容快照的指针。 但不只仅是这样,该提交对象还包含了做者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。首次提交产生的提交对象没有父对象,普通提交操做产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象,vim

为了更加形象地说明,咱们假设如今有一个工做目录,里面包含了三个将要被暂存和提交的文件。 暂存操做会为每个文件计算校验和(使用咱们在 起步 中提到的 SHA-1 哈希算法),而后会把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 对象来保存它们),最终将校验和加入到暂存区域等待提交:网站

$ git add README test.rb LICENSE
$ git commit -m 'The initial commit of my project'

当使用 git commit 进行提交操做时,Git 会先计算每个子目录(本例中只有项目根目录)的校验和,而后在 Git 仓库中这些校验和保存为树对象。 随后,Git 便会建立一个提交对象,它除了包含上面提到的这些信息外,还包含指向这个树对象(项目根目录)的指针。如此一来,Git 就能够在须要的时候重现这次保存的快照。版本控制

如今,Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个树对象(记录着目录结构和 blob 对象索引)以及一个提交对象(包含着指向前述树对象的指针和全部提交信息)。指针

Figure 9. 首次提交对象及其树结构

作些修改后再次提交,此次产生的提交对象会包含一个指向上次提交对象(父对象)的指针。code

Figure 10. 提交对象及其父对象

Git 的分支,其实本质上仅仅是指向提交对象的可变指针。 Git 的默认分支名字是 master。 在屡次提交操做以后,你其实已经有一个指向最后这个提交对象的 master 分支。 它会在每次的提交操做中自动向前移动。orm

Git 的 “master” 分支并非一个特殊分支。 它就跟其它分支彻底没有区别。 之因此几乎每个仓库都有 master 分支,是由于 git init 命令默认建立它,而且大多数人都懒得去改动它。对象

Figure 11. 分支及其提交历史

分支建立blog

Git 是怎么建立新分支的呢? 很简单,它只是为你建立了一个能够移动的新的指针。 好比,建立一个 testing 分支, 你须要使用 git branch 命令:

$ git branch testing

这会在当前所在的提交对象上建立一个指针。 两个指向相同提交历史的分支。

Figure 12. 两个指向相同提交历史的分支

Git 又是怎么知道当前在哪个分支上呢? 也很简单,它有一个名为 HEAD 的特殊指针。 请注意它和许多其它版本控制系统(如 Subversion 或 CVS)里的 HEAD 概念彻底不一样。 在 Git 中,它是一个指针,指向当前所在的本地分支(译注:将 HEAD 想象为当前分支的别名)。 在本例中,你仍然在 master 分支上。 由于 git branch 命令仅仅 建立 一个新分支,并不会自动切换到新分支中去。 HEAD 指向当前所在的分支。

Figure 13. HEAD 指向当前所在的分支

你能够简单地使用 git log 命令查看各个分支当前所指的对象。 提供这一功能的参数是 --decorate。

$ git log --oneline --decorate
f30ab (HEAD, master, testing) add feature #32 - ability to add new
34ac2 fixed bug #1328 - stack overflow under certain conditions
98ca9 initial commit of my project

正如你所见,当前 “master” 和 “testing” 分支均指向校验和以 f30ab 开头的提交对象。 分支切换

要切换到一个已存在的分支,你须要使用 git checkout 命令。 咱们如今切换到新建立的 testing 分支去:

$ git checkout testing

这样 HEAD 就指向 testing 分支了。 HEAD 指向当前所在的分支。

Figure 14. HEAD 指向当前所在的分支

这样的实现方式会给咱们带来什么好处呢? 如今不妨再提交一次:

$ vim test.rb
$ git commit -a -m 'made a change'

HEAD 分支随着提交操做自动向前移动。

Figure 15. HEAD 分支随着提交操做自动向前移动

如图所示,你的 testing 分支向前移动了,可是 master 分支却没有,它仍然指向运行 git checkout 时所指的对象。 这就有意思了,如今咱们切换回 master 分支看看:

$ git checkout master

检出时 HEAD 随之移动。

Figure 16. 检出时 HEAD 随之移动

这条命令作了两件事。 一是使 HEAD 指回 master 分支,二是将工做目录恢复成 master 分支所指向的快照内容。 也就是说,你如今作修改的话,项目将始于一个较旧的版本。 本质上来说,这就是忽略 testing 分支所作的修改,以便于向另外一个方向进行开发。 分支切换会改变你工做目录中的文件

在切换分支时,必定要注意你工做目录里的文件会被改变。 若是是切换到一个较旧的分支,你的工做目录会恢复到该分支最后一次提交时的样子。 若是 Git 不能干净利落地完成这个任务,它将禁止切换分支。

咱们不妨再稍微作些修改并提交:

$ vim test.rb
$ git commit -a -m 'made other changes'

如今,这个项目的提交历史已经产生了分叉(参见 项目分叉历史)。 由于刚才你建立了一个新分支,并切换过去进行了一些工做,随后又切换回 master 分支进行了另一些工做。 上述两次改动针对的是不一样分支:你能够在不一样分支间不断地来回切换和工做,并在时机成熟时将它们合并起来。 而全部这些工做,你须要的命令只有 branch、checkout 和 commit。 项目分叉历史。

Figure 17. 项目分叉历史

你能够简单地使用 git log 命令查看分叉历史。 运行 git log --oneline --decorate --graph --all ,它会输出你的提交历史、各个分支的指向以及项目的分支分叉状况。

$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the
* 34ac2 fixed bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my project

因为 Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,因此它的建立和销毁都异常高效。 建立一个新分支就至关于往一个文件中写入 41 个字节(40 个字符和 1 个换行符),如此的简单能不快吗?

这与过去大多数版本控制系统造成了鲜明的对比,它们在建立分支时,将全部的项目文件都复制一遍,并保存到一个特定的目录。 完成这样繁琐的过程一般须要好几秒钟,有时甚至须要好几分钟。所需时间的长短,彻底取决于项目的规模。而在 Git 中,任何规模的项目都能在瞬间建立新分支。 同时,因为每次提交都会记录父对象,因此寻找恰当的合并基础(译注:即共同祖先)也是一样的简单和高效。 这些高效的特性使得 Git 鼓励开发人员频繁地建立和使用分支。

转载

我的网站

相关文章
相关标签/搜索