原文地址:http://nvie.com/posts/a-successful-git-branching-model/#why-git
在这篇文章中,我介绍了一年前我为我的一些项目(无论是在工作还是私人)介绍的开发模式,并且已经证明是非常成功的。现在我已经写了一段时间的意思,但是到现在为止,我从来没有真正找到过这样的时间。我不会谈论任何项目的细节,只是关于分支策略和发布管理。
有关专业人士和Git的利弊进行深入讨论比较集中的源代码控制系统,看到该 网页。那里有很多火焰战。作为开发人员,我喜欢Git在今天的所有其他工具之上。Git真的改变了开发人员认为合并和分支的方式。从经典的CVS / Subversion世界我来,合并/分支一直被认为是一个可怕的(“小心合并冲突,他们咬你!”)和你只做了一个时间。
但是使用Git,这些操作非常便宜和简单,它们被认为是日常工作流程的核心部分之一。例如,在CVS / Subversion 书籍中,分支和合并将在后面的章节(高级用户)中首先讨论,而在 每个 Git 书中,它已经在第3章(基础知识)中讨论过。
由于其简单性和重复性,分支和合并不再是令人害怕的事情。版本控制工具应该协助分支/合并比其他任何东西。
足够的工具,让我们来开发模型。我将在这里介绍的模式基本上不过是每个团队成员必须遵循的一套程序,以便进行管理软件开发过程。
我们使用的存储库设置与这个分支模型一起工作,就是采用中央的“真相”回购。请注意,这个回购只 被认为 是中央的(因为Git是DVCS,在技术层面上没有像中央回购这样的东西)。我们将把这个repo称为origin
,因为这个名字是所有Git用户都熟悉的。
每个开发商拉起并推动起源。但除了集中的推拉关系,每个开发人员也可以从其他同伴提取更改以形成子组。例如,在推动正在进行的工作origin
过早之前,这可能有助于与两个或更多开发人员在一个大的新功能上合作 。在上图中,有Alice和Bob,Alice和David以及Clair和David的子集。
在技术上,这意味着没有什么比Alice已经定义了一个bob
指向Bob的存储库的Git remote,反之亦然。
在核心上,开发模式受到现有模型的极大启发。中央回购拥有两个主要的分支,无限生命:
master
develop
每个Git用户应该熟悉该master
分支origin
。与master
分支并行,存在另一个分支develop
。
我们认为origin/master
是源代码HEAD
总是反映生产就绪状态的主要分支 。
我们认为origin/develop
是主要的分支,源代码 HEAD
总是反映出下一个版本的最新交付发展变化的状态。有人会称之为“整合分支”。这是每个自动夜间建造的地方。
当develop
分支中的源代码达到稳定点并准备释放时,所有这些更改都应该以master
某种方式合并,然后用发布号标记。将详细讨论如何做到这一点。
因此,每次将更改合并到一起时master
,根据定义,这是一个新的生产版本。我们往往对此非常严格,所以理论上,我们可以使用Git钩子脚本在每次提交时自动构建和推出我们的软件到我们的生产服务器 master
。
接下来的主要分支master
和develop
,我们的发展模式,采用了多种支持分支机构,以帮助并行开发团队成员之间,缓解功能跟踪,生产准备释放,并协助快速修复现场制作的问题。与主要分支机构不同,这些分支机构总是有限的使用寿命,因为这些分支机构最终将被淘汰。
我们可能使用的不同类型的分支是:
这些分支中的每一个都具有特定的目的,并且必须遵守严格的规则,哪些分支可以是其原始分支,哪些分支必须是其合并目标。我们将在一分钟内走过他们。
从技术的角度来看,这些分支绝不是“特殊的”。分支类型根据我们如何使用它们进行分类。他们当然是古老的Git分支。
develop
develop
master
,develop
,release-*
,或者hotfix-*
功能分支(或有时称为主题分支)用于为即将到来的或将来发布的未来版本开发新功能。在开始开发功能时,此功能将被纳入的目标版本在这一点上可能是未知的。功能部门的实质是,只要功能处于开发阶段,它就会存在,但最终将被并入develop
(以便将来的新功能添加到即将发布的版本中)或被丢弃(在失败的实验的情况下)。
特征分支通常仅存在于开发者资源中,而不存在于origin
。
在启动新功能时,从develop
分支机构分支。
$ git checkout -b myfeature development 切换到新分支“myfeature”
完成的功能可能会合并到develop
分支中,以将其添加到即将发布的版本中:
$ git checkout开发
切换到分支'开发'
$ git merge --no-ff myfeature
更新ea1b82a..05e9557
(更改摘要)
$ git branch -d myfeature
已删除分支myfeature(is 05e9557)。
$ git push origin develop
该--no-ff
标志导致合并总是创建一个新的提交对象,即使可以使用快进执行合并。这避免丢失关于特征分支的历史存在的信息,并将所有提交的组合在一起,将一起添加该功能。比较:
在后一种情况下,不可能从Git历史记录中看到哪些提交对象一起实现了一个功能 - 您必须手动读取所有日志消息。恢复整个功能(即一组提交)在后一种情况下是一个真正的头痛,而如果使用--no-ff
标志,则很容易完成 。
是的,它会创建一些(空)提交对象,但收益远远大于成本。
develop
develop
和 master
release-*
发布分支机构支持准备新的生产版本。他们允许我的最后一分钟点,并且穿过t。此外,它们还允许对版本进行小错误修复和准备元数据(版本号,构建日期等)。通过在发布分支上执行所有这些工作,该develop
分支将被清除以接收下一个大版本的功能。
分支新发布分支的关键时刻develop
是开发(几乎)反映了新版本的期望状态。至少所有针对要发布的功能的功能必须develop
在此时被合并到 一起。所有针对未来版本的功能可能不一定要等到释放分支分支出现之后。
正是在发行版分支的开始,即将发布的版本被分配一个版本号,而不是更早版本。直到那一刻,develop
分支反映了“下一个版本”的变化,但是不清楚这个“下一个版本”是否将最终变成0.3或1.0,直到发布分支启动。该决定是在发布分支开始时进行的,并由项目的版本号碰撞规则进行。
分支是从develop
分支创建的。例如,说1.1.5版本是目前的生产版本,我们有一个大的版本。develop
“下一个版本” 的状态已经准备就绪,我们已经决定这将成为1.2版本(而不是1.1.6或2.0版本)。所以我们分支,给发行分支一个反映新版本号的名字:
$ git checkout -b release-1.2开发
切换到新分支“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
分支)上应用错误修复。严禁在此添加大量新功能。它们必须被合并develop
,因此等待下一个大的版本。
当发布分支的状态准备好成为一个真正的版本时,需要执行一些操作。首先,发布分支被合并到master
(因为每个提交master
都是定义的新版本,请记住)。接下来,该提交master
必须被标记,以便将来参考这个历史版本。最后,发布分支上所做的更改需要合并回来develop
,以便将来的版本也包含这些错误修复。
Git的前两步:
$ git checkout master
切换到分支'master'
$ git merge --no-ff release-1.2
由递归合并。
(更改摘要)
$ git tag -a 1.2
该版本现已完成,并标记以备将来参考。
编辑:您也可以使用
-s
或-u
标记来加密地签名您的标签。
为了保持在发行版中所做的更改,我们需要将这些更改合并到一起develop
。在Git:
$ git checkout开发
切换到分支'开发'
$ git merge --no-ff release-1.2
由递归合并。
(变更摘要)
此步骤可能会导致合并冲突(甚至可能,因为我们更改了版本号)。如果是这样,请修复并提交。
现在我们已经完成了,发布分支可能会被删除,因为我们不再需要它了
$ git branch -d release-1.2
删除了分支release-1.2(ff452fe)。
master
develop
和 master
hotfix-*
Hotfix分支机构非常喜欢发布分支机构,因为它们也是为了准备新的生产版本,尽管是计划外的。它们是由于必须在现场制作版本的不良状态下立即采取行动。当生产版本中的关键错误必须立即解决时,修补程序分支可能会从标记生产版本的主分支上的相应标记中分支出来。
实质是团队成员(develop
分支机构)的工作可以继续,而另一个人正在准备快速的生产修复。
Hotfix分支是从master
分支创建的 。例如,说1.2版本是目前的生产版本,由于严重的错误而直播并导致麻烦。但是变化develop
还不稳定。然后我们可以分支一个修补程序分支并开始解决问题:
$ git checkout -b hotfix-1.2.1 master
切换到新的分支“hotfix-1.2.1”
$ ./bump-version.sh 1.2.1
文件修改成功,版本碰到1.2.1。
$ git commit -a -m “Bumped version number to 1.2.1”
[hotfix-1.2.1 41e61bb]破解版本号为1.2.1
1个文件已更改,1个插入(+),1个删除( - )
分支后不要忘记颠覆版本号!
然后,修复错误并将修复提交到一个或多个单独的提交中。
$ git commit -m “修复严重的生产问题”
[hotfix-1.2.1 abbe5d6]修复严重的生产问题
5个文件已更改,32个插入(+),17个删除( - )
完成修补程序分支
完成后,该bugfix需要被合并回来master
,但也需要被合并回来develop
,以保证bugfix也包括在下一个版本中。这完全类似于发布分支的完成。
首先,更新master
和标记版本。
$ git checkout master
切换到分支'master'
$ git merge --no-ff hotfix-1.2.1
通过递归进行合并。
(更改摘要)
$ git tag -a 1.2.1
编辑:您也可以使用-s
或-u
标记来加密地签名您的标签。
接下来,还包括bugfix develop
:
$ git checkout开发
切换到分支'开发'
$ git merge --no-ff hotfix-1.2.1
由递归合并。
(变更摘要)
这里规则的一个例外是, 当发行版分支当前存在时,修补程序更改需要合并到该发行版分支中,而不是develop
。将修补程序反向合并到发行版分支中将最终导致在develop
发行版分支完成时将bugfix合并到一起。(如果工作develop
立即需要这个bug修复,不能等待发行版分支完成,您可以安全地将bugfix合并到develop
现在已经有了)
最后删除临时分支:
$ git branch -d hotfix-1.2.1 删除的分支hotfix-1.2.1(是abbe5d6)。
虽然这个分支模式没有什么真正令人震惊的新鲜事,但这篇文章开始的“大图”数字已经证明在我们的项目中非常有用。它形成了一个优雅的心理模型,易于理解,并允许团队成员共享对分支和释放过程的理解。