与传统的代码版本管理工具相比,Git有很多的优势,因而越来越成为程序员喜欢的版本管理工具。我觉得,Git这个代码版本管理工具最大的优势有以下几个。
如果有冲突,那么你要先解决冲突,然后做 `git rebase --continue` 。如下图所示,git在做 pull --rebase 时,会一个一个地应用(apply)本地提交的代码,如果有冲突就会停下来,等你解决冲突。
# 功能分支协同工作流
上面的那种方式有一个问题,就是大家都在一个主干上开发程序,对于小团队或是小项目你可以这么干,但是对比较大的项目或是人比较多的团队,这么干就会有很多问题。
最大的问题就是代码可能干扰太严重。尤其是,我们想安安静静地开发一个功能时,我们想把各个功能的代码变动隔离开来,同时各个功能又会有多个开发人员在开发。
这时,我们不想让各个功能的开发人员都在Master分支上共享他们的代码。我们想要的协同方式是这样的:同时开发一个功能的开发人员可以分享各自的代码,但是不会把代码分享给开发其他功能的开发人员,直到整个功能开发完毕后,才会分享给其他的开发人员(也就是进入主干分支)。
因此,我们引入“功能分支”。这个协同工作流的开发过程如下。
1. 首先使用 `git checkout -b new-feature` 创建 “new-feature”分支。
1. 然后共同开发这个功能的程序员就在这个分支上工作,进行add、commit等操作。
1. 然后通过 `git push -u origin new-feature` 把分支代码push到服务器上。
1. 其他程序员可以通过`git pull --rebase`来拿到最新的这个分支的代码。
1. 最后通过Pull Request的方式做完Code Review后合并到Master分支上。
就像上面这个图显示的一样,紫色的分支就是功能分支,合并后就会像上面这个样子。
我们可以看到,其实,这种开发也是以服务器为中心的开发,还不是Git分布式开发,它只不过是用分支来完成代码改动的隔离。
另外,我想提醒一下,为什么会叫“功能分支”,而不是“项目分支”?因为Git的最佳实践希望大家在开发的过程中,快速提交,快速合并,快速完成。这样可以少很多冲突的事,所以叫功能分支。
传统的项目分支开得太久,时间越长就越合不回去。这种玩法其实就是让我们把一个大项目切分成若干个小项目来执行(最好是一个小功能一个项目)。这样才是互联网式的快速迭代式的开发流程。
# GitFlow协同工作流
在真实的生产过程中,前面的协同工作流还是不能满足工作的要求。这主要因为我们的生产过程是比较复杂的,软件生产中会有各式各样的问题,并要面对不同的环境。我们要在不停地开发新代码的同时,维护线上的代码,于是,就有了下面这些需求。
整个代码库中一共有五种分支。
所谓`--no-ff`参数的意思是`——no fast forward`的意思。也就是说,合并的方法不要把这个分支的提交以前置合并的方式,而是留下一个merge的提交。这是把双刃剑,我们希望我们的`--no-ff`能像右边那样,而不是像左边那样。
对此的建议是:只有feature合并到developer分支时,使用–no-ff参数,其他的合并都不使用`--no-ff`参数来做合并。
另外,还有一个问题就是,在开发得足够快的时候,你会觉得同时维护Master和Developer两个分支是一件很无聊的事,因为这两个分支在大多数情况下都是一样的。包括Release分支,你会觉得创建的这些分支太无聊。
而你的整个开发过程也会因为这么复杂的管理变得非常复杂。尤其当你想回滚某些人的提交时,你就会发现这事似乎有点儿不好干了。而且在工作过程中,你会来来回回地切换工作的分支,有时候一不小心没有切换,就提交到了不正确的分支上,你还要回滚和重新提交,等等。
GitLab一开始是GitFlow的坚定支持者,后来因为这些吐槽,以及Hacker News和Reddit上大量的讨论,GitLab也开始不玩了。他们写了[一篇blog](https://about.gitlab.com/2014/09/29/gitlab-flow/)来创造了一个新的Workflow——GitLab Flow,这个GitLab Flow是基于GitHub Flow来做的(参看:[ GitHub Flow](http://scottchacon.com/2011/08/31/github-flow.html) )。
## GitHub Flow
所谓GitHub Flow,其实也叫Forking flow,也就是GitHub上的那个开发方式。
而有些时候,我们还会有不同版本的发布,所以,还需要有各种release的分支。如下图所示。Master分支是一个roadmap分支,然后,一旦稳定了就建稳定版的分支,如2.3.stable分支和2.4.stable分支,其中可以cherry-pick master分支上的一些改动过去。
这样也就解决了两个问题: