Git 还是 SVN?Nuance Healthcare 如何选择 Git 分支模型

这是一篇由 Nuance Healthcare 的 Matt Shelton 撰写的客座文章,其中讲述了他的团队从 Subversion 迁移到 Git 的故事,并说明了这样做的原因以及在此过程中遇到的问题。

背景

我的团队在 Nuance 医疗保健部门工作。我们在地理上分布在美国东海岸的几个办公室和住宅,以及浦那的一个办公室中。我们开发 Java Web 服务,为医疗保健市场提供 NLP[1] 解决方案。

在大多数情况下,我们的服务消费者是其他医疗保健软件公司(包括我们自己),例如 EHR 供应商和医疗分析公司。我们确实直接向医院销售部分产品,这些应用的最终用户包括从医生到医疗收费人员等不同人群。像我们这样的“普通人”从来没有使用过过我的团队开发的软件。

我们的团队在应用生命周期管理产品组合方面已经多次遇到障碍。我们最初混合了 Rally Enterprise 和 Seapine TestTrack Pro,在 Rational Team Concert 辛勤工作大约 14 个月,最后完全迁移到了 Atlassian 堆栈(JiraConfluenceBambooCrucibleBitbucket)。从历史上看,我们使用 Subversion 1.4/1.5 和具有准普通主干/分支/标记结构的 SCM。我们从一开始就使用 maven 来管理我们的构建项目和依赖关系,不久前从 Jenkins 切换到 Bamboo 进行持续集成 (CI),以便利用与 Jira 的更紧密集成及其灵活的构建和部署支持人员功能。出于某些原因[2],我们(现在)使用的所有东西都在防火墙后面。

Git 还是 SVN?

我们为四个产品系列中的大约十种单独的产品提供支持,这些产品的负责人一直在争夺优先顺序和时机。我们的工作需求量很高,这真是太好了,绝不是抱怨,但它也需要以奇怪的节奏削减版本,并且需要在冲刺[3]中间改变方向。

我们的开发流程有时确实让人望而却步。我的团队经常进行的对话内容是这样的:

:我们需要立即发布 1.8.0 到 QA 进行回归测试,这样客户 foo 可以在下周使用测试版。开发:我研究的 ABC-123 在主干中,还没完成。:Foo 不需要 ABC-123。我们可以把它放在下一个版本中。开发:但我已经研究了好几个星期了,没有明确的分支位置可以削减版本。:你需要手动拉取所有变更,我给你大约两个小时的时间,不然 QA 无法按时完成。

我知道,我听起来像个混蛋!我从来没有想过要成为一个混蛋,当然,我表达地有点夸张,但我们确实必须弄清楚如何暂时从某个地方取出这里的代码,这样我们才能剪出一个版本,然后马上把它放回去供下一个版本使用[4]。而且这种情况一直都有。

我知道现在有些人可能在想“子版本支持分支,Matt...”。确实如此,我们偶尔会将它们与 SVN 1.4 和 1.5 一起使用。在 SVN 中,分支是一项不错的操作,合并可能很痛苦。随着 SVN 的成熟,它肯定变得更好了。但是我们知道还有更好的选择,所以当出现 SCN 或 git 的问题时,我们就开始购买 Git 了。

旁注:我们简要查看了最新的 SVN(当时为 1.8),看看它是否足够强大,是否足以解决我们的问题,但并不完全满意。我们有个同行小组拥有大型的 Perforce 设置,有很多我们需要的东西,但我真的无法承受许可成本。我们也了解过 Mercurial,但最终,现有团队对 Git 的使用率告诉我们这是正确的方向。

更棒的是:Atlassian 的工具真的很适合使用 Git 的团队。其他 SCM 运行良好,我们的 SVN 集成足以将我们与给定用户故事的变更联系起来。但是,改用 Bitbucket[5] 的团队的集成能力在 Jira Software 界面和开发体验中既强大又更自然,Bamboo 也是如此。

知道了这一点,再加上 2013 年峰会上看到的一些精彩演示,我强烈鼓励团队去用。没有人反对,并且我们已经有了进行变更的许可证。

选择 Git 分支模型

在决定进行此变更后,我们遇到的第一个挑战是决定为我们的团队实现哪种 Git 分支模型。Atlassian 的 Git 微型网站以及 2013 年峰会的精彩演示更详细地解释了什么是分支模型。简单来说,它描述了您将如何使用 git 中的分支来支持您的开发工作流程。

在 SVN 中,我们有一个分支模型,我认为这个模型就是“一旦用了就会爱上”:

  • 最新的代码在 trunk 中。基于主干发布的版本号格式为 A.B.0-{build}

  • 如果需要对基于主干的版本进行修复(例如,1.2.0-64 版本存在一个缺陷),则需要创建一个分支,并基于该分支发布版本号格式为 A.B.C-{build} 的版本,其中 C 值随每次发布递增。对于给定的 A.B 来说,这些分支可能永远不存在,我们甚至可以有多个分支。

  • 我们还会在 tags 目录中标记每个版本。

关于版本的旁注 很多年前,当我刚开始管理开发团队时,我们的发布工程师有一个版本控制系统...我该怎么说?...确实不直观。本质上来说,每个版本都是前一个版本 (A.B.n) 的补丁,不考虑补丁的起源。要弄清楚某些东西来自哪里,在几乎所有情况下,版本顺序都需要您查看 svn log。我们把树印在墙上作为参考。此外,我们面向公众的版本号往往是 3.0、3.1、3.5、4.0 之类的数字,或者基本上是客户可以预见的。但请记住,我的团队开发的 Web 服务并不是固定产品。我们的 API 是合约。几年前,我告诉高管说,我的团队构建的版本以及发布的版本都将遵守语义版本控制规则。好几次,我都需要在高管面前坚守自己的立场,但现在大家都明白规则为何如此以及规则的内容,而且我们一直都没有后悔。合作伙伴很欣赏这种清晰度。

我之前提到过一个问题,那就是我们要开发一个版本(比方说 1.2.0),随着发布日期的临近,我们还有一项功能仍在开发中。我们需要把那个代码拉取出来,剪出我们的发行版,分支到 branches/1.2.1,然后把代码合并回去,同时不会导致硬盘崩溃[6]。

从共享主干中单独删除整个功能很痛苦。不得不这样做时,每个人都觉得生不如死。svn blame 也可能有用,功能强大的差异工具也是如此,但是使用起来还是很麻烦。我经常会觉得是自己的错,觉得是我的计划不周导致我们在发布版本前没能把事情都安排好[7]。我的团队已经花了够长的时间来处理这个问题。

有时候我们会为了避免痛苦而过度纠正,会要求开发人员暂停几天(如果愿意的话,还会冻结虚拟代码),这样我们就不会在发布之前污染主干。

所以我们知道至少需要功能分支。有一个简单的 Git 分支模型适用:主分支用于 prod 中的内容,对每个功能、错误等使用功能分支。团队必须管理合并顺序,以确保在主分支中发布的内容是发布版本时应该发布的内容。从本质上讲,这和我们以前做的一样,只是功能隔离更好,但我们希望通过我们的力量获得自由。

在我们的环境中,我们经常需要在生产环境中保留几个版本,并且可能需要修复旧版本中的缺陷,这些版本可能比我们现在正在开发的版本旧 2-3 个小版本。因此,除了功能分支之外,我们还需要某种版本分支或类似的分支来修复以前版本中的问题。他们在长时间运行的支持分支中进行修复,然后将它们合并到分支流中,这样修复就会进入所有支持流。

他们的模型看起来真的很不错,我们与这个模型进行了几次原型交互,看看它是否符合我们的需求。对他们来说,“杀手级应用”就是他们对开发分支的修复滚动合并。虽然我们喜欢这个概念,但每次尝试时,我们都会遇到一个或另一个与 maven 依赖关系有关的问题。而且,通常情况下,我们不能保证我们要将工作从一个版本直接合并到另一个版本。在某些情况下,我们需要在版本之间以略有不同的方式实现相同的修复,因此无法进行直接合并。

团队有一些成员强烈推荐这种 "git-flow" 变体。Git-Flow 是一套分支命名惯例和合并指南,由 Vincent Driessen 撰写。这对团队来说感觉很自然,我们喜欢这个结构,因为有了它,我们就不用问许多关于“需要做 x 时我该怎么做?”的问题。答案通常非常明显。与其解释什么是 git-flow,不如在 Atlassian 的教程中阅读更多关于它的信息。

我们在 git-flow 上遇到的唯一问题是如何处理那些长期运行的生产版本。由于主分支不断向前发展,我们无法使用 git-flow 修补程序工作流程修复先前版本中的错误。另一方面,我们并不总是想要一个支持分支。

大多数情况下,修补程序(仅修补生产中的最新版本)就足够了,只有当我们需要进一步研究时,或者出于某种原因需要保持兼容性时,才会提供支持。我们进一步分析了后一个用例,并提出了选择使用支持分支而不是修补程序和次要版本升级的标准:

1. 这段代码不能简单地合并回开发中。

2. 合作伙伴/客户无法处理最新版本附带的界面变更。

3. 存在无法更改的内部依赖关系。[8]

两个 git-flow 扩展包[9] 都支持支持分支概念,该概念不是 git-flow 最初草稿的一部分,但已经足够流行,值得纳入。

Git-Flow 提供了我们喜欢的工作流程,并提供了我们需要的工具支持。在下一篇文章中,我将介绍当我们在用来表示开发流程的 POC 项目中实际尝试使用它时发生了什么。算是...一次学习经历!

脚注

[1]:自然语言处理。我们可以读懂您的想法。(不,不完全是。

[2]:Atlassian 的云产品有很多吸引人的地方,但我们暂时需要侧重于服务器和数据。虽然我们个人不需要对 PHI 数据做太多处理,但我们的软件需要,并且尽可能确保其安全非常重要。

[3]:嘘...别告诉 Ken Schwaber。

[4]:反正可能只是过几天。

[5]:前身为 Stash。您好,Atlassian 秋季品牌重塑!

[6]:我知道我们可以随时将其从之前的提交中取出。我是在开玩笑。

[7]:通常情况并非如此,一般是因为别人的时间表提前,我们必须迅速做出反应。

[8]:这是我无法在自己的博客上介绍的事情之一。相信我就行了,“有原因”。

[9]:Vincent Driessen 的原始包已不再维护。但是,新的克隆会定期更新。

为您推荐

Bitbucket 博客

DevOps 学习路径

了解有关 Git 的更多信息

在此中心查找更多 Git 指南和资源。