实践gitflow
结合git flow,使用gitlab作为远端仓库管理,在实际的项目中是一种可行的方式,而且这种方式对与复杂大型的项目有较好的适应方式。
git flow
git flow源于Vincent Driessen在2010年提出的一个分支模型:
主要特点
两个长期分支
git flow中有两个长期的分支,一直不会被删除,这两个分支是develop和master。
分支 | 生命期 | 作用说明 |
---|---|---|
master | 长期 | 用于保持和生产环境一致或者半步先于生产环境,主要目的用于保证生产环境的实时可用状态 |
develop | 长期 | 开发的集成分支,主要目的用于显示最新的开发状况 |
实践原则1:
master 分支时常保持着软件可以正常运行的状态,一般不允许开发者直接对master 分支的代码进行修改和提交。结合gitlab的具体方式,可以将改分支设定为protect方式。
实践原则2:
master分支专人管理,结合gitlab的权限管理,设定团队负责发布的人具有owner或者master的权限,其余人禁止直接对此分支进行操作。
master分支的使用场景
master分支主要在以下场景中会用到:
生产环境bug再现与调查
当代码规模和团队规模到了一定程度之后,如果缺乏有效的管理,加之多团队的开发,尚未发布的功能,临时对应的bug,甚至还会有一些手动修改暂时未归入版本管理的文件,这些混杂在一起会变得非常的复杂。所以在问题发生时,想清楚的知道bug发生的生产化境所对应的源码基线都是一件困难的事情,此分支与上线功能息息相关,必须严格管理。
新的功能发布
特性分支开发完毕,完成测试,确认此分支内容可以进行发布之后,便可与master分支合并,发布时一般会附加版本编号。
紧急bug修正发布
紧急bug修正之后,需要立即体现在master分支上,不然下次发布依然会有很多问题
实践原则3
建议在项目中添加自动化测试环节,使用gitlab的webhook设定,在merge时根据项目需要自动运行自动化测试验收,如果不通过及时报警,此自动化测试即使是很少的覆盖率,也能防范大部分人为因素导致的问题,比如不小心将一个编译都没有通过的版本推到了master上,即使专人负责,但是专人并不一定对所有的技术和项目功能以及实时状态都有全面的掌控,另外所有的问题都需要人去检查和确认,一张会无限增长的checklist从此产生,最终会形成一个几千行甚至更多的一个虽都不会去看的敷衍了事的检查环节,此环节在很多项目实践中已经得到证实:只在事后检讨的时候有用,检讨之后这张checklist会变得更长。
develop分支的使用场景
特性开发合并
根据项目的模块进行拆分,不同的模块不同的团队负责,或者根据特性进行开发,不同的特性不同的团队负责,这些分支开发完毕时需要合并到develop分支上,以保证develop分支具有最新的功能。
紧急bug修正发布
紧急bug修正之后,需要立即体现在master分支上,但是同时需要在develop分支上进行反应,为何不直接在develop分支上进行管理,有很多因素需要考虑,比如新的功能虽然开发完毕了,但是由于其他制约的因素,发布的时期尚未确定,不能将这些已经完成开发和测试的内容发布到master分支上,简单来说此模型的出发点在于master分支用于和生产环境直接关联,之所以多一条分支就是为了管理各种复杂的难以掌控的项目实际需求的。紧急bug修正之后,master和develop分支都需要进行更新,前者保证下次发布不会将此紧急修正的内容覆盖,后者保证通过下次通过测试的内容合并到master上时不会覆盖此内容或者尽可能的减少merge操作。
实践原则4
develop分支是开发过程中代码中心分支,与master 分支一样,这个分支也是非常重要的分支,这个分支的合并操作可能每个人都会参与,如果做专人管理的话,基本上所有的开发者都需要参与。所以,合并之前需要建立基础的原则,比如java的项目最好做到如下内容才合并到本分支
本地编译通过
本地本次修改内容没有新的sonarqube高级别的defect对应
本地单体测试通过
手工或者自动化验收的测试通过
实践原则5
develop分支进行传统的每日构建甚至实时构建,结合不断完善的自动化测试,以可以接受的成本和代价保证开发的流水线不会断开。
实践原则6
设立时限原则:develop分支一旦发生构建或者关联的自动化测试出现问题,结合工具第一时间定位到对应的负责人进行回滚或者紧急修正,根据项目集成情况进行设立时限,比如平均每日由各个临时分支提交的功能为4次,则可设立时限最长不超过2小时,如果超过2小时无法对应,则意味着下次会有人在这个错误的版本上进行再一次的提交,错上加错是最需要避免的,发现问题在第一时间解决,超过时限,立即止损回滚。
三种临时分支
相比于长期存在的分支,git flow的模型中还有三种临时性的分支
分支类型 | 说明 | 是否可为多条 |
---|---|---|
Feature分支 | 特性分支 | 可为多条 |
Hotfix分支 | bug对应分支 | 可为多条 |
Release分支 | release实施分支 | 可为多条 |
特性分支
特性分支有时也被称为Topic分支,一般是用来开发新的特性,而其关联的发布可能近在眼前,也有可能需要很长一段时间,特性分支最终会被合并到develop分支或者最终会被丢弃。
丢弃特性分支开发的内容在敏捷开发中并不罕见,尤其是当项目需要不断地试验的阶段。
根据DevOps的调查显示,Trunc-based开发方式是主流推动的开发方式,但是由于项目的复杂性和实际推行时候各种制约因素的存在,
多条分支长时间的并行还是很难避免的,特性分支开发的时候在git flow模型中一般遵循如下步骤:
步骤 | 内容 | git 命令 |
---|---|---|
Step 1 | 以develop分支为基础创建特性分支 | git checkout -b 特性分支名称 develop |
Step 2 | 进行特性内容开发直至可以提交 | |
Step 3 | 特性分支开发完成后切换至develop分支 | git checkout develop |
Step 4 | 合并特性分支开发内容到develop分支 | git merge –no-ff 特性分支名称 |
Step 5 | 删除特性分支 | git branch -d 特性分支名称 |
Step 6 | push修改内容到远端仓库保存 | git push origin develop |
特性分支的使用注意事项如下:
注意事项 | 详细说明 |
---|---|
源分支 | 特性分支需要以develop分支为源分支 |
目标分支 | 特性分支的合并目标分支为develop分支,此模型中只能合并回develop分支 |
分支命名 | 除去master/develop/hotfix-/release-命名的所有分支 |
实践原则6
特性分支合并或者其他的分支合并,尽可能不使用fast forward的方式,以方便回滚和状态确认。
实践原则7
特性分支在git flow中仍然是临时性分支,使用之后请删除此分支,同时删除远程仓库上的该分支。另外,分支的增多以为着可能的沟通/合并/冲突等的增加,而这些正是精益开发中
所要规避的“waste”,不用即删,因为在实践中我们已经利用了git的非fast forward的方式,所以特性分支开发的信息已经完整地在develop分支保存,删除不会引起问题追踪上的问题。
Release分支
Release分支基于develop分支为基础进行创建,主要用于版本的发布,主要用于保证最后一步的安全和顺畅,为发布所需要的各种准备以及手工操作,甚至小规模的bug对应都可以在此分支上完成。而当Release分支上的一切做完之后,标志着最新develop分支的功能已经发布到了master,同时为下一次发布的功能特性的接收已经开始。
实践原则8
特性分支或者其他分支合并到develop开发主分支的策略需要项目根据情况自行创建,最主要的原则之一在于尽量避免由于发布时期的不同导致频繁的分支合并/版本挑选等手工作业的发生。
比如发布计划明明是最后,但是在项目已开始的时候就合并进了主分支,这样会导致后续每次发布的时候都需要将此内容挑选出去,虽然使用gitlab的rebase+cherry pick等功能可以实现这种要求,
但是单纯在提交的规范上进行要求则可以避免这种无用的浪费的发生。
实践原则9
Release分支主要目的是用于大型项目中各个特性分支开发完毕之后,定义了一个准备就绪到工作完成的中间阶段,一般会和项目的管理工作结合进行,比如进行发布的申请和批准,结果确认和审核,
由于不同的开发流程不仅需要考虑到技术本身,还要考虑到各个公司实际的流程规范/audit等等,以保证release能够正常进行。虽然可以在此分支上进行小量的修改,但是大规模的功能修改请尽可能地规避。
Release分支在使用时候一般遵循如下步骤:
步骤 | 内容 | git 命令 |
---|---|---|
Step 1 | 以develop分支为基础创建release分支 | git checkout -b release-版本号 develop |
Step 2 | 做Release准备或者小的bug对应,然后进行提交 | git commit -a -m “release相关信息” |
Step 3 | 切换至master分支 | git checkout master |
Step 4 | 合并Release分支内容到master分支 | git merge –no-ff release-版本号 |
Step 5 | 设定tag | git tag -a 版本号 -m “release的tag信息” |
Step 6 | 切换至develop分支 | git checkout develop |
Step 7 | 合并Release分支内容到develop分支 | git merge –no-ff release-版本号 |
Step 8 | 删除release分支 | git branch -d release-版本号 |
Step 9 | 推送develop分支到远程仓库 | git push origin develop |
Step 10 | 切换至master分支并推送master分支到远程仓库 | git checkout master; git push origin master |
Step 11 | 推送tag信息到远程分支 | git push origin 版本号 |
Release分支的使用注意事项如下:
注意事项 | 详细说明 |
---|---|
源分支 | release分支需要以develop分支为源分支 |
目标分支 | 特性分支的合并目标分支为develop分支和master,在此分支上的诸如小的bug修正必须同时反映到develop分支和master分支,不然就会给下次发布留下隐患 |
分支命名 | release-* |
实践原则10
为了避免提交或者tag信息被恶意修改,可以在使用签名技术实现这一保障。签名使得操作具有了不可抵赖性和无法篡改性两种保证。而在git中只是需要加入-u或者-s选项即可简单实现。
hotfix分支
hotfix分支与release分支非常相像,都牵扯到向生产环境ready的master进行版本的更新。但是不同的是hotfix往往是因为生产环境上出现了非常紧急的问题对应,需要立即对应,而项目的特性开发等又不想受到影响而中断,这时就可以依据master生成一个新的hotfix的分支,在此分支上进行bug的对应,既不影响develop主开发分支,又能保证到生产环境紧急问题的及时对应,而对应完毕之后同时向develop主开发分支进行同步跟新即可。
实践原则11
诸如hotfix和release上的对应需要向git flow的两条主线同时进行更新,如果一旦忘记其中一条,势必会引起不必要的后续问题,在流程规范或者工具的自动化里面建立增加对此操作执行的确认。
Hotfix分支在使用时候一般遵循如下步骤:
步骤 | 内容 | git 命令 |
---|---|---|
Step 1 | 以master分支为基础创建hotfix分支 | git checkout -b hotfix-版本号 master |
Step 2 | 进行bug对应,然后进行提交 | git commit -a -m “hotfix相关信息” |
Step 3 | 切换至master分支 | git checkout master |
Step 4 | 合并hotfix分支内容到master分支 | git merge –no-ff hotfix-版本号 |
Step 5 | 设定tag | git tag -a 版本号 -m “hotfix的tag信息” |
Step 6 | 切换至develop分支 | git checkout develop |
Step 7 | 合并hotfix分支内容到develop分支 | git merge –no-ff hotfix-版本号 |
Step 8 | 删除hotfix分支 | git branch -d hotfix-版本号 |
Step 9 | 推送develop分支到远程仓库 | git push origin develop |
Step 10 | 切换至master分支并推送master分支到远程仓库 | git checkout master; git push origin master |
Step 11 | 推送tag信息到远程分支 | git push origin 版本号 |
hotfix分支的使用注意事项如下:
注意事项 | 详细说明 |
---|---|
源分支 | hotfix分支需要以master分支为源分支 |
目标分支 | 特性分支的合并目标分支为develop分支和master,修正必须同时反映到develop分支和master分支,不然就会给下次发布留下隐患 |
分支命名 | hotfix-* |
实际使用
接下来我们使用gitlab10.4.2以及git1.8.3.1来模拟一下git flow开发的流程。
创建一个项目
首先我们使用gitlab的restapi创建一个项目,当然也可以直接在gitlab上进行图形界面操作,具体命令如下,请根据自己的gitlab的URL和token进行修改, 另外jq命令如果没有可以不使用,对结果不产生影响,仅仅对结果的显示格式进行整形而已
执行命令
curl –request POST –header “PRIVATE-TOKEN: sqiSUhn3tHYXe8nSGRDi” –data “name=gitflowmodel” “http://127.0.0.1:32001/api/v4/projects” |jq .
执行log
[root@devops ~]# curl --request POST --header "PRIVATE-TOKEN: sqiSUhn3tHYXe8nSGRDi" --data "name=gitflowmodel" "http://127.0.0.1:32001/api/v4/projects" |jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1907 100 1890 100 17 1711 15 0:00:01 0:00:01 --:--:-- 1711
{
"id": 2,
"description": null,
"name": "gitflowmodel",
"name_with_namespace": "Administrator / gitflowmodel",
"path": "gitflowmodel",
"path_with_namespace": "root/gitflowmodel",
"created_at": "2018-02-04T14:11:10.055Z",
"default_branch": null,
"tag_list": [],
"ssh_url_to_repo":