Git快速入门(04)Git底层原理

从前面我们可以知道,git底层是以元数据的方式存储,我们来新建一个仓库,查看一下objects内容:

find .git/object/

可以看到 Git 对 objects 目录进行了初始化,并创建了 pack 和 info 子目录,但均为空。

下面新建并提交一个新文件,看一下元数据,在仓库根目录查看元数据文件的命令:

find .git/objects/ -type -f

我们可以看到,只提交了一个文件,但是元数据多了三个,这三个分别是什么呢?这三个分别是数据对象,提交对象和树对象。下面来详细说一下这三个对象。也就是git的底层原理。








Git数据对象

Git 本质上是一个内容寻址的文件系统,其核心部分是一个简单的键值对数据库(key-value data store),在不进行提交操作的情况下,你仍然可以向数据库中插入任意内容,它会返回一个用于取回该值的hash 键(key)。如果内容一样,那么返回的key也一样。我们来创建一个新仓库,创建一个新文件hello.txt,然后插入到键值数据库中,尝试插入多次,查看返回的key是否一样:

git hash-object -w hello.txt

可以看到多次插入返回的key是一样的。我们稍微改变一下内容,就会发现返回的键发生变化:

还可以基于键获取指定的内容:

git cat-file -p 3ea522c66db4133cf6135a2764e33905c841780f

键值只要保证唯一,输入前几位即可。

上面演示的就是git作为简单键值对数据库的基本操作。前面说过,git是基于元数据的存储方式,元数据存储在.git文件夹下面的objects文件夹中。那么我们来查询一下objects下面所有的文件,也就是git对象:

find .git/objects/ -type f

显示两个文件名就是我们刚才保存的文件两个版本的key。这和上面演示的提交不同,单纯的把数据插入到数据库只生成一个元数据。

这个时候再来看版本回退,从本质上来说,版本变化了2次,目前文件内容是第2次的修改,如果想回退到第一个版本,那么本质上只需要把第一个版本的元数据写回到当前文件中即可,先来看一下现在文件的内容:

显示的是第2个版本,也就是最新版本的内容,我们现在来恢复到第一个版本,第一个版本的key是  3ea522c66db4133cf6135a2764e33905c841780f,我们来吧第一个版本的内容写到文件中:

git cat-file -p 3ea522c66db4133cf6135a2764e33905c841780f > hello.txt

这样就实现了一个版本的回退。文件内容也恢复到了第一个版本的内容。我们在进行git的版本回退时,git底层存储实际上就是这么做的。我们存储的元数据并不是普通的文件,而是一个blob对象,来看下数据类型:

git cat-file -t 3ea522c66db4133cf6135a2764e33905c841780f

blob对象就是我们git的实际存储的文件的数据类型。然而,记住文件的每一个版本所对应的 key并不现实;另一个问题是,在这个(简单的版本控制)系统中,文件名并没有被保存——我们仅保存了文件的内容。 上述类型的对象我们称之为数据对象(blob object)。 利用 cat-file -t 命令,可以让 Git 告诉我们其内部存储的任何对象类型,只要给定该对象的key值。

看完了上面的底层操作,再来看一下git命令操作,新建一个文件test.txt,然后添加到暂存区:

再来看一下元数据:

添加到暂存的命令虽然没有返回key,但是从元数据数量上来看,明显多了一条数据,其实本质上添加到暂存区也要执行git hash-object -w命令。而且添加到暂存区也只是新增了存储对象,并没有提交对象和树对象,因此只多了一条。我们上面用数据库操作把hello.txt文件添加到了键值数据库中,现在再来用git add添加,看看数据会不会增多:

可以看到数据依然是3条,没有变化,数据的key也没有改变,这是因为我们上面已经添加了,同样的数据添加几次生成的key都是一样的。








Git树对象

在上面的数据对象中,提到了记住每个版本对应的key肯定是不现实的,文件名也没有被保存,接下来要探讨的对象类型是树对象(tree object),它能解决文件名保存的问题,也允许我们将多个文件组织到一起。 Git 以一种类似于 UNIX 文件系统的方式存储内容,但作了些许简化。 所有内容均以树对象和数据对象的形式存储,其中树对象对应了 UNIX 中的目录项,数据对象则大致上对应了 inodes 或文件内容。 一个树对象包含了一条或多条树对象记录(tree entry),每条记录含有一个指向数据对象或者子树对象的 key指针,以及相应的模式、类型、文件名信息。

树对像解决了文件名的问题,它的目的是将多个文件名组织在一起,其内包含多个文件名称与其对应的Key和其它树对像的引用,可以理解成操作系统当中的文件夹,一个文件夹包含多个文件和多个其它文件夹。

上图中,顶层的tree对象下面有两个blob对象,代表两个文件,第三个是个tree对象,代表子文件夹,下面还是一个blob对象,表示子文件夹下面的文件。

简单介绍完树对象,下面我们新建一个仓库,提交一些内容:

上面我们新建了一个仓库test,在仓库下新建了一个test.txt文件和一个hello文件夹,在hello文件夹下面新建了一个hello.txt文件,然后回到仓库根目录对仓库所有内容进行了整体的一个提交。当前对应的最新树对象是这样的:

git cat-file -p master^{tree}

master^{tree} 语法表示 master 分支上最新的提交所指向的树对象。 请注意,hello 子目录(所对应的那条树对象记录)并不是一个数据对象,而是一个指针,其指向的是另一个树对象:

git cat-file -p 62be0186dd46837d7efe02bdf2e3c3731a7ed2fe

可以看到树对象下面存储了文件的key,文件的名称和下级的树对象,是个经典的树形结构。大家可以想象我们平时开发的项目提交到git仓库,就是文件系统转化为树对象和数据对象的过程。

可以想象到,文件夹下面的文件更新后,文件夹树对象以上的所有树对象都会发生变化,但是下层的不会变,虽然会生成很多key,但是指向关系非常明确,有兴趣可以自行验证。







Git提交对象

现在假如有三个树对象,分别代表了我们想要跟踪的不同项目快照。然而问题依旧:若想重用这些快照,你必须记住所有三个key。 并且,你也完全不知道是谁保存了这些快照,在什么时刻保存的,以及为什么保存这些快照。 而以上这些,正是提交对象(commit object)能为你保存的基本信息。什么是提交对象呢?来看一下操作日志:

第一行commit后面跟着一个hash值,就是提交对象的key,每次提交都会对应一个提交对象。提交对象下面是什么呢?来看一下:

可以看到提交对象对应的就是一个树对象,有几次提交就会对应几个树对象,树对象很抽象并且不好记住,但是提交对象通过提交记录很容易就能找到。我们再来看一下这个树对象下面是什么:

可以看到这个树对象就是我们上面根据分支查询到的树对象:

这就是提交对象和树对象对应的一个关系。提交对象的格式很简单:它先指定一个顶层树对象,代表当前项目快照;然后是作者/提交者信息(依据你的 user.name 和 user.email 配置来设定,外加一个时间戳);留空一行,最后是提交注释。

我们来修改一下test.txt文件并提交,再看一下提交对象:

这时候产生了第二个提交对象。

可以看到第二次的提交对象中还包含了一个父提交对象,指向的是第一次的提交对象。通过这种链接可以明确提交对象的顺序。

每次我们运行 git add 和 git commit 命令时, Git 所做的实质工作——将被改写的文件保存为数据对象,更新暂存区,记录树对象,最后创建一个指明了顶层树对象和父提交的提交对象。 这三种主要的 Git 对象——数据对象、树对象、提交对象——最初均以单独文件的形式保存在 .git/objects 目录下。 简单看一个提交三次后,各种对象的关系图:

大家可以想象我们通过这种方式管理元数据,一旦进行分支切换,版本回退等等操作,可以快速找到对应的key指向的内容,然后再把内容恢复到现在的目录中,是何等的快速!




上面三种对象是git仓库真正的核心部分,其他的像分支,标签等等都是对其中一个提交对象的引用,可以在.git/refs 文件下面看到。我们来看下master分支的引用:

这个key就是我们仓库的提交对象:

所谓的分支标签就是这么一回事。





















最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容