Git数据存储的原理浅析

https://segmentfault.com/a/1190000016320008
 

写作背景

进来在闲暇的时间里在看一些关系P2P网络的拓扑发现的内容,重点关注了Markle Tree的知识点,在一篇文章里(https://www.sdnlab.com/20095....),发现了了一句话“Merkle DAG的一个常见例子就是Git存储库”,于是查找了一些关于git存储库的原理,先整理如下。仅供自己和大家参考。

Git存储库解析

当时我的疑问:

  • git怎么存储数据的,如何能根据存储的数据可以很精确的回退到制定的版本?
  • git存储和docker的存储机制类似吗?是不是都是分层的存储?
  • git如果不是分层,每次提交都存储起来,那么数据量大了会怎么办?

解惑
我们的解惑路线是,从新建一个本地git仓库开始,一步一步增加数据和提交,观察内容的具体变化。

  • 首先使用git init新建一个本地仓库,然后打开仓库中的.git文件夹

    • HEAD表示当前提交的指针位置;
    • index是索引文件;
    • refs文件夹中的文件是不同分支指向的commitID;
    • logs文件夹中记录的是每次refs的历史记录;
    • objects文件夹中的内容就是用来存放git本地仓库对象
  • 既然找到了存储git数据的位置,那么git数据结构是什么样的呢?

    • git 是以键值的方式存储的,也就是说任何类型的数据都可以存储。因此也可以在任何时候通过键取出对应的值;
    • git中底层生成了4中数据的对象:

      1. tree对象:可以看作一个目录,管理一些“tree”对象或是“blob”对象。它有一串指向“blob”对象或是其它“tree”对象的指针,一般用来表示内容之间的目录层次关系(就像文件和子目录)。
      2. blob对象: 一个“blob”通常用来存储文件的内容。一个“blob”对象就是一块二进制数据,blob对象的键是根据SHA1算法生成的,所以若两个文件在一个目录树或是一个版本仓库中有同样的数据内容,那么它们将会共享同一个“blob”对象,和其所对应的文件所在路径、文件名是否改被更改都完全没有关系。
      3. commit对象:“commit”对象指向一个“tree对象”,并且带有相关的描述信息,标记项目某一个特定时间点的状态。它包括一些关于时间点的元数据,如时间戳、最近一次提交的作者、指向上次提交的指针等等。
      4. tag对象: 一个“tag”对象包括一个对象名(SHA1签名)、对象类型、标签名、标签创建人的名字(“tagger”), 还有一条可能包含有签名(signature)的消息。
  • 当新增一些内容的时候,进行git commit命令会出现什么变化?

    • 当进行一次提交的时候,objects、logs和refs文件夹都会发生变化,我们主要关注objects文件夹。
    • 每次commit都会对数据进行一次保存,会生成commit对象、tree对象和blob对象;
    • objects文件夹里面的数据存放的具体规则,对于这三种对象,都会用SHA-1对内容和头信息生成Hash值,去hash值的前两位为objects目录下面的文件夹的名字,取剩余38个字符为文件名,例:8b0c4fe1567a463214c09334b54977e0114c90fe,取8b在objects创建一个文件夹,取0c4fe1567a463214c09334b54977e0114c90fe为文件名在8b文件夹下创建一个文件。
  • 知识点学习了之后,我们怎么去验证呢?

    • 使用 git cat-file 对我们提交的内容进行验证。
    • 我进行了两次commit,一次是完全新建的一个README.md文件,里面是有一行数据(### You Know);第二次commit,新建一个test.py文件和在README.md中新添加了一些数据;
    • git cat-file -t查看对象的类型,git cat-file -p优雅的方式打印对象的内容。
  1. 使用git log --pretty=oneline查看我的两次提交
  2. 使用git cat-file -t 172b54c8cd3eedca2fc301374286c2cb807d674f查看第一次提交的类型
  3. 使用git cat-file -p 172b54c8cd3eedca2fc301374286c2cb807d674fe查看第一次提交的内容
  4. 使用git cat-file -p 8b0c4fe1567a463214c09334b54977e0114c90fe查看第一次提交的tree对象,可以看到tree对象中存放的是一个blob对象,就是我们第一次提交新建的文件README.md
  5. 使用git cat-file -p 67aeba604cea61ec63d19db0706b19d846c65ba4查看第一次提交的blob对象的内容为### You Know
  6. 使用git cat-file -p 03543a4c19023da01b5114d7f7a614d95a1bf084查看第二次提交的内容
  7. 使用git cat-file -p 03543a4c19023da01b5114d7f7a614d95a1bf084查看第二次提交的tree对象内容,包括修改的内容和新增的内容
  8. 使用git cat-file -p 03543a4c19023da01b5114d7f7a614d95a1bf084查看第二次提交的README.md blob对象内容,可以看到是整个文件的全部内容,而不是仅仅包含修改的数据。
  • 总结问题的答案

    • git的数据存储数据结构是键值类型,分为4个对象,并且每次提交都是整个文件的存储,而不是分层的增加存储,所以这样会导致存储的数据量很大,那git用的方法是使用zlib对数据进行压缩,所以我们打开存储的文件是这样的数据,那我们都是用cat-file命令来查看的,怎么才能这些内容是经过zlib压缩过的呢?
    • 我用GO语言写了一个简单的程序,来验证这些数据是经过zlib压缩之后的,运行这个程序的时候带上你要查看git对象的文件路径,就可以看到被还原的内容了;

      go程序代码

    • package main
      
      import (
          "bytes"
          "compress/zlib"
          "fmt"
          "io"
          "io/ioutil"
          "os"
      )
      
      //进行zlib压缩
      func DoZlibCompress(src []byte) []byte {
          var in bytes.Buffer
          w := zlib.NewWriter(&in)
          w.Write(src)
          w.Close()
          return in.Bytes()
      }
      
      //进行zlib解压缩
      func DoZlibUnCompress(compressSrc []byte) []byte {
          b := bytes.NewReader(compressSrc)
          var out bytes.Buffer
          r, _ := zlib.NewReader(b)
          io.Copy(&out, r)
          return out.Bytes()
      }
      
      func main() {
          args := os.Args
          {
              fmt.Println("Should input zlib file path.")
             return
          }   
      
          b, err := ioutil.ReadFile(args[])
          if err != nil {
              fmt.Print(err)
          }
      
          fmt.Println(string(DoZlibUnCompress(b)))
      }

最新文章

  1. Java 数组基础
  2. MAC && Linux terminal session clone
  3. windows快捷键集锦
  4. sublime3添加对react代码检查
  5. Sprint第二个冲刺(第十天)
  6. WP 8.1 中挂起时页面数据保存方式
  7. iOS8 获取通知设置状态
  8. 【服务器环境搭建-Centos】jdk的安装
  9. linux概念之内存分析
  10. Oracle命令(一):Oracle登录命令
  11. Ajax 生成流文件下载 以及复选框的实现
  12. hbase总结(二)-hbase安装
  13. about flashback_transaction_query
  14. 注册表被篡改,请重新安装应用程序 acdphotoEdit
  15. zookeeper原理介绍
  16. [Gitlab运维系列]Gitlab 403 forbidden 并发引起IP被封
  17. json和java对象相互转换
  18. VB用API模拟截屏键PrintScreen
  19. ECMAScript5.1的运算符、类型转换总结
  20. .NET Core中的路由约束

热门文章

  1. linux网络工具iproute2的使用简介
  2. OpenCV——膨胀与腐蚀
  3. JAVA框架 Mybaits 输入和输出映射
  4. tar 压缩 解压 打包命令
  5. 常用的php数组函数
  6. docker 端口映射错误解决方法
  7. struts2_文件上传的功能
  8. 2017战略No.2:开始电子化记账
  9. PHP调用WCF提供的方法
  10. LeetCode Generate Parentheses (DFS)