Git Workflow 完整教學
從個人到團隊
Git 學了就忘的核心原因 ─ 你學的是「命令」、不是「workflow」。記命令會忘、理解 workflow 一輩子受用。這篇從個人開發到團隊協作、完整拆解 ─ 分支策略、PR 規範、rebase vs merge、conflict 處理、10 個必學命令。附 cheatsheet。
先建立3 個觀念
01 Git 不是備份工具、是時光機
很多新手把 Git 當「把 code 傳到 GitHub 備份」 ─ 這是把法拉利當腳踏車用。
Git 真正的價值:你能回到任何過去的狀態、能看「誰、什麼時候、為什麼」改了什麼、能同時在多個分支實驗。
02 分支是免費的
Git 的 branch 不是「複製整個 project」、是「指針指向某個 commit」。建一個分支只要幾 KB。
所以 ─ 不要怕開分支。實驗一個想法、開分支。修一個 bug、開分支。寫一篇實驗、開分支。
03 Commit 是給未來的你看的
現在的 commit message 寫得好不好、決定 3 個月後的你能不能找到「為什麼這段 code 長這樣」。
這跟「commit 短或長」沒關係、跟「寫的人有沒有替未來著想」有關。
個人開發的最簡 workflow
如果你還沒進公司、自己練習用、這個就夠:
# 1. 建好 repo git init git remote add origin [email protected]:user/repo.git # 2. 每次寫新功能、開新分支 git checkout -b feat/login-page # 3. 寫 code、隨時 commit(重點:commit 頻率高比低好) git add src/Login.tsx git commit -m "feat(auth): add login form skeleton" # 4. 寫完、push 上去 git push -u origin feat/login-page # 5. 在 GitHub 開 PR、自己 review 自己(是的、自己跟自己 review) # 6. Merge 回 main # 7. 刪除本地分支 git checkout main git pull git branch -d feat/login-page
重點 ─ 就算只有你一個人、也用 PR 流程。
為什麼?因為這逼你「暫停 + 用第三人視角看自己 code」。多數 bug 在 PR 那一刻自己會發現。
團隊開發的3 種 workflow
01 GitHub Flow(最簡單、最常用)
適合:80% 的 web 產品團隊
結構:
main─ 永遠可上線feat/xxx、fix/xxx─ 短生命週期的分支
流程:
- 從
main開分支 - 在分支寫 code、commit
- push 上去、開 PR
- 同事 review + CI 跑過
- merge 回
main、自動部署
優點:簡單、適合持續部署。
缺點:沒有「準備上線」的中間狀態。
02 GitLab Flow(適合需要 release 控制)
適合:產品需要分版本上線(如 mobile app、企業軟體)
結構:
main─ 開發中production─ 線上版本release/v1.2─ 特定版本分支
03 Git Flow(複雜、大型專案用)
適合:傳統軟體公司、需要嚴格 release cycle
結構(複雜版):
main─ 線上develop─ 開發中feature/xxx─ 功能分支release/xxx─ 上線準備hotfix/xxx─ 緊急修復
優點:嚴謹、有專門流程。
缺點:對 web 產品太重、多數小團隊不需要。
建議 ─ 新手用 GitHub Flow、有需要再升級。
分支命名 ─ 有規律
推薦命名 convention:
feat/short-description # 新功能 fix/short-description # 修 bug refactor/short-description # 重構 docs/short-description # 文件 test/short-description # 測試 chore/short-description # 雜事(升級依賴、設定) # 範例 feat/oauth-google-login fix/payment-cancel-bug refactor/extract-pricing-service docs/update-readme chore/upgrade-node-20
好處:
- 看分支名就知道在做什麼
- GitHub 排序時類型相近的會聚在一起
- 跟 commit message 的 type 一致(feat / fix / refactor)
Commit Message ─ 結構化寫法
推薦 Conventional Commits 格式:
<type>(<scope>): <subject> <body 可選> <footer 可選>
範例:
feat(auth): add OTP for mobile users Mobile users were getting locked out due to SMS delivery delays. Added OTP fallback with 3-minute validity window. Closes #142
常用 type:
feat─ 新功能fix─ 修 bugrefactor─ 重構(不改行為)docs─ 文件test─ 測試chore─ 雜事perf─ 效能優化
這個寫法的好處 ─ 工具可以自動生成 changelog、可以根據 commit 自動 bump version。
Rebase vs Merge ─ 什麼時候用哪個
git merge
合併 2 個分支、保留所有歷史、會多一個「merge commit」。
什麼時候用:
- 合分支回 main(feat → main)
- 多人協作的 main、避免改寫已 push 的歷史
git rebase
把你的 commits「重新接到」另一個分支的最新狀態、歷史變成一條線。
什麼時候用:
- 把自己的 feature 分支同步到最新的 main、開 PR 前
- 整理 commit history(squash / reword)
關鍵原則
已 push 上去的、不要 rebase。
因為 rebase 會改變 commit hash、別人 pull 過的歷史會錯亂。
所以實務上:
- 個人 feature 分支(還沒給別人)→ 可以 rebase 整理
- 共享分支(main / develop / 別人也在用的分支)→ 永遠 merge
處理 Merge Conflict ─ 4 步法
很多新手卡在 conflict、其實有固定流程:
步驟 1:先 fetch 最新
git fetch origin git status # 確認當前狀況
步驟 2:拉最新 main 進你的分支
# 如果你還沒 push git checkout feat/your-branch git rebase origin/main # 如果已經 push 過 git checkout feat/your-branch git merge origin/main
步驟 3:開 editor、看 conflict 標記
Conflict 的檔案會有:
<<<<<<< HEAD 你的版本 ======= 別人的版本 >>>>>>> origin/main
選一個保留、或合併兩邊、刪掉 <<<、===、>>> 標記。
步驟 4:mark resolved + 繼續
git add <檔名> git rebase --continue # 如果用 rebase # 或 git commit # 如果用 merge
重點 ─ 不確定就 abort:
git rebase --abort # 取消 rebase git merge --abort # 取消 merge
回到沒 conflict 前的狀態、想清楚再來。沒人會逼你立刻解決 conflict。
10 個你會天天用的命令
| 命令 | 用途 |
|---|---|
git status | 看現在處於什麼狀態、哪些檔案被改 |
git diff | 看當前未 commit 的改動 |
git log --oneline -10 | 看最近 10 個 commits(精簡版) |
git add <檔> | 把改動加進 staging |
git commit -m "msg" | 提交(最常用) |
git checkout -b <分支> | 建新分支並切過去 |
git push -u origin <分支> | 第一次 push 新分支 |
git pull --rebase | 拉最新、用 rebase 不產生 merge commit |
git stash | 暫存當前改動、清空 working dir |
git reset --soft HEAD~1 | 撤銷上個 commit、但保留改動 |
進階心法 ─ 5 個
01 用 git rebase -i 整理 history
開 PR 前、把雜亂的 commit 整理成乾淨的:
git rebase -i HEAD~5 # 整理最近 5 個 commits
在編輯器內你可以:
pick─ 保留squash─ 跟前一個合併reword─ 改 commit messagedrop─ 刪掉這個 commit
這讓你的 PR 看起來像 senior 寫的。
02 用 git stash 切換 task
你正在寫 feature、突然 PM 說「線上有 bug、馬上修」:
git stash # 暫存當前改動 git checkout main git checkout -b fix/urgent-bug # 修 bug、merge 回 main git checkout feat/your-feature git stash pop # 拿回剛才的改動、繼續寫
這比「亂 commit 一個 WIP」乾淨多了。
03 git bisect 找出哪個 commit 引入 bug
已知 1 週前的 code 沒 bug、今天有了 ─ 不用一個個 commit 看:
git bisect start git bisect bad # 當前有 bug git bisect good <1週前的commit> # Git 會自動 checkout 中間的 commit、你測試 # 沒 bug → git bisect good # 有 bug → git bisect bad # Git 用二分法快速定位到引入 bug 的那個 commit
這個命令在大 codebase 是救命神器。
04 善用 .gitignore 跟 .gitattributes
基本的 .gitignore:
# Node node_modules/ .env* !.env.example # Editor .vscode/ .idea/ *.swp # OS .DS_Store Thumbs.db # Build dist/ build/ *.log
05 加 pre-commit hook
自動在 commit 前跑檢查(lint、format、test):
# 用 husky + lint-staged(最常見組合)
npm i -D husky lint-staged
npx husky init
# 在 .husky/pre-commit 寫
npx lint-staged
# 在 package.json
"lint-staged": {
"*.{ts,tsx}": ["eslint --fix", "prettier --write"]
}
設好之後、commit 前自動檢查、避免「我以為改好了結果壞了」。
3 個會嚇新手的情境
01 「我 commit 錯東西進去了」
# 還沒 push git reset --soft HEAD~1 # 撤銷 commit、保留改動 # 改完再 commit # 已經 push 了 # 別 git push --force(除非自己分支) # 用 git revert <commit> 做一個「反向 commit」
02 「我改錯分支了」
# 還沒 commit git stash git checkout <對的分支> git stash pop # 已經 commit 但沒 push git checkout <對的分支> git cherry-pick <那個 commit hash> git checkout <錯的分支> git reset --hard HEAD~1 # 移除錯的分支上的 commit
03 「我把 .env 不小心 commit 進去了」
# 立刻換掉 .env 裡的所有 secret(密碼、API key) # 然後從 history 完全移除 # 用 git filter-repo(推薦)或 BFG Repo Cleaner git filter-repo --path .env --invert-paths # 強制 push(這會改寫遠端歷史、團隊要先溝通) git push --force
重點 ─ 不要假裝沒事。被 commit 的 secret 已經外洩、立刻換掉、然後清歷史。
最後一個提醒
Git 的價值不是學會命令、是養成習慣。
每次寫 code 前 ─ 開分支。
每改完 1 件小事 ─ commit。
每個 commit ─ 替未來的自己寫 message。
這 3 個習慣養成 1 個月、你會發現 Git 變成「讓我能放心改 code」的工具 ─ 因為你知道任何錯誤都能回去。
Git 不是工程師需要學的東西、是用工程師方式工作本身。
Git 卡關、想要有人手把手帶?
30 分鐘 1-on-1 諮詢 NT$1,500 ─ 我帶你跑一次完整 workflow、處理你 repo 真的卡的 conflict / rebase 問題。