M4 分支:可移動的標籤
- 建立、切換、列出和刪除分支;
- 在每個命令之後,預測 HEAD 和各分支標籤會指向哪個 commit;
- 分辨 HEAD 的兩種狀態,並在 detached HEAD 下不弄丟工作;
- 分清楚 branch 與 worktree、
checkout與switch/restore。
1分支是一張會跟着走的標籤 A branch is a label that follows you
M3 的結論是:commit 一旦寫入就不會改變,而分支是 .git/refs/heads/ 裏一個寫着 commit 編號的小檔案。分支和標籤(tag)的分別只有一點:你在某條分支上提交時,那條分支會自動移到新的 commit。
2HEAD 的兩種狀態 Two states of HEAD
| 指向分支(一般情況) | detached HEAD(直接指向 commit) | |
|---|---|---|
.git/HEAD 的內容 | ref: refs/heads/main | 一個 commit 編號 |
git status 顯示 | On branch main | HEAD detached at 1a2b3c4 |
| 提交時 | 分支跟着往前走 | 只有 HEAD 往前走,沒有分支記住新 commit |
| 怎樣進入 | git switch main | git switch --detach <commit>、git checkout <編號或標籤> |
| 用途 | 日常工作 | 查看舊版本、臨時實驗 |
detached HEAD 不是錯誤,只是「不在任何分支上」。它唯一的風險在第 5 節。
3切換分支時改變了甚麼 What switching changes
git switch feature 做了兩件事:
- 把
.git/HEAD改成ref: refs/heads/feature; - 把工作目錄和暫存區換成 feature 所指 commit 的快照。檔案會在你眼前變成那個版本的樣子。
它不會複製任何東西,也不會新增任何物件。這正是 FAQ-001 的重點:一個資料夾(worktree)同一時間只能展示一條分支;要同時展示兩條,就要用 git worktree add 開第二個資料夾(M8)。
git stash 暫時收起(M8)。沙盒為了專注於分支,每次提交都會自動修改並 add 檔案,所以不會遇到這種情況。
4沙盒:commit 圖 Sandbox: the commit graph
5detached HEAD:不要讓 commit 變孤兒 Detached HEAD and orphaned commits
在 detached HEAD 狀態下提交,新 commit 只由 HEAD 指着。一旦你切換回某條分支,就再沒有任何 ref 指向它:它還在物件資料庫裏,但從任何分支都找不到,git log 也不會列出它。這種 commit 叫做懸空 commit(dangling commit)。
Git 會在你離開時提醒你:
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
9f3c2e1 試試另一種寫法
If you want to keep it by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> 9f3c2e1
照它說的做就能救回:git branch rescue 9f3c2e1 給那個 commit 貼上一張分支標籤。即使你錯過了這段提示,Git 也會在一段時間內(預設至少 30 天)保留記錄,可以用 git reflog 找回編號(M6)。
git checkout 加上 commit 編號或標籤名稱(例如 git checkout v1.0 想看舊版本);或者在 rebase 途中(M7)。看到 HEAD detached at … 時,先想清楚接下來要不要提交;要的話,先 git switch -c <新分支>。
6容易混淆的對照 Easily confused
branch 與 worktree
| branch | worktree | |
|---|---|---|
| 是甚麼 | 指向 commit 的標籤(歷史裏的一個位置) | 磁碟上的一個資料夾(一張書桌) |
| 數量 | 可以有幾百條,成本幾乎為零 | 每多一個就多一份工作檔案 |
| 切換 | git switch 改變這個資料夾展示的內容 | 到另一個資料夾工作,兩邊同時存在 |
詳見 FAQ-001,M8 會實際操作 worktree。
checkout 與 switch/restore
舊版 Git 只有 git checkout,它一條命令做兩件很不同的事。2019 年起(Git 2.23),Git 把它拆成兩條意思清楚的命令:
| 想做的事 | 舊寫法 | 新寫法 |
|---|---|---|
| 切換分支 | git checkout feature | git switch feature |
| 建立並切換 | git checkout -b feature | git switch -c feature |
| 查看舊 commit | git checkout 1a2b3c4(悄悄進入 detached HEAD) | git switch --detach 1a2b3c4(要你明確表態) |
| 放棄檔案修改 | git checkout -- notes.md | git restore notes.md(M1) |
舊寫法仍然有效,網上大量答案也還在用,所以要看得懂;但自己寫時用新寫法,因為命令名字就說明了它會影響甚麼。
- 「建立分支會自動切換過去。」不會。
git branch feature只建立標籤;要同時切換,用git switch -c feature。 - 「刪除分支會刪除它的 commit。」不會。只是撕掉標籤;commit 若沒有其他 ref 指向,就變成懸空 commit。所以
git branch -d在分支未合併時會拒絕,要你用-D確認。 - 「main 是特別的分支。」不是。它只是預設名字;Git 對它沒有任何特別待遇,「正本」的地位是團隊約定和保護規則給它的(M0、M11)。
7命令速查 Cheat sheet
| 命令 | 作用 |
|---|---|
git branch | 列出分支,* 是目前所在的 |
git branch feature | 在 HEAD 處建立分支(不切換) |
git branch fix 1a2b3c4 | 在指定的 commit 建立分支 |
git switch feature | 切換到分支 |
git switch -c feature | 建立並切換 |
git switch --detach v1.0 | 進入 detached HEAD 查看舊版本 |
git branch -d feature | 刪除已合併的分支(未合併會被拒絕) |
git branch -D feature | 強制刪除(它的 commit 可能變成懸空) |
git log --oneline --graph --all | 在終端機畫出所有分支的歷史 |
HEAD~1、main~2 | 「往回一步」「main 往回兩步」:沿第一個 parent 往回數 |
8延伸練習 More practice
Learn Git Branching(Peter Cottle 等人製作,MIT 授權,有繁體中文介面)是一套以關卡形式練習分支的網站。完成本單元後,可以先做它主要關卡裏的入門系列,熟悉提交、分支和切換;合併和 rebase 的關卡留到 M5、M7 之後再做。
9小測 Quiz
本單元重點 Summary
- 分支是會跟着提交往前走的標籤;只有 HEAD 所指的分支會走。
- 提交三步:新 commit 的 parent=HEAD 所指 commit → 目前分支移到新 commit → 其他分支不動。
git switch改變 HEAD,並把工作目錄換成那個 commit 的快照;不複製、不新增物件。- HEAD 有兩種狀態:指向分支(一般),或直接指向 commit(detached)。
- detached HEAD 下的提交,離開前要用分支保住,否則會變成懸空 commit。
- 用
switch切換分支、restore還原檔案;checkout兩者都做,看得懂就好。
延伸閱讀
- Pro Git 3.1 Branches in a Nutshell
- Pro Git 3.2 Basic Branching and Merging(合併部分是 M5 的內容)
- git switch 官方文件
- 有問題?記下來,它很可能也是其他同學的問題 → 學生問題庫