书接上文,Git基础知识,本文介绍Git在实际工作中的分支管理。
1. Git开发流程
1.1 最原始的开发流程
一家公司成立了,需要技术人员,张三通过层层筛选,成为公司的第一名开发人员
公司的第一个目标是做一个视屏平台,第一期的目标:公司提供影片,让用尸能够注册、登陆、观看即口
张三拿到需求以后,确定了第一个版本的开发内容:用户中心、web视屏播放
上线之后,用户增长很快,基于用户的反馈,公司决定对于 web视频播放模块增加弹幕功能
于是张三又开始开发了
这个时候有部分用户反馈,自己的头像修改不了,经过定位,发现是**线上bug**
张三正在开发弹幕功能,遇到了线上bug,要怎么办呢?
弹幕功能还没有开发完,这部分代码肯定不能发布到先上去,所以不能基于最新的代码修复bug
线上的bug 又要立马修复,所以张三新建了一个文件夹,重新拉取代码,回滚到v1上线的版本,开始了bug修复
v1-bug是基于v1开发的,落后于版本库中的版本,因此不能提交到版本库。因此,需要先把版本库中的【弹幕功能(30%)】合并到v1-bug中。但是,如果合并了的话,就包含了未开发完成的代码,不能上线。于是,只能在合并之前,在本地打包,交给运维发布到线上。
修复好bug以后,张三需要基于修复了bug的弹幕功能(30%) + v1-bug-1版本继续开发。
这个时候他可以:
- 直接在『文件夹2』下进行开发,因为此时『文件夹2』里的代码是最新的
- 回到『文件夹1』,拉取最新的代码,然后继续开发
一段时间后,弹幕功能完成V1.1上线
弹幕功能上线之后,成绩不错,用户提了许多建议,公司决定增加人手,加大力度发展业务
于是,李四被公司选中,跟张三成为了同事
公司认具考思了用尸的建议以后,决定上一个大版本V2,先把搜索引擎、影片后台管理系统做起来
今天是6月1号,给到的目标节点是:6月10号就要上线
张三跟李四商量了一下,决定自己做搜索引擎,让李四做影片后台管理
上面说了这么多,是想表达,无论是个人还是团队,在目标时间节点一致的情况下,即使你不知道分支是什么,也不会影响开发工作
1.2 单分支遇到的麻烦
时间回到第一个修复版本上线的时候,也就是李四入职之前……
弹幕功能上线之后,成绩不错,用户提了许多建议,公司决定增加人手,加大力度发展业务
于是,李四被公司选中,跟张三成为了同事
公司认具考思了用尸的建议以后,决定上一个大版本V2,先把搜索引擎、影片后台管理系统做起来
- 搜索引擎6月10号就要上线(v2.0)
- 影片管理后台可以晚儿天,6月15号上线(v2.1)
张三跟李四商量了一下,决定自己做搜索引擎,让李四做影片后台管理
在张三开发完成之前,李四不可以提交代码。不然搜索引擎上线的时候,代码里还包含看为开发完的影片管理功能。
这样的话,李四电脑的安全性就特别重要。万一出了什么事,导致李四的电脑出了问题,那代码可能就丢失了。
另一方面,公司也不方使跟进李四的工作进度。因为李四的代码在目己电脑上,只有他目己可以看到。
李四当然可以等张三开发完成之后再提交,但是如果李四不是一个人在开发呢?如果是李四带看一个小弟一起开发影片管理,会发生什么?
- 李四负责影片管理1开发工作
- 李四小弟负责影片管理2开发工作
李四小弟此时需要调用李四的接口,那李四的小弟如何能得到李四的代码呢?只能通过版本控制服务吧。但是李四此时又不能提交代码,因为张三的开发工作还没有完成。
李四团队的开发工作被阴塞,因为李四团队之间的代码没法共享。
1.3 用分支解决问题
我们都知道,版本控制服务的作用是管理代码的不同版本。到目前为止,我们都是在一条线上进行开发。然后我们发现一条线满足不了某些需求:提交代码受限、团队开发阻塞、历史版本bug修复不便。
我们先想一下,怎样解决这些问题呢?如果版本控制系统可以支持多条线的话……
这样的话,多人、多版本并行开发的问题就迎刃而解了
考虑一下上边遇到这个问题
![]()
v1-bug是基于v1开发的,落后于版本库中的版本,因此不能提交到版本库。因此,需要先把版本库中的【弹幕功能(30%)】合并到v1-bug中。但是,如果合并了的话,就包含了未开发完成的代码,不能上线。于是,只能在合并之前,在本地打包,交给运维发布到线上。
这样发布是有问题。接下来,需要思考的是:如何方便快捷的修复历史版本的线上问题呢?有了分支以后,我们可以重新思考一下开发流程。
回顾一下张三的工作
v1.0.0用户中心、视屏播放v1.0.0上线v1.1.0弹幕功能30%v1.0.0遇到线上bug,需要及时修复
1.3.1 创建dev分支的时机
一般而言,只要是新功能的开发,都会新建一个 dev 分支
dev分支的数量,会随着版本的增多而变多,难道要一直这么增长下去吗?当某些历史版本没有必要维护的情况下,可以删掉。这就取决于公司的业务需要了。
比如说公司研发了v1.0.0,卖的很好,接下来公司研发了v1.2.0。如果公司觉得v1.0.0没有必要维护了,那么好,可以删除v1.0.x分支。当客尸发现v1.0.0有问题的时候,公司会直接部署v1.2.0。如果公司觉得v1.0.0还有维护的必要,因为v1.2.0的研发投入了很多资源,v1.2.0里面的功能,需要客户购买。当客户发现v1.0.0有问题的时候,公司会在v1.0.x分支上进行修复,然后给客尸部署修复版本 v1.0.1
1.3.2 master分支的作用和定位
这里有必要来说一下 master的作用和定位。一般而言,master分支,会有如下两种情况:
- 有些公司上线的时候,是直接发布
master上的代码,开发人员没有操作master分支的权限,开发人员的代码能否合并到master,是测试人员决定的。如果测试人员测试之后觉得满足需求,并且功能稳定,就会合到master上去(如果产生冲突,会交给开发人员解决。 - 有些公司上线的时候,不是直接发布
master上的代码(下面会介绍这种情况)。master的定位是内部最稳定、次新(因为最新的代码是在dev上)的可构建版本,所有历史版本的bug修复都会合并到master,当前开发中的dev最小闭环走通之后,就可以合并到master,这个分支的作用就是让领导在第一时间,可以看到目前项目的最稳定、最新的功能。
1.4 聊聊release分支
开发过程中,会存在各种各样的配置,这些配置
-
有些是不区分环境的,在开发、测试、线上环境都是一样的
-
有些是区分环境的,在开发、测试、线上环境可能不一样
对于需要区分环境的配置,在更改环境之前,需要先修改对应的配置。在 Java开发中,可以在工程启动的时候,指定一个环境参数,程序会判断这个参数然后读取不同的配置文件(但是依然解决不了SNAPSHOT和正式版的问题)也就是说,每次上线之前、下个版本开始开发之前,都需要修改配置。所以,在实际开发过程中,流程可能是这样的:(下面以snapshot 代表非线上环境的配置)
这种繁琐、容易出错的体力劳动迫使工程师们思考:如何避免?如果避免不了,能否简化一点。之所以需要来回改动,是因为不同环境所需要的配置不一样(特别是线上环境)能不能做到只是在上线之前修改一次呢?改成线上配置以后,不影响开发分支。如果是这样的话,开发人员在平常的开发过程中,就不需要关心线上配置的问题只需要在每次上线之前,弄一下就行。这个可以交给测试去做,甚至可以做成目动化的。
1.4.1 tag
下面是改进后的方案:
变成这种结构之后,运维不再关注任何分支,只关注tag
简要步骤
- 每次上线之前,拉出一个新分支
- 这个新分支上只做一件事,就是把配置改成线上的
- 改成线上配置之后,打个tag。tag可以理解为特殊的分支,特殊之处就是不可以向前移动,也就是不可以提交内容
- 打完tag以后,把刚才新建的分支删掉,变成下面这样
当有多个版本就会变成下面这个样子
如果之前的内容你都理解了,那么这节的内容就很容易进行下去。这一节会从另外一个角度来看看版本控制服务的分支。之前的所有内容,我们都是以横向的线条来表示分支的,这种表示方式可以很容易看到分支的前进过程但是分支和分支之间的关系,却不是那么直观。
- 高版本的分支跟
master是双向的关系,也就是既可以推送,也可以拉取 - 低版本的分支跟
master是单向的关系,只可以推送,不可以拉取- 一旦
master上有比当前分支新的分支提交过代码,那么当前的分支就是低版本分支
- 一旦
之所以有这种规定,是因为
- 高版本分支推送的是自己新功能的代码,拉取的是低版本的功能和bug补丁
- 低版本分支如果拉取的话,就会融入高版本的功能,那么低版本的分支还是低版本分支吗?
说完分支之间的关系后,我们来考虑一下实际的场景
如果说,v2.0.0有很多功能需要开发,时间也比较紧,不过公司有多个开发团队都可以投入进去。这个时候多个团队虽然可以共用一个dev/2.0.0分支进行开发,但是团队间可能会互相影响,影响来自于两方面
- A团队的bug 可能会影响B团队的开发
- 开发过程中团队之间,代码冲突的次数可能会变多
所以一般而言,当团队规模比较大,甚至是跨团队协作的时候,为了减少团队之间的沟通协作成本一般会在dev分支的基础上,根据功能模块拉出多个分支,共多个开发小组/团队使用
事情发展到这一步呢,大家有没有发现一些不妥之处?dev的含义跟以前还是一样吗:
- 每个
dev分支都代表了一次完整的功能迭代 dev分支是可独立维护的分支。但是dev的字分支明显不可独立维护,因为字分支只是代表功能的一部分- 从生命周期来看,
dev分支也许是长期存在,但是dev分支的子分支呢?一般而言,当子分支的功能开发完毕以后,子分支就没有存在的必要了。
3.5.1 feature分支的创建时机及含义
我们把dev的子分支换一个名字,叫feature
变成这样之后,我们很容易看出,dev 代表完整功能的迭代
future 代表当前正在开发的功能
相比之前,概念更加清晰,也更容易反映当前项目的情况和开发状态
3.6 Git 分支管理实操
张三在本地创建了一个文件夹,关联到远程的仓库中。
~# mkdir zhang_san_local
~# cd zhang_san_local/
zhang_san_local# git init
然后,进行初始化,这里是配置了一下配置文件,就可以提交了。
zhang_san_local# echo "0.0.1-SNAPSHOT" > pom.xml
zhang_san_local# git push --set-upstream origin master
张三现在要开始开发第一个版本了v1.0.0:【用户中心】【视频播放】
新建一个分支并切换过去
zhang_san_local# git checkout -b dev/1.0.x
经过一段时间开发,【用户中心】开发完毕。
zhang_san_local# echo "用户中心(100%)" > user.file
继续开发【视频播放】,一段时间后同样开发完毕。
zhang_san_local# echo "视频播放(100%)" > play_video.file
推送到远程仓库
zhang_san_local# git add .
zhang_san_local# git commit -m "feat:用户中心、视频播放开发完毕"
zhang_san_local# git push origin dev/1.0.x
修改版本号
zhang_san_local# vim pom.xml
zhang_san_local# cat pom.xml
1.0.0-SNAPSHOT
zhang_san_local# git add .
zhang_san_local# git commit -m "workflow: 版本号变更"
zhang_san_local# git push --set-upstream origin dev/1.0.x
3.6.1 第一次上线
准备上线。
- 新建一个
release分支,该分支只需要在本地即可,无需推送到远端。 - 修改版本号为上线版。
- 在本地提交该分支
- 打一个
tag - 推送
tag到远程仓库 - 删除该
release分支
zhang_san_local# git checkout -b release\v1.0.0
zhang_san_local# vim pom.xml
zhang_san_local# git add .
zhang_san_local# git commit -m "workflow:修改版本号,准备上线"
zhang_san_local# git tag -a "release_v1.0.0" -m "release:v1.0.0 上线"
# 上线
zhang_san_local# git push origin release_v1.0.0
# 删除分支需要先切换到其他分支
zhang_san_local# git checkout master
zhang_san_local# git branch -D releasev1.0.0
然后我们需要把dev分支合并到master上。
zhang_san_local# git merge dev/1.0.x
3.6.2 V1.1.0 弹幕功能30%遇到V1.0.0线上bug
弹幕功能开发30%
zhang_san_local# git checkout -b dev/v1.1.x
zhang_san_local# echo "弹幕功能(30%)" > danmu.file
zhang_san_local# git add .
zhang_san_local# git commit -m "feat/弹幕功能(30%)"
我们此时需要切回V1.0.0。修改版本号V1.0.1,修复bug
zhang_san_local# git checkout dev/v1.0.x
zhang_san_local# vim pom.xml
zhang_san_local# echo "bug-fix" > bug1-fix.file
将修改后的V1.0.1推到远端
zhang_san_local# git add .
zhang_san_local# git commit -m "bug-fix"
zhang_san_local# git push
上线V1.0.1
zhang_san_local# git commit -am "workflow/修该上线配置"
zhang_san_local# git push --set-upstream origin release/v1.0.1
zhang_san_local# git tag -a release_v1.0.1 -m "fix bug"
zhang_san_local# git push origin release/v1.0.1
切换分支,删除 release/v1.0.1
zhang_san_local# git checkout master
zhang_san_local# git branch -D release/v1.0.1
将修复bug后的V1.0.1合并到master
zhang_san_local# git merge dev/v1.0.x
zhang_san_local# git push
3.6.3 继续开发弹幕功能
切换到V1.1.x分支,继续开发
zhang_san_local# git branch dev/v1.1.x
zhang_san_local# echo "弹幕功能(100%)" > danmu.file
zhang_san_local# git commit -am "feat/弹幕功能(100%)"
zhang_san_local# git push --set-upstream origin dev/v1.1.x
上线
zhang_san_local# git checkout -b release/v1.1.0
zhang_san_local# vim pom.xml
zhang_san_local# git commit -am "wordflow/修改上线配置"
zhang_san_local# git tag -a release_v1.1.0 -m "feat/弹幕功能"
zhang_san_local# git push origin release_v1.1.0
上线后的常规操作
合并到master,删除release
zhang_san_local# git checkout master
zhang_san_local# git merge dev/v1.1.x
zhang_san_local# git branch -D release/v1.1.0
zhang_san_local# git push
3.6.4 开发2.0版本
创建dev/v2.0.x分支
zhang_san_local# git checkout -b dev/v2.0.x
创建future/v2.0.0_search分支
zhang_san_local# git branch future/v2.0.0_search
创建future/v2.0.0_vip分支
zhang_san_local# git branch future/v2.0.0_vip
接下来切换到搜索引擎分支,创建搜索引擎的两个开发分支future/v2.0.0_search1、future/v2.0.0_search2
zhang_san_local# git checkout future/v2.0.0_search
zhang_san_local# git branch future/v2.0.0_search1
zhang_san_local# git branch future/v2.0.0_search2
这样张三和李四就可以基于future/v2.0.0_search,开发搜引擎。开发完成之后,合并回dev分支,走上线流程。