3、版本控制策略:Git分支模型、代码审查、标签与发布管理、子模块管理
版本控制这事儿,在车载软件开发里,真不是简单的「谁改了什么」。
我见过太多团队,代码写得挺好,一合并就崩。为什么?分支策略没想清楚。今天咱们就聊聊,怎么用Git把这摊事儿理顺。
3.1 Git分支模型:选对路子,少走弯路
车载软件有个特点:硬件依赖强,版本迭代慢,但稳定性要求极高。你想想看,一个刹车控制器的代码,能随便在主分支上改吗?
所以,分支模型的选择,直接决定了你的CI/CD能不能跑起来。
3.1.1 GitFlow:适合传统车载项目
我个人习惯在大型、多版本并行的项目里用GitFlow。它结构清晰,但有点重。
- master分支:只存发布过的代码。每次发布,打一个tag。
- develop分支:日常开发的主战场。所有feature分支都从这拉。
- feature分支:开发新功能。比如
feature/autopilot-v2。 - release分支:准备发布时用。只修bug,不加新功能。
- hotfix分支:线上出紧急问题,从master拉,修完合并回master和develop。
我的经验:在车载项目中,release分支特别重要。我记得有一次,一个ADAS项目要同时维护三个硬件平台的版本。没有release分支,光合并就能把人逼疯。
3.1.2 Trunk-Based:适合快速迭代的模块
说白了,Trunk-Based就是所有人都在主干上开发,或者最多拉个短期分支。适合那些频繁集成、自动化测试覆盖率高的模块。
为什么会有人用这个?因为快。没有合并地狱,没有长命分支。
- 分支存活时间不超过一天。
- 每次提交都要通过CI。
- 代码审查必须快,否则阻塞别人。
注意:Trunk-Based对测试要求极高。我曾经在一个项目中强行推Trunk-Based,结果单元测试覆盖率只有30%,上线第一天就崩了。嗯,从那以后我学乖了——测试不到位,别玩主干开发。
3.1.3 我的建议:混合策略
别死磕一种模型。我现在的做法是:
- 核心安全模块(如制动、转向)用GitFlow,严格管控。
- 非安全模块(如娱乐系统、OTA客户端)用Trunk-Based,快速迭代。
- 中间层(如通信中间件)用简化版GitFlow,只保留master和feature分支。
3.2 代码审查流程:不只是走个过场
代码审查,说白了就是「让别人看看你写的代码有没有坑」。但在车载领域,这步不能省。
3.2.1 审查什么?
- 功能正确性:逻辑对不对?边界条件处理了吗?
- 编码规范:MISRA C/C++合规吗?命名规范吗?
- 安全相关:有没有潜在的内存泄漏?有没有未初始化的变量?
- 测试覆盖:新代码有没有对应的单元测试?
3.2.2 审查流程
- 开发者提交PR,关联Jira/Redmine任务。
- CI自动运行:编译、静态检查、单元测试。
- 至少两名审查者:一个熟悉业务,一个熟悉架构。
- 审查者提出意见,开发者修改。
- 所有讨论resolve后,合并。
避坑指南:我曾经见过一个团队,审查者只看代码格式,不看逻辑。结果一个指针越界的bug在生产环境跑了三个月才被发现。所以,我建议审查时重点关注逻辑和边界条件,格式问题交给自动化工具。
3.3 标签与发布管理:给版本上个锁
标签,就是给某个commit打个标记,方便回滚和追溯。在车载软件里,标签就是你的「发布凭证」。
3.3.1 标签命名规范
| 类型 | 格式 | 示例 |
|---|---|---|
| 正式发布 | v主版本.次版本.修订号 | v2.1.0 |
| 预发布 | v主版本.次版本.修订号-rc.序号 | v2.1.0-rc.3 |
| 热修复 | v主版本.次版本.修订号-hotfix.序号 | v2.1.1-hotfix.1 |
3.3.2 发布流程
- 从develop分支拉release分支。
- 在release分支上修bug,打预发布标签。
- 通过所有测试后,打正式发布标签。
- 合并到master和develop。
- 在master上打正式标签,触发CI/CD构建。
我的经验:标签一定要和CI/CD联动。我习惯在CI脚本里加一步:检查当前commit是否有对应标签。没有标签,不允许部署到生产环境。这招帮我挡过好几次「手滑」的误操作。
3.4 子模块管理:别让依赖变成噩梦
车载软件里,子模块太常见了。比如AUTOSAR的配置、第三方库、甚至不同ECU的共享代码。Git子模块(submodule)是个好东西,但用不好就是灾难。
3.4.1 什么时候用子模块?
- 多个项目共享同一个库(如通信协议栈)。
- 第三方依赖需要固定版本(如某个芯片的驱动)。
- 不同模块有独立的版本节奏。
3.4.2 常见坑与解决方案
| 问题 | 表现 | 解决方案 |
|---|---|---|
| 子模块指针漂移 | 别人拉代码,子模块版本不对 | 每次提交父项目时,确保子模块指针已更新 |
| 子模块修改未提交 | 本地改了子模块,忘了push | CI中增加检查:子模块是否有未提交的修改 |
| 子模块过多 | 拉取代码慢,管理混乱 | 考虑用repo工具或Git subtree替代 |
注意:我曾经在一个项目中,子模块嵌套了三层。结果每次更新依赖,都要手动更新每一层的指针。后来我改用Git subtree,虽然合并时有点麻烦,但至少不会出现「子模块的子模块」这种鬼东西。
3.4.3 子模块管理最佳实践
- 子模块尽量扁平化,不要嵌套。
- 在README中写明子模块的用途和版本要求。
- CI脚本中增加
git submodule update --init --recursive。 - 定期清理不再使用的子模块。
好了,版本控制策略这块,说白了就是「怎么管代码、怎么审代码、怎么发代码、怎么管依赖」。你想想看,如果这四件事没做好,CI/CD再自动化也是白搭。嗯,下一章咱们聊聊自动化构建和测试的集成,那才是真正让CI/CD跑起来的关键。