这个模型是在2010年构思进去的,而当初距今已有10多年的历史,而Git自身才诞生不久。在那10年中,git-flow(本文介绍的分支模型)在许多软件团队中十分风行,以至于人们开始将其视为某种规范,但可怜的是,它也被当作教条或灵丹妙药。

在那10年中,Git自身就席卷了整个世界,并且与Git一起开发的最风行的软件类型正在越来越多地转向Web应用程序-至多在我的过滤泡中。Web应用程序通常是间断交付的,不会回滚,并且您不用反对晚期运行的软件的多个版本。

这不是我十年前写博客时想到的那种软件。如果您的团队正在继续交付软件,我建议您采纳更简略的工作流程(例如GitHub flow),而不是尝试将git-flow引入您的团队。

然而,如果您正在构建显式版本的软件,或者如果您须要在晚期支持软件的多个版本,那么git-flow依然可能像适宜您团队的人一样适宜您的团队最近10年。在这种状况下,请持续浏览。

总而言之,请始终记住万灵药不存在。思考您本人的状况。别厌恶 本人决定。

在这篇文章中,我介绍了大概一年前为我的一些我的项目(在工作中和在私人我的项目中)引入的开发模型,事实证明该模型十分胜利。我始终想写一阵子,然而直到现在,我还没有真正找到彻底这样做的工夫。我不会议论任何我的项目的细节,而只会议论分支策略和公布治理。

去中心化但中心化

咱们应用的并且与此分支模型配合良好的存储库设置是具备地方“实在”存储库的存储库设置。请留神,此 repo 仅 被视为 地方仓库(因为 Git 是 DVCS,因而在技术层面上没有地方仓库这样的货色)。咱们将此存储库称为origin,因为所有 Git 用户都相熟此名称。

每个开发人员pulls和pushes到名为origin的存储库。但除了中心化的推拉关系,每个开发者也可能会从其余同行那里fetch变动,造成子团队。

次要分支

要害地是,开发模型受到现有模型的极大启发。地方仓库领有两个具备有限生命周期的次要分支:

masterdevelop

每个Git的使用者都应该相熟origin的master分支。
与master分支平行,存在另一个分支,称为develop。

咱们认为origin/master是源代码HEAD始终反映生产就绪状态的次要分支 。

咱们认为origin/develop是主分支,其中的源代码 HEAD始终反映下一个版本最新交付的开发更改的状态。有些人将其称为“集成分支”。这是构建任何主动夜间构建的中央。

当develop分支中的源代码达到稳固点并筹备公布时,所有更改都应该合并到master 。

因而,每次将更改合并回 时master,依据定义,这是一个新的生产版本。咱们在这方面往往十分严格,因而实践上,咱们能够应用 Git 钩子脚本在每次提交 master。

反对分支

在次要分支master和旁边develop,咱们的开发模型应用各种反对分支来帮忙团队成员之间的并行开发、轻松跟踪性能、筹备生产版本并帮忙疾速修复实时生产问题。与次要分支不同,这些分支的生命周期总是无限的,因为它们最终将被删除。

咱们可能应用的不同类型的分支是:

Feature branchesRelease branchesHotfix branches

这些分支中的每一个都有特定的目标,并且在哪些分支能够是它们的原始分支以及哪些分支必须是它们的合并指标方面受到严格的规定束缚。

从技术角度来看,这些分支绝不是“非凡的”。分支类型依据咱们如何应用它们进行分类。它们当然是一般的Git 分支。

Feature branches
可能来自以下分支:
develop
必须合并到的分支:
develop
分支命名规定:
除了 master, develop, release-, 或hotfix-

性能分支(或有时称为主题分支)用于为行将公布或边远的将来版本开发新性能。当开始开发性能时,此时可能不晓得将合并该性能的指标版本。性能分支的实质在于,只有该性能处于开发阶段,它就存在,但最终会被合并回develop(明确将新性能增加到行将公布的版本中)或抛弃(如果试验令人悲观)。

性能分支通常只存在于开发者仓库中,而不存在于origin.

创立feature branches

当开始一个新个性的工作时,从这个develop分支创立一个新的分支。
Git命令

$ git checkout -b myfeature developSwitched to a new branch "myfeature"

将曾经开发的性能分支合并到develop分支中

能够将实现的featuren分支合并到develop分支中,以确保将它们增加到行将公布的版本中:

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

该--no-ff标记导致合并总是创立一个新的提交对象,即便合并能够用快进执行。这防止了失落无关性能分支的历史存在的信息,并将所有一起增加性能的提交组合在一起。两者比拟:

在后一种状况下,无奈从 Git 历史记录中看出哪些提交对象一起实现了某个性能——您必须手动读取所有日志音讯。复原整个个性(即一组提交),在后一种状况下是一个真正令人头疼的问题,而如果应用了--no-ff标记,这很容易做到 。

是的,它会创立更多(空)提交对象,但收益远大于老本。

Release branches
可能来自于
develop
必须合并到
develop and master

分支命名规定

release-*

Release branches反对筹备新的生产版本。它们容许在最初一刻打点 i 和穿插 t。此外,它们容许修复小谬误并为公布筹备元数据(版本号、构建日期等)。通过在公布分支上实现所有这些工作,该develop 分支被革除以接管下一个大版本的性能。

创立进去自于develop新Release branches,至多所有针对待构建版本的性能都必须合并 到develop。所有针对将来版本的性能可能都不是——它们必须等到Release branches分支创立后。

正是在release分支的开始,行将公布的版本被调配了一个版本号——而不是更早的版本。直到那一刻,develop 分支反映了“下一个版本”的变动,但不分明“下一个版本”最终会变成 0.3 还是 1.0,直到elease分支启动。该决定是在公布分支开始时做出的,并由我的项目对于版本号减少的规定执行。

创立release分支

release分支是从develop分支创立的。例如,假如版本 1.1.5 是以后的生产版本,咱们行将公布一个大版本。此时,develop分支已为"下一版本"做好筹备,这次发版将成为1.2版(而不是 1.1.6 或 2.0)。因而,创立一个release分支并给该分支命名一个反映新版本的名称

$ git checkout -b release-1.2 develop切换到新分支“release-1.2” $ ./bump-version.sh 1.2文件批改胜利,版本撞到1.2。$ git commit -a -m "Bumped version number to 1.2" [release-1.2 74d9424] Bumped version number to 1.2 1 个文件已更改,1 个插入(+),1 个删除(-)

在创立一个新分支并切换到它之后,咱们减少了版本号。这 bump-version.sh是一个虚构的 shell 脚本,它更改了工作正本中的一些文件以反映新版本。(这当然能够是手动更改——要害是某些文件产生了更改。)而后,提交了减少的版本号。

这个新分支可能会存在一段时间,直到正式公布。在此期间,可能会在此分支(而不是在develop分支上)谬误修复Bug,然而,禁止在此处增加大型新性能的代码。新的性能代码,必须合并到中develop分支,而后,期待下一个重要版本的公布。

实现一个release分支

当release分支的状态筹备好,能够筹备公布时,在筹备公布时,能够先执行一些操作。首先,将release分支合并到master分支(因为依据定义,每次提交到master的代码都是一个新版本)。接下来,必须将此次提交打上标记,以不便参考此历史版本。最初,在releasef分支提交的如bugfix的代码必须合并回develop分支,这样做将来的release分支也能够蕴含这些bug修复代码。
前两步对应git的操作

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

这个release分支工作曾经全副实现,并且曾经打上tag以便未来查看。

为了保留在release分支提交的代码,咱们须要将release分支合并到develop分支
对应的Git的操作

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

这步操作可能会导致合并抵触。如果产生了,解决抵触而后从新提交。
当初咱们真地做完所有的事件,此时,咱们不在须要release分支,对应的release分支能够被删除掉了。

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

hotfix 分支

此分支从master分支fork。此会合并回develop分支和master分支。
此分支命名的格局:
hotfix-*

热修复分支与公布分支十分类似,因为它们也意味着为一个新的生产版本做筹备,只管是计划外的。当必须立刻解决生产版本中的要害bug时,可能会从标记生产版本的主分支上的相应标记中拆散出一个热修复分支。
团队能够借用这个个性,一个人在develop分支上工作,另一个正在筹备一次生产的修复代码。

创立hotfix 分支

从master分支创立hotfix 分支。举例,假如1.2版本是以后正在运行的产品版本,并因为一个重大的bug而引起麻烦。然而处于开发中develop分支还未就绪。而后咱们能够创立一个hotfix 分支,并开始修复这个问题:

$ git checkout -b hotfix-1.2.1 masterSwitched to a new branch "hotfix-1.2.1"$ ./bump-version.sh 1.2.1Files modified successfully, version bumped to 1.2.1.$ git commit -a -m "Bumped version number to 1.2.1"[hotfix-1.2.1 41e61bb] Bumped version number to 1.2.11 files changed, 1 insertions(+), 1 deletions(-)

别忘记在hotfix分支名前面加版本号.而后在hotfix分支修复bug。

$ git commit -m "Fixed severe production problem"[hotfix-1.2.1 abbe5d6] Fixed severe production problem5 files changed, 32 insertions(+), 17 deletions(-)

实现hotfix分支

当在hotfix修复工作实现后,bug修复代码须要合并到master分支,同时也须要合并到develop分支,这样做的目标,是为了确保下一个版本中也蕴含bug修复代码,这跟release分支实现有些相似。
首先,将hotfix分支合并到master分支,并打上tag

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

接下来,将hotfix分支也合并到develop分支

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

这里有一个例外是,如果以后存在一个release分支时,须要将热修复代码合并到该release分支中,而不是开发。当release分支实现时,将bug修复程序反向合并release分支中最终将导致bug修复程序也合并到develop分支中。(如果在debelop的工作立刻须要修复这个bug,而不能期待release分支实现,那么您也能够平安地将这个bug修复合并到当初的develop分支中。)

最终,移除这个长期的分支

$ git branch -d hotfix-1.2.1Deleted branch hotfix-1.2.1 (was abbe5d6).

总结

尽管这一分支模型并没有什么陈腐的货色,但本文一开始提到的“大图”对咱们的我的项目十分有用。它造成了一个易于了解的优雅的心智模型,并容许团队成员开发对分支和公布过程的共享了解。