2、Git內部原理

1 前言

  • Git使用比較靈活,達到相同結果有多種方式。
  • 靠記憶不一樣場景下的命令組合,會停留在「知其然,不知其因此然」的層次。
  • 只有理解Git內部原理和Git命令的底層操做,才能深刻淺出的靈活運用Git。

2 Git內部原理

Git是由C語言開發的一套內容尋址文件系統,並在此之上提供了一個VCS用戶界面。node

2.1 Git目錄結構

使用git init命令初始化當前目錄,生成**.git**文件夾。git

git init 初始化

1. 工做區、暫存區和Git倉庫

  • 工做區是當前目錄(除去.git/),全部的編輯操做都在該目錄進行。
  • 暫存區對應.git/index文件,它包含了當前暫存區的信息,由它可生成git的tree對象。(git init執行後並無產生.git/index,而是在首次執行git add命令後才生成,並把由更新文件生成的blob對象放入**.git/objects/**內。)
    git add生成.git/index
  • git倉庫對應.git/,它存儲了項目的全部歷史快照,以供須要的時候使用。

2. .git/目錄

.git/包含了如下目錄和文件:github

  • branches/:新版本再也不使用
  • description:僅供GitWeb程序使用
  • config:當前項目的配置選項
  • info/:不一樣於.gitignore文件,可配置本地的文件忽略模式,不會push到remote庫而影響其餘人。
  • hooks/:目錄存放鉤子腳本
  • objects/:目錄存儲全部數據內容
  • refs/:目錄存儲指向數據的commit對象的指針
  • HEAD:文件內容爲當前分支
  • index:文件內容爲暫存區的信息

2.2 Git命令

Git包含底層命令(Plumbing)和高層命令(Procelain)。shell

  1. 用戶平時使用的Git命令通常爲高層命令,如add、commit、checkout等;高層命令對用戶友好,便於理解和操做。
  2. Git起初被設計爲供VCS使用的工具集,這些工具也稱爲底層命令;底層命令通常不被用戶直接使用,而是被shell或腳本調用。
    Git部分底層命令
    此處列舉幾個底層命令簡要說明:
    • checkout-index:Copy files from the index to the working tree.
    • cat-file:Provide content or type and size information for repository objects.
    • hash-object:Compute object ID and optionally creates a blob from a file.
    • update-index:Register file contents in the working tree to the index.
    • write-tree:Create a tree object from the current index.
    • commit-tree:Create a new commit object.

2.3 Git對象

Git定義了4種對象:blob、tree、commit和tag,它們都位於**.git/objects/**目錄下。git對象在原文件的基礎上增長了一個頭部,即對象內容 = 對象頭 + 文件內容。這種格式沒法直接經過cat命令讀取,須要使用git cat-file這個底層命令才能正確讀取。 對象頭的格式爲:對象頭 = 對象類型 + 空格 + 數據內容長度 + null byte,例如一個文件內容爲「hello world」,其blob對象頭爲"blob 11\000"。bash

git cat-file可讀取git的4種對象

  • blob:工做區的文件以blob對象的形式進入git倉庫,至關於UNIX中的inodes或文件內容。
  • tree:tree對象包含對blob對象以及其餘tree對象的引用,至關於UNIX中的目錄。
  • commit:包含了上一次commit對象的Hash串引用、該時間點項目快照的頂層tree對象的Hash串引用、做者/提交者信息、時間戳、空行,以及提交的註釋信息。
    commit、tree、blob的引用關係
  • tag:包含一個commit的Hash串引用、標籤名,以及其餘信息(由標籤類型決定)。

2.4 內容尋址

  1. 依賴底層命令git hash-object命令,對文件內容增長頭信息後計算hash值並返回,增長-w參數後在git倉庫內建立blob對象(blob對象 = 對象頭 + 文件內容)。
  2. blob對象存儲到git倉庫目錄(.git/objects/)時,依據40位(16進制字符)長度的hash串指定存儲目錄(hash串前2位)和命名文件(hash串後38位)。例如某blob對象的hash值爲62/0d4582bfbf773ef15f9b52ac434906a3cdf9c3,那麼它在git倉庫中的路徑爲.git/objects/62/0d4582bfbf773ef15f9b52ac434906a3cdf9c3
  3. Git內容尋址本質是:Git根據由文件內容(增長文件頭)產生的Hash值來標識和索引文件,另外進行命令操做時沒有必要寫完整的hash串,只要輸入的hash串長度是惟一可識別和索引的便可。
    根據文件內容的hash值索引文件
  4. 無需考慮Hash碰撞的狀況,在大型項目上也能夠放心使用Git。由於在機率上SHA-1產生的哈希值碰撞的機會能夠小到忽略。

2.5 Git版本機制

  1. HEAD指向當前分支。若master是當前分支,則HEAD文件內容爲ref: refs/heads/master
    HEAD指向當前分支
  2. 分支(本地分支、遠程分支、遠程跟蹤分支、跟蹤分支)和標籤(tag對象)都包含了對commit對象的引用。
    master分支引用了hash爲1ad0的commit對象
  3. commit對象包含了上次commit對象的引用(相似單鏈表)和本次提交的頂級tree對象的引用。
    commit對象引用了tree對象
    • 每一個頂級tree對象可看作是一個完整的版本。
    • 經過commit對象的鏈式結構進行串聯,造成提交歷史和版本歷史。
  4. 總之:git的分支和標籤經過引用commit對象來標註當前分支的版本信息。
    git的版本歷史機制
    Note:凡是對Git對象的引用,都指的是Git對象的40位長度的Hash串。

2.6 引用規格(refspec)

引用規格指的是遠程倉庫分支和本地分支的映射,可表示爲<src>:<dst>,這也暗示了數據流向爲src → dstide

2.6.1. fetch和push命令

# 兩命令都包含引用規格(refspec)來指定數據流向。
git fetch [remote repository] [remote branch]:[local branch]
git push [remote repository] [local branch]:[remote branch]
複製代碼

2.6.2 config文件配置refspec

當使用缺省的fetch/push命令時,Git會根據.git/config中的refspec配置進行操做。工具

2.6.2.1 當經過git remote add命令添加一個遠程分支的同時,會在.git/config文件中添加一個配置結點。
git remote add
fetch中的」+「是可選的,它告訴Git在不能fast-forward的狀況下,也強制去更新它。此後執行git fetch orgin這個缺省命令時,會拉取origin遠程倉庫的全部分支。
2.6.2.2 可經過git log origin/master來查看從遠程倉庫fetch的master分支。
# 如下三個命令是等價的,Git會把他們都擴展爲refs/remote/origin/master
git log origin/master
git log remote/origin/master
git log refs/remote/origin/master
複製代碼
2.6.2.3 refspec指定分支映射

1)可經過改寫fetch行爲fetch = +refs/heads/master:refs/remotes/origin/mymaster,指定把遠程的master分支映射爲本地的origin/mymaster分支。 2)也可指定多個映射,一次拉取多個指定分支。fetch

[remote "origin"]
      url = git@github.com:kivihub/test.git
      fetch = +refs/heads/master:refs/remotes/origin/master
      fetch = +refs/heads/experiment:refs/remotes/origin/experiment
      fetch = +refs/heads/qa/*:refs/remote/orgin/qa/*
複製代碼

3)可同時指定push的refspec
若要把本地的master分支push到遠程的qa/master分支,可配置以下:ui

[remote "origin"]
      url = git@github.com:kivihub/test.git
      fetch = +refs/heads/master:refs/remotes/origin/master
      fetch = +refs/heads/experiment:refs/remotes/origin/experiment
      fetch = +refs/heads/qa/*:refs/remote/orgin/qa/*
      push = refs/heads/master:refs/heads/qa/master 
複製代碼

4)刪除遠程分支
經過命令git push origin :master能夠刪除遠程origin庫的master分支。由於refspec的格式爲:,經過把置空表示把遠程分支變爲空,也就是刪除它。url

2.7 其餘

  1. git gc垃圾回收命令用於壓縮或刪除數據,節省磁盤空間。
    • 將鬆散對象進行打包存入packfile。
    • 將不被任何commit引用而且已存在一段時間(數月)的對象刪除。
參考文章
  1. GIT科普系列5:index in git
  2. 《Pro Git》相關章節內容
  3. Git命令列表
  4. .gitignore和exclude
相關文章
相關標籤/搜索