一个人写代码的时候,分支管理几乎是不存在的问题:所有改动都在 main 上,提交记录再乱也只有自己看得见。一旦进入多人协作,分支策略就会立刻变成绕不开的话题——谁的代码什么时候合、出了问题怎么回滚、发布版本和日常开发怎么并行,全都压在这一层上。
这篇文章不讲”某种分支模型是唯一正确答案”,而是把几种常见做法的适用场景、取舍和落地细节讲清楚,方便你在自己的团队规模下做出合适的选择。
一、单人阶段:一条主线就够用
单人开发阶段最常见的情况是这样的:一个 main 分支,改完就提交,偶尔打个 tag 标记版本。这个阶段过度设计分支模型反而是负担。
不过有两件事最好从一开始就养成习惯:
- 提交信息写清楚”为什么”。代码本身说明改了什么,但改动的动机往往只有当时记得。
- 不要提交半成品到
main。哪怕只有自己,一个能编译、能运行的主线,在回滚的时候也会救你一次。
如果只是想临时试个功能,随手开一个短命分支,试完合并或丢弃,成本几乎为零。Git 的分支本质上只是一个指向 commit 的指针,创建和销毁都很廉价。
二、进入协作:功能分支模型
两个人以上的团队,最基本的做法是功能分支(Feature Branch):每个功能、每个 bug 修复开一个独立分支,完成后合并回主分支。
一个典型的流程是:
main
└── feature/user-login # 开发登录功能
└── feature/export-csv # 开发导出功能
合并的时候有两条路可选:
- merge:保留完整的分支历史,多一条合并提交。历史”真实”,但会比较乱。
- rebase:把分支上的提交”搬到” main 最新提交之后,历史是一条直线。好看,但改写了提交哈希。
我的经验是:个人本地分支可以用 rebase 保持整洁,共享分支永远不要 rebase。一旦别人基于你的分支做了开发,rebase 会把他们的历史搞乱,而且冲突要重新解一遍。
三、Git Flow:发布节奏慢的团队
Git Flow 是一套比较完整的分支模型,包含 main、develop、feature/*、release/*、hotfix/* 五类分支。
它的核心假设是:发布是有节奏的,比如两周一个版本,版本之间要有稳定的集成分支(develop)。
结构大致是:
main ← 只放发布过的代码,每个发布打 tag
└── develop ← 日常集成
├── feature/A
└── feature/B
└── release/1.2 ← 发布前冻结,只做修复
适合:有固定发版周期、需要同时维护多个历史版本(比如桌面软件、需要给客户交付安装包的场景)。
不适合:持续部署的 Web 服务。Git Flow 的分支层数多,对”每天上线多次”的团队来说太重了。
四、GitHub Flow:持续部署的轻量选择
GitHub Flow 只保留两类分支:
main永远可部署;- 所有改动都在短命分支上完成,通过 Pull Request 合并进
main,合并后立即部署。
它的关键约束是:任何时刻 main 都必须是可发布的状态。这要求你有可靠的自动化测试和部署流水线,否则”随时可部署”只是一句口号。
适合迭代快、部署频率高的 Web 项目。团队对 PR 评审的依赖会比 Git Flow 更强,因为合并即发布,评审就是最后一道防线。
五、Trunk-Based:极致的持续集成
主干开发(Trunk-Based Development)更激进:所有人直接向主干提交小改动,或者用生命周期只有几小时的短命分支。需要发布未完成的功能时,用特性开关(Feature Flag)在代码层控制开关,而不是在分支层隔离。
这种模式对工程能力要求很高:
- 自动化测试覆盖要足够;
- 代码评审要快,否则主干会被频繁的小 PR 堵住;
- 特性开关需要有管理机制,否则代码里会堆满”临时开关”。
它的收益也很明显:几乎不存在长期分支的合并地狱,集成问题在第一时间暴露。
六、命名与规范:让分支看得懂
不管选哪种模型,分支命名都值得统一。一套常见约定:
| 类型 | 前缀 | 示例 |
|---|---|---|
| 功能 | feature/ |
feature/csv-export |
| 修复 | fix/ 或 hotfix/ |
fix/login-timeout |
| 发布 | release/ |
release/1.2.0 |
| 重构 | refactor/ |
refactor/db-layer |
再补充三条实用规则:
- 分支名用短横线连接英文单词,避免空格和中文,方便命令行操作。
- 一个分支只做一件事。”顺手改了三个不相关的地方”会让评审非常痛苦。
- 及时删除已合并的分支。仓库里的陈旧分支会干扰所有人的分支列表。
七、合并冲突:怎么少踩坑
冲突不是分支模型的问题,是协作密度的问题。几个能明显减少冲突的做法:
- 勤合并主线:功能分支活得越久,冲突越多。哪怕功能没做完,也可以定期把
main合进来(或 rebase 到最新)。 - 按模块划分责任区:两个人同时改同一个文件的同一区域,冲突几乎必然发生。
- 大的结构调整单独提交:把”移动文件/重命名”和”改逻辑”分开,Git 的重命名检测会友好很多。
遇到冲突时,先弄清楚两边各自想干什么,再决定怎么合。机械地”选这边或选那边”是冲突反复出现的根源。
八、回到选择本身
分支模型本质上是在协调成本和交付速度之间找平衡:
- 团队小、发布快 → GitHub Flow 甚至主干开发;
- 发布有固定节奏、要维护多版本 → Git Flow 的思路仍然有价值;
- 工程能力强、追求持续集成 → Trunk-Based。
没有哪种模型能替你解决协作问题本身。真正起作用的是:清晰的约定、短命的分支、可靠的自动化,以及”随时可以把主线恢复到可用状态”的底气。分支只是工具,纪律才是内核。
Comments (3)
我这边有个更简单的做法,但没这么稳。
按文中的做法试了一遍,本地跑通了,感谢。
这个坑我踩过,而且是两次。
Please Sign in to join the discussion.