Git/內部結構
瞭解 Git 的內部結構對於理解 Git 的工作原理至關重要。
以下是一個新初始化的 git v1.9.0 倉庫。[1]
.
└── .git/
├── HEAD
├── branches/
├── config
├── description
├── hooks/
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ └── update.sample
├── info/
│ └── exclude
├── objects/
│ ├── info/
│ └── pack/
└── refs/
├── heads/
└── tags/
隨著倉庫活動,可能會出現其他檔案和資料夾。
文字編輯器會將提交訊息儲存到這裡。
這裡儲存了最後一次 git-fetch(1) 操作的資訊,供之後 git-merge(1) 操作使用。
HEAD 指示當前檢出的程式碼。它通常會指向您當前正在工作的分支。
您還可以進入 Git 所謂的“分離 HEAD”狀態,在這種狀態下,您不在本地分支上。在這種狀態下,HEAD 直接指向提交而不是分支。
此 Git 倉庫的配置檔案。它可以包含有關如何在本地倉庫中管理和儲存資料的設定、已知的遠端倉庫、有關本地使用者的詳細資訊以及其他有關 Git 本身的配置資料。
您可以使用文字編輯器編輯此檔案,也可以使用 git-config(1) 命令進行管理。
由倉庫瀏覽器工具使用 - 包含有關此專案的描述。在非共享倉庫中通常不會更改。
這是暫存區。它以緊湊的形式包含已暫存到下次提交的所有檔案更改。
這是您自己的個人排除檔案,用於您的倉庫副本。
如果此檔案存在,它將包含分支(本地和遠端)以及為倉庫定義的標籤的定義,每行一個定義,除此之外,還可能在以下檔案中定義。refs/heads和refs/tags. 此檔案似乎用於包含大量分支或標籤的大型倉庫。
更改當前分支上的提交歷史記錄的操作會將 HEAD 的先前值儲存到這裡,以便從錯誤中恢復。
似乎從不使用。
包含在 Git 倉庫中發生特定事件時執行的指令碼。Git 提供了一組初始示例指令碼,這些指令碼的名稱末尾帶有.sample(請參閱上面的樹列表);如果您刪除了.sample字尾,Git 將在適當的時候執行該指令碼。
例如,鉤子可用於在建立每個提交之前執行測試、過濾上傳的內容以及實現其他此類自定義要求。
Reflogs 儲存在這裡。
這是所有檔案、目錄列表、提交等儲存的地方。
這裡既有此目錄下編號目錄中的未打包物件,也有“包”,這些包包含壓縮在包目錄中的許多壓縮物件。未壓縮物件將定期透過自動“git gc”執行收集到包中。
可以包含一個檔案,定義每個本地分支的頭部提交(但請參見info/refs上面)。
可以包含一個子目錄,用於每個已定義的遠端倉庫。在每個子目錄中,都包含一個檔案,定義該遠端倉庫上每個分支的尖端提交。
可以包含一個檔案,定義與每個標籤對應的提交(但請參見info/refs上面)。
如果您使用 git-svn(1) 與 Subversion 伺服器通訊,則此目錄將出現。
Git 倉庫由以下物件型別組成
- 一個 blob 儲存單個檔案的全部內容。它不儲存有關檔名或任何其他元資料的任何資訊,只儲存內容。
- 一個 tree 表示目錄樹的狀態。它包含所有元件檔案的路徑名及其模式,以及儲存其內容的 blob 的 ID。請注意,沒有單獨表示目錄,因此 Git 倉庫無法記錄建立或刪除子目錄的事實,只能記錄其中的檔案。
- 一個 commit 指向一個樹,表示該提交之後立即源樹的狀態。它還記錄提交的日期/時間、作者/提交者資訊,以及指向該提交的任何父提交的指標,表示源樹的立即之前狀態。
- 一個 tag 是一個指向提交的名稱。這些很有用,例如,用來標記發行版里程碑。標籤可以選擇進行數字簽名,以保證提交的真實性。
- 一個 branch 是一個指向提交的名稱。分支和標籤之間的區別在於,當一個分支是當前簽出的分支時,新增一個新的提交將自動更新分支指標以指向新的提交。
Blob、樹和提交都有 ID,這些 ID 是根據其內容的 SHA-1 雜湊計算得出的。這些 ID 允許不同機器上的不同 Git 程序判斷它們是否具有相同副本,而無需傳輸其全部內容。由於 SHA-1 是一種密碼學上強健的雜湊演算法,因此幾乎不可能對任何這些物件的內容進行更改而不會更改其 ID。Git 不會阻止您重寫歷史記錄,但您無法隱藏您已經重寫歷史記錄的事實。
一個提交可能具有 0、1 或多個父提交。通常只有一個沒有父提交的提交——一個 root commit——這是倉庫中的第一個提交。對某個分支進行一些更改的提交將只有一個父提交,即該分支上的前一個提交。從兩個或多個分支合併的提交將有兩個或多個父提交。
請注意,一個分支指向一個 單個 提交;提交鏈隱含在該提交的父提交中,以及它們的父提交,等等。
Git 中的提交歷史記錄被組織成一個 有向無環圖 (DAG)。為了理解這意味著什麼,讓我們逐步解釋這些術語。

- 在數學術語中,一個 圖 是由連線線 (邊) 連線的點 (節點) 組成的。

- 一個 有向 圖是指每個邊都有一個方向的圖,這裡用箭頭表示。請注意,箭頭從子節點指向父節點,而不是反過來;是子節點記錄了其父節點是誰,父節點沒有記錄其子節點是誰,因為子節點集合可以隨時更改,但父節點不能更改,否則會導致其 SHA-1 雜湊無效。

- 無環 指的是,如果您從任意一點開始,沿著箭頭方向遍歷邊,無論您在任何分支處做出何種選擇,您都永遠無法回到起點,任何子節點都永遠不能是它自己的(直接或間接)父節點!
在 Git 術語中,每個節點代表一個提交,線和箭頭代表父子關係。禁止迴圈僅僅意味著一個提交不能是它自己(直接或間接)的父節點或子節點!
Reflog 記錄了未作為提交歷史記錄一部分儲存的更改——比如變基、快進合併、重置等等。每個分支都有一個 reflog。Reflog 不是倉庫的公共部分,它嚴格地特定於您的本地副本,並且資訊只在其中保留有限的時間(預設情況下為 2 周)。它提供了一個安全網,允許您從錯誤中恢復,比如刪除或覆蓋您不想刪除或覆蓋的東西。
如果一個提交被分支、標籤或 reflog 條目指向,或者是一個可達提交的父提交,則該提交是 可達 的。相應地,如果一個樹被可達提交指向,則該樹是可達的;如果一個 blob 被可達樹指向,則該 blob 是可達的。其他提交/樹/blob 物件是 不可達 的,除了佔用空間之外,它們實際上沒有起到任何作用。
您的倉庫隨著時間的推移積累不可達物件是完全正常的,這可能是由於中止提交、刪除不需要的分支、類似的事情造成的。這樣的物件將被 git gc 命令從倉庫中刪除。這也會在一些其他命令執行時定期自動執行,因此很少需要顯式呼叫 git gc。
放在目錄中,它保證它會被提交,即使是空的。
包含要從版本控制中排除的檔案和資料夾。例如
/var/ /vendor/ /.env.*.local
包含一些屬性[1]。例如,要忽略 .gitattributes 和 .gitignore 在 "git archive" 匯出中的內容
.gitattributes export-ignore .gitignore export-ignore
- ^ 使用 tree v1.5.1.1 透過
tree -AnaF生成的。