網頁開發實戰
EP.12

Git 入門到實戰
版本控制的底層邏輯

commit / branch / merge / rebase — 工程師每天都在用,
面試最常考的 10 個問題

Joseph Chen

2025
12 min read
2.4k views

「Git 不是追蹤你改了什麼文字,而是替你的整個專案拍快照(snapshot)。每次 commit 都是一張快照,branch 是一條時間線,merge 是把兩條時間線合而為一。」

— 一旦理解這個心智模型,Git 所有指令都會變得直覺。

Git 是什麼?Snapshot vs Diff

很多人以為 Git 是「記錄每次改動了哪幾行」(diff-based),但這是錯的。Git 的底層是 snapshot-based:每一個 commit 是對整個專案當下狀態的一張完整快照(只有沒改動的檔案才共享指標節省空間)。

❌ 錯誤心智模型(Diff-based)

「記錄第 12 行從 A 改成 B」— 還原時需要逐一重播所有 diff,越久越慢。

✅ 正確心智模型(Snapshot-based)

「在時間點 T 整個專案長這樣」— 切換到任何 commit 都是 O(1),速度極快。

四個核心區域

理解 Git 的關鍵是知道「檔案現在在哪裡」。Git 把你的工作分成四個區域:

檔案流動路徑

Working Directory

你正在編輯的檔案

Staging Area

git add 後

Local Repo

git commit 後

Remote Repo

git push 後

Working Directory(檔案修改中)

你實際編輯的地方,git status 顯示為紅色 modified / untracked。

Staging Areagit add <file>

暫存區,又叫 Index。你挑選要放進下一個 commit 的改動,可以只 add 部分檔案。

Local Repositorygit commit -m "msg"

本機的 .git 資料庫,存放所有快照歷史。commit 後就永久記錄在這裡。

Remote Repositorygit push / git pull

GitHub / GitLab 等遠端伺服器,團隊共享的中央倉庫。

常用指令速查表

指令作用常見用法
git init初始化 Git 倉庫git init(空資料夾 → .git)
git clone <url>複製遠端 repo 到本機git clone https://github.com/user/repo.git
git add加入 Staging Areagit add . / git add src/index.ts
git commit建立快照git commit -m "feat: add login"
git push推到遠端git push origin main
git pull拉下遠端最新git pull origin main(= fetch + merge)
git branch管理分支git branch feat/login / git branch -d feat/login
git checkout / switch切換分支或 commitgit switch main / git checkout -b feat/x
git merge合併分支git merge feat/login(合進目前分支)
git rebase重放 commit 歷史git rebase main(在 feat 上執行)
git stash暫存目前工作區git stash / git stash pop
git log --oneline精簡歷史紀錄git log --oneline --graph --all
git diff查看差異git diff HEAD / git diff main feat
git reset回退 commit 或 staginggit reset --soft HEAD~1

merge vs rebase:兩種合併策略

這是面試最常問的問題。兩者都能把分支的改動整合進來,但產生的 commit 歷史長得完全不同。

git merge feat

A — B — C ← main

\ /

D — E — M ← merge commit

  • 產生一個新的 merge commit(M)
  • 保留完整分岔歷史,可追溯
  • 歷史較雜,有菱形圖
  • 適合:open source、需保留 PR 歷史

git rebase main

A — B — C ← main

\

D' — E' ← feat(重放)

  • 把 feat 的 commit 「搬」到 main 前端
  • 歷史是一條直線,乾淨易讀
  • commit hash 會改變(D → D')
  • 適合:個人 feature branch、內部開發
merge vs rebase — 實際指令
# merge:在 main 上把 feat 合進來
git checkout main
git merge feat
# → 產生 merge commit,保留分岔歷史

# rebase:在 feat 上重放到 main 最新位置
git checkout feat
git rebase main
# → feat 的 commit 被重寫,接在 main 後面

# interactive rebase(整理 commit 歷史)
git rebase -i HEAD~3
# → 可以 squash / reword / drop 最近 3 個 commit

HEAD 是什麼?Detached HEAD?

HEAD 是一個指標,指向你「目前所在的位置」。通常它指向某個 branch 名稱(間接指向該 branch 的最新 commit)。

正常狀態

HEAD → main → commit abc123

HEAD 指向 branch,commit 後 branch 自動前進,HEAD 跟著走。

Detached HEAD

HEAD → commit abc123(直接)

當你 git checkout <commit-hash> 時,HEAD 脫離 branch,進入「遊離」狀態。此時 commit 不屬於任何 branch,切換後會遺失。

HEAD 相關指令
git log --oneline        # 看 HEAD 指向哪
git checkout abc1234     # 進入 detached HEAD(只是查看)
git checkout -b new-feat # 從 detached HEAD 建新 branch,救回改動
git checkout main        # 回到正常狀態

面試常考題

以下 10 題是工程師面試中出現頻率最高的 Git 問題,每題附上標準答案要點。

Q1. git merge 和 git rebase 的差異?

merge 產生一個 merge commit,保留分岔歷史,適合需保留 PR 記錄的場景。rebase 把 commit 重放到目標 branch 前端,歷史是一條直線但 hash 改變,適合個人 branch 保持乾淨歷史。黃金法則:不要對已 push 的 public branch 做 rebase。

Q2. git reset 三種模式(--soft / --mixed / --hard)的差異?

--soft:回退 commit,但改動留在 Staging Area(可直接再 commit)。--mixed(預設):回退 commit,改動退回 Working Directory(需重新 add)。--hard:回退 commit,改動完全丟棄(危險!無法還原)。

Q3. git revert 和 git reset 的差異?

reset 是「修改歷史」,把 HEAD 移到舊 commit;revert 是「新增一個反向 commit」來撤銷改動,不改變歷史。已 push 到遠端時必須用 revert,不能用 reset(否則會讓其他人的歷史出現衝突)。

Q4. 什麼是 fast-forward merge?

當目標 branch 沒有新 commit(main 還停在分岔點),merge 不需要建立 merge commit,只是把 main 的指標向前移。可用 --no-ff 強制產生 merge commit 以保留歷史。

Q5. git stash 做什麼?什麼時候用?

把 Working Directory 和 Staging Area 的改動暫時收進 stash stack,讓工作區變乾淨。常見情境:做到一半需要緊急切換 branch 修 bug,又不想 commit 未完成的工作時。用 git stash pop 或 git stash apply 取回。

Q6. cherry-pick 是什麼?

把指定的某個 commit(或多個)複製到目前 branch,不需要整個 merge。常用於:hotfix 只需要把修 bug 的那個 commit 搬到其他 branch,而不是整個 feature branch。指令:git cherry-pick <commit-hash>。

Q7. 如何撤銷已 push 的 commit?

使用 git revert <commit-hash> 產生反向 commit,然後再 push。絕對不能對 public branch 用 git reset 然後 force push(會破壞其他人的歷史)。如果是自己私有 branch,可以 git push --force-with-lease(比 --force 安全)。

Q8. .gitignore 的優先順序?

已被 Git 追蹤的檔案,.gitignore 不會生效(需要先 git rm --cached <file>)。優先順序:命令列 pattern > 目錄層級 .gitignore(越深越優先)> 上層 .gitignore > ~/.gitignore_global。

Q9. git fetch 和 git pull 的差異?

fetch 只把遠端的新 commit 下載到本機(存在 origin/main 等 remote tracking branch),不修改你的 Working Directory。pull = fetch + merge(或 fetch + rebase,取決於設定)。好習慣:先 fetch,確認差異後再手動 merge,比直接 pull 更安全。

Q10. branch 是什麼?它儲存的是什麼?

branch 本質上只是一個指標(一個 40 bytes 的 commit hash 文字檔,存在 .git/refs/heads/ 下)。它指向某個 commit。建立 branch 的成本幾乎是零,切換 branch 也只是移動指標。這就是為什麼 Git branching 比其他版控工具快。

這篇學到什麼

📸Git 是 snapshot-based,不是 diff-based。每個 commit 是整個專案的快照。
🗂️四個區域:Working Directory → (git add) → Staging Area → (git commit) → Local Repo → (git push) → Remote Repo。
🌿branch 只是指向 commit 的指標,建立和切換成本幾乎為零。HEAD 指向你目前在哪。
🔀merge 保留分岔歷史;rebase 重寫歷史成一條直線。對 public branch 用 merge,個人 branch 可以 rebase。
↩️reset 修改歷史(三模式:soft/mixed/hard);revert 建新 commit 撤銷,已 push 只能用 revert。
🎯cherry-pick 複製指定 commit;stash 暫存工作區;fetch + 手動 merge 比 pull 更安全。
Git
版本控制
branch
merge
rebase
面試題
EP.12