当前位置: 首页 > 科技观察

Git分支模式如何选择?

时间:2023-03-19 10:07:13 科技观察

编写代码是软件开发和交付过程的起点,在线发布是开发工作的终点。代码分支模式贯穿开发、集成、发布全过程,是工程师最亲密的伙伴。那么如何根据自己的业务特点和团队规模选择合适的分支模式呢?本文分享了几种主流Git分支模型的流程和特点,并给出了选择建议。分支的目的是隔离,但是多一个分支也意味着维护成本的增加。我们可以从开发分支和发布分支的数量上做一个简单的组合,即:主干开发,主干发布。分支开发,主干发布。主线开发,分支发布。分支开发,分支发布。想象两种不同的场景:如果一个软件只有一个开发者,只需要一个发布版本,他需要什么样的分支模式?如果一个软件有10个开发人员,需要支持多个版本,那么他们需要什么样的分支模式呢?一个好的分支模式可以大大提高软件开发、集成和发布的效率。选择什么样的分支策略是每个开发团队在开始工作时首先面临的问题。那么,什么样的分支模式适合我们呢?在回答这个问题之前,我们先来看看几种常见的分支模式。主流的分支模式常见的分支模式有TBD(TrunkDevelopmentMode)、Git-FlowMode、Github-FlowMode和Gitlab-FlowMode。TBD(TrunkDevelopmentMode)是一种所有开发人员只在一个开发分支(即主干)上进行协作的模式。这种模式不允许新建长期存在的开发分支,只保留主干分支用于开发。开展协作。因为没有其他长期分离的开发分支,所以任何代码改动都会不断更新到主干,一定程度上避免了合并代码带来的麻烦。同时,在这种开发模式下,建议采用发布分支策略,根据软件版本的发布节奏拉出发布分支。在TBD模式下,所有修改都在主干上进行,即使是缺陷。缺陷修改后,cherrypick到release分支。其特点概括如下:开发分支只有一个,即主分支。所有更改都发生在主分支上。发布可以从主干中拉取发布分支。trunk上的修复需要根据缺陷的修复策略来判断是否cherrypick到对应版本的release分支。因为团队共享一个开发分支,在开发分支上进行集成验证,而每次代码提交都会触发集成验证,这就要求每次代码变更都可以在主干上快速验证,以确定是否接受下一次代码变更(每个代码changeshouldbebasedonpreviousstableversion),为了保证backbone始终处于工作状态,这就要求:每一个change都应该很小,这样在验证过程中可以控制范围。要快速完成验证,这就需要一个比较完备的自动化检测验证机制。因此,主干开发模型可以说是持续集成的关键使能器。骨干开发模型非常有利于持续集成,可以根据稳定性和骨干基线随时发布,实现持续交付。但这些都是基于团队成熟的协作能力和相对成熟的工程支持,快速完成主干变更提交的编译、检查和验证;同时,由于发布分支的做法,需要理清产品版本、分支、部署场景之间的对应关系,避免发布分支混乱,以及各分支上缺陷修改的修复策略。因为骨干网开发要求每次提交的变更要小,并且要快速验证,以保证骨干网处于可发布状态。对于开发过程中的一些特性,比如每次变更提交,并不代表完成了完整的特性。为了隔离“功能半成品”对主体的影响,一般采用功能开关(FeatureToggle)进行隔离。即频繁的代码变更提交可以先集成和验证,但是从发布的角度来说,先通过(FeatureToggle)隐藏相关特性,只有在特性完成后,才打开开关,特性被完全揭示。但是引入特性开关也不是没有代价的,因为特性开关是配置,本质上和我们经常使用的宏定义(#if#else)是一样的。本质上,它们也是代码的一个分支。特性开关的使用在一定程度上让你的代码更加脆弱。因此,特性开关的使用是基于良好的代码设计。为了弥补某个特性的开发需求,比如特性切换,现在在软件开发中,越来越多的团队共同完成某个特性,特性开发的分支模式应运而生,这就是特性分支开发模型,最具代表性的是Git-Flow。Git-FlowGit-Flow是一种解决多个不同功能之间并行开发需求的工作方式。当开始开发功能时,会从主干中拉出一个功能分支。该功能的所有开发工作都发生在该功能分支上。当功能的工作完成后,功能分支被合并回代码中。在主要路径上并准备发布。Git-Flow有以下分支:特性分支:开发者开发特性的分支。develop分支:集成已开发功能的分支。release分支:负责发布版本的分支。hotfix分支:修改在线缺陷的分支。master:拥有最新发布版本基线的分支。每个特性都有自己的开发分支,即特性分支。当一个开发者需要在两个特性上工作时,他需要做的就是通过checkout命令在两个分支之间切换。这样做的目的是为了防止两个特性开发工作在开发过程中相互干扰。在特性开发过程中,需要针对该特性单独进行验证。feature验证通过后,合并到一个称为develop分支的集成分支(大部分时候类似于master分支)对整个软件进行验证。develop分支始终保持为最新的未发布版本。develop分支的代码验证发布后,从develop分支中单独拉取release分支进行发布。当release分支被拉出发布时,如果发现缺陷,缺陷的修复发生在release分支上,缺陷修改不断同步到develop分支。当release分支发布时,其最终版本的代码会再次分别同步到develop分支和master主干。我们可以发现工作版本的基线总是保存在master上。develop分支保证开发集成中的最新版本。Git-Flow引入了一个名为hotfix的分支,专门用于修复在线缺陷。当缺陷被修复后,会被集成到develop分支并同步到master。其实我们可以理解hotfix是一个特殊的feature分支,但是它的变更提交在集成到develop分支的同时需要同步到master。是不是觉得这个模式很专业(fu)专业(zha),那我们就通过developer做一个featuredevelopment,走happypath:developer接到一个开发需求,从develop分支拉一个feature分支。大家完成自己本地的开发工作,完成本地的验证,将代码提交到特性分支。基于特性分支进行验证,不断合并新的开发代码。完成feature的开发,并验证feature分支无误,将feature分支的代码合并到develop分支。集成验证在develop分支上进行(这里可能会和其他合并的特性分支一起验证)。集成验证完成后,将删除该特性分支。当develop分支是成熟的release版本,比如完整的测试和bug修复,拉出release分支发布。发布完成后,将release分支合并到develop和master(master总是保存最新发布的代码),删除release分支。hotfix流程如下:如果发布后发现缺陷,则基于master拉出一个hotfix分支。修改并验证hotfix中的问题。错误修复合并到开发和主控中。删除修补程序分支。Git-Flow的分支模式提供了比较齐全的分支种类,覆盖了软件开发过程中的大部分场景,以至于很长一段时间,人们都认为这就是标准的Git分支模式(因为从它的名字看,也很容易产生这样的幻觉)。但是,Git-Flow也有一些明显的问题,比如:分支太多,而且每种分支都有特定且有限的用途,开发者很难记住分支是干什么用的。整个分支模型过于复杂,大大超出了大部分团队和项目的需求。特性分支生命周期长导致的合并冲突。如果一个feature的feature分支生命周期太长,它会更多的从develop分支发散,这样当feature集成到develop时,潜在的代码冲突将是集成的噩梦。像develop分支,感觉它存在的意义不是太大。完全可以替代通过master整合的作用。引入develop分支进行变更提交的整合,分支模式会变得更加复杂;同样,如果你取消了develop分支,那么hotfix分支的意义就没有必要了,因为此时hotfix分支和feature分支没有区别。那么,有没有一种分支模型,既包括开发任务与主线的隔离,又比Git-Flow更轻量级呢?我认为真正的解决方案本质上应该是极其简单的。这里,不得不介绍另一种分支模式,叫做GitHub-Flow。GitHub-Flow在GitHub-Flow上,第一步是Git-Flow中没有引入release分支。对于GitHub-Flow,发布应该是连续的,当发布准备好后,就可以部署了。同样,对于hotfix的处理,GitHub-Flow认为hotfix与那些小的feature修改没有区别,其处理方式应该是类似的。GitHub-Flow中的整体流程是:master分支上的所有代码都应该是最新的可部署的工作版本。如果想做新的工作,从master分支拉一个新的分支,并在工作任务之后明确命名,比如“new-scheduling-strategy”。尽可能频繁地向本地分支提交代码变更,同时尽可能频繁地同步到服务器上同名的分支。当您准备好将代码合并到主干分支时,通过发起合并请求提交代码审查。通过codereview后,或者同时需要将该分支部署到测试环境进行验证。审核通过后,将代码合并到master主干分支,立即上线生产。与Git-Flow相比,GitHub-Flow有一个明显的优势——简单。另一个好处是持续部署的需求,尽快发现master分支的问题,通过回滚等机制快速恢复。将所有内容合并到master分支并进行部署通常意味着您可以最大限度地减少未发布代码的数量,这是精益开发和持续交付所提倡的最佳实践。部署本来是一件很繁琐的事情,但是因为需要经常做,所以我们很容易把这样的事情简单化,达到持续交付的目的。虽然GitHub-Flow简化了Git-Flow的分支模型,但是关于部署、环境和发布的分支模型仍然有很多未解的问题。因此,我们希望通过GitLab-Flow为这些问题提供更多的参考。GitLab-Flow与GitHub-Flow相比,GitLab-Flow在开发端区别不大,只是将pullrequest改为mergerequest,mergerequest的用法与pullrequest类似,可以作为代码使用审查、获取一种反馈的交流形式。最大的区别体现在发布端,引入了生产环境对应的production分支和预发布环境对应的pre-production分支(如果有预发布环境的话)。这样master分支反映了集成环境部署的代码,pre-production分支反映了pre-release环境部署的代码,production分支反映了生产环境部署的最新代码。当一个特性开发完成后,提交合并请求,将该特性开发的代码合并到master中,部署到集成环境中进行验证;验证通过后,提交mergereqeust,将master合并到预生产分支,并部署到预发布环境,对预发布环境进行验证;当预发布环境验证成功后,提交合并请求,将预生产分支上的代码合并到生产分支中。除了上述将主干发布合并到下游并按环境顺序部署发布的过程。GitLab-Flow还支持不同版本的release分支,即不同的版本会从master拉取release分支,不同的release分支会以预生产分支和生产分支的形式发布。通过上面的介绍,我们发现GitLab-Flow在发布端做了更多的工作。同样的,GitLab-Flow也强烈依赖于GitLab工具,因此GitLab-Flow也与GitLab中的Issue系统有很好的集成。在其推荐的工作模式下,每创建一个新的feature分支,都是从一个issue发起的,即建立issue和feature开发分支之间的映射关系。prosvscons因此,如果还是按照开发和发布分支的数量来分类,以上分支模式属于:TBD应该是trunkdevelopment,可以是branchrelease也可以是trunkrelease。Git-Flow应该是分支开发,分支发布。GitHub-Flow应该是分支开发,主干发布。GitLab-Flow支持分支开发,但支持主干发布和分支发布。了解了这些常见的分支模式后,我们就可以在工作中根据自己的业务特点和团队规模选择合适的做法。没有绝对好的模式,只有适合的模式。根据团队自身和项目的特点,在选择最合适的分支实践时应该考虑什么?选择正确的做法首先是项目的发布周期。如果你的发布周期很长,Git-Flow是最好的选择。Git-Flow可以很好的解决新特性开发、版本发布、生产系统维护等问题;如果发布周期短,TBD和GitHub-Flow都是不错的选择。GitHub-flow具有集成的拉取请求和代码审查功能。如果项目已经使用了GitHub,GitHub-Flow是最佳选择。GitHub-Flow和TBD对持续集成、自动化测试等基础设施的要求比较高。如果相关基础设施不完善,不推荐使用。另外,从需要发布的数量来看:要支持一个产品的多个发布,使用Git-Flow。使用GitHub-Flow或TBD支持简单产品的单一发布。要支持复杂产品的单一发布,请使用GitLab-Flow。如果你发现现有的主流分支模式都不能满足你的要求,那么我们可以定义自己的分支模式。比如我们有一个团队是基于主干进行开发的,所以我们定义了春夏秋冬分支模式。春天用“春枝”,夏天用“夏枝”……我个人很喜欢,原因有二,好玩是不是?参考[1]待定:https://trunkbaseddevelopment.com/[2]一个成功的Git分支模型:https://nvie.com/posts/a-successful-git-branching-model/[3]]使用Git学习版本控制:https://www.git-tower.com/learn/git/ebook/cn/command-line/advanced-topics/git-flow[4]抽象的分支模型和最佳实践-设计版本控制:https://projekt202.com/blog/2018/branching-models-and-abstract[5]了解GitHub-Flow:https://guides.github.com/introduction/flow/[6]GitHubFlow:http://scottchacon.com/2011/08/31/github-flow.html[7]GitLabFlow介绍:https://docs.gitlab.com/ee/workflow/gitlab_flow.html