GitLab Flow - 合并/分支/发布

翻译自:https://docs.gitlab.com/ee/workflow/gitlab_flow.html
GitLab Flow

引言

使用Git的版本管理使分支和合并比旧版本管理系统(如SVN)容易得多。它允许各种分支策略和工作流程。几乎所有这些都是对git之前使用的方法的改进。但是许多组织止步于一个没有完整定义的、过于复杂或没有集成Issue追踪系统的工作流。因此,我们提出了GitLab Flow作为明确定义的一套最佳做法。它结合了功能驱动开发和使用Issue追踪功能分支

许多组织从其他版本空置系统转到Git会经常发现难以开发一个有效的工作流程。
来自其他版本控制系统的组织经常发现很难开发有效的工作流程。本文将介绍Git Flow与Issue追踪系统集成在一起的GitLab Flow。它提供了一个简单,透明和有效的方式来进行Git工作。

在这里插入图片描述

要使用git,你需要习惯一件事,那就是每当你提交一个和同事共享的版本时,都要先完成三个步骤。而大多数版本控制系统只需完成一步:把工作的备份提交到一个共享的服务器。在git中,你首先要把文件从文件拷贝添加到缓存区中,之后再把他们提交到本地库中,最后把他们推送到远程的共享仓库中。熟悉了这三个步骤后,分支模型就是接下来要面对的问题。

在这里插入图片描述

由于很多刚开始使用Git的组织都没有约定如何处理,所以很快就会变得混乱。他们遇到的最大的问题是许多长期运行的分支都包含部分变化。人们很难弄清楚他们应该在哪个分支开发或部署到生产环境中。 对这个问题经常的应对是采用标准化模式,例如 Git FlowGitHub Flow。我们认为仍有改进的余地,并将详细说明我们称之为 GitLab Flow的一套做法。

Git Flow 和它带来的问题

在这里插入图片描述

Git Flow比较早的使用的了git分支,并且已经获取了很多的关注。Git Flow提倡一个master分支和一个develop分支,同时也包括了feature分支、release分支、hotfix分支。开发工作在develop分支进行,然后提交到release分支,最后合并到master分支。Git Flow很标准但是使用很复杂,这导致了两个问题。

第一个问题是程序员必须使用develop分支来开发,而不是master分支。master分支被用来作为线上环境的分支。而作为惯例,master分支一般是默认分支,别的分支都来自master或者是最后合并到master。由于大部分工具自动的把master作为默认分支,而且git默认显示master分支,开发时不停切换分支是很麻烦的。

第二个问题是引入了hotfixrelease分支,这些分支对于大部分开发者来说都是没有必要的。现在大部分机构使用持续交付,这意味着你的默认分支最后会被部署。hotfixrelease分支应该避免他们自身引进的各种规范,例如合并到release分支。尽管一些工具解决了这些问题,但是这同样使问题变得复杂。在期间,开发者很可能会犯错,例如修改的代码直接合并到了master分支,却没有合并到develop分支。这些问题的最根本原因是Git Flow程对于大多数人来说太复杂了。

GitHub Flow 更简单的替代选择

在这里插入图片描述
Git Flow的一个更简单的替换方案是GitHub Flow。它只有一个feature分支和一个master分支,简单而干净,很多机构成功的使用这种方案。Atlassian推荐了一种类似的方案。合并代码到master分支并且发布,那么你应该尽量减小每次提交的代码量,坚持依赖和持续集成这些好的习惯。但是这种工作流中仍然有很多问题没有解决,例如部署、环境、发布和issue的管理。在GitLab Flow中,我们使用其他的规则来解决提到的这些问题。

GitLab Flow 中的生产分支

在这里插入图片描述
GitHub Flow 认为你可以通过合并feature分支直接把代码部署到线上。对于SaaS应用,这是可能的,但是在很多情况下并不是这样子的。一种情况是你无法控制准确的发布时间,例如IOS应用需要通过苹果的审核,或者是每天发布的时间是固定的,但是你merge的时间并不一定是那个时间。在这些例子中,你需要创建一个production分支来放置发布的代码。你可以通过合并masterproduction分支来发布一个新的版本。假如你想知道线上的版本包含了哪些代码,只需要查看production分支即可。而且大概的发版时间也可以从merge的信息中看到。假如你是自动部署production分支的,那么这个时间就很准确。假如你想记录更精确的时间,可以通过写脚本来给每次部署打tag。

GitLab Flow 的环境分支

在这里插入图片描述

有一个环境可以自动升级到master分支是一个好的选择。在这种情况下,环境的名字可能和分支名不同。假设你有一个staging环境,一个preproduction环境,和一个production环境。在这种情况下master部署在staging环境。当有人想部署到preproduction环境,他们会创建一个从master分支到preproduction分支的merge request。而且上线代码通过合并preproduction分支到production分支。这种下行(downstream)的工作流保证了所有代码都在环境中测试过。如果你需要cherry-pick一个hotfix,常见的是在feature分支中开发,然后发起merge request合并到master分支,先不要删除feature分支。如果master分支通过ci并且运行正常,就可以合并到其它分支。如果需要更多的手工测试,你可以发送合并请求从feature分支到downstream分支。一个environment分支的极端情况是为每一个feature分支建立一个环境,正如Teatro所做的

GitLab Flow 的发布分支

在这里插入图片描述
在很少的情况下你需要使用release分支对外界发布软件。在这种情况下,每个分支都会包含一个版本号(2-3-stable, 2-4-stable, 等等)。这种stable分支都会从master分支起始,并且要尽可能晚的创建。创建的越晚,你就会越可能的减少bug fix的合并。在release分支发布后,只有严重的bug修复才加到release分支中。如果可能的话,这种bug的修改首先应合并到master,然后cherry pickrelease分支。这样子,你就不会忘记把修复bug的代码cherry pickmaster,从而防止其他分支也出这个bug。这被称为upstream first上游优先策略,也被GoogleRed Hat使用。每次release分支修改bug之后,都要先设置tag,然后增加版本号(遵从语义化版本号)。有些项目也有stable分支用来指向最新发行版本的commit,这种情况下一般没有production分支(或Git Flowmaster 分支)。

GitLab Flow中的Merge/pull requests操作

在这里插入图片描述
mergepull都在git中创建,然后指向一个人,由此人合并两个分支。像GitHubBitbucket这样的工具会选择pull request的名字,因为第一个手动的操作是pull feature分支。像 GitLabGitorious这样的工具会选择merge request的名字,因为对被指向的人来说这是最后一个操作。在这篇文章中,我们会称这个操作为merge request

假如你在一个feature分支工作比较长时间,最好是向其他人实时的分享你的工作内容。具体操作为:进行merge request而不指向任何人。这意味着你的代码并没有准备好做merge request,但是欢迎大家来提意见。你的团队成员可以对这个merge request的整体或指定行提出意见。merge requests作为code review工具来使用,并不需要Gerritreviewboard。假如review时发现了一些问题或者bug,一般会由写代码的人来push一个fix commit。当新的commit push到这个分支后,merge request的代码也会自动更新。

当你觉得合适了,就把merge request指定给熟悉你正在做的事的人,提醒他来查看代码给你反馈。如果别人觉得你的代码不太好,他可以直接关掉merge request

在GitLab中,保护长时间存在的分支是很常见的,这是为了防止其他开发者修改这个分支代码。因此假如你想把代码合并到一个受保护的分支,那么你可以把merge request指定给对这个分支有master权限的人。

GitLab Flow如何处理issue

在这里插入图片描述

GitLab Flow可以使代码和issue之间的关系更加清晰。

代码的任何修改都应该开始于一个目标明确的issue。任何代码修改都应是有原因的,可以通知到团队中的每个人,并帮助开发人员保持feature分支的范围足够小。在GitLab Flow中,代码库的任何变更都源于问题追踪库中的一个issue。假如还没有issue,那么应该首先根据重大的任务创建Issue(超过1个小时的开发需求)。对于一些组织来说这是很自然的事,因为这些issue会用来评估冲刺(sprint)点。Issue的标题应该描述出最后想要的结果来,例如"作为一个管理员,我要删除用户,并且不发生错误",而不是"管理员不能删除用户"。

当你准备写代码时,首先为issue创建一个新分支,这个分支的名字应该以issue number开始,例如“15-require-a-password-to-change-it”

当你开发完毕或者想与人讨论代码的时候,就创建一个merge request,可以在线讨论和review代码。创建一个merge request需要手动进行,你并不是每次都需要把push的新分支代码合并,因为它很可能是一个长时间开发的分支或者是发布的分支。假如你创建merge request没有指定给任何人,表明这是一个正在开发的分支,目的是为了讨论推荐的实现方式,但还没有准备好在master分支包含此功能。小提示:这种merge request的标题使用[WIP]WIP 开头,避免还没准备好的功能被合并。

当作者认为代码已经准备好合并,就指定这个merge requestreviewer(代码评审员)。当reviewer认为代码已经准备好包含在master分支时,点击合并按钮,这样代码就合并了,并且生成了一个合并提交,之后可以很容易看到这个合并事件。merge request总是提交一个合并提交,即使是空提交。这种合并策略在Git中被称为不使用 Fast-Forward。在合并后feature分支就被删除,不需要使用它了,这个删除分支操作在GitLab中是可选的。

假设分支合并后,但遇到了问题,Issue被重新打开。这种情况下,重用相同的分支名称是没问题的,因为它在合并时已经被删除了。在任何时候,每个Issue最多有一个分支。一个feature分支可能会解决多个Issue。

使用Merge request关联和关闭Issue

在这里插入图片描述
从commit的message中或者是merge request的描述信息中可以关联相关的issue,具体语法是:fixes #14
closes #67等等。在GitLab中,这样的操作会在issue中创建一个评论,并且merge request会显示相关联的issue。假如这个merge request被接受,这个issue会自动被关闭。

假如你仅仅想关联这个问题,但是不关闭这个问题,你可以这样写:Ducktyping is preferred(普通注释信息). #12

假如你有一个issue关联着好几处的修改,最好的办法是为每一个修改都创建一个issue,最后把这些issue关联到一个issue。

使用rebase合并commits

在这里插入图片描述
Git中你可以使用一个交互式的rebase命令(rebase -i)来把多个提交压缩成一个,并且重新排序它们。在GitLab EE 和 GitLab.com你也可以在Web界面使用rebase before merge。假如你在开发过程中对于一个小功能有多次提交,你想要把他们合并成一个提交,或者你想给提交排序以显得更加有逻辑性,那这个功能就很有用了。但是假如你已经把commits推到了远程分支,那么就不可以使用rebase了。如果别人拉取或cherry pick了你的commit,当你rebase你的commits时,一切就变的混乱了。如果人们已经review完了你的代码,当你rebase后,别人就没法知道你哪一块是新提交的代码了。另一个原因是,rebase会导致作者信息丢失,可能有人创建了一个merge request,另一个人 push 提交到这里,第三个人合并了它,这种情况下,将所有提交合并,可规范引用和分享git blame 的代码片段,从而避开其他作者。

我们鼓励经常commit和push代码,这样别人就会知道你在做哪一部分工作。然而太多的commit,会导致提交历史很难被理解。但是总体来说,更多的commit优点还是更多一些。为了明白一处改变,我们可以查看merge commit的相关信息,这个commit把会很多小的commit分组了。

当你从feature分支把很多commit合并到master分支,就很难会做回滚操作。假如你把多个commits压缩成了一个,那么只需回滚一个就可以了。但是千万要记着,如果commits已经push了,那么就不可以再有rebase操作了。幸运的是,git提供了回滚之前的一个合并的功能。然而这需要你指定需要回滚的merge commit。假如你回滚了一个merge,你又改变想法了,又想回滚回去,这个操作是git不允许的。

当你创建一个merge commit时都应该使用参数--no-ff,这样你就可以回滚这次merge了。当你接受一个merge reques时,Git管理软件总是会创建一个merge commit。

不要用rebase对commits进行排序

在这里插入图片描述
feature分支你可以使用rebase来把commits排到master后面,这样就会避免一个merge commit,创造一个清晰的线性历史。然而如果已经推到了一个远程库,那么就不要使用rebase了。如果你按照之前的建议经常地把commit提交共享给别人,那么rebase就会导致工作变得很混乱。当你使用rebase更新feature分支时,你需要一次次解决类似的冲突。你有时候需要reuse recorded resolutions (rerere),但是如果不适用rebase,你仅仅需要一次来解决这样的冲突。也有更好的办法来避免很多的merge commits。

避免很多的merge commits办法就是不要频繁的把master分支合并到feature分支。我们接下来要讨论在master分支合并的三个原因:使用代码、解决merge冲突、长时间存在的分支。假如在你创建feature分支之后,你需要从master使用一些代码,可以使用cherry-pickin命令;

假如你的feature分支有一个冲突,那么创建一个merge commit很正常。你应该尽量避免冲突的发生,例如使用gitattributes 以随机排序文件内容。一个例子是在GitLab中,我们的CHANGELOG文件在.gitattributes中配置:CHANGELOG.md merge=union,这样可以减少合并冲突。

最后一个创建merge commit的原因是有一个长期存在的分支,你需要与项目的最新状态保持一致。Martin Fowler,在他有关feature分支的文章中,讲过Continuous Integration (CI)。在GitLab中我们把CI和分支测试混淆是不合适的。从Martin那里引用的话:“我听说大家在每个分支的每次提交的时候都做CI,因为他们要做build,可能是用一个CI服务器来做。那是持续的build,这是一件好事,但是没有集成,因此并不是CI”。避免很多merge commits的方法是,你的feature分支存在时间尽量短,大多数应该少于一天。假如你的分支存在多于一天了,那么就考虑工作分的更细一些或者使用feature toggles。

对于多于一天的分支,会有两种策略来解决。

  • 在CI策略中,你可以在开始阶段就从master分支合并,可以避免中途的合并。
  • 在同步策略中,你可以仅仅在规定好的时间来合并,例如一个tag。这种策略很被Linus Torvalds提倡,因为这些时间点的代码状态可以更容易被知道。

GitLab Enterprise Edition提供一种在合并前rebase的方式。你可以配置给每个项目,可在项目配置页面,勾选Merge Requests Rebase

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
在这里插入图片描述

在接受merge request之前,选择rebase before merge
在这里插入图片描述

GitLab将在合并之前,尝试干净的rebase。如果办不到,会使用常规合并。如果干净的rebase是可行的,目标分支的历史在这次合并后将更新。

总之,我们应该努力避免merge commit,但是不要限制他们。你的代码应该是干净的,但是你的历史应该准确的呈现出过去发生的事情。开发软件有时候会有一些混乱的操作,这些直接反映在历史中就可以了。你可以使用工具来查看commits的可视化图,以此来理解创建代码的乱七八糟的历史。假如你错误的使用rebase,那使用工具也没法纠正这个,因为工具也不能改变commit identifier。

给 Issue 和 Merge request点赞

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NtD6LqTA-1594205680074)(…/reference/imgs/award_emoji.png)]
在这里插入图片描述

通过+1 或 -1 来表示认同或不认同。在GitLab中,您可以使用emojis给issue和merge request点赞。

推送和删除分支

在这里插入图片描述
我们推荐大家经常的把feature分支push上去,即使还没有全部完成代码。这样做,可以防止别的成员去解决这个问题。在问题跟踪系统中,每个问题都会指定给一个人,也可以避免这种情况,可是有时候大家会忘记把问题指定给某个人。

当一个分支被合并后,这个分支应该被删除。在GitLab中当merge request时,这个选项是可选的,这保证了在gitlab中看到的分支都是在处理的issue的,并且当在一个新分支中重新解决一个issue时,分支可以使用之前的名字。

经常commit 并正确的书写描述信息

在这里插入图片描述
我们推荐经常地commit,每次完成一个功能点,都应该commit一次。这样做的优点是当写新代码或重构旧代码出现问题时,很容易回滚会旧的版本。对于SVN的用户,这是一个很大的改变,当工作完成要共享给其他人时才会commit代码。有一个小技巧是,当你准备把代码提交给别人时进行pull/merge操作即可,中间不需要进行。

commit的描述信息应反映你的目的,而不是提交的内容。commit的描述信息是最容易被看到的,问题是为什么要这么做,一个好的commit描述示例:“合并模板以规范用户视图”。也有不好的例子,因为它们没有包含足够的信息,例如:改动、改进和重构。也应该避免使用fixfixes这类词汇,除非与Issue数字放在一起。查看更多关于commit描述信息的内容,可以查看Tim Pope的博文

在合并前测试

在这里插入图片描述
在旧的工作流中,Continuous Integration (CI)服务器通常运行master分支的测试。开发者必须保证他们的代码不会把master分支搞坏。

而使用GitLab Flow,开发者从master分支创建自己的分支,改完代码在merge request之前必须测试完毕才可以合并代码。持续集成软件(Travis,GitLab CI)在merge request时会显示build结果,这样可以保证测试通过。这样有一个缺点是由于只测试自己的feature分支,没有测试合并后的代码。最好是有人可以测试合并后的代码,但是每次有人合并分支到master后就需要测试一次,这样的测试代价是昂贵的,而且需要经常地等待测试结果。假如没有合并冲突,feature分支合并到master的风险是可以承受的。假如有合并冲突,你需要先把master代码合并到feature分支重新进行测试。

假如你的feature分支好多天也没有关闭,那么你需要使issue更小一些。

使用feature分支工作

在这里插入图片描述
一般初始化一个feature分支时总是从最新的master分支拉取的代码。假如你之前就知道你的分支依赖别的代码,那么可考虑从别的分支拉取代码。假如你需要合并别的分支,那么需要在merge commit的信息中写清楚原因。假如你还没有把你的commit提交的远程库,那么可以使用rebase把你的commit合并在master或其他分支。

假如你的代码不合并upstream分支也可以正常工作,那么就不要合并。Linus说过“不应该随意的合并至upstream分支,应只合并主分支代码”。仅仅当需要的时候合并代码可以尽量的减少merge commit,这样可以使历史更清楚。

引用

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值