2、版本控制基础:Git工作流选择、分支策略设计、代码仓库权限管理、Git Hooks触发流水线
版本控制是CI/CD流水线的地基。说实话,我见过太多团队把流水线搭得花里胡哨,结果分支策略一团糟,最后上线前合并冲突能搞到通宵。这一章,咱们就把地基夯实。
2.1 Git工作流选择:别选最火的,选最合适的
Git工作流不是越多越好。我个人习惯根据团队规模和发布频率来选。这里我列三种最常见的,你直接对号入座。
| 工作流类型 | 适用场景 | 核心特点 |
|---|---|---|
| Git Flow | 大型项目、固定版本发布 | 分支多、规则严、适合传统SaaS |
| GitHub Flow | 小型团队、持续部署 | 只有main和feature分支,简单粗暴 |
| GitLab Flow | 环境分支+功能分支混合 | 按环境(dev/staging/prod)拉分支 |
我的建议:如果你的SaaS系统每天要发版3次以上,别用Git Flow。那玩意儿分支太多,合并一次能把你累死。我曾在项目中硬套Git Flow,结果光合并就花了半天。后来换成GitLab Flow,清爽多了。
核心原则:工作流是为人服务的,不是为流程服务的。选一个团队能坚持执行的,比选一个理论完美的更重要。
2.2 分支策略设计:别让分支变成「分支地狱」
分支策略设计,说白了就是定规矩。谁往哪分支上推代码,什么时候合并,都得说清楚。我见过最惨的一次,一个项目有40多个长期分支,没人知道哪个是活的。
2.2.1 推荐的分支模型
- main分支:只接受PR合并,禁止直接push。每次合并自动触发生产部署。
- develop分支:日常开发集成分支,所有feature分支从这里拉。
- feature分支:从develop拉出,命名规范:
feature/xxx-功能描述。 - release分支:从develop拉出,只做bug修复,不做新功能。
- hotfix分支:从main拉出,紧急修复,修复后合并回main和develop。
避坑指南:我曾经规定feature分支必须从最新的develop拉出,结果团队经常忘记rebase。后来我改成:每次提PR前,必须rebase一次develop。这个习惯救了我们很多次。
2.2.2 分支命名规范
命名不规范,运维两行泪。我建议统一用这个格式:
feature/<项目代号>-<功能简述>
bugfix/<问题编号>-<问题简述>
hotfix/<版本号>-<紧急修复描述>
release/<版本号>
举个例子:feature/PAY-123-新增支付宝支付。这样一眼就能看出是哪个项目、什么功能。
2.3 代码仓库权限管理:别让所有人都能改main
权限管理是很多团队容易忽略的点。你想想看,如果每个开发都能直接push到main,那流水线还有什么意义?
2.3.1 推荐权限模型
| 角色 | 权限 | 说明 |
|---|---|---|
| Owner | 全部权限 | 通常1-2人,负责仓库设置、删除分支 |
| Maintainer | 合并PR、管理分支 | 核心开发者,可以合并到main |
| Developer | 创建分支、提交PR | 普通开发,不能直接合并到main |
| Reporter | 只读权限 | QA、产品经理,只能看代码 |
注意:千万不要给所有人Maintainer权限。我见过一个团队,20个人全是Maintainer,结果有人误删了release分支,回滚花了一整天。权限越少,事故越少。
2.3.2 分支保护规则
在GitLab或GitHub上,一定要设置分支保护。我通常这样配:
- main分支:必须通过CI检查才能合并,至少1个approve,禁止强制push。
- develop分支:必须通过CI检查,至少1个approve。
- release分支:必须通过CI检查,至少2个approve。
2.4 Git Hooks触发流水线:让代码自己「报警」
Git Hooks是流水线的触发器。说白了,就是当有人push代码时,自动告诉CI系统:「嘿,有新代码了,快跑流水线!」
2.4.1 常用Hook类型
| Hook名称 | 触发时机 | 典型用途 |
|---|---|---|
| pre-commit | 提交前 | 代码格式化、静态检查 |
| commit-msg | 提交信息写入后 | 检查提交信息格式 |
| pre-push | 推送前 | 运行单元测试、检查分支名 |
| post-receive | 推送后(服务端) | 触发CI/CD流水线 |
2.4.2 实战:用pre-push Hook触发流水线
嗯,这里要注意。客户端Hook是本地执行的,服务端Hook才是真正触发流水线的。我建议在服务端配置post-receive Hook。
举个例子,在GitLab上配置Webhook:
# 在GitLab项目设置中 -> Webhooks
URL: https://your-ci-server.com/webhook/gitlab
Secret Token: your-secret-token
Trigger: Push events, Merge request events
SSL verification: Enable
然后在CI服务器上接收这个请求,启动流水线。我习惯用这种方式:
# 伪代码示例
def handle_webhook(request):
event = request.headers.get('X-Gitlab-Event')
if event == 'Push Hook':
branch = request.json['ref'].split('/')[-1]
if branch in ['main', 'develop', 'release/*']:
trigger_pipeline(branch)
elif event == 'Merge Request Hook':
if request.json['object_attributes']['action'] == 'merge':
trigger_pipeline('merge-check')
我的经验:不要所有push都触发流水线。我曾在项目中设置「只有push到main、develop、release/*时才触发」,结果feature分支的push从来不跑CI。后来我改成:feature分支push时跑轻量级检查(lint+单元测试),只有合并到develop时才跑完整流水线。这样既节省资源,又不漏检查。
2.4.3 客户端Hook:把问题扼杀在本地
服务端Hook是最后一道防线。但更好的做法是在本地就发现问题。我推荐用husky(前端项目)或pre-commit框架来管理客户端Hook。
# .husky/pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run lint
npm run test:unit
# 如果lint或测试失败,提交会被阻止
为什么要这么做?因为等代码推到远程再发现格式问题,已经浪费了CI资源。我曾经有个项目,团队20个人,每天CI排队要等10分钟,就是因为太多人把格式错误的代码推上去了。后来加了pre-commit Hook,排队时间直接降到2分钟。
2.5 避坑总结
最后,我把自己踩过的坑总结一下,你直接拿去用:
- 分支太多?定期清理已合并的分支,设置自动删除策略。
- 权限太乱?每季度审计一次仓库权限,移除离职人员的权限。
- Hook不生效?检查Hook文件是否有执行权限,
chmod +x .git/hooks/*。 - 流水线触发失败?先检查Webhook的Secret Token是否匹配,再检查CI服务器日志。
版本控制这块,说白了就是「规矩定好,工具配好,剩下的交给自动化」。下一章我们会讲构建与编译,到时候你会发现,如果版本控制没做好,后面全是坑。