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

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

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

为了更加形象地说明,我们假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件。 暂存操作会为每一个文件计算校验和(使用我们在 起步 中提到的 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. 首次提交对象及其树结构

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

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

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

Git 的 “master” 分支并不是一个特殊分支。 它就跟其它分支完全没有区别。 之所以几乎每一个仓库都有 master 分支,是因为 git init 命令默认创建它,并且大多数人都懒得去改动它。

Figure 11. 分支及其提交历史

分支创建

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 鼓励开发人员频繁地创建和使用分支。

转载

个人网站

git理解HEAD,master相关推荐

  1. git rebase origin master 和 origin/master

    google git rebase origin master 和 origin/master的区别,结合自己的理解,总结如下: 首先,git rebase是两个分支之间的作用,如下图 一般情况下,我 ...

  2. 《ProGit》读后感:我的git理解和常用Git命令

    git 中文乱码 输入:$env:LESSCHARSET='utf-8' ide中输入:LESSCHARSET=utf-8 git 特点: 本地存有所有的记录快照,而不是比较差异.而且一般只添加数据. ...

  3. git错误 ! [rejected] master -> master (non-fast-forward)

    问题描述 执行: $ git pull origin master:master 当要push代码到git时,出现提示: ! [rejected] master -> master (non-f ...

  4. VS2013中使用git发布解决方案master分支的时候出现错误

    VS2013中使用git发布解决方案master分支的时候出现错误 参考文章: (1)VS2013中使用git发布解决方案master分支的时候出现错误 (2)https://www.cnblogs. ...

  5. git branch set-upstream master 分支名/master

    场景:今天进行git操作的时候,被一个同事发现了,他说我的提交方式太麻烦,所以就给我指一条明路,.... 在这之前,我提交代码是这个画风: git add -A git commit -m'xx' g ...

  6. git push origin master报错

    git push origin master报错error: src refspec xxx does not match any / error: failed to push some refs ...

  7. 【已解决】github中git push origin master出错:error: failed to push some refs to(亲测)

    [背景] 折腾: [记录]将googlecode上面的crifanLib迁移到Github上 期间出错: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ...

  8. git 如何把master分支代码合并到自己的分支

    git 如何把master分支代码合并到自己的分支 master分支的代码领先自己的分支,git 如何把master分支代码合并到自己的分支 1.首先切换到主分支 git checkout maste ...

  9. git 回退远端master分支版本

    情景:脑残将自己分支上的内容合并到了master上面 需求: 将master分支还原到原来的版本. 方法: 1.  切换分支到master git checkout master 确认是否正确切换到了 ...

最新文章

  1. 【numpy】协方差计算
  2. Data Structure - 返回单链表的中间结点
  3. 零基础是学java还是python-零基础学习编程,Java、Python你会选择哪个?
  4. 2016计算机课程设计,2016年计算机组成原理课程设计-硬布线控制器的设计.doc
  5. python 建筑计算_制图小技巧:巧用Python和ELK瞬间完成总图建筑名称标注
  6. 镜头对摄像机性能的影响
  7. C8051汇编语言递归,基于C8051F310单片机的LED灯控制器汇编语言程序调试
  8. java 手势识别_【人体分析-手势识别】-Java示例代码
  9. mysql实现axure协同工作_Axure8利用Axure Share创建团队项目,进行多人协作开发
  10. 简单快速的视频上传分享网站,可做外链
  11. 《剑来》语句摘录(七)
  12. 金山词霸使用牛津词典取词的笨方法
  13. 计算机应用高级教程,大学计算机应用高级教程(第3版)简介,目录书摘
  14. 1.488Mpps是如何计算出来的
  15. 开启允许对外访问的端口8000
  16. Java 实现蓝桥杯不同非空子串
  17. 微信公众平台开发(92) 多客服(转)
  18. 《乔布斯转》读书笔记
  19. 苹果手机怎么把计算机放到桌面,iPhone怎么将电脑上的电影拖到手机上观看?
  20. 羧基水溶性量子点(PEG)标记鼻咽癌标志物EB病毒核EBNA1抗原|PEG-NH2修饰CdSe/ZnS 量子点(520nm)

热门文章

  1. unity 后台计时器实现
  2. 菜鸟学Linux - 用户与用户组基础
  3. 省市区三级联动菜单(附数据库)
  4. 装了linux开机出现错误,[已解决]安装完,启动出现错误!
  5. 机器学习——Day 1 数据预处理
  6. Congratulations! A Success Case on GDPR
  7. 面对海量请求,缓存设计还应该考虑哪些问题?(转)
  8. JavaScript自定义事件
  9. O-超大型LED显示屏
  10. sublime----------快捷键的记录