一直用git,你了解git的内部机制吗?
“?大家好~我是Coder 老C,很高興又和大家見面了~”
在工作過程中我們會不可避免的使用Git,但是你知道Git是如何存儲你的文件、如何保存你的提交信息嗎?等等 了解這些也便于我們更好的理解和記憶命令,更好的排查問題和使用Git,下面就讓我們來看一下吧~
本文主要依照官網的介紹根據真實項目中的變化總結整理而成
首先,我們要明確 Git 是一個分布式版本控制系統 其本質是一套 內容尋址文件系統
通俗點說,Git 從核心上來看不過是簡單地存儲鍵值對(key-value)。它允許插入任意類型的內容,并會返回一個鍵值,通過該鍵值可以在任何時候再取出該內容。
ps : 下面所說的SHA-1碼 和 commit_id 是同一種
首先,Git存儲在本地的表現形式
當你在一個新目錄或已有目錄內執行 git init 時,Git 會創建一個 .git 目錄,幾乎所有 Git 存儲和操作的內容都位于該目錄下。如果你要備份或復制一個庫,基本上將這一目錄拷貝至其他地方就可以了。如下圖:
info?目錄保存了一份不希望在 .gitignore 文件中管理的忽略模式 (ignored patterns) 的全局可執行文件
hooks?目錄保存了客戶端或服務端鉤子腳本
config?文件包含了項目特有的配置選項
objects?目錄存儲所有數據內容
refs?目錄存儲指向數據 (分支) 的提交對象的指針
HEAD?文件指向當前分支
index?文件保存了暫存區域信息
其中,HEAD 及 index 文件,objects 及 refs 目錄是 Git 的核心部分。
接下來,說一下Git的存儲方式
如上述所說,objects 目錄存儲所有數據內容,objects 目錄下的每一個文件是Git為每份存儲數據內容生成一個文件,取得該內容與頭信息的 SHA-1 校驗和,創建以該校驗和前兩個字符為名稱的子目錄,并以 (校驗和) 剩下 38 個字符為文件命名 (保存至子目錄下)。如下圖:打開00文件夾可以看到里面保存的內容:Git 以一種類似 UNIX 文件系統但更簡單的方式來存儲內容。所有內容以 tree 或 blob 對象存儲,其中 tree 對象對應于 UNIX 中的目錄,blob 對象則大致對應于 inodes 或文件內容。
一個單獨的 tree 對象包含一條或多條 tree 記錄,每一條記錄含有一個指向 blob 或子 tree 對象的 SHA-1 指針,并附有該對象的權限模式 (mode)、類型和文件名信息。
正如 Git的每一次提交都是對代碼倉庫的完整備份,也就是保存了一份代碼倉庫完整的快照所說,每一個commit都是存儲為一個Tree,如下圖:具體在git中為:可以看到,目錄作為tree存儲,文件作為blob存儲
之后,我們通過 git cat-file-p<id>命令可以發現存儲是樹型的,也就是對應于git的tree對象,保存的都是指向下一個部分的索引id 如下圖,每一步都是查看的上一步中的某個id:
上述所說每個commit創建一個樹快照,那么是通過什么創建的呢?
這就是我們上述說的 用于存儲暫存區信息的index文件了。
通常 Git 根據你的暫存區域或 index 來創建并寫入一個 tree 。因此要創建一個 tree 對象的話首先要通過將一些文件暫存從而創建一個 index 。
這也是為什么commit前必須要有文件被add到暫存區,如果暫存區為空,commit會報錯停止執行。
這個時候就有一個問題了,我們有多個快照樹,它們指向了你要跟蹤的項目的不同快照,其中也沒有關于誰、何時以及為何保存了這些快照的信息
此時,commit對象就出場了~ 每次commit提交后就會創建一個對應commit 對象,這個對象就是為你保存了這些基本信息的。
一般情況下,一次commit提交就可以理解為創建了一個tree樹,以commit_id為根節點的tree,該樹包含了當前項目的整體快照
當我們使用 git log命令查看提交歷史的時候,就展示了commit對象的一些基本信息,如下圖:
其中:commit 后跟的id就是當前commit快照的樹根節點id 其余的還包含作者,作者郵箱,創建時間等基本信息
Git每次commit提交會保存項目快照,難道是將所有的文件重新復制一份嗎?
當然不可能,在git的文件系統中,是存在共用文件的。
比如有三次commit提交,產生了三個tree樹,它們在向下引用的時候,如果兩個commit中的整個文件夾或者某個文件沒有改變,這兩個commit的tree會指向同一個對象。對于兩次提交修改了的文件,則會創建一個該文件的一個新的版本的文件,上一次提交指向舊的文件,修改文件的提交指向新版本的文件。
整體情況如下圖:另外,Git 用 zlib 壓縮文件內容,因此存儲的文件并不會占用太多空間
了解了git整體存儲方式之后,我們再看一下前面提到的存儲指向數據 (分支) 的提交對象的指針的 refs目錄
refs目錄內容如下圖:首先,也是思考一個問題:在項目開發中,有許多分支,每個分支的提交記錄都不相同,我們也不可能去記住每個commit_id,去執行像 git log1a410e 這樣的命令來查看完整的歷史,這樣的話你就要記得 1a410e 是你最后一次提交并且記得這個id,這樣才能在提交歷史中找到這些對象,git是怎樣的應對這個問題的呢?
這時候,我們需要一個文件來用一個簡單的名字來記錄這些 SHA-1 值,這樣就可以用這些指針而不是原來的 SHA-1 值去檢索了。在 Git 中,稱之為“引用”(references 或者 refs)。
可以在 .git/refs 目錄下面找到這些包含 SHA-1 值的文件。如下圖refs中heads文件下的文件,其中 每個文件存儲的是與文件名同名的分支的最新提交的commit_id:添加上refs文件夾下的文件后,我們的Git存儲結構就看起來像下圖:
接下來,再思考一個問題,git是怎么標識當前是在什么分支,從而找到refs中對應的映射文件獲取SHA-1值呢?
那就是前面所說的HEAD文件了,我們打開文件可以看到以下內容:ref:refs/heads/test_branch
這里標識的是當前指向的是test_branch分支,并且指定了要是用的映射文件的路徑,這樣就解決了上述問題,是不是特別簡單~
上述已經介紹了Git的三個主要類型:tree樹、commit對象、HEAD。下面我們說一下Git中另外一個重要的東西:Tag(標簽)Tag 對象比較簡單,Tag對象非常像一個 commit 對象---包含一個標簽,一組數據,一個消息和一個指針。最主要的區別就是 Tag 對象指向一個 commit 而不是一個 tree。它就像是一個分支引用,但是不會變化,永遠指向同一個 commit,僅僅是為了提供一個更加友好的名字。
好了,通過介紹了git的核心組成元素 HEAD及index文件,objects及refs目錄 , 你應該會對git的存儲和一些機制有一個簡單的整體了解,這對我們更好的理解git命令和更好的使用git是有幫助的。希望本片文章會對大家有些許幫助~
參考:git官網
如果感覺這篇文章對您有所幫助,請點擊一下“在看”或者“關注”博主,您的喜歡和關注將是我前進的最大動力!
這是本人在這幾年及春招的總結,歷時3個月,我覺得很全面了,對于面試很有幫助,目前,本人已經拿到了騰訊等大廠offer,進入到大廠不是夢想,github 地址:
https://github.com/OUYANGSIHAI/JavaInterview
這么辛苦總結,給個star好不好。?點擊閱讀原文,直達
總結
以上是生活随笔為你收集整理的一直用git,你了解git的内部机制吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 面试官 100% 会严刑拷打的 CMS
- 下一篇: Java 开发提升十倍生产力:idea