本文首發於公衆號“AntDream”,歡迎微信搜索“AntDream”或掃描文章底部二維碼關注,和我一起每天進步一點點
Git系列
- Git原理之時光機和分佈式
- Git的三個工作區和文件的三種狀態
- Git branch 命令 分支顯示不全
- Git倉庫目錄探祕
- Git是如何保存文件名和目錄關係的---樹對象
- Git是如何保存和記錄數據的——數據對象
提交對象
一般我們平時有了需要提交的文件,都是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
命令創建了一個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
命令中的第二個參數---父提交對象,每個提交對象都保存了上一個父提交對象的引用,這樣就串起來一個提交歷史記錄了。
歡迎關注我的公衆號查看更多精彩文章!