iT邦幫忙

2025 iThome 鐵人賽

DAY 14
0
Software Development

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

Day 14 - 深入一點點認識 Git:git diff 到底在比較什麼?

  • 分享至 

  • xImage
  •  

當我們做了多次 git addgit commit 指令,又在工作目錄中做了一些更改時,很容易忘記在哪邊改了什麼,這時就可以用 git diff 指令比較。

git diff 給予不同的引數(arguments)後,將可指定不同的比較對象。我們先複習在 Day 5 文章中,提到檔案從工作目錄經預存區到成為 commit 快照的流程:
https://ithelp.ithome.com.tw/upload/images/20250914/201785138Hizcs2YOl.png

接著,我們就可以開始進行實驗!

實驗前置準備

為了可以比較 git diff 不同用法,我們先讓實驗設定滿足以下條件:

  1. 形成至少兩個 commit。
  2. 有檔案被放到預存區 index,但還沒形成 commit。
  3. 工作目錄有還沒被放到預存區的更動。

要形成 1.,我們依序在一個經 git init 的資料夾輸入下列指令:

  1. echo "First content" > file.txt
  2. git add file.txt
  3. git commit -m "Initial commit"
  4. echo "Second content" > file.txt
  5. git add file.txt
  6. git commit -m "Second commit"

這時候共有兩筆 commit、同時預存區 index 存放的是第二筆 commit 的內容:
https://ithelp.ithome.com.tw/upload/images/20250914/201785136sfHIAszMC.png

接著透過以下指令,把預存區的內容也改到跟最新 commit 不同:

  1. echo "Third content" > file.txt
  2. git add file.txt

目前預存區的檔案變成 "Third content" 版本:
https://ithelp.ithome.com.tw/upload/images/20250914/20178513FB53hqV8i0.png

最後再輸入以下指令,把工作目錄檔案也成跟預存區不同:

  1. echo "Fourth content" > file.txt

經過以上操作,目前整體結構如下(物件雜湊碼與其關係由查看 .git/ 資料夾與多次下 git cat-file 指令得知):

https://ithelp.ithome.com.tw/upload/images/20250914/20178513LqePGGmOuu.png

實驗開始

接下來我們將比較 git diff 的不同使用方法,分別在什麼地方之間做比較。

git diff

如果單純下 git diff,可發現下列結果:
https://ithelp.ithome.com.tw/upload/images/20250914/201785131oyBf4FNs3.png
現在我們依序檢視其內容:

diff --git a/file.txt b/file.txt

表示這是一個 diff 區塊,要比較的對象有舊的(以 a/ 前綴)file.txt 與新的(以 b/ 前綴)file.txt

index 55d4074..b88ab8e 100644

這個 index 不是檔案經 git add 指令會加進去的預存區,只是說明這行要比較雜湊碼為 55d4074...b88ab8e... 兩者;結尾的 100644 為檔案模式,表示一個正常的不可執行檔案。

透過 git cat-file 指令可知道 55d4074..."Third content" 版本的 blob 物件雜湊碼,而目前還沒有 b88ab8e... 這個物件,既然沒這個物件,那 git diff 在比什麼?

https://ithelp.ithome.com.tw/upload/images/20250914/20178513zgV3PTzUAg.png
目前還沒有 b88ab8e 這個物件

git diff 非常聰明,即便目前在工作目錄的最新版本(即內容為 "Fourth content"file.txt 檔案)還沒有經 git add 做成 blob 物件,但它已經知道如果做成 blob 物件,其雜湊碼就會變成 b88ab8e...,以 git hash-object 指令觀察可證明:
https://ithelp.ithome.com.tw/upload/images/20250914/20178513h50Iid9lxb.png
以 git hash-object 觀察工作目錄檔案做成 blob 物件後的雜湊碼

為了方便解說,本文中如果再提到 b88ab8e...,就用「準 blob 物件」表示。

--- a/file.txt
+++ b/file.txt

首行以 a/ 為前綴,表示發生變動前的檔案、次行以 b/ 為前綴,表示變動後的檔案;在此範例中,發生變動前後的檔案都是 file.txt

@@ -1 +1 @@

頭尾的 @@ 稱為「塊標頭(chunk header 或 hunk header)」,中間 -1 代表從舊檔案的第一行開始有變化、共影響一行;+1 代表在新檔案的第一行開始有變化、共影響一行。

-Third content
+Fourth content

-Third content 表示存在於舊檔案、但新檔案沒有的內容;+Fourth content 表示新檔案才出現、新檔案沒有的內容。

值得留意的是,使用 git diff 核對都是直接看一整行,就算只是改變一個單字、甚至是一個標點符號,也是以行為單位在比較。

由上述實驗可發現,直接下 git diff 比較的是「預存區」與「工作目錄」之間的差異,也就是哪些改動有被 git add、哪些還沒。

git diff <commit>

如果在 git diff 後面加上一個 commit 的雜湊碼,會出現什麼呢?

我們以既有的兩個 commit 雜湊碼 19b6609...fd5ff72... 做實驗:
https://ithelp.ithome.com.tw/upload/images/20250914/20178513kDa79AZSHi.png

Git 雜湊碼為第二個 commit 19b6609... 時,由 index 開頭那行可發現比較的是 c44fde5... 這個 blob 物件與 b88ab8e... 這個「準 blob 物件」;最後兩行則顯示移除的內容為 "Second content"、新增的內容為 "Fourth content"

Git 的雜湊碼為第一個 commit fd5ff72... 時,比較對象為 5db1980... 這個 blob 物件與 b88ab8e... 這個「準 blob 物件」;移除的內容為 "First content"、新增的內容為 "Fourth content"

由上述實驗得知,當 git diff 後面接著一個 commit 雜湊碼時,比較對象為該 commit 指的 tree 物件所指向的 blob 物件,以及工作目錄中仍未被 git add 的狀態。

git diff --cached / --staged / --cached HEAD

這三個指令的結果相同,我們來觀察其比較的對象:
https://ithelp.ithome.com.tw/upload/images/20250914/20178513gA3KdzM7H1.png

由三指令的 index 那行可以得知,比較對象為最新 commit 指的 tree 物件指向的 blob 物件 c44fde5... ,以及預存區 blob 物件雜湊碼 55d4074...;移除的內容為 "Second content"、新增的內容為 "Third content"

由此得知,使用這三個指令比較的對象,包含最新 commit 指的 tree 物件所指向之 blob 物件、以及預存區中的 blob 物件。

git diff <commit> <commit>

如果在 git diff 後面加上兩個 commit 的雜湊碼如下:
https://ithelp.ithome.com.tw/upload/images/20250914/20178513nK10oIWZ13.png

比較對象即為兩個 commit 各自指向的 tree 物件所指的 blob 物件。

值得留意的是,先打的 commit 雜湊碼會變成前綴為 a/ 的舊檔案、後打的 commit 雜湊碼則為前綴為 b/ 的新檔案。

小結

git diff 可用來比較檔案差異,而依照後面接的引數不同,可指定不同比較對象:

  1. git diff:若無引數,比較對象為「預存區」與「工作目錄」。
  2. git diff <commit>:以一個 commit 雜湊碼為引數,比較對象為「指定 commit」與「工作目錄」。
  3. git diff --cached / --staged / --cached HEAD:三者相同,都是比較「最新的 commit」與「預存區」。
  4. git diff <commit> <commit>:比較兩個指定的 commit。

不管是哪一種情境,比較的都是 blob 物件(若從 commit 物件出發,則會先經過 tree 物件),以下圖示說明:
https://ithelp.ithome.com.tw/upload/images/20250914/20178513zNwx9hUzpD.png

參考資料

  1. git-diff
  2. 30 天精通 Git 版本控管 (09):比對檔案與版本差異

上一篇
Day 13 - 深入一點點認識 Git:git reset 三種不同模式與 git revert 對 .git/ 資料夾的影響
下一篇
Day 15 - 深入一點點認識 Git:可以是、也可以不是物件的 tag
系列文
深入一點點認識 Git15
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言