git对象库

来源:互联网 发布:捷安特xtc750车架数据 编辑:程序博客网 时间:2024/05/21 15:40

本篇博客我们来讨论一下git的对象库。

准备工作

开始之前呢,我们通过git init命令初始化一个对象库。然后,新增一个文件a.txt,并写入一个一句话“hello,git”。

git add a.txt
git commit -m 'add a.txt'

接下来,在根目录新建一个文件夹叫[folder1],在folder1下新建一个文件b.txt,并写入一句话”hello,b.txt”。然后,再修改a.txt,加入一句话”hello,a.txt”。

通过一下命令将a.txt和b.txt加入暂存区并提交

git add a.txt folder1/b.txt
git commit -m 'add b.txt and modify a.txt'

git cat-file

下面就开始今天的讨论了。我们先思考一下这个问题:既然git作为一个版本管理工具,可以记录下我们每次提交的文件内容,并且允许我们回到任意一次提交来看看当时每个文件的样子,那么,git是如何存储每次提交的文件呢?

通过git log --pretty=raw查看一下当前的log,会得到一下输出

$ git log --pretty=rawcommit 31a9707cfa227a20ff62c87701af054435653633tree 60b5b8c3452dbc636f1eb8bcbed82aab9ee619cbparent 1f16e7a88f22daba43220093f0c3f25bdb73299dauthor zdk <zdk@menhoo.com> 1497516908 +0800committer zdk <zdk@menhoo.com> 1497516908 +0800    add b.txt and modify a.txtcommit 1f16e7a88f22daba43220093f0c3f25bdb73299dtree f8201282dc9d2215f29421ea38b9702082b218a8author zdk <zdk@menhoo.com> 1497516613 +0800committer zdk <zdk@menhoo.com> 1497516613 +0800    add a.txt

这些40位的哈希值,到底是什么东西呢?git提供了cat-file命令来查看git对象,参数-t会输出git对象的类型,-p参数会输出git对象的内容。
下面先看一下第二次提交的tree这个哈希值对应的内容

$ git cat-file -t 60b5b8c3452dbc636f1eb8bcbed82aab9ee619cbtree
$ git cat-file -p 60b5b8c3452dbc636f1eb8bcbed82aab9ee619cb100644 blob 97c3279503234b905d2f51b0dc148bcc064df6d8    a.txt040000 tree 67d684c6a7cfdf08beaf30411af3ae1144b0713d    folder1

git告诉我们,60b5b8c3452dbc636f1eb8bcbed82aab9ee619cb这个id的类型是tree,内容包含了两个文件,以及这两个文件的id。
我们接着来再看一下a.txt中的内容

$ git cat-file -p 97c3279503234b905d2f51b0dc148bcc064df6d8hello,githello,a.txt

哦?一不小心看到了这次提交的内容。
由于这两次提交时我都修改了a.txt,那么,我第一次提交时的a.txt的内容应该也可以看到。
通过log可以看出,第一次提交时的文件树的id是f8201282dc9d2215f29421ea38b9702082b218a8,那我们就来扒一扒这个id下的内容

$ git cat-file -p f8201282dc9d2215f29421ea38b9702082b218a8100644 blob f28ffa36cdf69904e516babfdb3005e108dddfb7    a.txt

找到了第一次提交时的文件树中的a.txt的id

$ git cat-file -p f28ffa36cdf69904e516babfdb3005e108dddfb7hello,git

这就找到了第一次提交时a.txt的内容。

tree的概念

tree,或者叫文件树,有以下几个特点:

  • 每一次commit,都会生成一个全新的tree
  • 通过tree的id,可以以递归的方式追踪到下面的所有文件
  • 只存在于版本库。暂存区没有tree的概念,工作区也没有tree的概念,因为暂存区和工作区都只有一个当前版本,而git版本库则保存这每一个版本(或者叫每一个提交)时对应的文件树。

其实可以把tree理解为一个文件夹,这个文件夹中包含了其他的文件和子文件夹,然后子文件夹有可以包含其他文件和子子文件夹,只是,这里面提到的所有文件,都是一个引用。如果我们再修改一下a.txt并提交,git会再生成一个新的文件树并关联到这个提交,这个树中的a.txt将会指向一个新的id,而b.txt文件由于没有修改,所有id不变,也就是还指向原来的文件位置。这样做可以优化存储空间。

通过ls-tree命令来遍历tree

$ git ls-treeusage: git ls-tree [<options>] <tree-ish> [<path>...]    -d                    only show trees    -r                    recurse into subtrees    -t                    show trees when recursing    -z                    terminate entries with NUL byte    -l, --long            include object size    --name-only           list only filenames    --name-status         list only filenames    --full-name           use full path names    --full-tree           list entire tree; not just current directory (implies --full-name)    --abbrev[=<n>]        use <n> digits to display SHA-1s

输入git ls-tree命令,git会告诉我们这个命令的用法。先说这个<tree-ish>,它可以是一个提交id,也可以是一个树的id。当然,也可以是HEAD。指定了<tree-ish>后,git会列出那一次提交时对应的那个树的内容。默认情况下,是不递归遍历所有文件的。

$ git ls-tree HEAD100644 blob 97c3279503234b905d2f51b0dc148bcc064df6d8    a.txt040000 tree 67d684c6a7cfdf08beaf30411af3ae1144b0713d    folder1

通过-r参数,可以递归遍历文件树。

$ git ls-tree -r HEAD100644 blob 97c3279503234b905d2f51b0dc148bcc064df6d8    a.txt100644 blob cbcaaeab925e1aec25b56f69cc6af6f5108f8f5f    folder1/b.txt

还可以继续使用-t参数,来输出更为完整的信息,包括子树

$ git ls-tree -r -t HEAD100644 blob 97c3279503234b905d2f51b0dc148bcc064df6d8    a.txt040000 tree 67d684c6a7cfdf08beaf30411af3ae1144b0713d    folder1100644 blob cbcaaeab925e1aec25b56f69cc6af6f5108f8f5f    folder1/b.txt

通过ls-tree把你想看的某个文件树的内容都列出来了,再配合使用上面介绍的cat-file命令,你就可以看到每一次提交时的每一个文件的样子了。

对象库的存储

作为分布式的版本管理工具,git把所有文件都保存在了.git/objects文件夹下。这个文件夹中包含了很多已两个字符命名的子文件夹,到底是什么含义呢?

我们就以第二次提交时文件树中的a.txt文件的id97c3279503234b905d2f51b0dc148bcc064df6d8为例,这个是四十位的哈希值,前两位表示所在的文件夹,后面的是文件名,所以这个文件就被保存在.git/objects/97/c3279503234b905d2f51b0dc148bcc064df6d8

以上就是我对git对象库的理解,分享给你,如果你有收获,就把这篇博客分享给你的朋友吧。