Git对象存储和Index file

Git版本和存储中有两个重要概念,Git对象数据库和Index file,先来一波热身操作,为后续git操作提供基础数据。

$echo hi,gits >a.txt

$git add .

$git commit -m "first commit"

>[master (root-commit) fee2019] first commit
>1 file changed, 1 insertion(+)
>create mode 100644 a.txt

Git对象存储

git本地空间分为三种类型,work tree工作目录树/工作空间、index file和仓库。

工作目录树(work tree)

在没有执行git add操作时,文件停留在工作目录树/工作空间中。查看此空间的变更内容:

git diff

对比working<-->index file之间的差异。

index  file

执行git add文件的状态被扩大到index  file中。查看此空间的变量内容:

git diff --cached

对比index file<-->git repository之间的差异。

git仓库(git repository)

执行git commit操作后变更会被存入到git仓库中。

git diff HEAD

对比working<-->git repository之间的差异。

如果把这三种类型看成三个阶段,git提交过程为working>index file>git repository。文件的状态依次:working最新>index file稍旧>git repository最旧。

git status

git status命令会把working<-->index file、index file<-->git repository之间的差异分别列出。

git status

Git HEAD

HEAD所指向的是当前分支最近一次commit的信息。所有分支的HEAD都存放在.git/refs/heads目录下,示例为master。

当前分支的HEAD存放在 .git/HEAD中。commit指向了tree对象,tree记录了目录镜像/状态。tree对象包含了blob对象和子目录对象,blob对象包含了文件数据。以下是针对这结论的具体演示。

$git log

>: commit fee20199b1326896dab693650a37b44e3e11c3cf (HEAD -> master)

>Date:   Sat Nov 24 12:26:33 2018 +0800

>    first commit


$git cat-file -t fee20199b1326896da

>: commit

git cat-file -t列出指定log id对应的操作类型(对象类型),此处为commit。

$git cat-file commit fee20199b1326896da

>tree 37057b2e8a9041ef88b805a5b7c4e0e668a03be4
>author 解永辉 <nick@xieyonghui.com> 1543033593 +0800
>committer 解永辉 <nick@xieyonghui.com> 1543033593 +0800

>first commit

git cat-file commit列出指定类型的对象信息,其中tree id的指向可以是一个blob对象,也可以是另一个tree。

$git ls-tree 37057b2e

>:100644 blob 72943a16fb2c8f38f9dde202b7a70ccc19c52f34    a.txt

git ls-tree列出了tree详情,可以看出这棵树中包含了一个a.txt文件。

$git cat-file blob 72943a16fb2

>:hi,gits

git cat-file blob列出blob对象所包含的信息。

$ find .git/objects
>.git/objects
>.git/objects/37
>.git/objects/37/057b2e8a9041ef88b805a5b7c4e0e668a03be4
>.git/objects/72
>.git/objects/72/943a16fb2c8f38f9dde202b7a70ccc19c52f34
>.git/objects/fe
>.git/objects/fe/e20199b1326896dab693650a37b44e3e11c3cf
>.git/objects/info
>.git/objects/pack

所有对象都存放在.git/objects目录,其类型为commit、blob、tree和tag,其中包含了对象的类型、长度和内容。

$ cat .git/HEAD
>:ref: refs/heads/master

常用的HEAD则存放在 .git目录下,HEAD保存了当前分支的信息。

$ cat .git/refs/heads/master
>:fee20199b1326896dab693650a37b44e3e11c3cf

通过查看HEAD文件指向的路径文件,看到了一个随机的id,很明显就是上面所提到的commit对象id。

$ git cat-file commit fee20199b1326896dab693650a37b44e3e11c3cf
>tree 37057b2e8a9041ef88b805a5b7c4e0e668a03be4
>author 解永辉 <nick@xieyonghui.com> 1543033593 +0800
>committer 解永辉 <nick@xieyonghui.com> 1543033593 +0800

>first commit

因此,HEAD所指向的是当前分支最近一次commit的信息,为了证实这一点,新增一次提交。

$ git commit -m "second commit"
>[master f22d71f] second commit
>1 file changed, 1 insertion(+)
>create mode 100644 b.txt

然后,通过cat命令查看HEAD文件所指向的路径文件。

$  cat .git/refs/heads/master
>:f22d71f18043ac38f8dbce6cf9e48e598c5cc501

继续git cat-file命令 

$ git cat-file commit f22d71f18043ac38f8dbce6cf9e48e598c5cc501
>tree 61079fdd21307b94ad35ae0f4113a749d222ae03
>parent fee20199b1326896dab693650a37b44e3e11c3cf
>author 解永辉 <nick@xieyonghui.com> 1543037407 +0800
>committer 解永辉 <nick@xieyonghui.com> 1543037407 +0800

>second commit

由此,可以看出tree指向最近一次的commit对象,parent指向了上一次的commit对象。

Git index file索引文件

git index file(索引文件)位于.git/index路径下,是一种二进制文件,可以通过git ls-files命令查看:

git ls-files

先对a.txt做些修改,增加两行内容,此时还没有执行git add,通过git diff查看变更差异:

$ git diff
>diff --git a/a.txt b/a.txt
>index 72943a1..1802a74 100644
>--- a/a.txt
>+++ b/a.txt
>@@ -1 +1,3 @@
> hi,gits
>+bbb
>+ccc

然后,执行git add命令,再通过git diff查看:

$ git add .
$ git diff

这次git diff时并没有输出任何信息,因为git diff命令默认比较工作目录树与index file之间的差异。 

git ls-files查看index file

$ git ls-files --stage
>100644 1802a7474429e8c0f81d2a36bed657d4b29ed23c 0       a.txt
>100644 2d57b63e6946a9548531960c4f7b15ed10884f52 0       b.txt

通过git cat-file查看指定文件的类型。

$ git cat-file -t 1802a7474 >:blob

通过git cat-file指定文件类型和id查看文件,示例中文件类型为blob。

$ git cat-file blob 1802a7474
>hi,gits
>bbb
>ccc

由此,可以看出执行git add命令后,会创建了一个blob文件,记录最近的修改,并在index file中更新这个blob文件的引用。