刚进公司的时候,团队用SVN做版本控制。后来项目越来越大,SVN的集中式管理越来越不方便——断网就不能提交,分支合并痛苦不堪,速度还慢。去年我们决定迁移到Git,本以为换了工具就能解决所有问题,没想到新的混乱才刚刚开始。

一开始大家都不会用Git,各搞各的。有人直接在master上改代码,有人建了一堆名字乱七八糟的分支,有人提交完不push,有人push完不pull。最严重的一次,两个人同时改了同一个文件,合并冲突解决了半天,最后还丢了一部分代码。

痛定思痛,我们决定规范Git分支管理。研究了几种模型之后,选择了Git Flow,因为它适合我们这种有定期发布周期的团队。

Git Flow 核心概念

Git Flow的核心是五种分支:master、develop、feature、release、hotfix。

master分支:存放随时可以部署到生产环境的代码,只能从其他分支合并,不能直接在上面提交。每次合并都打tag,记录版本号。

develop分支:日常开发的主分支,包含最新的开发代码。所有feature分支都从develop切出,完成后合并回develop。

feature分支:开发新功能用的分支,从develop切出,命名规范是feature/功能名。比如feature/user-login、feature/article-search。开发完成后合并回develop,然后删除这个分支。

release分支:发布新版本前用的分支,从develop切出,命名规范是release/版本号,比如release/1.2.0。在这个分支上做发布前的测试和Bug修复,完成后同时合并到master和develop,然后打tag。

hotfix分支:生产环境出了紧急Bug时用的分支,从master切出,命名规范是hotfix/描述,比如hotfix/login-error。修复完成后同时合并到master和develop,然后打tag。

我们的实践经验

规范定好了,执行是关键。我们团队的具体做法是:

第一,每个新功能必须建feature分支,不允许直接在develop上开发。分支名用英文小写加连字符,比如feature/wechat-login,不能叫feature/zhangsan、feature/test这种无意义的名字。

第二,提交信息要规范。我们用了Angular的提交信息规范:type(scope): subject。type包括feat(新功能)、fix(修复Bug)、docs(文档)、style(格式)、refactor(重构)、test(测试)、chore(构建)。比如feat(article): add search function,fix(user): correct login timeout。这样看提交日志一目了然,还能自动生成changelog。

第三,合并用Pull Request(我们用的是GitLab),不允许直接push到develop和master。PR至少需要一个人review通过才能合并,review的时候要看代码逻辑、命名规范、有没有安全问题。一开始大家觉得review麻烦,后来发现确实能抓到不少低级错误。

第四,每天早上pull一次develop,保持本地代码最新,减少合并冲突。如果feature分支开发时间长,每周至少rebase一次develop,把最新的代码合进来,避免最后合并时冲突一大堆。

踩过的坑

实践过程中也踩了不少坑。第一个坑是rebase和merge搞混。有个同事在feature分支上用了git pull --rebase,结果把公共分支的历史改了,其他人pull之后各种问题。后来我们规定:公共分支(master、develop)只能用merge,个人分支(feature)可以用rebase,但rebase之后必须force push,而且要通知其他人。

第二个坑是分支不及时删除。feature分支合并后不删,时间一长仓库里堆了几十个废弃分支,看着就乱。我们在GitLab里设置了合并后自动删除源分支,解决了这个问题。

第三个坑是大文件提交。有人把几十兆的设计稿提交到Git里,导致仓库越来越大,clone一次要好几分钟。后来我们用了Git LFS来管理大文件,并且在.gitignore里忽略了常见的大文件格式。

总结

从SVN到Git,从混乱到规范,我们花了三个月时间。现在团队五个人协作,分支清晰,提交规范,合并冲突很少,代码质量也有提升。工具只是一方面,关键是团队成员都要遵守规范,养成良好的协作习惯。

Git Flow不是唯一的模型,也不一定适合所有团队。如果是持续部署的互联网产品,可能GitHub Flow更合适;如果是小团队快速迭代,甚至可以简化流程。重要的是找到适合自己团队的方式,并且坚持执行。