前言

一个人写代码,Git 怎么用都对;一个团队写代码,分支模型就是交通规则。这篇讲主流模型的取舍、PR 工作流和冲突处理的正确姿势。

一、分支模型选型

GitHub Flow(现代主流,推荐)

main ──●────●────●────●──►        (随时可部署的稳定线)
        \    \      \
feature  ●──┘     ●──┘           (短命特性分支, 合完即删)
hotfix         ●───┘

规则只有四条:

  1. main 永远可部署
  2. 想改代码 → 从 main 拉分支
  3. 分支上提交 + Push,开 PR
  4. 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

处理原则:

  1. <<<<<<< 到 ======= 是你的,======= 到 >>>>>>> 是 main 的
  2. 手动融合成正确版本(不是二选一!两边改动可能都要)
  3. 删掉所有标记
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 篇。