前端小微团队的gitlab实践

前端小微团队的gitlab实践

疫情期间我感觉整个人懒散了不少,慢慢有意识要振作起来了,恢复到正常的节奏。最近团队代码库从Gerrit迁移到了Gitlab,为了让前端团队日常开发工作有条不紊高效运转,开发历史可追溯,我也查阅和学习了不少资料。参考业界主流的Git工作流,结合公司业务特质,我也梳理了一套适合自己团队的Git工作流,在这里做下分享。

分支管理

首先要说的是分支管理,分支管理是git工作流的基础,好的分支设计有助于规范开发流程,也是CI/CD的基础。

分支策略

业界主流的git工作流,一般会分为develop, release, master, hotfix/xxx, feature/xxx等分支。各个分支各司其职,贯穿了整个开发,测试,部署流程。我这里也基于主流的分支策略做了一些定制,下面用一张表格简单概括下:

分支名 分支定位 描述 权限控制
develop 开发分支 不可以在develop分支push代码,应新建feature/xxx进行需求开发。迭代功能开发完成后的代码都会merge到develop分支。 Develper不可直接push,可发起merge request
feature/xxx 特性分支 针对每一项需求,新建feature分支,如feature/user_login,用于开发用户登录功能。 Develper可直接push
release 提测分支 由develop分支合入release分支。ps: 应配置此分支触发CI/CD,部署至测试环境。 Maintainer可发起merge request
bug/xxx 缺陷分支 提测后发现的bug,应基于develop分支创建bug/xxx分支修复缺陷,修改完毕后应合入develop分支等待回归测试。
master 发布分支 master应处于随时可发布的状态,用于对外发布正式版本。ps: 应配置此分支触发CI/CD,部署至生产环境。 Maintainer可发起merge request
hotfix/xxx 热修复分支 处理线上最新版本出现的bug Develper可直接push
fix/xxx 旧版本修复分支 处理线上旧版本的bug Develper可直接push

一般来说,develop, release, master分支是必备的。而feature/xxx, bug/xxx, hotfix/xxx, fix/xxx等分支纯属一种语义化的分支命名,如果要简单粗暴一点,这些分支可以不分类,都命名为issue/issue号,比如issue/1,但是要在issue中说明具体问题和待办事项,保证开发工作可追溯。

保护分支

利用Protected Branches,我们可以防止开发人员错误地将代码push到某些分支。对于普通开发人员,我们仅对develop分支提供merge权限。

保护分支

具体操作案例请前往下面的实战案例一节查看。

issue驱动工作

我们团队采用的敏捷开发协作平台是腾讯的TAPD,日常迭代需求,缺陷等都会在TAPD上记录。为了让Gitlab代码库能与迭代日常事务关联上,我决定用Gitlab issues来做记录,方便追溯问题。

里程碑

里程碑Milestone可以认为是一个阶段性的目标,比如是一轮迭代计划。里程碑可以设定时间范围,用来约束和提醒开发人员。

milestones

里程碑可以拆解为N个issue,新建issue时可以关联里程碑。比如这轮迭代一共5个需求,那么就可以新建5个issue。在约定的时间范围内,如果一个里程碑关联的所有issueClosed掉了,就意味着目标顺利达成。

创建issue

标签

Gitlab提供了label来标识和分类issue,我觉得这是一个非常好的功能。我这里列举了几种label,用来标识issue分类紧急程度

标签管理

issue分类

所有的开发工作应该通过issue记录,包括但不限于需求缺陷开发自测试用户体验等范畴。

需求&缺陷

这里大概又分为两种情况,一种是TAPD记录在案的需求和缺陷,另一种是与产品或测试人员口头沟通时传达的简单需求或缺陷(小公司会有这种情况…)。

对于TAPD记录的需求和缺陷,创建issue时应附上链接,方便查阅(上文中已有提到)。

对于口头沟通的需求和缺陷,我定了个规则,要求提出人本人在Gitlab上创建issue,并将需求或缺陷简单描述清楚,否则口头沟通的开发工作我不接(也是为了避免事后扯皮)。

ps:其实要求产品或者测试提issue,还不如上Tapd记录。我定这么个规则,其实就是借Gitlab找个说辞,杜绝口头类需求或缺陷,哈哈。

开发自测试

开发者自己发现了系统缺陷或问题,此时应该通过issue记录问题,并创建相应分支修改代码。

自测试issue

实战案例

我前面也说了,我的原则是issue驱动开发工作。

下面用几个例子来简单说明基本的开发流程。小公司里整个流程比较简单,没有复杂的集成测试,多轮验收测试,灰度测试等。我甚至连单元测试都没做(捂脸…)。

公共库和公共组件其实是很有必要做单元测试的,这里立个flag,后面一定补上单元测试。

需求开发

feature/1,一个特性分支,对应issue 1

创建需求

正常的需求当然来源于产品经理等需求提出方,由于是通过示例说明,这里我自己在TAPD上模拟着写一个需求。

TAPD创建需求

创建issue

创建Gitlab issue,链接到TAPD中的相关需求。

创建issue

一个issue

创建分支&功能开发

基于develop分支创建feature分支进行功能开发(要保证本地git仓库当前处于develop分支,且与远程仓库develop分支同步)。

1
git checkout -b feature/1

或者直接以远程仓库的develop分支为基础创建分支。

1
git checkout -b feature/1 origin/develop

ps:我这里用的feature/1作为分支名,其实这里的1是用的issue号,并没有用诸如feature/login_verify之类的名字,是因为我觉得用issue号可以更方便地找到对应的issue,更容易追踪代码。

接着我们开始开发新功能……

快乐地撸代码

commit & push

完成功能开发后,我们需要提交代码并同步到远程仓库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PS D:\projects\gitlab\project_xxx> git add .
PS D:\projects\gitlab\project_xxx> git cz
cz-cli@4.0.3, cz-conventional-changelog@3.1.0

? Select the type of change that you're committing: feat: A new feature
? What is the scope of this change (e.g. component or file name): (press enter to skip)
? Write a short, imperative tense description of the change (max 94 chars):
(9) 登录校验功能
? Provide a longer description of the change: (press enter to skip)

? Are there any breaking changes? No
? Does this change affect any open issues? Yes
? If issues are closed, the commit requires a body. Please enter a longer description of the commit itself:
-
? Add issue references (e.g. "fix #123", "re #123".):
fix #1

git push origin HEAD

git cz是利用了commitizen来替代git commit。详情请点击前端自动化部署的深度实践深入了解。

fix #1用于关闭issue 1

git push origin HEAD则代表推送到远程仓库同名分支。

创建Merge Request

开发人员发起Merge Request,请求将自己开发的功能特性合入develop分支。

创建Merge Request

接着Maintainer需要Review代码,确认无误后同意Merge。然后这部分代码就在远程Git仓库入库了,其他开发同学拉取develop分支就能看到了。

版本提测

issue/2,处理更新日志,版本号等内容,对应issue 2

每个团队的开发节奏都不同,有的团队会每日持续集成发版本提测,有的可能两天一次,这个就不深入讨论了……

那么当我们准备提测时,应该怎么做呢?

通过上节的了解,我们已经知道,迭代内的功能需求都会通过feature/xxx分支合入到develop分支。

提测前,一般来说,还是要更新下CHANGELOG.mdpackage.json的版本号,可以由Maintainer或其他负责该项事务的同学执行。

主要是执行npm version major/minor/patch -m ‘something done’,具体操作可以参考前端自动化部署的深度实践一文。

1
2
3
4
git checkout -b issue/2 origin/develop
npm version minor -m '迭代1第一次提测'
git push origin HEAD
然后发起merge request合入develop分支

此时,应以最新的develop分支代码在开发环境跑一遍功能,保证版本自测通过。

提测时,由Maintainer发起Merge Request,将develop分支代码合入release分支,此时自动触发Gitlab CI/CD,自动构建并发布至测试环境

版本提测后,各责任人应在TAPD上将相关需求和缺陷的状态变更为“测试中”

修复测试环境bug

bug/3,一个bug分支,对应issue 3

这里说的是在迭代周期内由测试工程师发现的测试环境中的系统bug,这些bug会被记录在敏捷开发协作平台TAPD上。修复测试环境bug的步骤与开发需求类似,这里简单说下步骤:

  1. 在Gitlab上创建issue

    创建issue,并附上TAPD上的缺陷链接,方便追溯

  2. 创建分支&修复缺陷

    基于develop分支创建分支:

    1
    2
    // 3是issue号
    git checkout -b bug/3 origin/develop

    接着改代码……

  3. commit & push

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    PS D:\projects\gitlab\project_xxx> git cz
    cz-cli@4.0.3, cz-conventional-changelog@3.1.0

    ? Select the type of change that you're committing: fix: A bug fix
    ? What is the scope of this change (e.g. component or file name): (press enter to skip)
    ? Write a short, imperative tense description of the change (max 95 chars):
    (11) 修复一个测试环境bug
    ? Provide a longer description of the change: (press enter to skip)

    ? Are there any breaking changes? No
    ? Does this change affect any open issues? Yes
    ? If issues are closed, the commit requires a body. Please enter a longer description of the commit itself:
    -
    ? Add issue references (e.g. "fix #123", "re #123".):
    fix #3

    git push origin HEAD
  4. 发起Merge Request

    开发人员发起Merge Request,请求将自己修复缺陷引入的代码合入develop分支。

    然后Maintainer需要Review代码,同意本次Merge Request

  5. 等待回归测试

    bug将在下一次CI/CD后,进入回归测试流程。

  6. 级别高的测试环境Bug

    如果是级别很高的bug,比如影响了系统运行,导致测试人员无法正常测试的,应以release分支为基础创建bug分支,修改完毕后合入release分支,再发起cherry pick合入develop分支。

发布至生产环境

经过几轮持续集成和回归测试后,一个迭代版本也慢慢趋于稳定,此时也迎来了激动人心的上线时间。我们要做的就是把通过了测试的release分支合入master分支。

release合入master

这一步相对简单,但是要特别注意权限控制(防止生产环境事故),具体权限控制可以回头看第一章节分支管理

release分支类似,master分支自动触发Gitlab CI/CD,自动构建并发布至生产环境

线上回滚

revert/4,一个回滚分支,对应issue 4

代码升级到线上,但是发现报错,系统无法正常运行。此时如果不能及时排查出问题,那么只能先进行版本回退操作。

先说说惯性思维下,我的版本回退做法。

首先还是创建issue记录下:

创建记录回滚的issue

基于master分支创建revert/4分支

1
git checkout -b revert/4 origin/master

假设当前版本是1.1.0,我们想回退到上个版本1.0.3。那么我们需要先查看下1.0.3版本的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PS D:\tusi\projects\gitlab\projectname> git show 1.0.3
commit 90c9170a499c2c5f8f8cf4e97fc49a91d714be50 (tag: 1.0.3, fix/1.0.2_has_bug)
Author: tusi <tusi@xxx.com.cn>
Date: Thu Feb 20 13:29:31 2020 +0800

fix:1.0.2

diff --git a/README.md b/README.md
index ac831d0..2ee623b 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,8 @@

只想修改旧版本的bug,不改最新的master

+1.0.2版本还是有个版本,再修复下
+
特性2提交

特性3提交

主要是取到1.0.3版本对应的commit id,其值为90c9170a499c2c5f8f8cf4e97fc49a91d714be50

接着,我们根据commit id进行reset操作,再推送到远程同名分支。

1
2
git reset --hard 90c9170a499c2c5f8f8cf4e97fc49a91d714be50
git push origin HEAD

接着发起Merge Requestrevert/4分支合入master分支。

回滚分支合入master

一般来说,这波操作没什么问题,能解决常规的回滚问题。

临时变通

由于master分支是保护分支,设置了不可push。如果不想通过merge的方式回滚,所以只能先临时设置Maintainer拥有push权限,然后由Maintainer进行回滚操作。

1
2
3
4
5
git checkout master
git pull
git show 1.0.3
git reset --hard 90c9170a499c2c5f8f8cf4e97fc49a91d714be50
git push origin HEAD

完事后,还需要记得把master设置为不可push

Q: 为什么不让Maintainer一直拥有masterpush权限?

A: 主要还是为了防止出现生产环境事故,给予临时性权限更稳妥!

git reset –hard存在什么问题?

如题,git reset --hard确实是存在问题的。git reset --hard属于霸道玩法,直接移动HEAD指针,会丢弃之后的提交记录,如果不慎误操作了也别慌,还是可以通过查询git reflog找到commitId抢救回来的;git reset后还存在一个隐性的问题,如果与旧的branch进行merge操作时,会把git reset回滚的代码重新引入。那么怎么解决这些问题呢?

一筹莫展

别慌,这个时候你必须掏出git revert了。

Q: git revert的优势在哪?

A: 首先,git revert是通过一次新的commit来进行回滚操作的,HEAD指针向前移动,这样就不会丢失记录;另外,git revert也不会引起merge旧分支时误引入回滚的代码。最重要的是,git revert在回滚的细节控制上更加优秀,可解决部分回滚的需求。

举个栗子,本轮迭代团队共完成需求2项,而上线后发现其中1项需求有致命性缺陷,需要回滚这个需求相关的代码,同时要保留另一个需求的代码。

我太难了

首先我们查看下日志,找下这两个需求的commitId

1
2
3
4
5
PS D:\tusi\projects\test\git_test> git log --oneline
86252da (HEAD -> master, origin/master, origin/HEAD) 解决冲突
e3cef4e (origin/release, release) Merge branch 'develop' into 'release'
f247f38 (origin/develop, develop) 需求2
89502c2 需求1

我们利用git revert回滚需求1相关的代码

1
git revert -n 89502c2

这时可能要解决冲突,解决完冲突后就可以push到远程master分支了。

1
2
3
git add .
git commit -m '回滚需求1的相关代码,并解决冲突'
git push origin master

感觉还是菜菜的,如果大佬们有更优雅的解决方案,求指导啊!

修复线上bug

hotfix/5,一个线上热修复分支,对应issue 5

比如线上出现了系统无法登录的bug,测试工程师也在TAPD提交了缺陷记录。那么修复线上bug的步骤是什么呢?

  1. 创建issue,标题可以从TAPD中的Bug单中copy过来,而描述就贴上Bug单的链接即可。

  2. 基于master分支创建分支hotfix/5

    1
    git checkout -b hotfix/5 origin/master
  3. 撸代码,修复此bug……

  4. 修复完此bug后,提交该分支代码到远程仓库同名分支

    1
    git push origin HEAD
  5. 然后发起cherry pick合入到masterdevelop分支,并在master分支打上新的版本tag(假设当前最大的tag1.0.0,那么新的版本tag应为1.0.1)。

修复线上旧版本bug

fix/6,一个线上旧版本修复分支,对应issue 6

某些项目产品可能会有多个线上版本同时在运行,那么不可避免要解决旧版本的bug。针对线上旧版本出现的bug,修复步骤与上节类似。

  1. 创建issue,描述清楚问题

  2. 假设当前版本是1.0.0,而0.9.0版本出了一个bug,应基于tag 0.9.0创建fix分支。

    1
    git checkout -b fix/6 0.9.0
  3. 修复缺陷后,应打上新的tag 0.9.1,并推送到远程。

    1
    2
    git tag 0.9.1
    git push origin tag 0.9.1
  4. 如果此bug也存在于最新的master分支,则需要git push origin HEAD提交该fix分支代码到远程仓库同名分支,然后发起cherry pick合入到master,此时很大可能存在冲突,需要解决冲突。

    cherry pick

cherry pick

在了解到cherry pick之前,我一直认为只有git merge可以合并代码,也好几次遇到合入了不想要的代码的问题。有了cherry pick,我们就可以合并单次提交记录,解决git merge时合并太多不想要的内容的烦恼,在解决bug时特别有用。

git rebase

经过这段时间的使用,我发现使用git merge合并分支时,会让git logGraph图看起来有点吃力。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
PS D:\tusi\projects\gitlab\projectname> git log --pretty --oneline --graph
* 7f513b0 (HEAD -> develop) Merge branch 'issue/55' into 'release'
|\
| * 1c94437 (origin/issue/55, issue/55) fix: 【bug】XXX1
| * c84edd6 Merge branch 'release' of host:project_repository into release
| |\
| |/
|/|
* | 115a26c Merge branch 'develop' into 'release'
|\ \
| * \ 60d7de6 Merge branch 'issue/30' into 'develop'
| |\ \
| | * | 27c59e8 (origin/issue/30, issue/30) fix: 【bug】XXX2
| | | * ea17250 Merge branch 'release' of host:project_repository into release
| | | |\
| |_|_|/
|/| | |
* | | | 9fd704b Merge branch 'develop' into 'release'
|\ \ \ \
| |/ / /
| * | | a774d26 Merge branch 'issue/30' into 'develop'
| |\ \ \
| | |/ /

接着我就了解到了git rebase,变基,哈哈哈。由于对rebase了解不深,目前也不敢轻易改用rebase,毕竟rebase还是有很多隐藏的坑的,使用起来要慎重!在这里先挖个坑吧,后面搞懂了再填坑……

注意事项

  1. 一般而言,自己发起的Merge Request必须由别的同事Review并同意合入,这样更有利于发现代码问题。
  2. 对了,TAPD还支持与Gitlab协同的。详情见源码关联指引

结语

实践证明,这套Git工作流目前能覆盖我项目开发过程中的绝大部分场景。不过要注意的是,适合自己的才是最好的,盲目采用别人的方案有时候是会水土不服的。

以上所述纯属前端小微团队内部的Gitlab实践,必然存在着很多不足之处,如有错误之处还请指正,欢迎交流。

公众号-前端司南

# git, gitlab
You forgot to set the qrcode for Alipay. Please set it in _config.yml.
You forgot to set the qrcode for Wechat. Please set it in _config.yml.
You forgot to set the business and currency_code for Paypal. Please set it in _config.yml.
You forgot to set the url Patreon. Please set it in _config.yml.
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×