0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Git 底层知识:详细分析对象查询流程和算法

454398 来源:机器之心 作者:阿里技术 2020-10-13 15:29 次阅读

本文将系统分享 Git 底层知识:对象生命周期变化,底层数据结构,数据包文件结构,数据包文件索引,以及详细分析对象查询流程和算法

状态模型

Git 底层知识:详细分析对象查询流程和算法

上图描述了 git 对象的在不同的生命周期中不同的存储位置,通过不同的 git 命令改变 git 对象的存储生命周期。

工作区 (workspace)

就是我们当前工作空间,也就是我们当前能在本地文件夹下面看到的文件结构。初始化工作空间或者工作空间 clean 的时候,文件内容和 index 暂存区是一致的,随着修改,工作区文件在没有 add 到暂存区时候,工作区将和暂存区是不一致的。

暂存区 (index)

老版本概念也叫 Cache 区,就是文件暂时存放的地方,所有暂时存放在暂存区中的文件将随着一个 commit 一起提交到 local repository 此时 local repository 里面文件将完全被暂存区所取代。暂存区是 git 架构设计中非常重要和难理解的一部分。

本地仓库 (local repository)

git 是分布式版本控制系统,和其他版本控制系统不同的是他可以完全去中心化工作,你可以不用和中央服务器 (remote server) 进行通信,在本地即可进行全部离线操作,包括 log,history,commit,diff 等等。完成离线操作最核心是因为 git 有一个几乎和远程一样的本地仓库,所有本地离线操作都可以在本地完成,等需要的时候再和远程服务进行交互。

远程仓库 (remote repository)

中心化仓库,所有人共享,本地仓库会需要和远程仓库进行交互,也就能将其他所有人内容更新到本地仓库把自己内容上传分享给其他人。结构大体和本地仓库一样。

文件在不同的操作下可能处于不同的 git 生命周期,下面看看一个文件变化的例子。

文件变化

对象模型

仓库结构

git 分布式的一个重要体现是 git 在本地是有一个完整的 git 仓库也就是 .git 文件目录,通过这个仓库,git 就可以完全离线化操作。在这个本地化的仓库中存储了 git 所有的模型对象。下面是 git 仓库的 tree 和相关说明:

git 主要有四个对象,分别是 Blob,Tree, Commit, Tag 他们都用 SHA-1 进行命名。

你可以用 git cat-file -t 查看每个 SHA-1 的类型,用 git cat-file -p 查看每个对象的内容和简单的数据结构。git cat-file 是 git 的瑞士军刀,是底层核心命令。

Blob 对象

只用于存储单个文件内容,一般都是二进制的数据文件,不包含任何其他文件信息,比如不包含文件名和其他元数据。

Tree 对象

对应文件系统的目录结构,里面主要有:子目录 (tree),文件列表 (blob),文件类型以及一些数据文件权限模型等。

如下图输出:

详细解释如下:

Commit 对象

是一次修改的集合,当前所有修改的文件的一个集合,可以类比一批操作的“事务”。是修改过的文件集的一个快照,随着一次 commit 操作,修改过的文件将会被提交到 local repository 中。通过 commit 对象,在版本化中可以检索出每次修改内容,是版本化的基石。

→ git cat-file -t fbf9e415f77008b780b40805a9bb996b37a6ad2ccommit→ git cat-file -p fbf9e415f77008b780b40805a9bb996b37a6ad2ctree bd31831c26409eac7a79609592919e9dcd1a76f2parent d62cf8ef977082319d8d8a0cf5150dfa1573c2b7author xxx 1502331401 +0800committer xxx 1502331401 +0800修复增量bug

详细解释如下:

Tag 对象

tag 是一个“固化的分支”,一旦打上 tag 之后,这个 tag 代表的内容将永远不可变,因为 tag 只会关联当时版本库中最后一个 commit 对象。

分支的话,随着不断的提交,内容会不断的改变,因为分支指向的最后一个 commit 不断改变。所以一般应用或者软件版本的发布一般用 tag。

git 的 Tag 类型有两种:

1 lightweight (轻量级)

创建方式:

git tag tagName

这种方式创建的 Tag,git 底层不会创建一个真正意义上的 tag 对象,而是直接指向一个 commit 对象,此时如果使用 git cat-file -t tagName 会返回一个 commit。

→ git cat-file -t v4commit→ git cat-file -p v4tree ceab4f96440655b0ff1a783316c95450fa1fb436parent 7f23c9ca70ce64fc58e8c7507c990c6c6a201d3dauthor 与水 1506224164 +0800committer 与水 1506224164 +0800

rawtest2

2 annotated (含附注)

创建方式:

git tag -a tagName -m‘’

这种方式创建的标签,git 底层会创建一个 tag 对象,tag 对象会包含相关的 commit 信息和 tagger 等额外信息,此时如果使用 git cat-file -t tagname会返回一个 tag。

→ git cat-file -t v3tag→ git cat-file -p v3object d5d55a49c337d36e16dd4b05bfca3816d8bf6de8 //commit 对象SHA-1type committag v3tagger xxx 1506230900 +0800

与水测试标注型tag

总结:所有对象模型之间的关系大致如下:

存储模型

概念

git 区别与其他 vcs 系统的一个最主要原因之一是:git 对文件版本管理和其他 vcs 系统对文件版本的实现理念完成不一样。这也就是 git 版本管理为什么如此强大的最核心的地方。

Svn 等其他的 VCS 对文件版本的理念是以文件为水平维度,记录每个文件在每个版本下的 delta 改变。

Git 对文件版本的管理理念却是以每次提交为一次快照,提交时对所有文件做一次全量快照,然后存储快照引用。

Git 在存储层,如果文件数据没有改变的文件,Git 只是存储指向源文件的一个引用,并不会直接多次存储文件,这一点可以在 pack 文件中看见。

如下图所示:

存储

随着需求和功能的不断复杂,git 版本的不断更新,但是主要的存储模型还是大致不变。如下图所示:

检索模型

→ cd .git/objects/→ ls03 28 7f ce d0 d5 e6 f9 info pack

git 的对象有两种:

一种是松散对象,就是在如上 .git/objects 的文件夹 03 28 7f ce d0 d5 e6 f9等,这些文件夹只有 2 个字符开头,其实就是每个文件 SHA-1 值的前 2 个字母,最多有 #OXFF 256 个文件夹。

一种是打包压缩对象,打包压缩之后的对象主要存在的是 pack 文件中,主要用于文件在网络上传输,减少网络消耗。

为了节省存储空间,可以手动触发打包压缩操作 (git gc),将松散对象打包成 pack 文件对象。也可以将 pack 文件解压缩成松散对象 (git unpack-objects)

→ cd pack→ lspack-efbf3149604d24e6ea427b025da0c59245b2c2ea.idx pack-efbf3149604d24e6ea427b025da0c59245b2c2ea.pack

为了加快 pack 文件的检索效率,git 基于 pack 文件会生成相应的索引 idx 文件。

pack 文件

pack 文件设计非常精密和巧妙,本着降低文件大小,减少文件传输,降低网络开销和安全传输的原则设计的。

pack 文件设计的概图如下:

pack 文件主要有三部分组成,Header, Body, Trailer

Header 部分主要 4-byte “PACK”, 4-byte “版本号”, 4-byte “Object 条目数”。

Body 部分主要是一个个 Git 对象依次存储,存储位置在 idx 索引文件中记录改对象在 pack 文件中的偏移量 offset。

Trailer 部分主要是所有 Objects 的名 (SHA-1)的校验和,为了安全可靠的文件传输。

下面我们看具体的 pack 文件:

从上图可知:通过 idx 索引文件在 pack 文件中定位到对象之后,对象的结构主要 Header 和 Data 两部分。

1 Header 部分

Header 中首 8-bits:1-bit 是 MSB,接着的 3-bits 表示的是当前对象类型,主要有 6种存储类型,接着的 4-bits 是用于表示该 Object 展开的 (length) 大小的一部分,只是一部分,完整的大小取决于MSB和接下来的多个 bits,完整算法如下:

如果 8-bits 中第一位是 1,表示下一个字节还是 header 的一部分,用于表示该对象展开的大小。

如果 8-bits 中第一位是 0,表示从下一个字节开始,将是数据 Data 文件。

如果对象类型是 OBJ_OFS_DELTA 类型, 表示的是 Delta 存储,当前 git 对象只是存储了增量部分,对于基本的部分将由接下来的可变长度的字节数用于表示 base object 的距离当前对象的偏移量,接下来的可变字节也是用 1-bit MSB 表示下一个字节是否是可变长度的组成部分。对偏移量取负数,就可知 base 对象在当前对象的前面多少字节。

如果对象类型是 OBJ_REF_DELTA 类型,表示的是 Delta 存储,当前 git 对象只是存储了增量部分,对于基本的部分,用 20-bytes 存储 Base Object 的 SHA-1 。

2 Data 部分

是经过 Zlib 压缩过的数据。可能是全部数据,也有可能是 Delta 数据,具体看 Header 部分的存储类型,如果是 OBJ_OFS_DELTA 或者 OBJ_REF_DELTA 此处存储的就是增量 (Delta) 数据,此时如果要取得全量数据的话,需要递归的找到最 Base Object,然后 apply delta 数据,在 base object 基础上进行 apply delta 数据也是非常精妙的,此文暂不做介绍。

从上面可以很清晰知道 pack 文件格式,我们再从本地仓库中一探究竟:

不是增量 delta 格式:

SHA-1 type size size-in-packfile offset-in-packfile

增量 delta 格式:

如 399334856af4ca4b49c0008a25b6a9f524e40350(SHA-1) 表示对象的 base object SHA-1 是 cb5a93c4cf9c0ee5b7153a3a35a4fac7a7584804,base 对象最大深度 (depth) 为 1,如果cb5a93c4cf9c0ee5b7153a3a35a4fac7a7584804 还有引用对象,则改变 depth 为 2。

pack Header 中最后 4-bytes 用于表示的 pack 文件中 objects 的数量,最多 2 的 32 次方个对象,所以一些大的工程中有多个 pack 文件和多个 idx 文件。

文件的 size (文件解压缩后大小) 有什么用呢,这个是为了方便我们进行解压的时候,设置流的大小,也就是方便知道流有多大。这里 size 不是说明下一个文件的偏移量,偏移量都是来自索引文件,见下面 idx:

index 文件

由于 version1 比较简单,下面用 version2 为例子:

分层模式:Header,Fanout,SHA,CRC,Offset,Big File Offset,Trailer。

Header 层

version2 的 Header 部分总共有 8-bytes,version 1 的 header 部分是没有的,前 4-bytes 总是 255, 116, 79, 99 因为这个也是版本 1 的开头四个字节,后面 4-bytes 用于表示的是版本号,在当前就是 version 2。

Fanout 层

fanout 层是 git 的亮点设计,也叫 Fanout Table(扇表)。fanout 数组中存储的是相关对象的数目,数组下标是对应 16 进制数。fanout 最后一个存储的是整个 pack 文件中所有对象的总数量。Fanout Table 是整个 git 检索的核心,通过它我们可以快速进行查询,用于定位 SHA 层的数组起始 - 终止下标,定位好 SHA 层范围之后,就可以对 SHA 层进行二分查找了,而不用对所有对象进行二分查找。

fanout 总共 256 个,刚好是十六进制的 #0xFF。fanout 数组用 SHA 的前面 2 个字符作为下标(对应 .git/objects 中的松散文件目录名,将 16 进制的目录名转换 10 进制数字),里面值就是用这两个字符开头的文件数量,而且是逐层累加的,后面的数组数量是包含前面数组的数据的个数的一个累加。

举例如下:

1)如果数组下标为 0,且 Fanout[0] = 10 代表着 #0x00 开头的 SHA-1 值的总数为 10 个。

2) 如果数组下标为 1,且 Fanout[1] = 15 代表着小于 #0x01 开头的 SHA-1 值的总数为 15 个,从 Fanout[0] = 10 知 Fanout[1] = (15-10)

为什么 git 设计上 Fanout[n] 会累加 Fanout[n-1] 的数量?这个主要是为了快速确定 SHA 层检索的初始位置,而不用每次去把前面所有 fanout[。.n-1] 数量进行累加。

SHA 层

是所有对象的 SHA-1 的排序,按照名称排序,按照名称进行排序是为了用二分搜索进行查找。每个 SHA-1 值占 20-bytes。

CRC 层

由于文件打包主要解决网络传输问题,网络传输的时候必须通过 crc 进行校验,避免传输过程中的文件损坏。CRC 数组对应的是每个对象的 CRC 校验和。

Offset 层

是由 4 byte 字节所组成,表示的是每个 SHA-1 文件的偏移量,但是如果文件大于 2G 之后,4 byte 字节将无法表示,此时将:

4 byte 中的第一 bit 就是 MSB,如果是 1 表示的是文件的偏移量是放在第 6 层去存储,此时剩下的 31-bits 将表示文件在 Big File Offset 中的偏移量,也就是图中的,通过 Big File Offset 层 就可以知道对象在 pack 中的 offset。

4 byte 中的第一 bit 就是 MSB,如果是 0 31-bits 表示的存储对象在 packfile 中的文件偏移量,此时不涉及 Big File Offset 层

Big File Offset 层

用于存储大于 2G 的文件的偏移量。如果文件大于 2G,可以通过 offset 层最后 31 bits 决定在 big file offset 中的位置,big file offset 通过 8 bytes 来表示对象在 pack 文件中的位置,理论上可以表示 2 的 64 次方文件大小。

Trailer 层

包含的是 packfile checksum 和关联的 idx 的 checksum。

索引流程

从上面的分层知道 git 设计的巧妙。git 索引文件偏移量的查询流程如下:

Git 底层知识:详细分析对象查询流程和算法

查询算法

通过 idx 文件查询 SHA-1 对应的偏移量:

在 pack 文件中通过偏移量找到对象:

如果是普通的存储类型。定位到的对象就是用 Zlib 压缩之后的对象,直接解压缩即可。

如果是 Delta 类型需要 递归查出 Delta 的 Base 对象,然后再把 delta data 应用到 base object 上(可参考 git-apply-delta)。

git 大多资料主要介绍是 git 使用,很少系统去讲解底层数据结构和原理。本文通过多个开源代码入手,结合 git 文档,参考相关 git 开发者或相关研究文章,git 邮件列表等。
编辑:hfy

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据结构
    +关注

    关注

    3

    文章

    573

    浏览量

    40158
  • Git
    Git
    +关注

    关注

    0

    文章

    201

    浏览量

    15786
收藏 人收藏

    评论

    相关推荐

    工业一体机在工业视觉设备中的应用

    工业一体机在工业视觉设备中的应用十分广泛,以下是对其应用的详细分析
    的头像 发表于 01-08 16:40 85次阅读

    adss光缆颜色详细分析

    过程中的识别,还便于后续的维护和故障排除。以下是对ADSS光缆颜色的详细分析: 一、光纤色谱排列 ADSS光缆内部的光纤通常按照一定的色谱进行排列,这些色谱包括蓝、桔(橙)、绿、棕、灰、白等颜色,具体排列方式可能因光缆的芯数不同而有所差异。例如: 2~24芯规格:每管4芯,色谱排列顺序
    的头像 发表于 01-08 10:47 73次阅读

     美国站群vps云服务器缺点详细分析

    美国站群VPS云服务器在提供多项优势的同时,也存在一些缺点。主机推荐小编为您整理发布美国站群vps云服务器缺点详细分析
    的头像 发表于 12-12 10:43 119次阅读

    【「从算法到电路—数字芯片算法的电路实现」阅读体验】+内容简介

    的Matlab建模和RTL设计,可帮助数字IC设计者掌握常用算法设计思路、工具和流程,从根本上提高设计基本算法电路和复杂算法电路的能力。本书共分为12章。第1~2章介绍
    发表于 11-21 17:14

    浅谈无刷电机的工作流程

    上一期的芝识课堂,我们跟大家一起分析了无刷电机的四个功能单元,并详细分析了PWM和逆变器单元的工作情况,今天我们继续来熟悉无刷电机工作流程中另外两个重要的部分——转子位置检测和波形驱动。
    的头像 发表于 11-12 13:46 520次阅读
    浅谈无刷电机的工作<b class='flag-5'>流程</b>

    如何分析美国站群服务器的成本效益?

    美国站群服务器的成本效益分析是一个复杂但至关重要的过程,它涉及到多个方面的考量。主机推荐小编为您推荐美国站群服务器成本效益的详细分析
    的头像 发表于 10-30 11:23 127次阅读

    锌银电池的优缺点_锌银电池的应用

    锌银电池,也称为银锌电池,是一种具有显著特点的电池类型。以下是锌银电池的优缺点详细分析
    的头像 发表于 10-03 14:51 1211次阅读

    OPA657跨阻放大器电路图的疑问求解

    各位TI专家,各位高手!附件电路图是一个OPA657跨阻放大器,有如下问题: 图中三级管在何时导通,三极管的作用具体是什么。 R1,R2,R3电阻的作用是什么?改变这三个电阻值会有什么影响。 另外请帮忙详细分析一下这个电路的具体参数和作用!谢谢
    发表于 09-24 07:58

    固态电池使用寿命

    固态电池的使用寿命是一个受到多方因素影响的复杂问题,以下是对其使用寿命的详细分析
    的头像 发表于 09-15 11:53 2919次阅读

    作物长势监测仪是什么?详细分析

    监测仪
    博科仪器
    发布于 :2024年07月05日 16:24:37

    bp神经网络算法的基本流程包括哪些

    BP神经网络算法,即反向传播神经网络算法,是一种常用的多层前馈神经网络训练算法。它通过反向传播误差来调整网络的权重和偏置,从而实现对输入数据的分类或回归。下面详细介绍BP神经网络
    的头像 发表于 07-04 09:47 682次阅读

    BP神经网络算法的基本流程包括

    、自然语言处理等。本文将详细介绍BP神经网络算法的基本流程,包括网络结构、激活函数、前向传播、反向传播、权重更新和训练过程等。 网络结构 BP神经网络由输入层、隐藏层和输出层组成。输入层接收外部输入信号,隐藏层对输入信号进行非线
    的头像 发表于 07-03 09:52 536次阅读

    NMOS LDO原理概括 NMOS LDO原理详细分析

    NMOS LDO详细工作原理见图2-17,图中是NMOS的输出特性曲线,让我们结合图2-16图2-17分析。假设一开始LDO工作状态在A点,当负载电流突然增加时Vo下降,Vi不变,由于VDS
    的头像 发表于 06-17 17:09 1.9w次阅读
    NMOS LDO原理概括 NMOS LDO原理<b class='flag-5'>详细分析</b>

    数控机床的加工特点以及主要加工对象

    特点及主要加工对象详细分析。 数控机床的加工特点 高精度 :数控机床通过精确的伺服电机控制,可以实现微米级的加工精度。 高效率 :自动化程度高,减少了人为操作的时间,提高了生产效率。 高柔性 :数控机床可以通过改变程序来适应不同的加工需求,具有很高的加工
    的头像 发表于 06-07 14:10 2601次阅读

    开关电源输出波形的分析方法

    开关电源作为电子设备中的重要组成部分,其输出波形的质量直接影响到整个系统的性能和稳定性。因此,对开关电源输出波形的深入分析显得尤为关键。本文将从多个角度对开关电源输出波形进行详细分析,并探讨其影响因素和改进方法。
    的头像 发表于 05-30 17:06 2522次阅读