• Git 是如何存储文件的


    01、存储方式

    Git 从核心上来看不过是简单地存储键值对(key-value)。它允许插入任意类型的内容,并会返回一个键值,通过该键值可以在任何时候再取出该内容。

    Git 存储数据内容的方式,为每份内容生成一个文件,取得该内容与头信息的 SHA-1 校验和,创建以该校验和前两个字符为名称的子目录,并以 (校验和) 剩下 38 个字符为文件命名 (保存至子目录下)。

    写入对象
    $ echo 'version 2' > test.txt
    $ git hash-object -w test.txt
    
    取回对象
    $ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
    
    返回对象的类型
    $ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a

    02、Git 对象

    数据对象 blob object

    树对象 tree object
    解决文件名的保存问题,允许我们将多个文件组织到一起,Git 以一种类似于 UNIX 文件系统的方式存储内容,但作了些许简化。

    所有内容均以树对象和数据对象的形式存储,其中树对象对应了 UNIX 中的目录项,数据对象则大致上对应了 inodes 或文件内容。 一个树对象包含了一条或多条树对象记录(tree entry),每条记录含有一个指向数据对象或者子树对象的 SHA-1 指针,以及相应的模式、类型、文件名信息。

    $ git cat-file -p master^{tree}
    100644 blob a906cb2a4a904a152e80877d4088654daad0c859      README
    100644 blob 8f94139338f9404f26296befa88755fc2598c289      Rakefile
    040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0      lib (这是一个文件夹)

    你可以自己创建 tree 。通常 Git 根据你的暂存区域或 index 来创建并写入一个 tree 。因此要创建一个 tree 对象的话首先要通过将一些文件暂存从而创建一个 index 。

    $ git write-tree
    
    $ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579

    image

    commit 对象
    commit 对象有格式很简单:指明了该时间点项目快照的顶层树对象、作者/提交者信息(从 Git 设置的 user.name 和 user.email中获得)以及当前时间戳、一个空行,以及提交注释信息。

    每一个 commit 对象都指向了你创建的树对象快照。

    当你执行git add, git commit, Git其实就是保存修改了的文件的 blob,更新索引,创建 tree 对象,最后创建 commit 对象,这些 commit 对象指向了顶层 tree 对象以及先前的 commit 对象。

    这三类 Git 对象 ── blob,tree 以及 commit ── 都各自以文件的方式保存在 .git/objects 目录下。

    image

    03、对象存储

    Git是如何存储对象的?

    构造文件头

    对象类型+空格+数据内容的长度+空字节(null byte)

    >> header = "blob #{content.length}"
    => "blob 1600"

    计算SHA-1 校验和

    Git 将文件头与原始数据内容拼接起来,并计算拼接后的新内容的 SHA-1 校验和:

    >> store = header + content
    => "blob 1600what is up, doc?"
    >> require 'digest/sha1'
    => true
    >> sha1 = Digest::SHA1.hexdigest(store)
    => "bd9dbf5aae1a3862dd1526723246b20206e5fc37"

    用 zlib 对数据内容进行压缩

    在 Ruby 中可以用 zlib 库来实现。首先需要导入该库,然后用 Zlib::Deflate.deflate() 对数据进行压缩:

    >> require 'zlib'
    => true
    >> zlib_content = Zlib::Deflate.deflate(store)
    => "x234K312311OR04c(317H,Q310,V(-320QH311O266a00_34a235"

    将用 zlib 压缩后的内容写入磁盘

    需要指定保存对象的路径 (SHA-1 值的头两个字符作为子目录名称,剩余 38 个字符作为文件名保存至该子目录中)。

    >> path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38]
    => ".git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37"
    >> require 'fileutils'
    => true
    >> FileUtils.mkdir_p(File.dirname(path))
    => ".git/objects/bd"
    >> File.open(path, 'w') { |f| f.write zlib_content }
    => 32

    所有的 Git 对象都以这种方式存储,惟一的区别是类型不同 ── 除了字符串 blob,文件头起始内容还可以是 commit 或 tree 。不过虽然 blob 几乎可以是任意内容,commit 和 tree 的数据却是有固定格式的。

    04、参考

  • 相关阅读:
    开机启动服务(ftp、apache、mysql)
    ElasticSearch + Canal 开发千万级的实时搜索系统【转】
    filebeat 报错 Unable to create runner due to error: Can only start a prospector when all related states are finished
    linux挂载 mount
    cas单点登录原理
    SpringBoot项目集成cas单点登录
    注解@Slf4j的作用
    Java操作Redis
    CentOS下firewalld添加开放端口
    Prometheus Querying Function rate() vs irate()
  • 原文地址:https://www.cnblogs.com/tonyq/p/7596131.html
Copyright © 2020-2023  润新知