Git 全景課 / 第一部分 建立心智模型 · M1

M1 三個區域:工作目錄、暫存區、倉庫

Git 的大部分命令,都只是把內容從一個區域搬到另一個區域。認清這三個區域,就看懂了一半的 Git。
約 45 分鐘沙盒 5 個任務 · 小測 4 題先修:M0
學完本單元,你能夠:
  1. 說出工作目錄、暫存區、倉庫各自是甚麼;
  2. 判斷一個檔案目前處於哪種狀態;
  3. 用 add、commit、restore 把內容在三個區域之間移動;
  4. 分清楚 git diff 和 git diff --staged 各自比較甚麼。

1為甚麼中間要多一區? Why a middle area?

M0 說過,commit 是整個專案在某一刻的快照。問題是:你的資料夾裏常常同時有幾件事做到一半。例如筆記改好了,草稿只寫了一半,另外還有一個臨時檔案。你想拍的快照,未必是資料夾「此刻的全部」。

所以 Git 在你的資料夾和歷史記錄之間,多放了一個區域。用打包寄件來比喻:

這樣你就可以只把「筆記的修改」封成一個 commit,讓寫了一半的草稿留在房間裏,下次再處理。

2三個區域與搬運命令 The three areas

三個區域與命令 工作目錄、暫存區、倉庫三欄由左至右排列。git add 把內容從工作目錄搬到暫存區;git commit 從暫存區存入倉庫。反方向:git restore --staged 用倉庫的內容蓋掉暫存區;git restore 用暫存區的內容蓋掉工作目錄。git diff 比較工作目錄與暫存區,git diff --staged 比較暫存區與倉庫。 工作目錄 working tree 你看到、編輯的檔案 暫存區 index · staging area 下一個 commit 的草稿 倉庫 repository(.git) 所有 commit;HEAD=最新 git add git commit git restore --staged 退出暫存(保留修改) git restore 放棄修改(不可復原) git diff git diff --staged
重點:暫存區是一份完整的草稿,不是「改動清單」 暫存區裏放着所有已追蹤檔案的內容,而不只是你剛剛 add 的那幾個。git commit 做的事很簡單:把暫存區整份拿去拍成快照。下面的沙盒裏,你會看到暫存區那一欄一直都有全部檔案。

3檔案的四種狀態 Four file states

Git 判斷一個檔案的狀態,只需要比較它在三個區域裏的內容:

狀態判斷方法git status 顯示為
未追蹤 untracked工作目錄有,暫存區和倉庫都沒有Untracked files
已修改 modified工作目錄 ≠ 暫存區Changes not staged for commit
已暫存 staged暫存區 ≠ 倉庫(HEAD)Changes to be committed
未修改 unmodified三個區域的內容相同不顯示(working tree clean)

留意「已修改」和「已暫存」是兩個獨立的比較,所以同一個檔案可以同時是兩種狀態:你 add 了一次,然後又再修改它。沙盒的任務 5 會讓你親眼看到。

4沙盒:親手搬運 Sandbox

互動 三區域沙盒

在下方輸入命令,或按常用命令的按鈕。按檔案卡片上的「編輯」,等於在編輯器裏改了檔案並儲存。卡片上的 #xxxx 是內容指紋:兩張卡片的指紋相同,代表內容完全一樣。

應用線:
小心:git restore <檔名> 會永久丟掉修改 它用暫存區的內容蓋掉工作目錄。如果那些修改從未 add 或 commit 過,Git 就沒有任何副本,無法復原。在沙盒裏放心試;在真實專案裏,執行前先用 git diff 看清楚會丟掉甚麼。

5容易混淆的對照 Easily confused

git diff 與 git diff --staged

git diffgit diff --staged
比較工作目錄 對比 暫存區暫存區 對比 倉庫(HEAD)
回答「我還有甚麼改動未放進紙箱?」「如果現在 commit,會存入甚麼?」
常見時機add 之前,檢查自己改了甚麼commit 之前,最後確認

--cached 和 --staged 是同一個意思,兩個寫法都可以。

常見誤解
  • 「git add 是用來加入新檔案的。」不只。它的意思是「把這個檔案現在的內容放進暫存區」,所以修改過的舊檔案同樣要 add。
  • 「add 之後再改,commit 時會包含最新的修改。」不會。暫存區記住的是 add 那一刻的內容,之後的修改要再 add 一次。
  • 「git diff 沒有輸出,代表沒有改動。」不一定。改動可能已經全部 add 了,要用 git diff --staged 才看得到。

6暫存區對知識工作的意義 Staging for knowledge work

寫筆記和文檔時,你常常一次過改幾個地方:修正了一個數據、順手調整了格式、又開始寫新的一節。暫存區讓你把這些改動分開提交,每個 commit 只講一件事,例如「修正第三節的樣本數」。半年後回看歷史,你能一眼找到那次數據修正,而不是一個叫「更新」、混雜了十件事的 commit。M2 會再談怎樣寫好 commit 說明。

7命令速查 Cheat sheet

命令方向作用
git status—比較三個區域,列出每個檔案的狀態
git add <檔名>工作目錄 → 暫存區把檔案現在的內容放進草稿
git commit -m "說明"暫存區 → 倉庫把整份草稿存成新的 commit
git restore --staged <檔名>倉庫 → 暫存區退出暫存,工作目錄的修改保留
git restore <檔名>暫存區 → 工作目錄放棄工作目錄的修改
git diff工作目錄 對比 暫存區看未暫存的改動
git diff --staged暫存區 對比 倉庫看將會提交的改動

舊教材和網上答案常見 git checkout -- <檔名> 和 git reset HEAD <檔名>,它們分別等於 git restore 和 git restore --staged。新寫法意思更清楚,本課一律使用新寫法。

8小測 Quiz

1. 你執行了 git add notes.md,然後再修改 notes.md 並儲存。現在 git commit,會存入哪個版本?
2. 你想知道「如果現在 commit,會存入甚麼」,應該用哪條命令?
3. 你用 git add . 把 draft.md 也誤加進暫存區了。想把它退出來,但保留修改,應該用:
4. 關於暫存區,哪一句正確?

本單元重點 Summary

延伸閱讀