数据库,顾名思义,即存储数据的仓库。
我们的Git是一个关系型对象数据库,怎么理解呢,它主要管理对象及对象之间的关系。而对象间的关系,组成了一个图结构。
图结构
我们先来看一下Git里对象的关系是怎样的。如下图。
这是我用git-show-objects工具生成的某个Git仓库的对象关系。
矩形框(包括折角矩形框)即我们要研究的对象,而椭圆形是我们的指针。下面我们来分别看一下Git里的对象和指针。
对象
Git中的对象主要分两大类:不可变对象
和可变对象
。
不可变意味着,如果对象的某个域要发生变化,我们会创建一个新的对象,而不是修改原来的对象。
这就有几个很有意思的事情:
- 频繁创建对象会占用大量磁盘,但它会带来更好的灵活性和可跟踪性
- 不可变的对象更安全,可以共享使用,这在一定程度上又节省了磁盘
对于可变对象,由于其域可变,故我们是决不可能用它来保存历史的,但是保存临时的或一次性的数据则非常理想。
不可变对象
Git的图结构主要是由不可变对象组成的,Blob、Tree和Commit是我们接触最多的三类不可变对象。
Blob
Blob对象用来保存我们项目文件的内容,对应上图中的折角矩形。
有一点比较关键,Blob对象只保存文件内容。这就意味着,如果我们有两个不同的文件,而其内容竟然一致,好了,我们只会生成一个Blob对象。
Tree
Tree对象用来保存我们项目目录的内容,对应上图中的蓝文字矩形。
跟Blob对象一样,如果我们有hello1/dir和hello2/dir两个目录,而两个目录的内容竟然递归相同,那我们的两个dir只需要一个Tree对象就可以了。
Tree对象内保存着一个列表,它的每一项,都是一个文件名+Blob对象
或目录名+另一个Tree对象
(子目录)。
Commit
Commit对象用来保存我们的提交动作,对应上图中的橘红色矩形。
Commit对象里保存的信息比Blob和Tree都要多,因为它已经是我们经常访问到的对象了。
Commit对象里有我们想知道的作者
、提交时间
信息,它还保存着此次提交的父提交。当然,父提交有可能是两个,三个或百八十个,只要你喜欢。
可变对象
Git中的可变对象最典型的就是Index对象,它是一个全局对象,或称其为单例对象。
Index对象维护着当前Git对应的文件及状态,是我们操作Git的入口。它维护一个列表,每一项包含了文件的mode
、对应的Blob
、文件对应路径
和一些Git自己使用的标记。
指针
上面提到保存着父提交
、保存着对应的Blob
,那这些是以何种类型保存到对象的域的呢?
答案是哈希值
。
我们知道,在计算机中,一个对象是有一个内存地址对应的,而Git的对象,用的是对象的哈希值。即,Git计算某个对象的哈希值,使用此值作为文件的路径,并将对象的内容序列化后存储到该路径。
实际上,Git将其要维护的对象统一存放在项目根目录的.git/objects/
位置,并以对象哈希值的前两个字符作目录名,剩余字符作文件名,生成对象目录。
我们之前说,Index对象是我们操作Git的入口。指针,即是我们访问对象的入口。
Git中的指针直接或间接指向Commit对象,分为常量指针和变量指针。它们对应上图中的椭圆形标记。
变量指针
随着我们对Git的操作,变量指针的指向有可能发生变化。
Head、Remote
变量指针就像我们的书签,标记着我们当前看到了哪一页。
最典型的变量指针是Head(Remote),也即是我们常说的分支。如上图中的heads/master
,它对应我们本地的master分支。
HEAD
HEAD是一个特殊的指针,跟Index相似,它有且只有一个,顺着它指的方向,我们总可以找到一个Commit。
这里要注意,HEAD正常情况下是指向Head的,但上图中它直接指向了某个Commit,这就是Git里面的游离态(detached)。
常量指针
常量指针是一旦创建,就很少发生变化的指针。注意,是很少,并非没有。
Tag
Tag是常量指针的典型,它主要充当我们的标记笔,考试画重点必备。
例如,我们可以用它来标记一个稳定的发行版本对应的Commit,以供伙伴们检出或下载。
常用操作
git init
初始化仓库,主要是初始化.git/目录,创建Index对象、HEAD指针,以准备接下来的工作。
git add
创建Blob对象,并将对应文件添加到Index对象。
git commit
递归创建Tree对象,创建Commit对象,并保存根目录对应的Tree对象。更新HEAD指向的指针
的指向。
问题
此时,一个简单的提交就完成了。当然,这连一个开始都够不上。
如果这三个命令你都理解了,不仿实操一下以下几个问题:
- fast-forware是如何操作的?
- 上面提到过detached,它有什么问题,应该如何避免?
- 根据Git的实现原理,如何设计系统可以更好的利用磁盘空间?
其实第三个问题恰是我们设计简洁系统的一个原则。
参考
根本停不下来?可以看一下maryrosecook写的《git from the inside out》,如果看英文费劲也可看一下我的粗陋翻译《彻底理解git》。
自己创建一个测试仓库,使用git-show-objects工具,边做边观察一下发生了什么。