Git-flow:git 的分支管理策略
A successful Git branching model 是一篇有名的分支管理策略(Git-Flow)的学习,下面文章以这篇文章为基础,介绍 Git-Flow 工作流,顺便练一下英语。这篇不是简单的英文翻译,里面杂合了我的理解与外部参考。
参考:
前言
这篇文章诞生于 10 年前,那时 Git 刚诞生不久。在这 10 年里,许多团队都把它当成了“准则”和“灵丹妙药”。现在,越来越多的软件类型转向了 web 应用,而这些应用一般是持续交付的,而不是回滚式的,因此不需要支持多版本管理。如果软件开发团队正在做持续交付的软件,那么可以参考更简单的工作流(如 GitHub 流)而不是非得使用 git-flow。
当然了,学习前需要记住,世界上没有灵丹妙药。要考虑自己的情况选择自己的工作流。
为什么选择 Git
Git 改变了程序员对“合并/分支”的看法。以前(CVS/Subversion 时代),“合并/分支”有点可怕:小心合并冲突,不然它会咬你!但有了 Git,这些操作将变得成本低廉和简单。由于其简单和可重复性的特性,“合并/分支”不再可怕。
“去中心化”了但是还要“中心化”
因为 Git 是 DVCS(分布式管理系统),在技术层面上就没有诸如”中心化“仓库的东西。但是要让我们的模型工作得好,首先就得有一个“中心”的“真实”的仓库。这个中心化的仓库我们称为 origin
(这个名字对 Git 用户来说比较熟悉)
每一个开发人员都从 origin
中拉取和推送,与此同时成员之间也互相拉取形成子团队。这适用于 2 到 3 个开发人员在对一个软件的重要特性的进行开发的场景。
上图中,alice 和 bob、alice 和 david、david 和 clair 组成了子团队,这意味着 Alice 定义了一个远程 Git 仓库名为 bob,反之亦然。
主分支
中心仓库掌握两个主要的分支,它们有着无限的生命周期:
- master 分支:主要分支,
HEAD
总是指向一个“可以作为产品发布”的状态。 - develop 分支:主要分支,
HEAD
总是指向为下一个版本做准备的最新交付开发版本,它包含了一些针对下一个版本的更改。
当 develop 中的源代码趋向稳定且做好发布准备时,所有的改变都应以某种方法“合并”回 master,并打上版本标签。因此,每一次把所有改变合并回 master,根据定义,这将是一个全新的发行版本。对于这一点,我们应严格遵守。所以从理论上来讲,每一次 master 分支存在提交,我们编写的 Git Hook 脚本会自动构建并导出我们的软件产品到产品服务器。
辅助分支
次于 master 和 develop 两个主分支,我们的开发模型使用许多辅助分支以帮助团队成员之间的平行开发、简化特性的追踪、为版本发行做好准备以及对产品临时出现的问题进行修复。与主分支不同,这些分支总是有着有限的生存周期,它们最终将会被永久地移除。
我们可能会用到的不同辅助分支:
- 特性分支(Feature branches)
- 预发行分支(Release branches)
- 热修复分支(Hotfix branches)
这些分支有不同的目的,且各自有严格规则约束,这些规则规定了它们原分支和目的分支。从技术上来讲,这些分支并不十分“特殊”,它们都是一般的 Git 分支,只不过我们以“使用它的方式”进行分类罢了。
特性分支
可能从哪个分支分出来 | 必须合并回哪个分支去 | 习惯命名 |
---|---|---|
develop | develop | 除了叫 master、develop、release-* 或 hotfix-*,其他都可以 |
特性分支(有时也称主题分支)是用来开发即将到来或远期发行版本新特性的分支。当我们开始开发一个特性时,包含这个特性的发行版本是未知的。特性分支的本质是,只要这个特性还在开发中,它就一直存在,但最终会合并回 develop(这就决定了新特性将加入即将发行的版本之中)或丢弃(以防最终的结果令人失望)。
特性分支一般只存在于开发中的仓库,而不在 origin
。
创建特性分支
当我们开始在新特性上进行开发时,我们从 develop 中分出新分支
1 | git checkout -b myfeature develop |
在 develop 分支中合并完成的特性
开发完成的特性将合并到 develop 分支中,这就决定了即将发行的版本将包含这个特性。
1 | # 合并新特性 |
--no-ff
参数解释:
默认情况下,Git 执行 " 快进式合并 "(fast-farward merge,参考 fast_forward),会直接将 Master 分支指向 Develop 分支。使用 --no-ff
参数后,会执行正常合并,在 Master 分支上生成一个新节点。为了保证版本演进的清晰,我们希望采用这种做法(即使这样做会产生空的 commit 结点,但利大于弊)。
预发行分支
可能从哪个分支分出来 | 必须合并回哪个分支去 | 习惯命名 |
---|---|---|
develop | develop,master | release-* |
预发布分支,它是指发布正式版本之前(即合并到 Master 分支之前),我们可能需要有一个预发布的版本进行测试。
预发行分支辅助新产品发行的准备工作,They allow for last-minute dotting of i’s and crossing t’s. 此外,它允许小错误的修复且为一个发行版本准备元数据(版本号、构建日期等)。通过所有在预发行分支所做的工作,develop 分支将被清除以接收下一个大版本的发行。
从 develop 分支分叉处新的预发行分支的时刻,就是 develop(几乎)反应新发行版本的理想状态的时刻,至少此时所有指向正在构建的发行版本的特性已经合并到 develop 中(但是指向未来发行版本的特性也许还没有合并进去,它们必须等到那个版本被分出来后,才考虑进行合并)。
正是在预发行分支存在开始,预发布的版本将被指定一个版本号(不会更早),一直到 develop 分支反映下一个发行版本变动的那一刻。但是,预发行版本的版本号最终是 0.3 还是 1.0 我们是不清楚的。版本号的决定是从预发行分支开始,并由项目的版本号制定规则决定。
创建一个预发行分支
预发行分支从 develop 分支分流出来的。例如,现在的产品版本为 1.1.5,我们将有一个大的发行版本出现,develop 分支也已经为下一个发行版本准备好了,而且我们决定这个版本将为 1.2(而不是 1.1.6 或 2.0)。所以我们分出一条预发行版本分支,其名字反映了新的版本号:
1 | git checkout -b release-1.2 develop # 切换到新分支 "release-1.2" |
在创建新分支并切换到它之后,我们提升了版本号。./bump-version.sh
在这里是一个虚构的 shell 脚本,他将改变当前工作中的文件(副本)以反映新的版本(当然,你可以自己手动更改,就是一些文件的更改)。最后,提升的版本号将被提交。
新的分支也许会存在一段时间,直到这个发行版被推出。在这段时间中,release 分支会有很多 bug 修复(而不是在 develop 分支中),但严格禁止在这里增加大的特性。大的特性应该合并到 develop 分支中,并等待下一个发行版本。
完成一个预发行分支
当预发行分支准备好发行时,我们必须进行一些操作。首先,预发行分支合并到 master 分支中(别忘了,master 中的每次提交代表的是一个新的发行版本)。其次,master 中的提交必须被贴上标签以便未来对这个历史版本的参考。最后,预发行分支中的改变需要合并回 develop,这样未来的发行版本中将会包含这些 bug 修复。
在 Git 中提交的步骤:
1 | git checkout master |
预发行已经完成,且进行了标签标注以便未来的参考。
为了保证预发行分支中的改变得到保留,我们需要将其合并到 develop 分支。在 Git 中这样做:
1 | git checkout develop # Switched to branch 'develop' |
这个步骤可能会导致合并冲突(或者说是必定冲突,因为我们改变了版本号),如果真的这样那就修复它然后提交。
现在我们真的要完事了,这个分支我们已经不需要了,把它删掉:
1 | git branch -d release-1.2 |
热修复分支
可能从哪个分支分出来 | 必须合并回哪个分支去 | 习惯命名 |
---|---|---|
master | develop,master | hotfix-* |
热修复分支非常像预发行分支,因为它也是为新产品发行做准备。它的出现是为了应对当前版本中出现的错误情况。当现行发行的产品出现一个紧急的 Bug,它必须立即修复,这时热修复分支会从 master 分支中对应版本的 tag 分流出来。
它的本质就是要在 develop 进行开发的工作团队继续它们的工作,要另一个人去准备好修复这个 Bug。
创建热修复分支
热修复分支可以从 master 分支中创建。例如,版本 1.2 是当前产品发行版本,然而出现了严重 Bug。但是 develop 分支中的改变还是不稳定的,我们需要分支出热修复分支并着手开始解决问题。
1 | git checkout -b hotfix-1.2.1 master # Switched to a new branch "hotfix-1.2.1" |
别忘了分支出热修复分支后提升版本号。
大约提交几次 commit 后,修复好 Bug。
1 | git commit -m "Fixed severe production problem" |
完成热修复分支
完成 Bug 修复后,需要把热修复分支合并到 master 中以及 develop 中,这是为了保证修复后的内容也被下一个版本包含。这就很像预发布分支完成后的操作。
首先,更新 master 并标注发行版本:
1 | git checkout master |
然后,把 Bug 的修复也合并到 develop 中:
1 | git checkout develop # Switched to branch 'develop' |
有一个例外规则是:当预发布分支存在时,热修复中的改变需要合并到这个预发布分支,而不是 develop。合并的预发布分支的修复,会在预发布分支完成后最终合并到 develop 中。当然,如果 develop 分支急需这个修复,你也可以安全的合并到 develop 中。
最后,移除热修复分支这个临时分支。
1 | git branch -d hotfix-1.2.1 |
总结
如果这个分支模型没有什么重大改变,下面这张图将对你的项目非常有用。
Author: Vincent Driessen
Original blog post: http://nvie.com/posts/a-succesful-git-branching-model
License: Creative Commons BY-SA