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 git@github.com: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(最簡單、最常用)

適合:多數輕量 web 產品團隊的預設選擇

結構:

  • main ─ 永遠可上線
  • feat/xxxfix/xxx ─ 短生命週期的分支

流程:

  1. main 開分支
  2. 在分支寫 code、commit
  3. push 上去、開 PR
  4. 同事 review + CI 跑過
  5. merge 回 main、自動部署

優點:簡單、適合持續部署。
缺點:沒有「準備上線」的中間狀態。

02 GitLab Flow(適合需要 release 控制)

適合:產品需要分版本上線(如 mobile app、企業軟體)

GitLab Flow 官方其實是兩種變體、實務上通常擇一採用:

  • Environment branches(環境分支)main ─ 開發中、production ─ 線上版本、依部署環境串接
  • Release branches(發布分支)main ─ 開發中、release/v1.2 ─ 特定版本分支、每個版本開一條

兩套結構不會疊在一起用 ─ 持續部署選 environment branches、需要並行維護多個版本選 release branches。

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 ─ 修 bug
  • refactor ─ 重構(不改行為)
  • 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 message
  • drop ─ 刪掉這個 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-with-lease   # 共享分支較安全、push 前會先確認遠端沒有你不知道的新 commit
# 純 --force 只適合純個人分支、共享分支避免用

重點 ─ 不要假裝沒事。被 commit 的 secret 已經外洩、立刻換掉、然後清歷史。


最後一個提醒

Git 的價值不是學會命令、是養成習慣
每次寫 code 前 ─ 開分支。
每改完 1 件小事 ─ commit。
每個 commit ─ 替未來的自己寫 message。

這 3 個習慣養成 1 個月、你會發現 Git 變成「讓我能放心改 code」的工具 ─ 因為你知道任何錯誤都能回去。

Git 不是工程師需要學的東西、是用工程師方式工作本身。

常見問題

rebase 跟 merge 差在哪,我到底該用哪個?
共享分支(main、develop、別人也在用的)一律用 merge;個人還沒分享出去的 feature 分支才用 rebase 整理歷史。核心原則是已經 push 的不要 rebase,因為它會改變 commit hash,讓別人 pull 過的歷史錯亂。
只有我一個人開發,還需要開 PR 嗎?
需要。就算只有你一個人,也用 PR 流程,因為它逼你暫停、用第三人視角看自己的 code。多數 bug 其實在你開 PR 的那一刻,自己就會發現。
我不小心把 .env commit 進去了怎麼辦?
先立刻換掉 .env 裡所有 secret(密碼、API key),因為被 commit 的 secret 已經等於外洩。接著用 git filter-repo 或 BFG 把它從 history 完全移除,最後強制 push。共享分支要先跟團隊溝通,別假裝沒事。

延伸閱讀

正在做東西、卡住了?

每兩週收到一篇工程現場筆記,或直接加 LINE 問我。

訂閱電子報 → 加 LINE 問我