Git分支原理

Git分支原理


前言

最近工作由SVN换成Git了,不由地想探寻一下这两种版本控制工具的差别到底在哪里,于是有了这篇笔记。


Git保存方式

Git和SVN的差别主要就在于对待数据的方式。

SVN将存储的信息看作是一组基本文件和每个文件随时间逐步累积的差异,因此称为是基于差异的版本控制,存储的是文件的变化或者差异。

SVN

而Git把数据看作是对小型文件系统的一系列快照。

每当你提交更新或保存项目状态时,它基本上就会对当时的全部文件创建一个快照并保存这个快照的索引。为了效率,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。

Git

至于什么是文件快照呢?请接着看。


备份与快照

要理解备份与快照的区别,先需要理解计算机存储文件的方式。

平常在写代码对文件进行操作的时候,文件基本上都是以数据流的形式进行操作的,所以可能看上去文件都是连续存储的。

但实际上文件并不是连续存储的,文件内容数据会被分成一块一块分布存储到各个存储空间中。平常能够看到的文件实际上是由数据块描述结构组成的,每个数据块描述结构记录着数据块指针、数据块长度、数据块修改时间等待信息。而这个数据块描述结构中的数据块指针指向的就是这一小块文件内容数据在存储空间中存储的地址。

文件结构

在理解文件存储方式之后,备份和快照的区别也就很好理解了。

简而言之,备份在存储空间多了全部的数据块,在数据块描述结构中,数据块指针全部指向新的数据块。而快照在存储空间中只多了修改过的数据块,在数据块描述结构中,被修改过的数据块指针指向新的数据块,没被修改过的数据块指针不变。

快照和备份

这也就是为什么Git使用快照作为保存数据的方式如此高效的原因。

这里参考大佬的文章方便更进一步理解。(上面两张图是拿的大佬的)

当然快照虽然快,但是还是存在一些缺陷的。比如,当原文件和快照数据块指针同时指向的地址中的数据发生修改或是出现异常,那么此时的快照无法成功还原到文件最初始的样子。这里不做深入探讨了,主要是需要理解快照的本质。


Git分支

每次commit操作,Git都会将其存储成一个数据结构。该commit数据结构中包括作者姓名、邮箱、评论、父对象指针和内容快照指针。

父对象指针指向的是该commit的上一次commit,每次提交都会父对象指针自动指向上一次的commit。

内容快照指针指向的是一个树形数据结构,其中存储着所有的快照对象。

对象

所谓的Git分支,本质上就是一个指向commit对象的指针。

每进行一次commit操作,该分支的指针就会指向刚刚那个commit对象。

那么如何知道当前使用者在哪个分支上呢?

Git有一个HEAD的特殊指针,指向使用者当前处在的分支指针。

分支

看到这里,再结合Git的快照存储方式,是不是瞬间就理解了Git的分支到底是如何实现的!


合并与变基

没有使用过Git的小伙伴可能对变基(rebase)感到有些陌生。本质上来说,merge和rebase都是Git提供用于整合不同分支的修改的方法。

这里用官方的范例通俗易懂地解释一下:

假设在开发任务的过程中出现了两个不同的分支,并且它们各自提交了新的内容。

分支1

如果是merge操作,那么就会把两个分支最新的commit和分支分叉的commit进行三方合并,之后将合并的结果形成一个新的快照并commit。从图中来看,就是C2、C3和C4三方合并成了C5。

merge

如果是rebase操作,那么就会把某一分支的修改全部移动到另一分支上,也就是可以认为这个分支以另一分支的最新commit为基础(base),重新提交这个分支之前修改的内容。

rebase

这两种整合分支的方法最终的结果都没有任何的区别。从Git提交记录上来看,rebase操作会使得提交记录更加整洁,看得更加舒服,但同时也会面临更大的风险。

因为变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。

如果A已经将提交推送至某个仓库,而B也已经从该仓库拉取提交并进行了后续工作。此时,如果A使用了rebase并再次推送,那么B因此将不得不再次把他的修改内容与A的提交进行整合。如果接下来A还要拉取并整合B修改过的提交,事情就会变得一团糟。具体实例这里就不写了,感兴趣的小伙伴可以看官网手册

总之,merge和rebase没有绝对的好与坏,各有利弊,开发者可以同时使用,也可以单方面使用一个,因各个项目实际情况而异。



补充:branch和tag的区别

在Git中,tag虽然在本质上和branch比较类似,但是在使用方式和使用含义上有较大的区别。

首先,tag在意义上作为发布的版本管理,通常来说,每发布一个版本就会打一个tag。如果我们想要在之前的版本上进行功能的开发,我们可以用tag快速跳转到旧版本,然后拉出新的branch进行开发。

其次,每个tag指向的是一个commit,是一个点,不可进行移动;相比branch来说,branch有一个HEAD指针指向branch上的commit。

总结来说,branch就是正常的开发流程,而tag就是某一commit的一个别名,以便于快速访问到这次commit的状态。



后记

很多内容都是参考的官网手册,这里只是用通俗的话进行了一个简单的总结和记录。

总而言之,学就完事了!

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值