一、Git对象
Git对象一共有4种:blob数据对象、tree树对象、commit提交对象、tag标签对象;
- blob对象会把二进制文件进行压缩存储,并输出一个(唯一的)40位hash值作为key
- git会把压缩内容存储在 objects 文件夹下,并以对应的key值40位的前两位hash值作为文件夹名,后38位作为文件名;
- git是全量快照存储,而非增量存储;
- blob对象的hash值与文件名无关; 故blob对象无法存储文件信息;
- tree对象可以解决文件名保存的问题, 并且使得项目的多个文件也组织到一起;
- tree对象虽然可以表示我们想要跟踪的快照,但是无法记录快照的信息,操作者信息;
- commit对象还可以指明它的父提交对象;从而形成一系列的log记录; 我们执行git log查看的就是一条条互相链接的提交对象。
二、Git 储存原理&&实操
1.创建新项目git-demo;cd git-demo && git init
mkdir git-demo
cd git-demo
git init // 初始化git仓库,项目中多出.git文件夹
2.在项目中add的2个文件
echo '111' > a.txt
echo '222' > b.txt
git add .
使用git cat-file来查看git object的信息
git cat-file -t 查看版本库对象类型(blob、tree、commit、tag)
git cat-file -p 查看版本库对象详细内容
git cat-file -s 查看版本库对象大小
前面git对象中第二点,我们有介绍到"git会把压缩内容存储在 objects 文件夹下,并以对应的key值40位的前两位hash值作为文件夹名,后38位作为文件名"。因此,我们这里要查看git object信息,需要使用hash值:即所在的文件夹名+文件名。
Git cat-file [-t] [-s] [-p] 58c9
Git cat-file [-t] [-s] [-p] c200
// 一般使用hash的前6位就可以定位到对象,我们这里文件少使用4位也可以的。
查看.git/objects中新增的2个对象信息,类型:blob对象、size: 4byte(算上了换行符)、内容: 文件具体内容。不难发现Blob是object的最小单位,储存文件的具体内容。
3.git commit暂存区的2个文件
git commit -m 'first commit'
同样通过git cat-file 查看新增2个对象信息
每次commit都会多出一个commit与一个tree对象。
commit对象详情内容中,可查看对应的tree对象、作者、提交信息以及上一次提交的commit(我们这里是第一次提交就没有显示了)。
tree对象的详情内容中,可查看到这次快照的目录结构、文件权限、文件名(即上一步执行git add,新增的 储存blob对象的文件信息)。
object之间的关联图(一)
![](https://i-blog.csdnimg.cn/blog_migrate/4d5ebbc8bacc48d391bd8e2ed9f4967b.png)
4.修改a.txt文件并执行git add && git commit
echo '333' > a.txt
git add a.txt
git commit –m "2nd commit"
![](https://i-blog.csdnimg.cn/blog_migrate/3b5893df0fc85407164f8515788d27fe.png)
会发现此时又多出3个对象,分别是blob、commit、tree对象。
blob对象,是修改a.txt文件新增的blob对象,修改前的a.txtd的blob对象无变动。
tree对象,关联修改后的a.txt对应blob对象以及未修改的b.txt对应的blob对象。证明git是全量快照存储,而非增量存储,每次快照都记录着,项目中当前git所追溯的所有文件。
commit对象,可查看对应的tree对象、作者、提交信息以及上一次提交的commit。
object之间的关联图(二)
5.打个标签git tag
git tag -a "V1.0.0" -m "V1.0.0"
// git tag –a –m 会生成一个tag objects,但直接git tag 不会生成
多出一个tag 对象,可查看对应commit、作者、tag标注,同时.git/refs/tags中也会多出一个文件
object之间的关联图(三)
6. 查看.git/HEAD文件
cat .git/HEAD
// 文件内容:.git/refs/heads/master
cat .git/refs/heads/master
// 文件内容:最新commit记录
object之间的关联图(四)
HEAD、分支、普通的Tag可以简单的理解成是一个指针,指向对应commit的SHA1值
变更文件时工作区域变动
三、.git目录结构
文件夹 | 类型 | 文件夹里面的内容 |
---|---|---|
objects | 文件夹 | 这就是实际意义上的 git数据库, 存数据的地方; 并且存了所有的历史记录; |
refs | 文件夹 | 目录存储指向数据(分支、远程仓库和标签等)的提交对象的指针; |
HEAD | 文件 | 它文件通常是一个符号引用(symbolic reference),指向目前所在的分支。 |
hooks | 文件夹 | 目录包含客户端或服务端的钩子脚本;我们最常用的就是pre-commit钩子了; |
FETCH_HEAD | 文件 | git fetch将更新git remote 中所有的远程repo 所包含分支的最新commit-id, 将其记录到.git/FETCH_HEAD文件中 |
ORIG_HEAD | 文件 | 当前分支,前一次commit-id |
config | 文件 | 包含项目特有的配置选项; |
index | 文件 | 文件保存暂存区信息; |
description | 文件 | 用来显示对仓库的描述信息,文件仅供 GitWeb 程序使用,我们无需关心; |
info | 文件夹 | 包含一个全局性排除文件; |
FQA
-
Git为啥是全量快照存储, 而非增量存储?
Git做了取舍,以空间换时间,把复杂度从O(n)降为O(1)。
全量快照存储:diff两次commit为O(1)复杂度,获取commit节点下的tree object,再把对应文件取出来做对比。
增量存储:diff两次commit为O(n)复杂度,去到每个文件第一个commit,叠加之后的变更记录,才能做对比 -
Blob对象的hash值为啥不保存文件名信息?
假如git把文件名信息存放在blob对象中,我们仅仅修改文件名,git会全量快照创建一份blob对象,
但是文件名储存在tree objects,git只要新建一份tree对象。大部分blob对象要比tree对象大很多,blob记录是文件内容,而tree对象只记录了文件同层信息。 -
Git怎么保证历史记录不可篡改?
Git和区块链的数据结构非常相似,都是基于哈希式和分布式,一个文件改动,都应的object、tree、commit都会改变。可以进行追溯。