Git是一个开源的分布式版本控制系统,是目前主流的版本控制系统,很多软件项目都会用它做源代码管理。Git的常用操作想必很多人都会,但是可能了解Git内部原理的人并不多。了解一些底层的东西,可以更好的帮你理清思路,遇到问题的时候也可以更好的去解决。

准备工作

在介绍Git如何存储数据之前,我们先做一些准备工作。
首先新建一个目录git-test,然后在这个目录右键,打开Git Bash,

然后,在Git Bash,分别执行以下命令,

$ git init  $ echo 'a' > a.txt$ echo 'b' > b.txt$ git add .

至此,准备工作完成。我们可以看到,git-test目录下现在有一个.git目录,以及两个txt文件(a.txt、b.txt)。

Git如何存储数据

.git目录是git init后在当前目录生成的一个管理git仓库的目录,这里包含所有git操作所需要的东西。其中objects目录下存放所有的git对象。经过上面的操作后,objects目录是这样的,

其中info和pack目录是执行git init以后就已经有的,而78和61目录分别对应着a.txt和b.txt文件,这两个目录是创建了a.txt和b.txt,并执行git add .命令后才生成的。

78目录下有一个文件,查看一下这个文件的内容,执行以下命令,

$ cat .git/objects/78/981922613b2afb6025042ff6bd878ac1994e85

我们看到文件的内容是一串乱码,这是因为Git将信息压缩成二进制。Git提供了一个能够帮助探索objects的命令:git cat-file [-t] [-p],-t可以查看object的类型,-p可以查看object存储的具体内容。分别执行以下命令,

$ git cat-file -t 7898  $ git cat-file -p 7898

blob类型的object

7898就是目录名加上文件名的前两位。可以看到,这个object是一个blob类型的object,而这个object存储的内容,就是我们写入到a.txt的文本。因此,上面的乱码其实就是a.txt的内容,也就是说,这个object存储着a.txt文件的内容。

blob类型的object存储的是一个文件的内容。然后,Git根据这个文件的内容经过SHA1哈希算法得到对应的哈希值(981922613b2afb6025042ff6bd878ac1994e85),作为这个object在Git仓库中的唯一id。现在的Git仓库是这样子的,如图:

tree类型的object

接着执行下一个命令,

$ git commit -m '第一次提交'

执行git commit命令后,objects目录下又多出了两个object,如图:

首先,用git cat-file -t命令查看f4目录下object的类型,如图:

可以看到,这个object的类型是tree,利用git cat-file -p命令查看这个object的内容,如图:

可以看到,tree类型的object存储了一个目录结构的快照,从左到右分别显示了每个文件的权限、类型、object的id(SHA1值)、以及文件名。现在的Git仓库是这样子的,如图:

commit类型的object

用同样的方法,查看3c目录下的文件,如图:

这是一个commit类型的object,而这个object存储了一个tree类型的object的id,以及提交的一些信息。现在的Git仓库是这样子的,如图:

分支

实际做项目都会有很多分支,Git的分支信息就存储在/.git/refs/heads目录下,如图:

因为现在只有一个master分支,所以只有一个master文件。直接打开master这个文件,可以看到这个文件存储了3c0acd6df4df30074678a2b97967a82efd9c8acf这样一串字符串,这正是上面的commit类型object的id。现在的Git仓库是这样子的,如图:

HEAD

在/.git/HEAD这个文件下,记录内容如下:

ref: refs/heads/master

这个内容告诉Git当前修改的内容是基于哪个分支上的,我们可以理解为这是一个指针。现在的Git仓库是这样子的,如图:

至此,一个完整的Git存储结构就出来了。

Git的三个分区

Git有3个分区,分别是工作区、暂存区和版本库。

工作区: 就是项目所在目录(除去.git目录),所有代码开发编辑都在这上面完成。

暂存区: 英文叫 stage 或 index。一般存放在.git目录下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。

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

有了3个分区,整个结构如下图:

Git的内部运作

现在,我们尝试修改a.txt文件,如图:

这时候,除了工作目录下的a.txt文件内容有变化,暂存区和Git仓库都是没有变化的。接着,执行git add a.txt命令,这时/.git/objects目录下又多了一个对象,如图:

这个新增的object也是blob类型,对应着新修改的a.txt文件。这时,整个结构如下图:

最后,我们执行git commit -m '修改a.txt'命令,不出意外,/.git/objects目录会生成两个object,根据上面的介绍,这两个object分别是tree和commit类型,如图:

这时,整个结构如下图:

从上图可以看到,master分支已经指向新的commit object,并且新的commit object记录着它的parent object,也就是旧的那个commit object,这使得我们可以查看Git的提交历史。

本文来自博客园,作者:Yi00,转载请注明原文链接:https://www.cnblogs.com/ayic/p/16678252.html

聊聊技术,聊聊人生。欢迎关注我的公众号!^_^