设为首页 加入收藏

TOP

Git 内部原理之 Git 对象(一)
2018-06-25 08:46:54 】 浏览:282
Tags:Git 内部 原理 对象

最近在读《Pro Git》这本书,其中有一章讲Git的内部原理,写得非常好,读完之后对于Git的理解会提升到一个新的层次。今后,我会写一系列的关于Git内部原理的文章,以帮助读者加深对Git的认识。内容主要参考《Pro Git》这本书,但不同的是,我会对内容进行重新组织,以使大家更容易理解。

这篇文章的主题的Git对象。

从根本上来讲,Git是一个内容寻址的文件系统,其次才是一个版本控制系统。记住这点,对于理解Git的内部原理及其重要。所谓“内容寻址的文件系统”,意思是根据文件内容的hash码来定位文件。这就意味着同样内容的文件,在这个文件系统中会指向同一个位置,不会重复存储。

Git对象包含三种:数据对象、树对象、提交对象。Git文件系统的设计思路与linux文件系统相似,即将文件的内容与文件的属性分开存储,文件内容以“装满字节的袋子”存储在文件系统中,文件名、所有者、权限等文件属性信息则另外开辟区域进行存储。在Git中,数据对象相当于文件内容,树对象相当于文件目录树,提交对象则是对文件系统的快照。

下面的章节,会分别对每种对象进行说明。开始说明之前,先初始化一个Git文件系统:

$ mkdir git-test
$ cd git-test
$ git init

接下来的操作都会在git-test这个目录中进行。

数据对象

数据对象是文件的内容,不包括文件名、权限等信息。Git会根据文件内容计算出一个hash值,以hash值作为文件索引存储在Git文件系统中。由于相同的文件内容的hash值是一样的,因此Git将同样内容的文件只会存储一次。git hash-object可以用来计算文件内容的hash值,并将生成的数据对象存储到Git文件系统中:

$ echo 'version 1' | git hash-object -w --stdin
83baae61804e65cc73a7201a7252750c76066a30
$ echo 'version 2' | git hash-object -w --stdin
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
$ echo 'new file' | git hash-object -w --stdin
fa49b077972391ad58037050f2a75f74e3671e92

上面示例中,-w表示将数据对象写入到Git文件系统中,如果不加这个选项,那么只计算文件的hash值而不写入;--stdin表示从标准输入中获取文件内容,当然也可以指定一个文件路径代替此选项。

上面讲数据对象写入到Git文件系统中,那如何读取数据对象呢?git cat-file可以用来实现所有Git对象的读取,包括数据对象、树对象、提交对象的查看:

$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30
version 1
$ git cat-file -t 83baae61804e65cc73a7201a7252750c76066a30
blob

上面示例中,-p表示查看Git对象的内容,-t表示查看Git对象的类型。

通过这一节,我们能够对Git文件系统中的数据对象进行读写。但是,我们需要记住每一个数据对象的hash值,才能访问到Git文件系统中的任意数据对象,这显然是不现实的。数据对象只是解决了文件内容存储的问题,而文件名的存储则需要通过下一节的树对象来解决。

树对象

树对象是文件目录树,记录了文件获取目录的名称、类型、模式信息。使用git update-index可以为数据对象指定名称和模式,然后使用git write-tree将树对象写入到Git文件系统中:

$ git update-index --add --cacheinfo 100644 83baae61804e65cc73a7201a7252750c76066a30 test.txt
$ git write-tree
d8329fc1cc938780ffdd9f94e0d364e0ea74f579

--add表示新增文件名,如果第一次添加某一文件名,必须使用此选项;--cacheinfo <mode> <object> <path>是要添加的数据对象的模式、hash值和路径,<path>意味着为数据对象不仅可以指定单纯的文件名,也可以使用路径。另外要注意的是,使用git update-index添加完文件后,一定要使用git write-tree写入到Git文件系统中,否则只会存在于index区域。

树对象仍然可以使用git cat-file查看:

$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30  test.txt
$ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579
tree

上面表示这个树对象只有test.txt这个文件,接下来我们将version 2的数据对象指定为test.txt,并添加一个新文件new.txt

$ git update-index --cacheinfo 100644 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
$ git update-index --add --cacheinfo 100644 fa49b077972391ad58037050f2a75f74e3671e92 new.txt
$ git write-tree
0155eb4229851634a0f03eb265b69f5a2d56f341

查看树对象0155eb,可以发现这个树对象有两个文件了:

$ git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341
100644 blob fa49b077972391ad58037050f2a75f74e3671e92  new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a  test.txt

我们甚至可以使用git read-tree,将已添加的树对象读取出来,作为当前树的子树:

$ git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git write-tree
3c4e9cd789d88d8d89c1073707c3585e41b0e614

--prefix表示把子树对象放到哪个目录下。查看树对象,可以发现当前树对象有一个文件夹和两个文件:

$ git cat-file -p 3c4e9cd789d88d8d89c1073707c3585e41b0e614
040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579  bak
100644 blob fa49b077972391ad58037050f2a75f74e367
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇ERROR 3009 (HY000) : Column cou.. 下一篇Git 内部原理之 Git 对象存储

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目