在每一個 git 倉庫中都有一個隱藏的.git目錄,git 的很多秘密都藏著這裏麵,包括分支、提交記錄、配置、日誌等等。
作為一個有實力有低調的開發者來說,你瞭解其中每個目錄、每個檔案都代表著什麼嗎?它們的作用又是什麼呢?
以下是 .git目錄的完整結構,看上去也是比較簡單的,git 就是用這個結構管理這複雜的版本關係。
.git |----config |----objects | |----0d | | |----1d218acc58d857329cde3778d7429037079627 | | |----60436b1116e54b3ed9a4b9fec1f2f9916a73ca | |----59 | | |----a10af7aa35f521b9a41569785d4481850192e3 | |----92 | | |----5f4810ed4da9873771f14f2ca1ab97db81283a | | |----6c85f1ad90849159b2c616a9744ba62abe8f6a | | |----c359f0b8d09e2fc137c865043d4f6951989cdd |----HEAD |----info | |----exclude |----logs | |----HEAD | |----refs | | |----heads | | | |----main | | |----remotes | | | |----origin | | | | |----main |----description |----hooks | |----commit-msg.sample | |----pre-rebase.sample | |----pre-commit.sample | |----applypatch-msg.sample | |----fsmonitor-watchman.sample | |----pre-receive.sample | |----prepare-commit-msg.sample | |----post-update.sample | |----pre-merge-commit.sample | |----pre-applypatch.sample | |----pre-push.sample | |----update.sample | |----push-to-checkout.sample |----refs | |----heads | | |----main | |----tags | |----remotes | | |----origin | | | |----main |----index |----COMMIT_EDITMSG
HEAD(當前分支)
有一個叫做 HEAD檔案,這個檔案一聽名字就比較重要,它的作用就是儲存當前倉庫正在使用的分支名稱。
檢視其內容,如下面這樣,表示當前正在使用 main 分支。
ref: refs/heads/main
或者下面這樣,表示正在使用 dev 分支。
ref: refs/heads/dev
refs
在 .git/refs 目錄下有 heads、tags、remotes三個子目錄,用來儲存最新的提交記錄引用的。
|----refs | |----heads | | |----main | |----tags | |----remotes | | |----origin | | | |----main
這三個目錄的作用都是用來標示最新一次的提交記錄的。
.git/refs/heads
例如我本地只有 main分支,那就會在這裏存在一份.git/refs/heads/mian的檔案,檔案內容如下:
2ddc65b74a186f4332d7b218be0f18dd404c82ef
這個值就是 main 分支最新一次提交記錄的引用,引用指向 .git/objects這個目錄,這裏麵記錄了每一次提交的詳情資訊。
.git/refs/remotes
.git/refs/heads存的是本地分支提交記錄的引用,而 .git/refs/remotes存的是遠端分支的提交記錄引用,這樣一來,git就能夠在本地跟蹤遠端倉庫的分支變化。
.git/refs/tags
我們在管理程式碼的時候,可能在某些時候對當前版本打一個標籤,標記某些重要的版本,這樣就會在 .git/refs/tags目錄下形成記錄,標示某個tag的最新一次提交記錄引用。
.git/refs/stash
除了這三個目錄外,還有可能包含 stash這個目錄,如果你用過「本地暫存」這個功能的話。
什麼情況下會用這個功能呢?
臨時切換到其他分支,當你正在當前分支上進行工作,但需要暫時切換到其他分支,而又不想提交當前工作目錄的改動時,可以使用git stash將改動儲存起來,切換分支後再用git stash pop或git stash apply恢復。
臨時儲存,假設你兩天前拉取了某個檔案,改了一部分,但是一直沒完成。在這期間呢,你的同事也把這個檔案改了,那你提交的時候就會有衝突。這種情況下,你可以先把你本地的暫存起來,然後再拉取最新程式碼,之後再把暫存的改動恢復過來。
提交記錄 (.git/objects)
前面介紹 refs時說的最新一次的提交記錄引用,其引用的目標就是.git/objects這個目錄。拿前面那個 main分支的最後一次提交引用來講。
上面這一串16進位制字串,可以分為兩個部分,前兩位是一個部分,後兩位是一個部分,前兩位作為目錄,後面的是檔名稱。
所以在 .git/objects中存在 .git/objects/2d/dc65b74a186f4332d7b218be0f18dd404c82ef這樣一個檔案,裡面存的就是某次提交的詳細資訊,直接檢視的話,只能看到亂碼,要用 git 自己的命令才行。
git cat-file -p 2ddc65b74a186f4332d7b218be0f18dd404c82ef
檢視到的提交詳情,主要是提交樹、作者、提交者以及提交message
透過 tree的記錄值,可以檢視本次提交所涉及的所有檔案樹。依然是 透過下面的命令檢視,後面的一串hash值,就是上圖的 tree 的值。
git cat-file -p f8803d7d049edbbc981da732b855419d039f79ff
得到的結果如下:
再用 git cat-file -p 命令可以檢視上圖型別為 blob的檔案的內容。
日誌 (.git/logs)
這裏記錄的每一次的提交日誌,包括當前使用的分支、本地分支、遠端分支。
|----logs | |----HEAD | |----refs | | |----heads | | | |----main | | |----remotes | | | |----origin | | | | |----main
以行為單位,每一行都儲存了一次提交的具體資訊。
從左到右分別表示提交之前的hash、提交之後的hash、提交使用者、時間戳、提交的message。
config (配置)
.git/config 是當前倉庫的配置檔案,這個檔案,大多數開發同學應該還是比較熟悉的。這個配置只對當前倉庫有效。
瞭解了前面的內容後,再看一下這個配置檔案,應該就很好理解了。
[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true ignorecase = true precomposeunicode = true [remote "origin"] url = https://github.com/xxxx/xxxx-blog.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "main"] remote = origin merge = refs/heads/main
index (暫存區)
.git/index 檔案是 暫存區。它儲存了當前倉庫中所有被 git 跟蹤的檔案的狀態資訊,包括檔名、檔案的許可權和位置等。當執行git add命令時,git 將要提交的檔案的快照資訊暫存到這個索引檔案中,而不是直接提交到倉庫。
當執行git commit命令時,git 會將索引檔案中的內容提交到版本庫中,從而建立一個新的提交記錄。
hooks (鉤子)
hook 對於程式設計師來講肯定不陌生,可以看做是一個生命週期的事件處理,例如提交程式碼後,觸發某個動作,「提交程式碼後」這個節點就是個 Hook,在這個Hook裡我們可以加入一些自定義的處理邏輯來達到某些目的。
例如 pre-commit(在執行提交前執行)、post-commit(在提交後執行)、pre-push(在執行推送前執行)等。
怎麼樣, 學廢了嗎?