简介:本教程旨在让你可以初步了解git内部是如何运作的,仅仅灌鸭模式让您能快速的了解git内部。如要发掘更多的细节、更多好玩的东西,建议多看git教程。推荐pro git这本书,此书可以在git官网下载。
1. 基本入门
1.1 git目录构成

HEAD:当前被检出的分支指向一个refs hash code.
branches: 类似refs功能,新版已经不再使用了,废弃此目录。
config:包含当前工作目录的配置信息,比如我们的git账号、密码、用户信息之类的。
description: 供gitweb使用
hooks:放置 服务端或者客户端的钩子脚本
info:包含非记录在.gitignore内的排除文件
objects:git的数据仓库
refs:记录每个提交所指向对应版本文件的hash
index:暂存区内容
1.2 hash code如何生成
git的hash code,是通过将当前文件内容与头部信息在一起做SHA-1校验运算得出来的校验和。
1.2.1 什么是头部信息?
git 首先取得文件类型,然后添加一个空格,随后是数据内容长度,最后一个是空字节(null byte)。
这就是一个git头部信息。

1.2.2 什么是当前文件内容?
就是当前修改的文件内容,不同的内容有不同的信息。git有三种存储类型blob,tree,commit。比如blob就是整体的文件内容。tree则是包含提交的文件列表。commit就是提交信息。
2. 小试牛刀,看看git保存我们的提交的
我们在.git目录中查看所有文件

可以看到我们是没有index目录的。现在我们回到上一层,创建一个test.md文件,然后写点内容进去。

接着,我们运行git add .将文件添加至暂存区。接着,我们来查看.git目录的文件与文件夹。

可以看到,git创建了index文件。
运行以下命令,来看看index中都包含了什么内容:

一共运行了两个命令,第一个 git ls-files --stage是查看暂存区的文件内容,第二个git cat-file -t e27963是查看,符合这个hash值的文件类型。知道类型之后,可以使用这个命令来查看内容。

可以看到,这个hash值对应的文件内容就是我输入的内容。
现在我们commit保存修改。
这个时候,objects文件夹中就出现了hash开头的文件夹,与对应的内容。

通过git cat-file可以查看对应hash值中的内容。

要注意一个点,就是 objects下的文件夹,后续都是hash code.比如 be/49bf,那么他的hash code就是be49bf(“SHA-1 值的前两个字符作为子目录名称,后 38 个字符则作为子目录内文件的名称”)。

可以看到,这些hash结构的内容,对应的,分别是当前提交内容包含哪些文件、文件内容、commit信息。
2.1 git是如何存储文件的?
我对test.md文件末尾追加了一些内容。现在我们来看看,git存储中,test.md文件是多少大小。

通过上图发现,最新提交的文件在git存储里面是35字节。那么,之前的版本是多少呢?我们来看看。

是17个字节。由此,我们可以得出一个结论,所有的文件,每次修改提交在git中都是一个全新的文件。
那么由此带来的一个问题就是,按照这样来说的话,我们的项目目录不是会很大?
git开发者也想到了这个问题,所以,我们在git push的时候是否会发现,有类似下面的信息:

这个是git帮我们做了压缩。对应的git命令是git gc。我们来运行一下这个命令看看。

可以看到,运行命令之后,objects下的内容都不见了。随之而来则是生成了idx,pack文件。
新创建的**包文件和索引文件**。 包文件包含了刚才从文件系统中移除的所有对象的内容。 索引文件包含了包文件的偏移信息,我们通过索引文件就可以快速定位任意一个指定对象。
使用git verify-pack -v来查看当前索引文件中包含了哪些内容

由于前几次的文件过小,git还是采用了完全快照模式。所以进行了多次修改,我尝试将这个文件变大之后,现在我们可以看到 e0ffd0c1这个提交与49bfbc这个提交。git采用了差异保存模式保存文件。
git之所以让原始版本使用差异模式保存,是为了能快速访问文件的最新版本。
3. git存储类型
3.1 tree
树对象,是保存文件内容变更的简化模型。通过下图可以发现,一个文件夹对应一棵树,如果包含了另外一个文件将,那么,将通过tree hash code 来访问。简单的说,就是tree仅有一层真实文件目录,文件夹都是通过引用来访问。
tree由不同版本的文件组成。他们看起来像这样的结构:

3.2 blob
blob对应的都是实体文件信息。hash指定blob的文件名,存储在objects中,属于最原子结构。
3.3 commit
每一个commit信息,包含了指向的某个tree节点,父commit节点,提交人,提交信息等。

