edp icon indicating copy to clipboard operation
edp copied to clipboard

git分支管理规范

Open otakustay opened this issue 11 years ago • 43 comments

现在EFE在git上的仓库众多,但是每个仓库都有自己的一套开发模型,有些是master推送,有些是分支开发,有些是git flow。这不利于工程师贡献,因此最好有一个统一的分支管理的规范

下面是我经过N种玩法后总结出来的一种方式,供讨论


MUST、SHOULD、MAY、RECOMMEND不再赘述


本规范强制用于github上各项目的开发管理,对于其它环境下基于git的开发不作硬性要求,但建议(RECOMMEND)参考此规范


版本

版本号必须符合semver,其的形式为{major}.{minor}.{patch}[-{pre-release-type}.{pre-release}]

其中majorminorpatch和{pre-release}`必须为十进制数字,且随版本发布递增。

{pre-release-type}必须选择以下关键词之一:

  • alpha表示内部测试版本,不建议任何非参与开发人员所在团队使用,在alpha版本期间会不断增加新的功能并修复已有BUG
  • beta表示公开测试版本,不建议稳定项目使用,在beta版本期间会酌情增加新功能,修复已知BUG
  • rc表示发布候选版本,推荐各项目使用,在rc期间不得增加任何新功能,仅修复BUG。如果rc版本未发现任何BUG,则此版本直接转为正式发布版

分支

所有项目采用分支开发方式,分支分为开发分支发布分支功能分支修复分支master分支

  • 实际的分支以版本号为前缀,其中版本号精确到第1个子版本,如1.03.1
  • 开发分支的形式为{version}/develop,此类分支用于维护一个版本的开发过程
  • 发布分支的形式为{version}/release,此类分支用于维护每一个发布版本的状态,不得将功能或修复直接合并至此分类支
  • 功能分支的形式为{version}/feature/{feature-desc},此类分支用于开发单一的原子性的功能
  • 修复分支的形式为{version}/fix/{bug-desc},此类分支用于修复一个已有的BUG
  • master分支即master,此分支用于维护当前主流版本的稳定状态,不得将任何功能、修复、开发分支合并至此分支

对于fork式开发,也需要按以上形式建立分支

开发

  1. 开发任何功能前,应该(SHOULD)有一个github issue对应其功能
  2. 从对应版本的开发分支中拉取功能分支,命名形式为{version}/feature/{feature-desc}
  3. 在分支上完成开发,在分支的commit中应该(SHOULD)添加形如Fix #xxx的信息对应github issue
  4. 开发完成后,发起pull request,需注意pull request的对应目标为 开发分支
  5. 进行Code Review,通过后merge
  6. 删除开发分支

发布

  1. 开发分支 上修改版本号为即将发布的版本
  2. 开发分支 向对应的 发布分支 发起Pull Request
  3. 进行Code Review,通过后merge
  4. 发布分支 创建一个tag,tag的命名必须(MUTST)为符合semver的当前发布的版本号
  5. 如果需要,同时将版本发布至edp

在创建tag后,如果发现紧急的BUG,可在12小时内删除该tag,通过 修复分支 修复BUG后再行发布,不应该(SHOULD NOT)删除任何发布超过12小时的tag

紧急BUG修复

该过程仅用于发布后发现的必须修复的紧急BUG,不适用于正常开发规划中对BUG的修复

  1. 在github上添加一个对应的issue
  2. 从对应版本的 发布分支 中拉取 修复分支 ,命名形式为{version}/fix/{bug-desc}
  3. 在分支上开发,在分支的commit中应该(SHOULD)添加形如Fix #xxx的信息对应github issue
  4. 开发完成后,发起2个pull request,对应目标分别为 开发分支发布分支
  5. 进行Code Review,通过后merge
  6. 删除修复分支

同步master

当且仅当目前的主流版本的发布分支稳定(确认不再会有紧急BUG修复产生)后,可通过pull request合并至master分支

任何时候不得直接向master分支提交来自非发布分支的pull request

otakustay avatar Jul 27 '14 05:07 otakustay

+1

leeight avatar Jul 27 '14 10:07 leeight

再补充下对版本的控制:

  1. major版本,第1位数字变化表示一个major版本,此类版本通常是完全的重新设计的版本,可自由做任意更改
  2. minor版本,第2位数字变化表示一个minor版本,此类版本会有新功能的引入,但应该(SHOULD)保持向后兼容性,如有向后兼容性问题,必须(MUST)在发布的时候说明,并必须(MUST)提供升级指导
  3. patch版本,第3位数字变化表示一个patch版本,此类版本不得(MUST NOT)引入新的功能或接口,仅能包含重构或功能修复,不得(MUST NOT)引入不向后兼容的变化
  4. 例外的,在alpha状态时,可以对当前版本中新增的功能和接口引入非兼容性的变化,即所有新的功能或接口在beta版本发布前处于不稳定状态,alpha阶段的使用者不应该(SHOULD NOT)依赖新接口的稳定性
  5. 在发布新的major或minor版本后,应当(SHOULD)保证原版本的BUG修复工作,不应该(SHOULD NOT)放弃2个minor版本之内的版本的维护,可以(MAY)放弃2个minor版本之前的版本的维护,即如发布了3.4.0,由应该(SHOULD)继续维护3.2.x3.3.x版本,可以(MAY)不再维护3.0.x3.1.x

对使用者的建议:

  1. 所有依赖的版本控制在minor版本不变,使用如1.3.x形式的semver表达式
  2. 如非参与开发者,不要使用alpha版本
  3. 如无能力发现和处理BUG,不要使用beta版本
  4. 对于任何minor版本有变化的更新,升级后应该(SHOULD)进行谨慎地测试
  5. 对于任何major版本有变化的更新,先确定这还是不是你要的那个库

@leeight 如果此规范得以实行,则pre-release版本的类型将受限,因此进一步建议edp支持可配置升级时指定排除alpha或beta或rc版本

otakustay avatar Jul 27 '14 11:07 otakustay

+1

有两个问题:

  1. 12 小时内删除 tag 有什么讲究,为什么是 12 小时?
  2. 这个开发流程,能否基于 git-flow 或 git-ext 实现?

chriswong avatar Jul 27 '14 15:07 chriswong

  1. 这个数字没有科学论证,我大致整出来的,可以讨论来定。我基本上认为12小时是个界限,12小时足够其它人开始用这个更新,同时超过12小时还没发现BUG这次发布也太失败了
  2. git flow我也有试过,事实上我上面描述的模型其实就是个加强的git flow你应该看得出来。git flow就一个缺点,不支持多个版本并行,即我现在主流开发4.2版本,但我不能直接删了3.3,因为有一些功能可能同时要加到两个版本中形成4.33.4,也有可能一些BUG修复要同时放进不同的版本里不断维护。像jq就同时维护1.9和2.0`两个版本,这在git flow只支持release、develop、feature、hotfix、master这几种分支的模型上是办不了的,也有人在git flow的issue上提出了这问题但短时间我看不到那边解决的意愿……至于自己开发工具,以后的事了?

otakustay avatar Jul 27 '14 15:07 otakustay

因此进一步建议edp支持可配置升级时指定排除alpha或beta或rc版本

这个貌似不是通过edp来控制的,因为大部分package都是通过npm来安装的,所以应该是在npm安装的时候指定版本,例如:

npm i -g edp-build@stable edp-project@alpha

leeight avatar Jul 28 '14 02:07 leeight

github上也有不少包是edp import和edp update管理的啊

在 2014年7月28日,上午10:02,leeight [email protected] 写道:

因此进一步建议edp支持可配置升级时指定排除alpha或beta或rc版本

这个貌似不是通过edp来控制的,因为大部分package都是通过npm来安装的,所以应该是在npm安装的时候指定版本,例如:

npm i -g edp-build@stable edp-project@alpha — Reply to this email directly or view it on GitHub.

otakustay avatar Jul 28 '14 02:07 otakustay

如果是edp import或者edp update来用的话,可以在发布package的时候,指定tag,例如:

npm publish --tag [stable | alpha | beta | rc]

如果不指定的话,默认用的是latest这个tag。

我可以升级来edp import的功能默认只用stablerc的tag,不过现在已经发布过的package,貌似是没有办法更新了,不知道人肉去修改数据库是否可以。

leeight avatar Jul 28 '14 02:07 leeight

能不用管publish,只在import时先拿到版本列表,找版本的时候看一下pre-release版本,找最后一个符合条件的吗?

otakustay avatar Jul 28 '14 02:07 otakustay

找版本的时候看一下pre-release版本,找最后一个符合条件的吗?

你指的pre-release应该是非alpha和beta吧,rc和stable都算作pre-release?

leeight avatar Jul 28 '14 05:07 leeight

pre-release指3个数字后面的那部分,没有的就是stable,按这个标准的话是允许alpha、beta、rc

然后我建议是,在dependencies中,可以写er: '3.1.x@stable'这种形式,表示允许3.1.x版本,但不要alpha和beta,除了@stable外,还能写@beta@alpha,分别表示允许选定的类型及之后的(beta包含beta + rc + stable,依次类推)

otakustay avatar Jul 28 '14 05:07 otakustay

另外,这种形式的分支创建不了吧?莫非我用法的问题?

➜  edp-package git:(master) git checkout -b '0.5/feature/hello-world'
error: unable to resolve reference refs/heads/0.5/feature/hello-world: Not a directory
fatal: Failed to lock ref for update: Not a directory

leeight avatar Jul 28 '14 05:07 leeight

你必须没有任何一个分支叫0.5或者0.5/feature,git不允许一个东西又是分支名又是目录

~/Dev/er on ⭠ use-eoo ⌚ 13:49:15
$ git co -b 4.0/features/test
Switched to a new branch '4.0/features/test'

otakustay avatar Jul 28 '14 05:07 otakustay

然后我建议是,在dependencies中,可以写er: '3.1.x@stable'这种形式,表示允许3.1.x版本,但不要alpha和beta,除了@stable外,还能写@beta和@alpha,分别表示允许选定的类型及之后的(beta包含beta + rc + stable,依次类推)


  1. er: 3.1.x,应该是(stable和rc),实际上等同于er: 3.1.x@rc
  2. er: 3.1x@stable,应该只有(stable)
  3. er: 3.1x@beta,应该有(beta, rc, stable)

...

leeight avatar Jul 28 '14 05:07 leeight

OK,这个可以有

otakustay avatar Jul 28 '14 05:07 otakustay

但要考虑到向后兼容,比如我现在已经是3.1.0-beta.2了,edp升级后再update总不能退回3.0.4稳定版去

otakustay avatar Jul 28 '14 05:07 otakustay

因为dependencies中的value可以写的很复杂,例如:

{ "dependencies" :
  { "foo" : "1.0.0 - 2.9999.9999"
  , "bar" : ">=1.0.2 <2.1.2"
  , "baz" : ">1.0.2 <=2.3.4"
  , "boo" : "2.0.1"
  , "qux" : "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0"
  , "asd" : "http://asdf.com/asdf.tar.gz"
  , "til" : "~1.2"
  , "elf" : "~1.2.3"
  , "two" : "2.x"
  , "thr" : "3.3.x"
  }
}

另外,此时再版本号后面再添加'@beta', '@alpha',其实就不是一个合法的semver了。

所以通过命令行添加参数来支持?比如 edp import --tag=alpha 之类的?

leeight avatar Jul 31 '14 06:07 leeight

主要是担心2种情况:

  1. package.json里写完依赖,一次edp import自动安装,这种情况出现在clone一个github包后要看demo的情况
  2. edp update自动升级

那能不能edp update --accept=beta这样所有包都会自动找beta版本呢?

otakustay avatar Jul 31 '14 06:07 otakustay

那能不能edp update --accept=beta这样所有包都会自动找beta版本呢?

再考虑考虑。


@errorrik @otakustay @chriswong @firede @Justineo @kener 现在通过webhook自动发布到edp-registry.baidu.com上面的package,如果create tag的时候,带有alpha, beta, rc之类的话,npm publish的时候会自动添加对应的dist-tag,不会更新latest。如果没有的话,会更新latest这个dist-tag。

所以建议都配置好web hook,发布package的时候不要手工操作了,让那个jenkins的job自己去搞。

leeight avatar Aug 05 '14 02:08 leeight

这货为啥不是在ecomfe/spec下的issue...

errorrik avatar Aug 05 '14 06:08 errorrik

放错地方了……

otakustay avatar Aug 05 '14 06:08 otakustay

总的来说,我觉得:

  1. 需要规范开发过程
  2. 这个过程有点复杂

具体我的问题如下:

  1. 分支name建议以-分隔,我们知道git会出现origin/master这样的分支代表远程分支的本地映射,用/分隔容易凌乱
  2. 功能分支和开发分支是否有些职责重复,是否需要区分这么细?如果按照现在的说明,我们很可能建立开发分支-建立功能分支-开发功能-切换开发分支- 合并功能分支-干掉功能分支
  3. 发布是否有些麻烦?现在看来,如果不发布到npm,没人用的话,bug也看不出来。如果有bug,要修复,npm也必须增加版本号。我觉得,发布后如果发现紧急的BUG,需要修正,也应该是up patch version。如果是这样,那现在的发布流程就有些不必要

errorrik avatar Aug 05 '14 07:08 errorrik

分支name建议以-分隔,我们知道git会出现origin/master这样的分支代表远程分支的本地映射,用/分隔容易凌乱

这个是从git flow参考过来的,分隔符我觉得都可以

功能分支和开发分支是否有些职责重复,是否需要区分这么细?

单个功能一个分支,使用尽量多的分支是能体现git优势的一个良好的开发习惯,至少有助于:

  1. 多个功能并行开发时,一个功能要抛弃了不需要去看回滚哪些commit,直接删除分支即可
  2. 每一个分支都可以做完自己的测试再合并到开发分支,保持开发分支的代码相对稳定
  3. 一个功能开发一半,但另一个比较急的功能要做,现有的功能又处于还不能用的状态,却没有一个分支可以作为base拉取新的功能分支

在我实际的开发过程中,确实出现过很多次1和3的情况,当然如果一个库真的是完全线性开发的,不会有多个功能穿插着来,那确实这个没有太大的帮助

发布是否有些麻烦?现在看来,如果不发布到npm,没人用的话,bug也看不出来。如果有bug,要修复,npm也必须增加版本号。我觉得,发布后如果发现紧急的BUG,需要修正,也应该是up patch version。如果是这样,那现在的发布流程就有些不必要

我想保证的是:

  1. master上的永远对应现在主流版本
  2. 所有release和master的代码都是绝对可用的,不会有“因为开发了一半所以不能参考”这样的情况

发布不能回滚我也比较认同,但会产生“一个版本有严重BUG不能用”这样的问题,而我们没有机制告诉别人哪个版本不能用

otakustay avatar Aug 05 '14 07:08 otakustay

关于功能分支

好吧,确实会有1和3这种情况出现,但是当多个feature同时开发时,如果有多个分支,可能会出现后来的分支需要手工merge的情况。2不是问题,我觉得2是每个开发者都需要保证的。

所以我觉得,这个可以有,但是需要对开发分支和功能分支的作用和使用场景,做一些简单的说明。比如什么场景需要用,什么场景就算了。

关于release

我认同要保证的两点,不过我通常的做法是,dev测试充分后,打tag,master merge。这样就代表了一个发布。相比之下,我没看出一个release分支的好处?

errorrik avatar Aug 05 '14 07:08 errorrik

关于release

release分支有2个作用:

  1. 减去去找最新tag的步骤,很多人上github都不一定找得到tag在哪里,但能切到release分支就一定能看到最新的发布的代码
  2. 如果有一个BUG比较重要紧急,要基于现有发布的版本+仅仅此BUG的修复后发一个新版本,那么可以从release分支接一个hotfix分支出来,开发完merge回release和当前的develop分支,再从release分支重新发版本,这样develop分支上新增的功能(可能还不稳定)不会被一起发布出去。没有release分支的话,要从tag去拉分支,这是个很麻烦的操作,会出现dettached head状态让人头疼

其实基本就是git flow,在它的基础上加了版本空间

otakustay avatar Aug 05 '14 07:08 otakustay

  1. 我认为这个不是理由,找不到tag就去找药
  2. 从release分支接hotfix是从最近的release还是以前的release?

errorrik avatar Aug 05 '14 07:08 errorrik

从release分支接hotfix是从最近的release还是以前的release?

每个大版本(x.x)只有一个release分支,永远代表这个大版本下最新的那个tag的内容

otakustay avatar Aug 05 '14 07:08 otakustay

我其实一直没有完整的走过git flow,因为,感觉实在是有点重

errorrik avatar Aug 05 '14 07:08 errorrik

那我可以基本认为,最大一个release应该==master。

虽然不太属于这个issue的内容,但我还是想问一句:如果从一个从前的大版本的release出来hotfix,搞定后如何merge到最新的release?

因为如果允许从历史release去hotfix,并且无法自动merge到最新release,那就会面临下面的选择:

  1. 手工merge
  2. 不merge

如果不允许,就不会有这些问题

errorrik avatar Aug 05 '14 07:08 errorrik

如果从一个从前的大版本的release出来hotfix,搞定后如何merge到最新的release?

避免这种情况,hotfix肯定在最新发布的版本上搞,不然会出现3.1.43.1.2加上一个BUG修复,其功能比3.1.3更差,这是不合理的

otakustay avatar Aug 05 '14 08:08 otakustay

我原来也认为git flow实在太重,但在维护了这么多仓库,这么多版本之后,慢慢地我改变了想法……

otakustay avatar Aug 05 '14 09:08 otakustay