前言
一个人写代码,Git 怎么用都对;一个团队写代码,分支模型就是交通规则。这篇讲主流模型的取舍、PR 工作流和冲突处理的正确姿势。
一、分支模型选型
GitHub Flow(现代主流,推荐)
main ──●────●────●────●──► (随时可部署的稳定线)
\ \ \
feature ●──┘ ●──┘ (短命特性分支, 合完即删)
hotfix ●───┘
规则只有四条:
main永远可部署- 想改代码 → 从 main 拉分支
- 分支上提交 + Push,开 PR
- Review 通过 → 合并 → 立即部署
适合:持续部署的 Web/云原生团队(配合 CI/CD 篇的流水线)。分支生命周期短(小时~天),冲突自然少。
Git Flow(重型传统)
main ──●───────────●────●──► (只放生产版本 tag)
develop ─●──●──●──●──●───────► (集成分支)
\ \ \
feature ●──● ● (功能开发)
release ●──● (发布准备/测试修复)
hotfix ●──────► main (紧急修复直上生产)
适合:版本发布周期固定(App/客户端/嵌入式)。Web 服务团队用它通常是被仪式感拖累——多数团队应选 GitHub Flow。
Trunk-Based(极致简化)
所有人直接往 main 提交(未完成功能用 feature flag 隐藏),配合强大的 CI。Google 等大厂玩法,对小团队反而需要纪律。
二、PR/MR 工作流
# 标准动作(以 GitHub 为例, GitLab 叫 MR)
git switch main && git pull # 1. 永远基于最新 main 开工
git switch -c feature/user-export # 2. 分支名规范: 类型/描述
# 3. 开发提交(小的、原子的提交, 别一坨)
git add -p
git commit -m "feat: 导出接口-支持CSV格式"
git push -u origin feature/user-export # 4. 推上去
# 5. 平台开 PR: 填目的/测试方式/截图
PR 纪律:
| 项 | 建议 |
|---|---|
| 体量 | < 400 行改动为佳,大改拆多个 PR |
| 存活期 | 当天~3天,僵尸 PR 果断关闭 |
| 标题 | feat:/fix:/refactor: 前缀 |
| 描述 | 改了什么、为什么、怎么验证 |
| Reviewer | 至少 1 人;小 PR 换来快速 review |
三、合并冲突的正确处理
# 场景: 你的分支落后 main, 远端要求先同步
git switch feature/x
git fetch origin
git rebase origin/main
# CONFLICT (content): Merge conflict in src/api.py
打开冲突文件:
<<<<<<< HEAD
def export_csv(rows):
=======
def export_csv(rows, delimiter=","): # main 上的新版本
>>>>>>> origin/main
处理原则:
<<<<<<<到=======是你的,=======到>>>>>>>是 main 的- 手动融合成正确版本(不是二选一!两边改动可能都要)
- 删掉所有标记
vim src/api.py # 修好
git add src/api.py
git rebase --continue # 继续处理下一个冲突
# 想反悔
git rebase --abort
预防冲突的日常习惯:
# 每天开工先同步
git switch feature/x && git fetch && git rebase origin/main
# 长命分支 = 冲突制造机: 分支尽早合并、尽早删除
四、保护规则(平台配置)
GitHub/GitLab 仓库设置里配:
✔ 禁止直接 push 到 main(走 PR)
✔ PR 至少 1 个 approve 才能合并
✔ CI 必须绿(status check: test/lint)才能合并
✔ 禁止 force push 到保护分支
✔ 分支删除后自动删远端
这些规则是团队协作的护栏——写进制度不如配进平台。
五、提交规范与历史整洁
feat: 新功能 fix: 修 bug
docs: 文档 refactor: 重构(不改行为)
test: 测试 chore: 构建/工具杂务
perf: 性能 ci: CI 配置
示例:
feat(auth): 支持 LDAP 登录
fix(api): 修复分页 offset 为负时的 500
squash merge:PR 合并时压成一个提交,main 历史干净可读(平台按钮选 “Squash and merge”)。
六、协作事故急救
事故一:把 WIP 推到了 main
# main 上多了 2 个错误提交, 还没被别人拉取
git switch main
git reset --hard HEAD~2
git push --force-with-lease origin main # 比 -f 安全: 有人先推了会拒绝
事故二:敏感信息已提交
# 1. 立即作废密钥(git 历史只是次要问题!)
# 2. 从历史中清除
git filter-repo --replace-text <(echo "sk-12345==>REMOVED") # 需要 git-filter-repo
git push --force-with-lease
# 3. 团队所有人重新 clone(历史已改写)
事故三:rebase 到了错误的分支
git reflog # 找到 rebase 前的 HEAD
git reset --hard HEAD@{5}
事故四:feature 分支被误删
git switch -c recovered origin/feature/x # 远端还在
# 远端也删了:
git log --all --oneline | grep <关键词> # reflog 里捞
七、团队模板落地
.github/PULL_REQUEST_TEMPLATE.md
---
## 改动说明
## 影响范围
## 自测方式
- [ ] 本地测试通过
- [ ] CI 绿
.gitmessage(git config commit.template .gitmessage)
# <type>(<scope>): <subject>
# <body>
八、协作 checklist
- 分支模型选定并在团队宣讲(默认 GitHub Flow)
- main 开保护:禁直推、必 review、CI 门禁
- 分支命名与提交前缀规范统一
- 每日 rebase main,分支存活 ≤ 3 天
- PR 模板与 CHANGE-CAUSE 注解
- 定期清理已合并分支
小结
| 场景 | 做法 |
|---|---|
| 模型选型 | 默认 GitHub Flow,固定版本才 Git Flow |
| 日常 | main 拉分支 → 小提交 → PR → squash 合并 |
| 冲突 | rebase main,手动融合双版本 |
| 护栏 | 保护分支 + CI 门禁 |
| 急救 | reflog + force-with-lease |
本文是「工具」系列第 2 篇。