內容標題
四大物件
git 裡有四個很重要的物件,分別是 blob、tree、commit、tag。但我要分享的內容暫且不包括的 tag 物件。如果要深入討論的話,我們首先要到之前介紹過的 .git 目錄!首先,我們先創立一個新的資料夾並輸入 git init
來初始化。接著我們要進到 .git 這個目錄。在進入之前,我們先用 ls
來看 .git 有沒有成功創立。
$ ls
結果卻看不到任何東西!原因是 .git 目錄是隱藏檔,所以使用者一般是看不到的。如果我們想要看到這個檔案的話,要用到我們之前講到的 [option]。指令如下:
$ ls -a
其中 -a
代表的是 all,這時就會列出所有檔案,包括 隱藏檔。這時就會看到 .git 目錄了。現在,我們進入到 git 目錄:
$ cd .git
這裡我直接開資料夾給大家看
這個資料夾裡有一個 objects 資料夾,是存放我們檔案資訊的地方,等等我們要進行的東西都會在這裡面!
blob 和 tree
我們可以看到 objects 目錄裡面只有 info 跟 pack,因為我們還沒 commit 或 add 任何東西進去。此時我們在 新資料夾 創立一個 test.cpp。我們先 add 這個檔案。接著我們回到 .git 的 objects 目錄,會發現資料夾底下多一個目錄同時底下還有一個檔案。
我們可以發現這個資料夾和其檔案的名字都很奇怪。事實上,兩個名字合起來就是我們之前提到的 SHA-1 (讀者和我的可能會有不同,隨檔案內容和其他因素而變!)。現在我們要用這個 SHA-1 來看看更多內容。我們先介紹一個新的指令:
$ git cat-file [option] [SHA-1]
這個指令可以用來查看 SHA-1的更多資訊,其中 -t
可以用來查看 檔案的種類,-p
可以用來查看 檔案的內容。我們直接操作一次!
$ git cat-file -t 7a8f31216c572273b492d941f359aba22d203a92
blob
結果顯示這個檔案的種類是 blob 物件。
$ git cat-file -p 7a8f31216c572273b492d941f359aba22d203a92
std::cout << Hello World << std::endl;
內容則是 std::cout << Hello World << std::endl;。接著我們在原資料夾創立一個目錄叫 hello 且目錄底下有一個 test.py 檔。同樣將檔案 test.py 加到 git裡:
$ git add hello/test.py
其中 hello/
代表在 hello 的目錄底下。此時我們在 .git/objects 裡確實有多了一個目錄!
做完這些後我們來 commit 吧!這時我們發現,除了 7a 跟 b3 外,多了 3d,95 跟 f0。我們先來看看 f0:
$ git cat-file -t f0aaab3baacbfe0ac7c1b8fc0ec3655c68fca405
tree
我們發現這是一個新物件 tree。我們接著看一下他的內容:
$ git cat-file -p f0aaab3baacbfe0ac7c1b8fc0ec3655c68fca405
040000 tree 3dd16ef17d4831946a45d03d7069524613f9c964 hello
100644 blob 7a8f31216c572273b492d941f359aba22d203a92 test.cpp
我們可以看到這個 tree 物件連著兩個物件,第一個是檔案資料夾 hello,另一個是檔案是 test.cpp。我們又知道,檔案是 blob 物件,現在我們來看看資料夾 hello 是什麼:
$ git cat-file -t 3dd16ef17d4831946a45d03d7069524613f9c964
tree
我們發現 hello 在 git 裡是一個 tree 物件!他的內容是:
$ git cat-file -p 3dd16ef17d4831946a45d03d7069524613f9c964
100644 blob b376c9941fda362c8d2c5c8ddb35db3e0b003402 test.py
指向了資料夾裡面的 test.py!
我們可以從上面做一個小結論,tree 物件會指向 blob 物件和 tree 物件。另外,當我們 commit 的時候,git 會為原目錄做一個 tree 物件,我們暫時稱它為 原目錄 tree,他指向目錄裡的東西!這裡的角色是 f0。
這裡我做了一個簡單的圖讓大家更清楚檔案的關係,其中 藍色 代表 tree,黃色 代表 blob。
commit
剛剛我們還留了一個 95 沒看,現在我們來看一下:
$ git cat-file -t 9536cf16258f9d5451f735137e3679c8e8177cd7
commit
這是一個 commit 物件,我們接著看他的內容:
$ git cat-file -p 9536cf16258f9d5451f735137e3679c8e8177cd7
tree f0aaab3baacbfe0ac7c1b8fc0ec3655c68fca405
author Justin <justin900429@gmail.com> 1581931210 +0800
committer Justin <justin900429@gmail.com> 1581931210 +0800
First commit
首先,他連結到 tree 物件,也顯示時間,作者資訊以及紀錄。也就是我們第一次的commit!所以目前的完整圖是:
之後的 commit
今天的最後一部分就是要來看一下之後的 commit 會變成什麼樣子!這裡我新增一個 test.js 並修改 test.cpp 的內容。之後 add 這些物件。這裡我輸入 git status
查看狀態,他會提示我們還沒 commit 進去!(我們之前有提過,但還沒示範 XD)
接著我們同樣 commit,並查看 .git/objects 的檔案有什麼變化:
這裡多了 1d、27、55 和 e4。這裡我們就不一個一個看了,1d 是一個 tree 物件,指向 hello、test.js 和 test.cpp。但有一點要注意!因為我們剛剛改過 test.cpp 的內容,所以 git 會創立新的 SHA-1,因此新的 test.cpp 是由 e4 所代表,不再是 7a。另外,55 是我們新的 test.js。接著我們來看最後的 27:
$ git cat-file -p 27f1e3e8264a97374afa1a78613a0274e6d76929
tree 1d5aee817d6fb6d2ec393997e564e7f50b7e9593
parent 9536cf16258f9d5451f735137e3679c8e8177cd7
author Justin <justin900429@gmail.com> 1582014575 +0800
committer Justin <justin900429@gmail.com> 1582014575 +0800
Modified test.cpp and create test.js
這是一個 commit物件,指向了 1d 這個目錄 tree。特別的是有一個 parent,那是我們前一個 commit!所以我們又可以知道新的 commit 會指向舊的 commit。因此我們現在的圖應該是,其中 ./ 代表的是當前目錄 (原目錄):
小總結
今天我們討論了 commit 的真面目,希望這個部分可以讓更多人理解 commit 是怎麼運作的!明天我們會講解在不同的 commit 間切換,以及介紹分支。
參考資料
- 為你自己學 Git [高見龍 著]
- missing semester lectures from MIT