专题: 项目规范化提交

318 阅读8分钟

基础知识

git的提交说明可以分为三个部分: Header, Body, Footer

Header

Header: 包括三个字段: type(必需)scope(可选)subject(必需)

<type>(<scope>): <subject>
example: 
feat(登录模块): 初始化登录页面
// 忽略scope(模块范围)
fix: 修复bug `#1024`

type

用于说明commit的提交类型

描述
feat新增一个功能
fix修复bug
docs文档变更
style代码格式(对功能无影响, 例如空格, 分号, 单引双引等)
refactor代码重构
perf改善性能
test测试
build变更项目构建或外部依赖
ci更新持续集成相关配置或package.json中scripts命令
chore变更构建流程或辅助工具???
revert代码回退

scope

说明commit的影响范围, 或者说功能模块, 且可以省略

subject

是对commit的概要

Body

commit详细说明

Footer

如果提交的代码是不兼容变更关闭Issues, 则Footer是必须的, 否则可以省略 不兼容变更: 当前代码与上一个版本不兼容, 则FooterBREAKING CHANGE开头, 后面是对变动的描述以及变动的理由和迁移方法 提交Demo: image.png

相关库介绍

Commitizen

一个提交的命令行工具库, packageName为: commitizen, 地址为commitizen/cz-cli 启动命令为cz或者git cz

npx cz

cz的默认适配器是streamich/git-cz 执行cz命令会使用streamich/git-cz adapter 执行git cz命令只是同git commit一样工作 什么是适配器呢? 我认为就是一个提交的约定规范, 如上面的Header下的type类型, 就是AngularJS的提交规范.

Commitizen适配器

cz-conventional-changelog

可以生成AngularJS提交规范的适配器, 可直接使用以下命令使用:

commitizen init cz-conventional-changelog --save --save-exact

上面的命令做了三件事:

  1. 安装 cz-conventional-changelog
  2. 将适配器包名保存在package.jsondevDependencies
  3. 将 config.commitizen 键添加到 package.json 文件的根目录,如下所示:
"config": {
  "commitizen": {
    "path": "cz-conventional-changelog"
  }
}

上面命令执行成功之后就可以执行cz命令, 提交符合AngularJS规范的提交说明

cz-customizable

这个适配器可自定义项目的提交规范

安装: npm install cz-customizable --save-devpackage.json中更改Commitzen的适配器路径:

"config": {
  "commitizen": {
    "path": "node_modules/cz-customizable"
  }
}

到根目录下新建.cz-config.js, 这是cz-customizable的配置文件,可在其中自定义提交规范 下面是一个简单的.cz-config.js示例, 官方示例请点击这里

module.exports = {
  // 可选类型
  types: [
    { value: 'feat', name: 'feat:     新功能' },
    { value: 'fix', name: 'fix:      修复' },
    { value: 'docs', name: 'docs:     文档变更' },
    { value: 'style', name: 'style:    代码格式(不影响代码运行的变动)' },
    {
      value: 'refactor',
      name: 'refactor: 重构(既不是增加feature,也不是修复bug)'
    },
    { value: 'perf', name: 'perf:     性能优化' },
    { value: 'test', name: 'test:     增加测试' },
    { value: 'chore', name: 'chore:    构建过程或辅助工具的变动' },
    { value: 'revert', name: 'revert:   回退' },
    { value: 'build', name: 'build:    打包' }
  ],
  // 消息步骤
  messages: {
    type: '请选择提交类型:',
    customScope: '请输入修改范围(可选):',
    subject: '请简要描述提交(必填):',
    body: '请输入详细描述(可选):',
    footer: '请输入要关闭的issue(可选):',
    confirmCommit: '确认使用以上信息提交?(y/n/e/h)'
  },
  // 跳过问题
  skipQuestions: ['body', 'footer'],
  // subject文字长度默认是72
  subjectLimit: 72
}
​

之后就可以使用cz命令进行提交.

Commitizen提交校验

commitlint: 检验提交说明是否符合规范, packageName: @commitlint/cli @commitlint/config-conventional: 符合AngularJS风格的校验规则 安装:

npm i -D @commitlint/cli  @commitlint/config-conventional

在根目录下新建commitlint.config.js, 设置校验规则如下:

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

测试commitlint: echo 'foo: bar' | commitlint 结果如下: image.png image.png 之后可以放在huskyHOOKS:commit-msg中进行使用, 具体请看后面章节: #提交时自动格式化代码

规范化提交

czg版

  1. 安装
npm i -D czg
  1. (可选) 添加配置文件commitlint.config.js
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
​
const scopes = fs
  .readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
  .filter((dirent) => dirent.isDirectory())
  .map((dirent) => dirent.name.replace(/s$/, ''));
​
// precomputed scope
const scopeComplete = execSync('git status --porcelain || true')
  .toString()
  .trim()
  .split('\n')
  .find((r) => ~r.indexOf('M  src'))
  ?.replace(/(/)/g, '%%')
  ?.match(/src%%((\w|-)*)/)?.[1]
  ?.replace(/s$/, '');
​
/** @type {import('cz-git').UserConfig} */
module.exports = {
  ignores: [(commit) => commit.includes('init')],
  extends: ['@commitlint/config-conventional'],
  rules: {
    'body-leading-blank': [2, 'always'],
    'footer-leading-blank': [1, 'always'],
    'header-max-length': [2, 'always', 108],
    'subject-empty': [2, 'never'],
    'type-empty': [2, 'never'],
    'subject-case': [0],
    'type-enum': [
      2,
      'always',
      [
        'feat',
        'fix',
        'perf',
        'style',
        'docs',
        'test',
        'refactor',
        'build',
        'ci',
        'chore',
        'revert',
        'wip',
        'workflow',
        'types',
        'release',
      ],
    ],
  },
  prompt: {
    /** @use `yarn commit :f` */
    alias: {
      f: 'docs: fix typos',
      r: 'docs: update README',
      s: 'style: update code format',
      b: 'build: bump dependencies',
      c: 'chore: update config',
    },
    customScopesAlign: !scopeComplete ? 'top' : 'bottom',
    defaultScope: '',
    scopes: [...scopes, 'mock'],
    allowEmptyIssuePrefixs: false,
    allowCustomIssuePrefixs: false,
​
    // English
    // typesAppend: [
    //   { value: 'wip', name: 'wip:      work in process' },
    //   { value: 'workflow', name: 'workflow: workflow improvements' },
    //   { value: 'types', name: 'types:    type definition file changes' },
    // ],
​
    // 中英文对照版
    messages: {
      type: '选择你要提交的类型 :',
      scope: '选择一个提交范围 (可选):',
      customScope: '请输入自定义的提交范围 :',
      subject: '填写简短精炼的变更描述 :\n',
      body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
      breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
      footerPrefixsSelect: '选择关联issue前缀 (可选):',
      customFooterPrefixs: '输入自定义issue前缀 :',
      footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
      confirmCommit: '是否提交或修改commit ?',
    },
    types: [
      { value: 'feat', name: 'feat:     新增功能' },
      { value: 'fix', name: 'fix:      修复缺陷' },
      { value: 'docs', name: 'docs:     文档变更' },
      { value: 'style', name: 'style:    代码格式' },
      { value: 'refactor', name: 'refactor: 代码重构' },
      { value: 'perf', name: 'perf:     性能优化' },
      { value: 'test', name: 'test:     添加疏漏测试或已有测试改动' },
      { value: 'build', name: 'build:    构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' },
      { value: 'ci', name: 'ci:       修改 CI 配置、脚本' },
      { value: 'revert', name: 'revert:   回滚 commit' },
      { value: 'chore', name: 'chore:    对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
      { value: 'wip', name: 'wip:      正在开发中' },
      { value: 'workflow', name: 'workflow: 工作流程改进' },
      { value: 'types', name: 'types:    类型定义文件修改' },
    ],
    // emptyScopesAlias: 'empty:      不填写',
    // customScopesAlias: 'custom:     自定义',
  },
};
  1. package.json中添加scripts
{
  "scripts": {
    "cz": "git add . && npx czg"
  }
}

然后就可以使用了, npm run cz

cz-git版:

  1. 安装依赖
npm i -D commitizen @commitlint/cli @commitlint/config-conventional cz-git
  1. 添加commitlint.config.js作为@commitlint/clicz-git的配置文件
// commitlint.config.js
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
​
const scopes = fs
  .readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
  .filter((dirent) => dirent.isDirectory())
  .map((dirent) => dirent.name.replace(/s$/, ''));
​
// precomputed scope
const scopeComplete = execSync('git status --porcelain || true')
  .toString()
  .trim()
  .split('\n')
  .find((r) => ~r.indexOf('M  src'))
  ?.replace(/(/)/g, '%%')
  ?.match(/src%%((\w|-)*)/)?.[1]
  ?.replace(/s$/, '');
​
/** @type {import('cz-git').UserConfig} */
module.exports = {
  ignores: [(commit) => commit.includes('init')],
  extends: ['@commitlint/config-conventional'],
  rules: {
    'body-leading-blank': [2, 'always'],
    'footer-leading-blank': [1, 'always'],
    'header-max-length': [2, 'always', 108],
    'subject-empty': [2, 'never'],
    'type-empty': [2, 'never'],
    'subject-case': [0],
    'type-enum': [
      2,
      'always',
      [
        'feat',
        'fix',
        'perf',
        'style',
        'docs',
        'test',
        'refactor',
        'build',
        'ci',
        'chore',
        'revert',
        'wip',
        'workflow',
        'types',
        'release',
      ],
    ],
  },
  prompt: {
    /** @use `yarn commit :f` */
    alias: {
      f: 'docs: fix typos',
      r: 'docs: update README',
      s: 'style: update code format',
      b: 'build: bump dependencies',
      c: 'chore: update config',
    },
    customScopesAlign: !scopeComplete ? 'top' : 'bottom',
    defaultScope: '',
    scopes: [...scopes, 'mock'],
    allowEmptyIssuePrefixs: false,
    allowCustomIssuePrefixs: false,
​
    // English
    // typesAppend: [
    //   { value: 'wip', name: 'wip:      work in process' },
    //   { value: 'workflow', name: 'workflow: workflow improvements' },
    //   { value: 'types', name: 'types:    type definition file changes' },
    // ],
​
    // 中英文对照版
    messages: {
      type: '选择你要提交的类型 :',
      scope: '选择一个提交范围 (可选):',
      customScope: '请输入自定义的提交范围 :',
      subject: '填写简短精炼的变更描述 :\n',
      body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
      breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
      footerPrefixsSelect: '选择关联issue前缀 (可选):',
      customFooterPrefixs: '输入自定义issue前缀 :',
      footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
      confirmCommit: '是否提交或修改commit ?',
    },
    types: [
      { value: 'feat', name: 'feat:     新增功能' },
      { value: 'fix', name: 'fix:      修复缺陷' },
      { value: 'docs', name: 'docs:     文档变更' },
      { value: 'style', name: 'style:    代码格式' },
      { value: 'refactor', name: 'refactor: 代码重构' },
      { value: 'perf', name: 'perf:     性能优化' },
      { value: 'test', name: 'test:     添加疏漏测试或已有测试改动' },
      { value: 'build', name: 'build:    构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' },
      { value: 'ci', name: 'ci:       修改 CI 配置、脚本' },
      { value: 'revert', name: 'revert:   回滚 commit' },
      { value: 'chore', name: 'chore:    对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
      { value: 'wip', name: 'wip:      正在开发中' },
      { value: 'workflow', name: 'workflow: 工作流程改进' },
      { value: 'types', name: 'types:    类型定义文件修改' },
    ],
    // emptyScopesAlias: 'empty:      不填写',
    // customScopesAlias: 'custom:     自定义',
  },
};
  1. package.json中添加配置
{
  "scripts": {
    "cz": "git add . && cz"
  },
  "config": {
    "commitizen": {
      "path": "node_modules/cz-git"
    }
  },
}
  1. 运行 npm run cz

传统版

  1. 安装依赖
npm i commitizen cz-customizable -D
  1. 配置package.json
{
  "config": {
    "commitizen": {
      "path": "node_modules/cz-customizable"
    }
  }
}
  1. 创建提交规范文件 .cz-config.js
module.exports = {
  // 可选类型
  types: [
    { value: 'feat', name: 'feat:     新功能' },
    { value: 'fix', name: 'fix:      修复' },
    { value: 'docs', name: 'docs:     文档变更' },
    { value: 'style', name: 'style:    代码格式(不影响代码运行的变动)' },
    {
      value: 'refactor',
      name: 'refactor: 重构(既不是增加feature,也不是修复bug)'
    },
    { value: 'perf', name: 'perf:     性能优化' },
    { value: 'test', name: 'test:     增加测试' },
    { value: 'chore', name: 'chore:    构建过程或辅助工具的变动' },
    { value: 'revert', name: 'revert:   回退' },
    { value: 'build', name: 'build:    打包' }
  ],
  // 消息步骤
  messages: {
    type: '请选择提交类型:',
    customScope: '请输入修改范围(可选):',
    subject: '请简要描述提交(必填):',
    body: '请输入详细描述(可选):',
    footer: '请输入要关闭的issue(可选):',
    confirmCommit: '确认使用以上信息提交?(y/n/e/h)'
  },
  // 跳过问题
  skipQuestions: ['body', 'footer'],
  // subject文字长度默认是72
  subjectLimit: 72
}
  1. 安装依赖
npm i -D @commitlint/config-conventional @commitlint/cli
  1. 根目录下创建commitlint.config.js,注意, 是UTF8格式.
module.exports = {
  extends: ['@commitlint/config-conventional'],
  roles: {
    'type-enum': [
      2,
      'always',
      [
        'feat',
        'fix',
        'docs',
        'style',
        'refactor',
        'perf',
        'test',
        'chore',
        'revert',
        'build'
      ]
    ],
    'subject-case': [0]
  }
}

提交时自动格式化代码

  1. 安装 husky, 生成配置文件
npm i husky -D

安装完成之后, 在控制台执行下面代码:

npx husky install
  1. husky安装完成后, 执行以下命令添加husky钩子
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

commit-msg这个hook会在提交信息时使用 commitlint进行校验, 至此, 不规范的 commit信息将不可被提交.

  1. 继续添加husky钩子
npx husky add .husky/pre-commit "npx eslint --ext .js,.vue src"

huskypre-commit会在commit前对src下的js, vue文件进行eslint校验, 如有eslint error, 则会中止提交, 抛出错误. 此时存在两个问题:

  • 只是修改了个别文件, 却检测了所有代码文件
  • 只抛出错误, 需手动修改无实际意义的格式问题(如;``空格等)
  1. eslint错误自动修复

a. 安装依赖插件, 该插件可以只更新本次修改的代码, 并且在出现错误的时候, 自动修复并且推送

npm i  lint-staged -D

b. package.json添加配置

{
  "lint-staged": {
    "src/**/*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "package.json": [
      "prettier --write"
    ],
    "*.vue": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{scss,less,styl,html}": [
      "prettier --write"
    ],
    "*.md": [
      "prettier --write"
    ]
  }
}

c. 修改.husky/pre-commit文件

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

以上操作完成之后, 执行npm run cz, 则会自动进行eslint校验``prettier格式化代码``commit msg 信息是否符合标准三重校验.

相关参考:
juejin.cn/post/684490…
github.com/commitizen/…
cz-git.qbb.sh/zh/