分类: 新兴技术

We firmly believe that long-lived version-control branches harm valuable engineering practices such as continuous integration, and this belief underlies our dislike for Gitflow. —— ThoughtWorks Technology Radar 2015 Nov

什么是Gitflow

Gitflow是基于Git的强大分支能力所构建的一套软件开发工作流,最早由Vincent Driessen在2010年提出。最有名的大概是下面这张图。

01

在Gitflow的模型里,软件开发活动基于不同的分支:

  • The main branches
    *   master 该分支上的代码随时可以部署到生产环境
    
    • develop 作为每日构建的集成分支,到达稳定状态时可以发布并merge回master
  • Supporting branches
    *   Feature branches 每个新特性都在独立的feature branch上进行开发,并在开发结束后merge回develop
    
    • Release branches 为每次发布准备的release candidate,在这个分支上只进行bug fix,并在完成后merge回master和develop
    • Hotfix branches 用于快速修复,在修复完成后merge回master和develop

Gitflow通过不同分支间的交互规划了一套软件开发、集成、部署的工作流。听起来很棒,迫不及待想试试了?等等,让我们先看看Gitflow不是什么。

  • Gitflow不是Git社区的官方推荐工作流。是的,不要被名字骗到,这不是Linux内核开发的工作流也不是Git开发的工作流。这是最早由Web developer Vincent Driessen和他所在的组织采用并总结出的一套工作流程。
  • Gitflow也不是Github所推荐的工作流。Github对Gitflow里的某些部分有不同看法,他们利用简化的分支模型和Pull Request构建了适合自己的工作流Github Flow
  • 现在我要告诉你,Gitflow在企业软件开发中甚至不是一个最佳实践。ThoughtWorks Technology Radar在2011年7月刊2015年1月刊里多次表明了Gitflow背后的feature branch模型在生产实践中的危害,又在最近一期2015年11月刊里专门将Gitflow列为不被推荐的技术。

为什么Gitflow有问题

Gitflow对待分支的态度就像: Let’s create branches just because… we can!

很多人吐槽吐槽,为什么开发一个新feature非得新开一个branch,而不是直接在develop上进行,难道就是为了……废弃掉未完成的feature时删除一个branch比较方便?

很多人诟病Gitflow太复杂。将这么一套复杂的流程应用到团队中,不仅需要每个人都能正确地理解和选择正确的分支进行工作,还对整个团队的纪律性提出了很高的要求。毕竟规则越复杂,应用起来就越难。很多团队可能不得不借助额外的帮助脚本去应用这一套复杂的规则。

然而最根本问题在于Gitflow背后的这一套feature branch模型。

VCS里的branch本质上是一种代码隔离的技术。使用feature branch通常的做法是:当developer开始一个新feature,基于develop branch的最新代码建立一个独立branch,然后在该branch上完成feature的开发。开发不同feature上的developers因为工作在彼此隔离的branch上,相互之间的工作不会有影响,直到feature开发完成,将feature branch上的代码merge回develop branch。

我们能看到feature branch最明显的两个好处是:

  1. 各个feature之间的代码是隔离的,可以独立地开发、构建、测试;
  2. 当feature的开发周期长于release周期时,可以避免未完成的feature进入生产环境。

后面我们会看到,第一点所带来的伤害要大于其好处,第二点也可以通过其他的技术来实现。

merge is merge

说到branch就不得不提起merge。merge代码总是痛苦和易错的。在软件开发的世界里,如果一件事很痛苦,那就频繁地去做它。比如集成很痛苦,那我们就nightly build或continuous integration,比如部署很痛苦,那我们就频繁发布或continuous deployment。 merge也是一样。所有的git教程和git工作流都会建议你频繁地从master pull代码,早做merge。

然而feature branch这个实践本身阻碍了频繁的merge: 因为不同feature branch只能从master或develop分支pull代码,而在较长周期的开发完成后才被merge回master。也就是说相对不同的feature branch,develop上的代码永远是过时的。如果feature开发的平均时间是一个月,feature A所基于的代码可能在一个月前已经被feature B所修改掉了,这一个月来一直是基于错误的代码进行开发,而直到feature branch B被merge回develop才能获得反馈,到最后merge的成本是非常高的。

现代的分布式版本控制系统在处理merge的能力上有很大的提升。大多数基于文本的冲突都能被git检测出来并自动处理,然而面对哪怕最基本的语义冲突上,git仍是束手无策。在同一个codebase里使用IDE进行rename是一件非常简单安全的事情。如果branch A对某函数进行了rename,于此同时另一个独立的branch仍然使用旧的函数名称进行大量调用,在两个branch进行合并时就会产生无法自动处理的冲突。

如果连rename这么简单的重构都可能面临大量冲突,团队就会倾向于少做重构甚至不做重构。最后代码的质量只能是每况愈差逐渐腐烂。

持续集成

如果feature branch要在feature开发完成才被merge回develop分支,那我们如何做持续集成呢?毕竟持续集成不是自己在本地把所有测试跑一遍,持续集成是把来自不同developer不同team的代码集成在一起,确保能构建成功通过所有的测试。按照持续集成的纪律,本地代码必须每日进行集成,我想大概有这几种方案:

  1. 每个feature在一天内完成,然后集成回develop分支。这恐怕是不太可能的。况且如何每个feature如果能在一天内完成,我们为啥还专门开一个分支?
  2. 每个分支有自己独立的持续集成环境,在分支内进行持续集成。然而为每个环境准备单独的持续集成环境是需要额外的硬件资源和虚拟化能力的,假设这点没有问题,不同分支间如果不进行集成,仍然不算真正意义上的持续集成,到最后的big bang conflict总是无法避免。
  3. 每个分支有自己独立的持续集成环境,在分支内进行持续集成,同时每日将不同分支merge回develop分支进行集成。听起来很完美,不同分支间的代码也可以持续集成了。可发生了冲突、CI挂掉谁来修呢,也就是说我们还是得关心其他developer和其他团队的开发情况。不是说好了用feature branch就可以不管他们自己玩吗,那我们要feature branch还有什么用呢?

所以你会发现,在坚持持续集成实践的情况下,feature branch是一件非常矛盾的事情。持续集成在鼓励更加频繁的代码集成和交互,让冲突越早解决越好。feature branch的代码隔离策略却在尽可能推迟代码的集成。延迟集成所带来的恶果在软件开发的历史上已经出现过很多次了,每个团队自己写自己的代码是挺high,到最后不同团队进行联调集成的时候就傻眼了,经常出现写两个月代码,花一个月时间集成的情况,质量还无法保证。

如果不用Gitflow…

如果不用Gitflow,我们应该使用什么样的开发工作流?如果你还没听过Trunk Based Development,那你应该用起来了。

02

是的,所有的开发工作都在同一个master分支上进行,同时利用Continuous Integration确保master上的代码随时都是production ready的。从master上拉出release分支进行release的追踪。

可是feature branch可以确保没完成的feature不会进入到production呀。没关系,Feature Toggle技术也可以帮你做到这一点。如果系统有一项很大的修改,比如替换掉目前的ORM,如何采用这种策略呢?你可以试试Branch by Abstraction。我们这些策略来避免feature branch是因为本质上来说,feature branch是穷人版的模块化架构。当你的系统无法在部署时或运行时切换feature时,就只能依赖版本控制系统和手工merge了。

Branch is not evil

虽然long lived branch是一种不好的实践,但branch作为一种轻量级的代码隔离技术还是非常有价值的。比如在分布式版本控制系统里,我们不用再依赖某个中心服务器,可以进行独立的开发和commit。比如在一些探索性任务上,我们可以开启branch进行大胆的尝试。

技术用的对不对,还是要看上下文。

[参考文献]

  1. Long-lived-branches-with-gitflow in radar: https://www.thoughtworks.com/radar/techniques/long-lived-branches-with-gitflow
  2. Gitflow in radar: https://www.thoughtworks.com/radar/techniques/gitflow
  3. Feature Branching in radar: https://www.thoughtworks.com/radar/techniques/feature-branching
  4. Fowler on feature branch: http://martinfowler.com/bliki/FeatureBranch.html
  5. Fowler on continuous integration: http://www.martinfowler.com/articles/continuousIntegration.html
  6. Paul Hammant on TBD: http://paulhammant.com/2015/12/13/trunk-based-development-when-to-branch-for-release/
  7. Google’s Scaled Trunk Based Development: http://paulhammant.com/2013/05/06/googles-scaled-trunk-based-development/
  8. Trunk Based Development at Facebook: http://paulhammant.com/2013/03/04/facebook-tbd/
  9. Fowler on feature toggle: http://martinfowler.com/bliki/FeatureToggle.html
  10. Jez Humble on branch by abstraction:http://continuousdelivery.com/2011/05/make-large-scale-changes-incrementally-with-branch-by-abstraction/
  11. Fowler on branch by abstraction: http://martinfowler.com/bliki/BranchByAbstraction.html

发表评论

评论

28 条评论

  1. 「现在我要告诉你,Github在企业软件开发中甚至不是一个最佳实践。」
    ——–
    这里是 Gitflow 而不是 Github 吧?

    还有这里:
    ·「然而最根本问题在于Github背后的这一套feature branch模型。」

  2. 对作者的一些观点和论据有些质疑, 怀疑作者是重度svn用户,还没真正把握git的精髓。

    随便举几个例子:

    > 如果feature开发的平均时间是一个月,feature A所基于的代码可能在一个月前已经被feature B所修改掉了,这一个月来一直是基于错误的代码进行开发,而直到feature branch B被merge回develop才能获得反馈,到最后merge的成本是非常高的。

    您都说是长期分支了,那么这个分支相对而言,不会随随便便的出问题。好吧,就像你说的出问题了,并且也已经清楚了feature B已经修复, 那么完全可以再开发feature A的分支上merge feature B, 难道必须等到1个月feature A开发完成? 而且对于bug fixes, gitflow是不建议在开发新功能的同时修复bug, 最好新建专门的分支修复bug. 我们在软件开发中,知道去耦合,在git的使用上就不知道了?

    > 然而面对哪怕最基本的语义冲突上,git仍是束手无策。在同一个codebase里使用IDE进行rename是一件非常简单安全的事情。如果branch A对某函数进行了rename,于此同时另一个独立的branch仍然使用旧的函数名称进行大量调用,在两个branch进行合并时就会产生无法自动处理的冲突。

    大哥你说的是重构了,相当于您要修改接口了,那版本至少是从1.0 跨越到 1.1了, 您在1.1版本分支上去合并1.0开发分支,当然会出问题,如果这样做,只能说修改代码太没有大局观了,太没有替小伙伴考虑了,最起码,您要通知下伙伴,我要重构了,我们先对下代码。 另外,您对于冲突的理解也有些偏差,什么时候出现冲突,仅仅当出现3种不同路子的时候才会冲突。比如分支A上,让他向北走, 分支B上让他向东走, 那当然会冲突了,最后当然需要由人决定,到底怎么办? 解决冲突,更多的应该是总裁。

    另,作者对于分支的看法,还停留在svn的时代, 对于分支,对于分布式,还没理解精髓。 在git中,分支仅仅是个指针,不是像svn重新复制一份。用git,就是要善用分支,控制好粒度,把Feature再细分成小的模块,在更小的模块上操作,作为后,测试没问题,就合并。

    文章还没有开完, 有些观点不同,于是吐槽下。Git这么好的东西,希望给大家或新人一个正确的指引。

    后续我会写一篇博客,对作者的文章逐一推敲,评论,以正Git.

    话说的有点直,还望见谅,各抒己见而已。

    • 作者说基于的代码被修改了,没说是因为bugfix,开发新feature也会修改已有代码。
      新建更多分支也不一定能减少耦合,有时候耦合是代码本身的耦合造成的,与分支无关。

      第二点我的理解是,作者说重构困难,你说尽量别重构。

    • 对这位同学的质疑,我们已经在https://ruby-china.org/topics/29263 进行了讨论。以下是原贴中的回复:

      Hi, 我是原文的作者。很高兴能在这里看到对这篇文章的讨论。

      原文的观点是long lived branch considered harmful。关于什么是Gitflow章节的反驳我就不花太多精力了,关于如果不用gitflow章节的质疑,原文也给出了链接,TrunkBasedDevelopment和feature toggle等技术是被Google, Facebook, ThoughtWorks等技术公司验证的最佳实践。

      剩下的部分其实基于一个基本假设,这个假设也是软件开发行业几十年来的共识:我们应该尽可能地缩短反馈周期。在这里feature branch会鼓励长时间分支,每个分支在合并到主干前都无法获得及时的反馈。楼主举的例子很好:

      我们新开一个分支,把功能做好,测试确认没问题,马上就合并到枝干上,把这个分支给删除了。这个分支,甚至都不会出现在中心仓库的分支里,升值自己的remote仓库里,仅仅在个人本地仓库出现,可能一天,或者半天我们就完成了一个merge迭代,把这个分支给删除了。
      这也正是原文所鼓励的,避免long lived branch。无论是否用gitflow或feature branch,使用者在实践中都会发现频繁地跟主干合并代码(频繁从主干rebase回feature branch,频繁把feature branch代码merge回主干)能大大减轻big bang conflict的merge。而当你的每个feature branch只存在于本地一天或半天时,为什么还要单拉这么一个branch呢?毕竟本质上git并没有主干分支,每个repository都是一个独立分支。

      楼主对于原文一些例子的质疑,可能因为楼主的工作背景是嵌入式领域,对于互联网模式/敏捷软件开发中的许多基本实践(代码共有,重构,持续集成)缺乏了解。如果不澄清这一点可能会对一些基本的实践产生观念冲突,而完全展开也不是三言两语就能覆盖的。如果有兴趣我们可以case by case地聊。

      另外原文讨论的是基于企业软件开发的上下文。如果是开源软件开发的协作就是另外的情况了。事实上开源社区的繁荣和魅力的根源也正是其多样性——每个人都可以fork自己的分支。

      • 在 gitflow 中,跟 master 长期并存的是 develop 分支,不是feature分支。develop分支的目的才是隔离作用,避免开发中的代码污染master线上的稳定环境。feature 不应该是一个 long lived branch,他应该粒度被尽可能小,尽快的合并到develop分支中,这是好的gitflow实践,也符合作者提倡的持续集成的思想。

        另外一点就是,作者提出的git方案,没有解决分布式开发的问题。基于feature分支的开发,更重要的作用是分布式。

      • “这也正是原文所鼓励的,避免long lived branch。无论是否用gitflow或feature branch,使用者在实践中都会发现频繁地跟主干合并代码(频繁从主干rebase回feature branch,频繁把feature branch代码merge回主干)能大大减轻big bang conflict的merge。而当你的每个feature branch只存在于本地一天或半天时,为什么还要单拉这么一个branch呢?毕竟本质上git并没有主干分支,每个repository都是一个独立分支。”

        上一条回复已经说明了,gitflow中,短周期小粒度feature频繁的合并到develop这中方式,就是一种避免long lived branch的实践。隔离的作用是develop分支,feature分支更重要的作用是 commit语义和解决分布式开发。

        按照作者提出的第二种方式,commit频繁的提交到master上,如果多人同时在做开发,并不能解决冲突问题,因为你每次提交有可能照成冲突,每次都需要解决。还有一个问题就是你一下我一下杂乱的提交会破坏commit语义。合并一个feature有一个完整的语义,而不是作者提出方案的各种加错的片段。

        “在软件开发的世界里,如果一件事很痛苦,那就频繁地去做它。”
        “能大大减轻big bang conflict的merge。”

        频繁的解决小冲突,和一口气解决一个稍微大一点的冲突(feature),哪种方式更好呢?我不清楚。但是我觉得gitflow更有利于分布式和语义。

  3. 我想问下如何才能做到不同的人同时在 master 上进行开发。毕竟任何人一旦把代码 clone 到本地,本地 master 和 origin/master 就形成了两个不同的 branch。当你在本地 master 上开发完毕想 push 到 origin/master 时,别人的代码说不定已经 push 进来了。这时候你的本地 master 就相当于一个 feature branch,只是换了一个名字而已。

    • 你说的没错。但是这种情况下本地分支和远程分支只差几个提交,合并难度比较小。

    • Microsoft Office组就是这样的。其实解决起来很简单,我们一天会有几百个push到main branch上面,后push的那个如果有冲突需要sync前面的那个下来merge,然后就形成了树的关系。到了一天结束的时候,服务器就把所有的新push拿起来编译,最后有一个精通所有代码的人(根据观察有几百个人轮流执行)值班把造成build break的那颗子树删掉,顺便发邮件让他们重来。就这样运行了几十年,迭代非常快。

    • 对这位同学的质疑,我们已经在https://ruby-china.org/topics/29263 进行了讨论。以下是原贴中的回复:

      @steveltn 是的,前面也提到了代码clone到本地就成为分支。分布式版本控制系统其实没有严格意义上trunk的概念,我们只是挑选一个master作为主分支。branch按周期分有long lived branch, short term branch和temporary branch,按用途分有feature branch, release branch… branch是一种事实,而feature branch特指利用branch隔离feature代码进行开发的一种使用方式。

      原文的点在于避免long lived branch,而feature branch会鼓励长期跟master并行的分支。原文鼓励的是频繁地跟master合并,以release track(bug fix), 探索性活动为目的创建分支,而不是以feature代码隔离为目的创建分支。

      • “原文的点在于避免long lived branch,而feature branch会鼓励长期跟master并行的分支。原文鼓励的是频繁地跟master合并,以release track(bug fix), 探索性活动为目的创建分支,而不是以feature代码隔离为目的创建分支。”

        在gitflow中,跟master长期并存的是develop分支,不是feature分支。develop的作用是隔离。feature是小粒度,并频繁的创建和合并到develop分支中的,他的主要作用是解决分布式开发和commit语义的作用。

        当develop分支积累到一定程度,测试通过,才会合并到master分支。develop分支合并到master的过程中,通常不会照成冲突,因为按照gitflow流程来实践,是不会有分支直接提交到master的,除了一种情况就是bugfix分支,他来源于master分支,最终合并于master和develop分支。bugfix合并到develop分支后,其分支根基础的变更和master又是相同的了。所以,虽然develop分支是一个长期分支,但是通常并不会产生 big merge,所有并不存在作者所说的问题。

        至于feature,把他用作一个长期分支,隔离分支,本身就是对gitflow的错误理解和错误使用,或者并不是一个好的实践。

      • 还有一种可能是这样的,就是作者认为 feature 分支被滥用的,错误的使用了。这个是确实存在的,在现实中的很多团队中。但是,这不是gitflow的锅,并不是分支的锅。文字却冠名 《gitflow有害论》,这非常冤枉。

  4. 作者是重度svn用户无误,整篇理论基本胡扯。

    两点反驳:
    1.在Gitflow中,保持feature的粒度尽可能小是好的实践。作者认为feature分支会存在很长时间,是因为作者吧feature分支当成了release part分支,这么看当然无法马上合并到develop,自己为是,根本就没理解。

    2.合并冲突这个是永恒的问题,不论哪种方式,什么工作流,都无法解决,除非一个人开发。作者认为合并feature冲突很痛苦,我想问,第二种方法,大家都在master上开发,就不会冲突了吗?

    git是分布式的,冲突是必然的无法避免的,作者是在用svn集中化的思想去理解git。

    在gitflow中,feature分支除了有隔离代码的作用外,本身这个概念也是有意义的,因为feature的合并给了commit一个明确和实际的意义。提交历史,代码的衍生情况对git来说是有意义的。一个符合gitflow实际的代码树可读性是非常棒的。第二种方式,产生的代码树毫无可读性。

    关于feature分支好的实践的两点建议:
    1.保持feature和bugfix分支的粒度尽可能小是一种好的实践。
    2.即便按照1来做,冲突仍然是不可避免的。相比合并产生冲突,在合并之前,对feature分支进行rebase到最新的develop结点是一种好的实践。和合并冲突相比,工作量相当,但是产生的代码树可读性更好。

    • Hi,首先我不是svn重度用户。相反,我们在2008年以前就开始大规模使用分布式版本控制系统,并在社区和业界推动 svn 向 git/hg 的转换(http://www.infoq.com/cn/articles/thoughtworks-practice-partiv)。

      对@TakWolf 同学的两点反驳我试着回应一下:

      1. 本文里明确区分了feature branch和release branch,并在结尾部分指出用release branch进行track或hotfix。short branch over long lived branch是值得肯定的实践。我们曾经多次在技术雷达里表达对long lived branch的担忧。然而基于我们对业界大量团队的观察,使用Gitflow的团队非常容易偏向long lived branch,这也是为什么我们在雷达里专门highlight了Gitflow。

      还有一个比较有趣的点是。如果feature开发周期比较短(几次commit)就能完成,即使不用feature branch也能很好地完成任务。毕竟在DVCS里每个repository都是独立的分支。即使几天的feature,往往也可以通过先后端、后前端的开发方式减少对用户的影响。然而遗憾的是,我们看到太多的团队把feature branch当做一种向用户隐藏未完成feature的手段,而不是采用更合理的模块划分或特性开关等技术。

      2. 本文承认合并冲突这个是永恒的问题,并指出“在软件开发的世界里,如果一件事很痛苦,那就频繁地去做它”。通过在master上进行开发,可以以更小步的增量获得更快速的反馈。鼓励快速反馈,鼓励增量式变化而不是big bang change是TDD, 持续集成等很多实践的前提,相信@TakWolf 同学也是同意的。

      我们大量使用Trunk Based Development团队的经验表示,关于git历史的阅读并没有什么问题。这里有个小实践:为每一个commit规定固定格式(#storyNumber [author] commit message,如 #123 [shangqi] add validation for creating user),这样就能在code review时非常方便地找到相关的代码。支持这样的固定格式只需要十几行bash脚本(还能自动缓存上个commit的story number,author等信息)。

      值得一提的是,这种更小步的merge反而更容易在code reivew时找到问题。big bang merge往往会导致代码面目全非,review起来非常费劲。毕竟人眼不是compiler或test suites,很难在review里快速发现冲突带来的问题。

      至于是否有必要为feature专门保留一个版本历史树,我个人持保留态度。实践中更高效的方式是直接阅读最新代码,如果有问题,使用git blame。

  5. 在补充一个点:

    作者认为github没有使用gitflow,而是使用了自己的一套简化的pr流程。
    github这没做不是因为gitflow流程有问题,而是因为在github的模式上,代码的合并需要权限管理以及质量评审。
    使用pr的方式提交代码,可以使这个提交附带issues功能,保证权限,也可以是大家产生讨论和评估。
    换句话说,pr是一个升级版的feature或者bugfix分支。除去issues和权限,pr跟feature或者bugfix没啥本质区别.

    • 补充一下。Github Flow跟Pull Request不完全划等号。Github没有使用Gitflow是因为”it does have its issues”, “one of the bigger issues for me is that it’s more complicated than I think most developers and development teams actually require”,对于Github这种需要持续部署(feature as hotfix)的组织来说Gitflow太过繁琐。

      相关的背景在本文原文里有引用,可以在这里(http://scottchacon.com/2011/08/31/github-flow.html)找到。

  6. Branch by Abstraction本来就是一个多余的东西。使用正确的面向对象分析做出来的架构,每一个部分天然就有一个“抽象”,又是在炒冷饭,40年前的知识了。

    • 因为看了一圈发现很多人又做错了。不断有人无视软件行业几十年来积攒下来的那一点点经验。而Branch by Abstraction是说,恩你又做错了,如果你不想推翻重来,可以试试这个…

  7. 根据作者给出的图片以及 Trunk Based Development 关键词搜索了一下。感觉这篇文章写得更清晰明白,建议大家直接看这个文章。

    http://blog.jobbole.com/73930/

    实际上是两种分支模式 Feature Branches 和 Feature Toggle,跟gitflow没啥直接关系。
    两种方式各有优缺点,分别适用于不同的场景,没有所谓好坏之分,更不存在啥的有害论。

    作者估计是想安利 Trunk Based Development 这种开发模式,但是却起了一个《Gitflow有害论》这种一看就让人撕逼的标题。

    全文只有最后一句写的好:技术用的对不对,还是要看上下文。

  8. 标题可以再斟酌一下。。文章的内容对于大规模的软件团队(特别是刚从svn转过来,觉得git的分支超爽)来说的确是存在的问题,深有体会。。

  9. 如果线上有紧急 bug 需要修复,是从 release 分支修复吗?如果从 master 分支修复,可能会带入新的 bug 的吧。

    还有这种方式对于测试要求更严格

    • 对于线上紧急bug的修复,推荐的方式是在master分支上做修复,然后把修复的commit cherry-pick到release分支上。不是整个merge哦。

  10. 首先我曾经是svn重度用户,最近两年才开始使用git的。维护过不少大团队长时间的git branch开发。就我的个人经验来说,如果git都冲突,svn早都冲突到死了。我比较楼上说的,作者还没有领略到git的精髓。
    和作者以及大家一样,对long-lived version-control branches 深恶痛绝。但是怎么解决,并没有看到特别有说服力的,或者特别系统的解决方案。说说我个人的经验,第一,就是能细分就细分,细分不但解决了long-lived问题,而且在工程管理上,也极大的降低了风险;从质量和发布上,也可控和回滚操作性强。第二,就是无法细分的项目,其实这样的项目,一般都是公司和人的问题,没有不能拆分的代码,只能不愿意拆分的代码,实在拆不开也只是说明烂到死,应该重构了。但是,遇到这样开发层面无法解决的问题,我们怎么积极的解决问题?我们的方案就是定期从master拉到feature分支上,让feature分支的开发来handle这些master的新变更。然后在feature branch上完成了full regression再回去。

    • git基于行的diff在处理冲突时确实比svn更好,问题不是出在git和svn的区别,而是long lived branch和short lived branch的却别。如果使用svn的本地代码提交频率跟git的feature branch提交频率一样低,那合并起来会更痛苦。

      关于解决方案,文章后面没有展开讲,其实就是Trunk Based Development加持续集成,这已经是业界非常成熟的实践了,Google数十亿行代码都在单一代码库上使用基于主干开发的策略。

      另外一点就是你提到的“能细分就细分”,文章也点了一下没有展开(“feature branch是穷人版的模块化架构”),做到架构解耦才是治本的方法。

  11. # master 是什么?

    * Anything in the master branch is deployable

    * 禁止直接提交到 master 分支

    # 稳定分支

    * master 是稳定分支

    * 其他分支 都是不稳定分支

    # 在分支上做什么

    * 一个分支做且只做一件事 (包括但不限于功能开发 修复Bug 整合 发布 紧急修复 等)

    * 如果要做多件事,开多个分支

    * 尽量避免多个人在同一分支上做事

    # 分支生命周期

    * 如果做完一件事,分支的生命周期就结束了,要删掉这个分支

    * 分支的生命周期,越短越好,不要长期持有一个分支

    # 从哪里来,到哪里去

    * 分支的上游只能是 master

    * 分支的下游只能是 release

    # 如何上线

    * 测试的版本就是上线的版本,上线的版本必须经过测试

  12. 不管 gitflow 的方式流程是否优秀但是它 “不仅需要每个人都能正确地理解和选择正确的分支进行工作,还对整个团队的纪律性提出了很高的要求”的缺点在实际工作中导致的问题经常是致命的!这点我深有体会