Git分支管理策略

作者: 阮一峰

日期: 2012年7月 5日

如果你严肃对待编程,就必定会使用"版本管理系统"(Version Control System)。

眼下最流行的"版本管理系统",非Git莫属。

相比同类软件,Git有很多优点。其中很显著的一点,就是版本的分支(branch)和合并(merge)十分方便。有些传统的版本管理软件,分支操作实际上会生成一份现有代码的物理拷贝,而Git只生成一个指向当前版本(又称"快照")的指针,因此非常快捷易用。

但是,太方便了也会产生副作用。如果你不加注意,很可能会留下一个枝节蔓生、四处开放的版本库,到处都是分支,完全看不出主干发展的脉络。

Vincent Driessen提出了一个分支管理的策略,我觉得非常值得借鉴。它可以使得版本库的演进保持简洁,主干清晰,各个分支各司其职、井井有条。理论上,这些策略对所有的版本管理系统都适用,Git只是用来举例而已。如果你不熟悉Git,跳过举例部分就可以了。

一、主分支Master

首先,代码库应该有一个、且仅有一个主分支。所有提供给用户使用的正式版本,都在这个主分支上发布。

Git主分支的名字,默认叫做Master。它是自动建立的,版本库初始化以后,默认就是在主分支在进行开发。

二、开发分支Develop

主分支只用来分布重大版本,日常开发应该在另一条分支上完成。我们把开发用的分支,叫做Develop。

这个分支可以用来生成代码的最新隔夜版本(nightly)。如果想正式对外发布,就在Master分支上,对Develop分支进行"合并"(merge)。

Git创建Develop分支的命令:

  git checkout -b develop master

将Develop分支发布到Master分支的命令:

  # 切换到Master分支
  git checkout master

  # 对Develop分支进行合并
  git merge --no-ff develop

这里稍微解释一下,上一条命令的--no-ff参数是什么意思。默认情况下,Git执行"快进式合并"(fast-farward merge),会直接将Master分支指向Develop分支。

使用--no-ff参数后,会执行正常合并,在Master分支上生成一个新节点。为了保证版本演进的清晰,我们希望采用这种做法。关于合并的更多解释,请参考Benjamin Sandofsky的《Understanding the Git Workflow》

三、临时性分支

前面讲到版本库的两条主要分支:Master和Develop。前者用于正式发布,后者用于日常开发。其实,常设分支只需要这两条就够了,不需要其他了。

但是,除了常设分支以外,还有一些临时性分支,用于应对一些特定目的的版本开发。临时性分支主要有三种:

  * 功能(feature)分支

  * 预发布(release)分支

  * 修补bug(fixbug)分支

这三种分支都属于临时性需要,使用完以后,应该删除,使得代码库的常设分支始终只有Master和Develop。

四、 功能分支

接下来,一个个来看这三种"临时性分支"。

第一种是功能分支,它是为了开发某种特定功能,从Develop分支上面分出来的。开发完成后,要再并入Develop。

功能分支的名字,可以采用feature-*的形式命名。

创建一个功能分支:

  git checkout -b feature-x develop

开发完成后,将功能分支合并到develop分支:

  git checkout develop

  git merge --no-ff feature-x

删除feature分支:

  git branch -d feature-x

五、预发布分支

第二种是预发布分支,它是指发布正式版本之前(即合并到Master分支之前),我们可能需要有一个预发布的版本进行测试。

预发布分支是从Develop分支上面分出来的,预发布结束以后,必须合并进Develop和Master分支。它的命名,可以采用release-*的形式。

创建一个预发布分支:

  git checkout -b release-1.2 develop

确认没有问题后,合并到master分支:

  git checkout master

  git merge --no-ff release-1.2

  # 对合并生成的新节点,做一个标签
  git tag -a 1.2

再合并到develop分支:

  git checkout develop

  git merge --no-ff release-1.2

最后,删除预发布分支:

  git branch -d release-1.2

六、修补bug分支

最后一种是修补bug分支。软件正式发布以后,难免会出现bug。这时就需要创建一个分支,进行bug修补。

修补bug分支是从Master分支上面分出来的。修补结束以后,再合并进Master和Develop分支。它的命名,可以采用fixbug-*的形式。

创建一个修补bug分支:

  git checkout -b fixbug-0.1 master

修补结束后,合并到master分支:

  git checkout master

  git merge --no-ff fixbug-0.1

  git tag -a 0.1.1

再合并到develop分支:

  git checkout develop

  git merge --no-ff fixbug-0.1

最后,删除"修补bug分支":

  git branch -d fixbug-0.1

(完)

珠峰培训

简寻

留言(74条)

git-flow 不错,推荐一下~

不错的最佳实践指南。

Scott Chacon 说太复杂了!要用 github-flow! http://scottchacon.com/2011/08/31/github-flow.html

创建 develop 分支:git checkout -b develop master ,为什么后面还多个 master?

引用bravluna的发言:

创建 develop 分支:git checkout -b develop master ,为什么后面还多个 master?

后面是跟一个master说明,创建的develop分支是基于当前的master, 如果当前就在master分支,则可以不写。 如果当前分支HEAD指向的是别的分支,又想基于master分支创建,就必须写master

阮兄,说句题外话。你的版式似乎对阅读器的支持不是很好,例如Safari的Reader,经过实验估计是p元素太多,但没有突出文章主体,话说只要把文章内容放在article标签里就好了。

引用老赵的发言:

阮兄,说句题外话。你的版式似乎对阅读器的支持不是很好,例如Safari的Reader,经过实验估计是p元素太多,但没有突出文章主体,话说只要把文章内容放在article标签里就好了。

赵姐夫也关注阮哥啊。

有点异议, fixbug 那个我看原文指示的是 fixbug-版本号,我们公司内部的做法是 fixbug-<bug 跟踪系统编号>,配合 bug 跟踪系统使用更加完善。

引用老赵的发言:

阮兄,说句题外话。你的版式似乎对阅读器的支持不是很好,例如Safari的Reader,经过实验估计是p元素太多,但没有突出文章主体,话说只要把文章内容放在article标签里就好了。

另外可以考虑使用 h1/h2 这些来区分标题,而不是单纯加粗,使用 pre 替换 block 来表示代码,block 是表示引用的。

应该最先是这位人兄推荐这样的做法:http://nvie.com/posts/a-successful-git-branching-model/

然后再搞出了一个 git-flow :https://github.com/nvie/gitflow/

引用老赵的发言:

你的版式似乎对阅读器的支持不是很好,例如Safari的Reader,经过实验估计是p元素太多,但没有突出文章主体,话说只要把文章内容放在article标签里就好了。

加了一个article标签,好像解决了。

但是为什么作者和日期信息,都会被省略呢……

感觉分享。
这篇文章怎么说呢,感觉像是原创,其实只是高级翻译了一下。
不如说一下Git flow的实践..

这不就是ClearCase的默认设置嘛 还是CC最好用啊

我们公司用的是SVN,感觉也很不错。

阮兄把原文提炼了一下,还另外配了图,但是老实说,原文写的更详尽一些,更容易理解,而且,原文的图画的更好,呵呵。

这样我就明白公司为什么会有两条线,开发者流和测试版本流。在两个版本之间做了一个缓冲。

文章中的流程就是 gitflow 的流程,以前的公司里就是这样用的。默认情况下 gitflow 的修补bug分支叫“hotfix”。

挑个小错误,正文 git merge 的选项 `--no-ff` 被写成了 `--no--ff`,多出来一个‘-’

引用found a tiny error的发言:

挑个小错误,正文 git merge 的选项 `--no-ff` 被写成了 `--no--ff`,多出来一个‘-’

谢谢指出,已经更正了。

引用阮一峰的发言:

加了一个article标签,好像解决了。

但是为什么作者和日期信息,都会被省略呢……

是用<time>标签定义日期吗?
试试这个,阮兄
<article>
  <header>
    <h1>title</h1>
    <p>Date:<time pubdate="pubdate">7/09/2012</time></p>
    <p>Posted by: steven</p>
  </header>
  <footer>
    <p>Copyright</p>
  </footer>
</article>

学习develop分支和--no-ff,谢谢阮大师的漂亮文章。

第一次留言,太狼狈了。:(
可参考 http://html5doctor.com/the-article-element/

git 2年用户,帮顶一下,我主页也有推广过,哈哈

排版太舒服了, 让我有点不太适应。
非常好的博文, 会长期关注。

公司里使用SVN,创建一个分支的代价太高了。要完整co几G的文件。
还是svn好啊

还是svn用用好了,已经完全能够达到自己的需求,就不去穷装升级使用git了

很不错的best practice

有几个基线。
1主分支,所有的生产环境都是从主干部署的。
2开发分支,开发是持续进行的工作,几乎没有阶段性,几乎没有停止性。
3其他分支,满足一些场景的需要,然后会合并到主分支或开发分支。

场景:
1测试需要。
2正式版本之后的bug修补需要。

不太明白的就是feature分支,为什么?新功能,直接在develop分支上进行不就可以了吗?反正最后也是合并到develop分支,为什么非要单搞一个分支呢?可以举个例子吗?

写的很好啊,对我们刚入git的人来说,这样的材料就是最合适的了
搞不懂为什么总有人诸多挑剔
无语

学习了,终于大概知道 git 是干嘛的了

引用virusswb的发言:

不太明白的就是feature分支,为什么?新功能,直接在develop分支上进行不就可以了吗?反正最后也是合并到develop分支,为什么非要单搞一个分支呢?可以举个例子吗?

如果这个功能还没出生就胎死腹中了,那样连合到Develop的过程都不必了吧

master:提供给用户使用的正式版本
develop:日常开发


开发测试完成后,合并develop到master上发布给用户,发布后发现bug(陆陆续续的发现,可能不止一个),是每个bug都要单独建fixbug分支吗?

非常好!我正在使用Git管理自己的代码和文章。但是没有很好的使用分支来进行管理,这好这里有了最佳实践!顶一个!哈哈

初尝试Git没多久,今晚掉进Git里了。看了LZ博文,豁然开朗!

帮助甚大啊~~~--no-ff这个参数很重要

阮老师,您好!问个弱智点的问题:创建从feature或者fixbug分支,需要提交到远程服务器上吗?为什么?

预发布分支
“预发布分支是从Develop分支上面分出来的”,预发布结束以后,我看您是先合并到master分支,再合并到develop分支
这里我就不太懂了

预发布分支是从Develop分支上出来的,发布结束,应该先合并回到Develop,再合并到master吧?

最近勉强会用git管理代码了。但是,对于像我这样,整天写的都是小玩意儿的同学,感觉git用处不大。做大项目才会有用吧。

最近有好心人出了一个在线的形象化的教程 http://pcottle.github.io/learnGitBranching/
能够动态展示分支管理的过程,并提供好几套练习,欢迎大家去帮忙完善啊。

这个写的真心好,很好的指导帮助!谢谢

我是新手,请问,git 和 svn 是同类工具么???

受教了!!!
请教两个问题。
1如何使用git开发,能最大程度的减少冲突。
2如果使用git来开发的话,是不是也需要每天上班来的时候先从pull获取分支上的最新代码再进行开发呢?

写的非常清晰,谢谢

我理解的是,实验性的新功能开发分支,有可能会被砍的。


引用virusswb的发言:

有几个基线。
1主分支,所有的生产环境都是从主干部署的。
2开发分支,开发是持续进行的工作,几乎没有阶段性,几乎没有停止性。
3其他分支,满足一些场景的需要,然后会合并到主分支或开发分支。

场景:
1测试需要。
2正式版本之后的bug修补需要。

不太明白的就是feature分支,为什么?新功能,直接在develop分支上进行不就可以了吗?反正最后也是合并到develop分支,为什么非要单搞一个分支呢?可以举个例子吗?

相当受用。
谢谢您。

ryf的文字展现的功底真强。

这个是给实践派用的。
看了gitbook上的东西,完全搞不懂git存在的意义。
这样讲,就觉得git好用多了。

简单明了,帮助很大!谢谢!

简洁明了。。谢谢!

还是没有搞清楚为什么要有“预发布分支”,直接在"Develop分支"测试不可以吗?

版主,我最近一直在为merge分支代码烦恼,请问我现在要把标签的代码合到分支上。咋弄???

@virusswb:

阶段性的截取 develop 上阶段的新功能,用来测试,并不往上面添加新功能。避免夹带大量的bug。

引用Dennis的发言:

还是没有搞清楚为什么要有“预发布分支”,直接在"Develop分支"测试不可以吗?

一个例子:预发布分支的功能基本没有bug了,但是devlop可能还有正在开发的新功能还不稳定或者根本不能用,会导致没办法发布。预发布就是用来从develop截取下一个版本要发布的功能,不在夹杂本次不要的功能,专注于修复bug。

有点不理解的地方是,release合并到master这个过程是有风险的。直接就上线了?

为什么bugfix分支要基于master,基于develop再反合到master,不行么?

全文拜读, 非常不错,最近正在研究GIT部署问题。

有时间的话,可以写一篇关于git的分支处理细节的代码。
比如git rebase,git squash

引用阿苗的发言:

为什么bugfix分支要基于master,基于develop再反合到master,不行么?

Bugfix之所以从master checkout 因为遇到紧急bug时适用,直接在master修改,这样就避免了走develop分支,develop 分支可能有新开发的功能和未经过测试的代码,避免bug衍生bug,master上的代码都是测试后才合并的,所以紧急bug的场景应该在master分支checkout。建议了解一下git flow。

阮老师 有错别字
主分支只用来分布重大版本
阮老师 我有一套分支管理流程图
应该算是你的总结形象版吧

写得很好,谢谢。

阮一峰前辈的日志非常清晰、实用,是我模仿的好榜样~。本篇文章提出的分支管理守则很好地解决了我手上的问题,致谢!

我有个问题关于远程master权限读写的问题。因为远程master默认谁都可以往上push,这对于一个正式发布版本来说肯定不允许的,哪怕是误操作。于是在gitlab上可以设置保护远程master分支不被普通开发人员push,这样一来本地master岂不是形同虚设,因为本地develop也会是push到远程dev分支,最后由主程序员或项目组长来线上merge dev到master?不知道是不是这样的流程。

而且现在远程master普通开发人员不可写,这样一来本地hotfix临时分支就无法更新到远程master,而develop分支上又有新的功能在开发,这可如何是好?

在github上面我看大家都是先fork到自己的仓库,然后提交pull request,管理员再合并请求。而看到的gitlab文章都是直接git clone 到本地,然后开始add,commit,push了,怎么会有这么大的差别呢

找到一篇雷同的文章,似乎更好读一些:)

http://nvie.com/posts/a-successful-git-branching-model/

两个问题:
1,既然功能开发和bugfix都是单独拉分支的,那么什么情况下会在develop分支上直接提交代码呢?
2,很多时候是多个功能分支同时进行开发的。假设功能分支1已经开发完成,准备合入develop进入测试,如果测试发现问题很多,比预期的发布时间要延迟。而功能分支2也已经开发完成,等待着测试上线,这种情况怎么搞?只能等待功能分支1发布完成或者回退功能分支1的代码吗?

引用beyond_cheng的发言:

两个问题:
1,既然功能开发和bugfix都是单独拉分支的,那么什么情况下会在develop分支上直接提交代码呢?
2,很多时候是多个功能分支同时进行开发的。假设功能分支1已经开发完成,准备合入develop进入测试,如果测试发现问题很多,比预期的发布时间要延迟。而功能分支2也已经开发完成,等待着测试上线,这种情况怎么搞?只能等待功能分支1发布完成或者回退功能分支1的代码吗?

是的 最后发版如果是把develop合如master发版,就无法满足你说的这种情况 前后上线需求 单独上线需求 我的解决办法是 从master拉稳定分支开发 合并到develop做开发测试 最后上线 从我的自己开发分支直接合进master上线 别人有没有搞定 就不用担心了 等他开发完 再把自己分支合进master上线就是了 develop只用来当做一个集成开发测试分支 QQ 392190627 欢迎加我讨论git 之前我也是有这疑问非常久

个人觉得master不应该是用来发布版本的, 项目最初其实只有develop版本,只有在devlop版本到一定程度才会有release(图中的master)版本。

引用Sean的发言:

我有个问题关于远程master权限读写的问题。因为远程master默认谁都可以往上push,这对于一个正式发布版本来说肯定不允许的,哪怕是误操作。于是在gitlab上可以设置保护远程master分支不被普通开发人员push,这样一来本地master岂不是形同虚设,因为本地develop也会是push到远程dev分支,最后由主程序员或项目组长来线上merge dev到master?不知道是不是这样的流程。

而且现在远程master普通开发人员不可写,这样一来本地hotfix临时分支就无法更新到远程master,而develop分支上又有新的功能在开发,这可如何是好?


第一个问题:
feature分支是基于develop分支的,bugfix有的地方叫hotfix分支基于master分支。发布时,基于develop分支创建预发步分支release-x,由release分支合并到master, 也就是说 develop 分支和master分支之间还有个 预发布分支 release分支。

第二个问题:
“假设功能分支1已经开发完成,准备合入develop进入测试,如果测试发现问题很多” 这句话说明单元测试和集成测试没有做好,才会出现快要提交develop-->release测试时候发现 “问题很多”, 建议交给测试之前先写好单元测试。并测试通过再考虑测试。

请问博主,预发布分支的tag和fixbug的tag如何管理?
例:
预发布成功,tag1.2。但是后来修改bug成功,tag应该是1.2.1?1.3?还是不沿用预发布分支的tag版本号。
希望帮忙解答,多谢。

master和 develop 怎么感觉作用重复了,每次开发完或者bugfix后都需要同时合并到master和 develop,感觉它们的状态基本都是一致的,是不是可以去掉一个,比如 master?

从 dev 分支切出 feature 1 分支和 feature2 分支,feature 1 分支开发完成合入 dev ,从 dev 切出 rc.1 分支进行测试,然后发现feature1上了预发有 bug,呼哧呼哧的改,这时候 feature2也合入 dev 再切 rc.2 测试了,feature2测试通过,这时候 rc.2 分支中包含了 feature 1 的代码,所以必须等 feature1 修完 bug 才能发布。这不是互相拖延了吗?

引用Dor的发言:

从 dev 分支切出 feature 1 分支和 feature2 分支,feature 1 分支开发完成合入 dev ,从 dev 切出 rc.1 分支进行测试,然后发现feature1上了预发有 bug,呼哧呼哧的改,这时候 feature2也合入 dev再切 rc.2 测试了,feature2测试通过,这时候 rc.2 分支中包含了 feature 1 的代码,所以必须等 feature1 修完 bug 才能发布。这不是互相拖延了吗?

对于你的这个例子我的理解是软件开发上线周期是固定的,如果feature1, feature2都要赶上接下来的上线时间点上线,那么预发布是应该在feature1,feature2全部合并到dev后进行测试使用的,这个时候本来就是期望能够尽早发现bug并修复,所以并无互相拖延说法。如果其中一个feature开发进度比较慢会影响正常的升级时间,应该评估是否放弃这个上线时间点上线,考虑延后一个上线时间点上线。

P.S. 一般来说feature1,feature2是相互独立的功能,可以考虑在合并到dev之前先自测。
P.S. 软件就像一直前进的地铁,地铁里的每个乘客就像是软件的一个功能,每个站台(上线时间点)乘客有上有下,赶不上这趟就赶下趟,不能因为你一个人耽误了所有人的时间

引用zyren的发言:

master和 develop 怎么感觉作用重复了,每次开发完或者bugfix后都需要同时合并到master和 develop,感觉它们的状态基本都是一致的,是不是可以去掉一个,比如 master?

master 和 develop 并没有重复。master一直保持很稳定,只接受经过充分测试后的代码。develop一直很活跃,所有的新功能先来找它。它们的状态基本上是不一致的,只有从develop 拉出release分支测试然后合并到master和develop分支这个时间点才有可能是状态一致。但是这就要求这段时间内没有develop分支的变化。

问个问题,假设我的git库有两个分支master 和test, 在test分支上打了tag 比如v0.1, 然后又做了其他修改,现在我要合并master分支和test分支上的v0.1这个版本的代码,怎么合并?
我试过先在test上checkout到v0.1这个版本,然后再checkout到master上合并,结果还是合并的test分支的head,而不是v0.1这个版本。谢谢!

您好,问下修复bug好了之后给测试人员进行测试,是同时建(release)分支跟(fixbug)分支,还是直接用fixbug上面的代码给测试人员测试,测试好了没问题在建release分支

我要发表看法

«-必填

«-必填,不公开

«-我信任你,不会填写广告链接