团队用了6年的Git分支管理规范,合并再也没有打过架
一、那次上线,差点把我们送走
刚接手团队那会儿,代码管理用的是SVN。
说是"管理",其实跟没管一样。每个人往同一个主干上提交,没有分支策略,没有提交规范,没有合并机制。全靠自觉。
自觉这东西,在代码面前不值钱。
有一天发版,测试环境跑得好好的,结果上线前一小时,一个开发突然在群里问:"诶,我在 trunk 上改的那个订单状态流转怎么没了?"
然后另一个开发接了一句:"啊?我也改了那个文件,我以为是我自己加的判断逻辑不对,就给你覆盖重新提交了。"
全组沉默了三秒。
那天晚上,我们三个开发加一个测试,围着一台电脑,打开 Beyond Compare,把 trunk 上的代码一行一行跟昨天的备份做对比。订单、支付、退款,三个核心模块的代码混在一起,谁也搞不清哪行是对的、哪行是后来覆盖上去的。
从晚上八点比对到凌晨一点。五个小时,比对出来 17 处差异。其中 3 处是功能缺失——代码被覆盖后直接丢了。最要命的一个,退款金额计算漏了一行乘法,上线就是资损。
最后发版推迟到第二天。老板在群里发了一句话:"技术部在干什么。"
这句话比任何 bug 都难受。
二、换了 Git,问题就解决了吗?没有
后来团队从 SVN 迁移到 Git。我以为换个现代工具就没事了。
结果该乱的还是乱。
Git 比 SVN 强大,但没人定规矩,强大就意味着更花式的混乱。有人从 master 拉分支,有人从 dev 拉分支,有人直接在 master 上改。一个项目同时活着七八个长期分支,谁也不知道哪个是最新的。合并的时候,Git 的冲突提示比 SVN 还长——因为每个人的基线都不一样。
那段时间我明白了一件事:
工具换了没用,换的是习惯。习惯不改,GitHub 来了照样乱。
于是我坐下来,跟两个组长一起,花了整整两天,把我们踩过的坑一个一个倒出来,反推出了一套分支管理规范。
这套规范我们用了六年,迭代了好几个版本。从 5 个人的小团队用到几十号人的技术部门,合并冲突的次数一只手数得过来。再也没出现过"上线前发现代码丢了"这种事。
下面就是这套规范的全部内容。没有花活,全是实操。
三、三个核心原则
在讲具体流程之前,先定三条铁律。这些铁律每一条后面都有血泪教训。
原则一:主干永远可发布
不管什么时候,不管谁在干什么,trunk(主分支)上的代码必须是可以直接编译、直接部署、直接上线的。如果 trunk 挂了,整个团队的交付就停了。
怎么保证?后面讲。
原则二:一次提交只做一件事
修 bug 就是修 bug,重构就是重构,加功能就是加功能。三个搅在一起,出了问题你根本不知道是哪部分引起的。而且三个月后谁也看不懂这条 commit 到底干了什么。
原则三:提交信息要能让人看懂
"fix bug""update code""修改"——这种提交信息等于没写。提交信息要回答三个问题:做了什么、为什么做、影响范围是什么。后面有具体格式。
四、正常版本迭代流程
当一个需求版本定下来之后,按以下步骤走:
第1步:从 trunk 拉出版本分支
确认 trunk 是最新的,从 trunk 拉出发布分支。分支名带上版本号和 SNAPSHOT 标记,比如 release/1.0.0-SNAPSHOT。
SNAPSHOT 后缀的意思是:这个版本还在开发中,随时可能变。等测试验收全部通过,去掉 SNAPSHOT,就是正式的发布版本。
这样做的原因是:一眼就能看出哪个版本是开发中、哪个版本是已发布。避免把未完成的版本当正式版发出去了——我们干过这种事。
第2步:开发人员在分支上开个人功能分支
不是直接在 release 分支上改代码。每个人从 release 分支再拉出自己的功能分支 feature/xxx,开发完再合回 release。
多一层分支的好处:功能之间互相隔离。A 的功能没写完,不影响 B 的功能测试。
第3步:自测 + 联调
个人功能分支合入 release 后,在 release 分支上做自测和联调。这个阶段发现问题直接在 release 上修。
第4步:发布到测试环境,交给测试
自测通过后,把 release 分支的代码发到测试环境。测试人员在测试环境进行功能测试和性能测试。这期间开发人员只修测试提的 bug,不加新需求——新需求走下一个版本。
第5步:主干代码合并到版本分支
测试通过后,把 trunk 的最新代码合并到 release 分支。
这一步很多团队会搞反——把 release 合回 trunk。为什么要反过来?因为测试期间 trunk 上可能有其他人合了线上 bug 修复或者公共组件改动,这些改动发布分支还没包含。先把 trunk 合到 release,确保 release 分支包含了测试期间 trunk 上所有的变更。
第6步:预发布环境回归测试
合并完成后,把 release 发布到预发布环境,做回归测试。范围包括:本次需求版本的全部功能 + 测试期间 trunk 上合入的补丁内容。
这一关过了,才算真过了。
第7步:提交产品验收
测试通过后让产品部门在预发布环境验收。产品说 OK,代码才算 OK。不要跳过这一步——测试验的是"对不对",产品验的是"是不是我要的"。两个维度。
第8步:去掉 SNAPSHOT,改为正式版本号
产品验收通过后,把 1.0.0-SNAPSHOT 改成 1.0.0。版本号正式确定的那一刻起,这个分支的代码就是发布版,不允许再有任何修改(除非发布后发现问题,走补丁流程)。
第9步:发布上线 + 线上回归
发布到生产环境后,立即在线上做一轮快速回归——验证核心业务流程是否正常。不要等用户来报 bug。
第10步:将版本分支合并回 trunk
线上稳定运行后(通常观察 1-2 天),把 release 分支合回 trunk。至此 trunk 上包含了这个版本的全部正式代码,一个完整的迭代周期结束。
五、线上 Bug 修复流程
线上出了问题要马上修,流程跟正常迭代不一样。核心区别就一个字:快。但快不等于乱。
第1步:在最新的版本分支上修复
紧急 bug 从哪里修?不是从 trunk,是从当前线上对应的 release 分支拉出修复分支。
如果修复涉及公共组件或基础 jar 包,需要改基础包的代码,则在基础包的版本号后加临时 SNAPSHOT 标记,比如 common-1.0.1-SNAPSHOT。表示这是一个临时构建,待补丁验证通过后再去掉 SNAPSHOT 发正式版。
第2步:开发自测联调
修完代码后在本地自测。紧急补丁不能省略自测——越急越容易出错。
第3步:提交到测试环境,测试验证
测试人员只测这个补丁本身,不测全量功能(时间不允许)。但补丁涉及的核心路径必须覆盖。
第4步:预发布环境验证
补丁通过后发预发布,在预发布环境再做一轮验证。
第5步:有基础包修改的,去掉 SNAPSHOT
确认补丁没问题后,把临时 SNAPSHOT 版本号改为正式版本号。临时构建绝不带到生产环境。
第6步:发布上线 + 线上回归
发版后立即验证补丁是否生效。别发了就关了,确认线上问题真的解决了再收工。
第7步:将修复分支合并回 trunk
补丁在线上验证通过后,把修复代码合并回 trunk。这一步不能省——否则下一个版本发布的时候,这个补丁就丢了,bug 会重新上线。
六、关于 AB 角:为什么合并代码不能只有一个人做
我们团队有一个规矩:代码合并到主干,由各组的 AB 角来操作。
什么是 AB 角?就是每个模块分配两个人——A 角是主要负责人,B 角是备份人。日常合并 A 角做,A 请假了 B 顶上。但有一条硬规则:自己不能合自己的代码。 你写的代码,必须由另一个人来看一眼再合。
为什么定这个规矩?
起源是有一次,一个开发把自己写的 feature 分支直接合到了 trunk,没有通知任何人。他觉得"我就改了几行配置文件"。结果那几行配置文件里有一个数据库连接字符串的改动,直接导致预发布环境连上了测试库。全组排查了两个小时。
后来我们定了规矩:代码合并必须两个人。不是因为不信任,是因为任何人都会对自己的代码有盲区。你自己写的代码,你脑子里的上下文是完整的,你看不出问题。但旁边的人一眼就能看出不对劲。
具体操作很简单:
- 每个开发组指定 AB 角两个人
- 代码合并到 trunk 由 AB 角执行
- 合并的人不是写代码的人
- 合并前看一眼 diff,确认没有不该进去的东西
- A 角休假,B 角接管,确保合并通道不会因为一个人不在就中断
七、提交代码的规矩
提交信息格式我们强制要求写清楚三件事:
[类型] 解决了什么问题
涉及模块:xxx
修复版本:x.x.x
类型只允许这几种:
| 类型 | 用途 |
|---|---|
fix | 修 bug |
feat | 新功能 |
refactor | 重构(不改功能) |
docs | 文档 |
chore | 构建、依赖、配置 |
举个例子——好的提交信息长这样:
fix: 修复退款金额计算缺少手续费扣除的问题
涉及模块:退款模块 RefundService
修复版本:1.2.1
差的提交信息:
fix bug
区别一目了然。前者半年后出问题,看一眼就知道这条提交干了什么、影响了哪里、在哪个版本修了。后者翻出来等于没翻。
八、这套规范怎么落地
规范写出来容易,推下去难。我们当时是这样做的:
-
先跟组长对齐。 把两个组长叫到一起,让他们先理解为什么每条规则存在。组长不认可,下面的人更不会执行。
-
第一次发布让所有人参与。 不是说发就完了,是让每个人亲自走一遍流程。走一遍比看十遍文档有用。
-
头三次发布,组长在旁边看着。 有人漏步骤立刻指出。三次以后形成肌肉记忆,就不用盯了。
-
违反规范的后果要说清楚。 不是罚钱,是"谁违规导致的问题谁负责善后"。半夜上线你合并漏了代码?你来修复,我来陪。做一次就记住了。
九、总结
这套规范的核心思想就一句话:让每一步操作都可追溯,让每一个决定都有据可查。
不是约束你,是保护你。规范越清楚,出问题的时候越不用慌——因为你知道是哪一步出了错、谁做的、什么时候做的、怎么恢复。
六年前那次上线前的崩溃,教会了我这件事。希望你的团队不用经历同样的教训。
我把这套规范整理成了一份「Git 分支管理 Checklist 模板」,包含发布流程检查清单 + 提交信息模板 + AB 角操作说明。需要的评论区留言,我发你。