使用GitHub的开发流程

在开发流程中使用GitHub,可以将开发团队的能力发挥到最大限度。
开发流程--使用了Git和GitHub的团队开发所涉及的规则及步骤。接下来我们将讲解两种开发流程,每个流程都有各自不同的特征。在实际开发中究竟使用哪一种,需要根据现场团队的情况来决定。

团队使用GitHub时的注意事项

在详细讲解使用Git与GitHub的开发流程之前,我们先来看一看由软件开发者们组成的团队想要最大限度地发挥出他们的能力所需要具备哪些前提条件

  • 一切从简

面向企业发售的开发者工具或协作工具往往拥有十分丰富的功能。某些企业为使用这些丰富功能,会专门为其制定软件开发规则。然而我们真的需要这么多功能和规则吗?
GitHub的各项功能都非常简单,就是因为在实际的软件开发中,往往用不到那些复杂度极高的功能。

  • ........项目管理工具与GitHub的区别

例如著名开源项目管理工具Redmine的新建问题页,从繁多的可输入项目中我们就可以看出其功能的丰富程度。而且Redmine还有众多插件,可以为其进一步添加功能。然而GitHub的New Issue页却非常简单。

  • ........项目管理工具与GitHub相异的原因

Redmine等项目管理工具是以管理项目为目的的,势必要考虑管理人员会输入哪些信息,以及需要提醒管理人员输入哪些信息,所以会拥有众多可输入项目。
而GitHub是一款为软件开发者提供支持的工具,与项目管理工具相比,它更注重辅助开发者高速开发高品质软件。要知道,往往事物越是简单,人们实施起来就越快。
GitHub的这些简单功能,完全能够应付软件开发中的需要。想让团队最大限度发挥实力,建议剔除复杂规则,只以最简单的规则进行开发。

  • 不Fork仓库的方法

已经将GitHub利用到开源软件开发中的读者们想必会以下面的流程进行Pull Request

  • 在GitHub上进行Fork
  • 将上述的仓库clone到本地
  • 在本地环境中创建特性分支
  • 对特性分支进行代码修改并进行提交
  • 将特性分支push到GitHub上相应仓库中
  • 在GitHub上对Fork来源仓库发送Pull Request
    在无法给不特定的多数人赋予提交权限的公开软件开发中,这种流程能够防止仓库收到计划之外的提交。
    然而在公司企业的开发中,开发者每天都要见面,要经常互相发送Pull Request,这种流程就显得有些繁琐了。因此,下面我们要介绍一个不需要Fork仓库的工作流程。这种方法可以让每一名开发者都掌握一个本地仓库和一个远程仓库,使整个开发流程变得简单。

GitHub Flow ——以部署为中心的开发模式

这一流程十分简单,在GitHub公司,大致会让15到20人组成团队,利用这一流程进行同一项目的开发。

GitHub Flow的流程

整个开发流程大致如下:

  • 令master分支市场保持可以部署的状态

  • 进行新的作业时要从master分支创建新的分支,新分支名称要具有描述性

  • 在新分支的本地仓库分支中进行提交

  • 在GitHub端仓库创建同名分支,定期push

  • 需要帮助或反馈时创建Pull Request,以Pull Request进行交流

  • 让其他开发者进行审查,确认作业完成后与master分支合并

  • 与master分支合并后立即部署

  • 随时部署,没有发布的概念

这个流程必须遵守"令master分支随时保持可以部署的状态"这一规则,没隔几小时进行一次部署,可以有效防止同时出现多个严重BUG。
虽然有时仍会有一些小BUG出现,但只要将相应的提交revert或者提交修正过的代码即可轻松应对。这一流程要以小时甚至分钟为单位持续地进行部署,所以不存在发布的概念。因此,不会出现让HEAD返回去指向很久之前的提交。
由于master分支时常保持着可以部署的状态,所以开发者可以随时创建新的分支。
要注意,没有进行过测试或者测试未通过的代码绝不可以合并到master分支。因此势必要用到持续集成等手段。

  • 进行新的作业时要从master分支创建新分支

进行新的作业时要从master分支创建新分支,无论是添加新功能还是修复BUG都是如此。此外,新分支的名称要具有描述性。
所谓描述性的名称,是指该名称能直观正确地表达这个分支的特性,例如:

  • user-content-cache-key
  • submodules-init-task
  • redis2-transition
    其他开发者可以通过这些名字清楚地了解到该分支正在进行什么工作。
    采用这一方式,开发者在查看远程仓库的分值列表时,能够对当前团队正在实施的任务一目了然。另外,由于分支明确描述了工作内容,即便开发者需要先去做其他工作,回来时也能很快想起该分支的工作目标。
    查看GitHub的分支列表页面还可以轻松掌握各分支与master分支的差别。
  • 在新创建的分之中进行提交

在新创建的特性分支上修改代码,并进行提交。修改代码注意决不能进行与该分支无关的修改。
在这一阶段,开发者要在提交的力度上多花心思。有意识地减小提交的规模,一方面清除地表达目的,另一方面有助于其他开发者对Pull Request进行审查。
比如在添加一个方法时,确认添加位置以及类之后,开发者往往还需要进行下面的操作:

  • 修正附近代码的缩进问题
  • 发现变量单词拼写错误并进行修正
  • 添加本次作业中需要添加的方法
    如果将上述工作在一次提交中完成,那么一个差别将包含3种含义,这种提交的粒度就有些不妥。如果将3个工作分为3次提交,那么每个差别就有了更清晰的含义。
  • 定期push

在这一开发流程中,由于除了master分支之外都是作业中的分支,所以push作业分支时不需要有太多顾虑。在开发过程中,建议开发者定期将本地仓库中创建的分支以同名形式push到GitHub端的远程仓库。
这样一来不仅可以备份代码,还会定期给开发者团队创造交流的机会。其他开发者在做什么工作,是否需要帮助等,团队成员可以通过GitHub的分支列表页面一目了然。
在开发过程中,最好让其他开发者能够看到自己编写的代码,同时养成积极查看其他人代码的习惯。通过代码进行交流时开发者的特权,我们没有理由不去利用。

  • 使用Pull Request

Pull Request不一定非要在与master分支合并时才使用。既然是团队开发,完全可以尽早创建Pull Request让其他开发者进行审查,一边听取反馈一边编写代码,没必要等到与master分支合并时再进行。
Pull Request具有现实差别以及对单行代码插入评论的功能,开发者可以利用这些进行交流。另外,如果希望得到特定开发者的反馈或者建议,可以在评论中加入@用户名,给该用户发送Notifications。对方注意到后,照例都会以某种形式进行反馈。

  • 务必让其他开发者进行审查

一个分支的作业结束后,需要注明作业已完成,让其他开发者进行审查。找其他开发者看一看自己的代码,可以有效防止想当然的错误或者低级失误。审查时要选择没有参与编写的人,被指出问题时,要积极进行修改。前提是大部分代码通过自己的测试。
审查之后如果认为可以与master合并分支,则需要明确地告知对方,按照GitHub的文化,这里会用到:+1:shipit:等表情。偶尔也会看大LGTM(look good to me)等字样。

  • 合并后立刻部署

代码合并至master分支并且通过所有自动测试之后,需要立即进行部署。在部署之后,需要确认刚刚合并的代码是否存在问题。

时间GitHub Flow的前提条件

  • 部署作业完全自动化

首先,部署作业必须完全自动化。这一开发流程在一天当中需要多次部署,以旧有开发模式按部署文档进行部署作业会浪费相当多的时间,同时还很可能发生操作失误,作为一名程序员,不应该每天为这些工作花费精力。

  • ........使用部署工具

于是,我们要使用Capistrano等部署工具,让部署时所需的一系列流程自动化。一旦实现自动化,部署工作就能简化成一条指令,同时大幅减少粗心大意导致的人为失误,让所有参与开发的人都能够放心地实施部署工作。
另外,这类部署工具都有回滚(revert)功能。不小心部署了有问题的代码时,只需要一条指令就可以将版本回滚至部署前。为此,最好让所有参与开发的人都能进行回滚操作。

  • ........通过Web界面进行部署的工具

Capistrano等部署工具需要使用命令执行操作,开发者以外的人很难实施部署。而Webistrano和Strano等工具则提供了通过Web执行部署命令的界面,能够帮助团队成员解决这个问题。一个团队除了开发者以外,往往还包含美工或HTML编辑等人,在开发过程中,创建一个让团队所有相关人员都能放心部署的环境至关重要。

  • ........导入开发时的注意事项

随着团队人数增多以及成熟度提高,开发速度会越来越快。这时往往一个部署尚未完成,另一名开发者就已经处理完下一个Pull Request,开始实施下一个部署了。在这种情况下,一旦正式环境中出现问题,很难分辨是哪个部署造成的影响。为了应对这种情况,建议在部署实施过程中通过工具上锁,或者在实施部署时通知整个团队等。通过严格贯彻这类规则来消除隐患。

  • 重视测试

  • ........让测试自动化

如果每次部署到正式环境前都需要在测试环境中手动进行测试,那这一开发流程也就无法谈起了。所以必须让测试自动化,令其自动检测是否有代码被恶意破坏,以及是否出现BUG。

  • ........编写测试代码,通过全部测试

每一名开发者都必须编写测试代码。成品代码的Pull Request中如果不含测试代码,是不可以合并至master分支中。只有包含测试代码并且通过了所有测试的成品代码才可以被合并至master分支。
开发者确认代码在本地环境中通过了所有测试后,将其push到远程仓库。随后Jenkins或Travis CI等CI工具会自动对其进行测试,测试结果有CI工具第一时间通知开发者。经过这一流程,系统能够自动检测出软件是否遭到破坏。

  • ........维护测试代码

模拟体验GitHub Flow

假设各位是负责给某软件开发功能的开发者,并且所在团队正在实践GitHub Flow。账目名为ituring,仓库名为fizzbuzz(https://github.com/ituring/fizzbuzz)。将仓库Fork至自己的GitHub账户下。

  • Fizzbuzz的说明

假设我们开发的一款软件名叫Fizzbuzz,它在输出1到100数字时会如下显示:

  • 3的倍数显示fizz
  • 5的倍数显示buzz
  • 3和5的公倍数显示fizzbuzz
  • 除以上情况外显示数字
  • 添加新功能

我们被分配的工作就是添加如下新功能:

  • 含有7的数字时显示GitHub
  • 创建新的分支

在GitHub Flow中,无论是实现新功能还是修正BUG,都需要从能正常运行的最新master分支中新建一个分支。所有实际修改都在这个新的分支中进行。

  • ........如果尚未clone仓库

首先需要Fork已经公开的仓库。如果尚未获取仓库,则需要使用下面命令进行clone:

$ git clone git@github.com:swordsman1990/fizzbuzz.git
$ cd fizzbuzz
  • ........如果之前clone过仓库

如果之前已经clone过仓库了,现在正在开发途中并不需要重新clone,我们应该将master分支更新成远程仓库最新master分支的状态。首先切换到本地仓库的master分支下,然后将远程仓库的master分支pull到本地即可:

$ git checkout master
$ git pull git@github.com:ituring/fizzbuzz.git master
  • ........创建特性分支

现在我们已经完成了从master分支创建新分支的所有准备工作。我们将新分支的名字定为7-case-output-github
在master分支中使用下述命令创建新的分支:

$ git checkout -b 7-case-output-github

为了方便团队其他人知道我们在做什么,我们在GitHub端的远程仓库中创建一个同名分支:

$ git push -u origin 7-case-output-github

定期将这个特性分支push到远程仓库

  • 实现新功能

现在我们来实现新功能——含有数字7时显示GitHub:

这里是ruby代码,修改完成后测试,运行成功后进行提交

提交本次实现内容:

$ git commit -am"Add output GitHub"

新功能已经顺利实现,将其push到远程仓库:

$ git push
  • 创建Pull Request

至此,我们已经顺利实现了新功能,接下来就是从7-case-output-github分支创建一个Pull Request发送给master分支,请求与master分支合并。创建Pull Request的相关操作参照第6章。
在Pull Request中写明希望得到审查。如果想让特定的人来进行审查,可以在评论中加入@用户名,这样该用户就会收到Notification。

  • 接收反馈

距离发送Pull Request已经过了几个小时,我们再次登录GitHub。此时已有其他开发者已经发来反馈
对方为我们指出了2个问题:

  • 缩进不正常
  • 没有测试代码
    点击缩进好像不太对上方信息in后的地址所指链接,我们可以看到评论所指代码的位置
    至于测试代码确实没有
  • 修正缩进

修正后将修改提交至本地的7-case-output-github分支。

$ git commit -am"fix indent"

接下来将该分支push到GitHub端的远程仓库,为远程仓库分支添加这项修改

$ git push

我们再打开GitHub查看Pull Request,会发现这个用于修正的提交已经添加至Pul Request。

  • 添加测试

在GitHub Flow中,不可以将没有代码测试的成品代码放入master分支,因此我们被其他开发者指出没有编写测试代码。
一般来说应该是下面顺序:

  • 将master分支更新到最新状态
  • 在自己的开发环境中确认通过所有测试
  • 从master分支创建新分支
  • 编写测试代码
  • 编写实现目标功能的代码
  • 确认通过所有测试并且没有出现退步(Regression)现象
  • 发送Pull Request请求合并至master分支
    也就是应该编写目标功能的测试代码,以保证测试代码全部通过为基准编写功能代码。这个操作顺序能够极力减少出现BUG的可能,并且可以随时修改功能代码。现在回过头来为其添加一份测试代码
  • 培育Pull Request

  • Pull Request被合并

团队实践GitHub Flow时的几点建议

  • 减小Pull Request的体积

将目标功能细分,尽量缩小Pull Request的体积,保证每几小时至几天向master分支发送一次Pull Request,通过多次合并来实现一个功能。这样一来,不但能有一个很好的开发节奏,还能确保软件更加安全可靠。

  • 准备可供试运行的环境

  • 对数据库进行了大幅修改
  • 实施了大规模重构
  • 对充值处理部分进行了大幅修改
    类似以上对系统有重大影响的关键性修改,为安全起见,最好先将其部署到预演环境中进行试运行。
  • 不要让Pull Request中有太多反馈

  • 不要积攒Pul Request

GitHub Flow的小结

对于各种问题,请遵循以下两点去寻找解决方案:

  • 开发流程以部署为中心
  • 高速源于简单

Git Flow——以发布为中心的开发模式

  • 便于理解的标准流程

流程用分支名表示标准软件开发中开发状态的迁移:

  • 从开发板的分支(develop)创建工作分支(feature branches),进行功能的实现或修正
  • 工作分支的修改结束后,与开发板的分支进行合并
  • 重复上述两个步骤,不断实现功能直至可以发布
  • 创建用于发布的分支(release branches),处理发布的各项工作
  • 发布工作完成后与master分支合并,打上版本标签(Tag)进行发布
  • 如果发布的软件出现BUG,以打了标签的版本为基础进行修正(hotfixes)
  • 有时显得过于复杂

各位的团队在采用这一开发流程之前必须进行系统学习,冲分掌握其优势与劣势。

导入Git Flow前的准备

git-flow是一款辅助Git Flow的工具,虽然不安装它也可以实施该开发流程,但是所有工作就必须手动完成。为防止出现人为失误,建议安装这个工具。

  • ........Mac下安装

$ brew install git-flow
  • ........Linux下的安装

$ sudo apt-get install git-flow
  • ........确认运行状况

$ git flow
  • 仓库的初始位置

假设正在开发博客软件

  • ........创建仓库

首先要在GitHub上新建一个Git仓库。接着clone这个仓库。

$ git clone git@github.com:xxxx/blog.git
  • .......进行git flow的初始设置

执行下面命令后,仓库中就会自动生成开发流程所需的分支。

$ cd blog
$ git flow init -d

查看已创建的分支

$ git branch -a

可以看到develop分支已经创建完毕,切换到这一分支。

  • ........在远程仓库中也创建develop分支

$ git push -u origin develop
$ git branch -a

模拟体验Git Flow

  • master分支与develop分支的区别

  • ........master分支

只在发布成品时进行合并

  • ........develop分支

用来创建特性分支,维持正常开发过程

  • 在feature分支中进行的工作

feature分支以develop分支为起点,是开发者直接更改代码发送提交的分支。一下流程进行:

  • 从develop分支创建feature分支
  • 在feature分支中实现目标功能
  • 通过GitHub向develop分支发送Pull Request
  • 接受其他开发者审查后,将Pull Request合并至develop分支

与develop分支合并后,已经完成工作的feature分支就失去了作用,可以在适当时候删除。

  • ........创建分支

上面我们提到过的develop分支是feature分支的起点。我们新建一个feature分支,实现添加用户的功能,命名为add-user分支.
首先将develop分支更新至最新状态。

$ git pull

创建feature分支add-user,用来实现添加用户的功能:

$ git flow feature start add-user
图示过程

我们已经创建并切换到了feature/add-user分支。查看当前分支:

$git branch
  • ........在分支中进行作业

接下来在刚刚创建的feature/add-user分支中实现目标功能并进行提交。

  • 发送Pull Request

功能实现后,通过GitHub发送Pull Request,请求develop分支合并feature/add-user分支的内容。注意,这里不能与本地的Git仓库合并,而是利用GitHub的Pull Request功能接受代码审查,然后再合并到远程仓库的分支中。这样可以让其他开发者看到我们的代码,从而指出其中存在问题。如果在设计上还有不同意见,还可以进行讨论,以便写出更高质量的代码。

$ git push origin feature/add-user
  • 如果是与其他开发者一同开发feature分支,那么远程仓库的add-user分支可能已经被更新,记得通过pull命令获取最新add-user分支代码。
  • 在我们开发这个feature分支的过程中,develop分支可能有了最新的代码。在push之前养成获取最新develop分支的习惯。
  • 通过代码审查提高代码质量

  • 由其他开发者进行代码审查,在Pull Request中提供反馈
  • 修正代码以反映反馈内容
  • 将feature/add-user分支push到远程仓库
  • 重复前三步
  • 确认Pull Request没有问题后,由其他开发者将其合并至develop分支
  • 更新本地的develop分支

我们发送Pull Request在GitHub端与develop合并后,为让其反映到本地的develop分支中,我们需要进行以下操作:

  • 切换至develop分支
  • 执行git pull(fetch & merge)
$ git checkout develop
$ git pull

每当从develop分支创建feature分支时,一定要先执行上述操作,确保develop分支处于最新状态。
在实际开发中,我们会不断重复之前这一系列流程,不断为develop分支添加功能。当功能积攒到足以发布时,就会用到release分支。

  • 在release分支中进行的工作

在发布阶段,我们要实现所有要发布的功能,发送Pull Request并且与develop分支合并。
接下来给软件分配一个版本号进行发不。今后将只对这个版本的软件做BUG修复,不做功能添加。

  • ........创建分支

从develop分支着手,开始1.0.0版本的release工作:

$ git checkout develop
$ git pull
$ git flow release start '1.0.0'

release/1.0.0分支已经成功创建,它就是这次的release分支。

  • ........分支内的工作

在这个分支中,我们只处理与发布前准备相关的提交。比如版本编号变更等元数据的添加工作。如果软件部署到预演环境后经测试发现BUG,相关的修改也要提交给这个分支。

  • ........进行发布与合并

发布前的修正全部处理完成,我们结束这一分支:

$  git flow release finish '1.0.0'

release分支合并至master分支。分支在合并时会询问提交信息,如果没有特别声明的事项,可以保持默认状态。

$ merge branch 'release/1.0.0'

接下来,合并后的master分支会加入一个与版本号相同编号的标签。

Release 1.0.0

随后,将release分支的状态合并至develop分支。如果出现合并提交,则系统会询问提交信息:

Merge branch 'release/1.0.0' into develop

全部工作结束后,会显示如下字样:

$ git flow release finish '1.0.0'
  • ........查看版本标签

通过前面一系列操作,我们创建了与发布版本号相同的Git标签

$ git tag

今后如果遇到什么问题,只要指定这个标签,就可以将软件回溯到相应版本。

  • 更新到远程仓库

$ git push origin develop
$ git checkout master
$ git push origin master
$ git push --tags
  • 在hotfix分支中进行的工作

hotfix分支并不是预期计划中出现的分支。它是一个紧急应对措施,只有当前发布的版本中出现BUG或漏洞,而且其严重程度要求开发方必须立刻处理,无法等到下一个版本发布时,hotfix分支才会被创建。
因此,hotfix分支都是以发布版本的标签或master分支为起点。借助hotfix分支,可以在不影响develop分支正常开发的情况下,由其他开发者处理成品的修正工作。

  • ........创建分支

一下情况需要创建hotfix分支

  • develop分支正在进行开发新的功能,无法面向用户发布
  • 漏洞需要及早处理,无法等到下一版本发布

将远程仓库的最新信息获取到本地,确认标签的版本编号是否有误。

$ git fetch origin

现在以1.0.0的标签信息为起点,创建名为1.0.1的hotfix分支

$ git flow hotfix start '1.0.1' '1.0.0'

以1.0.0标签为起点成功创建了hotfix/1.0.1分支,我们在这个分支中修复软件的漏洞并进行提交。
修复等工作全部结束后,将hotfix分支push到GitHub端远程仓库,并向master分支发送Pull Request

$ git push origin hotfix/1.0.1
  • ........创建标签和进行发布

利用GitHub的功能创建1.0.1的标签了。
访问GitHub仓库页面,选择release,打开该仓库的发布信息。页面中显示之前创建的1.0.0标签的相关信息。
填写相关信息,提交新的标签1.0.1
本地仓库再获取一次标签,确认1.0.1标签是否成功:

$ git fetch origin
$ git tag
  • ........从hotfix分支合并至develop分支

虽然修正了master和发布版本的代码问题,但是开发中的develop分支仍然存在这些漏洞和BUG。我们需要将1.0.1版的修改合并至develop分支中。Pull Request即可。
合并后如果develop分支出现了异常,首先将合并完成,其次在develop分支中修改,千万不要在hotfix/1.0.1分支中修改,因为它是针对master分支的。

推荐阅读更多精彩内容

  • 多种多样的工作流使得在项目中实施Git时变得难以选择。这份教程提供了一个出发点,调查企业团队最常见的Git工作流。...
    Ketine阅读 2,182评论 2 8
  • Git分支管理 master:主分支,当前分支上的代码随时可以直接发布,并且只能通过Pull Request从其他...
    UEUEO阅读 4,248评论 4 26
  • 又到换季时分 “是时候给自己换个包包了” 是LV的手提袋呢 还是Hermers的设计款呢 等等 好像Fendi的杀...
    味他菜谱阅读 105评论 0 0
  • 我轻松地,坚定地坚持做有意义的重要的事情 我允许自己的大脑休息 我允许自己缓慢地,深入地听课 我把知识点分析地很透...
    cllian119阅读 26评论 0 0
  • 相信很多人都认识蒋欣吧,她演了很多的电视,刻画了一个个的人物形象,我们甚至不记得她叫蒋欣,只记住了她扮演的角色的名...
    风中蓝荷阅读 53评论 0 3