git相关的文章和教程非常多,但是系统介绍和了解工作流的人并不多,在使用过程中用错或用偏的也不少,这里分享的是,假设你已经入门的情况下,我们如何去选择适合团队需要的工作流。
git优势
这里先唠叨git的优势,对比传统的代码管理工具,git至少有以下这些优点:
- 有温度的工具:由 Git 衍生出来的 GitHub/GitLab 可以帮你很好地管理编程工作,比如GitHub包括即时通信功能,开发者可以互相审核、评论和打分。同时还包括 wiki、fork、pull request、issue……集成了与编程相关的工作,让我们的工作可以呈现在一个工作平台上,并以此来规范整个团队的工作。
- 分布式管理:你可以离线提交代码,而不用担心网络问题。
- 合并分支管理:合并分支后也能查看整个代码的变更记录
- 分支快速切换:Git 切换分支的时候通常很快,不像其他版本管理器,比如SVN,每个分支一份拷贝。
git常用命令
跳转这里查看常用命令:常用命令
这里记录自己平时经常使用的一些命令,并不是本文的重点。另外叨叨一下,有些人喜欢在集成的IDE下通过界面方式来进行操作,比如vs code或者eclipse都有傻瓜化的集成,这里推荐使用命令行操作,我觉得有如下一些优势:
- 第一、可以和IDE解耦,不用换一个IDE就要去了解对应界面的使用方法;
- 第二、命令行的操作非常快速,当然缺点是你需要记忆一些命令,但是常用的命令真心不多;
- 第三、命令行拥有界面没有的体验,比如一个简单的git status你可以查看内部更多的详情;git stash命令,可以把当前没有完成的事先暂存一下,然后去忙别的事;git cherry-pick命令可以让你有选择地合并提交。git add -p可以让你挑选改动提交。
git工作流介绍
市面上有以下几种常见的工作流:
- 中心式协同工作流【主干开发】
- 功能分支协同工作流【特性分支】
- github协同工作流
- gitlab协同工作流
- gitflow协同工作流
中心式协同工作流
这种方式等于把git当做svn适用,很多同学估计都是这么用的。这种协同方式是所有的人都在 Master 分支上共享他们的代码。
流程
这里的流程是这样的:
- 从服务器上把代码同步下来;
- 本地编码后,再add/commit到本地仓库
- 最后再push到origin master上
这里的第三步有可能出错,因为可能别人在你之前在相同地方已经提交了代码,这时候你需要先(git pull --rebase)一下,如果发现冲突,就解决冲突然后继续合并(git rebase --continue)。到这里你是否感觉和svn越来越像,每天早上过来定时的update一下,然后再编码,上传代码之前也是这样先update一下,再做上传操作?
焦油坑
这里有几个坑需要注意:
- 代码冲突:建议每天编码之前和代码上传之前不定期、频繁的进行git pull --rebase。
- 代码干扰:随着开发团队规模的扩大,可能你pull一个不可运行的代码下来(push上去的人没有用心检查是否可以编译通过),这时候你的麻烦开始产生了,你要停下手上的活,花费可能很久的时间去把代码编译通过,于是我们经常听到很多开发人员在那边互相抱怨,怎么就不能好好检查后再上传呢,还让不让人写代码,好好学习一下svn怎么用吧……
总结:
中心式的协同显然无法满足团队规模的扩展和代码的干扰冲突,而且产品上线后,master的BUG会直接影响生产环境,也就说master其实是不稳定的,而用不稳定的主干直接和生产环境挂钩,后果可想而知。所以该流程不适用产品线迭代开发和持续更新,如果你只有1-2个开发人员那就罢了,否则一般不建议使用这种方式,接下来就要介绍如何去避免上面的这些焦油坑,也就是我们的功能分支协同工作流上场了。
功能分支协同工作流
上面的那种方式有一个问题,就是大家都在一个主干上开发程序,对于小团队或是小项目你可以这么干,但是对比较大的项目或是人比较多的团队,这么干就会有很多问题。最大的问题就是代码可能干扰太严重。尤其是,我们想安安静静地开发一个功能时,我们想把各个功能的代码变动隔离开来,同时各个功能又会有多个开发人员在开发。
这时,我们不想让各个功能的开发人员都在 Master 分支上共享他们的代码。我们想要的协同方式是这样的:同时开发一个功能的开发人员可以分享各自的代码,但是不会把代码分享给开发其他功能的开发人员,直到整个功能开发完毕后,才会分享给其他的开发人员(也就是进入主干分支),接下来就是我们的功能分支上场了。
流程:
这里的流程是这样的:
- 切割开发分支:从git服务器上clone一份代码下来,并在本地切割出一个分支(git checkout -b jackyfei_dev);
- 编码提交:在分支下进行本地编码,后进行git add和git commit;
- 合并分支:切换到master分支(git checkout master),合并jackyfei_dev分支(git merge jackyfei_dev);
- 推送到服务器:最后push到服务器(git push -u origin master),注意这里推送后不会把分支一起推送到服务器,如果要推送分支上去可以使用命令:git push origin jackyfei_dev;
- 代码重审或代码测试[可选步骤]:代码审查人员或代码测试人员在你的分支上审查或测试通过后,在服务器端进行合并操作。该步骤根据团队大小进行选择,非必做项。
- 删除分支[可选步骤]:这里有点阅后即焚的感觉(git branch -d jackyfei_dev),当然你不删除也无妨,后续可以继续使用。
切割分支:
合并分支:
推送分支:
注意事项
- 删除分支:如果你在还没合并分支的时候,就要进行分支删除操作,git会有一个提示不能删除的操作,如果有重要的代码没有合并,建议不要-D强制删除。
- 分支冲突:在合并的过程会出现冲突,如下图所示,这时候需要手动解决冲突,方式和svn一样。手动合并后,再git add/git commit就可以了。
- 版本同步:这里的master和线上的版本保持同步,所以master必须尽量保证是干净的,稳定的,一般不要轻易去动他。
焦油坑
这里和注意事项不同,罗列的是该协作方式的缺陷:
- 线上出现BUG,本地分支正开发到一半,还没经过测试,无法进行发布,这时该如何解决?
- master无法保证一定是纯净的,因为每个人都可以merge上去
总结:
我们可以看到,其实,这种开发也是以服务器为中心的开发,还不是 Git 分布式开发,它只不过是用分支来完成代码改动的隔离。这里隔离的内容不叫项目而是小功能,这符合了产品快速迭代的需求。
如果团队规模不大,采用这种分支协同反而可能增加不必要的工作,所以要根据团队规模和现实当中的问题进行选型,一般团队超过两个人以上,而且线上环境频繁变更导致经常性的出现不稳定的异常,这种协同方式还是挺不错的。这里要思考的是,如果线上出了问题,本地开发一半无法进行发布的问题该如何处理呢?
GitFlow协同工作流
针对功能分支工作流的不足,GitFlow出现了,该工作流总共有5个分支,而其中的master和developer两个分支需要长期维护,其他的分支都是可以随时“阅后即焚”的。
流程
这里的流程是这样的:
- Feature 分支:也就是功能分支,用于开发功能,其对应的是开发环境,可以“阅后即焚”。
- Developer 分支:是开发分支,一旦功能开发完成,就向Developer 分支合并,合并完成后,删除功能分支。这个分支对应的是集成测试环境。
- Release 分支:当 Developer 分支测试达到可以发布状态时,开出一个 Release 分支来,然后做发布前的准备工作。这个分支对应的是预发环境。之所以需要这个 Release 分支,是我们的开发可以继续向前,不会因为要发布而被 block 住而不能提交。一旦 Release 分支上的代码达到可以上线的状态,那么需要把 Release 分支向 Master 分支和 Developer 分支同时合并,以保证代码的一致性。然后再把 Release 分支删除掉。
- Hotfix 分支:是用于处理生产线上代码的 Bug-fix,每个线上代码的 Bug-fix 都需要开一个 Hotfix 分支,完成后,向 Developer 分支和 Master 分支上合并。合并完成后,删除 Hotfix 分支。
- Master 分支:也就是主干分支,用作发布环境,上面的每一次提交都是可以发布到线上环境的。
主要事项
- 我们需要长期维护 Master 和 Developer 两个分支。
- Release 和 Hotfix 分支需要同时向两个分支作合并。所以,如果没有一个好的工具来支撑的话,这会因为我们可能会忘了做一些操作而导致代码不一致。
- GitFlow 协同虽然工作流比较重。但是它几乎可以应对所有公司的各种开发流程,包括瀑布模型,或是快速迭代模型。
焦油坑
- 分支太多,所以会出现 git log 混乱的局面。
- 在开发得足够快的时候,你会觉得同时维护 Master 和 Developer 两个分支是很太无聊,因为大部分情况下两者都是一样的。
- 管理变得非常复杂。尤其当你想回滚某些人的提交时,你就会发现这事似乎有点儿不好干了。
- 工作过程中,你会来来回回地切换工作的分支,有时候一不小心没有切换,就提交到了不正确的分支上,你还要回滚和重新提交等等。
GitHub Flow
流程
除非你是开源项目或者有购买github企业版,否则一般企业不会把核心代码托管在公共的github上面。
- 开发人员都把github上的代码 fork 到自己的代码仓库中。
- 开发容易代码库有两个远程仓库,一个是自己fork的库,一个是官方的库。
- 开发人员在本地建“功能分支”,在这个分支上做代码开发。
- 开发完成后,功能分支被 push 到开发人员自己的代码仓库中。
- 然后,向“官方库”发起 pull request,并做 Code Review。
- 一旦通过,就向官方库进行合并。
焦油坑
- GitHub Flow 这种玩法虽然变得很简单,但是没能把我们的代码和运行环境给联系在一起。
GitLab Flow
流程
以上是引入环境分支流程:
- 从master分支拉取一个预发布环境分支
- 从预发布环境分支拉取一个生产环境分支
流程虽然简单,但是增加分支后都是工作量,如果有很好的引入CI/CD,其实这一步也是多余的。以上主要是针对2C的互联网业务,2B的要看情况来处理,实时性没有那么强。
以上是引入版本分支流程:
对于版本和代码分支的问题,我觉得这应该是有意义的,但是,最好不要维护太多的版本,版本应该是短暂的,等新的版本发布时,老的版本就应该删除掉了。
总结
总之,GitFlow大而全,但是很重;中心式协同流简单但扩展性不好;功能分支和GitHub协同流轻量灵活(无须关注环境和多版本),适应性强大(既能适应快速开发和迭代,也适应CI/CD),个人倾向使用这功能分支协同工作流。当然没有一招鲜吃遍天的银弹,具体选择什么协作流程还是要具体分析,比如GitFlow的这种流程,可以考虑把Release分支裁剪掉,保证轻量化的同时,也能满足实际流程的需要;中心式的流程增加版本分支,也能很好的管理产品的版本代码。
当我们把精力放在软件架构和开发流程优化上,我们的git协同流会变得更加简单,所以与其熟练玩弄git协同流,不如放心思放在架构和开发流程的优化上,这才有事倍功半的效果。最后附上对比图,供你选择和参考,如果你们的团队有更好的用法,请多赐教。