总览(一张流程图给大家先镇镇惊)
http://static.cyblogs.com/git分支总图概览.jpg
它主要体现了Git
对我们源代码版本的管理。
(转载者加)一般情况:
master
和develop
并行。master
上始终是最稳定的代码,develop
是正在开发的代码。feature
则是某个开发为了自己的功能拉的分支。不一般情况:develop
正在开发,如果你上线突然被拒绝了,这时候就要从master上开一个热分支,或者release
分支也行,改好之后在分别合并到其他分支。但,本人感觉release通常意味着终止。别在从release
上拉分支了。 对于Git
与其他集中式代码管理工具相比的优缺点的全面讨论,请参见这里。这样的争论总是喋喋不休。作为一个开发者,与现今的其他开发工具相比较,我更喜欢Git
。Git
真得改变了开发者对于合并和分支的思考。我曾经使用经典的CVS/Subversion
,然而每次的合并/分支和其他行为总让人担惊受怕(“小心合并里的冲突,简直要命!”)。但是对于Git
来说,这些行为非常简单和搞笑,它们被认为是日常工作中的核心部分。例如,在很多CVS/Subversion
书里,分支与合并总是在后面的章节中被讨论(对于高级用户使用),然而在每个Git
书中,在第3章就已经完全涵盖了(作为基础)。简单和重复的特性带来的结果是:分支与合并不再是什么可以害怕的东西。分支/合并被认为对于版本管理工具比其他功能更重要。关于工具,不再多说,让我们直接看开发模型吧。这个模型并不是如下模型:在管理软件开发进度方面,面对每个开发过程,每个队员必须按一定次序开发。
对于这种分支模型,我们设置了一个版本库,它运转良好,这是一个"事实上" 版本库。不过请注意,这个版本库只是被认为是中心版本库(因为Git
是一个分布式版本管理系统,从技术上来讲,并没有一个中心版本库)。我们将把这个版本库称为原始库,这个名字对所有的Git用户来说都很容易理解。
http://static.cyblogs.com/git分布式集中式.jpg
每个开发者都对origin
库拉代码和提交代码。但是除了集中式的存取代码关系,每个开发者也可以从子团队的其他队友那里获得代码版本变更。例如,对于2个或多个开发者一起完成的大版本变更,为了防止过早地向origin
库提交工作内容,这种机制就变得非常有用。在上述途中,有如下子团队:Alice
和Bob,Alice
和David
,Clair
和David
。从技术上将,这意味着,Alice
创建了一个Git
的远程节点,而对于Bob
,该节点指向了Bob
的版本库,反之亦然。
http://static.cyblogs.com/develop与master分支的关系.jpg
在核心部分,研发模型很大程度上靠其他现有模型支撑的。中心库有2个可一直延续的分支:
每个Git
用户都要熟悉原始的master
分支。与master分支并行的另一个分支,我们称之为develop
分支。我们把原始库/master
库认作为主分支,HEAD的源代码存在于此版本中,并且随时都是一个预备生产状态。
我们把origin/develop
库认为是主分支,该分支HEAD
源码始终体现下个发布版的最新软件变更。有人称这个为“集成分支”,而这是每晚自动构建得来的。当develop
分支的源码到达了一个稳定状态待发布,所有的代码变更需要以某种方式合并到master分支,然后标记一个版本号。如何操作将在稍后详细介绍。所以,每次变更都合并到了master
,这就是新产品的定义。在这一点,我们倾向于严格执行这一点,从而,理论上,每当对master
有一个提交操作,我们就可以使用Git
钩子脚本来自动构建并且发布软件到生产服务器。
我们的开发模型使用了各种辅助性分支,这些分支与关键分支(master和develop)一起,用来支持团队成员们并行开发,使得易于追踪功能,协助生产发布环境准备,以及快速修复实时在线问题。与关键分支不同,这些分支总是有一个有限的生命期,因为他们最终会被移除。我们用到的分支类型包括:
每一种分支有一个特定目的,并且受限于严格到规则,比如:可以用哪些分支作为源分支,哪些分支能作为合并目标。我们马上将进行演练。从技术角度来看,这些分支绝不是特殊分支。分支的类型基于我们使用的方法来进行分类。它们理所当然是普通的Git分支。
http://static.cyblogs.com/20181126103335995_580-0.jpg
可能是develop
分支的分支版本,最终必须合并到develop
分支中。分支命名规则:除了master
、develop
、release-
、hotfix-
之外,其他命名均可。功能分支(有时被称为topic
分支)通常为即将发布或者未来发布版开发新的功能。当新功能开始研发,包含该功能的发布版本在这个还是无法确定发布时间的。功能版本的实质是只要这个功能处于开发状态它就会存在,但是最终会或合并到develop
分支(确定将新功能添加到不久的发布版中)或取消(譬如一次令人失望的测试)。功能分支通常存在于开发者的软件库,而不是在源代码库中。创建一个功能分支开始一项功能的开发工作时,基于develop
创建分支。
$ git checkout -b myfeature develop
Switched to a new branch "myfeature"
合并一个功能到develop分支完成的功能可以合并进develop分支,以明确加入到未来的发布:
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop
--no-ff
标志导致合并操作创建一个新commit
对象,即使该合并操作可以fast-forward
。这避免了丢失这个功能分支存在的历史信息,将该功能的所有提交组合在一起。比较:
http://static.cyblogs.com/t_70_580-0.jpg
后一种情况,不可能从Git
历史中看到哪些提交一起实现了一个功能——你必须手工阅读全部的日志信息。如果对整个功能进行回退 (比如一组提交),后一种方式会是一种真正头痛的问题,而使用--no-ff flag
的情况则很容易. 是的,它会创建一个新的(空)提交对象,但是收益远大于开销。不幸的是,我还没找到一种方法,让--no-ff时作为合并操作的默认选项,但它应该是可行的。
Release
分支可能从develop
分支分离而来,但是一定要合并到develop
和master分支上,它的习惯命名方式为:release-*
。Release
分支是为新产品的发布做准备的。它允许我们在最后时刻做一些细小的修改。他们允许小bugs的修改和准备发布元数据(版本号,开发时间等等)。当在Release
分支完成这些所有工作以后,对于下一次打的发布,develop分支接收features
会更加明确。从develop
分支创建新的Release
分支的关键时刻是develop
分支达到了发布的理想状态。至少所有这次要发布的features
必须在这个点及时合并到develop分支。对于所有未来准备发布的features
必须等到Release分支创建以后再合并。在Release
分支创建的时候要为即将发行版本分配一个版本号,一点都不早。直到那时,develop
分支反映的变化都是为了下一个发行版,但是在Release
分支创建之前,下一个发行版到底叫0.3
还是1.0
是不明确的。这个决定是在Release
分支创建时根据项目在版本号上的规则制定的。
创建一个release分支Release
分支是从develop
分支创建的。例如,当前产品的发行版本号为1.1.5
,同事我们有一个大的版本即将发行。develop
分支已经为下次发行做好了准备,我们得决定下一个版本是1.2
(而不是1.1.6
或者2.0
)。所以我们将Release
分支分离出来,给一个能够反映新版本号的分支名。
$ git checkout -b release-1.2 develop
Switched to a new branch "release-1.2"
$ ./bump-version.sh 1.2
Files modified successfully, version bumped to 1.2.
$ git commit -a -m "Bumped version number to 1.2"
[release-1.2 74d9424] Bumped version number to 1.2
1 files changed, 1 insertions(+), 1 deletions(-)
创建新分支以后,切换到该分支,添加版本号。这里,bump-version.sh
是一个虚构的shell
脚本,它可以复制一些文件来反映新的版本(这当然可以手动改变--目的就是修改一些文件)。然后版本号被提交。这个新分支可能会存在一段时间,直到该发行版到达它的预定目标。在此期间,bug的修复可能被提交到该分支上(而不是提交到develop
分支上)。在这里严格禁止增加大的新features
。他们必须合并到develop
分支上,然后等待下一次大的发行版。
完成一个release分支当一个release
分支准备好成为一个真正的发行版的时候,有一些工作必须完成。首先,release分支要合并到master
上(因为每一次提交到master
上的都是一个新定义的发行版,记住)。然后,提交到master
上必须打一个标签,以便以后更加方便的引用这个历史版本。最后,在release
分支上的修改必须合并到develop分支上,以便未来发行版也包含这些bugs
的修复。在Git
中的前两步是:
$ git checkout master
Switched to branch 'master'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2
发行版现在已经完成,为以后引用打上标签。**编辑:**你可能也想使用the-sor-u <key>flags
来标记你的标签。为了是修改保持在release
分支上,我们需要合并这些到develop
分支上去,在Git
上:
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff release-1.2
Merge made by recursive.
(Summary of changes) 这个步骤可能会导致合并冲突(可能由于改变版本号更是如此)。如果是这样,修复它然后提交。现在我们真正的完成了,这个release
分支将被删除,因为我们不再需要它了。
$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).
http://static.cyblogs.com/t_70_580-1.jpg
可以基于master
分支,必须合并回develop
和master
分支。分支名约定:hotfix-*
热修复分支与发布分支很相似,他们都为新的生成环境发布做准备,尽管这是未经计划的。他们来自生产环境的处于异常状态压力。当生成环境验证缺陷必须马上修复是,热修复分支可以基于master
分支上对应与线上版本的tag
创建。其本质是团队成员(在develop
分支上)的工作可以继续,而另一个人准备生产环境的快速修复。创建修补bug分支hotfix branch
(修补bug分支)是从Master
分支上面分出来的。例如,1.2版本是当前生产环境的版本并且有bug
。但是开发分支(develop
)变化还不稳定。我们需要分出来一个修补bug
分支(hotfix branch
)来解决这种情况。
$ git checkout -b hotfix-1.2.1 master
Switched to a new branch "hotfix-1.2.1"
$ ./bump-version.sh 1.2.1
Files 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.1
1 files changed, 1 insertions(+), 1 deletions(-)
分支关闭的时侯不要忘了更新版本号(bump the version
) 然后,修复bug
,一次提交或者多次分开提交。
$ git commit -m "Fixed severe production problem"
[hotfix-1.2.1 abbe5d6] Fixed severe production problem
5 files changed, 32 insertions(+), 17 deletions(-)
完成一个hotfix分支完成一个bugfix
之后,需要把butfix
合并到master
和develop分支去,这样就可以保证修复的这个bug
也包含到下一个发行版中。这一点和完成release
分支很相似。首先,更新master
并对release
打上tag
:
$ git checkout master
Switched to branch 'master'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes)
$ git tag -a 1.2.1
编辑:你可能也会想使用 -sor-u <key>
参数来对你的tag进行加密 下一步,把bugfix
添加到develop
分支中:
$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff hotfix-1.2.1
Merge made by recursive.
(Summary of changes) 规则的一个例外是:如果一个release
分支已经存在,那么应该把hotfix合并到这个release分支,而不是合并到develop分支。当release
分支完成后, 将bugfix
分支合并回release
分支也会使得bugfix
被合并到develop
分支。(如果在develop
分支的工作急需这个bugfix
,等不到release
分支的完成,那你也可以把bugfix
合并到develop分支) 最后,删除临时分支:
(Summary of changes) 规则的一个例外是:如果一个release分支已经存在,那么应该把hotfix合并到这个release分支,而不是合并到develop分支。当release分支完成后, 将bugfix分支合并回release分支也会使得bugfix被合并到develop分支。(如果在develop分支的工作急需这个bugfix,等不到release分支的完成,那你也可以把bugfix合并到develop分支) 最后,删除临时分支:
$ git branch -d hotfix-1.2.1
Deleted branch hotfix-1.2.1 (was abbe5d6).
尽管这个分支模型没有任何震撼的新东西, 文章开头的图表在我们的项目中表现出惊人的实用性。它形成了一个优雅的思维模型,易于领悟并使团队成员发展出对分支和发布过程的共同理解。
如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。如果想加入微信群的话一起讨论的话,请加管理员微信号:chengcheng222e,他会拉你们进群。