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

ahnniu · 2016年03月11日 · 最后由 GDoom 回复于 2019年07月06日 · 18113 次阅读
本帖已被管理员设置为精华贴

昨天,在翻看自己 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 用户一个不错误的指引。可能也有不对的地方,欢迎探讨。

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

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

#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 基本都会出现的,不知楼主现在怎么认为呢?

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