基于Git分支模型的理解与思考
进入企业后,开发过程中为了实现多人同步协作,企业往往会有一套 git 分支规范,这和学习中遇到的分支大有不同。本文是对 git 成熟模型 A successful Git branching model 的理解和对公司约定的 git 分支规范的思考。
在实际生产开发的过程中,如果每个人都随意的创建分支,随意的提交commit,必将导致整个git仓库非常的混乱,不易于团队协作。Vincent Driessen 同学为了解决这个问题提出了 A successful Git branching model,最后形成了业内普遍采用的 git 工作流程,大家都在约定的流程内使用git,使得团队协作效率大大提高。
为什么使用Git?
Git 是一个分布式版本管理工具,在多人协作的条件下高效处理任何规模的软件工程,并对项目版本、状态做环境隔离。每一个Git克隆都是一个完整的文件库,含有全部历史记录和修订追踪能力,不依赖于网络连接或中心服务器。最大特色是分支和合并操作非常快速、简便。
与SVN相比,git有以下5点优势:
版本库本地化,支持离线提交,相对独立不影响协同开发。每个开发者都拥有自己的版本控制库,在自己的版本库上可以任意的执行提交代码、创建分支等行为。例如,开发者认为自己提交的代码有问题?没关系,因为版本库是自己的,回滚历史、反复提交、归并分支并不会影响到其他开发者。
更少的“仓库污染”。git对于每个工程只会产生一个.git目录,这个工程所有的版本控制信息都在这个目录中,不会像SVN那样在每个目录下都产生.svn目录。
把内容按元数据方式存储,完整克隆版本库。所有版本信息位于.git目录中,它是处于你的机器上的一个克隆版的版本库,它拥有中心版本库上所有的东西,例如标签、分支、版本记录等。
支持快速切换分支方便合并,比较合并性能好。在同一目录下即可切换不同的分支,方便合并,且合并文件速度比SVN快。
分布式版本库,无单点故障,内容完整性好。内容存储使用的是SHA-1哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。
主分支
对于 Git 初学者来讲,应该都很熟悉 master-develop 双分支模型吧。我们一般把 origin/master
库认作为主分支,HEAD 的源代码存在于此版本中,并且随时是预备生产状态。而 origin/develop
分支 HEAD 源码始终体现下个发布版的最新变更,当 develop
分支的源码到达了一个稳定状态待发布,所有的代码变更需合并到 master
分支,然后标记一个版本号。
所以,每次变更都合并到了master,这就是新产品的定义。理论上,每当对master
有一个提交操作,我们就可以使用Git钩子脚本来自动构建并且发布软件到生产服务器。
辅助性分支
开发模型一般会使用各种辅助性分支,这些分支与关键分支(master
和develop
)一起,用来支持团队成员并行开发,协助生产发布环境准备,以及快速修复实时在线问题。与关键分支不同,这些分支是有生命周期的,因为它们最终会被移除。
我们用到的分支类型包括:
- 功能分支(
feature
) - 发布分支(
release
) - 测试分支(
sit
) - 预发布分支(
uat
) - 热修复分支(
hotfix
)
每一种分支有一个特定目的,并且有一定程度的规则,比如:可以用哪些分支作为源分支,哪些分支能作为合并目标。
功能分支(feature)
功能分支通常为即将发布或者未来发布版开发的新的功能。当新功能开始研发时,负责此需求的工作者应该本地创建一个相应的功能分支进行开发(注意不要将本地分支 push 到远程)。
可能是 develop/release 分支的分支版本,最终必须合并到 develop/release 分支中。
根据企业自身状况,功能分支可能也会从 release/版本号 中拉取,因为也许企业需要两个版本的系统并行开发,只有 develop
分支作为主要的开发环境不太能满足这种需求。
创建一个功能分支
开始一项功能的开发工作时,基于develop
创建分支
1 | git checkout -b feature/v1.0/myfeature develop |
合并一个功能到develop分支
完成的功能可以合并进develop
分支,明确加入到未来的发布:
1 | git checkout develop |
–no-ff 标志导致合并操作创建一个新commit对象,即使该合并操作可以fast-forward。这避免了丢失这个功能分支存在的历史信息,将该功能的所有提交组合在一起。
如果直接 git merge,不可能从 Git 历史中看到哪些提交一起实现了一个功能,必须手动阅读全部的日志信息。因此回滚一组提交也变得非常困难。
发布分支(release)
Release
分支是为新产品的发布做准备的,创建时要为即将发行版本分配一个版本号。
Release
分支可能从 develop
分支分离,但是最终一定要合并到 develop
和 master
分支上。
从develop
分支创建新的 release
分支的关键时刻是 develop
分支达到了发布的理想状态。release
分支允许一些小bugs的修改和准备发布元数据(版本号、开发时间)。
创建一个release分支
若当前产品的发行版本是1.1.5,同时有一个大的版本即将发行。develop
分支为下次发行做好了准备,决定好下一个版本是1.2。所以此时可以将release
分支分离出来,给一个能反映版本号的分支名。
1 | git checkout -b release-1.2 develop |
创建新分支以后,切换到该分支,添加版本号。这里,bump-version.sh 是一个虚构的shell脚本,它可以复制一些文件来反映新的版本(这当然可以手动改变–目的就是修改一些文件)。然后版本号被提交。
这个新分支可能会存在一段时间,直到该发行版到达它的预定目标。在此期间,bug的修复可能被提交到该分支上(而不是提交到develop
分支上)。在这里严格禁止增加大的新features。他们必须合并到develop
分支上,然后等待下一次大的发行版。
完成一个release分支
当一个release
分支准备好成为一个真正的发行版时(即已经通过所有测试),release
分支要合并到master
上(因为每一次提交到master
上的都是一个新定义的发行版)。提交到master
上必须打一个标签,以便以后更加方便引用历史版本。最后,在release
分支上的修改必须合并到develop
上,以便未来的发行版包含这些bugs的修复。
1 | git checkout master |
合并到develop
上:
1 | git checkout develop |
最后删除掉这个release
分支
1 | git branch -d release-1.2 |
测试分支(sit)
测试环境分支,只接受 feature
,hotfix
, release
分支的合并。研发内测联调通过后,由研发人员将自己的 feature
分支或 hotfix
分支代码合并到 sit
。该分支对应测试环境,测试人员的专用测试环境。
创建一个 sit
sit
可以基于 feature
分支创建
1 | git checkout -b sit myfeature |
完成一个 sit
1 | git checkout -b sit myfeature |
预发布分支(uat)
产品版本即将发布上线时,必须经过预发布分支的测试。
预发布环境,只接受 hotfix
、release
分支的代码合并。
创建一个uat
一般此分支是基于 release
分支创建:
1 | git checkout -b uat-1.2 release |
如果生产环境下突然出现需要紧急修复的情况,uat
也可以支持 hotfix
的合并
完成一个uat
1 | git checkout -b uat-1.2 release |
热修复分支(hotfix)
热修复分支通常用于紧急修复的情况,项目上线到生产环境后多多少少会有一些漏洞需要进行紧急修复,因此热修复分支可以基于master
分支上对应与线上版本的tag创建。
可以基于 master
分支,必须合并回 develop
和 master
分支。
创建修补bug分支
hotfix branch
是从 master
分支上面分出来的。例如,1.2 版本是当前的生产环境版本并且有 bug。但是开发分支变化还不稳定,需要分出来一个修补 bug 分支来解决这个问题。
1 | git checkout -b hotfix-1.2.1 master |
分支关闭时不要忘记更新版本号,然后修复bug
1 | git commit -m "Fixed severe production problem" |
完成一个 hotfix 分支
完成一个hotfix
之后,需要把hotfix
合并到master
和develop
分支去,这样就可以保证修复的这个bug也包含到下一个发行版中。这一点和完成release
分支很相似。
首先,更新master
并对release
打上tag:
1 | git checkout master |
编辑:你可能也会想使用 -sor-u
下一步,把bugfix添加到develop
分支中:
1 | git checkout develop |
如果一个release
分支已经存在,那么应该把hotfix
合并到这个release
分支,而不是合并到develop
分支。当release
分支完成后, 将hotfix
分支合并回release
分支也会使得hotfix
被合并到develop
分支。
最后,删除临时分支:
1 | git branch -d hotfix-1.2.1 |