GitLab Flow

本文介绍了GitLab工作流的最佳实践,包括如何使用分支、合并请求、问题追踪系统等工具来提高开发效率和代码质量。重点讲解了如何通过标准化的工作流程来避免常见的版本控制问题。

转载:https://www.15yan.com/story/6yueHxcgD9Z/


相较于SVN等老旧的版本控制系统,使用git进行版本管理会让分支和合并更加容易。git允许更多样的分支策略和工作流。相比较于git出现以前应用的方法,现在几乎全部的方法都得到了改进。但是现在很多组织仍然使用一个没有明确定义的,过度复杂的或者是没有集成错误追踪系统的工作流程来工作。因此我们推荐gitlab工作流作为最佳的实践方式。它整合了feature driven development和有issue跟踪的feature branches。

使用其他版本控制系统的组织转移到git后发现很难去开始一个有效的工作流程。这篇文章描述的gitlab工作流集成了git工作流和一个错误追踪系统。它提供了一个简单、易懂并且有效地方式去使用git工作。


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


因为很多不熟悉git的组织没有约定如何使用git工作,导致工作很快变得一团糟。他们产生的最大的问题是同时存在太多的开发中的分支,每个分支都包含了部分修改。人们很难弄清楚哪一个分支应该继续开发或者把它变成产品。通常,应对这种问题的方法是采用一个标准的模型比如git flow或是github flow。我们认为这里面还有可以提升的空间,并且把它详细描述为gitlab flow。

git flow和它的问题

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

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

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

github flow作为一个更简单的替代选择

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

gitlab flow中的生产分支

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

gitlab flow的分支使用

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

gitlab flow的发布分支

在很少的情况下你需要使用release分支对外界发布软件。在这种情况下,每个分支都会包含一个版本号(2-3-stable, 2-4-stable, etc)。这种stable分支都会从master开始,并且要尽可能晚的创建。创建的越晚,你就会越可能的减少bug fix的合并。在release分支发布后,只有严重的bug修复才加到release分支中。如果可能的话,这种bug的修改首先应合并到master,然后cherry pick到release分支。这样子,你就不会忘记把修复bug的代码cherry pick到master,从而防止其他分支也出这个bug。这叫做“upstream first”策略,被google和redhat使用。每次release分支修改bug之后,都要先设置tag,然后增加版本号。有些项目也有stable分支用来指向最近release版本的commit,这种情况下一般没有production分支。

在gitlab共工作流中的Merge/pull requests操作

merge和pull都在git中创建,然后指向一个人来合并两个分支。像GitHub and Bitbucket这样的工具会选择pull request的名字,因为第一个手动的操作是拉取feature分支。像 GitLab and Gitorious这样的工具会选择merge request的名字,因为对被指向的人来说这是最后一个操作。在这篇文章中,我们会称这个操作为merge request。

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

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

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

gitlab flow如何处理issue

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

代码的任何修改都应该开始于一个目标明确的issue。对于团队里的每个人来说,每一段代码的编写应都有一个明确的目标,也可以通过这个目标来判断每个feature的分支工作量大小。在gitlab glow中,每次代码的编写都源于问题追踪库中的一个issue。假如还没有issue,那么应该根据每个重要的功能点来创建issue。对于一些组织来说这是很自然的事,这些issue都已经被估计为了每个sprint点。issue的标题应该描述出最后想要的结果来,例如“作为一个管理员,我要删除用户,并且不发生错误”,而不是“管理员不能删除用户”

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

当你写完了代码或者想与人讨论的时候,就创建一个merge request,可以在线讨论代码,review代码。创建分支需要手动进行,你并不想每次都把push的代码合并,它很可能是一个长时间开发的分支或者是发布的分支。

假如你创建merge request没有指定给任何人,表明这是一个正在开发的分支,大家可以讨论但是不做合并。

从merge中关联和关闭问题

从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

你可以使用rebase命令把多个提交压缩成一个,并且重新给排序。假如你在开发过程中对于一个小功能有多次提交,你想要把他们合并成一个提交,或者你想给提交排序,那这个功能就很有用了。但是假如你已经把commits推到了远程分支,那么就不可以使用rebase了。如果别人拉取或cherry pick了你的commit,当你rebase你的commits时,一切就变的混乱了。如果人们已经review完了你的代码,当你rebase后,别人就没法知道你哪一块是新提交的代码了。

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

当你从feature分支把很多commit合并到master分支,就很难会做回滚操作。假如你把多个commits压缩成了一个,那么只需回滚一个就可以了。但是千万要记着,如果commits已经push了,那么就不可以再有rebase操作了。幸运的是,git提供了功能:回滚之前的一个合并。然而对于你想回滚的commits,这也需要明确的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分支。我们接下来要讨论这三个愿原因:使用代码、解决merge冲突、长时间存在的分支。假如你需要从master合并一些代码,可以使用cherry-pickin命令;假如你的feature分支有一个冲突,那么merge commit很正常。你应该尽量避免冲突的发生。一个例子是CHANGELOG文件,每个重要的代码修改都要在这里记录。对于当前版本,每个人不应该都把修改放在文件的最下边,而是应该随机的插入现在的文件里。当多个分支都对这个文件修改时,可以减少一些冲突的合并。最后一个原因是对于长期存在的分支,又想要更新为最新的代码。Martin Fowler在他的文章中讨论者这种ci的问题。在GitLab中我们把ci和分支测试混淆是不合适的。从Martin那里引用的话:“我听说大家在每个分支的每次提交的时候都做ci,因为他们要做build,可能在一个ci服务器上。那是持续的build,这是一件好事,但是没有集成,因此并不是ci”。避免很多merge commits的方法是,你的feature分支存在时间尽量短,大多数应该少于一天。假如你的分支存在多于一天了,那么就考虑工作分的更细一些或者使用feature toggles。对于多于一天的分支,会有两种策略来解决。在ci策略中,你可以在开始阶段就从master合并,可以避免中途的合并。在同步策略中,你可以仅仅在规定好的时间来合并,例如一个tag。这种策略很被Linus Torvalds赞同,因为这些点的代码状态可以更好地被知道。

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

投票功能,在merge request

在merge request时,右上角有一个投票功能。

推送代码,删除feature分支

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

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

[图片]

要经常commit,并且写正确的、完整的描述

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

commit时需要认真的写描述内容,commi的message是最容易被看到的,应该尽量多的包含这次commi的信息。一篇文章:如何写message

merge之前做测试

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

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

merge别的代码注意事项

一般初始化一个feature分支时总是从最新的master拉取的代码。假如你之前就知道你的分支依赖别的代码,那么可考虑从别的分支拉去代码。假如你需要合并别的分支,那么需要在merge commit的信息中写清楚原因。假如你还没有把你的commit提交的远程库,那么可以使用rebase把你的commit合并在master或其他分支。 假如你的代码不合并upstream分支也可以正常工作,那么就不要合并。linus说“不应该在任何时候合并upstream分支,仅仅应合并主分支代码”仅仅当需要的时候合并代码可以尽量的减少merge commit,这样可以使历史更清楚。

参考

Sketch file with vectors of images in this article

Git Flow by Vincent Driessen


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值