Git存储数据的原理 · 语雀

3,098 阅读4分钟
原文链接: yuque.com

Git存储数据的方式

Git 是一个内容寻址文件系统,也就是说他实际上是一个键值对数据库(key-value data store)。Git 存储一份数据,然后返回一个40位的 Hash 值作为键值,通过这个键值,我们就能找到所存储的数据。
- .git/
  - objects/
    - 9d/
      aeafb9864cf43055ae93beb0afd6c7d144bfa4
Hash 值的前两位作为目录,后38位为文件名。文件的内容就是 键值对 ,也就是 Git 存储的一系列数据。

Git会存储哪些数据?

Git会存储哪些数据呢?文件有三种状态: 未暂存、已暂存和已提交。Git 会存储 已暂存已提交 的文件。同时,当我们提交数据时,当前的提交信息也会被保存下来。所以,Git 会存储两种数据:
  1. 已暂存已提交 的文件。
  2. 每次提交时的信息数据,也就是 commit 的注释、作者信息等。

Git如何存储数据?

Git 以键值对的方式存储数据, 就是 .git/objects 下的每个目录名及文件名, 就是文件的内容,这些值也称为 对象 。而对应于所存储的两种数据,Git 存储的对象有三种: 数据对象树对象提交对象

数据对象

Git 使用数据对象来记录每一个文件的数据。比如将一个文件添加到暂存区(index)中,这时,在 objects 目录下就会产生一个数据对象。可以用此命令查看该数据对象: git cat-file -t <hash>

树对象

Git 在每次提交时,会将暂存区的所有数据保存起来,这时会产生一个 树对象 。树对象中的数据记录了在提交前,处于暂存区中的每个文件的状态。这个记录并非将数据都拷贝到当前树对象的文件中,而是记录了暂存区中每个文件的数据对象的 值。树对象与数据对象存储的结构关系如下图:树对象与数据对象的存储关系

提交对象

每次向 Git 仓库中提交文件时,Git 会生成一个提交对象,用以保存当前提交的信息,包括此次提交的父提交对象,作者的信息等。这个提交对象的保存方式也是以 Hash 值为文件名的文件。Git就是将所需要用的到数据转为这三种对象,并且以 Hash 值为文件名的文件的形式,保存在 .git/objects 中。实际上,除了这三种对象,还有第四种对象,即 标签(tags) 对象。保存形式也是一样的。

实际操作

数据存储方式

初始化一个 Git 仓库,当前 .git/objects/ 目录如下:
-.git/
  - objects/
    - info/
    - pack/
添加一个文件:
$ echo 'test' > test.txt
将其加入到暂存区:
$ git add test.txt
可以看到 objects/ 下多了一个文件夹:
- objects/
    - info/
    - pack/
    - 9d/
        aeafb9864cf43055ae93beb0afd6c7d144bfa4
这个就是保存的在暂存区中的文件。将暂存区的文件提交:
$ git commit -m "first commit"
可以看到 objects/ 下多了两个文件夹:
- objects/
  - 2b/
    297e643c551e76cfa1f93810c50811382f9117
  - 25/
    b94c3791ec86eef47f62bd9ad23e5d5fb9b2b2
  - 9d/
    aeafb9864cf43055ae93beb0afd6c7d144bfa4
多出来的两个文件,一个是此次提交的树对象,一个是此次提交的提交对象。

三种提交对象

运行以下命令:
$ git cat-file -t 9daeafb9864cf43055ae93beb0afd6c7d144bfa4
blob  # 表明是一个数据对象
$ git cat-file -t 2b297e643c551e76cfa1f93810c50811382f9117
tree # 表明这是一个树对象
$ git cat-file -p 2b297e643c551e76cfa1f93810c50811382f9117
100644 blob 9daeafb9864cf43055ae93beb0afd6c7d144bfa4    test.txt 
 ## 这个树对象就是我们将 test.txt 文件提交到仓库中生成的树对象。
 ## 这个树对象存储的是提交前在暂存区中存储的数据,存储的 Hash 值指向了暂存区中的数据。
$ git cat-file -t 25b94c3791ec86eef47f62bd9ad23e5d5fb9b2b2
commit # 表明这是一个提交对象
$ git cat-file -p 25b94c3791ec86eef47f62bd9ad23e5d5fb9b2b2
tree 2b297e643c551e76cfa1f93810c50811382f9117
author Vactor <123456@gmail.com> 838388484 +0800
committer Vactor <123456@gmail.com> 838388484 +0800

first commit
# 以上打印出的就是我们提交时的信息。