一. 前言:真正需要管理的不是 branch,而是混亂 #
很多人剛學 Git 時,
最先記住的是 add、commit、push。
等到專案開始多人協作,
才會發現 branch 才是真正決定 repo 健康度的地方。
你可能看過這些場景:
- 所有人都直接往
main推 - 功能做到一半才想到要切 branch
- 緊急修 bug 時,把半成品一起帶進正式環境
- PR 大到 reviewer 根本不想看
- repo 裡躺著一堆兩個月前的殭屍分支
這些問題的核心, 不是 Git 指令不夠熟。 而是 branch strategy 沒有先講清楚。
一套好的 branch 策略, 應該回答這幾個問題:
- 哪個分支代表可部署版本?
- 新功能從哪裡切出去?
- 緊急修正時該走哪條路?
- PR 應該如何合併才不會讓歷史失控?
今天這篇,
拍拍君想用最實用的版本來講。
不從過度複雜的企業流程開始,
而是從多數團隊最夠用的模型出發:
main、feature/*、hotfix/*。
如果你還沒熟悉 Git 基礎, 可以先看拍拍君之前寫的 Git 入門。 如果你想把分支策略接上 CI/CD, 之後再搭配 GitHub Actions 入門 與 GitHub Actions 進階 會很順。
二. 安裝與基本設定:先把預設整理好 #
分支策略本身不是安裝教學,
但很多混亂其實從本機預設開始。
有人新 repo 預設是 master,
有人是 main;
有人 pull 習慣 rebase,
有人是 merge。
如果你希望團隊規則穩定, 最起碼先把自己的 Git 設定整理好。
先確認版本:
git --version
如果還沒安裝 Git:
# macOS
brew install git
# Ubuntu / Debian
sudo apt update
sudo apt install git
# Windows
winget install --id Git.Git -e
設定作者資訊:
git config --global user.name "拍拍君"
git config --global user.email "pypy@example.com"
把預設主分支改成 main:
git config --global init.defaultBranch main
這個設定很小, 但對團隊一致性很重要。 你的文件、腳本、CI 設定, 都會因此少掉很多分支名稱差異。
拍拍君還會順手加上這幾個:
git config --global fetch.prune true
git config --global rerere.enabled true
git config --global pull.rebase false
它們的作用分別是:
fetch.prune true:遠端刪掉的 branch, 本機追蹤也會順便清理rerere.enabled true: Git 會記住你處理過的衝突方式pull.rebase false: 預設git pull先走 merge 模式, 對多數初學者更直觀
可以用這個指令檢查:
git config --global --list | rg 'user|init.defaultBranch|fetch.prune|rerere|pull.rebase'
設定不是 branch strategy 的全部, 但它是很好的起跑點。 如果連主分支名稱都不統一, 後面流程通常也很難乾淨。
三. 最小可用模型:main 穩定,feature/* 短命
#
對多數團隊來說, 最實用的 branch strategy 不一定是 Git Flow。 很多時候, 下面這套就已經很夠用:
main:永遠保持可發布、可回滾、可信任feature/*:每個功能一條 branch,做完就合併hotfix/*:線上出事時,從main切出修正
你可以把它想成這樣:
main
├─ feature/login-page
├─ feature/payment-api
└─ hotfix/session-timeout
這裡最重要的概念只有一個:
main 不是工作台,main 是可交付狀態。
也就是說,
你在 main 上看到的內容,
應該是隨時可以拿去部署、打 tag、回滾的。
如果團隊把半完成的內容直接丟進 main,
那 main 就失去了「主幹」該有的意義。
3.1 建一個示範 repo #
我們先用一個小專案感受這個流程。
mkdir branch-demo
cd branch-demo
git init
echo '# Branch Demo' > README.md
git add README.md
git commit -m 'chore: initialize repository'
現在看分支:
git branch
你應該會看到:
* main
3.2 新功能永遠從 main 切出去
#
假設今天要做登入頁面,
請不要直接在 main 上開始寫。
標準手勢應該是:
git switch main
git pull origin main
git switch -c feature/login-page
接著才開始開發:
mkdir -p src
echo 'console.log("login page")' > src/login.js
git add src/login.js
git commit -m 'feat: add initial login page'
如果你又補了表單驗證, 可以再切一個小 commit:
echo 'console.log("validate input")' >> src/login.js
git add src/login.js
git commit -m 'feat: add login form validation'
這樣做的好處很直接:
main不會被半成品污染- 每個功能的變更範圍清楚
- PR 開出來時比較好 review
- 日後要回滾也比較容易定位
3.3 為什麼 feature branch 要短命? #
很多 repo 的問題不是 branch 太少, 而是 branch 活太久。 一條功能分支如果拖兩週、三週, 通常就會開始出現這些症狀:
- 與
main的差距越來越大 - 合併衝突越積越多
- PR 變成超大包,沒人想 review
- 作者自己都忘了某段改動原本為什麼存在
所以拍拍君的建議是:
- 一條 feature branch 只做一件事
- 最好能在 1 到 3 天內完成
- 功能太大時,拆成多個可逐步合併的小 PR
branch 的價值, 不是讓你把所有事塞進同一條支線。 而是讓你把工作切成可管理、可合併、可驗證的小單位。
3.4 branch 命名不要靠通靈 #
如果分支名稱只有作者自己看得懂, 團隊協作時就會很痛苦。 拍拍君偏好的命名格式是:
feature/<topic>
fix/<topic>
hotfix/<topic>
chore/<topic>
refactor/<topic>
例如:
feature/login-page
feature/oauth-github
fix/avatar-cache-bug
hotfix/payment-timeout
refactor/user-service-split
如果團隊平常會搭配 issue 編號, 也可以改成這樣:
feature/142-login-page
hotfix/317-session-null
命名規則沒有唯一真理, 但整個團隊必須一致。 規則一旦統一, PR 列表、CI 腳本、文件說明都會輕鬆很多。
四. feature/* 的完整生命週期:從切出到刪除
#
很多文章只說「切分支、合回來」。 真正容易出問題的, 反而是中間那段日常節奏。
拍拍君比較推薦的流程是:
- 從最新的
main切出feature/* - 小步 commit
- 定期同步
main - 開 Pull Request
- review 通過後合併
- 刪除 branch
4.1 切 branch 前,先同步主幹 #
不要從一個過期的 main 切出新 branch。
請先更新:
git switch main
git pull origin main
git switch -c feature/oauth-github
這個習慣可以幫你減少很多沒必要的衝突。
如果你從三天前的 main 切出去,
那你的 feature branch 一開始就已經落後。
4.2 開發途中,要定期把 main 帶進來
#
假設你做功能做到一半,
團隊另外兩個 PR 已經合進 main。
這時不要假裝沒看到。
常見同步方式有兩種。 第一種是 merge:
git switch feature/oauth-github
git fetch origin
git merge origin/main
第二種是 rebase:
git switch feature/oauth-github
git fetch origin
git rebase origin/main
怎麼選? 拍拍君的建議很務實:
- 團隊剛起步、對 rebase 不熟: 先用 merge,同步成本低
- 想保留線性歷史、作者自己很熟 Git: 合併前可用 rebase 整理
- 不熟 rebase 時: 不要在共享 branch 上亂改歷史
也就是說, rebase 很強, 但不是每個團隊都要第一天就全員上手。 先把節奏跑順, 比一開始就追求漂亮歷史更重要。
4.3 開 PR 前,把 commit 稍微整理一下 #
如果你的 commit 長這樣:
wip
fix
try again
final
final2
那 reviewer 很難快速看懂你真正做了什麼。 比較理想的訊息是:
feat: add GitHub OAuth login flow
feat: persist oauth state in session
test: add callback route integration tests
docs: document local oauth environment variables
如果這條 branch 只有你自己在用, 可以在開 PR 前用互動式 rebase 稍微整理:
git rebase -i origin/main
不需要為了「歷史漂亮」硬修到神經緊繃, 但至少讓 commit 反映出真正的邏輯單位。 未來 debug 或追蹤問題時, 你會非常感謝現在的自己。
4.4 合併後一定要刪 branch #
很多 repo 的分支列表之所以難看, 不是因為開太多 branch, 而是因為做完之後沒人清。
合併完成後, 記得清掉本機與遠端分支:
git branch -d feature/oauth-github
git push origin --delete feature/oauth-github
如果你有開 fetch.prune,
之後同步時本機追蹤資料也會跟著整理。
短命 branch 的最後一環, 就是讓它真的離場。
五. hotfix/* 才是救火線:修 production 不要順手夾帶半成品
#
很多團隊在壓力大的時候, 最容易做錯的就是 hotfix 流程。 看似只是「快點修」, 實際上卻把還沒完成的內容一起推上線。
想像一個情境。
目前正式環境跑的是昨天從 main 部署的版本。
同時有人正在 feature/payment-refactor 裡大改付款邏輯,
還沒有做完。
這時線上突然出現 session 過期 bug,
使用者無法正常結帳。
如果你直接在那條重構分支上修, 再想辦法一起 merge 回來, 就有機會把未完成的付款重構也帶進 production。 這不是 hotfix, 這是把火場跟施工現場綁在一起。
正確做法是:
從可信任的 main 切出 hotfix/*。
git switch main
git pull origin main
git switch -c hotfix/session-timeout
然後只做最小修正:
vim src/session.js
git add src/session.js
git commit -m 'fix: handle expired session refresh correctly'
修好後開 PR 回 main,
review、合併、部署。
必要時可以順手打 tag:
git tag v1.2.3
git push origin v1.2.3
5.1 hotfix merge 完,別忘了同步回進行中的分支 #
這一步非常多人漏掉。
如果 hotfix 已經進 main,
但正在開發的 feature branch 沒有同步最新主幹,
那幾天後這條 branch merge 回來時,
舊 bug 可能會復活。
所以 hotfix 完成後, 正在進行中的功能分支要記得同步:
git switch feature/payment-refactor
git fetch origin
git merge origin/main
或是:
git rebase origin/main
真正完整的 hotfix 流程, 不只是在 production 救火, 也要避免舊分支把火再帶回來。
5.2 什麼時候需要 release/*?
#
拍拍君的答案很誠實: 多數小團隊其實不需要。
如果你們的節奏是:
- PR 合進
main - CI 通過
- 很快就能部署
那 main + feature + hotfix 通常就夠用了。
release/* 比較適合這些情境:
- 你們有明確發版凍結期
- 需要維護多個已發布版本
- QA 驗證流程很重
- 發版與開發節奏分得很開
如果團隊規模還小, 先不要急著把流程做厚。 太多分支模型, 很多時候不是更專業, 只是更容易讓人忘記哪條才是真的主幹。
六. 團隊協作流程:branch 規則要跟 PR 規則一起設計 #
branch strategy 不是只有命名規則。 它必須跟 Pull Request、review、保護設定一起運作。
拍拍君會把協作規則拆成三層。
6.1 第一層:團隊口頭規則 #
先定幾條最基本也最能落地的:
- 不直接 push 到
main - 一個需求或 bugfix 對應一條 branch
- PR 標題要能看懂做了什麼
- PR 太大就拆小
- 合併前至少要有基本測試結果
這些規則看起來很普通, 但很多團隊其實連這五條都沒形成共識。 如果你現在 repo 很亂, 往往不是因為技術太難, 而是因為連最基本的約定都沒固定。
6.2 第二層:讓平台幫你守門 #
如果你用 GitHub、GitLab 或 Bitbucket,
請盡量把 main 保護起來。
常見設定包括:
- 禁止直接 push
- 禁止 force push
- 必須透過 Pull Request 合併
- 至少一位 reviewer approve
- 狀態檢查通過後才能 merge
- 分支同步最新
main後才能 merge
這些不是不信任同事, 而是把規則從「靠記憶遵守」 升級成「系統自動守門」。
如果你之後把 CI 接上來, branch strategy 的價值會更明顯。 例如:
- push 到 feature branch 自動跑 lint
- PR 自動跑單元測試
- merge 到
main再觸發部署
這時 branch 不只是工作切分工具, 也變成自動化流程的控制點。
6.3 第三層:決定 PR 要怎麼合進 main
#
Pull Request 的 merge 方式, 會直接影響歷史可讀性。 常見有三種。
Merge commit #
main ----o----o---------M
\ /
feature o----o----o
優點:
- 保留 branch 的存在痕跡
- 對多 commit PR 很直觀
- 不改寫原始 commit SHA
缺點:
- 歷史圖比較分岔
- 長期看起來可能偏花
Squash merge #
main ----o----o----S
feature o----o----o
優點:
main很乾淨- 一個 PR 對應一個 commit
- 回滾通常比較直觀
缺點:
- branch 上細碎但有價值的 commit 會被壓平
- 如果你很重視 commit 細節,資訊會少一點
Rebase and merge #
main ----o----o----o----o----o
feature o----o----o
優點:
- 歷史最線性
git log很舒服
缺點:
- 需要大家比較熟 Git
- 不小心時會改寫共享歷史
拍拍君自己的建議是:
- 小團隊、追求簡潔:先用
squash merge - 想保留 branch 脈絡:可用
merge commit - 團隊 Git 熟悉度高:再考慮
rebase and merge
沒有絕對標準答案。 真正重要的是整個團隊一致。
6.4 三種常見模型怎麼選? #
| 模型 | 分支結構 | 適合情境 | 代價 |
|---|---|---|---|
| Trunk-Based | main + 極短 branch |
高頻部署、成熟 CI 團隊 | 需要很強測試與 feature flag 習慣 |
| Main + Feature + Hotfix | main、feature/*、hotfix/* |
多數中小團隊 | 規則必須穩定執行 |
| Git Flow | main、develop、release/*、hotfix/*、feature/* |
發版重、維護多版本產品 | 複雜度高,新人容易迷路 |
如果你現在還在猶豫該用哪一種, 拍拍君通常會建議先選第二種。 先把簡單規則跑順, 再決定是否真的需要更厚重的流程。
七. 一次走完整個實戰:功能開發、hotfix、再同步 #
光看原則很容易抽象, 我們來模擬一個比較像真實團隊的情境。
現在有兩個人:
拍拍君跟拍拍醬。
repo 的 main 目前穩定。
今天同時發生兩件事:
- 拍拍君要做通知中心
- 拍拍醬要修頭像快取 bug
7.1 拍拍君開功能 branch #
git switch main
git pull origin main
git switch -c feature/notification-center
然後切幾個小 commit:
git add src/notifications/model.ts
git commit -m 'feat: add notification domain model'
git add src/notifications/api.ts
git commit -m 'feat: add notification fetch API'
git add src/notifications/view.tsx
git commit -m 'feat: add notification center UI'
此時功能 branch 很清楚, 就是專門承載通知中心這件事。
7.2 同時,拍拍醬從 main 開 hotfix
#
git switch main
git pull origin main
git switch -c hotfix/avatar-cache-bug
修完後提交:
git add src/avatar/cache.ts
git commit -m 'fix: invalidate avatar cache after profile update'
這個 PR merge 回 main 後,
production 問題就能先被處理。
而且因為 hotfix 是從 main 切出,
所以不會夾帶通知中心的半成品。
7.3 功能 branch 要同步最新 main
#
hotfix merge 完後, 拍拍君的 branch 不能當作沒看到。 他應該同步最新主幹:
git switch feature/notification-center
git fetch origin
git merge origin/main
如果團隊偏好 rebase, 也可以是:
git rebase origin/main
這一步的價值在於:
把衝突提早到 branch 還小的時候解掉。
不要等到 PR 最後一刻,
才發現自己已經跟 main 差了好幾十個 commit。
7.4 PR 描述也很重要 #
好的 branch strategy, 通常也伴隨好的 PR 習慣。 例如你可以這樣寫描述:
## Summary
- 新增通知中心資料模型
- 新增 API 讀取邏輯
- 新增通知中心畫面
## Testing
- 手動測試通知列表載入
- 單元測試通過
## Notes
- 已同步 main 中 avatar cache hotfix
這樣 reviewer 很快就能知道: 你做了什麼、怎麼驗證、 以及這條 branch 與主幹目前的關係。
八. 常見陷阱:repo 變亂通常不是因為 Git 太難 #
最後整理幾個最常見的翻車點。
8.1 直接在 main 開工,想說等等再整理
#
這是最常見也最危險的習慣之一。
因為你一旦在 main 上累積了幾個半完成修改,
後面再切 branch 時,
很容易把不相干的變更一起帶走。
最好的防呆不是意志力, 而是固定手勢:
git switch main
git pull origin main
git switch -c feature/your-task
先切 branch 再開始寫, 會乾淨很多。
8.2 一條 branch 做太多事 #
今天本來要修登入頁, 結果順手改了 API client、 又順手整理 lint 設定、 最後連 CI 參數都改了。
這種 branch 的典型症狀是:
- PR 很大
- reviewer 很慢
- merge 後出事很難回滾
如果你發現 branch 已經長出多個主題, 拍拍君建議直接拆。 核心功能一條、 純重構一條、 工具調整另一條。
8.3 feature branch 放太久不更新 #
這通常是衝突地獄的起點。
如果 main 每天都在動,
你的 branch 又一週以上不同步,
那最後的合併成本幾乎一定會上升。
比較好的做法是:
- 每天或隔天同步一次
main - 先開 draft PR,提早讓團隊看到方向
- 功能太大就拆成多個子 PR
8.4 隨手加一條 develop,但沒人真的知道怎麼用
#
有些團隊會覺得:
「我們是不是也該有 develop,看起來比較專業?」
如果你們真的有清楚規則,
例如 main 對正式環境、
develop 對整合測試環境、
還有明確 release cut 時點,
那當然可以。
但如果只是因為別人有, 自己也想加一條, 通常最後只會變成:
develop常常壞掉- 大家不知道該 merge 去哪裡
- 最後還是手動把東西搬回
main
複雜流程不是成熟的同義詞。 能長期執行的簡單流程, 通常更有價值。
8.5 只靠口頭約定,不開 branch protection #
口頭約定最大的问题, 就是它會在最忙的那天失效。
所以只要 repo 稍微重要, 就盡量把這些自動化:
main禁止直接 pushmain禁止 force push- PR 必須通過 CI
- 至少一位 reviewer approve
工具不是拿來增加官僚, 而是拿來避免人類在高壓下走捷徑。
結語:branch strategy 的目標,是讓團隊穩定前進 #
Git 分支很強, 但 branch strategy 真正的價值, 從來不是把圖畫得很花。 而是讓每一條 branch 都有清楚角色, 讓每一個人都知道現在該走哪條路。
如果你現在正想整理團隊流程, 拍拍君建議先把下面三件事做好:
main永遠保持可信任- 每個工作項目走自己的
feature/* - production 修復一律從
main開hotfix/*
等這套跑順了, 再慢慢把 rebase、release branch、 feature flag、CI gate 等進階做法接進來。 順序不要反過來。
工程流程不是名詞越多越成熟。
而是大家能不能每天照著同一套規則穩定前進。
如果你今天就想開始改善,
最簡單的一步就是:
把分支命名規則寫進 CONTRIBUTING.md,
再把 main 的 protection 打開。
未來很多麻煩,
真的會少一大截。