>[success] # .git 文件
1. 初始化一個本地`git init`倉庫,找到`.git` 目錄如圖

其中`Git objects`是`git`的實際存儲數據
* 第一次初始化的 object 文件打開

2. 開始創建文件信息并且通過 `git add` 放入暫存區
~~~
echo '111' > a.txt
echo '222' > b.txt
~~~
* 將 `a.txt` 和 `b.txt` 放入執行`git add` 放入暫存區后結構目錄,多出兩個文件 `58` 和`c2`

* 通過 tree 指令查看(tree 指令安裝參考集成Tree 指令章節),`tree .git/objects`

如果直接查看 `58` 和`c2` 中兩個文件發現是個一串亂碼,這是因為Git將信息壓縮成二進制文件,`cat .git/objects/58/c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c `

3. `git `提供了相應指令專門用來查看這種文件,`git cat-file [-t] [-p]`,`-t`可以查看object的類型,`-p`可以查看object儲存的具體內容
* `git cat-file -t 58c9` `58c9` 文件夾加文件名(文件名可以取前幾位即可),類型是一個`blob`類型

* `git cat-file -p 58c9` 查看加密信息實際內容

**blob類型**,它只**儲存的是一個文件的內容**,不包括**文件名等其他信息**。然后將這些**信息經過SHA1哈希算法得到對應的哈希值**,也就是說`58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c` 現在對應是一個文本內容
4. 將暫存區內容提交到 `git commit -m 'c'`, 執行完成后會多出兩個文件

* `tree -t .git/objects`

`git cat-file [-t] [-p]`查看兩個文件中任意一個類型和內容
* `git cat-file -t 4caa` 打印結果是一個 `tree` 類型文件

* `git cat-file -p 4caa`,查看加密信息

* `git cat-file -t 71b5` 打印結果是一個 `commit` 類型文件1

* `git cat-file -p 71b5`,查看加密信息

**tree類型**它將當前的目錄結構打了一個快照。從它儲存的內容來看可以發現它儲存了一個目錄結構(類似于文件夾),以及每一個文件(或者子文件夾)的權限、類型、對應的身份證(SHA1值)、以及文件名
**commit類型**,它儲存的是一個提交的信息,包括對應目錄結構的快照tree的哈希值,上一個提交的哈希值(這里由于是第一個提交,所以沒有父節點。在一個merge提交中還會出現多個父節點),提交的作者以及提交的具體時間,最后是該提交的信息
* [圖片來自](https://www.lzane.com/tech/git-internal/),因此可能和上面操作效果有出入


>[danger] ##### 多幾次文件修改
1. 現在繼續執行`echo '333' >> a.txt` ,并將文件執行一次完整流程 `git add` 和 `git commit`
2. 執行 `git log --graph` 打印一個日志樹,可以看到日志樹記錄的都是 `commit` 類型的`id`

* 可以通過 執行 `git cat-file -t c4f4` 來確認文件類型

* 執行 `git cat-file -p c4f4` 查看 `commit `類型中具體內容

* 執行`git cat-file -p fcfa` 查看 `c4f4` 指向當前 `tree `類型文件信息

因為本次之更改 `a.txt` 信息,會發現`a.txt` 儲存的 `blob` 類型記錄值發生了更改(從`58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c`變`f39c1520a7dee8f5610920364b6faba45b01bfd0`),相對沒有進行更改的`b.txt ` `blob` 類型記錄值沒有改變(`c200906efd24ec5e783bee7f23b5d7c941b0c12c`)
* 執行`git cat-file -p f39c` 查看 修改后`a.txt` 內容,雖然只在 `a.txt` 文件內追加了 `333` 這個內容但可以看到之前`111` 也在本次文件中

Git儲存的是全新的文件快照,而不是文件的變更記錄。也就是說,就算你只是在文件中添加一行,Git也會新建一個全新的blob object,雖然這樣會浪費空間但是checkout一個commit,或對比兩個commit之間的差異。如果Git儲存的是問卷的變更部分,那么為了拿到一個commit的內容,Git都只能從第一個commit開始,然后一直計算變更,直到目標commit,這會花費很長時間。而相反,Git采用的儲存全新文件快照的方法能使這個操作變得很快,直接從快照里面拿取內容就行了
*****
* 形成一個圖結構`Git`中的文件內容存儲成`blob`(要注意是內容,不是文件。文件的名字和模式不存儲在`blob`。因此如果兩個文件內容相同,則只會存儲一份`blob`)

>[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/)