入职新公司一月有余,上线了两个需求,每次上线,都或多或少有一些问题,其问题主要聚焦在代码的分支管理上,虽然知道要如何去做,也在每次操作中仔细确认,但还是出现一些错误,因此,做一个整理。
目前的版本管理方式
目前的版本管理方式类似于GitLab Flow,但又有一些区别
环境说明与分支说明
总共有四个环境:
- 联调环境:用于与后端进行线上联调使用
- 测试环境:当功能自测通过后,需要发布到测试环境给测试工程师进行验证
- 灰度环境:测试工程师在测试环境验证通过后,需要发布到灰度环境回归
- 生产环境:在灰度环境回归完成后,再发布到生产环境,在生产环境回归完成后,即可上线
以上四个环境分别对应了四个分支:
- dev:联调环境分支
- test:测试环境分支
- gray-release:灰度环境分支
- pre-release:生产环境分支
- master:该分支在实际开发中并未使用到,就是个摆设
打包部署流程
目前负责的项目打包是线下打包部署的方式。项目包括PC端和Mobile端,同时,还有一个release项目,打包时,将PC端和Mobile端代码build到release项目中,然后在release项目中将打包后的文件提交到仓库,再通过内部的发布系统发布上线。(PS:根据要打包部署的环境,将项目都切换到对应的分支)
发布系统
发布系统分为:
- 发布系统1:主要部署正式环境和测试环境
- 发布系统2:主要部署灰度环境和联调环境
以发布测试环境的代码为例,其流程是:
- 打开发布系统1
- 填写分支名:test
- 点击发布「测试环境」
开发流程
以开发PC端的新需求为例:
- 打开PC端项目,从
pre-release拉开发分支,以姓名-日期-需求名作为分支名,在该分支上进行开发 - 当后端开发完成后,会将代码部署到联调环境,前后端在联调环境进行联调
- 当联调没有问题后,前后端需要将代码部署到测试环境给测试工程师测试
- 因为在开发期间,
pre-release分支有可能已更新,所以,先要在pre-release分支拉取最新的代码,合并到开发分支 - 再切换到
test分支,并拉取最新的代码,然后将开发分支合并到test分支,到此为止分支合并完成 - 然后打开release项目并切换到test分支,同时,拉取最新的代码
- 在PC端项目build后,会将生成的文件copy到release项目文件夹之下,然后,需要在release项目中将新生成的文件提交到仓库
- 最后,使用公司内部的发布系统,按照上述的发布流程,进行发布
开发中遇到的问题
从上可知,目前的开发、发布流程还是很繁琐的,基本上全是手动完成,在开发中遇到的主要问题有以下几点:
- 拉开发分支时,源分支拉取错误:开发分支的来源只能是pre-release分支,如果从其他分支新建开发分支,测试通过后,合到pre-release分支,就会将很多测试不通过的功能、或者暂时未上线的功能发布到正式环境
- 合并分支时,将其他分支合并到开发分支:在开发过程中,经常需要合并分支,例如:为了保持与pre-release分支同步,需要经常合并pre-release分支到开发分支;当测试时,需要合并开发分支到测试分支等操作都涉及到分支的合并,如果误操作将非pre-release分支合并到开发分支就会导致生产环境有问题
- 线下打包时,打包错了分支:由于是线下打包,所以,可以将任何分支打包到release项目中,并提交发布,这样也会导致错误
- 发布时,分支名或发布环境操作错误:从上可知,在发布时,先要填写分支名,然后再选择要发布的环境,每次发布时都需要这样重复操作,如果不注意,很容易填错了分支,或发错了环境
在我实际操作中,出现过两次错误:
- 将test分支合并到开发分支
- 从test分支新建开发分支
为了避免再次重复第一个错误,我在合并分支时,先在开发分支搜索commit message中是否包含有test字段,然后再确认是否将test分支合并到了开发分支?如果是的,则舍弃当前分支,从pre-release重新建开发分支,再将原开发分支中的代码通过git cherry-pick commit-id拉回到新建的开发分支。当然,仅仅确认test分支还不够,还需要确认是否合并了dev分支、gray-release分支到开发分支,因此,最好是搜索merge字段来逐一确认合并信息。
针对第二个问题就不是那么好确认了,在git中,我并未找到分支是从哪个分支新建的相关信息,唯一能够确认当前分支来源的方式就是通过分析Git Graph
Git中分支
- Git在对文件暂存(
git add)时,会为每个文件计算校验和,该校验和是基于文件内容和目录结构计算出来的 - 每次commit提交时,会创建一个
提交对象,该对象包含的内容有:作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针,普通提交产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象,在Git Graph中的体现就是在合并分支时,如果是三向合并,则可以清晰的看到有两条线合并成一个提交记录的图形,在下文的三向合并方式中可以看到 - 由于每次提交时,在生成的提交对象中,都会有一个指针指向其父对象(首次提交除外),这种结构类似于一个链式结构
Git分支合并
介绍以下两种合并方式
- fast-Foward方式
- 三向合并方式
fast-forward方式合并
当试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”
三向合并方式
在上图中可以看到将hotfix-1分支合并到了master分支的信息:Merge branch 'hotfix-1' into master,在这次合并中做的就是一个三向合并。
因为master所在的提交点(或者说master指针指向的提交点)并不是hotfix-1分支的直接祖先,在合并前,master指针指向的是master: modify4节点,hotfix-1指针指向的是hotfix-1: text.md-fix2节点,合并时,git需要根据这两个节点以及它们的共同祖先feature-1:modify2节点等三个节点做三向合并,并生成一个新的提交记录,然后,将master指针指向它——也就是Merge branch 'hotfix-1' into master这条提交记录,如果在合并的过程中有冲突,例如:master分支与hotfix-1分支修改了同一个位置,git会中止合并,并提示。
Git Graph分析
进行Git Graph分析的目的主要是为了明确以下问题:
- Git Graph中每一条线是否表示一个分支?
- 分支是基于哪个分支创建的?
- 如何确定当前分支包含了哪些分支的内容?或者说合并了哪些分支到当前分支?
要解开这三个问题,需要先熟悉上文中提到的Git中合并分支的方式以及Git中的提交操作
Git Graph中每一条线是否表示一个分支?
按一下流程进行操作
- 新建一个仓库,并在master分支上提交两次修改
- 从master分支新建feature-1分支
- 在feature-1分支上提交两次修改
- 将feature-1合并到master分支上
问题:
- 执行以上操作后,在Git Graph中是否可以看到代表feature-1分支的线?
- 将feature-1合并到master分支时,git是否会为这次合并生成一次提交记录?
从上面三个图可以得出以下结论:
- 创建feature-1分支后,在Git Graph中并没有代表feature-1分支的线
- feature-1合并到master分支也没有生成一次提交记录
- 从分支指针指向的节点作为顶点往下就是该分支中包含的所有提交记录
为什么feature-1合并到master分支没有生成提交记录?
因为这种合并方式在Git中被称为fast-forward,master只是简单的将指针向前移动到feature-1分支指针指向的节点
什么样的合并才会出现提交记录?
从上文中可以知道,如果是三向合并,就会生成一次合并的提交记录
分支是基于哪个分支创建的?
查阅了相关资料后并未发现有命令可以知道分支是基于哪个分支创建的,反过来思考:
为什么需要知道分支是基于哪个分支创建的?
是因为要确定当前分支是否混入了不应该有的代码,例如:上面描述的pre-release分支中包含有test分支的代码
如何确定当前分支混入了其他分支的代码?
从当前分支指针指向的节点向下查找,尤其注意触发三向合并的提交记录
从上图可知,master分支混入了test分支的代码,也可以通过test分支指针指向的节点来向下追查到test分支上的提交信息,从而确定test分支中包含的内容
其他
Git中一些基本知识点
- 新增文件处于未跟踪状态(untrack)
- git add文件后,文件处于暂存状态(staged)
- git commit文件后,文件处于已提交状态(commited)
- 已跟踪的文件被修改后,就处于已修改、已跟踪,但是未暂存的状态,需要使用git add暂存文件
- git diff命令并不会显示自上次提交以来所做的改动,它只会显示尚未暂存的修改,所以,当使用git add暂存了所有的修改文件之后,使用git diff命令查看变动内容时,不会显示任务文件改动的信息。如果需要查看已暂存文件的修改,可使用命令:
git diff --cached/--staged
最后
Git的分支合并策略对理解Git的合并机制至关重要,尤其是三向合并策略。要确定分支上包含有哪些内容,可直接通过Git Graph分析得知。