关于git-flow的思考

我们有自己的一套git代码分支管理规则,在实际生产环境的运用中似乎也没发现什么问题,但最近看了下git-flow的代码分支管理规则,发现是时候要改进下我们的分支管理流程了。

git-flow是Vincent Driessen提出的,这套代码管理规则,他在项目中运用的很好,于是就把它公开出来。发布后不久就火遍全网,一时间成为各个大大小小公司的标准规范。这是在2010年推出的,2020年的时候他又更新了他的博客,说是随着软件行业的发展,各种敏捷开发标准层出不穷,这套管理规范可能并不适合现在的快节奏开发了。其实我觉得作者是谦虚了,这套流程放到现在照样很适合。

接下来我会分4个部分来讲解,首先介绍下我现在使用的流程,接着再分析需要改进的点,然后再详细介绍git-flow的核心思想,最后结合git-flow的流程对我们现有的流程做改进。

我使用的流程

先说下整体的思路,release和dev是永久分支,不会删除。ota、ota-xxx、bugfix是版本迭代过程中的分支,其中:

(1)release是最新的主分支,只会从dev合入,不做修改。

(2)dev是基于最新的release分支创建的,只从ota和bugfix分支合入,不能直接修改。

(3)ota是下一个版本要发布的新特性,它是基于最新的dev创建的,存在于远端和本地。

(4)ota-xxx是小组成员基于ota拉取的分支,比如ota-david、ota-bob等,本应该可以不用这个分支并且即使创建了也本应该只存在于本地。

(5)bugfix分支是基于最新的dev创建的,用于修改线上问题,修改好以后会在下一个版本前的某个节点发布。

乍一看没啥问题,但其实上面流程有很多不合理的地方。

现有流程需要改进的点

首先是release分支,这个分支刚开始有点用,毕竟由它创建了dev,但之后的版本迭代过程中,这个分支的作用似乎只是为了保存最新的发布版本。那要它有何用?因为dev已经保存了最新的版本。

每次开发下一个版本的新特性,其实只要一个ota分支即可,ota-xxx可有可无,即使创建了也不应该把这个分支push到线上,这是完全没必要的。

软件从上线到现在有一年多时间了,因为有些用户不喜欢升级,所以线上的版本会有很多,如果某一个用户报了一个问题,也有版本号,我们需要复现的话就只能通过AndroidStudio自带的git图形界面找到当时发布的时间点,然后checkout过去来复现,这是很麻烦的一件事。其实只需要在dev或者release打上tag即可。

git-flow的核心思想

先总体概述下各分支的含义。

master和develop是永久分支,hotfix是线上版本的热修复分支,release是预发布的分支,feature是开发新需求的分支。上面图大家可以花点时间看几眼,有个大概得印象。

再详细的解释下各分支的作用以及创建时机。

(1)主分支,该分支只做代码合入,禁止修改。

(2)当有新的代码合入时,立即打个tag,tag一般用即将要发布版本的版本号来命名。

(1)第一时间从master分支创建出来,跟master一样,只支持合入,禁止修改。

(2)用于存放不稳定的版本,比如持续集成到测试环境的版本。

(3)需要开发任何功能的时候,从 develop 创建出新的 feature branch,开发完成后合并回 develop(合并的时候使用 –no-ff),然后删掉 feature branch。

(4)当下一正式版本需要的所有功能开发完成之后,从 develop 上创建新的 release branch,并在 release branch 合并到 master 后合并回 develop(合并的时候用 –no-ff),然后删掉 release branch。这里一定要非常严格,确定当前develop分支的状态能够完全覆盖下一版本的需求。

release

(1)每次下一版本的功能开发完毕后,从 develop 上创建。

(2)创建完成后,更新版本号,然后单独做一个新的 commit。

(3)如果有bug要修复,直接在release分支上修改,注意只修改bug,不新增需求。

(4)该分支作为预发布分支,比如我们软件正式交付给客户之前大概有两周时间会招募测试用户,这段时间用户测试出来的问题都在这个分支上修改。

(5)当版本稳定后,合并到 master 和 develop(使用 –no-ff),然后被删掉。

创建release分支:

$ git checkout -b release-1.2 develop

完成release分支并合入到master:

$ git checkout master
Switched to branch 'master'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2

合入到dev:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)

删除:

$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).

feature

(1)每次开发新功能是从 develop 创建。

(2)开发完成后确定要发布的版本才合并到 develop(使用 –no-ff),然后被删掉。

创建feature分支:

$ git checkout -b myfeature develop
Switched to a new branch "myfeature"

合并feature分支到master:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop

合并feature分支到dev:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)

最后删除:

$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).

hotfix

已正式发布的产品发现 bug,直接从 master 或者出问题的 tag 上创建 hotfix branch,进行紧急修复,修复完成后合并到 master 和 develop(或 release branch 如果有的话)(使用 –no-ff),然后被删掉。

创建hotfix:

git checkout -b hotfix-1.2.1 master

完成hotfix并合入到master:

$ git checkout master
Switched to branch 'master'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2.1

合入到dev:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)

删除:

$ git branch -d hotfix-1.2.1

这套流程能涵盖日常项目开发过程中所有的场景,整个过程看似复杂,认真对照着图看几遍基本就能记住大概,其实就是各个分支要职责明确,什么时候要创建分支,什么时候要合入,这些搞明白了,那整个git-flow就会烂熟于胸。当然上面列出的可能还不够详细,大家可以直接看作者的原文

现有流程改进

再回过头看看我自己的流程,结合git-flow来改进下。

首先项目中master分支保留,作为项目的产品发布分支,该分支记录线上最新的代码,每次发版要新增tag记录,方便出问题后快速定位到版本后排查问题。

然后是dev分支保留,合入策略同git-flow,只从ota分支和bugfix分支合入。

然后是release分支,该分支策略同git-flow,当下一版本的新特性开发完成(必须要非常严格对待)并合入到dev后,从dev创建出release分支来,比如下一版本是1.2版本,可以命名为release-1.2,该分支上能做的事情就是修改版本号,修改bug,做一些小的功能修改。原文中描述:Adding large new features here is strictly prohibited. They must be merged into develop, and therefore, wait for the next big release.

然后是bugfix分支,线上版本发现问题后,根据问题发生的版本号,定位到问题,然后从master分支对应的tag上创建一个bugfix分支,修改bug并验证通过后合入到master和dev,但是也有例外情况:

The one exception to the rule here is that, when a release branch currently exists, the hotfix changes need to be merged into that release branch, instead of develop. Back-merging the bugfix into the release branch will eventually result in the bugfix being merged into develop too, when the release branch is finished. (If work in develop immediately requires this bugfix and cannot wait for the release branch to be finished, you may safely merge the bugfix into develop now already as well.)

意思是如果此时有release分支了,就只需要合入到release分支即可,但如果dev分支急需要这个bug修复否则工作就没法进展了,就要小心的将bugfix合入到dev分支。

最后是ota分支,小组成员如果多人在开发新版本需求时,可以建立个人分支如ota-david,但是个人分支仅存在于本地,不要push到线上,并且新特性开发完成后要及时删除,生命周期同ota分支保持一致。

以上就是完整的git-flow流程的介绍和思考。多看不如多练,如果觉得上面流程自己熟悉了,并且能帮助到自己和自己的小组,那就赶紧在项目中用起来吧。