>[success] # .git 文件
1. 初始化一个本地`git init`仓库,找到`.git` 目录如图
![](https://img.kancloud.cn/49/27/4927b8ed2e77f7feb0018765b34d57cd_338x256.png)
其中`Git objects`是`git`的实际存储数据
* 第一次初始化的 object 文件打开
![](https://img.kancloud.cn/c2/49/c2494854e1217c8bfda1cefc419b2434_332x103.png)
2. 开始创建文件信息并且通过 `git add` 放入暂存区
~~~
echo '111' > a.txt
echo '222' > b.txt
~~~
* 将 `a.txt` 和 `b.txt` 放入执行`git add` 放入暂存区后结构目录,多出两个文件 `58` 和`c2`
![](https://img.kancloud.cn/26/0b/260b2d19414230543c3d3e719825c0ea_319x147.png)
* 通过 tree 指令查看(tree 指令安装参考集成Tree 指令章节),`tree .git/objects`
![](https://img.kancloud.cn/c6/5f/c65f1c0b090450b1c94e4acc00bf40cd_368x145.png)
如果直接查看 `58` 和`c2` 中两个文件发现是个一串乱码,这是因为Git将信息压缩成二进制文件,`cat .git/objects/58/c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c `
![](https://img.kancloud.cn/f9/2d/f92dc2cdd58307e6253f4184bbf3a0b7_483x38.png)
3. `git `提供了相应指令专门用来查看这种文件,`git cat-file [-t] [-p]`,`-t`可以查看object的类型,`-p`可以查看object储存的具体内容
* `git cat-file -t 58c9` `58c9` 文件夹加文件名(文件名可以取前几位即可),类型是一个`blob`类型
![](https://img.kancloud.cn/56/25/56258d80a7a25137c8754a37c514c10a_197x40.png)
* `git cat-file -p 58c9` 查看加密信息实际内容
![](https://img.kancloud.cn/39/0e/390e222db1f5c8700e63b6c2437088ab_248x39.png)
**blob类型**,它只**储存的是一个文件的内容**,不包括**文件名等其他信息**。然后将这些**信息经过SHA1哈希算法得到对应的哈希值**,也就是说`58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c` 现在对应是一个文本内容
4. 将暂存区内容提交到 `git commit -m 'c'`, 执行完成后会多出两个文件
![](https://img.kancloud.cn/eb/77/eb77d9dfedc24c16a7f24551bf0fe3e4_335x181.png)
* `tree -t .git/objects`
![](https://img.kancloud.cn/13/b3/13b3fc3c782cafb80efb516b794af24d_391x238.png)
`git cat-file [-t] [-p]`查看两个文件中任意一个类型和内容
* `git cat-file -t 4caa` 打印结果是一个 `tree` 类型文件
![](https://img.kancloud.cn/41/30/4130bf3861e642e4dbfd9439b275f0ab_368x39.png)
* `git cat-file -p 4caa`,查看加密信息
![](https://img.kancloud.cn/f0/a5/f0a57e34450eb701f962780a6f7450a8_480x64.png)
* `git cat-file -t 71b5` 打印结果是一个 `commit` 类型文件1
![](https://img.kancloud.cn/7e/37/7e371b9338763c08d8088594b073ffb7_326x53.png)
* `git cat-file -p 71b5`,查看加密信息
![](https://img.kancloud.cn/4e/29/4e29292e62657b1c282a635ef14f5642_403x106.png)
**tree类型**它将当前的目录结构打了一个快照。从它储存的内容来看可以发现它储存了一个目录结构(类似于文件夹),以及每一个文件(或者子文件夹)的权限、类型、对应的身份证(SHA1值)、以及文件名
**commit类型**,它储存的是一个提交的信息,包括对应目录结构的快照tree的哈希值,上一个提交的哈希值(这里由于是第一个提交,所以没有父节点。在一个merge提交中还会出现多个父节点),提交的作者以及提交的具体时间,最后是该提交的信息
* [图片来自](https://www.lzane.com/tech/git-internal/),因此可能和上面操作效果有出入
![](https://img.kancloud.cn/dc/f8/dcf88add701bf326391f40ad52d3de7f_702x400.png)
![](https://img.kancloud.cn/ee/7a/ee7a66525e3dca36a30734fe8738092c_1069x574.png)
>[danger] ##### 多几次文件修改
1. 现在继续执行`echo '333' >> a.txt` ,并将文件执行一次完整流程 `git add` 和 `git commit`
2. 执行 `git log --graph` 打印一个日志树,可以看到日志树记录的都是 `commit` 类型的`id`
![](https://img.kancloud.cn/72/94/7294f03a558cfb529830ca561db168a6_571x212.png)
* 可以通过 执行 `git cat-file -t c4f4` 来确认文件类型
![](https://img.kancloud.cn/8d/a0/8da0e77dafab25c743f6d3c5290d4fd5_289x40.png)
* 执行 `git cat-file -p c4f4` 查看 `commit `类型中具体内容
![](https://img.kancloud.cn/d6/c2/d6c2c477cceee669e6941e1f06356968_608x124.png)
* 执行`git cat-file -p fcfa` 查看 `c4f4` 指向当前 `tree `类型文件信息
![](https://img.kancloud.cn/51/fc/51fcd0a2cc51e082769ce2b03ce5297e_517x54.png)
因为本次之更改 `a.txt` 信息,会发现`a.txt` 储存的 `blob` 类型记录值发生了更改(从`58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c`变`f39c1520a7dee8f5610920364b6faba45b01bfd0`),相对没有进行更改的`b.txt ` `blob` 类型记录值没有改变(`c200906efd24ec5e783bee7f23b5d7c941b0c12c`)
* 执行`git cat-file -p f39c` 查看 修改后`a.txt` 内容,虽然只在 `a.txt` 文件内追加了 `333` 这个内容但可以看到之前`111` 也在本次文件中
![](https://img.kancloud.cn/0e/79/0e794e0aed6e1defdb5ede3bee3da424_277x68.png)
Git储存的是全新的文件快照,而不是文件的变更记录。也就是说,就算你只是在文件中添加一行,Git也会新建一个全新的blob object,虽然这样会浪费空间但是checkout一个commit,或对比两个commit之间的差异。如果Git储存的是问卷的变更部分,那么为了拿到一个commit的内容,Git都只能从第一个commit开始,然后一直计算变更,直到目标commit,这会花费很长时间。而相反,Git采用的储存全新文件快照的方法能使这个操作变得很快,直接从快照里面拿取内容就行了
*****
* 形成一个图结构`Git`中的文件内容存储成`blob`(要注意是内容,不是文件。文件的名字和模式不存储在`blob`。因此如果两个文件内容相同,则只会存储一份`blob`)
![](https://img.kancloud.cn/95/df/95dfc910947a41ee36a0409a7c65d9aa_1521x2562.png)
>[danger] ##### 其他问题
**为什么要把文件的权限和文件名储存在tree object里面而不是blob object呢?**
想象一下修改一个文件的命名。
如果将文件名保存在blob里面,那么Git只能多复制一份原始内容形成一个新的blob object。而Git的实现方法只需要创建一个新的tree object将对应的文件名更改成新的即可,原本的blob object可以复用,节约了空间。
*****
**Git怎么保证历史记录不可篡改**
通过SHA1哈希算法和哈系树来保证。假设你偷偷修改了历史变更记录上一个文件的内容,那么这个问卷的blob object的SHA1哈希值就变了,与之相关的tree object的SHA1也需要改变,commit的SHA1也要变,这个commit之后的所有commit SHA1值也要跟着改变。又由于Git是分布式系统,即所有人都有一份完整历史的Git仓库,所以所有人都能很轻松的发现存在问题。
*****
`Git`中的文件内容存储成`blob`(要注意是内容,不是文件。文件的名字和模式不存储在`blob`。因此如果两个文件内容相同,则只会存储一份`blob`)
*****
`Git`中的文件夹对应为`tree`。`Tree`中含有这个`tree`包含的`blob`和`tree`的名字,模式,类型和`SHA`等信息
*****
`Commit`非常简单,只是指向了一个`tree`,并且包含了作者,提交者,提交信息,和所有的直属`parent commit`
>[info] ## 参考来源
[这才是真正的GIT——GIT内部原理](https://www.lzane.com/tech/git-internal/)
[ Git object 类型笔记](https://nanxiao.me/git-object-type-note/)