天天看點

commit時Git都幹了些啥?--- 送出對象

本文首發于公衆号“AntDream”,歡迎微信搜尋“AntDream”或掃描文章底部二維碼關注,和我一起每天進步一點點

送出對象

一般我們平時有了需要送出的檔案,都是2步走:add,然後commit

add操作

第一步:添加檔案

//添加檔案到暫存區
git add test.txt           

這一步Git做了2件事:

  • 将檔案的内容用之前資料對象一節中提到的方法建立資料對象并儲存到Git資料庫中(計算SHA-1值、生成檔案目錄、寫入壓縮後的内容)
  • 更新 Index檔案,也就是我們平時說的 暫存區,增加或是更新指向

    text.txt

    檔案的索引,等待後續的第二步操作
commit:建立送出記錄
//送出到Git本地倉庫
git commit -m "XXX"           

這個步驟是建立了一個送出對象,送出對象裡面就記錄了送出的時間、作者、以及送出的原因等資訊。

上述

git commit

指令做了以下幾件事:

  • 首先所有具體檔案的資料,已經在

    add

    操作時用資料對象記錄在Git資料庫中,并且所有檔案的索引都儲存在暫存區中,是以

    commit

    操作就不用再建立資料對象了
  • 如果暫存區中存在目錄關系,就會先建立樹對象來記錄檔案目錄關系,這樣檔案資料和目錄關系都有了記錄
  • 然後會再建立一個樹對象,代表目前項目快照,這個樹對象裡面包含的就是上述資訊,也就是所有要儲存記錄的資料
  • 然後用這個樹對象,配置中的user.name和email,以及目前的時間戳和

    -m

    參數後面的内容生成送出對象

我們可以用

git log

指令,檢視送出的曆史記錄,就能拿到

commit

的 SHA-1值(也就是我們平時說的commit id)

然後用

git cat-file

指令就能檢視這個送出裡面的資訊

git cat-file -p xxxxxxx0ab25ce7b1c6019c411e760a17205d7b0
//輸出
tree 9bfb857f532d280ecd7704beb40a2ea4ba332f5a
author xxx <[email protected]> 1561964726 +0800
committer xxx <[email protected]> 1561964726 +0800

first commit           

可以看到,

commit

裡面确實是有一個樹的索引,這個樹對象就是前面建立的頂層樹對象。

此時,有個疑問:已經用暫存區的内容建立了送出對象,那暫存區的内容還在嗎?

答案是:仍然存在

Git在執行commit指令時會根據暫存區建立樹對象,暫存區沒變,建立的樹對象就是同一個,也就是不會重複建立。

最後我們看看commit對象的一個示意圖

commit時Git都幹了些啥?--- 送出對象
commit曆史記錄

上面我們用

git commit

指令建立了一個commit對象,實際上底層調用的是

commit-tree

指令。

commit-tree

指令裡面需要确定2個參數:一個是需要指定一個樹對象,第二個需要指定上一個送出對象(作為父送出對象)

// -p 後面的就是上一個送出對象
echo 'second commit' | git commit-tree 0155eb -p fdf4fc3           

下面我們來模拟連續送出3次

echo 'version 1' > test.txt
git add .
git write-tree
//輸出樹對象SHA-1值
d8329fc1cc938780ffdd9f94e0d364e0ea74f579

//建立第一個送出對象
echo 'first commit' | git commit-tree d8329fc
//輸出送出對象的SHA-1值
xxxxxxxc670d30e9817fd1af481aca92f8d700c2

//然後添加新檔案,建立新的樹對象
echo 'version 2' > test.txt 
echo 'new file' > new.txt
git add .
git write-tree 
//輸出新的樹對象的SHA-1值
0155eb4229851634a0f03eb265b69f5a2d56f341

//接着建立新的送出對象
echo 'second commit' | git commit-tree 0155eb -p 9f17fcc
//輸出新送出對象的SHA-1值
xxxxxxx9f57c7a0632084e2c9eb38e3584180e23


//先把之前的第一版的test.txt讀入暫存區作為子樹
git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579
git write-tree
//輸出新樹的SHA-1值
3c4e9cd789d88d8d89c1073707c3585e41b0e614

//接着建立新的送出對象
echo 'third commit' | git commit-tree 3c4e9c -p 8565637
//輸出第3次送出的SHA-1值
xxxxxxx747d9e83f3c05d10cbc68f876a8abf0d1           

以上我們用 git的底層指令模拟了實際的三次 git commit操作,我們來看一下成果:

//--stat參數可以讓git顯示每一次送出改動的檔案資訊
git log --stat f32da7d           

通過以上的步驟我們就從底層知道了我們每次

git add

git commit

的過程,同時我們知道,每一次我們

commit

的時候,都會記錄上一個

commit

的 SHA-1值,這樣一個個的

commit

就串起了我們的送出記錄。

那麼問題來了,Git是怎麼知道新

commit

對象的上一個

commit

對象的呢?實際上就是上面

commit-tree

指令中的第二個參數---父送出對象,每個送出對象都儲存了上一個父送出對象的引用,這樣就串起來一個送出曆史記錄了。

歡迎關注我的公衆号檢視更多精彩文章!