M10 協作流程:分支、PR 與審查
- 完成一次 PR 循環:開分支、推送、開 PR、回應審查、合併、同步;
- 處理因合併次序而產生的 PR 衝突;
- 分辨 fork 和分支,以及 PR(GitHub)和 MR(GitLab);
- 比較 GitHub flow、trunk-based、Git flow,為團隊選擇合適的流程。
1為甚麼需要流程 Why a workflow
M9 學會了 push 和 pull,但如果每個人都直接推送到 main,就會出現三個問題:沒有人看過改動就進了正本;同時推送時要不斷處理被拒絕和衝突;出了問題也很難說清楚是哪一次、為甚麼改的。M0 說過,分散式的代價是需要約定單一正本。協作流程就是這個約定的具體做法。
2功能分支:每件事一條分支 Feature branches
最基本的規則:main 只放已完成、已審查的工作;每件新工作都在自己的分支上進行。分支名說明在做甚麼,例如 background、fix-sample-size。分支完成後經 PR 合併,然後刪除。
這也是 M7「一個 commit 一件事」的延伸:一條分支一件事,一個 PR 一件事,審查的人才看得懂。
3PR:提出改動,請人審查 Pull requests
PR(pull request)不是 Git 的功能,而是 GitHub、GitLab 等託管服務的功能。它的意思是:「請把我這條分支合併進 main」。一個 PR 包含:
- 改動:分支相對於 main 多出的 commit,以及合併後的 diff;
- 討論:說明為甚麼改、審查者逐行留言;
- 審查結論:批准(approve)、要求修改(request changes)或只留言;
- 檢查:自動測試等是否通過(M11);
- 合併方式:合併 commit、squash、rebase(M5、M7)。
PR 開出之後,在同一條分支上繼續推送 commit,PR 會自動更新,不用另開一個。審查的來回就是:審查者提意見 → 你修改並推送 → 再審查 → 批准 → 合併。
gh(例如 gh pr create),GitLab 的是 glab(glab mr create)。
4沙盒:一次完整的 PR 循環 Sandbox: a full PR cycle
5合併次序與 PR 衝突 Merge order and conflicts
兩個 PR 各自都沒有問題,但先合併的那個會改變 main;後合併的那個,就要和「新的 main」比較。如果兩者改了同一處,後者就會顯示衝突,不能直接合併。解決方法和 M5 一樣,只是在 PR 的分支上進行:
gh pr checkout 2 # 取得 PR #2 的分支(等於 fetch+switch)
git merge origin/main # 把最新的 main 合併進來,解決衝突
git add 檔案 && git commit
git push # PR #2 自動更新,衝突消失
也可以用 git rebase origin/main 代替 merge,但那是改寫歷史,只適合只有作者一個人在用的分支(M7 黃金規則),推送時要 --force-with-lease。
6fork:沒有寫入權時的做法 Forks
開源專案或大型課程倉庫,不會讓每個人都有寫入權。這時的流程是:
- 在 GitHub 上 fork:在你自己的帳號下複製一份倉庫(伺服器端的 clone);
- clone 你的 fork 到電腦;它是
origin。再把原倉庫加為第二個遠端,習慣叫upstream:git remote add upstream <原倉庫網址>; - 在分支上工作,push 到你的 fork(origin);
- 從你的 fork 對原倉庫開 PR;
- 原倉庫更新時,
git fetch upstream,再把upstream/main合併或 rebase 進來。
| 分支 | fork | |
|---|---|---|
| 在哪裏 | 同一個倉庫裏的一張標籤 | 另一個倉庫(在你的帳號下) |
| 需要原倉庫的寫入權嗎 | 需要(才能推送分支) | 不需要 |
| 適合 | 團隊成員 | 外部貢獻者、學生交作業到課程倉庫 |
7三種常見的團隊流程 GitHub flow, trunk-based, Git flow
| 流程 | 怎樣運作 | 適合 |
|---|---|---|
| GitHub flow | 只有一條長期分支 main;每件事開短期分支 → PR → 審查 → 合併 → 部署 | 大部分小團隊、網站、文件和課程倉庫 |
| trunk-based | 所有人頻繁(每天甚至每小時)把小改動合併進 main;分支壽命以小時計,未完成的功能用開關(feature flag)隱藏 | 有完善自動測試、需要快速整合的團隊 |
| Git flow | 長期分支 main(已發佈)和 develop(開發中),另有 feature/release/hotfix 分支,按規則互相合併 | 有明確版本號、需要同時維護多個已發佈版本的產品 |
選擇的準則是:能滿足需要的最簡單流程。分支越多、壽命越長,合併時的衝突就越多。大部分團隊從 GitHub flow 開始就夠了;需要同時維護舊版本時,再加上發佈分支和 cherry-pick(M7)。
8審查:寫給人看的 PR Code review
| 開 PR 的人 | 審查的人 |
|---|---|
| 一個 PR 一件事,越小越容易審 | 先看說明,弄清楚目的再看 diff |
| 標題說做了甚麼,說明寫為甚麼、怎樣驗證 | 對事不對人;指出問題時附上理由或建議 |
| 開 PR 前自己先看一遍 diff,整理好 commit(M7) | 分清「必須改」和「可以考慮」 |
| 回應每一條意見:修改,或說明不改的理由 | 改好了就批准,不要一直加新要求 |
知識工作同樣適用:一份報告的 PR,審查者看的是論點、數據來源和用詞,GitHub 會逐行顯示文字的改動(所以 M5 建議一句一行)。
9容易混淆的對照 Easily confused
- 「PR 是 Git 的命令。」不是。Git 只有分支和合併;PR 是託管服務在其上加的討論和審查流程。
- 「PR 開了就要等合併,不能再改。」可以繼續推送到同一條分支,PR 會自動更新。
- 「PR 合併了,我的電腦就自動更新了。」不會。合併發生在 GitHub 上;你要
git pull才拿到(M9)。 - 「fork 等於分支。」fork 是另一個倉庫;只有沒有寫入權時才需要。
10命令速查 Cheat sheet
| 命令 | 作用 |
|---|---|
git switch -c 名字 → git push -u origin 名字 | 開功能分支並推送 |
gh pr create --title "…" --body "…" | 為目前分支開 PR |
gh pr list / gh pr view 2 | 列出 PR/查看某個 PR 的狀態 |
gh pr checkout 2 | 把 PR #2 的分支取到本機 |
gh pr review 2 --approve / --request-changes -b "…" | 批准/要求修改 |
gh pr merge 2 --merge / --squash / --rebase | 以合併 commit/squash/rebase 合併 |
gh pr merge 2 --squash --delete-branch | 合併並刪除分支(遠端和本機) |
git fetch --prune | 清理遠端已刪除的分支的 origin/* 記錄 |
git remote add upstream <網址> | fork 流程:加入原倉庫為第二個遠端 |
11小測 Quiz
本單元重點 Summary
- main 只放已審查的工作;每件事一條短期分支,經 PR 合併後刪除。
- PR 是託管服務的功能:改動+討論+審查+檢查;繼續推送即可更新 PR。
- 合併次序會改變 main,後合併的 PR 可能因此衝突;在 PR 分支上合併最新的 main 解決。
- 沒有寫入權時用 fork,原倉庫加為
upstream。 - 選擇能滿足需要的最簡單流程;大部分團隊用 GitHub flow 就夠。
延伸閱讀
- Pro Git 5.1–5.3 Distributed Workflows、Contributing to a Project
- Pro Git 6.2 GitHub:Contributing to a Project
- gh pr 官方手冊
- GitHub Docs:GitHub flow
- Trunk Based Development
- 有問題?記下來 → 學生問題庫