原文:A successful Git branching model

作者:Vincent Driessen,写于 2010 年 1 月 5 日。下文的第一人称指原作者。

插图来自原作者,未作修改;原文标注的图片许可为 CC BY-SA。

在这篇文章中,我将介绍一种开发模型。大约一年前,我把它用在工作和个人项目中,效果很好。这里不讨论具体项目,只讨论分支策略和发布管理。

Git 分支模型总览

为什么是 Git

Git 改变了开发者对分支和合并的看法。在过去使用 CVS、Subversion 时,创建分支和合并往往是需要谨慎对待、偶尔才做的事情。使用 Git 后,这些操作的成本很低,也成了日常开发的一部分。

接下来介绍的模型,本质上是一组团队共同遵守的流程,用来组织开发和版本发布。

分布式与中央仓库

Git 是分布式版本控制系统,从技术上讲,没有一个仓库天然就是中央仓库。这里的中央仓库是团队约定的代码汇集点,我们把它称为 origin。

每个开发者从 origin 拉取代码,并向它推送修改。开发者之间也可以直接交换修改。例如,两个人合作开发一个较大的功能时,可以先相互拉取代码,再把完成的工作推到 origin。

这种协作只需要添加一个指向对方仓库的远程仓库,并不需要特殊的分支类型。

中央仓库与开发者之间的协作

主要分支

中央仓库长期保留两个分支:

  • master:代码始终处于可发布的状态。
  • develop:汇集下一版本的开发内容,也可以称为集成分支。每日构建从这里产生。

当下一版本准备好发布时,将对应的修改合并到 master,并打上版本标签。在这个模型中,每次向 master 合入一个版本,都代表一次正式发布。

master 与 develop 主分支

辅助分支

除了两个长期分支,还会用到功能分支、发布分支和紧急修复分支。它们都有明确的用途,完成工作后就可以删除。

分支从哪里创建合并到哪里命名
功能分支developdevelop除保留名称外的其他名称
发布分支developmaster、developrelease-*
紧急修复分支master 或对应的发布标签master、develop;存在发布分支时见后文hotfix-*

这些都是普通的 Git 分支,区别在于团队约定的使用方式。整体分支关系见文首的流程图。

功能分支

功能分支用于开发新功能。开始开发时,这个功能最终进入哪个版本可能还没有确定。功能完成后合并回 develop;如果只是一次没有采用的尝试,也可以直接放弃。

功能分支从 develop 创建,也合并回 develop。名称不能使用 master、develop,或以 release-、hotfix- 开头。原作者的流程中,功能分支通常只保留在开发者自己的仓库。

功能分支的创建与合并

创建功能分支

git checkout -b myfeature develop

在这个分支上开发并提交代码。

合并功能分支

功能完成后:

git checkout develop
git merge --no-ff myfeature
git branch -d myfeature
git push origin develop

--no-ff 表示即使可以快进合并,也创建一个合并提交。这样能从历史中看出一个功能分支从哪里合入,哪些提交属于这次功能开发。

快进合并不会丢掉原来的提交,但不会额外留下这个合并节点。使用 --no-ff 会多出一个提交,换来更清楚的功能边界,也方便按整个功能处理回退。

快进合并与 no-ff 合并对比

发布分支

发布分支用来做正式发布前的准备,包括少量问题修复、版本号和构建信息调整。这些工作在发布分支上完成,develop 就可以继续接收下一版本的功能。

创建发布分支时,计划进入本次发布的功能应该已经合入 develop。准备留到后续版本的功能,要等发布分支切出以后再合入,避免被带进本次发布。

版本号也在创建发布分支时确定。此前 develop 只表示“下一版本”,并不一定已经决定它叫 1.2 还是 2.0。

创建发布分支

假设当前发布版本是 1.1.5,develop 已经准备好发布 1.2:

git checkout -b release-1.2 develop

然后修改项目中的版本号并提交:

./bump-version.sh 1.2
git commit -a -m "Bumped version number to 1.2"

bump-version.sh 是原文用来表示“修改版本号”的示例脚本,不是 Git 自带命令。实际操作时可以使用项目自己的脚本,也可以手动修改版本文件。

发布分支可能保留一段时间。在此期间,针对这次发布的问题在这个分支修复,新的大功能仍然进入 develop,等待下一次发布。

完成发布分支

准备发布时,先合并到 master,再创建版本标签:

git checkout master
git merge --no-ff release-1.2
git tag -a 1.2 -m "Release 1.2"

-a 创建附注标签;需要对标签做数字签名时,可以使用 -s,或用 -u <key> 指定签名密钥。

接着把发布期间的修改合并回 develop,保证后续版本也包含这些修复:

git checkout develop
git merge --no-ff release-1.2

这里可能出现冲突,例如两个分支都修改了版本号。解决冲突后,暂存解决后的文件并提交,再删除发布分支:

git branch -d release-1.2

紧急修复分支

紧急修复分支处理线上需要立即解决的问题。它和发布分支类似,但属于计划外的发布。

当线上版本出现严重问题,而 develop 还不稳定时,可以从 master 上对应线上版本的提交或标签创建修复分支。修复线上问题的同时,其他人仍然可以继续开发下一版本。

紧急修复分支的创建与合并

创建修复分支

假设线上运行的是 1.2,master 正好指向这个版本,需要发布 1.2.1:

git checkout -b hotfix-1.2.1 master
./bump-version.sh 1.2.1
git commit -a -m "Bumped version number to 1.2.1"

修复问题后,把修改的文件加入暂存区,再提交:

git commit -m "Fixed severe production problem"

完成修复分支

先合并到 master,并创建新的版本标签:

git checkout master
git merge --no-ff hotfix-1.2.1
git tag -a 1.2.1 -m "Release 1.2.1"

然后把修复合并回 develop:

git checkout develop
git merge --no-ff hotfix-1.2.1

这里有一种例外:如果已经存在一个发布分支,修复应该合入这个发布分支,而不只是合回 develop。 等发布分支完成时,修复也会随之进入 develop。如果 develop 上的开发马上需要这次修复,也可以提前合入。

确认需要的分支都已包含修复后,删除临时分支:

git branch -d hotfix-1.2.1

以上合并、标签和分支删除都是本地操作。要把发布结果同步到中央仓库,还需要推送相应的分支和标签。例如,完成 1.2.1 修复并合回 develop 后:

git push origin master develop
git push origin 1.2.1

这套模型给不同阶段的工作划定了分支:功能开发进入功能分支,发布准备进入发布分支,线上问题进入紧急修复分支。团队成员对修改从哪里来、最后合到哪里有共同的约定,开发和发布就能并行进行。

最后修改:2016 年 09 月 03 日
如果觉得我的文章对你有用,请随意赞赏