Git对象(objects)简介

Git对象(objects)简介

  • Git对象(objects)简介
    • 数据对象(blob)
    • 树对象(tree)
    • 提交对象(commit)
    • 总结

Git对象(objects)简介

Git对象一共有三种:数据对象、树对象以及提交对象,这些对象都被保存在了.git/objects目录下, 下面我们将通过git命令来观察,分析这几个对象的产生过程,以期对Git版本控制系统的内部原理有个深入的理解。

数据对象(blob)

数据对象的产生是在使用git add命令将文件或者目录加入到暂存区时产生的,Git会把一个文件中要存储的数据和一个头部信息一起做SHA-1散列运算,将得到的散列值作为这个文件的路径,下面我们将通过具体过程来进行展示和分析。初始化一个Git仓库,将一个test.txt加入仓库暂存区
Git对象(objects)简介_第1张图片
这时我们去objects目录下就会看到一个文件 12/651515fdbe068a7c5c74e3af3ba9be428d6f51
该文件名是由文件原内容加上特定头部信息一起做SHA-1散列值得到的,散列值前两字符用于命名子目录,余下的38个字符则用作文件名,这样我们就已经得到了一个Git下的数据对象
Git对象(objects)简介_第2张图片
我们很好奇这个文件到底保存了什么东西,我们使用vim打开发现数据是被压缩的
在这里插入图片描述
我们可以通过cat-file命令来查看这个文件中的数据,为cat-file指定-p选项可以指示该命令自动判断内容的类型,并未我们显示格式友好的内容
在这里插入图片描述
显示的内容与加入暂存区之间文件中的内容是一样的,所以其实这个散列值路径文件中保存的只是源文件的一种压缩形式。
文件是这样加入到暂存区的,如果我用git add 将一个目录增加到暂存区会怎样:
Git对象(objects)简介_第3张图片
经过上面的过程我们将一个有一个文件的目录加入到了暂存区,我们查看objects后发现仅仅只是多了一个新文件
Git对象(objects)简介_第4张图片
使用cat-file 命令观察发现 该文件中没有任何关于目录test的信息,Git仅仅将里面的文件new.txt转化为Git中的一个数据对象。要解决Git如何存储目录信息这个问题我们就得了解树对象。
在这里插入图片描述

树对象(tree)

接下来我们探讨Git中的树对象, 它能解决文件名保存问题,也允许我们将多个文件组织到一起, Git以一种类似于UNIX系统的方式存储内容,但做了些许简化。所有内容均已树对象和数据对象的形式存储,其中树对象对应了UNIX中的目录项,数据对象大致对应了文件内容。现在我们通过git commit
命令来观察生成的树对象
我们将前面放在暂存区的文件进行commit
Git对象(objects)简介_第5张图片
最后两行显示,Git为每一个文件设置了文件模式100644,表明这两个文件都是普通文件, 其他两种种模式选择包括:100755,表示一个可执行文件;120000,表示一个符号链接。上述三种模式即是Git数据对象的所有合法模式(当然对于目录项和子模块还有其他的一些模式)
提交之后我们再次观察objects目录内容:
Git对象(objects)简介_第6张图片
我们可以看到这里面多了三个文件(为了方便起见我使用整个散列值的前6个字符来表示该文件)
分别是012d1f、1360eb和ee0b2b,在接下来树对象的讨论中我们主要分析012d1f和ee0b2b,而1360eb则属于提交对象的内容
我们使用cat-file -p读取ee0b2b文件, 这个文件中包含了一条树对象记录和一个数据对象记录,其中每条记录都指向一个数据对象或者是子树对象的SHA-1指针以及相应的模式、类型、文件名

第一条记录表示该树对象指向一个126551的数据对象,树对象的文件名为test.txt,模式为100644
第二条记录表示该树对象指向一个012d1f的树对象,树对象的名字为test,模式为040000(表示该文件名为一个目录)
在这里插入图片描述
然后我们读取 012d1f 树对象
在这里插入图片描述
该树对象包含一个指向698e7f的数据对象的指针,文件名为new.txt,模式为100644。
根据上面的分析我们弄清楚了目录问题:Git会将一个目录存储为一个树对象进行保存, 这个树对象中会有每个子目录的树对象条目和每个文件的数据对象条目,这样就可以根据一种树结构来保存整个目录。

提交对象(commit)

现在我们来分析1360eb文件,用cat-file读取:
Git对象(objects)简介_第7张图片
我们会发现这个提交对象中包含一个树对象条目,代表着当前项目快照,从这树对象开始我们就能找到所有所有提交的数据对象,从而形成Git中的一个版本
其他之外还有一些作者/提交者的信息,留空一行,最后是提交注释。

总结

我们经历了完整的一次git提交过程,现在我们来思考一下git提供的这三种对象带来的高效率,首先git会对所有的文件内容进行压缩,这使得即使仓库中存储了非常多的内容,而.git也不会很大,然后就是速度,考虑这样的情况,当我们修改了一个文件的时候,git会去计算这个文件的SHA-1散列值,如果该散列值所得到的路径已经存在,那就说明,这个文件并没有被真正修改(或许是改了然后又改了回来。。。。),这不会带来任何字符串匹配问题,只是一个路径搜索过程,如果我们真正的修改了一个文件,那么git会计算这个文件的散列值,然后将这个文件压缩存储在objects中,这样我们需要创建一个相应的树结构来对原来的提交进行更改,这其实并不是一个困难的过程,我们只要为每一次提交都创建一个顶层树对象来表示这个提交快照。git可能会对比前一个提交的顶层树对象,然后将没有改变的树对象或数据对象直接复制到新创建的这个顶层树对象中,将改变的树对象像这样递归的进行。所以说决定你仓库大小的并不是完全在于每个文件的大小,而是你修改提交的次数,修改的次数越多,产生的树对象和数据对象也就越多。

你可能感兴趣的:(Git)