Git 工作流程模式 是做什麼的?
Git 版本控制、分支策略與協作開發的最佳實踐。
何時啟用
- 為新專案設定 Git 工作流程
- 決定分支策略(GitFlow、主幹開發、GitHub flow)
- 撰寫提交訊息與 PR 描述
- 解決合併衝突
- 管理發布與版本標籤
- 讓新團隊成員熟悉 Git 實務
分支策略
GitHub Flow(簡單,推薦用於多數專案)
最適合持續部署與中小型團隊。
main (受保護,隨時可部署)
│
├── feature/user-auth → PR → 合併至 main
├── feature/payment-flow → PR → 合併至 main
└── fix/login-bug → PR → 合併至 main
規則:
main隨時可部署- 從
main建立功能分支 - 準備好審查時開啟 Pull Request
- 審查通過且 CI 通過後,合併至
main - 合併後立即部署
主幹開發(高速度團隊)
最適合具備強大 CI/CD 與功能開關的團隊。
main (主幹)
│
├── 短期功能分支 (最多 1-2 天)
├── 短期功能分支
└── 短期功能分支
規則:
- 所有人直接提交至
main或極短暫的分支 - 功能開關隱藏未完成的功能
- 合併前 CI 必須通過
- 每天可部署多次
GitFlow(複雜,以發布週期為導向)
最適合排程發布與企業專案。
main (正式發布版本)
│
└── develop (整合分支)
│
├── feature/user-auth
├── feature/payment
│
├── release/1.0.0 → 合併至 main 與 develop
│
└── hotfix/critical → 合併至 main 與 develop
規則:
main僅包含可正式上線的程式碼develop是整合分支- 功能分支從
develop分出,合併回develop - 發布分支從
develop分出,合併至main與develop - 修補分支從
main分出,合併至main與develop
何時使用哪種策略
| 策略 | 團隊規模 | 發布節奏 | 最適合情境 |
|---|---|---|---|
| GitHub Flow | 任何規模 | 持續部署 | SaaS、網頁應用、新創 |
| 主幹開發 | 5 人以上經驗豐富 | 每天多次 | 高速度團隊、功能開關 |
| GitFlow | 10 人以上 | 排程發布 | 企業、受監管行業 |
提交訊息
Conventional Commits 格式
<類型>(<範疇>): <主旨>
[可選內文]
[可選頁尾]
類型
| 類型 | 用途 | 範例 |
|---|---|---|
feat | 新功能 | feat(auth): add OAuth2 login |
fix | 錯誤修正 | fix(api): handle null response in user endpoint |
docs | 文件 | docs(readme): update installation instructions |
style | 格式調整,無程式碼變更 | style: fix indentation in login component |
refactor | 程式碼重構 | refactor(db): extract connection pool to module |
test | 新增或更新測試 | test(auth): add unit tests for token validation |
chore | 維護任務 | chore(deps): update dependencies |
perf | 效能改善 | perf(query): add index to users table |
ci | CI/CD 變更 | ci: add PostgreSQL service to test workflow |
revert | 復原先前的提交 | revert: revert "feat(auth): add OAuth2 login" |
好範例與壞範例
# 壞:模糊、無上下文
git commit -m "fixed stuff"
git commit -m "updates"
git commit -m "WIP"
# 好:清晰、具體、說明原因
git commit -m "fix(api): retry requests on 503 Service Unavailable
外部 API 在尖峰時段偶爾會回傳 503 錯誤。
新增指數退避重試邏輯,最多 3 次嘗試。
Closes #123"
提交訊息範本
在儲存庫根目錄建立 .gitmessage:
# <類型>(<範疇>): <主旨>
# # 類型:feat, fix, docs, style, refactor, test, chore, perf, ci, revert
# 範疇:api, ui, db, auth 等
# 主旨:祈使語氣,無句號,最多 50 字元
#
# [可選內文] - 解釋原因,而非內容
# [可選頁尾] - 重大變更、關閉問題編號
啟用方式:git config commit.template .gitmessage
合併與 Rebase
合併(保留歷史)
# 建立合併提交
git checkout main
git merge feature/user-auth
# 結果:
# * 合併提交
# |\
# | * 功能分支的提交
# |/
# * main 的提交
適用時機:
- 將功能分支合併至
main - 想要保留完整的歷史記錄
- 多人共同作業於同一個分支
- 分支已推送且其他人可能基於此分支繼續開發
Rebase(線性歷史)
# 將功能分支的提交重新建立在目標分支之上
git checkout feature/user-auth
git rebase main
# 結果:
# * 功能分支的提交(已重寫)
# * main 的提交
適用時機:
- 將本機功能分支更新至最新的
main - 想要簡潔、線性的歷史
- 分支僅存在於本機(未推送)
- 只有你一個人在此分支上開發
Rebase 工作流程
# 在發 PR 之前,用最新 main 更新功能分支
git checkout feature/user-auth
git fetch origin
git rebase origin/main
# 解決衝突
# 測試應仍然通過
# 強制推送(僅限於你獨自貢獻的分支)
git push --force-with-lease origin feature/user-auth
何時不該使用 Rebase
# 絕對不要對下列分支使用 rebase:
- 已推送至共享儲存庫的分支
- 其他人已基於此分支開發的分支
- 受保護分支(main, develop)
- 已合併的分支
# 原因:Rebase 會重寫歷史,破壞他人的工作
Pull Request 工作流程
PR 標題格式
<類型>(<範疇>): <描述>
範例:
feat(auth): add SSO support for enterprise users
fix(api): resolve race condition in order processing
docs(api): add OpenAPI specification for v2 endpoints
PR 描述範本
## 做了什麼
此 PR 的簡短說明。
## 為什麼做
說明動機與背景。
## 如何實作
值得強調的關鍵實作細節。
## 測試
- [ ] 單元測試已新增/更新
- [ ] 整合測試已新增/更新
- [ ] 已執行手動測試
## 螢幕截圖(如適用)
UI 變更的前後對比截圖。
## 檢查清單
- [ ] 程式碼遵循專案風格指南
- [ ] 已完成自我審查
- [ ] 複雜邏輯已加入註解
- [ ] 文件已更新
- [ ] 未引入新的警告
- [ ] 本機測試通過
- [ ] 相關問題已連結
Closes #123
程式碼審查清單
給審查者:
- 程式碼是否解決了陳述的問題?
- 是否遺漏了任何邊界情況?
- 程式碼是否可讀且易於維護?
- 是否有足夠的測試?
- 是否有安全疑慮?
- 提交歷史是否整潔(必要時已壓縮)?
給作者:
- 請求審查前已完成自我審查
- CI 通過(測試、lint、型別檢查)
- PR 規模合理(理想狀態小於 500 行)
- 僅涉及單一功能或修正
- 描述清楚說明變更內容
衝突解決
辨識衝突
# 在合併前檢查衝突
git checkout main
git merge feature/user-auth --no-commit --no-ff
# 若有衝突,Git 會顯示:
# CONFLICT (content): Merge conflict in src/auth/login.ts
# Automatic merge failed; fix conflicts and then commit the result.
解決衝突
# 查看衝突檔案
git status
# 在檔案中檢視衝突標記
# <<<<<<< HEAD
# 來自 main 的內容
# =======
# 來自功能分支的內容
# >>>>>>> feature/user-auth
# 選項 1:手動解決
# 編輯檔案,移除標記,保留正確內容
# 選項 2:使用合併工具
git mergetool
# 選項 3:接受其中一方
git checkout --ours src/auth/login.ts # 保留 main 版本
git checkout --theirs src/auth/login.ts # 保留功能分支版本
# 解決後,暫存並提交
git add src/auth/login.ts
git commit
衝突預防策略
# 1. 保持功能分支小而短命
# 2. 頻繁對 main 進行 rebase
git checkout feature/user-auth
git fetch origin
git rebase origin/main
# 3. 與團隊溝通關於觸碰共用檔案
# 4. 使用功能開關取代長期分支
# 5. 及時審查並合併 PR
分支管理
命名慣例
# 功能分支
feature/user-authentication
feature/JIRA-123-payment-integration
# 錯誤修正
fix/login-redirect-loop
fix/456-null-pointer-exception
# 修補(線上問題)
hotfix/critical-security-patch
hotfix/database-connection-leak
# 發布
release/1.2.0
release/2024-01-hotfix
# 實驗/POC
experiment/new-caching-strategy
poc/graphql-migration
分支清理
# 刪除已合併的本機分支
git branch --merged main | grep -v "^\*\|main" | xargs -n 1 git branch -d
# 清理遠端追蹤參考(已刪除的遠端分支)
git fetch -p
# 刪除本機分支
git branch -d feature/user-auth # 安全刪除(僅限已合併)
git branch -D feature/user-auth # 強制刪除
# 刪除遠端分支
git push origin --delete feature/user-auth
Stash 工作流程
# 暫存進行中的工作
git stash push -m "WIP: 使用者認證"
# 列出暫存項目
git stash list
# 套用最近的暫存
git stash pop
# 套用特定暫存
git stash apply stash@{2}
# 丟棄暫存
git stash drop stash@{0}
發布管理
語意化版本
MAJOR.MINOR.PATCH
MAJOR:重大變更
MINOR:新功能,向下相容
PATCH:錯誤修正,向下相容
範例:
1.0.0 → 1.0.1 (patch:錯誤修正)
1.0.1 → 1.1.0 (minor:新功能)
1.1.0 → 2.0.0 (major:重大變更)
建立發布
# 建立附註標籤
git tag -a v1.2.0 -m "Release v1.2.0
功能:
- 新增使用者認證
- 實作密碼重設
修正:
- 解決登入重新導向問題
重大變更:
- 無"
# 推送標籤至遠端
git push origin v1.2.0
# 列出標籤
git tag -l
# 刪除標籤
git tag -d v1.2.0
git push origin --delete v1.2.0
變更日誌產生
# 從提交產生變更日誌
git log v1.1.0..v1.2.0 --oneline --no-merges
# 或使用 conventional-changelog
npx conventional-changelog -i CHANGELOG.md -s
Git 設定
必要設定
# 使用者身分
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
# 預設分支名稱
git config --global init.defaultBranch main
# 拉取行為(使用 rebase 而非合併)
git config --global pull.rebase true
# 推送行為(僅推送目前分支)
git config --global push.default current
# 自動修正拼寫錯誤
git config --global help.autocorrect 1
# 更好的 diff 演算法
git config --global diff.algorithm histogram
# 彩色輸出
git config --global color.ui auto
實用別名
# 加入 ~/.gitconfig
[alias]
co = checkout
br = branch
ci = commit
st = status
unstage = reset HEAD --
last = log -1 HEAD
visual = log --oneline --graph --all
amend = commit --amend --no-edit
wip = commit -m "WIP"
undo = reset --soft HEAD~1
contributors = shortlog -sn
Gitignore 模式
# 相依套件
node_modules/
vendor/
# 建置輸出
dist/
build/
*.o
*.exe
# 環境檔案
.env
.env.local
.env.*.local
# IDE
.idea/
.vscode/
*.swp
*.swo
# 作業系統檔案
.DS_Store
Thumbs.db
# 日誌
*.log
logs/
# 測試覆蓋率
coverage/
# 快取
.cache/
*.tsbuildinfo
常見工作流程
開始新功能
# 1. 更新 main 分支
git checkout main
git pull origin main
# 2. 建立功能分支
git checkout -b feature/user-auth
# 3. 進行變更並提交
git add .
git commit -m "feat(auth): implement OAuth2 login"
# 4. 推送至遠端
git push -u origin feature/user-auth
# 5. 在 GitHub/GitLab 上建立 Pull Request
使用新變更更新 PR
# 1. 進行額外變更
git add .
git commit -m "feat(auth): add error handling"
# 2. 推送更新
git push origin feature/user-auth
同步 Fork 與上游
# 1. 新增 upstream 遠端(僅一次)
git remote add upstream https://github.com/original/repo.git
# 2. 擷取 upstream
git fetch upstream
# 3. 將 upstream/main 合併至你的 main
git checkout main
git merge upstream/main
# 4. 推送至你的 fork
git push origin main
復原錯誤
# 復原最後一次提交(保留變更)
git reset --soft HEAD~1
# 復原最後一次提交(捨棄變更)
git reset --hard HEAD~1
# 復原已推送至遠端的最後一次提交
git revert HEAD
git push origin main
# 復原特定檔案的變更
git checkout HEAD -- path/to/file
# 修正最後一次提交的訊息
git commit --amend -m "新訊息"
# 將遺漏的檔案加入最後一次提交
git add 遺漏的檔案
git commit --amend --no-edit
Git Hooks
Pre-Commit Hook
#!/bin/bash
# .git/hooks/pre-commit
# 執行 lint
npm run lint || exit 1
# 執行測試
npm test || exit 1
# 檢查機密資訊
if git diff --cached | grep -E '(password|api_key|secret)'; then
echo "偵測到可能的機密資訊。提交已中止。"
exit 1
fi
Pre-Push Hook
#!/bin/bash
# .git/hooks/pre-push
# 執行完整測試套件
npm run test:all || exit 1
# 檢查 console.log 陳述式
if git diff origin/main | grep -E 'console\.log'; then
echo "推送前請移除 console.log 陳述式。"
exit 1
fi
反模式
# 壞:直接提交至 main
git checkout main
git commit -m "fix bug"
# 好:使用功能分支與 PR
# 壞:提交機密資訊
git add .env # 包含 API 金鑰
# 好:加入 .gitignore,使用環境變數
# 壞:巨大的 PR(超過 1000 行)
# 好:拆分成更小、更專注的 PR
# 壞:「更新」類的提交訊息
git commit -m "update"
git commit -m "fix"
# 好:具描述性的訊息
git commit -m "fix(auth): resolve redirect loop after login"
# 壞:重寫公開歷史
git push --force origin main
# 好:對公開分支使用 revert
git revert HEAD
# 壞:長期功能分支(數週或數月)
# 好:保持分支短命(數天),頻繁 rebase
# 壞:在合併前壓縮提交會遺失脈絡
# 好:審查後再壓縮,或保持乾淨的提交歷史
# 壞:忽略衝突標記(``<<<<<<<``)
# 好:仔細解決每個衝突