Git 是分散式版本控制系統的代表,理解其核心概念是高效使用的基礎。本章涵蓋 Git 的三區模型、物件儲存機制、分支管理以及常見操作。


一、Git 三區模型#

Git 的工作流程圍繞三個核心區域運作:

工作區 (Working Directory)
    ↓ git add
暫存區 (Staging Area / Index)
    ↓ git commit
版本庫 (Repository / .git)

工作區(Working Directory)#

你實際編輯檔案的地方,所有的修改都從這裡開始。

暫存區(Staging Area)#

暫存區是 Git 最具特色的設計。它提供了「緩衝」與「組織」變更的空間:

  1. 方案試錯:嘗試某種修改但未確定時,可先暫存。若後續方案不如預期,可用暫存區內容覆蓋回來。
  2. 原子化提交:若同時修改五個檔案但屬於兩個功能,可分批暫存並分開提交,保持版本歷史清晰。

版本庫(Repository)#

位於 .git 目錄中,儲存所有版本歷史與元資料。


二、基本操作#

建立倉庫#

# 在既有專案中初始化
cd my-project
git init

# 建立新專案
git init my-new-project

組態使用者資訊#

# 全域設定
git config --global user.name "Your Name"
git config --global user.email "your@email.com"

# 倉庫級設定(優先於全域)
git config user.name "Work Name"
git config user.email "work@company.com"

當同時存在 global 與 local 設定時,Git 優先採用當前倉庫(local)的設定。這對於區分公司與個人專案的身份非常實用。

標準工作流程#

# 1. 查看狀態
git status

# 2. 將檔案加入暫存區
git add <file>        # 單一檔案
git add .             # 所有變更
git add -u            # 僅已追蹤的檔案

# 3. 提交變更
git commit -m "描述這次變更"

# 4. 查看歷史
git log
git log --oneline --graph

直接在工作目錄新增檔案後立刻 git commit 不會成功,因為檔案未加入暫存區。請先 git add 再提交。


三、Git 物件模型#

Git 使用三種核心物件來管理版本:

Commit(提交物件)#

  • 每次 git commit 產生一個 commit 物件
  • 包含作者資訊、提交訊息、時間戳
  • 每個 commit 必定對應一棵 tree

Tree(樹物件)#

  • 代表該次提交的檔案系統快照
  • 記錄目錄結構、檔名與權限
  • 可巢狀包含子樹(子目錄)或 blob(檔案)

Blob(檔案內容物件)#

  • 儲存檔案的純內容(不含檔名)
  • Git 以內容雜湊識別,相同內容只存一份
commit
  └── tree (根目錄)
        ├── tree (子目錄)
        │     └── blob (檔案內容)
        └── blob (檔案內容)

Git 的「內容可尋址」設計帶來高效儲存:相同內容的檔案無論檔名如何,只儲存一份 blob。

檢視物件內容#

# 查看物件類型
git cat-file -t <hash>

# 查看物件內容
git cat-file -p <hash>

四、分支管理#

分支基本操作#

# 查看分支
git branch

# 建立分支
git branch <branch-name>

# 切換分支
git checkout <branch-name>
git switch <branch-name>      # Git 2.23+ 推薦

# 建立並切換
git checkout -b <branch-name>
git switch -c <branch-name>

# 刪除分支
git branch -d <branch-name>   # 安全刪除(已合併)
git branch -D <branch-name>   # 強制刪除

HEAD 指標#

HEAD 是指向當前工作位置的指標:

  • 正常狀態:HEAD → 分支 → Commit
  • 分離頭指標(Detached HEAD):HEAD 直接指向 Commit

在分離頭指標狀態下產生的 Commit,若沒有建立分支保存,切換離開後可能會遺失。

相對引用#

HEAD^     # 父節點(上一版)
HEAD^^    # 祖父節點(上上一版)
HEAD~n    # 前 n 代祖先

# 範例:比較當前與前兩個版本的差異
git diff HEAD HEAD~2

五、合併策略#

Merge(合併)#

# 將 feature 分支合併到當前分支
git merge feature

產生一個合併節點,保留完整的分支歷史。

Rebase(變基)#

# 將當前分支變基到 main
git rebase main

將提交「重新播放」到目標分支上,產生線性歷史。

選擇建議

  • 公共分支使用 Merge,保留歷史脈絡
  • 個人分支使用 Rebase,保持歷史整潔
  • 已推送到遠端的提交,避免 Rebase

六、衝突解決#

當兩個分支修改同一檔案的同一區域時,Git 無法自動合併:

# 嘗試合併
git merge feature-branch

# 發生衝突
CONFLICT (content): Merge conflict in file.txt

衝突標記#

<<<<<<< HEAD
你的修改
=======
對方的修改
>>>>>>> feature-branch

解決步驟#

  1. 開啟衝突檔案,決定保留哪些內容
  2. 刪除衝突標記(<<<<<<<=======>>>>>>>
  3. 儲存檔案
  4. 標記為已解決並提交
git add <resolved-file>
git commit -m "resolved conflict"

如果解決過程中發現搞砸了,可以放棄這次合併:

git merge --abort

七、版本回退#

git reset#

將分支指標移動到指定 commit,可選擇保留或捨棄變更:

# 軟重置:保留工作區與暫存區
git reset --soft <commit>

# 混合重置(預設):保留工作區,清空暫存區
git reset <commit>
git reset HEAD        # 取消所有暫存

# 硬重置:捨棄所有變更
git reset --hard <commit>

git reset --hard 是破壞性指令,會徹底丟失目標 commit 之後的所有變更。請確認不需要這些變更才使用。

git revert#

建立一個新的 commit 來「反轉」指定 commit 的變更:

git revert <commit>

reset vs revert

  • reset:修改歷史,適用於本地未推送的 commit
  • revert:新增歷史,適用於已推送的 commit

八、暫存工作(Stash)#

當需要臨時切換分支但當前工作未完成時:

# 暫存當前工作
git stash

# 查看暫存列表
git stash list

# 恢復工作(並刪除暫存)
git stash pop

# 恢復工作(保留暫存)
git stash apply

Pop vs Apply

  • 確定恢復後不再需要備份,使用 pop
  • 不確定是否會有衝突,或想應用到多個分支,使用 apply

九、.gitignore 組態#

定義哪些檔案應該被 Git 忽略:

# 忽略編譯產物
*.class
*.o
target/

# 忽略日誌
*.log
logs/

# 忽略環境組態
.env
.env.local

# 忽略 IDE 設定
.idea/
.vscode/

# 忽略系統檔案
.DS_Store
Thumbs.db

路徑結尾斜槓的影響

  • doc/(加斜槓):僅匹配目錄
  • doc(不加斜槓):同時匹配檔案與目錄

取得範本#

GitHub 維護了各種語言的標準 .gitignore 範本:

  • 在 GitHub 搜尋 gitignore 專案
  • 找到對應語言(如 Java、Node)
  • 複製到專案中並依需求調整

十、修改歷史#

修改最近一次 Commit#

# 修改訊息
git commit --amend -m "新的訊息"

# 修改內容(先 add 新變更)
git add <file>
git commit --amend

修改歷史中的 Commit#

使用互動式 rebase:

# 選擇要修改的 commit 的父節點
git rebase -i <parent-commit>

在編輯器中,將 pick 改為:

  • rewordr):修改訊息
  • squashs):合併到前一個 commit
  • edite):修改內容

已推送到遠端的 commit,請勿使用 --amendrebase 修改歷史。這會改變 commit hash,造成團隊協作問題。

合併多個 Commit (Squash)

將最近 4 個 commit 合併為一個:

# 找到第 5 個 commit 作為 base
git rebase -i HEAD~4

在編輯器中:

pick abc1234 第一個 commit
squash def5678 第二個 commit
squash ghi9012 第三個 commit
squash jkl3456 第四個 commit

儲存後,再編輯合併後的 commit 訊息。


常用指令速查#

操作指令
初始化倉庫git init
查看狀態git status
加入暫存區git add <file>
提交變更git commit -m "message"
查看歷史git log --oneline --graph
建立分支git branch <name>
切換分支git switch <name>
合併分支git merge <name>
暫存工作git stash
取消暫存git reset HEAD <file>
捨棄修改git checkout -- <file>
版本回退git reset --hard <commit>