iT邦幫忙

2025 iThome 鐵人賽

DAY 10
0
Software Development

深入一點點認識 Git系列 第 10

Day 10-深入一點認識 Git:git add 之後,.git/ 資料夾發生了什麼變化?

  • 分享至 

  • xImage
  •  

在 Day 5 文章中,我們知道要形成一個 commit 前,要先下 git add 指令把檔案放到預存區(staging area)。

所以預存區是什麼?為什麼不直接 commit 就好?

預存區(Staging Area)是什麼?

根據 git 官方文件,預存區的存在是要讓使用者可以不必每次都把所有修改過的檔案都放到 commit 裡,而是可以只取其中一部分,這樣的設計可避免彼此不相關的修改被無條件放進同一個 commit 裡。

https://ithelp.ithome.com.tw/upload/images/20250910/20178513GXigMRTV1l.png
預存區的機制讓我們可以把無關的修改放到兩個 commit 中

有了預存區的機制,我們將得以透過不整理 commit 的方式(要整理 commit 也不是不行,但相對複雜一些),把一次大改動拆成許多小部分,再一一放進不同的 commit,讓每個 commit 快照追蹤到的變化更為細緻。

複習:用 git status 觀察 git add 前後的變化

在學習上層瓷器指令時,我們知道可以用 git status 來看預存區的狀態。

例如現在我們透過以下指令生成 hello_world.txt 檔案:

echo "Hello, world!" > hello_world.txt

經過 git add 指令被放到預存區,用 git status 來看就會變這樣:

https://ithelp.ithome.com.tw/upload/images/20250910/20178513JtLRRAgnH1.png

在區塊 1 中,hello_world.txt 尚未被放到預存區,表示 git 還不知道這份檔案,目前仍是未被追蹤(untracked)狀態,標示紅字。

在區塊 2 中,hello_world.txt 透過 git add 指令被放到預存區。

在區塊 3 中,透過 git status 可發現 hello_world.txt 變成綠色,表示已經被放到預存區,git 已經知道這份檔案、為被追蹤(tracked)狀態,可以接著進入下一次的 commit。

.git/ 資料夾在 git add 之後的變化

透過上述觀察,我們發現下完 git add 指令後,主要發生兩件事:

  1. 指定檔案開始被 git 追蹤
  2. 指定檔案被放進預存區

現在我們來觀察 .git/ 資料夾,看看在 git add 之後,發生了什麼變化。
原本只有經 git init 的 .git/ 資料夾結構長這樣:

.git/
├── hooks/
│   ├── applypatch-msg.sample
│   └── ...
│
├── info/
│   └── exclude
│
├── objects/
│   ├── info/
│   └── pack/
│
├── refs/
│   ├── heads/
│   └── tags/
│
├── config
├── description
└── HEAD

經過 git add 後,發生了兩個變化:

.git/
├── hooks/
│   ├── applypatch-msg.sample
│   └── ...
│
├── info/
│   └── exclude
│
├── objects/                 # 多了一個af/資料夾,內含38碼檔案
│   ├── af/
│   │   └── 5626b4a114abcb82d63db7c8082c3c4756e51b
│   ├── info/
│   └── pack/
│
├── refs/
│   ├── heads/
│   └── tags/
│
├── config
├── description
├── HEAD
└── index             # 多一個index檔案,這裡就是預存區(staging area)

預存區 index 檔案出現了

先來看看新增的 index 檔案,這個 index 檔案就是預存區!如果單純打開會看到亂碼:
https://ithelp.ithome.com.tw/upload/images/20250910/20178513LPnBeLyPy6.png

這是因為裡面存的是二進制資料,得用其他指令打開,看當中由 01 組成的內容,但轉成那樣我們依然不會看懂,因此這裡用管路指令 git ls-files --stage 打開,觀察預存區 index 裡的資料:

https://ithelp.ithome.com.tw/upload/images/20250910/20178513cZkkeiSH1c.png

跑出的結果是 100644 af5626b4a114abcb82d63db7c8082c3c4756e51b 0 hello_world.txt,讓我們一一拆解:

  • 100644:代表檔案模式,100644 表示一個正常的不可執行檔案(也就是一個死的、不能跑也不能當參照路徑的檔案);若為如 .exe 之類的執行檔,則為 100755;而若為表示檔案路徑的符號鏈接(symbolic link),則以 120000 表示。
  • af5626b4a114abcb82d63db7c8082c3c4756e51b:這看起來是一組雜湊碼,而且跟 objects/ 裡面的新資料夾名稱 af/ 與當中檔案 5626b4a114abcb82d63db7c8082c3c4756e51b 組合起來一樣!這個我們稍後來看。
  • 0:處理合併衝突時使用,單純下 git add 指令會跑出 0
  • hello_world.txt:檔案名稱。

多了一個 blob 物件

git add 之後,objects/ 還多了一個名為 af/ 的資料夾,裡面有個名為 5626b4a114abcb82d63db7c8082c3c4756e51b 的檔案,這兩者加起來共 40 位數,也存在剛剛觀察的 index 檔案裡面。

objects/ 的意思是物件,這個資料夾確實就是 Day 3 提到的 git 物件,因此我們發現:git add 指令會生成 git 物件,而資料夾名稱加上內部檔案名稱共 40 位數,就是用來識別此物件的雜湊碼。

但 Day 3 提到的 git 物件有三種,這裡是哪種物件呢?如果直接把檔案打開來看,一樣會發現亂碼:
https://ithelp.ithome.com.tw/upload/images/20250910/20178513RCzrNT8Icw.png

因此改用 git cat-file 這管路指令,如下:

  • git cat-file -t af5626b-t 表示查看物件種類,顯示 blob。
  • git cat-file -s af5626b-s 表示查看物件大小,hello_world.txt 內容是 Hello, world! ,共 13 個字母,一個字母佔 1 位元組,加上結尾的換行符號 \n,因此共 14 位元組,顯示 14。
  • git cat-file -p af5626b-p 表示查看物件內容,也就是 Hello, world!

https://ithelp.ithome.com.tw/upload/images/20250910/20178513JM7LZaYnyY.png

小結

經過 git add 指令,會發生下列兩件事:

  1. 產生 blob 物件與其雜湊碼,並放在 objects/ 資料夾。
  2. 預存區 index 出現關於新增之 git 物件雜湊碼等相關資訊。

現在我們應該也能看懂 Day 1 及 Day 2 說的底層管路指令在做什麼:

  1. git hash-object:產生一組 blob 物件,並計算物件 ID(也就是其雜湊碼)。
  2. git update-index --add:把第 1. 步產生的 blob 物件放進預存區。

第 1. 步會影響 .git/objects/ 資料夾、第 2. 步才影響預存區 index 檔案,而上層瓷器指令 git add 則是一次把兩步驟一起完成。

下篇文章,我們將一同探討進一步下 git commit 後, .git/ 資料夾發生的變化。

參考資料

  1. About - Staging Area
  2. What's the use of the staging area in Git?
  3. git-ls-files
  4. 深入 Git:index 檔案
  5. git-cat-file

上一篇
Day 9-深入一點點認識 Git:分支與 HEAD 的本質:參考(refs)
下一篇
Day 11-深入一點點認識 Git:git commit 之後,.git/ 資料夾發生了什麼變化?
系列文
深入一點點認識 Git15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言