Git 对象存储结构分析

1. 前言

Git 对象有:blob (数据块), tree (目录树), commit (提交), tag (标签)。

本文通过一个示例,以blob为例来讨论对象的存储结构。示例采用的git版本为2.17

2. 实践讨论

2.1. 生成Blob对象文件

首先创建一个测试git仓库

$ mkdir hello
$ cd hello
$ git init

然后通过创建一个文件 test,test的内容为 "hello", 可以看到test文件的字节长度为6, 是因为创建文件的时候自动在行末加上了换行符\n。对文件其执行 git add , 可以看到在.git/objects目录下面生成了一个子目录ce,ce目录下面有个文件013625030ba8dba906f756967f9e9ca394464a。

$ echo "hello" > test
$ du -b test
6       test
$ git add test
$ find .git/objects/ -type f
.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a

该文件即为git为 test 文件数据内容生成的blob对象文件,该对象的SHA值为ce013625030ba8dba906f756967f9e9ca394464a.

至此,产生两个问题:

  1. 对象文件的数据结构是什么样的 ?
  2. 对象的SHA值又是如何生成的 ?

2.2. 对象数据结构及SHA值

根据 Git-Internals-Git-Objects 文中的描述:

首先,对象文件数据结构如下图:

git-object-storage.png
  • content: 表示数据内容
  • head: 对象头部信息
    • object type:对象类型,可选值为 blob, tree, commit
    • whitespace: 一个空格字符
    • content byte size:数据内容的字节数字符串
    • NUL:空字符,ASCII码值为0

然后, 对象的SHA值就是对上面这个数据结构执行SHA1 hash摘要算法得到的。

2.3. 动手验证

根据2.2中的规则来编码对2.1中的test文件内容生成一个SHA值,看是否和git生成的SHA值一致?

    // object content
    String content = "hello\n";
    byte[] contentBytes = content.getBytes();

    ByteBuffer buf = ByteBuffer.allocate(1024);


    buf.put("blob".getBytes()); // object type
    buf.put((byte) ' ');        // whitespace
    buf.put(Integer.toString(contentBytes.length).getBytes());  // content byte size numeric string
    buf.put((byte) 0);          // NUL
    buf.put(contentBytes);      // content

    buf.flip();

    // whole object bytes
    byte[] objectBytes = new byte[buf.remaining()];
    buf.get(objectBytes);

    // Execute SHA1 hash digest
    MessageDigest md = MessageDigest.getInstance("SHA1");
    byte[] shaBytes = md.digest(objectBytes);

    // Show in hex
    String shaHex = Hex.encodeHexString(shaBytes);
    System.out.println(shaHex);

上述代码输出:ce013625030ba8dba906f756967f9e9ca394464a

和 2.1 中git生成的SHA值一致,2.2 中数据结构和SHA值的生成得到验证。

2.4. 对象压缩

根据 Git-Internals-Git-Objects 可知git 对象文件是经过Zlib::Deflate.deflate 压缩存储的。

$ cat .git/objects/ae/a941d707291bf3f2103c096479b068f7bed4f8
x☺K
cat: write error: Input/output error

可以看到通过cat命令是无法直接输出内容的。

        InputStream is = new InflaterInputStream(new FileInputStream(
                ".git\\objects\\ce\\013625030ba8dba906f756967f9e9ca394464a"));

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        int b = 0;
        while ((b = is.read()) != -1) {
            baos.write(b);
        }

        byte[] res = baos.toByteArray();
        System.out.println(new String(res));

        is.close();
        baos.close();

通过如上代码可输出对象的数据结构:

blob 6hello

注其中包括不可见字符NUL和换行符。

通过git cat-file命令可以直接查看对象的数据内容:

$ git cat-file -p ce013625030ba8dba906f756967f9e9ca394464a
hello

3. 总结

  1. 对象数据结构为:
git-object-storage.png
  1. 对象SHA值为对( 1. 对象数据结构)执行SHA1消息摘要算法生成;
  2. 对象存储结构为:对(1. 对象数据结构)进行deflate压缩后存储;

4. 参考资料

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏jiajia_deng

Qt 注册自定义数据类型提供信号和槽函数传递参数

Qt 信号和槽函数参数只能是基于 Qt 的基础类型的,比如 QString、int、bool 等,如果想传递自定义类型默认情况下是行不通的。要想在 Qt 的信号...

94520
来自专栏公众号_薛勤的博客

Java多线程编程核心技术(三)多线程通信

通过本节可以学习到,线程与线程之间不是独立的个体,它们彼此之间可以互相通信和协作。

13280
来自专栏运维技术迷

shell生成随机字符的几种方法

一般在写shell脚本的时候,会有需要生成一些随机字符,比如在写批量生成用户+随机密码的脚本的时候,就会用到随机生成的字符串来作为新建用户的密码。以下的几种方式...

1.8K60
来自专栏码字搬砖

scrapy之ip池

备注: process_request(request, spider) 当每个request通过下载中间件时,该方法被调用。 process_re...

36520
来自专栏漏斗社区

Burp Suite API学习思路(二)

在上篇工具| Burp Suite API学习思路文章中,斗哥介绍了BurpSuite扩展开发的一些准备工作与利用IHttpListener接口监听模块接收返回...

12320
来自专栏云鼎实验室的专栏

Drupal CVE-2018-7600 分析及 PoC 构造

漏洞分析 Drupal 在 3 月 28 日爆出一个远程代码执行漏洞,CVE 编号 CVE-2018-7600,通过对比官方的补丁,可以得知是请求中存在 # 开...

43050
来自专栏架构之路

Struts2 中的值栈的理解

通过对struts2的一段时间的接触,将自己对OGNL的核心值栈说说,值栈:简单的说,就是存放action的堆栈,当我们提交一个请求道服务器端 action时,...

27330
来自专栏张善友的专栏

URL安全的Base64编码

Base64编码可用于在HTTP环境下传递较长的标识信息。在其他应用程序中,也常常需要把二进制数据编码为适合放在URL(包括隐藏表单域)中的形式。此时,采用Ba...

64690
来自专栏BinarySec

NETBIOS主机名编码算法

最近在看SMB协议,在自己构造数据包的时候发现了一个问题。 经过查阅资料发现NETBIOS对主机名的编码方式如下: 1.将字符补齐到16字节,不够的用空格补 ...

42680
来自专栏安恒网络空间安全讲武堂

WriteUp分享 | CTF-web

题目 各种绕过哦 TXT? 文件上传测试 本地包含 考细心 正则? PHP很烦人? 一道签到题 抽抽奖 never give up I have a j...

3.4K80

扫码关注云+社区

领取腾讯云代金券