这才是真正的Git——Git内部原理揭秘

2023-06-03 0 596

责任编辑以三个具体文本范例紧密结合Zopfli如是说了Git的外部基本原理,包括Git是是不是存储他们的标识符和更改发展史的、更动三个文档时,Git外部是是不是变动的、Git这样同时实现的有甚么益处之类。

通过范例说明确切上面这张Zopfli,让大家如是说Git的外部基本原理。假如你已经能看懂这那哥了,上面的文本可能对你而言会较为此基础。
这才是真正的Git——Git内部原理揭秘

序言

近些年控制技术发展极为极为迅速,让部份老师教养了一类自学科学知识逗留在表层,只会初始化许多指示的生活习惯。他们经常有一类“就要用那个控制技术、那个架构”的幻觉,要到或者说碰到问题,才辨认出事没有所以单纯。

而Git也是三个绝大部份人都晓得怎样去采用它,晓得有甚么样指示,却只有一小部份人晓得具体文本基本原理的小东西。如是说许多下层的小东西,可以更快的帮你理清路子,晓得你或者说在操作方式甚么,不能沉沦在Git大批的指示和模块上面。

Git是是不是存储重要信息的

这儿会用三个单纯的范例让我们简单领略git是是不是存储重要信息的。

具体而言他们先建立三个文档

$ git init $ echo 111 > a.txt $ echo 222 > b.txt $ git add *.txt

Git会将整座资料库存储在.git/产品目录下,假如你这时去查阅.git/objects产品目录,你会辨认出库房里头多了三个object。

$ tree .git/objects .git/objects ├── 58│ └── c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c ├── c2 │ └──00906efd24ec5e783bee7f23b5d7c941b0c12c ├── info └── pack

好奇的他们来看一下里头存的是甚么小东西

$cat .git/objects/58/c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c xKOR0a044K%

是不是是一串乱码?这是因为Git将重要信息压缩成二进制文档。但是不用担心,因为Git也提供了三个能帮助你探索它的api git cat-file [-t] [-p], -t可以查阅object的类型,-p可以查阅object存储的具体文本文本。

$ git cat-file -t58c9 blob $ git cat-file -p 58c9 111

可以辨认出那个object是三个blob类型的节点,它的文本是111,也就是说那个object存储着a.txt文档的文本。

这儿他们碰到第一类Git object,blob类型,它只存储的是三个文档的文本,不包括文档名等其他重要信息。然后将这些重要信息经过SHA1哈希算法得到对应的哈希值

58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c,作为那个object在Git库房中的唯一身份证。

也就是说,他们这时的Git库房是这样子的:

这才是真正的Git——Git内部原理揭秘

他们继续探索,他们建立三个commit。

$ git commit -am[+] init $ tree .git/objects .git/objects ├── 0c │ └── 96bfc59d0f02317d002ebbf8318f46c7e47ab2 ├──4c │ └── aaa1a9ae0b274fba9e3675f9ef071616e5b209 …

他们会辨认出当他们commit完成之后,Git库房里头多出来三个object。同样采用cat-file指示,他们看看它们分别是甚么类型以及具体文本的文本是甚么。

$ git cat-file -t 4caaa1 tree $ git cat-file -p 4caaa1 100644 blob 58c9bdf9d017fcd178dc8c0… a.txt 100644 blob c200906efd24ec5e783bee7… b.txt

这儿他们碰到了第二种Git object类型——tree,它将当前的产品目录结构打了三个快照。从它存储的文本来看可以辨认出它存储了三个产品目录结构(类似于文档夹),以及每三个文档(或者子文档夹)的权限、类型、对应的身份证(SHA1值)、以及文档名。

这时的Git库房是这样的:

这才是真正的Git——Git内部原理揭秘
$ git cat-file -t 0c96bf commit $ git cat-file -p 0c96bf tree 4caaa1a9ae0b274fba9e3675f9ef071616e5b209 author lzane 李泽帆 1573302343 +0800 committer lzane 李泽帆 1573302343 +0800 [+] init

接着他们辨认出了第三种Git object类型——commit,它存储的是三个提交的重要信息,包括对应产品目录结构的快照tree的哈希值,上三个提交的哈希值(这儿由于是第三个提交,所以没有父节点。在三个merge提交中还会出现多个父节点),提交的作者以及提交的具体文本时间,最后是该提交的重要信息。

这时他们去看Git库房是这样的:

这才是真正的Git——Git内部原理揭秘

到这儿他们就晓得Git是是不是存储三个提交的重要信息的了,那有老师就会问,他们平常接触的分支重要信息存储在哪里呢?

$ cat .git/HEADref:refs/heads/master $ cat .git/refs/heads/master 0c96bfc59d0f02317d002ebbf8318f46c7e47ab2

在Git库房里头,HEAD、分支、普通的Tag可以单纯的理解成是三个指针,指向对应commit的SHA1值。

这才是真正的Git——Git内部原理揭秘

其实还有第四种Git object,类型是tag,在添加含附注的tag(git tag -a)的时候会新建,这儿不详细如是说,有兴趣的朋友按照上文中的方法可以深入探究。

至此他们晓得了Git是是不是存储三个文档的文本、产品目录结构、commit重要信息和分支的。其本质上是三个key-value的资料库加上默克尔树形成的有向无环图(DAG)。这儿可以蹭一下区块链的热度,区块链的数据结构也采用了默克尔树。

Git的三个分区

接下来他们来看一下Git的三个分区(工作产品目录、Index 索引区域、Git库房),以及Git更改记录是是不是形成的。如是说这三个分区和Git链的外部基本原理之后可以对Git的众多指示有三个“可视化”的理解,不能再经常搞混。

接着上面的范例,目前的库房状态如下:

这才是真正的Git——Git内部原理揭秘

这儿有三个区域,他们所存储的重要信息分别是:

工作产品目录 ( working directory ):操作方式系统上的文档,所有标识符开发编辑都在这上面完成。索引( index or staging area ):可以理解为一个暂存区域,这儿头的标识符会在下一次commit被提交到Git库房。Git库房( git repository ):由Git object记录着每一次提交的快照,以及链式结构记录的提交更改发展史。

他们来看一下更新三个文档的文本那个过程会发生甚么事。

这才是真正的Git——Git内部原理揭秘

运行echo “333” > a.txt将a.txt的文本从111修改成333,这时如上图可以看到,这时索引区域和git库房没有任何变动。

这才是真正的Git——Git内部原理揭秘

运行git add a.txt将a.txt加入到索引区域,这时如上图所示,git在库房里头新建了三个blob object,存储了新的文档文本。并且更新了索引将a.txt指向了新建的blob object。

这才是真正的Git——Git内部原理揭秘

运行git commit -m update提交这次修改。如上图所示

Git具体而言根据当前的索引生产三个tree object,充当新提交的三个快照。建立三个新的commit object,将这次commit的重要信息储存起来,并且parent指向上三个commit,组成一条链记录更改发展史。将master分支的指针移到新的commit结点。

至此他们晓得了Git的三个分区分别是甚么以及他们的作用,以及发展史链是是不是被建立起来的。基本上Git的绝大部份指示就是在操作方式这三个分区以及这条链。可以尝试的思考一下git的各种指示,试一下你能不能在上图将它们“可视化”出来,那个很重要,建议尝试一下。

假如不能很好的将日常采用的指示“可视化”出来,推荐阅读 图解Git

许多有趣的问题

有兴趣的老师可以继续阅读,这部份不是文章的主要文本

问题1:为甚么要把文档的权限和文档名存储在tree object里头而不是blob object呢?

想象一下修改三个文档的命名。

假如将文档名保存在blob里头,所以Git只能多复制一份原始文本形成三个新的blob object。而Git的同时实现方法只需要建立三个新的tree object将对应的文档名更动成新的即可,原本的blob object可以复用,节约了空间。

问题2:每次commit,Git存储的是全新的文档快照还是存储文档的更改部份?

由上面的范例他们可以看到,Git存储的是全新的文档快照,而不是文档的更改记录。也就是说,就算你只是在文档中添加一行,Git也会新建三个全新的blob object。那这样子是不是很浪费空间呢?

这其实是Git在空间和时间上的三个取舍,思考一下你要checkout三个commit,或对比三个commit之间的差异。假如Git存储的是问卷的更改部份,所以为了拿到三个commit的文本,Git都只能从第三个commit开始,然后一直计算更改,直到目标commit,这会花费很长时间。而相反,Git采用的存储全新文档快照的方法能使那个操作方式变得很快,直接从快照里头拿取文本就行了。

当然,在涉及网络传输或者Git库房真的体积很大的时候,Git会有垃圾回收机制gc,不仅会清除无用的object,还会把已有的相似object打包压缩。

问题3:Git是不是保证发展史记录不可篡改?

通过SHA1哈希算法和哈系树来保证。假设你偷偷修改了发展史更改记录上三个文档的文本,所以那个问卷的blob object的SHA1哈希值就变了,与之相关的tree object的SHA1也需要改变,commit的SHA1也要变,那个commit之后的所有commit SHA1值也要跟着改变。又由于Git是分布式系统,即所有人都有一份完整发展史的Git库房,所以所有人都能很轻松的辨认出存在问题。

希望我们读完有所收获,下一篇文章会写许多我日常工作中觉得较为实用的Git技巧、经常被问到的问题、以及发生许多事故时的处理方法。

作者:lzaneli,腾讯 TEG 前端开发工程师

参考

Scott Chacon, Ben Straub – Pro Git-Apress (2014) 免费,有兴趣继续深入的老师推荐阅读这本书Jon Loeliger, Matthew McCullough – Version Control with Git, 2nd Edition – OReilly Media (2012) 作为上面那本书的补充

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务