Git 是開發者用來記錄檔案歷史、回退改動、跟別人協作的版本控制工具,就像是一台時光機。而在 AI Agent 進入日常開發流程之後,這件事變得更嚴重 —— Agent 一次改十幾個檔案是家常便飯,如果沒有 Git 當安全網,開發者連「剛才它到底改了什麼」都說不清楚,更別說反悔。
這篇文章會從 Git 最基本的架構開始理解,帶過日常會用到的指令,再進入分支、reset、stash 等反悔技巧。最後會用一整章談 AI Agent 時代的協作模式,示範如何把 Git 變成跟 Agent 一起工作時的第二層大腦。如果從沒碰過 Git,可以跟著章節順序讀;已經會基礎操作的,可以直接跳到第七章跟第八章。
什麼是 Git
Git 是一套分散式版本控制系統(Distributed Version Control System,簡稱 DVCS),由 Linux 創始人 Linus Torvalds 在 2005 年為了管理 Linux kernel 的原始碼而寫出來。它的核心工作只有一件事:把專案裡的檔案隨時間演化的過程完整記錄下來,並且讓開發者可以在這條時間線上自由來回——回到上週某一個版本、把昨天的改動丟掉、或是把三個人各自寫的功能合併成一份。
AI Agent 普及之後,越來越多 Git 操作其實可以交給 Agent 代勞。Claude Code、Cursor、Copilot Agent 都能自動 stage 檔案、寫 commit message、開 branch、甚至處理簡單的 merge 衝突。但這並不代表開發者可以不懂 Git,就像會計師可以幫公司產生完整的財務報表,老闆自己還是得看得懂報表在說什麼,否則連公司最近有沒有虧錢都判斷不出來。Agent 幫我們敲指令沒問題,但「這次 commit 的範圍對不對」「該不該 reset」「分支該從哪裡切」這些判斷終究是開發者自己要下的。
獨立運行,也能跟遠端伺服器同步
Git 和許多人以為的不一樣:它並不需要 GitHub 才能運作。只要在任何一個資料夾裡執行 git init,就會多出一個 .git/ 目錄,裡面完整保存了整份專案的歷史紀錄。即使永遠不連網、不推到任何伺服器,也可以單靠本地的 Git 做 commit、開 branch、回滾、比對差異,所有版本控制功能都不缺。這正是「分散式」這三個字的意思:每一份 clone 下來的倉庫都是獨立完整的,沒有中央伺服器這種單點依賴。
當需要和別人協作時,才會再接上一個遠端伺服器來同步。最知名的是 GitHub(由微軟收購後成為全球最大的開源社群),其次是 GitLab(可以自架、企業常用)、Gitea(輕量級的自架方案)、Bitbucket 等等。這些平台的共通點是它們都只是 Git 的「遠端倉庫」而已——Git 指令本身完全不需要在意我們用的是哪一家,git push / git pull 在所有服務上都通用。換伺服器就像換信箱,寄信的方式沒有變。
為什麼 AI 時代更需要懂 Git
在傳統的開發流程裡,Git 的主要角色是「團隊協作」與「歷史紀錄」,大部分的 commit 都出自工程師本人之手,每一筆都清楚記得自己做了什麼。但 AI Agent 加入之後情況完全不一樣了:Claude Code、Cursor、Copilot Agent 一次 task 可能修改十幾甚至數十個檔案,有時候還會主動建立、刪除檔案。回過神來想看看改了哪些地方,肉眼比對幾乎不可能。
這時候 Git 從「協作工具」升級成「Agent 的稽核紀錄與安全網」。改動不滿意?git restore 一句就回到原狀。改到一半想暫停做別的?git stash 暫存起來。Agent 把 main 分支搞亂了?在獨立 branch 裡翻車就好,主線完全不受影響。換句話說,Git 熟不熟會直接影響「敢不敢放手讓 Agent 做事」——不懂 Git 的人只敢讓 Agent 改一個檔案,懂 Git 的人敢讓 Agent 同時跑三個 worktree。
本體透過文字指令操作
Git 本體是透過指令在 Terminal、PowerShell、CMD(命令提示字元)上操作的,因此擅長處理文字的 AI Agent 天生就很會操控 Git,在本篇章節也是以文字操控為主,這樣 Agent 下指令的時候我們才知道他想做什麼。除了文字介面也有專門的桌面軟體、VS Code上的外掛、GitHub 桌面版與網頁版可以操控 Git,對 Git 有初步理解之後也可以使用他們。
安裝與基本設定
Git 在三大作業系統都有官方版本:macOS 上有 Xcode Command Line Tools 內建,也可以用 Homebrew 安裝;Linux 透過套件管理器(apt / dnf / pacman)一行就好;Windows 則建議從 git-scm.com 下載 Git for Windows,它附帶一個 Git Bash 終端機。macOS 使用者如果升級作業系統後突然 Git 不能用,可能是 Command Line Tools 路徑被改掉了,可以參考舊文 更新到 macOS 13 Ventura 之後無法使用 Git、Brew 的解法。
裝完後第一件事是告訴 Git 我們是誰。這個身份資訊會寫進每一個 commit,讓歷史紀錄知道這筆是誰做的。
# 設定使用者名稱與信箱(全域,一次設定即可)
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
# 設定預設分支名稱為 main(現代慣例,取代舊的 master)
git config --global init.defaultBranch main
# 檢查 Git 版本
git --version
# 檢查目前所有設定
git config --list
專案內覆蓋:不同 repo 用不同身份
我們一個專案資料夾稱為一個 repo(Repository, 儲存庫),上一小節使用 --global 設定的資料會套用到這台電腦上的所有 repo,但實務上我們常常要處理好幾種身份:公司專案希望用公司信箱、個人 side project 想掛 GitHub 提供的 noreply 信箱(格式類似 [email protected],避免個人主要信箱曝光在公開歷史裡)、貢獻某個開源專案又被要求用另一個 email。這時候可以使用 Terminal 進到該 repo 的目錄下,使用沒有 --global 的方式再設定一次,就只會覆蓋這一個 repo。
# 進到要單獨設定的 repo
cd ~/workspace/company-project
# 只對這個 repo 生效的 user.name 與 user.email
git config user.name "Kyle"
git config user.email "[email protected]"
# 確認這個 repo 最終會用哪個身份 commit
git config user.name
git config user.email
Repo 層級的設定會寫進該專案的 .git/config,優先權高於全域設定;沒有特別覆蓋的 repo 則自動沿用 --global 的值。換句話說,只需要在「例外」的 repo 設定一次,其他 repo 什麼都不用動。要注意的是,身份資訊是在 commit 當下寫入歷史的,事後才發現 email 寫錯的那幾筆 commit 作者資訊已經定型,再改 config 只會影響之後的新 commit,要修正舊的 commit 就得動用 git rebase 或 git filter-repo 重寫歷史,麻煩許多。所以接手新專案或 clone 下新 repo 時,養成先確認一次 git config user.email 的習慣,比事後補救划算。
設定完成後有兩種方式開始一個 Git 專案:git init 把現有資料夾變成 Git 倉庫,或 git clone 把 GitHub / GitLab 上的遠端倉庫抓下來。一般情況下新專案用 init,接手別人的專案用 clone。
Git 的基礎架構:三區加一層
很多人學 Git 卡關的主因是沒搞懂「檔案到底住在哪裡」。實際上 Git 把檔案分成三個區域,加上遠端一共四個地方:
- Working Directory(工作目錄):我們實際在資料夾和編輯器裡看到、改到的檔案
- Staging Area(暫存區,又叫 index):即將要 commit 進歷史的檔案快照。這是 Git 最獨特的設計
- Local Repository(本地倉庫):commit 過的歷史紀錄,存在
.git/資料夾裡 - Remote Repository(遠端倉庫):GitHub、GitLab 上那一份
檔案在這四個區域之間流動,就是 Git 日常操作的全部。把這張圖記起來,後面學任何指令都只是在記「它會把檔案從哪裡搬到哪裡」。

其他版本控制工具(例如 Subversion)沒有 staging area 這個概念,直接從工作目錄 commit 到倉庫。Git 多一個 staging area 是為了讓一次 commit 裡只包含我們想要的改動——即使工作目錄裡有十個檔案改了,也可以只挑兩個進 commit。對 AI Agent 時代的開發者來說,這個設計特別重要,後面會再回頭討論。
日常四件事:status、add、commit、log
90% 的 Git 日常操作就是這四件事的排列組合。先看一張時序圖,再逐一講指令。

git status — 看當下狀態
最常用的指令,它會告訴我們:工作目錄改了哪些檔案、哪些已經進 staging、目前在哪個分支、跟遠端有沒有落差。每次下其他指令前建議都先跑一次 git status,避免在不知道自己狀態的情況下亂下指令。
git add — 放進 staging
# 把單一檔案加入 staging
git add README.md
# 把整個目錄的改動加入 staging
git add src/
# 把所有改動加入 staging(新手謹慎使用,容易誤加 .env 等機敏檔)
git add .
# 互動式挑選要加的 hunk(更精細的控制)
git add -p
注意:新手常犯的錯是直接 git add .,結果把 .env、node_modules/、編輯器暫存檔一起加進去。比較安全的習慣是先 git status 看清楚,再用 git add 指定具體檔案或目錄。
.gitignore — 告訴 Git 不要管的東西
為了不讓 git add 誤抓到不該進版控的檔案(密碼、編譯產物、依賴套件、IDE 設定等),專案根目錄可以放一個 .gitignore 檔案,列出要忽略的路徑或 pattern,Git 就會當它們不存在。
# 環境變數(含密碼 / API key)
.env
.env.local
# Node.js 依賴
node_modules/
# 編譯產物
dist/
build/
*.pyc
__pycache__/
# IDE 設定
.vscode/
.idea/
# 作業系統暫存檔
.DS_Store
Thumbs.db
常用 .gitignore 範本可以到 github/gitignore 這個 repo 抓,它依語言、框架、作業系統分類好了。有了 .gitignore 後,git add . 雖然還是偏粗暴,但至少踩到地雷的機率會小很多。
想保留空資料夾:放一個 .gitkeep
Git 只追蹤檔案、不追蹤資料夾,所以空資料夾 git add 下去完全沒反應,clone 到別的機器上也不會存在。但實務上常有需要保留空目錄結構的情境,例如 logs/、uploads/、tmp/cache/ 這種程式啟動時預期要存在、但一開始沒任何檔案的資料夾。
社群慣用做法是在資料夾裡丟一個名為 .gitkeep 的空檔案,讓 Git 有東西可以追蹤、資料夾自然就被保留下來:
# 在空資料夾裡建一個佔位檔
touch logs/.gitkeep
git add logs/.gitkeep
git commit -m "chore: 保留 logs/ 空目錄"
.gitkeep 不是 Git 官方的特殊檔名,純粹是慣例——檔名換成 .keep、PLACEHOLDER 也行,只要那個資料夾裡至少有一個被追蹤的檔案就達到目的。另一種作法是在該資料夾放一個 .gitignore,內容寫成「忽略自己以外的所有檔案」:
# logs/.gitignore
* # 忽略此資料夾內所有檔案
!.gitignore # 但保留 .gitignore 本身
這種寫法的好處是:資料夾保留下來,而且執行時產生的 log、cache 檔案會自動被忽略,不用再到上層 .gitignore 寫一條 logs/*。
git commit — 寫進歷史
# 建立 commit 並直接寫訊息
git commit -m "feat: 新增使用者登入功能"
# 建立 commit 並開編輯器寫訊息(適合寫較長說明)
git commit
# 已經 add 過的檔案,同時 add + commit(只對已追蹤檔案有效)
git commit -am "fix: 修正密碼驗證邏輯"
Commit message 的格式建議遵循 Conventional Commits:以 feat(新功能)、fix(修 bug)、docs(文件)、refactor(重構)、test(測試)、chore(雜項)等前綴分類(後面章節會有詳細介紹),讓歷史紀錄在 git log 看起來有規律,CI 工具也能自動產生 CHANGELOG。
git log — 讀歷史
# 看完整 commit 歷史
git log
# 一行一個 commit,快速瀏覽
git log --oneline
# 圖形化顯示分支結構(加到 .gitconfig 當 alias 很好用)
git log --oneline --graph --all --decorate
# 看某個檔案的歷史
git log -- path/to/file.js
與遠端協作:push、pull、fetch
做到這邊,所有操作都還在本機。要把改動同步到 GitHub 或讓團隊其他人看到,就要進到「遠端」的世界。三個核心指令:push、pull、fetch,差別如下圖。

# 第一次設定遠端(clone 下來的專案會自動有 origin)
git remote add origin [email protected]:user/repo.git
# 把本地的 main 分支推上去(-u 設定上游,之後可以直接 git push)
git push -u origin main
# 從遠端拉取並合併到本地(fetch + merge 的組合技)
git pull
# 只下載遠端的最新狀態,但不自動合併(安全,看到內容再決定)
git fetch
# 查看所有遠端
git remote -v
其實 git pull 背後就是 git fetch 加上 git merge。如果覺得 pull 太粗暴(例如擔心直接合併會有衝突),可以改用 git fetch 先把遠端改動抓下來,用 git log origin/main 看看別人做了什麼,再決定要怎麼合。
遇到衝突怎麼辦
第一次遇到 merge conflict 的開發者通常會嚇一跳,但其實沒那麼可怕。衝突只會發生在「同一個檔案的同一個區域」被兩邊都改了,Git 搞不清楚該留哪個。這時候 Git 會在檔案裡標出衝突區塊:
<<<<<<< HEAD
我的改動版本
=======
對方的改動版本
>>>>>>> origin/main
解法很樸素:打開檔案,手動決定要留誰、或怎麼合併兩邊,然後把 <<<<<<<、=======、>>>>>>> 這三行標記刪掉,最後 git add + git commit 完成合併。VS Code、JetBrains IDE 都有內建的視覺化衝突解決工具,會更直覺。
分支:多線工作的基礎
分支(branch)是 Git 最有價值的功能之一。每開一個新 branch,就等於把當前狀態複製一份獨立運作,彼此互不影響,做完再合回主線。這件事在傳統 SVN 時代很難做到,Git 把它變得幾乎零成本。
個人開發者常以為「我一個人寫 code 不需要分支」,其實剛好相反:個人開發才更需要分支。試驗一個可能失敗的想法、同時開發兩個互斥的功能、或是(重點來了)讓 AI Agent 在隔離環境下工作,分支都是最乾淨的方式。典型的 branch 流程像下圖:

# 看目前有哪些分支(* 代表當前所在分支)
git branch
# 建立新分支但不切過去
git branch feat/login
# 建立並切換到新分支(最常用)
git switch -c feat/login
# 切回 main 分支
git switch main
# 把 feat/login 合併進當前分支
git merge feat/login
# 合併完、分支用不到了就刪掉
git branch -d feat/login
注意:git switch 是 Git 2.23 之後的新指令,用來專門切換分支。舊指令 git checkout 同時負責切換分支跟丟棄檔案改動,語意混亂,現在已經拆成 git switch(切分支)與 git restore(丟棄改動)兩個。很多網路上的舊教學還在寫 git checkout -b,功能一樣但新語法比較清楚。
分支命名慣例
分支名 Git 本身不管,任何合法字串都能當分支名。但團隊協作時如果沒有命名規則,git branch 列出來會變成 fix、temp、kyle-test、new 一堆看不出目的的名字。業界常見的慣例是「前綴+斜線+簡短描述」的形式,像 feat/login、fix/api-timeout,前綴用來標示分支的類型:
| 前綴 | 用途 | 範例 |
|---|---|---|
feat/ | 新功能開發 | feat/google-login |
fix/ | 修 bug | fix/checkout-amount-wrong |
refactor/ | 重構,不改外部行為 | refactor/extract-auth-service |
docs/ | 只改文件 | docs/api-reference |
chore/ | 雜項(依賴升級、設定檔) | chore/upgrade-node-22 |
hotfix/ | 線上緊急修復 | hotfix/payment-500 |
release/ | 準備發版 | release/v2.3.0 |
描述的部分建議用小寫英文+連字號(kebab-case),例如 feat/user-profile-avatar,避免空白、底線、中文字。分支名會出現在指令列、GitHub 網址、CI 工作流 log 裡,純英文 kebab-case 最不容易踩到字元編碼或引號問題。如果公司用 Jira 或 Linear,常見做法是把 ticket 編號接在前綴後面,變成 feat/AUTH-123-google-login,這樣從分支名就能直接對到對應的 ticket。
前綴的選擇其實跟前面提到的 Conventional Commits 是同一套邏輯——當分支前綴與 commit 前綴使用相同詞彙,PR 標題與 CHANGELOG 的分類可以彼此呼應。這個呼應不是強制規範,但採用後整個團隊的歷史紀錄會變得一致。
merge 與 rebase
把一個分支的改動合進另一個分支,有兩種做法:git merge 會產生一個 merge commit,保留兩條歷史平行合流的樣子;git rebase 則把自己的 commit 搬到對方最新的 commit 上,讓歷史看起來像一條直線。
網路上常見「rebase 比 merge 乾淨、一律用 rebase」的說法,其實不完全對。Rebase 本質上是「改寫歷史」,如果分支已經 push 到遠端、別人也拉下來了,再 rebase 會讓協作者的歷史跟遠端對不上,很煩人。實務上的安全守則是:公開歷史用 merge,本地還沒 push 的 commit 可以用 rebase 整理。
反悔技巧:stash、restore、reset、revert
Git 強大的地方在於「幾乎任何操作都可以反悔」。只要改動有被 Git 看過(進過 staging 或 commit),就幾乎不可能真的弄丟。以下四個指令是日常反悔的主力。
git stash — 暫存未完成的工作
場景:手邊改到一半想先切分支做別的事,但現在的改動還不想 commit。
# 把目前工作目錄的改動暫存起來
git stash push -m "進行中的登入功能"
# 看有哪些 stash
git stash list
# 把最近一筆 stash 拿回來
git stash pop
# 拿回特定一筆 stash(不刪除 stash 紀錄)
git stash apply stash@{1}
git restore — 丟棄工作目錄的改動
場景:改了檔案發現越改越亂,想直接回到上一次 commit 的狀態。
# 丟棄單一檔案的未 staged 改動
git restore README.md
# 丟棄所有未 staged 改動(危險,確認後再下)
git restore .
# 把已經 add 的檔案從 staging 拿掉(但保留工作目錄的改動)
git restore --staged README.md
git reset — 回退 commit
git reset 是很多新手搞不清楚的指令,關鍵在於它有 --soft、--mixed、--hard 三種模式,對三個區域的影響完全不同。

# --soft:只把 HEAD 往回移,staging 跟工作目錄不動
# 用途:想把剛才的 commit 拆開重新 commit
git reset --soft HEAD~1
# --mixed(預設):HEAD 往回移、staging 清掉,但工作目錄保留
# 用途:想取消 commit 但保留改動,重新挑選要 commit 的檔案
git reset HEAD~1
# --hard:三個區域全部清掉,回到目標 commit 的乾淨狀態
# 用途:想徹底丟掉 commit 跟所有相關改動(危險!改動會不見)
git reset --hard HEAD~1
注意:--hard 會真的把未 commit 的改動永久丟掉,沒有垃圾桶可以救。下這個指令前務必 git status 確認工作目錄沒有重要改動。另外 reset 只適合用在「還沒 push 到遠端」的 commit,已經公開的歷史請用下面的 revert。
git revert — 安全地「反做」一筆 commit
場景:某筆 commit 已經 push 上去、別人都拉下來了,現在發現有 bug 要還原。revert 會產生一筆新的 commit 來「抵銷」原本那筆,不會改寫歷史。
# 產生一筆新 commit 來反做指定 commit
git revert abc1234
# 反做最近一筆
git revert HEAD

上圖的觀察重點是下排的「檔案狀態」:兩種方法最終的工作目錄都等於 B 的內容,差別在於 歷史的樣子。reset --hard B 把 HEAD 拉回 B、C 從歷史徹底消失,commit 數量變少;revert C 則是新增一筆 D,D 的 diff 正好是 C 的反向,歷史變成 A→B→C→D,commit 數量變多。因此當 C 已經推到遠端(別人本地也有那筆)時只能用 revert——reset 會造成歷史分歧,別人下次 pull 會撞上「非 fast-forward」錯誤。簡單記:本地亂改用 reset,公開歷史用 revert。
AI Agent 時代的 Git 協作
從這章開始進入本文最想分享的內容。Agent(Claude Code、Cursor、Copilot Agent、Aider 等)已經不是純補完工具,而是會自己讀檔、改檔、執行指令的同事。跟這種同事合作,Git 不只是輔助工具,而是協作介面本身。以下七個 pattern 是長期跟 Agent 一起工作後歸納出的實用做法。
Agent 改完先 git diff 再接受
Agent 是同事不是老闆,它的產出值得信任但也要驗證。最便宜的驗證方法就是 git diff:
# 看 Agent 在工作目錄做了哪些未 stage 的改動
git diff
# 看已經 stage 的改動
git diff --staged
# 只看檔案名稱清單,不看詳細內容(先掃一眼改了哪些檔)
git diff --stat
# 只看某個目錄的改動
git diff src/auth/
Agent 寫的 code 常見三種小問題:多加不必要的 import、修到不該修的地方、留下除錯用的 console.log。先跑一次 git diff --stat 看動了哪些檔,再用 git diff 細看邏輯,比直接按「Accept」安全得多。這個習慣養成後,debug 時間會明顯縮短。
另外一個好消息是,現在看 diff 的管道比以前多很多,不一定要在 terminal 裡硬讀。Claude Code、Cursor、Copilot 這類 Agent 工具本身在每次改動後都會直接秀 diff 視窗讓我們確認;VS Code、Antigravity、JetBrains 這類 IDE 都有內建的 Source Control 面板,一鍵就能看到逐檔、逐行的改動並且可以選擇性 stage;Sourcetree、Gitfox、Fork 這類本地 GUI 工具則提供更完整的視覺化比對;推到遠端後,GitHub、GitLab、Gitea 的網頁介面也會直接把 PR 的改動以 side-by-side 或 unified 格式呈現。
小 commit,讓 revert 變成後悔藥
鼓勵 Agent 一次只做一件事,並且頻繁 commit。這樣出問題時,可以精準 revert 那一筆,其他改動全部保留。反過來說,如果讓 Agent 一次改 15 個檔案、最後一個 commit 通通包進去,發現其中一個檔案有問題時只能整筆 revert,連其他 14 個改對的檔案一起丟掉。
實務上可以在 CLAUDE.md、.github/copilot-instructions.md 等 Agent 設定檔裡明確要求「每完成一個獨立步驟就建立一個 commit」,例如「新增 schema」「實作 handler」「補測試」分三筆。關於 Copilot 的設定檔,可以參考 GitHub Copilot Agent 設定教學。
用 git diff 把關,做不做合併由人決定
把 Git diff 看成 code review,建立一個穩定的人機協作迴圈:Agent 提案改動 → 人看 diff → 通過就 commit、不通過就要求 Agent 修,直到滿意為止。這個迴圈如下圖:

迴圈的關鍵是「不要直接把 Agent 的輸出當成最終版本」。即使 Agent 說「我已經完成了、測試都通過」,也建議在 commit 之前看一次 diff、跑一次測試。這不是不信任 Agent,而是知道 Agent 對「完成」的定義可能跟人不同 —— 它可能認為跑過單元測試就叫完成,但沒注意到 integration test 掛了。
用 branch 或 worktree 隔離 Agent 的工作
不要讓 Agent 直接在 main 分支上幹活,開一個 feat/xxx 分支給它,翻車只要 git switch main + git branch -D feat/xxx 就沒事。更進一步,可以用 git worktree 讓多個 Agent 同時在不同分支、不同資料夾並行工作,互不干擾:
# 在 ../project-feat-login 建立新的 worktree,用 feat/login 分支
git worktree add ../project-feat-login feat/login
# 同時在另一個地方開第二個 worktree
git worktree add ../project-feat-payment feat/payment
# 列出所有 worktree
git worktree list
# 用完後移除(注意:不會刪掉 branch)
git worktree remove ../project-feat-login
這樣主資料夾繼續寫 code,同時可以另開兩個終端機,在兩個 worktree 裡各跑一個 Claude Code 或 Cursor session,一個做登入、一個做金流,彼此完全隔離。架構如下:

Worktree 的好處還有:不用頻繁切分支、IDE 可以同時開兩個視窗各看一個功能的 code、Agent 執行長時間任務時不會卡住主工作流程。這大概是 Git 最近幾年被 AI 工具放大價值最明顯的功能。
用 pre-commit hook 攔住 Agent 漏掉的檢查
Agent 常忽略的事情:跑 linter、檢查型別、跑測試。即使 CLAUDE.md 要求「commit 前務必 run tests」,Agent 在忙著解更大問題時還是會忘記。這時候 pre-commit hook 是最後一道防線:
# 在 .git/hooks/pre-commit 建立一個 shell script
#!/bin/sh
# 檢查 lint
npm run lint || exit 1
# 跑測試
npm test || exit 1
# 檢查是否有 console.log 殘留
if git diff --cached | grep -q "console.log"; then
echo "偵測到 console.log,請移除後再 commit"
exit 1
fi
記得給這個檔案執行權限(chmod +x .git/hooks/pre-commit)。更進階的做法是用 pre-commit 框架或 Husky(Node.js 專案),讓 hook 設定可以一起進版控、團隊共用。當 Agent 下 git commit 時 hook 會自動跑,沒通過就擋下來,Agent 看到錯誤訊息後會自己嘗試修。
commit message 讓 Agent 寫,但不要盲信
Agent 很會寫 commit message,省人力。但實測下來有兩個常見坑:
- 分類下錯:明明是 refactor,Agent 誤判成 feat;小修 bug 用 feat 前綴讓 CHANGELOG 看起來一堆新功能
- 過度描述:一筆 commit 只改三行,Agent 寫出一整段解釋,看起來很專業但其實
git log --oneline看不清楚
建議在 CLAUDE.md 裡寫清楚規則:「使用 Conventional Commits、第一行不超過 72 字、只寫 what 不寫 why(why 留給 PR 描述)」。commit 前快速掃一眼 message,前綴不對就改,改完再 commit 並不費事。
用約定式提交(Conventional Commits)讓 Agent 有規則可循
上一節提到的 Conventional Commits(中文常譯作「約定式提交」)是一套替 commit message 加上結構化前綴的慣例,由 conventionalcommits.org 維護規格。格式很簡單:<type>(<scope>): <描述>,例如 feat(auth): 新增 Google 登入。這套慣例的好處在於它讓 commit 歷史可以被機器解析——CI 流程可以自動根據 commit 類型決定要不要觸發 release、CHANGELOG 工具可以自動把 feat 歸到「新功能」、fix 歸到「錯誤修正」,不必手工整理。
常用的 type 就那幾個,記熟之後寫 commit 不太需要思考:
| 類型 | 用在什麼時候 | 範例 |
|---|---|---|
feat | 新增功能(使用者能感知到的) | feat(search): 支援日期區間篩選 |
fix | 修正 bug | fix(auth): 修正 token 過期時未重導向 |
refactor | 重構,外部行為不變 | refactor(api): 抽出 request 驗證邏輯 |
docs | 只動文件 | docs(readme): 補充安裝步驟 |
test | 補測試、調整測試 | test(user): 補齊 email 驗證的邊界測試 |
chore | 雜項(依賴升級、設定檔、工具調整) | chore(deps): 升級 axios 到 1.7 |
perf | 效能改善 | perf(list): 虛擬捲動改善大列表延遲 |
style | 純排版、格式、不影響邏輯 | style: 套用 prettier 格式化 |
ci | CI/CD 相關調整 | ci: 加入 Node 22 測試矩陣 |
Breaking change(破壞性變更)有兩種標記方式:type 後加 !(例如 feat!: 改用新版 API schema),或是在 commit body 加上 BREAKING CHANGE: 說明區塊。兩種都會被解析工具識別,自動把版本號跳到 major。
Agent 寫的 commit message 如果只靠自由發揮,容易每次風格都不一樣——這次 Add login feature、下次 implemented user auth,CHANGELOG 會變成一鍋粥。在 CLAUDE.md、.github/copilot-instructions.md 或 Cursor Rules 裡明確寫「所有 commit message 使用 Conventional Commits 規範、使用繁體中文描述」,Agent 就會穩定產出一致格式。可以在專案根目錄附上一個簡短 cheat sheet 讓 Agent 對照。我們只要在 commit 前快速檢查「type 是不是下對、scope 是不是太大」,大多數情況 Agent 寫的已經能直接用。
反悔組合拳:reset –hard + clean -fd
最後分享當 Agent 把 local 搞得一塌糊塗,最快的復原方式是:
# 回到最近一次 commit 的乾淨狀態(三個區域全清)
git reset --hard HEAD
# 把沒被 Git 追蹤的新檔案跟資料夾也清掉(Agent 亂建的檔案在這步消失)
git clean -fd
# 如果連 .gitignore 裡的也要清(更狠)
git clean -fdx
注意:這個組合非常危險,所有未 commit 的改動都會永久消失。它只適合用在「確認 Agent 的改動不要了、要回到上次 commit 的狀態」的情境。在執行前先 git status 跟 git stash 一下比較安全。但對「Agent 亂改一通我想重來」這個場景,這兩行是最快的。
日常指令速查表
把最常用的指令集中在一張表,寫文章寫到最後連我都要查一下的那種。
| 情境 | 指令 | 備註 |
|---|---|---|
| 看狀態 | git status | 下其他指令前先看一次 |
| 加入 staging | git add <file> | 避免 git add . |
| 建立 commit | git commit -m "..." | 遵循 Conventional Commits |
| 看歷史 | git log --oneline --graph | 建議設成 alias |
| 推上遠端 | git push | 第一次加 -u origin <branch> |
| 拉下遠端 | git pull | 謹慎用,可先 fetch |
| 切換分支 | git switch <branch> | 新指令,取代 checkout |
| 開新分支 | git switch -c <branch> | 建立並切過去 |
| 看改動 | git diff | Agent 產出先看再接受 |
| 丟棄改動 | git restore <file> | 未 staged 的改動 |
| 暫存工作 | git stash | 改到一半要切分支 |
| 回退 commit | git reset --soft HEAD~1 | 保留改動,重寫 commit |
| 反做 commit | git revert <hash> | 公開歷史唯一安全做法 |
| 多 worktree | git worktree add ... | 多 Agent 並行必備 |
學會 Git 之後,AI Agent 才真的變成能放膽使用的工具,而不是每次都擔心檔案被改壞。因此對現在的開發者來說,Git 不再只是「團隊協作工具」,它是跟 Agent 共事時最重要的安全帶、溝通介面,甚至是信任的基礎。指令背後的三區心智模型一旦建立起來,後續只是熟練度問題——每次用到新指令,回到「它把檔案從哪裡搬到哪裡」這個問法,十次有九次能自己推敲出來。