做者:馮偉堯git
咱們在切換不一樣分支,或者切換到指定歷史提交記錄時,Git 是怎麼把文件的歷史版本調出來的?也就是說:github
在開始前,爲了不部分歧義,先統一下對提交記錄的認識:咱們都知道每一個提交記錄都會有惟一對應的一個 40 位字符串,這是 Git 根據咱們的提交內容利用 SHA-1 算法計算出來的哈希值。這個 40 位的字符串,有人稱 id 或 哈希值 或 校驗和 或 SHA-1 值。由於 SHA-1 是一種哈希算法,這裏咱們統一下,這個字符串就叫哈希值。算法
Git 如何存儲數據,答案主要就在 .git 文件夾的 objects 目錄裏。先不解釋 objects 目錄是什麼,咱們先看看裏面都有什麼東西。如今你能夠隨意打開一個 Git 倉庫,找到其中 .git 目錄(默認狀況下它多是隱藏的)下的 objects 文件夾。他應該是下面這樣的:markdown
這裏面是不少以 2 個字符命名的文件夾,文件夾裏又是若干數量以一串 38 個字符命名的文件。若是你足夠敏感的話,或許能猜到這是一個相似指定 commit 提交記錄的哈希值。藉助 git cat-file
命令能夠查看這個文件裏的內容。結果以下所示:oop
這裏的結果是我項目中 CHANGELOG.md 文件的內容。更確切的說,它是 CHANGELOG.md 文件在某一次提交時的完整內容。而這個 objects 文件夾,就是 Git 存放數據的地方。spa
Git 在每次提交時,會找到存在變動的文件,利用 SHA-1 算法根據內容計算出 40 位的字符串爲文件命名,放在 objects 文件夾。 哈希值的前兩個字符用於命名子目錄,餘下的 38 個字符則用做文件名。 也就是說,你的文件每次發生變動,都會有一個對應的快照,保存了該版本文件的全部內容。若是 Git 想恢復文件到某一次歷史版本,只須要拿到這個文件該次版本對應的哈希值便可。指針
但是 Git 怎麼知道某次提交時該文件對應版本的哈希值是什麼呢?也就是說 Git 如何把文件不一樣版本和提交記錄關聯起來的?code
實際上在 objects 文件夾裏主要存放着三類信息,除了咱們上面提到的文件內容信息,還有文件路徑信息以及提交信息。它們分別以 文件對象 blob object、樹對象 tree object、提交對象 commit object 的形式存在。每個對象都是 objects 目錄下的一個文件,和保存文件內容信息的方式相似,Git 會經過 SHA-1 算法根據對象內容計算哈希值,獲得一串 40 位的字符串爲文件命名,用於找到他們。 接下來咱們來看樹對象和提交對象都包含了哪些內容。orm
當你提交時,Git 會把你當前的目錄結構保存下來,對應的 tree object 結構以下:對象
包含了文件的文件名和文件對象的哈希值,以及子文件夾名字和對應的樹對象哈希值。也就是說,你只須要找到某次提交根目錄的樹對象哈希值,就能找到該次提交全部文件對應的文件名以及文件對象哈希值。 也就能獲得文件的歷史版本內容了。
那我如何找到這個樹對象?
就是經過咱們最熟悉的提交對象。git log
命令能夠找到對應提交的哈希值,咱們能夠看到提交對象的結構以下:
tree 指向的就是當前提交的樹對象。parent 是上一次提交的提交對象。其餘的是做者,提交人,時間和描述信息。
如今我就能夠知道某次提交的某個文件的歷史版本內容是什麼了。由此咱們也大概瞭解了 Git 是怎麼存儲文件的歷史版本,又是怎麼把歷史版本和提交記錄聯繫起來的。
當使用 git commit 進行提交操做時,Git 會先計算每個子目錄的哈希值,而後在 Git 倉庫中將這些校驗和保存爲樹對象。 隨後,Git 便會建立一個提交對象,它除了包含做者,時間和描述信息外,還包含指向這個樹對象(項目根目錄)的哈希值以及上一次提交的哈希值。如此一來,Git 就能夠在須要的時候重現這次保存的快照。
如今咱們知道,只要拿到某次提交對象的哈希值,咱們就能獲得該次提交對應的快照。若是咱們想要某個分支的文件內容,又是如何作到的?
分支只是一個可變的指針,指向分支最新的提交對象。
在 .git 裏的 refs 文件夾中,保存了全部分支對應的最新提交對象的哈希值。目錄結構以下:
其中 heads 文件夾保存的是本地分支,remotes 文件夾保存的是遠程分支。以本地分支 master 爲例,利用 cat master
能夠看到,該文件中保存了最新提交對象的哈希值。若是你如今進行一次新的提交,會發現該文件中的內容會變爲新提交對象的哈希值。
同時 Git 中有一個特殊的引用 HEAD,它老是指向當前檢出的分支或提交對象。HEAD 指向的提交對象保存在 .git 裏的 HEAD 文件中。 以下圖,HEAD 指向了 master 分支。若是咱們手動檢出 master 分支的上一次提交,會發現該文件指向了一個肯定的提交對象,即上一次提交的提交對象。
最後,能夠用一張圖來歸納分支和提交對象之間的關係。