git diff 沒有輸出卻有修改?檢查暫存區與 HEAD 的差異
執行 git add 後,git diff 突然沒有輸出,但編輯器仍顯示修改,先看 git diff --cached。不帶參數的 diff 比較暫存區與工作檔案;stage 完成後,這兩份內容相同就會空白。 已暫存的修改仍可進入一般 commit。
還有一種更容易誤判的情況:stage 後在編輯器把內容改回原樣,git diff HEAD 也是空白,但暫存區仍保存先前的修改。要確認下次提交內容,必須看對比較的兩端。
先用唯讀命令找出是哪份快照
在專案目錄執行:
git status --shortgit diff --name-statusgit diff --cached --name-statusgit diff HEAD --name-status依 Git diff 文件,這三個比較回答不同問題:
| 命令 | 比較的兩端 | 用途 |
|---|---|---|
git diff | 暫存區 → 工作檔案 | stage 後又改了什麼 |
git diff --cached | HEAD → 暫存區 | 一般 commit 準備提交什麼 |
git diff HEAD | HEAD → 工作檔案 | 磁碟上的檔案相對上次 commit 改了什麼 |
--staged 和 --cached 是同義選項。要看某個檔案的內容差異,把確切路徑放在 -- 後面,例如 git diff --cached -- settings.txt。
暫存區(index)保存的是 stage 當時的內容,並不會持續追蹤編輯器的新版本。Pro Git 的記錄變更章節 說明了「stage 後再編輯」為何會同時出現兩份修改。
status 的兩欄各看一個比較
對已有追蹤、沒有未解 merge 的一般檔案,short status 第一欄描述 index 相對 HEAD,第二欄描述工作檔案相對 index:
M settings.txt M settings.txtMM settings.txt?? notes.txt這是四種不同情境,不是同一個檔案同時列出四次。M 是已 stage; M 是尚未 stage;MM 表示兩端都有修改,要分別看 cached 與一般 diff。?? 是未追蹤檔案,上面三種一般 diff 不會自動列出其內容。
如果 MM 搭配空白的 git diff HEAD,可能是磁碟內容回到 HEAD,而 index 還在另一個版本。不能據此說「Git 狀態壞掉」,也不需要先 reset 或刪除 .git。
重現工作檔案改回去,暫存區仍保留修改
以下用 Bash 執行,只在 mktemp 建立的獨立目錄操作,沒有 remote。本文維護流程於 2026-10-10 在 macOS、Git 2.54.0(Apple Git-157)執行;斷言涵蓋一般文字檔,不包含衝突、submodule 或自訂 diff driver。
set -eufixture_dir="$(mktemp -d)"git init -q -b main "$fixture_dir"cd "$fixture_dir"git config user.name Fixtureprintf 'theme=light\n' > settings.txtgit add -- settings.txtgit -c commit.gpgsign=false commit -qm Baseline
printf 'theme=dark\n' > settings.txtgit add -- settings.txtgit diff --quiet -- settings.txtif git diff --cached --quiet -- settings.txt; then echo '預期暫存區應有修改' >&2 exit 1else test "$?" -eq 1fi
printf 'theme=light\n' > settings.txtgit diff HEAD --quiet -- settings.txttest "$(git status --short)" = 'MM settings.txt'test "$(git show :settings.txt)" = 'theme=dark'
git -c commit.gpgsign=false commit -qm 'Commit staged content'test "$(git show HEAD:settings.txt)" = 'theme=dark'test "$(cat settings.txt)" = 'theme=light'test "$(git status --short)" = ' M settings.txt'printf '暫存快照檢查通過:%s\n' "$fixture_dir"git diff --quiet 的狀態碼 0 代表沒有差異,1 代表有差異;其他非零狀態要當錯誤處理。這裡第一個比較成功,接著確認 cached 比較並非空白。
工作檔案改回 light 後,三個位置是:HEAD 為 light、index 為 dark、工作檔案為 light。兩端相同,所以 git diff HEAD 空白;一般 commit 仍提交 index 的 dark,磁碟內容則維持 light。
測試不會清掉暫存 repository,最後一行會顯示路徑,方便查看。不要把這些初始化與提交命令換成正式專案路徑。
決定要交哪個版本,再改暫存區
先確認目前暫存的內容:
git diff --cached -- settings.txtgit show :settings.txt如果準備提交的就是這一版,可以保留後續未 stage 的編輯。若應提交的是磁碟上的新版本,才再次 stage 該確切檔案:
git add -- settings.txtgit diff --cached -- settings.txtgit status --short在上面 fixture 的提交前狀態,重新 stage light 會讓 index 回到 HEAD,該檔案的修改消失;這是選擇了目前工作內容,不是 Git 遺失資料。
cached diff 的預覽適用於一般、沒有路徑參數或 -a 的 commit。Git commit 文件 說明 -a 會先納入已追蹤檔案的修改與刪除,帶路徑的提交則有不同的內容選擇規則。不要看完 cached diff,卻換用另一種提交方式後仍假設結果相同。
多個 Agent 在同一專案工作時,先用 Git worktree 隔離工作目錄,再各自確認 HEAD、index 與工作檔案。即使只剩一位操作者,也應在 commit 前看暫存版本,而不是用空白的 git diff HEAD 代替驗收。
參考資料: