引言
Git 作为一个强大的分布式版本控制软件,对比 SVN、CVS 等版本控制系统,其架构和设计在在研发管理和协作上有着极大的优势。 在个人开发的代码管理和团队协作过程中,合理使用分支能在极大程度上提升管理效率。
从不用版本管理到使用 Git
分支管理的故事,也就是从这个时候开始的。。。
某公司只有一个程序员,一开始并没有版本管理的概念。因为项目开发只有一个人在参与,所以也没用版本管理工具。
后来,老板又招了两个程序员,老板说:“研发管理要规范”,经过一番调研,选用了 Git,三个人开始使用 Git 进行开发上的协作。
一开始,三个人都是通过一个仓库,在 master
分支上进行协作。每天上班第一件事就是先把最新的代码从服务器上拉到本地的 master
分支,下班前再把代码给推到服务器上的 master
分支。项目这样展开了协作。
三个人通过本地仓库 master 分支向远程仓库 master 分支提交代码
解决频繁的代码提交冲突
协作了几天,团队发现提交代码 master
时候,经常产生代码冲突,要么张三和李四的代码冲突,要么李四跟赵六的代码冲突。这时总要有人把代码拉下来解决冲突。才能保证后续开发工作顺利进行。 同时,由于缺少代码审查。部分质量较差的代码和无关内容也时不时被提交上去。团队在解决冲突和代码重构的问题上花费了不少精力。
为了解决单一分支频繁更新容易发生冲突,三人开始研究使用分支的方式进行协作,通过在本地 master
检出新分支,修改后将新分支推送到远程仓库,再通过拉取合并请求(Pull Request) 在远程仓库上合并分支到 master 分支。最后再把代码从远程 master
拉取到本地 master
进行更新。 在这个基础上,团队也开始展开相互的 代码审查(Code Review) 工作
本地 master 分支检出新分支开发,推送到远端仓库后通过 Pull Request 合并到 master,然后拉回本地 master
初步解决代码迭代版本问题
经过一番折腾,几个人的协作总算能比较稳定的进行。在后续的数周内,团队的开发工作顺利得以展开,项目也得以落地,进入快速迭代阶段。为了解决迭代的版本问题。团队使用分支对每次版本发布检出一个分支进行保留。
通过远程仓库 master 分支在版本发布时检出一个以版本号命名的分支,作为特定版本管理
团队增长带来的困扰
两个月后,公司迎来了业务的快速增长。技术团队从原来三个人快速增长到十来个人。原来的成员开始带着新人做业务,随着团队的增长,原有的协作方式再次遇到各种各样的问题。经过短短一周的磨合,三人无比疲惫地坐到一起,对过去一周遇到的问题进行了复盘:
- 随着协作人数增多,远程仓库分支数量快速增长,查找起来很麻烦,经常出现分支重名问题
- 分支命名混乱,提交新功能的分支和修复Bug的分支经常混淆在一块。
- 版本迭代的速度太快,产生了一大堆的 Realease 分支,又带来了一堆的管理问题。
- 未来得及合并 或 独立维护的分支,时间久了容易出现管理遗漏和维护混乱。
- 虽然有 Code Review,但程序的 Bug 数和 Crash 频率明显随团队规模而增长,生产事故发生率和回滚率提高。
- 还有有人把手头未完成开发的分支扔到远程仓库进行托管。。。
经过讨论三个人都意识到了问题所在,但无奈三人对于多人协作开发经验不多,讨论无果后,决定各自调研,再对比讨论。两天后,三个人带着方案展开了探讨。
单个仓库还是多个仓库?
在仓库管理的方案上,张三主张使用 单仓库多分支
的方式进行管理,李四则主张使用 多仓库多分支
的方式进行管理。
仓库管理方式 | 差异对比 | 优点 | 缺点 |
---|---|---|---|
单个仓库多分支 | 团队成员通过将主仓库Clone到本地,在本地Checkout出新分支后进行开发,并向主仓库提交分支,然后向主仓库提交PR。 | 单个仓库便于管理 | 所有成员分支都会提交到仓库中,分支数量多,不便管理 |
多仓库多分支 | 团队成员通过Fork主仓库,然后将个人Fork的仓库拉取到本地,在本地Checkout出新分支后进行开发,并向Fork的仓库提交分支,再从Fork的仓库向主仓库提交PR。 | 成员分支可以在 fork 的仓库中开发管理 | 大量 fork 仓库不便管理,每次都需要拉人进仓库协作,成员离职后需要跟进删除 |
经过讨论,为了避免反复加仓库协作者名单的协作成本问题,三人统一意见继续保留使用 单仓库多分支
的方式。
用「分支模型」规范分支管理
在分支策略上,张三提出了使用 分支模型 作为分支管理规范化的解决方案,从 分支类型、用途 等角度规范开发的协作方式
分支类型 | 常见分支名格式 | 分支用途和说明 | 协作指向和流程 | 读写/删除策略 |
---|---|---|---|---|
Production 分支 | master | 生产分支,用于存放发布到生产环境的代码。该分支只能从其他分支合并,不能直接基于该分支修改。 | 该分支将从 Develop/Release/Hotfix 分支合并数据。 | 原则上仅允许通过 Pull Request 写入,长期保留 |
Develop 分支 | develop | 主开发分支,包含所有要发布到下一个Release的代码 | 该分支将从 Feature/Release/Hotfix 分支合并数据,最终数据将合并到 Product分支中去 | 原则上仅允许通过 Pull Request 写入,长期保留 |
Feature 分支 | feature/* | 特性分支,主要是用来开发一个新的功能,一旦开发完成,我们合并回Develop分支进入下一个Release | 从 Develop 分支检出,最终将合并到Develop分支中去 | 被合并后可删除/可保留 |
Release分支 | release/* | 版本分支,用于版本发布调整使用过。 | 从 Develop 分支检出,最终将合并到 Product分支和 Develop分支中去 | 被合并后不再允许写入,可删除/可保留 |
Hotfix分支 | hotfix/* | 补丁分支,用于紧急修复生产环境的缺陷(Bug) | 从 Product分支检出,最终将合并到 Product和Develop分支中去,同时进入下一个Release | 被合并后可删除 |
经过一番讨论,三人达成了一致的意见,并针对上面的分支模型 提出了些许细节的调整。
灵活使用 git tag 和发行版管理功能
针对单个仓库协作可能存在分支数量增长的问题,张三提出通过 git tag
来取代 release/*
版本分支的作用。
在 git 中,标签(tag)是特定提交(commit)一个指针,也就是每个 tag 对应一个特定的 commit。release/* 系列分支在实质上就是合并到 Product (master) 分支上成为一个特殊提交,所以 tag 的存在使得没有必要保留 release/* 分支
另外,一般形如[码云]这样的 git 代码托管平台,本身自带「发行版(Release)」功能。
通过 git 本身只能记录项目的修改,而版本发布带来的项目构建物(特别是二进制文件)本身在某种意义上就不适合通过 git 进行存储。
通过「发行版(Release)」功能,可以将对应版本的源代码和生成的项目构建物(比如exe/dmg)保存下来,还支持编写对应的 Changelog,便于查找下载。
使用 git-flow 脚本规范本地分支和开发
除了在远程仓库上的管理方案,张三还建议提倡团队成员通过 [git-flow
] 一系列的脚本扩展,规范本地的分支管理和开发流程。
现网络上最流行的 git-flow
方案应该是 AVH 版 git-flow
:https://nvie.com/posts/a-successful-git-branching-model/ ,通过安装后,开发成员可以在本地通过对项目的各类功能分支进行定义。
git-flow 通过交互方式定义各类功能分支
通过一系列命令简化各种分支模型的管理操作,小范围功能可以通过本地合并到本地分支,或直接推送到远程再通过 Pull Request 合并操作
经过一番调研和讨论,三人最终决定了团队在代码协作上使用 单仓库多分支
的方式,采用分支模型进行管理。
接下来的问题就比较简单了,在码云平台 gitee.com 上新建仓库,选择相应的分支模型即可。
Git 的分支相比 SVN 来说是非常轻量级的,善用分支有利于更清晰的进行开发过程的管理。