简介
Git 中常见有四种类型的对象 blob、tree、 commit 、tag:
- blob:用来存储文件数据,通常是一个文件
- tree:一个tree上可以有多个 blob 或tree
- commit:指向一个”tree”,用来标记项目某状态。它包括信息数据,如时间戳、提交的作者、父提交(parent commits)等
- tag:标记某一个提交(commit)
下图很好的说明他们的关系
Git 是一个内容寻址文件系统,其核心部分是一个简单的键值对数据库。向该数据库插入任意类型的内容,它会返回键值(SHA-1 哈希值),我们可以利用此值来重新获取内容。 我们应该先初始化一个新的 Git 版本库,因为老版本库东西太多,容易让我们混淆。
初始化一个新的 Git 版本库:
$ git init
Initialized empty Git repository in C:/Users/Administrator/Desktop/gitstudy/.git/
$ find .git/objects
.git/objects
.git/objects/info
.git/objects/pack
可以看到 Git 对 objects 目录进行了初始化(objects 目录会是对象文件存储的地方),并创建了 pack 和 info 的空目录。
数据对象(blob object)
向 Git 数据库存入一些文本:
$ echo 'test' | git hash-object -w --stdin
9daeafb9864cf43055ae93beb0afd6c7d144bfa4
- 可以通过下面命令查看更多帮助信息
$ git hash-object -h
- -w 选项指示 hash-object 命令存储数据对象;若不指定此选项,则该命令仅返回对应的键值(SHA-1 哈希值)。
- –stdin选项则指示该命令从标准输入读取内容;若不指定此选项,则须在命令尾部给出待存储文件的路径。 该命令输出一个长度为 40 个字符的校验和。 这是一个 SHA-1 哈希值——一个将待存储的数据外加一个头部信息(header)一起做 SHA-1 校验运算而得的校验和。
现在我们可以查看 Git 是如何存储数据的:
$ find .git/objects/ -type f
.git/objects/9d/aeafb9864cf43055ae93beb0afd6c7d144bfa4
find .git/objects -type f
这个linux命令可以列出.git/objects下所有的类型为一般文件的文件- 一个文件对应一条内容,以该内容加上特定头部信息一起的 SHA-1 校验和为文件命名
- 校验和的前两个字符用于命名子目录(别忘了),余下的 38 个字符则用作文件名
如果是一个文件,我们也可以用同样方法:
$ echo '1.txt' > 1.txt
$ git hash-object -w 1.txt
1f67fc4386b2d171e0d21be1c447e12660561f9b
修改内容,在此存储,并查看对象:
$ echo 'test txt' > 1.txt
$ git hash-object -w 1.txt
5eb252be93508457ff0b5ef442577dcd50daf6e4
$ find .git/objects/ -type f
.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
.git/objects/5e/b252be93508457ff0b5ef442577dcd50daf6e4
.git/objects/9d/aeafb9864cf43055ae93beb0afd6c7d144bfa4
可以看到,内容都被存储。由于是对文件和内容进行 SHA-1 校验,即使在不同电脑,上面的校验和还是一致。
获取数据/还原版本
可以通过 cat-file 命令从 Git 那里取回数据:
$ git cat-file -p 9daeafb9864cf43055ae93beb0afd6c7d144bfa4
test
$ git cat-file -p 1f67fc
1.txt
$ git cat-file -p 5eb252
test txt
$ git cat-file -t 1f7a7a
blob
- 可以通过下面命令查看更多帮助信息
$ git cat-file -h
- -p 选项可指示该命令自动判断内容的类型,并为我们显示格式友好的内容
- -t 命令,查看其对象类型
既然可以通过上面方法获取数据了,那么我们可以通过流,将旧版本的数据写入文件并覆盖,从而达到版本还原的功能。
现在可以把文件内容恢复到第一个版本:
$ git cat-file -p 1f67fc > 1.txt
$ cat 1.txt
1.txt
或者第二个版本:
$ git cat-file -p 5eb252 > 1.txt
$ cat 1.txt
test txt
树对象(tree object)
想要记住文件的每一个版本所对应的 SHA-1 值并不现实(随着版本增多,objects 目录内容会越来越多)。而且,当前的方式我们只保存了文件内容,而文件名没有进行保存。所以,有了树对象(tree object)的概念,它能保存文件名,也允许将多个文件组织到一起。
Git 会根据某一时刻暂存区所表示的状态创建并记录一个对应的树对象。 可以通过底层命令 update-index 为一个单独文件创建一个暂存区:
$ git update-index --add --cacheinfo 100644 \
> 5eb252be93508457ff0b5ef442577dcd50daf6e4 1.txt
- –add:让git不忽略掉新文件(未放入暂存区的)
- –cacheinfo:将文件放入暂存区
- 100644,文件模式;参考了常见的 UNIX 文件模式 - 100644(普通文件),100755,表示一个可执行文件,120000,表示一个符号链接等
创建树对象
可以通过 write-tree 命令将暂存区内容写入一个树对象:
$ git write-tree
578d3113e3145dbb260e6a33aff45b408ce36c3d
$ git cat-file -p 578d31
100644 blob 5eb252be93508457ff0b5ef442577dcd50daf6e4 1.txt
$ git cat-file -t 578d31
tree
创建一个新的树对象,它包括 1.txt 文件的第二个版本,以及一个新的文件 2.txt,然后观察它的结构:
$ echo '2.txt' > 2.txt
$ git update-index --add 2.txt
$ git write-tree
d0b4a7dfd637bc58aceb020c09e8be28377289f4
$ git cat-file -p d0b4a7
100644 blob 5eb252be93508457ff0b5ef442577dcd50daf6e4 1.txt
100644 blob e7b4ad382349ff96dd8199000580b9b1e2042eb0 2.txt
往树对象添加树对象
可以将第一个树对象加入第二个树对象,使其成为新的树对象的一个子目录。通过调用 read-tree 命令,可以把树对象读入暂存区:
$ git read-tree --prefix=bak 578d31
$ git write-tree
e4c2c43c04d78d2ac7b82d82c543c3d0999afade
$ git cat-file -p e4c2c4
100644 blob 5eb252be93508457ff0b5ef442577dcd50daf6e4 1.txt
100644 blob e7b4ad382349ff96dd8199000580b9b1e2042eb0 2.txt
040000 tree 578d3113e3145dbb260e6a33aff45b408ce36c3d bak
这个时候,我们数据的结构应该如下图:
提交对象(commit object)
要我们记住所有 SHA-1 哈希值来使用快照,会显得十分不方便。 提交对象(commit object)能为你保存基本信息:谁保存了这些快照,在什么时刻保存的,以及为什么保存这些快照。
可以通过调用 commit-tree 命令创建一个提交对象,为此需要指定一个树对象的 SHA-1 值(代表项目的快照,用于以后使用),也可以提供该提交的父提交对象,让他们有一个关联关系:
$ echo 'first' | git commit-tree e4c2c43
c3fe2f808a34b7c40671b8a27dbdbc2afcb3479f
$ git cat-file -p c3fe2f808a34b7c40671b8a27dbdbc2afcb3479f
tree e4c2c43c04d78d2ac7b82d82c543c3d0999afade
author oDevilo <15757166470@163.com> 1486459157 +0800
committer oDevilo <15757166470@163.com> 1486459157 +0800
first
我们将创建另两个提交对象,它们分别引用各自的上一个提交(作为其父提交对象):
$ echo 'second' | git commit-tree -p c3fe2f d0b4a7
14c01f0c70573e12f1d59f9853adfdca9a6afd1d
$ echo 'third' | git commit-tree -p 14c01 578d31
e9775cfb8e302a700f2f67106895f139a781fea0
查看历史
至此,我们可以通过下面 git log
查看 Git 提交历史了:
$ git log --stat e9775
commit e9775cfb8e302a700f2f67106895f139a781fea0
Author: oDevilo <15757166470@163.com>
Date: Tue Feb 7 17:29:06 2017 +0800
third
2.txt | 1 -
1 file changed, 1 deletion(-)
commit 14c01f0c70573e12f1d59f9853adfdca9a6afd1d
Author: oDevilo <15757166470@163.com>
Date: Tue Feb 7 17:28:12 2017 +0800
second
bak/1.txt | 1 -
1 file changed, 1 deletion(-)
commit c3fe2f808a34b7c40671b8a27dbdbc2afcb3479f
Author: oDevilo <15757166470@163.com>
Date: Tue Feb 7 17:19:17 2017 +0800
first
1.txt | 1 +
2.txt | 1 +
bak/1.txt | 1 +
3 files changed, 3 insertions(+)