1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 这才是真正的 Git:Git 内部原理揭秘!

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

时间:2018-11-14 19:19:04

相关推荐

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

本文以一个具体例子结合动图介绍了 Git 的内部原理,包括 Git 是什么储存我们的代码和变更历史的、更改一个文件时,Git 内部是怎么变化的、Git 这样实现的有什么好处等等。视频链接:/video/av77252063

PPT 链接:/slide/git-under-the-hood

前言

近几年技术发展十分迅猛,让部分同学养成了一种学习知识停留在表面,只会调用一些指令的习惯。我们时常有一种 “我会用这个技术、这个框架” 的错觉,等到真正遇到问题,才发现事情没有那么简单。而 Git 也是一个大部分人都知道如何去使用它,知道有哪些命令,却只有少部分人知道具体原理的东西。了解一些底层的东西,可以更好的帮你理清思路,知道你真正在操作什么,不会迷失在 Git 大量的指令和参数上面。

Git 是怎么储存信息的

这里会用一个简单的例子让大家直观感受一下 git 是怎么储存信息的。首先我们先创建两个文件$gitinit

$echo"111">a.txt

$echo"222">b.txt

$gitadd*.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 储存的具体内容。$gitcat-file-t58c9

blob

$gitcat-file-p58c9

111

可以发现这个 object 是一个 blob 类型的节点,他的内容是 111,也就是说这个 object 储存着 a.txt 文件的内容。这里我们遇到第一种 Git object,blob 类型,它只储存的是一个文件的内容,不包括文件名等其他信息。然后将这些信息经过 SHA1 哈希算法得到对应的哈希值

58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c,作为这个 object 在 Git 仓库中的唯一身份证。也就是说,我们此时的 Git 仓库是这样子的:我们继续探索,我们创建一个 commit。$gitcommit-am"[+]init"

$tree.git/objects

.git/objects

├──0c

│└──96bfc59d0f02317d002ebbf8318f46c7e47ab2

├──4c

│└──aaa1a9ae0b274fba9e3675f9ef071616e5b209

...

我们会发现当我们 commit 完成之后,Git 仓库里面多出来两个 object。同样使用cat-file命令,我们看看它们分别是什么类型以及具体的内容是什么。$gitcat-file-t4caaa1

tree

$gitcat-file-p4caaa1

100644blob58c9bdf9d017fcd178dc8c0...a.txt

100644blobc06efd24ec5e783bee7...b.txt

这里我们遇到了第二种 Git object 类型 ——tree,它将当前的目录结构打了一个快照。从它储存的内容来看可以发现它储存了一个目录结构(类似于文件夹),以及每一个文件(或者子文件夹)的权限、类型、对应的身份证(SHA1 值)、以及文件名。此时的 Git 仓库是这样的:

$gitcat-file-t0c96bf

commit

$gitcat-file-p0c96bf

tree4caaa1a9ae0b274fba9e3675f9ef071616e5b209

authorlzane李泽帆1573302343+0800

committerlzane李泽帆1573302343+0800

[+]init

接着我们发现了第三种 Git object 类型 ——commit,它储存的是一个提交的信息,包括对应目录结构的快照 tree 的哈希值,上一个提交的哈希值(这里由于是第一个提交,所以没有父节点。在一个 merge 提交中还会出现多个父节点),提交的作者以及提交的具体时间,最后是该提交的信息。此时我们去看 Git 仓库是这样的:

到这里我们就知道 Git 是怎么储存一个提交的信息的了,那有同学就会问,我们平常接触的分支信息储存在哪里呢?$cat.git/HEAD

ref:refs/heads/master

$cat.git/refs/heads/master

0c96bfc59d0f02317d002ebbf8318f46c7e47ab2

在 Git 仓库里面,HEAD、分支、普通的 Tag 可以简单的理解成是一个指针,指向对应 commit 的 SHA1 值。

其实还有第四种 Git object,类型是 tag,在添加含附注的 tag(git tag -a)的时候会新建,这里不详细介绍,有兴趣的朋友按照上文中的方法可以深入探究。至此我们知道了 Git 是什么储存一个文件的内容、目录结构、commit 信息和分支的。其本质上是一个 key-value 的数据库加上默克尔树形成的有向无环图(DAG)。这里可以蹭一下区块链的热度,区块链的数据结构也使用了默克尔树。Git 的三个分区

接下来我们来看一下 Git 的三个分区(工作目录、Index 索引区域、Git 仓库),以及 Git 变更记录是怎么形成的。了解这三个分区和 Git 链的内部原理之后可以对 Git 的众多指令有一个 “可视化” 的理解,不会再经常搞混。接着上面的例子,目前的仓库状态如下:

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

工作目录 ( working directory ):操作系统上的文件,所有代码开发编辑都在这上面完成。

索引( index or staging area ):可以理解为一个暂存区域,这里面的代码会在下一次 commit 被提交到 Git 仓库。

Git 仓库( git repository ):由 Git object 记录着每一次提交的快照,以及链式结构记录的提交变更历史。

我们来看一下更新一个文件的内容这个过程会发生什么事。

运行echo "333" > a.txt将 a.txt 的内容从 111 修改成 333,此时如上图可以看到,此时索引区域和 git 仓库没有任何变化。

运行git add a.txt将 a.txt 加入到索引区域,此时如上图所示,git 在仓库里面新建了一个 blob object,储存了新的文件内容。并且更新了索引将 a.txt 指向了新建的 blob object。

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

Git 首先根据当前的索引生产一个 tree object,充当新提交的一个快照。

创建一个新的 commit object,将这次 commit 的信息储存起来,并且 parent 指向上一个 commit,组成一条链记录变更历史。

将 master 分支的指针移到新的 commit 结点。

至此我们知道了 Git 的三个分区分别是什么以及他们的作用,以及历史链是怎么被建立起来的。基本上 Git 的大部分指令就是在操作这三个分区以及这条链。可以尝试的思考一下 git 的各种命令,试一下你能不能够在上图将它们“可视化”出来,这个很重要,建议尝试一下。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。