【前端架构】git提交规范

55 阅读5分钟

前端项目如何约束git规范?

如何规范组内童鞋的提交?

如何确保大家的代码在提交到远程仓库之前,都是做过ts或lint检查和格式化,都已经通过单元检测,而且大家的commit 提交的都安装提交规范包含有枚举的类型和主题,方便追踪和管理代码提交?

如何确保大家在提交之前已经合远程仓库同步?

前端项目里,通常是用husky结合git hooks来达到git规范的目的,两者结合可以做的事情有:

ts规范检测

eslint规范检测

commit message规范检查

跑单测

git远程仓库是否同步检查

如何使用Husky

1. 安装husky

npm install --save-dev husky

2. 初始化

npx husky init

这个操作会做3件事:

  1. 在根目录生成.husky文件夹

  2. 在.husky文件夹中生成pre-commit脚本:默认文件里会写npm run test

  3. package.json文件中增加script: "prepare": "husky"

这样,后续在git commit的时候,就会先跑.husky/pre-commit脚本,执行里面写的任何命令,这里就是npm run test命令。

git hooks 简介

git hooks可以理解成和React hooks类似,可以在git的不同阶段去执行自定义的脚本。这些hooks分为两类,一类是客户端hooks,一类是服务端hooks。客户端hooks在我们执行commit和merge的时候触发,而服务端hooks会在我们执行git push之后当服务端接受到该pushed commit的时候会触发。

这里主要讲下客户端git hooks。

客户端git hooks介绍

  1. git commit工作流阶段的hooks:

pre-commit -> prepare-commit-msg -> commit-msg -> post-commit

其中常用的有pre-commit和commit-msg这两个。

2.git push工作流阶段的hooks:

pre-push

  1. git rebase阶段的hooks:

pre-rebase

  1. git merge阶段

post-merge

  1. git checkout阶段

post-checkout

所以,这里原理就是,把相应的git hooks脚本放在.husky文件夹下,以该git hooks的名字命名,比如commit-msg, pre-commit, pre-push, pre-rebase等,然后把需要在对应阶段执行的命令更新到对应的脚本文件中,当我们执行相应的git操作的时候就会自动去执行相应的脚本。

比如在git commit的时候,会先去执行pre-commit, 然后执行prepare-commit-msg,再执行commit-msg,最后执行post-commit。如果有任何一个失败,就不会成功commit。其它阶段也同理。

常用客户端git hooks

下面就常用的3个客户端git hooks介绍以及如何使用。

1. pre-commit

结合lint-staged包,仅针对缓存工作区的文件做相应检查,而非是全局检查,既耽误时间又没必要。

lint-staged 如何结合使用

commit之前针对修改的文件做前置检查,常见的如ts检查、eslint检查、prettier格式化、跑单测等,所有你想在commit之前做的事情。

首先,安装lint-staged包

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

npm install --save-dev lint-staged

其次,配置config文件

可以选用需要的文件格式,也可以再package.json里配置,这里以config.js为例。

注意:如有需要,需要安装相应的npm包,比如eslint需要安装eslint的包,prettier需要安装prettier的包。

格式:key为需要处理的文件,一般会用正则匹配相关类型的文件; value 是需要执行的脚步命令

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

// lint-staged.config.js
module.exports = {
  "src/**/*.(ts|tsx)": () => "tsc --noEmit", // ts check
  "src/**/*.(ts|tsx|js|jsx|mjs)": () => [`eslint --cache --fix`], // eslint check
  "**/*": () => ["prettier --write --ignore-unknown"], // prettier
};

最后,更新.husky/pre-commit脚本

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

npx lint-staged

2. commit-msg

结合commitlint包使用

首先,安装@commitlint/cli。

可以直接extend现有的通用的配置,需要下载相应的包,比如@commitlint/config-conventional。

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

npm install --save-dev @commitlint/cli 
// 按需是否安装 
npm install --save-dev @commitlint/config-conventional

其次,配置config文件

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

module.exports = {
  rules: {
    "body-leading-blank": [1, "always"],
    "body-max-line-length": [2, "always", 100],
    "footer-leading-blank": [1, "always"],
    "footer-max-line-length": [2, "always", 100],
    "header-max-length": [2, "always", 100],
    "scope-case": [2, "always", "lower-case"],
    "subject-case": [
      2,
      "never",
      ["sentence-case", "start-case", "pascal-case", "upper-case"],
    ],
    "subject-empty": [2, "never"],
    "subject-full-stop": [2, "never", "."],
    "type-case": [2, "always", "lower-case"],
    "type-empty": [2, "never"],
    "type-enum": [
      2,
      "always",
      [
        "feat",
        "fix",
        "hotfix",
        "merge",
        "docs",
        "refactor",
        "revert",
        "style",
        "test",
        "chore",
        "ci",
        "perf",
        "build",
        "log",
      ],
    ],
  },
};

当然,可以直接用通用的配置。

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

module.exports = {
  extends: ["@commitlint/config-conventional"],
};

最后,更新.husky/commit-msg脚本

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

npx --no -- commitlint --edit

3. pre-push

一般在将本地代码提交到远程之前,可以和远程代码仓库检查下是否已经同步过远程的master主分支,如果落后master分支,则提示先同步更新远程代码之后再提交。这样可以阻止强制推送,防止代码覆盖等意外。

这里没有第三方的包,而是手写的脚本参考,所以直接更新.husky下对应的脚本文件即可。

.husky/pre-push脚本文件:

--javascripttypescriptbashsqljsonhtmlcssccppjavarubypythongorustmarkdown

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

gitDiffRemote(){
    git fetch origin
    NB_COMMITS_BEHIND=$(git rev-list --left-right --count $1...@ | cut -f1)
    CURRENT_FEATURE=`git symbolic-ref --short -q HEAD`

    if [ "${NB_COMMITS_BEHIND}" -gt "0" ]; then
        echo "\033[0;31m ${CURRENT_FEATURE} 落后远端 $1 ${NB_COMMITS_BEHIND}个提交 \n \033[0m"
        echo "\033[0;31m 请更新远端代码分支:$1 \n \033[0m"
        exit 1
    else
        echo "\033[0;32m ✔ 当前分支已是最新 \033[0m"
    fi
}

gitBranchExists(){
    {
        git rev-parse --verify ${1} > /dev/null 2>&1

        if [ "$?" == "0" ]; then
            gitDiffRemote ${1}
        else
            echo "\033[0;31m ⚠ 远端分支${1}不存在,跳过检验 \033[0m"
        fi
    } || {
        echo "\033[0;31m ⚠ 远端分支${1}不存在,跳过检验 \033[0m"
    }
}

echo "\033[0;32m ➜ Fetching master...\n \033[0m"
gitBranchExists "origin/master"

备注:

如果项目有成熟的单测,建议将单测也加入到git 规范里来。按项目需求,可以再pre-commit阶段或者pre-push阶段做单测检查,约束如果单测不通过不能成功commit或者push。

当然这个得看具体需求,但是好多前端项目这个很难做起来,因为一般业务需求都比较紧急,也不会单独给单测用例预留开发时间,也不会有额外时间去开发,这样单测覆盖率做的会比较差,很容易出现说单测用例未及时更新而导致无效,或者老旧单测无法通过的情况。

当然针对不经常变的utils或者固定的逻辑,建议还是用单测用例覆盖检查,这样可以防止后续因为新手不熟悉业务而改出问题来。而如何写这些成熟的测试用例,还是很考验功底的。

拓展阅读: