乐趣区

关于前端:前端架构师的-git-功力你有几成火候

本文从前端工程,团队合作,生产部署的角度,介绍架构人员须要把握的 git 实际能力。

纲要预览

本文介绍的内容包含以下方面:

  • 分支管理策略
  • commit 标准与提交验证
  • 误操作的撤回计划
  • Tag 与生产环境
  • 永恒杜绝 443 Timeout
  • hook 实现部署?
  • 终极利用: CI/CD

分支管理策略

git 分支弱小的同时也非常灵活,如果没有一个好的分支管理策略,团队人员随便合并推送,就会造成分支凌乱,各种笼罩,抵触,失落等问题。

目前最风行的分支管理策略,也称工作流(Workflow),次要蕴含三种:

  • Git Flow
  • GitHub Flow
  • GitLab Flow

我司前端团队结合实际状况,制订出本人的一套分支管理策略。

咱们将分支分为 4 个大类:

  • dev-*
  • develop
  • staging
  • release

dev-* 是一组开发分支的统称,包含集体分支,模块分支,修复分支等,团队开发人员在这组分支上进行开发。

开发前,先通过 merge 合并 develop 分支的最新代码;开发实现后,必须通过 cherry-pick 合并回 develop 分支。

develop 是一个独自分支,对应开发环境,保留最新的残缺的开发代码。它只承受 cherry-pick 的合并,不容许应用 merge。

staging 分支对应测试环境。当 develop 分支有更新并且筹备公布测试时,staging 要通过 rebase 合并 develop 分支,而后将最新代码公布到测试服务器,供测试人员测试。

测试发现问题后,再走 dev-* -> develop -> staging 的流程,直到测试通过。

release 则示意生产环境。release 分支的最新提交永远与线上生产环境代码放弃同步,也就是说,release 分支是随时可公布的。

当 staging 测试通过后,release 分支通过 rebase 合并 staging 分支,而后将最新代码公布到生产服务器。

总结下合并规定:

  • develop -> (merge) -> dev-*
  • dev-* -> (cherry-pick) -> develop
  • develop -> (rebase) -> staging
  • staging -> (rebase) -> release

为什么合并到 develop 必须用 cherry-pick?

应用 merge 合并,如果有抵触,会产生分叉;dev-* 分支多而杂,间接 merge 到 develop 会产生盘根错节的分叉,难以理清提交进度。

而 cherry-pick 只将须要的 commit 合并到 develop 分支上,且不会产生分叉,使 git 提交图谱(git graph)永远放弃一条直线。

再有,模块开发分支实现后,须要将多个 commit 合为一个 commit,再合并到 develop 分支,防止了多余的 commit,这也是不必 merge 的起因之一。

为什么合并到 staging/release 必须用 rebase?

release 译为变基,合并同样不会产生分叉。当 develop 更新了许多性能,要合并到 staging 测试,不可能用 cherry-pick 一个一个把 commit 合并过来。因而要通过 rebase 一次性合并过来,并且保障了 staging 与 develop 齐全同步。

release 也一样,测试通过后,用 rebase 一次性将 staging 合并过来,同样保障了 staging 与 release 齐全同步。

commit 标准与提交验证

commit 标准是指 git commit 时填写的形容信息,要合乎对立标准。

试想,如果团队成员的 commit 是随便填写的,在合作开发和 review 代码时,其他人基本不晓得这个 commit 是实现了什么性能,或是修复了什么 Bug,很难把控进度。

为了直观的看出 commit 的更新内容,开发者社区诞生了一种标准,将 commit 依照性能划分,加一些固定前缀,比方 fix:feat:,用来标记这个 commit 次要做了什么事件。

目前支流的前缀包含以下局部:

  • build:示意构建,公布版本可用这个
  • ci:更新 CI/CD 等自动化配置
  • chore:杂项,其余更改
  • docs:更新文档
  • feat:罕用,示意新增性能
  • fix:罕用:示意修复 bug
  • perf:性能优化
  • refactor:重构
  • revert:代码回滚
  • style:款式更改
  • test:单元测试更改

这些前缀每次提交都要写,刚开始很多人还是记不住的。这里举荐一个十分好用的工具,能够主动生成前缀。地址在这里

首先全局装置:

npm install -g commitizen cz-conventional-changelog

创立 ~/.czrc 文件,写入如下内容:

{"path": "cz-conventional-changelog"}

当初能够用 git cz 命令来代替 git commit 命令,成果如下:

而后高低箭抉择前缀,依据提醒即可不便的创立符合规范的提交。

有了标准之后,光靠人的自觉遵守是不行的,还要在流程上对提交信息进行校验。

这个时候,咱们要用到一个新货色 —— git hook,也就是 git 钩子。

git hook 的作用是在 git 动作产生前后触发自定义脚本。这些动作包含提交,合并,推送等,咱们能够利用这些钩子在 git 流程的各个环节实现本人的业务逻辑。

git hook 分为客户端 hook 和服务端 hook。

客户端 hook 次要有四个:

  • pre-commit:提交信息前运行,可查看暂存区的代码
  • prepare-commit-msg:不罕用
  • commit-msg:十分重要,查看提交信息就用这个钩子
  • post-commit:提交实现后运行

服务端 hook 包含:

  • pre-receive:十分重要,推送前的各种查看都在这
  • post-receive:不罕用
  • update:不罕用

大多数团队是在客户端做校验,所以咱们用 commit-msg 钩子在客户端对 commit 信息做校验。

侥幸的是,不须要咱们手动去写校验逻辑,社区有成熟的计划:husky + commitlint

husky 是创立 git 客户端钩子的神器,commitlint 是校验 commit 信息是否合乎上述标准。两者配合,能够阻止创立不合乎 commit 标准的提交,从源头保障提交的标准。

husky + commitlint 的具体应用办法请看这里

误操作的撤回计划

开发中频繁应用 git 拉取推送代码,难免会有误操作。这个时候不要慌,git 反对绝大多数场景的撤回计划,咱们来总结一下。

撤回次要是两个命令:resetrevert

git reset

reset 命令的原理是依据 commitId 来复原版本。因为每次提交都会生成一个 commitId,所以说 reset 能够帮你复原到历史的任何一个版本。

这里的版本和提交是一个意思,一个 commitId 就是一个版本

reset 命令格局如下:

$ git reset [option] [commitId]

比方,要撤回到某一次提交,命令是这样:

$ git reset --hard cc7b5be

下面的命令,commitId 是如何获取的?很简略,用 git log 命令查看提交记录,能够看到 commitId 值,这个值很长,咱们取前 7 位即可。

这里的 option 用的是 --hard,其实共有 3 个值,具体含意如下:

  • --hard:撤销 commit,撤销 add,删除工作区改变代码
  • --mixed:默认参数。撤销 commit,撤销 add,还原工作区改变代码
  • --soft:撤销 commit,不撤销 add,还原工作区改变代码

这里要分外留神 --hard,应用这个参数复原会删除工作区代码。也就是说,如果你的我的项目中有未提交的代码,应用该参数会间接删除掉,不可复原,谨慎啊!

除了应用 commitId 复原,git reset 还提供了复原到上一次提交的快捷方式:

$ git reset --soft HEAD^

HEAD^ 示意上一个提交,可屡次应用。

其实素日开发中最多的误操作是这样:刚刚提交完,忽然发现了问题,比方提交信息没写好,或者代码更改有脱漏,这时须要撤回到上次提交,批改代码,而后从新提交。

这个流程大抵是这样的:

# 1. 回退到上次提交
$ git reset HEAD^
# 2. 批改代码...
...
# 3. 退出暂存
$ git add .
# 4. 从新提交
$ git commit -m 'fix: ***'

针对这个流程,git 还提供了一个更便捷的办法:

$ git commit --amend

这个命令会间接批改以后的提交信息。如果代码有更改,先执行 git add,而后再执行这个命令,比上述的流程更快捷更不便。

reset 还有一个十分重要的个性,就是 真正的后退一个版本

什么意思呢?比如说以后提交,你曾经推送到了近程仓库;当初你用 reset 撤回了一次提交,此时本地 git 仓库要落后于近程仓库一个版本。此时你再 push,近程仓库会回绝,要求你先 pull。

如果你须要近程仓库也后退版本,就须要 -f 参数,强制推送,这时本地代码会笼罩近程代码。

留神,-f 参数十分危险!如果你对 git 原理和命令行不是十分相熟,切记不要用这个参数。

那撤回上一个版本的代码,怎么同步到近程更平安呢?

计划就是上面要说的第二个命令:git revert

git revert

revert 与 reset 的作用一样,都是复原版本,然而它们两的实现形式不同。

简略来说,reset 间接复原到上一个提交,工作区代码天然也是上一个提交的代码;而 revert 是新增一个提交,然而这个提交是应用上一个提交的代码。

因而,它们两复原后的代码是统一的,区别是一个新增提交(revert),一个回退提交(reset)。

正因为 revert 永远是在新增提交,因而本地仓库版本永远不可能落后于近程仓库,能够间接推送到近程仓库,故而解决了 reset 后推送须要加 -f 参数的问题,进步了安全性。

说完了原理,咱们再看一下应用办法:

$ git revert -n [commitId]

把握了原理应用就很简略,只有一个 commitId 就能够了。

Tag 与生产环境

git 反对对于历史的某个提交,打一个 tag 标签,罕用于标识重要的版本更新。

目前广泛的做法是,用 tag 来示意生产环境的版本。当最新的提交通过测试,筹备公布之时,咱们就能够创立一个 tag,示意要公布的生产环境版本。

比方我要发一个 v1.2.4 的版本:

$ git tag -a v1.2.4 -m "my version 1.2.4"

而后能够查看:

$ git show v1.2.4

> tag v1.2.4
Tagger: ruims <2218466341@qq.com>
Date:   Sun Sep 26 10:24:30 2021 +0800

my version 1.2.4

最初用 git push 将 tag 推到近程:

$ git push origin v1.2.4

这里留神:tag 和在哪个分支创立是没有关系的,tag 只是提交的别名。因而 commit 的能力 tag 均可应用,比方下面说的 git resetgit revert 命令。

当生产环境出问题,须要版本回退时,能够这样:

$ git revert [pre-tag]
# 若上一个版本是 v1.2.3,则:$ git revert v1.2.3

在频繁更新,commit 数量宏大的仓库里,用 tag 标识版本显然更清新,可读性更佳。

再换一个角度思考 tag 的用途。

下面分支管理策略的局部说过,release 分支与生产环境代码同步。在 CI/CD(上面会讲到)继续部署的流程中,咱们是监听 release 分支的推送而后触发主动构建。

那是不是也能够监听 tag 推送再触发主动构建,这样版本更新的直观性是不是更好?

诸多用途,还待大家思考。

永恒杜绝 443 Timeout

咱们团队外部的代码仓库是 GitHub,家喻户晓的起因,GitHub 拉取和推送的速度十分慢,甚至间接报错:443 Timeout。

咱们开始的计划是,全员开启 VPN。尽管大多时候速度不错,然而的确有偶然的一个小时,甚至一天,代码死活推不下来,重大影响开发进度。

起初忽然想到,速度慢超时是因为被墙,比方 GitHub 首页打不开。再究其根源,被墙的是拜访网站时的 http 或 https 协定,那么其余协定是不是就不会有墙的状况?

想到就做。咱们发现 GitHub 除了默认的 https 协定,还反对 ssh 协定。于是筹备尝试一下应用 ssh 协定克隆代码。

用 ssh 协定比拟麻烦的一点,是要配置免密登录,否则每次 pull/push 时都要输出账号密码。

GitHub 配置 SSH 的官网文档在这里

英文吃力的同学,能够看这里

总之,生成公钥后,关上 GitHub 首页,点 Account -> Settings -> SSH and GPG keys -> Add SSH key,而后将公钥粘贴进去即可。

当初,咱们用 ssh 协定克隆代码,例子如下:

$ git clone git@github.com:[organi-name]/[project-name]

发现霎时克隆下来了!再测几次 pull/push,速度飞起!

不论你用哪个代码治理平台,如果遇到 443 Timeout 问题,请试试 ssh 协定!

hook 实现部署?

利用 git hook 实现部署,应该是 hook 的高级利用了。

当初有很多工具,比方 GitHub,GitLab,都提供了继续集成性能,也就是监听某一分支推送,而后触发主动构建,并主动部署。

其实,不论这些工具有多少花色,外围的性能(监听和构建)还是由 git 提供。只不过在外围性能上做了与自家平台更好的交融。

咱们明天就抛开这些工具,寻根究底,应用纯 git 实现一个 react 我的项目的主动部署。把握了这套外围逻辑,其余任何平台的继续部署也就没那么神秘了。

因为这一部分内容较多,所以独自拆出去一篇文章,地址如下:

纯 Git 实现前端 CI/CD

终极利用: CI/CD

下面的一些中央也提到了继续集成,继续部署这些字眼,当初,千呼万唤始进去,配角正式退场了!

能够这么说,下面写到的所有标准规定,都是为了更好的设计和实现这个配角 ——— CI/CD。

首先理解一下,什么是 CI/CD?

外围概念,CI(Continuous Integration)译为继续集成,CD 包含两局部,继续交付(Continuous Delivery)和继续部署(Continuous Deployment)

从全局看,CI/CD 是一种通过 自动化流程 来频繁向客户交付利用的办法。这个流程贯通了利用的集成,测试,交付和部署的整个生命周期,统称为“CI/CD 管道”。

尽管都是像流水线一样自动化的管道,然而 CI 和 CD 各有分工。

继续集成 是频繁地将代码集成到骨干分支。当新代码提交,会主动执行构建、测试,测试通过则主动合并到骨干分支,实现了产品疾速迭代的同时放弃高质量。

继续交付 是频繁地将软件的新版本,交付给品质团队或者用户,以供评审。评审通过则能够公布生产环境。继续交付要求代码(某个分支的最新提交)是随时可公布的状态。

继续部署 是代码通过评审后,主动部署到生产环境。继续部署要求代码(某个分支的最新提交)是随时可部署的。

继续部署与继续交付的惟一区别,就是部署到生产环境这一步,是否是自动化

部署自动化,看似是小小的一步,然而在实际过程中你会发现,这反而是 CI/CD 流水线中最难落实的一环。

为什么?首先,从继续集成到继续交付,这些个环节都是由开发团队施行的。咱们通过团队外部合作,产出了新版本的待发布的利用。

然而将利用部署到服务器,这是运维团队的工作。咱们要实现部署,就要与运维团队沟通,然而开发同学不理解服务器,运维同学不理解代码,沟通起来困难重重。

再有,运维是手动部署,咱们要实现主动部署,就要有服务器权限,与服务器交互。这也是个大问题,因为运维团队肯定会顾虑平安问题,因此推动起来节节碰壁。

目前社区成熟的 CI/CD 计划有很多,比方老牌的 jenkins,react 应用的 circleci,还有我认为最好用的 GitHub Action 等,咱们能够将这些计划接入到本人的零碎当中。

这篇文章篇幅曾经很长了,就到这里完结吧。接下来我会基于 GitHub Action 独自出一篇具体的 react 前端我的项目 CI/CD 实际,记得关注我的专栏哦。

我的专栏:前端 devops

退出移动版