Git 分支模型与协作
Git 分支模型与协作
分支模型是团队协作的「交通规则」:它规定代码怎么流、谁能合、什么时候能发。选错模型不会立刻出事,代价会以「合并越来越难、主干越来越不敢发」的形式慢慢累积。
1. 分支的本质
在 Git 里,分支就是一个指向某次提交的可移动指针,.git/refs/heads/ 下只是一个存着提交哈希的小文件。创建分支是写一个 41 字节文件,切换分支是改一下 HEAD 并更新工作区——所以 Git 的分支又快又廉价,随便建、随便删。
正因为分支廉价,「分支模型」讨论的从来不是技术成本,而是协作约定:什么时候开分支、分支存活多久、如何合回主干、发布怎么打、事故怎么修。下面三种模型对应三种协作节奏。
2. 三种主流模型
2.1 Git Flow
由 Vincent Driessen 提出的经典模型,用长期分支承担明确职责:
| 分支 | 职责 | 生命周期 |
|---|---|---|
main | 只放可发布的稳定版本,每次合并对应一次发布 | 长期 |
develop | 集成分支,日常开发的汇合点 | 长期 |
feature/* | 单个特性开发 | 短期 |
release/* | 发布前的稳定与测试 | 短期 |
hotfix/* | 线上事故的紧急修复,直接基于 main | 短期 |
发布流程大致是:feature 合入 develop → 从 develop 切出 release → 在 release 上只做修 bug → 合回 main 并打 tag → 同步回 develop。它适合多版本并行、发布窗口明确的产品(如客户端、需要长期维护多个版本线)。
代价是分支多、合并路径复杂,release/develop 之间的双向同步很容易漏合,小型团队用起来偏重。
2.2 主干开发(Trunk-Based Development)
核心是所有人频繁地把改动合入唯一的主干(main),分支存活时间以「小时到一两天」计。半成品靠特性开关(Feature Flag)隐藏在代码里,而不是靠长期分支隔离。
git switch -c tbc/login-timeout # 短分支
# 小步提交、当天合并
git push -u origin HEAD # 走 PR,CI 通过即合优点是集成早、冲突小、可随时发布;代价是对 CI 纪律、自动化测试和特性开关治理要求很高——没有这些基础设施,主干会频繁变红。
2.3 GitHub Flow
可以看作主干开发在开源/Web 场景下的简化版:main 永远可发布,任何改动都从一个短分支发起 Pull Request,经评审、CI 通过后合并回 main,随后立即部署。只有一条长期分支,规则简单,非常适合持续交付的 Web 服务。
2.4 如何选择
| 场景 | 建议模型 |
|---|---|
| Web 服务、持续交付、小到中型团队 | 主干开发 / GitHub Flow |
| 多版本并行、有明确发布窗口 | Git Flow 或其简化版 |
| 开源项目、外部贡献者多 | GitHub Flow(PR 即入口) |
| 强合规/需长期维护旧版本 | Git Flow + 维护分支 |
现实里更常见的是简化版 Git Flow:保留 main + develop 与 hotfix,砍掉 release,在 develop 上直接打 tag 发布。
3. PR 与 Code Review 流程
无论选哪种模型,把改动合入受保护分支通常都要走 PR(GitLab 称 MR):
一套可落地的评审约定:
- PR 要小:单次 PR 改动控制在几百行以内,越小的 PR 越容易被认真评审,也越容易回滚。改了两个不相关的事,就拆成两个 PR。
- 描述写清「为什么」:标题写做了什么,正文写背景、方案取舍、验证方式、影响面。评审者不该靠猜。
- CI 必须绿:编译、单测、静态检查、格式检查全部通过才允许合并,把机器能判断的事交给机器。
- 至少一人评审:涉及核心模块或安全相关代码时要求双人。评审关注正确性、边界、可维护性,风格问题交给自动格式化工具,别在评论里争论缩进。
- 合并策略明确:
squash保持主干每提交一个特性,merge commit保留分支结构,rebase and merge保持线性。团队统一一种即可,别混用。
4. 冲突解决
冲突不是 Git 的 bug,而是「两个人改了同一段」的必然结果。清晰的模型能减少冲突,但无法消除。处理流程:
git fetch origin
git rebase origin/main # 或 git merge origin/main
# 打开冲突文件,处理 <<<<<<< ======= >>>>>>> 标记
git add <已解决的文件>
git rebase --continue # 若 rebase;merge 则 git commit
# 无法收拾时:git rebase --abort 回到原状减少冲突的实践:
- 小步、频繁合并:分支活得越久,偏离主干越远,冲突越重。缩短分支寿命是性价比最高的办法。
- 同文件避免并行大改:涉及公共文件(如公共常量、路由表)的改动尽量串行或提前打招呼。
- 冲突时先沟通:看不懂对方为什么这么改,就当面问,别硬猜「留自己的」。
- 改完必须编译 + 跑测试:冲突的语义错误(语法能过、逻辑已错)比文本冲突更危险。
5. Commit 规范
统一提交信息是自动生成 CHANGELOG、语义化版本和追溯问题的基础。Conventional Commits 是主流约定,格式为:
<type>(<scope>): <subject>常用 type:feat(新功能)、fix(修 bug)、docs(文档)、refactor(重构)、test(测试)、chore(杂项/构建)、perf(性能)、style(格式,不影响逻辑)。示例:
feat(order): 支持按状态过滤订单
fix(login): 修正验证码过期判断
docs: 补充部署说明好处很直接:feat 会触发次版本号递增、fix 触发修订号递增(配合语义化版本工具),git log --grep 能一键筛选,CI 还能据此决定是否发布。约定要落地,最好用 commitlint 之类的钩子在提交时校验,而不是靠自觉。
6. 分支保护与门禁
受保护的分支(通常是 main、develop)应开启平台侧门禁:
- 禁止直接推送:只能通过 PR 合入,杜绝绕过评审的改动。
- 要求 CI 状态检查通过:检查未过不允许合并。
- 要求评审批准:设定最少批准人数。
- 禁止强推与删除:保护历史不被重写、分支不被误删。
- 要求分支与主干同步:合并前必须是最新的,避免「本地绿、合并后红」。
再配合 pre-commit 钩子(格式化、密钥扫描、提交信息校验),把能自动化的检查全部前移,评审者才只需关注真正需要人判断的部分。
7. 实践要点与常见坑
- 分支名可读:
feature/order-filter、fix/login-npe比dev1、test有用得多,一眼能看出意图。 - 及时删除已合并分支:本地
git branch -d,远端合并后自动删,别让仓库里堆积几十个僵尸分支。 - 别长期 hold 一个大分支:那是「集成地狱」的温床。宁可拆成多个可独立合并的小步。
- 提交与分支对应:一个分支只做一件事,别混入无关改动,否则回滚时牵连无辜。
main永远可发布:任何时候从main打包都应能上线,这是所有模型共同的底线。- 紧急修复要有专门路径:线上事故走
hotfix/*,修复后必须同步回集成分支,否则下次发布又把 bug 带回来。
8. 小结
- 分支只是轻量指针;分支模型本质是团队协作约定。
- Git Flow 适合多版本并行,主干开发/GitHub Flow 适合持续交付;多数团队用的是简化版。
- PR + Code Review + CI 门禁是保证主干质量的三件套,PR 要小、要写清「为什么」。
- 冲突靠短分支 + 频繁集成预防,处理时要编译验证而非只解决文本标记。
- 用 Conventional Commits 规范提交信息,让版本号、CHANGELOG、追溯都能自动化。
回顾底层操作与撤销机制,见 Git 详解。