0%

GitFlow 工作流

工作中会用到的 Git 分支模型。


闲言

学生生涯比较 low 。没用过分之管理。都是直接在 master 分之上简单粗爆。

毕业后进入一个公司,可能工作是运维。发现身边同事也没有分之管理的意识。也是在 master 上简单粗爆。

偶然机会好朋友跟我分享说他们是使用 GitFlow 分之管理的。带着改变自己开发习惯的心态了解了一波。

GitFlow 工作流定义了一个围绕项目发布的严格分支模型。虽然比功能分支工作流复杂几分,
但提供了用于一个健壮的用于管理大型项目的框架。

偌大的图觉得有些晕。别怕,下面来看下具体描述

GitFlow 工作流

在你开始阅读之前,请记住:流程应被视作为指导方针,而非“铁律”。我们只是想告诉你可能的做法。
因此,如果有必要的话,你可以组合使用不同的流程

工作方式

GitFlow 工作流仍然用中央仓库作为所有开发者的交互中心。和其它的工作流一样,开发者在本地工作并push分支到要中央仓库中。

分支介绍

master: 这个分支的代码是发布到生产的代码

develop: 这个分支的代码是预发布到生产的代码

release: 这个分支的代码是新版本发布到生产的代码

feature: 这个分支的代码是新需求开发的代码

hotfix: 这个分支的代码是紧急修复生产 bug 的代码

常用的分支约定

  • 用于新建发布分支的分支: develop
  • 用于合并的分支: master
  • 分支命名: release-* 或 release/*

历史分支

GitFlow 工作流使用2个分支来记录项目的历史。master 分支存储了正式发布的历史,
而 develop 分支作为功能的集成分支。这样也方便master分支上的所有提交分配一个版本号。

功能分支

每个新功能位于一个自己的分支,这样可以push到中央仓库以备份和协作。
但功能分支不是从master分支上拉出新分支,而是使用develop分支作为父分支。
当新功能完成时,合并回develop分支。新功能提交应该从不直接与master分支交互。

注意,从各种含义和目的上来看,功能分支加上develop分支就是功能分支工作流的用法。但Gitflow工作流没有在这里止步。

发布分支

一旦 develop 分支上有了做一次发布(或者说快到了既定的发布日)的足够功能,就从develop分支上fork一个发布分支。
新建的分支用于开始发布循环,所以从这个时间点开始之后新的功能不能再加到这个分支上 —— 这个分支只应该做Bug修复、文档生成和其它面向发布任务。
一旦对外发布的工作都完成了,发布分支合并到master分支并分配一个版本号打好Tag(所有在 master 分支上的 commit应该 Tag)。
另外,这些从新建发布分支以来的做的修改要合并回develop分支。

使用一个用于发布准备的专门分支,使得一个团队可以在完善当前的发布版本的同时,另一个团队可以继续开发下个版本的功能。
这也打造定义良好的开发阶段(比如,可以很轻松地说,『这周我们要做准备发布版本4.0』,并且在仓库的目录结构中可以实际看到)。

维护分支

维护分支或说是热修复(hotfix)分支用于生成快速给产品发布版本(production releases)打补丁,这是唯一可以直接从master分支fork出来的分支。修复完成,修改应该马上合并回master分支和develop分支(当前的发布分支),master分支应该用新的版本号打好Tag。

为Bug修复使用专门分支,让团队可以处理掉问题而不用打断其它工作或是等待下一个发布循环。你可以把维护分支想成是一个直接在master分支上处理的临时发布。

场景设想

下面列举一些可能你在工作中会经常面对的场景

  1. 组长分配新需求下来,安排下周上线(假设是 1227 号),你看看当前有没有下周版本的分支?有的话很简单,checkout 下周分支(feature_app1.1.0_1227)来开发就行,没有的话这时需要新建分支,从 develop 分支创建新的 feature 分支(feature_app1.1.0_1227),然后将对应的 pom.xml 版本号修改成 1.1.0-SNAPSHOT,注意命名,比如这里我用 feature 做前缀,你也可以自己设定一个规则。

  2. 开发完 feature_app1.1.0_1227 需求,移交了测试,很遗憾,测试出现了 n 个 bug,这时依旧在 feature_app1.1.0_1227 上修复 bug。

  3. 终于到了发版前一天,测试 MM 说 n 轮测试完了,没问题,拉上线版本,再做一次回归测试。这时,你就需要把 feature_app1.1.0_1227 分支合并到 develop 分支,然后从 develop 分支中创建新的分支 release_app1.1.0_1227,然后修改对应的版本号为 1.1.0-RELEASE。

  4. 到了发版日早上了,测试 MM 用了 release_app1.1.0_1227 版本测试了一番,又发现了一个 bug。别慌,只要不是生产的 bug,都好解决。这时你要在 release_app1.1.0_1227 修复 bug,切记不能在 feature_app1.1.0_1227 上修改,feature_app1.1.0_1227 分支已经没有多大作用了,只用来看代码提交记录。

  5. 安安全全的到了晚上,开始发版了,发完版突然发现了有异常,定位问题后发现是有一行代码写错了,跟组长确认后,在 release_app1.1.0_1227 分支上做了修改,重新打包后发版,验证了一段时间,没问题了。。。

  6. 发版总算完成了,这时,别忘记把 release_app1.1.0_1227 版本合并到 develop 和 master 分支。还有一点很重要的,把 develop 分支代码合并到 1227 以后的版本(如果已经有1227 以后的版本的话)。注意:这个步骤合并代码要谨慎,如果有别人的代码合并冲突比较大,需要找那个开发的同事一起合并代码。总算可以睡个好觉了。。。

  7. 告别了旧需求,迎来了新需求,接下来的需求开发就按上面的步骤走。。。

  8. 第二天,突然生产上一直报 NullPointerException,定位发现是一行代码没有判空导致的,三番确认,原来这个数据以前是不为空的,现在确实需要支持有些数据为空的,需要紧急修复这个 bug,和组长确认之后,从 master 分支上拉了一个 hotfix_app1.1.1_1228 分支代码,修复了 NullPointerException,打包后上线,验证没问题后,把 hotfix_app1.1.1_1228 分支合并到 develop 和 master 分支,并把 develop 分支合并到 1227 以后的版本。

示例

下面的示例演示本工作流如何用于管理单个发布循环。假设你已经创建了一个中央仓库。

1. 创建开发分支

第一步为master分支配套一个develop分支。简单来做可以本地创建一个空的develop分支,push到服务器上

1
2
git branch develop
git push -u origin develop

以后这个分支将会包含了项目的全部历史,而master分支将只包含了部分历史。其它开发者这时应该克隆中央仓库,建好develop分支的跟踪分支

1
2
git clone url
git checkout -b develop origin/develop

现在每个开发人员都有了这些历史分支的本地拷贝。

2. 工程师A和工程师B开始开发新功能

这个示例中,工程师A和工程师B开始各自的功能开发。他们需要为各自的功能创建相应的分支。新分支不是基于master分支,而是应该基于develop分支:

1
git checkout -b some-feature develop

他们用老套路添加提交到各自功能分支上:编辑、暂存、提交

1
2
3
git status
git add
git commit
3. 工程师A完成功能开发

添加了提交后,工程师A觉得她的功能OK了。

如果团队使用Pull Requests,这时候可以发起一个用于合并到develop分支。

否则她可以直接合并到她本地的develop分支后push到中央仓库。

1
2
3
4
5
git pull origin develop
git checkout develop
git merge some-feature
git push
git branch -d some-feature

git pull 命令在合并功能前确保develop分支是最新的。

注意,功能决不应该直接合并到master分支。

4. 工程师A开始准备发布

这个时候工程师B正在实现他的功能,工程师A开始准备她的第一个项目正式发布。

像功能开发一样,她用一个新的分支来做发布准备。这一步也确定了发布的版本号:

1
git checkout -b release-0.1 develop

这个分支是清理发布、执行所有测试、更新文档和其它为下个发布做准备操作的地方,像是一个专门用于改善发布的功能分支。

只要工程师A创建这个分支并push到中央仓库,这个发布就是功能冻结的。任何不在develop分支中的新功能都推到下个发布循环中。

5. 工程师A完成发布

一旦准备好了对外发布,工程师A合并修改到master分支和develop分支上,删除发布分支。合并回develop分支很重要,因为在发布分支中已经提交的更新需要在后面的新功能中也要是可用的。

另外,如果工程师A的团队要求Code Review,这是一个发起Pull Request的理想时机。

1
2
3
4
5
6
7
git checkout master
git merge release-0.1
git push
git checkout develop
git merge release-0.1
git push
git branch -d release-0.1

发布分支是作为功能开发(develop分支)和对外发布(master分支)间的缓冲。只要有合并到master分支,就应该打好Tag以方便跟踪。

1
2
git tag -a 0.1 -m "Initial public release" master
git push --tags

Git有提供各种勾子(hook),即仓库有事件发生时触发执行的脚本。可以配置一个勾子,在你push中央仓库的master分支时,自动构建好对外发布。

6. 最终用户发现Bug

对外发布后,工程师A回去和工程师B一起做下个发布的新功能开发,直到有最终用户开了一个Ticket抱怨当前版本的一个Bug。

为了处理Bug,工程师A(或工程师B)从master分支上拉出了一个维护分支,提交修改以解决问题,然后直接合并回master分支:

1
git checkout -b issue-#001 master

Fix the bug

1
2
3
git checkout master
git merge issue-#001
git push

就像发布分支,维护分支中新加这些重要修改需要包含到develop分支中,所以工程师A要执行一个合并操作。然后就可以安全地删除这个分支了:

1
2
3
4
git checkout develop
git merge issue-#001
git push
git branch -d issue-#001

总结

Git是一个不错的版本管理工具,但也仅仅是工具。记住,这里演示的工作流只是可能用法的例子,而不是在实际工作中使用Git不可违逆的条例。

所以不要畏惧按自己需要对工作流的用法做取舍。不变的目标就是让Git为你所用。

一个标准的 Git 分支模型,在不管工作上还是学习上,在需要分支管理的时候,回忆起有这么一个图,根据你的场景再应用进去,肯定会少走很多弯路。

最后我想了想,应该保持分之不删除。这样以后可以在 log 中和仓库中可以追溯历史。因原文有,所以我保留了,待我实践过这套流程后再回来修复


参考链接