复杂项目的版本管理及git分支管理建议
Posted zero13_小葵司
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了复杂项目的版本管理及git分支管理建议相关的知识,希望对你有一定的参考价值。
在复杂项目中,特别是多团队的快速迭代中,版本管理与分支管理,总是我们难以回避的问题,这里分享一下我们在团队中使用的规范,以及对应的每一步的步骤。
为什么需要GIT使用建议
团队开发中,遵循一个合理、清晰的Git使用流程,是非常重要的。
否则,每个人都提交一堆杂乱无章的commit,项目很快就会变得难以协调和维护。
没有规则的分支管理,同样会使版本变得混乱,系统难以进行持续发布、持续集成。
版本号规则
版本命名规范
软件版本号有四部分组成,第一部分为主版本号,第二部分为次版本号,第三部分为修订版本号,第四部分为日期版本号加希腊字母版本号,希腊字母版本号共有五种,分别为base、alpha、beta 、RC 、 release。
版本号修改规则
- 主版本号:当功能模块有较大的变动,比如增加模块或是整体架构发生变化。此版本号由项目决定是否修改。
- 次版本号:相对于主版本号而言,次版本号的升级对应的只是局部的变动,但该局部的变动造成程序和以前版本不能兼容,或者对该程序以前的协作关系产生了破坏,或者 是功能上有大的改进或增强。此版本号由项目决定是否修改。
- 修订版本号:一般是Bug 的修复或是一些小的变动或是一些功能的扩充,要经常发布修订版,修复一个严重 Bug 即可发布一个修订版。此版本号由项目经理决定是否修改。
- 日期版本号:用于记录修改项目的当前日期,每天对项目的修改都需要更改日期版本号。此版本号由开发人员决定是否修改。
- 希腊字母版本号:此版本号用于标注当前版本的软件处于哪个开发阶段,当软件进入到另一个阶段时需要修改此版本号。此版本号由项目决定是否修改。
软件版本阶段说明(字母版本号说明)
- Base:此版本表示该软件仅仅是一个假页面链接,通常包括所有的功能和页面布局,但是页面中的功能都没有做完整的实现,只是做为整体网站的一个基础架构。
- Alpha :软件的初级版本,表示该软件在此阶段以实现软件功能为主,通常只在软件开发者内部交流,一般而言,该版本软件的Bug较多,需要继续修改,是测试版本。测试人员提交Bug经开发人员修改确认之后,发布到测试网址让测试人员测试,此时可将软件版本标注为alpha版。
- Beta :该版本相对于Alpha 版已经有了很大的进步,消除了严重错误,但还需要经过多次测试来进一步消除,此版本主要的修改对象是软件的UI。修改的的Bug 经测试人员测试确认后可发布到外网上,此时可将软件版本标注为 beta版。
- RC :该版本已经相当成熟了,基本上不存在导致错误的Bug,与即将发行的正式版本相差无几。
- Release:该版本意味"最终版本",在前面版本的一系列测试版之后,终归会有一个正式的版本,是最终交付用户使用的一个版本。该版本有时也称标准版。
版本发布周期
- 非紧急情况:首先由测试人员测试并提交Bug,其次开发人员会尽量在当天修复Bug并在第二天发布该版本的alpha版,然后由测试人员测试验证关闭Bug之后在第三天会发布该版本的 beta 版。
- 紧急情况:如果Bug比较紧急可跳过一般流程,由开发人员尽快修复Bug(从master分支切出hotfix-代号 分支),测试确认之后直接发布该版本的 beta版。
版本修改举例说明
如此时版本号为:1.0.0.0321_alpha ,此时为内部测试阶段。
开发人员修复了测试人员提交的bug并经测试人员测试验证关闭bug之后,发布到外网时,此时就进入了软件的下一个阶段,版本号可改为:1.0.0.0321_beta ,如当前日期跟上一个版本号的日期不一样,版本号可改为:1.0.0.0322_beta。
如果修复了一些重大Bug 并按照流程发布到外网时就可发布一个修订版,如1.0.1.0322_beta,日期为发布的当前日期。
如果对软件进行了一些功能上的改进或增强,进行了一些局部变动的时候要修改次版本号,如:1.1.0.0322_beta(上一级有变动时,下级要归零)。
当功能模块有较大变动,增加模块或整体架构发生变化时要修改主版本号,如新增加了退款功能,则版本号要改为:2.0.0.0322_beta 。
GIT分支规划
在工作中,用一个分支来处理开发、测试、发布、线上hotfix等等流程是难以控制的:项目上线前你只想让其他人提交一个小修改,结果fetch下来的是另一个人为下次上线提交上来的几百个文件修改。
GIT提供了良好的分支管理功能,通过对分支的规划管理,能够有效管理项目,保证软件质量,提高工作效率,有利于CI/CD的推进。
我们将分支分为 主分支 与 辅分支。
主分支
master
master分支上存放的应该是随时可供在生产环境中部署的代码(Production Ready state)。当开发活动告一段落,产生了一份新的可供部署的代码时,master分支上的代码会被更新。同时,每一次更新,添加对应的版本号标签(TAG)。
- 打包触发方式:手动触发
- 新产出分支:dev、hotfix-*
- 更新来源分支:release、hotfix-*
- 合并去向分支:无
- 权限:只有master可进行该分支代码合并,developer可提交合并申请由master由master审核通过后合并进分支
- 发现bug:由master切出hotfix-*分支,在该分支进行bug修复并在进行测试,测试通过后将hotfix-*分支代码合并进dev、release、master分支,用master分支进行上线。
dev
dev分支包含开发过程中最新的提交变更。有人会称之为"集成分支"。该分支是自动化每日构建的代码源。
当dev分支上的源码到达一个稳定的状态时,就可以发布版本。所有dev上的变更都会在到达一定阶段时合并进release分支,每次测试环境的发版需要版本更新。
- 打包触发方式:每日构建,手动触发
- 新产出分支:feature-*
- 更新来源分支:feature-、hotfix-、release、自身
- 合并去向分支:feature-*、release
- 权限:无特殊
- 发现bug:可直接在dev分支修复也可切出新的辅分支进行修复。
dev分支是保存当前最新开发成果的分支。通常这个分支上的代码也是可进行每日夜间发布的代码(Nightly build)。因此这个分支有时也可以被称作"integration branch"。
当dev分支上的代码已实现了软件需求说明书中所有的功能,通过了所有的测试后,并且代码已经足够稳定时,就可以将所有的开发成果合并进release分支了。
对于release、master分支上的新提交的代码建议都打上一个新的版本号标签(TAG),供后续代码跟踪使用。因此,每次将dev分支上的代码合并回release分支时,我们都可以认为一个新的可供在准生产环境中部署的版本就产生了。当release分支上的代码合并回master分支时,一个新的可供在生产环境中部署的版本就产生了。
通常而言,"仅在发布新的可供部署的代码时才更新master分支上的代码"是推荐所有人都遵守的行为准则。基于此,理论上说,每当有代码提交到master分支时,我们可以使用Git Hook触发软件自动测试以及生产环境代码的自动更新工作。这些自动化操作将有利于减少新代码发布之后的一些事务性工作。
操作示例:
从master分支切出dev分支:git checkout -b(如果不存在 dev 分支,则加-b,已存在则不加) dev
该版本主要特性都实现且无明显bug时切换至release分支:git checkout release
合并dev分支代码至release:git merge –no-ff dev
辅分支
辅助分支是用于组织解决特定问题的各种软件开发活动的分支。辅助分支主要用于组织软件新功能的并行开发、简化新功能开发代码的跟踪、辅助完成版本发布工作以及对生产代码的缺陷进行紧急修复工作。这些分支与主分支不同,通常只会在有限的时间范围内存在。
“辅助分支”,大体包括如下几类:"管理功能开发"的分支、"帮助构建可发布代码"的分支、"可以便捷的修复发布版本关键BUG"的分支等等。
feature
feature分支(有时也被称作topic分支)是用来为下一发布版本开发新特性。当开始开发一个特性的时候,该feature会成为哪个发布版本的一部分,可能还不知道。feature分支的重点是,只要特性还在开发,该分支就会一直存在,不过它最终会被合并回dev分支(将该特性加入到发布版本中),或者被丢弃(如果试验的结果令人失望)。
feature分支代码可以保存在开发者自己的代码库中而不强制提交到主代码库里,或者某一个小组创建一个临时的用于一个较大功能的开发。
Feature分支每天从dev分支获取最新代码,进行代码合并,避免累积过多更新,导致合并代码时出现太多冲突代码。
Feature分支每天将自测通过没有问题的代码合并后提交至中央仓库,进行每日构建。
- 打包触发方式:自行掌控
- 切出分支:feature-*
- 并入分支:feature-*、dev
- 权限:无特殊
- 发现bug:直接在分支内修改即可
使用示例
- 从dev分支创建新的feature分支:git checkout -b myfeature dev
- 开发完后切换回dev分支:git checkout dev
- 将feature分支合并进dev分支:git merge -no-ff myfeature(no-ff,即not fast forward,其作用是:要求git merge即使在fast forward条件下也要产生一个新的merge commit)(此处,要求采用-no-ff的方式进行分支合并,其目的在于,希望保持原有"Feature branches"整个提交链的完整性)
- 删除feature分支:git branch -d myfeature
- Dev分支代码提交至中央仓库:git push origin dev
release
release分支用于进行生产部署,此分支为迭代内容在dev实现并已基本稳定,此版本发布为beta版。
release分支为准备新的产品版本发布做支持。它允许你在最后时刻检查所有的细节。此外,它还允许你修复小bug以及准备版本发布的元数据(例如版本号,构建日期等等)。在release分支做这些事情之后,dev分支就会显得比较干净,也方便为下一大版本发布添加新的功能。
从 dev分支合并发布分支的时间通常是dev分支(差不多)能反映新版本所期望状态的时候。也就是说,这是时候将版本发布所计划的特性都已经合并回了dev分支。而未来其它版本发布计划的特性则不应该合并,它们必须等到当前的版本分支创建好之后才能合并。
在release分支更新的时候,对应的版本发布才获得一个版本号。在该时刻之前,dev分支反映的是"下一版本"的相关变更,但不知道这"下一版本"到底会成为0.1还是1.0,直到release分支被创建。版本号是在发布分支创建时,基于项目版本号规则确定的。
更新后打上对应版本号标签(TAG)。
- 打包触发方式:并入代码hook触发自动构建,手动触发
- 更新来源分支:dev、hotfix-*、自身
- 合并去向分支:dev、master
- 权限:只有master可进行该分支代码合并,developer可提交合并申请由master由master审核通过后合并进分支
- 发现bug:直接在当前分支修改。
hotfix
hotfix分支和release分支十分类似,它的目的也是发布一个新的产品版本,尽管是不在计划中的版本发布。当产品版本发现未预期的问题的时候,就需要理解着手处理, 这个时候就要用到hotfix分支。当产品版本的重大bug需要立即解决的时候,我们从对应版本的标签创建出一个hotfix分支。
使用hotfix分支的主要作用是(dev分支上的)团队成员可以继续工作,而另外的人可以在hotfix分支上进行快速的产品bug修复。
使用示例:
- 从master创建新的hotfix分支:git checkout -b hotfix-1.2.1 master
- 开始修复工作前:git commit -a -m “Bumpt version to 1.2.1” (然后可以开始问题修复工作)
- 修复后提交:git commit -m “Fixed severe production problem” (在问题修复后,进行第二次提交)
- 返回master分支:git checkout master
- bug修复后合并分支:git merge --no-ff hotfix-1.2.1
- 为master分支打上新版本:git tag -a 1.2.1.0118.release
- 切换至dev分支:git checkout dev
- 合并hotfix至dev分支:git merge --no-ff hotfix-1.2.1
- 销毁hotfix分支:git branch -d hotfix-1.2.1
stgfix
与hotfix类似,用于修复release分支的bug。
gitlab与code review
在了解如何review之前先明确几个观点:
- master分支中的任何版本都是可以立即部署到生产的。
- 每一次master分支的变更都来自于其它分支向master的合并操作。
- 对master、release的修改需要review。
- 借助于gitlab的merge request机制,与上面说的工作流程,我们可以在dev分支合并到release分支、release分支合并到master分支之前,建立merge request,在其他人review完成这次合并之后再正式进行合并,这样的机制将会提高准生产环境以及生产环境的产出质量。
如何review
- 将需要合并到release、master的分支push到gitlab
- 进入工程 -> merge request -> create new merge request
- 选择源分支、目标分支 -> 确定
- review负责人进入merge request,确认没有问题之后选择Auto Merge(或者手动在本地合并之后再push到gitlab),并关闭这个merge request,完成
- 如果发现问题那么在有问题的行下注释,并提醒request的发起人及时修改
打标签
同大多数 VCS 一样,Git 也可以对某一时间点上的版本打上标签。人们在发布某个软件版本(比如 v1.0 等等)的时候,经常这么做。本节我们一起来学习如何列出所有可用的标签,如何新建标签,以及各种不同类型标签之间的差别。
列显已有的标签
列出现有标签的命令非常简单,直接运行 git tag 即可:
$ git tag
v0.1
v1.3
显示的标签按字母顺序排列,所以标签的先后并不表示重要程度的轻重。
我们可以用特定的搜索模式列出符合条件的标签。在 Git 自身项目仓库中,有着超过 240 个标签,如果你只对 1.4.2 系列的版本感兴趣,可以运行下面的命令:
$ git tag -l 'v1.4.2.*'
v1.4.2.1
v1.4.2.2
v1.4.2.3
v1.4.2.4
新建标签
Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。轻量级标签就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。而含附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。
一般我们都建议使用含附注型的标签,以便保留相关信息;当然,如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。
含附注的标签
创建一个含附注类型的标签非常简单,用 -a (译注:取 annotated 的首字母)指定标签名字即可:
$ git tag -a v1.4 -m 'my version 1.4'
$ git tag
v0.1
v1.3
v1.4
而 -m 选项则指定了对应的标签说明,Git 会将此说明一同保存在标签对象中。如果没有给出该选项,Git 会启动文本编辑软件供你输入标签说明。
可以使用 git show 命令查看相应标签的版本信息,并连同显示打标签时的提交对象。
$ git show v1.4
tag v1.4
Tagger: Scott Chacon <schacon@gee-mail.com>
Date: Mon Feb 9 14:45:11 2009 -0800
my version 1.4
commit 15027957951b64cf874c3557a0f3547bd83b3ff6
Merge: 4a447f7... a6b4c97...
Author: Scott Chacon <schacon@gee-mail.com>
Date: Sun Feb 8 19:02:46 2009 -0800
Merge branch 'experiment'
我们可以看到在提交对象信息上面,列出了此标签的提交者和提交时间,以及相应的标签说明。
签署标签
如果你有自己的私钥,还可以用 GPG 来签署标签,只需要把之前的 -a 改为 -s (译注: 取 signed 的首字母)即可:
$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "zero <zero@phoenix-force.com>"
1024-bit DSA key, ID F721C45A, created 2021-01-09
现在再运行 git show 会看到对应的 GPG 签名也附在其内:
$ git show v1.5
tag v1.5
Tagger: zero <zero@phoenix-force.com>
Date: Mon Feb 9 15:22:20 2021 -0800
my signed 1.5 tag
----BEGIN PGP SIGNATURE----
Version: GnuPG v1.4.8 (Darwin)
iEYEABECAAYFAkmQurIACgkQON3DxfchxFr5cACeIMN+ZxLKggJQf0QYiQBwgySN
Ki0An2JeAVUCAiJ7Ox6ZEtK+NvZAj82/
=WryJ
----END PGP SIGNATURE----
commit 15027957951b64cf874c3557a0f3547bd83b3ff6
Merge: 4a447f7... a6b4c97...
Author: zero <zero@phoenix-force.com>
Date: Sun Feb 8 19:02:46 2021 -0800
Merge branch 'experiment'
轻量级标签
轻量级标签实际上就是一个保存着对应提交对象的校验和信息的文件。要创建这样的标签,一个 -a,-s或 -m 选项都不用,直接给出标签名字即可:
$ git tag v1.4-lw
$ git tag
v0.1
v1.3
v1.4
v1.4-lw
v1.5
现在运行 git show 查看此标签信息,就只有相应的提交对象摘要:
$ git show v1.4-lw
commit 15027957951b64cf874c3557a0f3547bd83b3ff6
Merge: 4a447f7... a6b4c97...
Author: zero <zero@phoenix-force.com>
Date: Sun Feb 8 19:02:46 2020 -0800
Merge branch 'experiment'
后期加注标签
你甚至可以在后期对早先的某次提交加注标签。比如在下面展示的提交历史中:
$ git log --pretty=oneline
15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch 'experiment'
a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support
0d52aaab4479697da7686c15f77a3d64d9165190 one more thing
6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch 'experiment'
0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function
4682c3261057305bdd616e23b64b0857d832627b added a todo file
166ae0c4d3f420721acbb115cc33848dfcc2121a started write support
9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile
964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo
8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme
我们忘了在提交 “updated rakefile” 后为此项目打上版本号 v1.2,没关系,现在也能做。只要在打标签的时候跟上对应提交对象的校验和(或前几位字符)即可:
$ git tag -a v1.2 9fceb02
可以看到我们已经补上了标签:
$ git tag
v0.1
v1.2
v1.3
v1.4
v1.4-lw
v1.5
$ git show v1.2
tag v1.2
Tagger: zero <zero@phoenix-force.com>
Date: Mon Feb 9 15:32:16 2021 -0800
version 1.2
commit 9fceb02d0ae598e95dc970b74767f19372d61af8
Author: zero <zero@phoenix-force.com>
Date: Sun Apr 27 20:43:35 2021 -0700
updated rakefile
…
分享标签
默认情况下,git push 并不会把标签传送到远端服务器上,只有通过显式命令才能分享标签到远端仓库。其命令格式如同推送分支,运行 git push origin [tagname] 即可:
$ git push origin v1.5
[new tag] v1.5 -> v1.5
如果要一次推送所有本地新增的标签上去,可以使用 --tags 选项:
$ git push origin --tags
[new tag] v0.1 -> v0.1
[new tag] v1.2 -> v1.2
[new tag] v1.4 -> v1.4
[new tag] v1.4-lw -> v1.4-lw
[new tag] v1.5 -> v1.5
现在,其他人克隆共享仓库或拉取数据同步后,也会看到这些标签。
通过.gitignore文件忽略部分文件
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
Exclude maven wrapper
!/.mvn/wrapper/maven-wrapper.jar
*.class
.svn
_svn
BlueJ files
*.ctxt
Mobile Tools for Java (J2ME)
.mtj.tmp/
Package Files #
*.jar
*.war
*.ear
STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
.nb-gradle/
/.mvn/
/mvnw
/mvnw.cmd
Windows:
Thumbs.db
ehthumbs.db
Desktop.ini
上传忽略列表内文件
有些时候,你想添加一个文件到Git,但发现添加不了,原因是这个文件被.gitignore忽略了:
$ git add App.class
The following paths are ignored by one of your .gitignore files:
App.class
Use -f if you really want to add them.
如果你确实想添加该文件,可以用-f强制添加到Git:
$ git add -f App.class
或者你发现,可能是.gitignore写得有问题,需要找出来到底哪个规则写错了,可以用git check-ignore命令检查:
$ git check-ignore -v App.class
.gitignore:3:*.class App.class
Git会告诉我们,.gitignore的第3行规则忽略了该文件,于是我们就可以知道应该修订哪个规则。
小结
• 忽略某些文件时,需要编写.gitignore;
•.gitignore文件本身要放到版本库里,并且可以对.gitignore做版本管理!
以上是关于复杂项目的版本管理及git分支管理建议的主要内容,如果未能解决你的问题,请参考以下文章