Git GIT 之我见 - 反驳 TW 洞见《GITFLOW 有害论》

ahnniu · 2016年03月11日 · 最后由 GDoom 回复于 2019年07月06日 · 10366 次阅读
本帖已被设为精华帖!

昨天,在翻看自己Inoreader订阅的文章时,看到一个标题《GITFLOW有害论》,研读之下,发觉作者的观点和论据都太过牵强,甚至发觉作者对Git的应用和精髓都不熟悉,何来GITFLOW有害论? 这是对先进思想的诋毁,遂发文以反驳之,以正观点,避免给更多人带来错误的指引。

《GITFLOW有害论》这篇文章出自TW洞见,版权也归Thoughtworks公司所有。 本文作为一篇评论,仅是引用,本身也无意去侵权。

小插曲: 昨日晚6点多钟,正值下班的时间,看到这篇文章后,在下面做了评论,阐述了自己的观点,反驳了作者的一些论据,谁知今早居然被删除,表示无语。

下面进入正题,开始一句一句的解读,评论和反驳,仅仅是各抒己见,绝无诋毁作者之意,另,本人也仅仅是一名普通的软件开发者,对Git极其喜爱和推崇,发现有错误的观点,遂忍不住站出来要为Git说一句公道话。

为什么发到RubyChina, 个人认为RubyChina是言论自由的地方,可容纳各种观点的碰撞与摩擦。另外,我也是一个Ruby爱好者,正在学习Ruby, 菜鸟一个。这应该是我在RubyChina的第一个帖了。当然,也发布到了我的博客, 我的博客就是自言自语,记录个人的一些想法罢了,也没什么人看。

文章通过一段一段的引用作者的原文章,然后跟上自己的评论,最后得出自己的观点。

开始解读,反驳

章节之 - 什么是Gitflow

引用原文:

什么是Gitflow

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

在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

上面这些介绍,没什么可说的,仅有一点:

上面引用的那个图, 出自:A successful Git branching model, 我觉得应该像原作者标题,这仅仅是 A successful Git branching model,Gitflow这个词先前好像没有听过,是个自造词?

A successful Git branching model一文中,给出了大家如何更加规范的使用分支,我们可以用树的成长来比作软件开发:

  • master是树干,是基本
  • develop是大一点的枝干
  • feature, bugfixes是一些小的子叶,长的好,可能会变成另外一个枝干,长的不好可能会终结,腐烂掉。

原作者主要体现的是,区分哪些是稳定的,主干的,哪些是异变的,是可能会出问题的。一切不稳定的东西,都不能影响大局。 这是全盘考虑,大局的结果。

如果整个都是一个枝干,那么如果出现严重问题,后果是比较严重的。

关于分支的理解,待下面再做详细解释。

有另外一个概念,叫:Workflow, 参见:Distributed Git - Distributed Workflows, 中文版:5.1 分布式 Git - 分布式工作流程,概念还是别搞混淆了好, 上述的工作流程中:给出了三种:

集中式工作流

集中式工作流程使用的都是单点协作模型。一个存放代码仓库的中心服务器,可以接受所有开发者提交的代码,这是类似svn的工作方式。

集成管理员工作流

这个类似GitHub上开源项目的Fork和Pull Request,开源贡献者无中心仓库的push权限,但是可以把代码发到自己的仓库里,然后发Pull Request请求到开源项目管理者,Review代码,若合适,则考虑合并到中心仓库。

司令官与副官工作流

这种模式,我认为是上述两种模式的综合。

Note: 这其实是《Pro Git》 这本书的章节,现已经合并到git官网,因V2中的图片有一个无法显示,中文版,我引用第一版。


引用原文:

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

Gitflow不是Git社区的官方推荐工作流。是的,不要被名字骗到,这不是Linux内核开发的工作流也不是Git开发的工作流。这是最早由Web developer Vincent Driessen和他所在的组织采用并总结出的一套工作流程。

这里是个小批注: 根据上文,Gitflow是自造词, Linux内核开发的工作流, 作者又一次把工作流和分支模型概念搞混淆。 我们权当说树枝式的分支模型吧。

我们说下Linux社区吧,我觉得关于Linux,我还是有些发言权的。我司是做工控领域的嵌入式Linux, 虽不在那个团队,但也是有所见闻,并且咨询过的。 就说最近比较火的树莓派,不错, 树莓派每年出货量的一半,大概100W套,是我司生产的。 大家看看它的分支:

Github - raspberrypi/linux , 看看有多少个活跃分支,看看rpi-3.18.y这些分支是不是最终被合并到主干上。

引用原文:

Gitflow也不是Github所推荐的工作流。Github对Gitflow里的某些部分有不同看法,他们利用简化的分支模型和Pull Request构建了适合自己的工作流Github Flow。 现在我要告诉你,Gitflow在企业软件开发中甚至不是一个最佳实践。ThoughtWorks Technology Radar在2011年7月刊,2015年1月刊里多次表明了Gitflow背后的feature branch模型在生产实践中的危害,又在最近一期2015年11月刊里专门将Gitflow列为不被推荐的技术。 为什么Gitflow有问题

作者一会说分支模型,一会又说Pull Request工作流。 关于GitHub Pull Request,是对集中式工作流的简化,本质还是集中式工作流,只是系统帮着简化了一些操作,比如:

  • Fork, 相当于首先clone到本地,然后在我的个人github空间创建一个同名的仓库,再push过去,而Github Fork点击下按钮就完成了
  • Pull Request, 相当于个人开发者修复了一个bug或完成了一个功能,然后push到自己的远端仓库,然后告诉Leader,我做完了这个功能,放到了哪里,那个分支,您Review下看看有没有问题,没问题请合并下。

引用原文:

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最明显的两个好处是:

各个feature之间的代码是隔离的,可以独立地开发、构建、测试; 当feature的开发周期长于release周期时,可以避免未完成的feature进入生产环境。 后面我们会看到,第一点所带来的伤害要大于其好处,第二点也可以通过其他的技术来实现。

作者的这一大段都在说分支,那么我说下我对分支的理解。

我们大家都知道,Git是分布式的,往大的方面说,其实分布式也可以理解为一个个中心仓库的分支,概念和branch极其的相似。就比如master分支,我们本身没有创建额外的分支,但是事实上存在了很多分支,比如:

  • blessed_repo_remote/master
  • blessed_repo_director_local/master
  • develop1_remote/master
  • develop1_local/master
  • develop2...

再谈git branch的概念:

《Pro Git》中提到,git branch仅仅是一个指针,创建和销毁一个branch的开销极其的少,也极其的容易。

$ git checkout -b new_branch

一句话,就可以在checkout的时候新出来一个branch.

而认为分支的精髓在于:

  • branch是一个个枝叶,他的生长变化不会影响到祖先枝干。
  • 而且祖先枝干可以有选择的最终向哪个方向(branch)发展,主动权在祖先枝干。

如果扩散到中心仓库和分布式的开发者本地,远端仓库,也是这个道理。主动权在中心仓库,Fork的分支可以提供路线,但是不能左右中心仓库的发展。

作者说 为什么开发一个新feature非得新开一个branch,而不是直接在develop上进行,难道就是为了……废弃掉未完成的feature时删除一个branch比较方便?, 对分支枝干,枝叶的模型完全没有理解,就是不希望这个新的feature去影响主干,难道团队里面都是精英,一点差错都不会出?如果feature开发的过程中,发现一个原来的bug怎么办? 新功能还没开发完,bug又急需解决? 告诉我怎么处理?

即便还是使用develop分支,因为git是分布式的,如果有多个人开发,那么就说明有多少个develop分支的副本分支,只要不把完成一半的东西push到总仓库,也还好。 背后其实您已经再用分支了。

章节之 - merge is merge

引用原文:

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这么简单的重构都可能面临大量冲突,团队就会倾向于少做重构甚至不做重构。最后代码的质量只能是每况愈差逐渐腐烂。

关于分支的应用,正如上文所说,创建一个分支没什么开销,不管是命令,还是图形界面都很容易。同样,删除也是。

我们新开一个分支,把功能做好,测试确认没问题,马上就合并到枝干上,把这个分支给删除了。这个分支,甚至都不会出现在中心仓库的分支里,升值自己的remote仓库里,仅仅在个人本地仓库出现,可能一天,或者半天我们就完成了一个merge迭代,把这个分支给删除了。

作者的举例,也是有很多问题,请看:

  • 关于feature开发的平均时间是一个月,合并到develop的例子,有几个地方有问题:
    • 为什么在开发feature的时候去修复bug? 到底是feature引发的bug还是原来的bug? 如果解决,会不会因为feature带来更多的问题? ==》 根据树木成长的模型,bug我们要追本溯源,找到到底是哪里的问题,然后再哪里,新起一个分支解决,解决完,测试无问题,即可合并到主干。Feature的分支,可以随时获取develop分支,已获得更稳定的基础代码。
    • 同样,体现软件开发工程中耦合的问题。软件开发中,我们清楚减少耦合,git应用中,我们同样需要。
  • 关于说merge冲突的例子:
    • 作者拿代码重构来举例,显然不是很合适。代码重构小的说是rename方法,大方面说是改接口,再往大的说是升级版本。简单说 1.0, 1.1都在不断迭代,然后作者说把 1.0, 1.1合并吧,这不搞笑吧。
    • 对冲突的理解,也有偏差。git merge什么时候会出现冲突:当出现两种截然不同的路子时候。比如原版本是A(向南走), 你提交的是B(向东走), 合并别人的是C(向北走),向东,向北,是两种截然不同的方向,git当然不知道选择谁? 解决冲突更多的是仲裁,相当于裁判员,这个是机器帮不了的,尽管是用svn,svn同样也会出现冲突,同样需要仲裁。

章节之 - 持续集成

引用原文:

持续集成

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

每个feature在一天内完成,然后集成回develop分支。这恐怕是不太可能的。况且如何每个feature如果能在一天内完成,我们为啥还专门开一个分支? 每个分支有自己独立的持续集成环境,在分支内进行持续集成。然而为每个环境准备单独的持续集成环境是需要额外的硬件资源和虚拟化能力的,假设这点没有问题,不同分支间如果不进行集成,仍然不算真正意义上的持续集成,到最后的big bang conflict总是无法避免。 每个分支有自己独立的持续集成环境,在分支内进行持续集成,同时每日将不同分支merge回develop分支进行集成。听起来很完美,不同分支间的代码也可以持续集成了。可发生了冲突、CI挂掉谁来修呢,也就是说我们还是得关心其他developer和其他团队的开发情况。不是说好了用feature branch就可以不管他们自己玩吗,那我们要feature branch还有什么用呢? 所以你会发现,在坚持持续集成实践的情况下,feature branch是一件非常矛盾的事情。持续集成在鼓励更加频繁的代码集成和交互,让冲突越早解决越好。feature branch的代码隔离策略却在尽可能推迟代码的集成。延迟集成所带来的恶果在软件开发的历史上已经出现过很多次了,每个团队自己写自己的代码是挺high,到最后不同团队进行联调集成的时候就傻眼了,经常出现写两个月代码,花一个月时间集成的情况,质量还无法保证。

关于持续集成, 因为我是嵌入式行业的,对互联网中真正的开发,了解的不多,但是还可以类比一下,互联网一天一发布,我们一个月已发布,周期不同而已。

这里的问题核心在于计划和执行。今天要发布的,有哪些:

  • 做哪些新功能,feature A, feature B
  • 修复多少bug, bug1, bug2

然后开发者去做,当然,结果可能有的feature开发完了,有的没有,bug同样。如果基于上述树形的分支来去做一个个feature和bug, 最后集成的时候,反而更从容些,比如某些功能没做完,那咱就不合并了,如果仅仅基于一个分支做,我怀疑下班前应该发布不了的,为什么,功能没做完,最新的代码是有问题的代码。

章节之 - 如果不用gitflow

引用原文:

如果不用Gitflow…

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

是的,所有的开发工作都在同一个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进行大胆的尝试。

作者又退回到了svn的流程,是好是坏咱不做评价。

引用原文:

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

这句话说的好。

引用原文:

[参考文献]

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

没怎么看。

小结

A successful Git branching model 提到的分支模型,从大局,从软件开发的生命周期出发的一种低耦合,可持续健康成长(类树木成长)的的模型,应该很科学才对。

另外,把分支模型,和Git的工作流混为一谈,也是不合理的,不管是分支,还是Fork, Pull Request的工作流, 都很科学。

本文只是自己的看法,和这些年来应用git的一些心得, 希望能给新的Git用户一个不错误的指引。 可能也有不对的地方,欢迎探讨。

另外,再次声明:绝无诋毁作者之意,也绝无侵权作者文章的意图,看到后,有不同观点,各抒己见。

共收到 38 条回复

果然没有头像的人发的东西不能看

#2楼 @hooopo 嗯,社区新人一个,这是第一次发帖,有何不妥当的地方,欢迎指教。 排版中,因为有大量引用,所以显得条理欠缺。 头像咱马上换个 :)

GitHubFlow 和 GitFlow 其实是很类似的结构,唯一差别就是 Fork 出来的 Branch 是在自己而不是公共的 Repo 里。 GitFlow 简单说就是做得有点过了。SmartGit 现在内置的 GitFlow 自带一个简化版,就是只使用 Feature Branch,轻量了很多,用起来也非常舒服。 另外这种模型本身也适合较短的开发周期,一个 Feature 应该在几天或者一两周内完成。持续集成测试保证了每次 merge 的时候都是可用的。对于 Ruby 这种周期短又有测试覆盖的环境来说非常合适。但是对于其他一些环境来说可能就不那么合适了。

我们针对git的合并做法就是谁合并,谁就要解决合并冲突问题。若是发起merge request/pull request时就有冲突,那么这个请求就review不过,自然不能合并。 还有就是自己在做feature的时候也要同步主线内容,要不然使用gitflow最后合并的时候就会造成很大的冲突,搞死自己。

写的有点乱,原作者的核心观点其实就是branch被滥用了,然后说明了滥用的地方,只要有效反驳这一点就可以言之成理,其它都是枝节

#6楼 @fsword 嗯,谢谢指教,写的确实乱,系统性不强。应该提出论点,论据即可

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自己的分支。

我觉得这倒不是滥用,这种代码松耦合正是git的好处。

#9楼 @liuzelei 这不是代码松耦合。代码松耦合是说如果你的模块划分合理,修改模块A不用关心模块B,二者可以独立地修改变化。而分支只是推迟了冲突解决,最终你的代码还是耦合在一起的。所以在原文里我提到分支是穷人版的模块架构,既没在本质上消除代码的耦合,又不能在运行时切换。

11楼 已删除

#10楼 @alex 所以long live分支要经常rebase dev branch,保持更新。

#12楼 @msg7086 不仅要经常rebase,还要经常往dev branch rebase或merge回去,要不然随着其他人的提交,相同一段代码可能要rebase很多遍。

我们团队在早期用 gitflow 时也觉得过于复杂,后来改用 github flow 就简单多了

为什么不用pull request进行协作,只要控制好测试流程和代码审核流程就没问题了,我们团队就是用pull request进行协作,文中提到的问题通通没遇上

#15楼 @zxzllyj pull request模型就是分支呀。fork一个codebase也好,clone一个codebase也好,都是分支。看你们发PR和合并PR的频率,如果频率太低攒一堆commit一块合并,上面提到的问题全都会遇到。

其实pull request的初衷是鼓励更多人参与到open source开发里。传统的open source模式,你做贡献要先从找issue提交补丁做起,每个补丁都是由开源软件的commiter/maintainer来review然后合并进去的。慢慢做的贡献多了被社区认可,成为commiter。再慢慢进入core team,可以参与设计方向的决策讨论。多少有点等级金字塔的感觉。

pull request是把软件的分支和分裂常态化了。不管你是高手还是菜鸟,都可以fork一个自己的分支,然后你自己的分支就由自己维护自己说了算啦。如果需要你可以将自己的改动发PR反馈给上游社区。这么做虽然会让软件和社区分裂碎片化,却能极大促进创新和功能的衍化。以前我想往emacs加入一个X、Y、Z feature需要RMS同意需要说服社区一大批用户,现在我直接fork一个XEmacs单干,建立我自己是社区。此消彼长,有时候上游社区会逐渐接受分支的反馈便得更好,有时候新的分支反而取代原有版本成为主流。开源社区也因为这种分裂和衍化而蓬勃发展。

从企业软件开发的角度看,因为不会出现无限分裂的情况,代码最终还是回归一个主干,还是更接近传统的open source模式(或者说传统open source模式是从传统软件开发沿袭而来)。pull request除了改善code review,这种协作模式下起到的作用有限。所以说技术的应用还要看上下文。

#16楼 @alex 你说的低频率导致的问题确实存在,我们的解决办法就是定期合并主分支! 当提交的pr与主分支存在冲突时,就合并主分支解决冲突,保证在合并前能够毫不冲突的被主分支合并

#13楼 @alex 这就涉及到软件架构了。架构太差导致一个模块的变动会导致另一个模块冲突,就没法玩了。

老实说原文说的有道理多了

Gitflow过于复杂,过过过过过过过过过过过于复杂

在一个敏捷团队中,如果按照Gitflow的流程,团队中每个成员开发每个feature的时候都拉一个branch单独开发,会出现两个问题:

  1. 成员A不知道成员B写了什么代码,除非等到成员B开发完这个feature,最后把代码merge到develop分支上,成员A才能看见。这会导致团队成员代码交流不及时。如果他们都工作在一个分支上,并且小步提交,成员之间都可以很快知道对方干了什么。
  2. 持续集成(CI)是敏捷开发中一个很好的实践。理想状态是:每当你提交一次代码,你开发的功能持续集成到系统。你可以通过持续集成马上判断你写的代码对整个系统的影响。如果使用GitFlow,CI只能监控集成develop分支。你在feature branch上的commit无法及时的集成到系统中。只有当你的feature branch合并到develop分支的时候,你才能知道你写的代码对整个系统造成的影响。

最近工作中在用GitFlow,感受就是GitFlow把简单的问题搞得过于复杂了。Trunk based development才是简单又好用的团队工作方式。

我在原帖下面回复了,不过看这里讨论比较多,还是放到这里来吧:

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

#23楼 @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代码隔离为目的创建分支。

#24楼 @alex 可是在 truck 上进行开发的话,如果我在本地开始开发一个 new feature,由于要保证 master 可部署,所以我在该 feature 开发完成之前也完全无法 push 到 master 不是么。那么,它如何能防止 long lived branch 的产生呢?

#25楼 @steveltn 代码还是正常push,使用feature toggle将未完成的feature turn off,master还是可以部署。

#26楼 @alex 不是所有 feature 都可以 toggle 啊。

現在業界支持 feature toggle 的人相當多,我認爲各有優點,應該根據團隊自身實際情況選擇相應的開發模型。

#27楼 @steveltn 能举个不能被toggle的feature的例子吗?我承认有些改动feature toggle帮不上忙,需要branch by abstraction等其他手段。但大部分feature都可以使用feature toggle。

新feature的toggle、类库的feature flag这些很成熟的就不提了。我们项目曾经有一次UI redesign的大改版,从UI v2到UI v3的整个过程都是用feature toggle控制渐进开发的(v1改v2的时候项目还没上线)。整个过程始终基于master进行开发,小步提交和持续集成,没有影响到终端用户。由我们组负责的UI redesign开发周期有一个多月,于此同时其他几个组在开发其他的feature。对于UI这种影响整个应用横切层的大规模改动,如果不小步增量修改持续集成,我没办法想象怎么把一个big bang change merge过去而不破坏其他功能。

#29楼 @alex 比如升级某个 gem 的版本?

另一个问题是,本来就用版本管理工具就能解决的问题,需要污染代码逻辑来解决,得不偿失。用 feature branch 的话,经常 rebase master 就可以了,merge 时并不会有大冲突。

#30楼 @steveltn 你是说升级依赖的库或框架?没有太大的break change一两天就能搞定了吧直接提交呗。如果说有很大变更,原文引用的文献里有一篇是Jez Humble讲我们用branch by abstraction把一个项目的ORM整个换掉。

用feature branch的团队总爱说经常rebase master就没事了,事实是如果feature branch存在的时间较长,只rebase master是不够的,因为还有若干并行开发的branch没有被merge回master。假设每个feature的开发周期1个月左右,可能第三周结束的时候另一个team把他们的feature branch合并回master,你们rebase的时候发现这三周的工作所依赖的代码全部被改掉了。而如果集成能更早发生,你们也许在第一周的时候就能基于新的代码继续工作。

#31楼 @alex 嗯,这个 scenario 确实有道理。我再仔细想想。

覺得「GitFlow」是 @alex 自創詞的樓主,可以看看 bitbucket 教程,裏面有提到這個術語。

GitFlow确实不好,太复杂.多数对git了解不够深入的开发人员玩不转.所以还是用Trunk based development. 当然如果个个都是git高手,那还是可以考虑用GitFlow的,每个feature,每个hotfix的过程都能保留下来,简单来说就是可以做出完美的历史记录.

#34楼 @qinshulei 无关Git技巧,即使对于Github的开发团队来说,Gitflow这套流程也过于复杂了。

有点意思, 有深度

以我自己的开发经验,我觉得原文说的有道理的。以前某个开发周期过长的时候确实 merge 困难

#22楼 @slim 你认为GitFlow把简单的问题搞复杂了,那是基于你的团队还很小。当团队不断壮大,项目代码量不断上升,GitFlow是很好的并行开发实践了。

列表项1表明Git服务的局限性,但是可以用Jira等项目协作工具来弥补,Git或Git服务本身也许不需要考虑这些。 列表项2只能说明所用的CI服务的限制性,我司的实践是feature分支也会自动被CI服务器构建,所以这点于GitFlow无关。

为了回复楼主注册了个账户,原文中提到的gitflow遇到最主要的问题其实就是feature分支持续时间过长,导致集成延迟的问题,而楼主写了这么多还是没能解决这个问题,而这个问题是使用gitflow基本都会出现的,不知楼主现在怎么认为呢?

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册