Git 全景課 / 第一部分 建立心智模型 · M1
M1 三個區域:工作目錄、暫存區、倉庫
Git 的大部分命令,都只是把內容從一個區域搬到另一個區域。認清這三個區域,就看懂了一半的 Git。
學完本單元,你能夠:
- 說出工作目錄、暫存區、倉庫各自是甚麼;
- 判斷一個檔案目前處於哪種狀態;
- 用
add、commit、restore把內容在三個區域之間移動; - 分清楚
git diff和git diff --staged各自比較甚麼。
1為甚麼中間要多一區? Why a middle area?
M0 說過,commit 是整個專案在某一刻的快照。問題是:你的資料夾裏常常同時有幾件事做到一半。例如筆記改好了,草稿只寫了一半,另外還有一個臨時檔案。你想拍的快照,未必是資料夾「此刻的全部」。
所以 Git 在你的資料夾和歷史記錄之間,多放了一個區域。用打包寄件來比喻:
- 工作目錄(working tree)是你的房間。東西隨便放、隨時改。
- 暫存區(index,又叫 staging area)是一個打開着的紙箱。你挑選要寄的東西放進去,封箱之前可以隨時加、隨時拿出來。
- 倉庫(repository)是已封箱、貼了標籤、登記在冊的包裹。封箱(
git commit)之後,內容就固定下來,成為歷史的一部分。
這樣你就可以只把「筆記的修改」封成一個 commit,讓寫了一半的草稿留在房間裏,下次再處理。
2三個區域與搬運命令 The three areas
重點:暫存區是一份完整的草稿,不是「改動清單」
暫存區裏放着所有已追蹤檔案的內容,而不只是你剛剛
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
小心:
git restore <檔名> 會永久丟掉修改
它用暫存區的內容蓋掉工作目錄。如果那些修改從未 add 或 commit 過,Git 就沒有任何副本,無法復原。在沙盒裏放心試;在真實專案裏,執行前先用 git diff 看清楚會丟掉甚麼。
5容易混淆的對照 Easily confused
git diff 與 git diff --staged
git diff | git 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
本單元重點 Summary
- Git 有三個區域:工作目錄(你編輯的檔案)、暫存區(下一個 commit 的草稿)、倉庫(所有 commit)。
add把內容推向右邊,commit把草稿封存;restore --staged和restore把內容拉回左邊。- 暫存區是一份完整的草稿,記住的是
add那一刻的內容。 - 檔案狀態只是三個區域之間的比較,所以一個檔案可以同時「已暫存」又「已修改」。
git restore <檔名>會永久丟掉未保存的修改,使用前先git diff。
延伸閱讀
- Pro Git 1.3 What is Git?(The Three States 一節)
- Pro Git 2.2 Recording Changes to the Repository
- 有問題?記下來,它很可能也是其他同學的問題 → 學生問題庫