每天都提交代碼,那你知道.git目錄內(nèi)部的秘密嗎?
在每一個(gè) git 倉庫中都有一個(gè)隱藏的.git目錄,git 的很多秘密都藏著這里面,包括分支、提交記錄、配置、日志等等。
作為一個(gè)有實(shí)力有低調(diào)的開發(fā)者來說,你了解其中每個(gè)目錄、每個(gè)文件都代表著什么嗎?它們的作用又是什么呢?
圖片
以下是 .git目錄的完整結(jié)構(gòu),看上去也是比較簡單的,git 就是用這個(gè)結(jié)構(gòu)管理這復(fù)雜的版本關(guān)系。
.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(當(dāng)前分支)
有一個(gè)叫做 HEAD文件,這個(gè)文件一聽名字就比較重要,它的作用就是存儲(chǔ)當(dāng)前倉庫正在使用的分支名稱。
查看其內(nèi)容,如下面這樣,表示當(dāng)前正在使用 main 分支。
ref: refs/heads/main
或者下面這樣,表示正在使用 dev 分支。
ref: refs/heads/dev
refs
在 .git/refs 目錄下有 heads、tags、remotes三個(gè)子目錄,用來存儲(chǔ)最新的提交記錄引用的。
|----refs
| |----heads
| | |----main
| |----tags
| |----remotes
| | |----origin
| | | |----main
這三個(gè)目錄的作用都是用來標(biāo)示最新一次的提交記錄的。
.git/refs/heads
例如我本地只有 main分支,那就會(huì)在這里存在一份.git/refs/heads/mian的文件,文件內(nèi)容如下:
2ddc65b74a186f4332d7b218be0f18dd404c82ef
這個(gè)值就是 main 分支最新一次提交記錄的引用,引用指向 .git/objects這個(gè)目錄,這里面記錄了每一次提交的詳情信息。
.git/refs/remotes
.git/refs/heads存的是本地分支提交記錄的引用,而 .git/refs/remotes存的是遠(yuǎn)程分支的提交記錄引用,這樣一來,git就能夠在本地跟蹤遠(yuǎn)程倉庫的分支變化。
.git/refs/tags
我們在管理代碼的時(shí)候,可能在某些時(shí)候?qū)Ξ?dāng)前版本打一個(gè)標(biāo)簽,標(biāo)記某些重要的版本,這樣就會(huì)在 .git/refs/tags目錄下形成記錄,標(biāo)示某個(gè)tag的最新一次提交記錄引用。
.git/refs/stash
除了這三個(gè)目錄外,還有可能包含 stash這個(gè)目錄,如果你用過「本地暫存」這個(gè)功能的話。
什么情況下會(huì)用這個(gè)功能呢?
- 臨時(shí)切換到其他分支,當(dāng)你正在當(dāng)前分支上進(jìn)行工作,但需要暫時(shí)切換到其他分支,而又不想提交當(dāng)前工作目錄的改動(dòng)時(shí),可以使用git stash將改動(dòng)保存起來,切換分支后再用git stash pop或git stash apply恢復(fù)。
- 臨時(shí)保存,假設(shè)你兩天前拉取了某個(gè)文件,改了一部分,但是一直沒完成。在這期間呢,你的同事也把這個(gè)文件改了,那你提交的時(shí)候就會(huì)有沖突。這種情況下,你可以先把你本地的暫存起來,然后再拉取最新代碼,之后再把暫存的改動(dòng)恢復(fù)過來。
提交記錄 (.git/objects)
前面介紹 refs時(shí)說的最新一次的提交記錄引用,其引用的目標(biāo)就是.git/objects這個(gè)目錄。拿前面那個(gè) main分支的最后一次提交引用來講。
圖片
上面這一串16進(jìn)制字符串,可以分為兩個(gè)部分,前兩位是一個(gè)部分,后兩位是一個(gè)部分,前兩位作為目錄,后面的是文件名稱。
所以在 .git/objects中存在 .git/objects/2d/dc65b74a186f4332d7b218be0f18dd404c82ef這樣一個(gè)文件,里面存的就是某次提交的詳細(xì)信息,直接查看的話,只能看到亂碼,要用 git 自己的命令才行。
git cat-file -p 2ddc65b74a186f4332d7b218be0f18dd404c82ef
查看到的提交詳情,主要是提交樹、作者、提交者以及提交message
圖片
通過 tree的記錄值,可以查看本次提交所涉及的所有文件樹。依然是 通過下面的命令查看,后面的一串hash值,就是上圖的 tree 的值。
git cat-file -p f8803d7d049edbbc981da732b855419d039f79ff
得到的結(jié)果如下:
圖片
再用 git cat-file -p 命令可以查看上圖類型為 blob的文件的內(nèi)容。
圖片
日志 (.git/logs)
這里記錄的每一次的提交日志,包括當(dāng)前使用的分支、本地分支、遠(yuǎn)程分支。
|----logs
| |----HEAD
| |----refs
| | |----heads
| | | |----main
| | |----remotes
| | | |----origin
| | | | |----main
以行為單位,每一行都存儲(chǔ)了一次提交的具體信息。
圖片
從左到右分別表示提交之前的hash、提交之后的hash、提交用戶、時(shí)間戳、提交的message。
config (配置)
.git/config 是當(dāng)前倉庫的配置文件,這個(gè)文件,大多數(shù)開發(fā)同學(xué)應(yīng)該還是比較熟悉的。這個(gè)配置只對(duì)當(dāng)前倉庫有效。
了解了前面的內(nèi)容后,再看一下這個(gè)配置文件,應(yīng)該就很好理解了。
[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 (暫存區(qū))
.git/index 文件是 暫存區(qū)。它保存了當(dāng)前倉庫中所有被 git 跟蹤的文件的狀態(tài)信息,包括文件名、文件的權(quán)限和位置等。當(dāng)執(zhí)行g(shù)it add命令時(shí),git 將要提交的文件的快照信息暫存到這個(gè)索引文件中,而不是直接提交到倉庫。
當(dāng)執(zhí)行g(shù)it commit命令時(shí),git 會(huì)將索引文件中的內(nèi)容提交到版本庫中,從而創(chuàng)建一個(gè)新的提交記錄。
hooks (鉤子)
hook 對(duì)于程序員來講肯定不陌生,可以看做是一個(gè)生命周期的事件處理,例如提交代碼后,觸發(fā)某個(gè)動(dòng)作,「提交代碼后」這個(gè)節(jié)點(diǎn)就是個(gè) Hook,在這個(gè)Hook里我們可以加入一些自定義的處理邏輯來達(dá)到某些目的。
例如 pre-commit(在執(zhí)行提交前運(yùn)行)、post-commit(在提交后運(yùn)行)、pre-push(在執(zhí)行推送前運(yùn)行)等。
怎么樣, 學(xué)廢了嗎?