一、开发分支模型分类

目前所在部门使用是主要是四种:dev(开发)、test(测试)、uat(预发)、release(生产)

小公司可能就一个 dev、一个 master 就搞定了,测试都是开发人员自己来🤣。

二、开发主体流程

  1. 需求评审
  2. 开发排期
  3. 编码开发
  4. 冒烟测试(自检验)
  5. 冒烟通过,提交测试,合并代码到测试分支,部署测试环境
  6. 测试环境测试,开发修 bug
  7. 测试完成,提交预发,合并代码到预发分支,部署预发环境
  8. 预发环境测试,开发修 bug(修完的 bug 要重新走测试再走预发,这个下面会解释)
  9. 测试完成,产品验收
  10. 验收完成,提交生产,合并代码到生产分支,部署生产环境
  11. 生产运营(客户)验收
  12. 验收完成,结项

三、具体操作

1. 拉取代码

一般都会在本地默认创建一个 master 分支

shell

 代码解读
复制代码git clone https://code.xxx.com/xxx/xxx.git

2. 初次开发需求前,要先拉取生产/预发分支,然后基于这个分支之上,创建自己的特性分支进行开发

shell 代码解读复制代码git fetch origin release:release

git checkout release

git checkout -b feat-0131-jie

此时,在你本地已经有了一个 release 分支对应着远程仓库的 release 分支,还有一个内容基于 release 分支的特性分支,之后便可以在这个特性分支上进行需求开发了。

如果不是初次开发,本地已有生产/预发分支,则需要重新拉取远程的最新代码,然后再创建

shell 代码解读复制代码## 小tips:输入已有的分支名时,可以只输入前几个字符,然后按 tab 自动补充
git checkout release

git pull origin release

git checkout -b feat-0229-jie

注意1:分支名称是有规范和含义的,不能乱取。

推荐格式:分支责任-需求日期/需求号-开发人姓名,一般按部门规范来,常见的有以下几种。

js 代码解读复制代码  - feat:新功能

  - fix:修补bug

  - doc:文档

  - refactor:重构(即不是新增功能,也不是修改bug的代码变动)

  - test:测试

  - chore:构建过程或辅助工具的变动

注意2:为啥拉取的是生产/预发分支

之所以要拉取 release/uat 分支而不是拉取 dev/test,是因为后者可能包含着一些其他成员还未上线或者可能有 bug 的需求代码,这些代码没有通过验证,如果被你给拉取了,然后又基于此进行新的需求开发,那当你需求开发完成,而其他成员的需求还没上线,你将会把这些未验证的代码一起发送到 uat/release 上,导致一系列问题。

3. 需求开发完成,提交&合并代码

首先先在本地把新的改动提交,提交描述的格式可以参考着分支名的格式

  • 如果是新需求的提交,可以写成 "feat: 需求0131-新增账期"
  • 如果是 bug 修复,可以写成 "fix: 禅道3387-重复请求"
shell 代码解读复制代码git add .

git commit -m "提交描述"

此时,本地当前分支已经记录了你的提交记录,接下来进行代码合并了

在代码合并之前,我们先要梳理一下我们应该如何对分支进行管理(非常重要!)

  1. 首先,我们需要认知到的是,每一个分支应该只对应一个功能,例如当我们开发需求 01 时,那么就创建一个 feat-01-jie 分支进行开发;开发需求 02 时,就另外创建一个 feat-02-jie 分支进行开发;修改生产环境的某个 bug 时,就创建 fix-jie-3378 进行开发,等等。

    这样做的目的是,能够把不同的功能/需求/修改分离开来。想象一下这样一个场景,如果有某些紧急的需求是需要提前上线的,而此时你的分支里既包含了这些紧急的需求,又包含了其他未开发好的需求,那么这两种需求就不能拆开来分别进行提测和上线了。

  2. 其次,在合并代码时,我们要将四种分支模型(dev、test、uat、release)作为参照物,而不是把关注点放在自己的分支上。比如我们要在 dev 上调试,那就需要把自己的分支合并到 dev 分支上;如果我们需要提测,则把自己的分支合并到 test 分支上,以此类推。

    即,我们要关注到,这四个环境的分支上,会有什么内容,会新增什么内容。切记不能反过来将除了 release 之外的三个分支合并到自己的代码上!! 如果其他成员将自己的代码也提交到 dev 分支上,但是这个代码是没有通过验证的,此时你将 dev 往自己的分支上合,那之后的提测、上预发、生产则很大概率会出问题。所以一定要保持自己的分支是干净的! 而 release 分支对应的是生产环境,一般是最新的稳定版本,所以合并到自己的分支以获取最新的更改也是没什么问题的。

接下来介绍合并代码的方式:

第一种:线上合并,也是推荐的规范操作

shell

 代码解读
复制代码git push origin feat-0131-jie

先接着上面的提交步骤,将自己的分支推送到远程仓库。

然后在线上代码仓库中,申请将自己的分支合并到 xx 分支(具体是哪个分支就根据你当前的开发进度来,如 test),然后在线上解决冲突。如果有权限就自己通过了,如果没有就得找 mt 啥的

第二种,本地合并(前提你要有对应环境分支 push 的权限)

shell 代码解读复制代码## 先切换到你要提交的环境分支上,如果本地还没有就先拉取下来
git fetch origin test:test

git checkout test

## 然后将自己的分支合并到环境分支上(在编辑器解决冲突)
git merge feat-0131-jie

## 最后将环境分支推送到远程仓库
git push origin test
shell 代码解读复制代码## 先切换到你要提交的环境分支上,如果本地已有该分支,则需要先拉取最新代码
git checkout test

git pull origin test

## 然后将自己的分支合并到环境分支上(在编辑器解决冲突)
git merge feat-0131-jie

## 最后将环境分支推送到远程仓库
git push origin test

两种方式有何区别?为什么推荐第一种?

这是因为在团队协作开发的过程中,将合并操作限制在线上环境有以下几个好处:

  1. 避免本地合并冲突:如果多个开发人员同时在本地进行合并操作,并且对同一段代码进行了修改,可能会导致冲突。将合并操作集中在线上环境可以减少此类冲突的发生,因为不同开发人员的修改会先在线上进行合并,然后再通过更新拉取到本地。
  2. 更好的代码审查:将合并操作放在线上环境可以方便其他开发人员进行代码审查。其他人员可以在线上查看合并请求的代码变动、注释和讨论,并提供反馈和建议。这样可以确保代码的质量和可维护性。
  3. 提高可追溯性和可回滚性:将合并操作记录在线上可以更容易地进行版本控制和管理。如果出现问题或需要回滚到之前的版本,可以更轻松地找到相关的合并记录并进行处理。

当然,并非所有情况都适用于第一种方式。在某些特定情况下,例如个人项目或小团队内部开发,允许本地合并也是可以的。但在大多数团队协作的场景中,将合并操作集中在线上环境具有更多优势。

4. 验收完成,删除分支

当我们这一版的需求完成后,本地肯定已经留有了很多分支,这些分支对于之后的开发已经意义不大了,留下来只会看着一团糟。

shell 代码解读复制代码git branch -d <分支名>

## 如果要强制删除分支(即使分支上有未合并的修改)
git branch -D <分支名>

四、一些小问题

1. 前面提到,预发环境修完的 bug 要重新走测试再走预发,为什么呢?

预生产环境是介于测试和生产环境之间的一个环境,它的目的是模拟生产环境并进行更真实的测试。 它是一个重要的测试环境,需要保持稳定和可靠。通过对修复的bug再次提交到测试环境测试,可以确保预生产环境中的软件版本是经过验证的,并且没有明显的问题。

当然,也不是非要这么做不可,紧急情况下,也可以选择直接发到预生产重新测试,只要你保证你的代码 99% 没问题了。

2. 代码合并错误,并且已经推送到远程分支,如何解决?

假设是在本地合并,本来要把特性分支合并到 uat 分支,结果不小心合到了 release 分支(绝对不是我自己的案例,绝对不是。。。虽然好在最后同事本地有我提交前的版本,事情就简单很多了)

首先切换到特性分支合并到的错误分支,比如是 release

shell

 代码解读
复制代码git checkout release

然后查看最近的合并信息(按 q 退出查看)

shell

 代码解读
复制代码git log --merges

撤销合并

shell

 代码解读
复制代码git revert -m 1 <merge commit ID>
  • 这里的 merge commit ID 就是上一步查询出来的 ID 或者 ID 的前几个字符

最后,撤销远程仓库的推送

shell

 代码解读
复制代码git push -f origin release
  • 这个命令会强制推送本地撤销合并后的 release 分支到远程仓库,覆盖掉远程仓库上的内容。(即,得通过一个新的提交来“撤销”上一次的提交,本质上是覆盖)

3. 当前分支有未提交的修改,但是暂时不想提交,想要切换到另一个分支该怎么做?

例如:你正在开发 B 需求,突然产品说 A 需求有点问题,让你赶紧改改,但是当前 B 需求还没开发完成,你又不想留下过多无用的提交记录,此时就可以按照下面这样做:

首先,可以将当前修改暂存起来,以便之后恢复

shell

 代码解读
复制代码git stash

然后切换到目标分支,例如需求 A 所在分支

shell

 代码解读
复制代码git checkout feat-a-jie

修改完 A 需求后,需要先切换回之前的分支,例如需求 B 所在分支

shell

 代码解读
复制代码git checkout feat-b-jie

如果你不确定之前所在的分支名,可以使用以下命令列出暂存的修改以及它们所属的分支:

shell

 代码解读
复制代码git stash list

最后从暂存中恢复之前的修改

shell

 代码解读
复制代码git stash pop

此时你的工作区就恢复如初了!

4. 扩展

本来是想详细补充这部分的一些重要知识的,但是考虑到如果是小白的话,可能一下子接受不了这么多内容。如果你对以下内容感兴趣的话,也可以单独进行搜索,或者看看之后有没有时间可以单独出几篇讲讲🤤

4.1 cherry-pick 指令

  1. 作用:选择某些提交的变更并将其应用到当前分支
  2. 与 merge 的区别:如果你需要另一个分支的所有代码变动。那么就采用 merge;如果你只需要部分代码变动(某几个提交),那么就采用 cherry-pick
  3. 场景:有时候分支不一定是完全按照需求号来开发的,可能按照周期来进行开发,那当前版本内的分支上,可能就会包含着很多需求的提交,这时候,如果产品要求你只上某一个需求,但是其他的暂时还不能上,那就需要使用到 cherry pick 的操作,将该需求囊括的所有提交应用到对应环境分支上。一定要注意梳理清楚被拆分需求是由多少 commit 组成的,不要有遗漏和多选。
  4. 简单示例:
shell 代码解读复制代码## 查看最近提交(按 q 退出查看)
git log

## 切换到要应用的分支(比如提测时)
git checkout test

## 将指定的提交应用于当前分支(commitHash 就是通过第一步查询到的),这会在当前分支产生一个新的提交
git cherry-pick <commitHash>
  1. 了解更多可以阅读:阮一峰的 git cherry-pick 教程

4.2 rebase 指令

  1. 作用:对一个分支做「变基」操作。将当前分支的提交挪动到另一个分支的最新提交之后,这样可以创建一个更线性、清晰的提交历史
  2. 与 merge 的区别:merge 会保留合并分支的提交历史,而 rebase 会改写提交历史
  3. 场景1:合并多次提交纪录。例如之前提到的:“当前分支有未提交的修改,但是暂时不想提交,想要切换到另一个分支该怎么做?”的另一个解决办法,就是先提交了,之后再把这些提交记录合并成一个提交记录。
  4. 场景2:合并分支。
  5. 切记:永远不要 rebase 一个共享分支

注意:rebase 和 cherry-pick 使用不当都会给团队协作带来一些不可预估的问题,请尽量在了解清楚后再进行使用。

4.3 三种广泛使用的 Git 工作流程规范

  1. Git flow
  2. Github flow
  3. Gitlab flow(本文类似于该工作流程)

具体可以阅读:阮一峰的 Git 工作流程

在团队内部使用规范的 Git 工作流程,可以帮助我们提高协作效率,减少混乱和冲突。比如规定好分支结构和命名规范,将使得代码库的分支结构更加清晰明了,更易于管理。反之,开发者可能会按照自己的喜好随意创建分支,导致代码库分支结构混乱。

除此之外还有很多好处,降低错误风险、增加代码可追溯性、简化发布流程、促进持续集成与持续交付等等。

五、写在最后

Git 从发布至今,已经发展了近 20 年,在这期间衍生了成百上千种有关 Git 的管理工具和协同规范,不同公司甚至是公司内部不同集团不同部门使用的规范都可能不尽一致,本文只分享本人在工作过程中真实使用到的开发工作流程,并且个人认为以上内容是具有一定普适性的,能够帮助到新人或者小白的一些基础知识。最后,遵循团队项目规范才能真正提高团队的协作效率。

查看原文