『現學現忘』Git對象 — 17、Commit對象

1、Commit對象介紹

現在來介紹最後一種Git對象commit對象,也叫提交對象。

提交對象可以理解為是對樹對象的一層封裝,提交信息包括基於當前暫存區中索引文件生成的tree對象,還有包含了提交時間,提交者信息,作者信息,以及提交備註等內容,更重要的是裏面還包含了父提交的ID,由此就可以形成Git提交的有向無環圖。(是鏈式的關係,把所有commit對象關聯起來)

即:commit對象通常指向一個 tree 對象,並且封裝了文件的提交時間,提交者信息,作者信息,提交備註,以及父提交引用等數據。

下面是commit對象的存儲結構:

image

2、Commit對象說明

我們通過練習來說明commit對象,接着用前面Tree對象的本地版本庫。

(1)創建一個commit對象

我們可以通過調用commit-tree命令創建一個提交對象,為此需要指定一個樹對象的SHA-1值,以及該提交的父提交對象。

說明:使用commit-tree命令來創建提交對象,一般都需要和父提交進行關聯,如果是第一次將暫存區的文件索引數據提交到本地版本庫,那麼該提交操作就不需要指定父提交對象。

1)我們可以先查看一下此時Git本地庫中的對象,如下

.git/objects/01/ab2a43b1eb150bcf00f375800727df240cf653 # 第三個tree樹對象
.git/objects/0c/1e7391ca4e59584f8b773ecdbbb9467eba1547 # test.txt第二個版本(blob對象)
.git/objects/16/3b45f0a0925b0655da232ea8a4188ccec615f5 # 第二個tree樹對象
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # test.txt第一個版本(blob對象)
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # 第一個tree樹對象
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # new.txt第一個版本(blob對象)

2)我們通過第一個樹對象,創建一個commit對象

# 1.做提交操作,創建一個commit對象
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ echo 'first commit' | git commit-tree d8329f
3ceba95d3cd9cce982d31e41e3b995ece72f755d

# 2.確定該對象類型
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ git cat-file -t 3ceba95d3c
commit

# 3.查看該對象內容
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ git cat-file -p 3ceba95d3c
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author sun_wk <[email protected]> 1618190880 +0800
committer sun_wk <[email protected]> 1618190880 +0800

first commit

說明:

  • tree:表示該commit對象所指向的tree對象的索引
  • author:表示該文件的作者。
  • committer:表示該文件的提交者。
  • first commit:這段文本是提交備註。(備註與前面留空一行)
  • 因為是第一次進行commit提交操作,所以沒有父提交信息。
  • 1618190880 +0800:表示時間,一個時間戳。

即:commit對象的格式很簡單:指明了該時間點項目快照的頂層樹對象、作者/提交者信息(從 Git 設置的 user.nameuser.email中獲得),以及當前時間戳、留空一行,最後是提交注釋。

提示:git commit-tree命令不但生成了提交對象,而且會將對應的快照(樹對象)提交到本地庫中。

(2)創建第二個commit對象

根據第二個tree對象和第一個commit對象,來創建第二個commit對象。

通過-p選項指定父提交對象。

# 1.創建第二個commit對象
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ echo 'second commit' | git commit-tree 163b45f0a09 -p 3ceba95d3cd9cc
60e1c209e9de87314ec47cf28e61de8df5362fe6

# 2.查看該對象內容
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/git_learning (master)
$ git cat-file -p 60e1c209e9de8
tree 163b45f0a0925b0655da232ea8a4188ccec615f5
parent 3ceba95d3cd9cce982d31e41e3b995ece72f755d
author sun_wk <[email protected]> 1618193286 +0800
committer sun_wk <[email protected]> 1618193286 +0800

second commit

提交對象的格式很簡單:

它先指定一個頂層樹對象,代表當前項目快照;

然後是可能存在的父提交;

之後是作者/提交者信息(依據你的 user.nameuser.email 配置來設定,外加一個時間戳);

留空一行,最後是提交注釋。

第三個commit提交,同上,這裡就不演示了。

3、本地庫中對象之間的關係

我們可以查看一下此時Git本地庫中的對象

.git/objects/01/ab2a43b1eb150bcf00f375800727df240cf653 # 第三個tree樹對象
.git/objects/0c/1e7391ca4e59584f8b773ecdbbb9467eba1547 # test.txt第二個版本(blob對象)
.git/objects/16/3b45f0a0925b0655da232ea8a4188ccec615f5 # 第二個tree樹對象
.git/objects/3c/eba95d3cd9cce982d31e41e3b995ece72f755d # 第一個commit提交對象
.git/objects/46/ab608799a0e65e970b67b9b52f6c1407c39036 # 第三個commit提交對象
.git/objects/60/e1c209e9de87314ec47cf28e61de8df5362fe6 # 第二個commit提交對象
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # test.txt第一個版本(blob對象)
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # 第一個tree樹對象
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # new.txt第一個版本(blob對象)

可以從上面看到,此時的本地版本庫中共有9個對象,三個blob對象,三個tree對象,三個commit對象。

他們之間的關係如下圖:

image

4、總結

  1. 提交是我們經常使用的Git動作,每次提交操作都指向一個樹對象,同時會產生一個commit對象。
    即:一個commit對象包含了一個tree對象,這個tree對象記錄了在那個時間點,項目包含了什麼文件夾和什麼文件。
  2. 一個提交對象可以有一個或者多個父提交。
  3. 每次commit操作都會基於當前索引文件index新建tree對象。那麼當前索引文件,是在上次提交的基礎上更新來的,所以每次提交產生的commit對象,與其他的commit對象,都有前後關係或者稱為父子關係。
  4. 對於我們來說,不需要直接訪問blob對象和tree對象,我們直接訪問commit對象就可以了。
    即:commit對象對應的tree對象下面,又包含了小的tree對象和blob對象,子的tree對象一層層展開,最後葉子節點就是一個個blob對象,也就是一個個文件。

到這裡,我們就能夠清楚的了解,什麼叫一個Git版本。tree對象才是一次項目版本的快照,提交對象是對tree對象的一次封裝。

即:

  • 項目的快照就是一個樹對象。
  • 項目的版本就是一個提交對象。

而且Git的每一個版本,存儲的不是增量,而存儲的是當前項目的快照。同時objects目錄中相當於存放了項目的所有歷史記錄,回滾就相當的方便了,找到對應的commit對象的hash就可以了。

5、練習

請問下圖中包含多少個tree對象和blob對象?

image

一共包含兩個tree對象,一個blob對象,一個commit對象。

說明:

  • 一個commit對象一定對應一個tree對象(這個tree對象應該是一個完整項目倉庫的快照)
  • doc目錄下有一個blob對象,也就是readme文件。

6、本文用到的命令總結

Git底層命令:

  • git commit-tree:生成一個commit對象。
  • git cat-file -t 鍵:查看Git對象的類型。
  • git cat-file -p 鍵:查看Git對象的內容。

參考: