Git 天天用 但是 Git 原理你瞭解嗎?

前言

做技術一定要知其然知其所以然,意思就是:知道它是這樣的,更知道它爲什麼是這樣的。我主要通過4塊內容來簡單介紹 Git 原理是什麼樣的。這4塊內容如下:

  • Git 存儲目錄結構介紹
  • Git 是如何存儲的
  • Git 的對象
  • Git引用

當然 Git 原理不僅僅包含這些,想要更深入瞭解請查看官方教程 https://git-scm.com/book/zh/v2/

本文內容是我在 GitChat 分享關於Git 的Chat 《Git實用操作手冊》 摘抄一個章節,關於《Git實用操作手冊》其他內容請訪問 https://gitbook.cn/gitchat/activity/5cb46e9dd877c443a183f9d4

Git 存儲目錄結構介紹

首先我們先從 Git 存儲目錄說起,通過 git init 創建一個空的 Git 倉庫,具體操作如下圖:

在這裏插入圖片描述

創建完成後進入 .git 目錄,如下圖所示:

在這裏插入圖片描述

  • hooks 該目錄用於配置 客戶端執提交操作用於觸發服務端的腳本配置,一般用於自動化部署使用
  • info 該目錄用於配置一些不希望被 Git 管控的文件。
  • objects 該目錄用於存儲所有數據對象內容,這些數據內容類型有 commit tree blob tag
  • refs 該目錄用於存儲 Git 本地以及遠程分支的引用,當然還有一種特殊的引用標籤引用
  • config 該文件包含項目特有的配置選項,並且該配置僅對該 Git 倉庫有效
  • description 該文件僅供 GitWeb 程序使用
  • HEAD 該文件表示當前 Git 倉庫處於哪個分支
  • index 該文件保存暫存區信息 (空倉庫下該文件不會顯示一旦執行 git add 操作該文件就會出現)

通過 git config --local 查看 config 文件的變化

我們通過 git config --local 配置僅對於 gitLearn 項目有效,用戶名和郵箱配置如下圖所示:

在這裏插入圖片描述

查看 config 文件,會發現該文件新增用戶信息配置。

在這裏插入圖片描述

Git 是如何存儲的

Git 是一個內容尋址文件系統 其核心部分是一個簡單的鍵值對數據庫(key-value data store)。 你可以向該數據庫插入任意類型的內容,它會返回一個 40 位字符串鍵,通過該 40 位字符串鍵可以在任意時刻再次檢索(retrieve)該內容。

什麼是內容尋址?

每次我們進行提交會通過 SHA-1 算法生成一個長度爲 40 個字符的校驗和(checksum hash)(也就是我們的 key)然後根據校驗和去獲取我們文件的內容。這種通過唯一標識的 key(也可以理解爲內容的地址)去獲取我們的內容的操作就是內容尋址

15.4 Git 的對象

在 Git 中有四種對象分別爲:

  • blob 是具體的文件對象
  • tree 是某個時刻提交目錄的內容
  • commit 執行一次 commit 就會產生一個 commit 對象
  • tag 可以理解成 commit 的別名,一個 tag 對應一個 commit

瞭解 Git 的對象需要使用如下命令進行查看:

  • git cat-file -p 對象 hash 值 查看對象的內容
  • git cat-file -t 對象 hash值 查看對象的類型
  • git ls-files --stage 查看 index 文件內容
  • git hash-object 查看文件的 hash 值

查看 commit tree blob 三個對象演示

我們創建一個 first.txt 文件,並將其提交到暫存區中。

在這裏插入圖片描述在這裏插入圖片描述

進入 .git 文件夾下會發現新增了一個 index 文件。

在這裏插入圖片描述

我們可以通過 git ls-files --stage 查看 index 文件的內容。

在這裏插入圖片描述

進入 objects 目錄發現 9c 文件夾名稱+文件名稱 和 index 文件中的一段字符串內容相同。

在這裏插入圖片描述

我們通過 git cat-file -t 9c59e24b8393179a5d712de4f990178df5734d99 我查看該表示對象類型 如下圖所示表示該標識對象類型是 blob

在這裏插入圖片描述

執行 git commit -m 將 first.txt 文件提交到本地倉庫中。

在這裏插入圖片描述

執行 git log 查看我們的提交記錄。

在這裏插入圖片描述

如下圖所示我們通過 git cat-file -p commitId 查看我們提交的內容。如下圖所示:我們最新一次提交包含了一個 59b06 開頭的 tree 對象。

在這裏插入圖片描述

在 ./git/objects 目錄中可以找到我們對應的文件。
在這裏插入圖片描述

我們通過 git cat-file -p tree對象哈希值,查看該 tree 對象的內容 。如下圖所示顯示就是我們 git add參生的 blob對象。

在這裏插入圖片描述

在通過 git cat-file -p blob對象哈希值,查看我們 blob對象內容,如下圖所示 blob對象 內容就是我們 first.txt 文件的內容。

在這裏插入圖片描述

我們將 first.txt 文件提交到本地倉庫 會產生一個 commit 一個 tree 和一個 blob 對象。

tag 對象原理演示

首先我們通過 git log 查看最新的提交是 add a.txt 註釋的 commit 如下圖所示。

在這裏插入圖片描述

通過 git tag -a v1.1 -m ‘add a.txt tag’ 爲該 commit 創建一個附註標籤。

在這裏插入圖片描述

在我們的 .git/refs/tags/ 目錄下會新增 v1.1 文件。

在這裏插入圖片描述
v1.1 內容如下:

在這裏插入圖片描述

看到這個你肯定想到了這是一個 Git 對象,我們通過 git cat-file -t 查看這個哈希值對象類型。如下圖所示它是一個 tag。

在這裏插入圖片描述

然後通過 git cat-file -p 查看它的內容,如下圖所示,該tag包含了commit 對象和 標註的信息。

在這裏插入圖片描述

Git 引用

什麼是 Git 的引用?

這裏的引用我們可以理解成一個書籤,你在閱讀一本書的時候可以爲你讀到的部分打上標籤。然後你可以通過這個書籤快速找到你之前閱讀的位置。在 Git 中我們 創建新的 Commit 或者創建分支都會進行一次打標籤的操作。我們各個不同 commit 之間的切換或分支的切換其實就是標籤的切換。

Git 引用的三種類型

在 Git 中有三種類型的的引用 分別是:

  1. HEAD 引用:用來爲我們的本地倉庫打上標籤使用。
  2. Tag 引用:用來爲我們的 Git 倉庫 tag 標籤使用。
  3. 遠程引用:用來爲我們的遠程倉庫打標籤使用。

HEAD 引用

HEAD 引用原理 我的個人理解是一個 Head 頭指針+當前分支 指向當前最新提交的 commit 對象。我們也可以通過 git reset --hard 來切換我們commit 記錄,切換後的 commit 以前的 commit 記錄就沒有了,不過我們可以通過 git reflog 查詢操作記錄將以前 commit 找回。

爲了更好的理解 HEAD,引用建議大家訪問 http://onlywei.github.io/explain-git-with-d3/#zen 執行 Git 相關的操作來理解什麼是 HEAD引用。

如下圖所示,我們的 Head頭指針分支 指向 master分支 同時指向最新的 commit e137e。

在這裏插入圖片描述
我們執行一次 commit Head頭指針分支 + master分支 就指向的是最新的提交 896ee 上。

在這裏插入圖片描述

通過 git branch b1 創建新的分支名稱爲b1,如下圖所示,我們發現 Head 頭指針還是指向 master 。

在這裏插入圖片描述

當執行 git checkout b1 的時候,如下圖所示,此時 Head 指針指向分支 b1。

在這裏插入圖片描述

我們在分支 b1 上執行 commit Head 指針指向了在該分支下的最新提交 3c7acb。

在這裏插入圖片描述

當執行 git checkout master 是 Head 指針指向了 master 上最新的 commit 896ee。

在這裏插入圖片描述

當執行 git merge b1 的時候,會將 b1分支最新 commit 合併到 master,我們 Head指針 + master 同時指向最新的 commit 3c7ba 上。

在這裏插入圖片描述

通過上面的動態演示,這回對 Head 應用如何切換 commit 切換分支有了一定的理解。

這裏非常建議你自己通過訪問
http://onlywei.github.io/explain-git-with-d3/#zen
來親自體驗一下。

接下來我們來通過查看 .git 目錄來介紹 Head 指針是如何實現的。進入 .git 目錄你會發現有一個 HEAD 文件,它的內容是 ref: refs/heads/master,如下圖所示。

在這裏插入圖片描述
在這裏插入圖片描述

refs/heads/master 是具體文件路徑,我們查 refs/heads 目錄下的 master 文件內容,如下圖所示,master 文件內容就是我們最新的 commit。

在這裏插入圖片描述

在這裏插入圖片描述

在這裏插入圖片描述

我們執行 git chekcout demoBranch3。

在這裏插入圖片描述
在這裏插入圖片描述

此時我們的 HEAD 文件內容變成了 ref: refs/heads/demoBranch3。

在這裏插入圖片描述

查看 refs/heads/ 路徑下的 demoBranch3 文件,如下圖所示 demoBranch3 文件記錄是demoBranch3 分支下最新的 commit。

在這裏插入圖片描述

在這裏插入圖片描述

標籤引用

標籤引用是一個特殊的引用,它不像 HEAD 引用可以通過 git checkout 來更改引用的指針指向。標籤引用不會移動它永遠會指向一個 Commit 對象。

標籤引用的演示請參看 Tag 對象原理演示部分

遠程引用

  1. 遠程引用是隻讀的我們不能通過切換到遠程引用執行 commit 將提交更新到遠程倉庫中

  2. 我們在本地分支執行 push 操作,Git 都會幫我們記錄 push 到遠程分支的最新 comit,當執行push 的時候,如果發現遠程分支最新 commit 和我們本地倉庫記錄最後一次 push 的 commit 不同會報 Note about fast-forwards 異常,如下圖所示:

在這裏插入圖片描述

出現 Note about fast-forwards 我們需要執行 git pull 將遠程最新的 commit 拉取下來 然後再執行 git push 操作 或者 直接執行 git puhs -f。這裏強調一下不建議執行 git puhs -f 操作因爲會強制將本地的歷史記錄覆蓋到遠程倉庫的歷史記錄。

通過查看 .git 目錄理解遠程引用

在 .git/refs/remotes/origin 文件夾中 HEAD 文件內容表示內容表示遠程分支處於哪個分支。

在這裏插入圖片描述

在 .git/refs/remotes/origin 文件夾中 master 文件內容,表示最後一次 push 到遠程倉庫的commit,如下圖所示:

在這裏插入圖片描述
在這裏插入圖片描述

在 .git/refs/remotes/origin 文件夾中 demoBranch1 文件表示,最後一次 push 到 demoBranch1 分支提交的 commitId。

在這裏插入圖片描述
在這裏插入圖片描述

參考文獻

Git 官方教程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章