[前端项目规范]知晓git commit提交规范以及通晓commitlint

4,836 阅读8分钟

1.前言

在git commit中提交信息中,如果能找按照规范写好,能提高可读性以及项目维护效率。本文针对message的规范提交检测工具进行介绍。

2.参考的规范:Angular Team Commit Specification

详细的提交信息可以让人知道本次提交对原项目进行哪方面的修改。是修复bug、添加组件或模块、修改代码风格还是重构。从message中也可以知道针对哪部分的文档或模块进行修改。

但这个message到底要怎么去写比较好?这里可以参考Angular团队的git-commit-guidelines。以下为该团队对message做出的规范格式:

<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>

每一次提交信息都包含着header、body和footer。头部包括type,scope和subject。

如果本次提交时是对上一次的提交进行回滚,则header需要以revert:开头,在body里需要写明:This reverts commit .hash指的是每次commit提交后git生成的SHA值。每次提交的SHA值可以通过git log查看到:

1.header

在header中,type和subject是必填项,scope是选填项。下面对头部这三个部分进行详细介绍:

(1)type

必填项,用于说明本次提交做出哪种类型的修改,必须是以下任意一值:

  • feat: A new feature(新功能)
  • fix: A bug fix(bug的修复)
  • docs: Documentation only changes(修改项目中的文档)
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)(不影响代码逻辑下的样式修改,通常是风格修改,例如空格、格式、分号方面的修改等)
  • refactor: A code change that neither fixes a bug nor adds a feature(重构,不包括修复bug和添加新功能)
  • perf: A code change that improves performance(性能优化)
  • test: Adding missing or correcting existing tests(添加或者修改测试代码)
  • chore: Changes to the build process or auxiliary tools and libraries such as documentation generation(对构建过程或辅助工具和库(如文档生成)的更改)

(2)scope

选填项,用于说明本次提交中改动的位置。国内通常以项目名/模块名为格式。一次提交中如果有多处修改,官方说可以用*去缺省。但个人倾向于每次提交都是针对单处修改,这样子阅读提交信息会更详细。

(3)subject

必填项,用于简要地描述本次提交中所做出的修改。而且需要满足格式:

  • 如果用英文描述,则英文单词只能是一般现在时或者祈使语气
  • 首字母不能大写
  • 不能以.作为结尾

2.body

选填项,相对于上述的subject,body用于更加详细的描述本次提交中所做出的修改。其中需要包括本次修改的动机以及对比于修改之前的改进之处。

3.footer

选填项,主要包括以下两点:

  • break changes:任何关于与上个版本不兼容的更改(breaking change)的信息,例如版本升级、数据字段更改、接口参数变化等。如果存在breaking change,则需要以BREAKING CHANGE:开头后接空格或者两行新行后,写清楚更改的内容。
  • affect issues:指明本次修改是否针对某次issues。

3.提交信息预格式化工具

(1)commitizen

官方推荐的用于编写合格的提交信息的工具,用于替代git commit指令。详细配置可看:commitizen。我本人没有用到这个工具,是因为vscode提交已经习惯了用按钮去提交,如果项目中用到commitizen,则之后提交需要用git cz替代git commit指令进行提交。会改变整个操作流程,在团队里推广不了。

(2)Commit Message Editor

这里我选择了一款VSCode中的生成格式化提交信息的工具Commit Message Editor,操作可看下图:

(1)普通操作时

(2)使用Angular格式提交信息时

4.提交信息检查工具

1.安装

npm install -D @commitlint/config-conventional @commitlint/cli husky

husky:一个git hooks工具。

commitlint:一个用于检测提交信息格式(commit message format)的工具

2.配置

(1).huskyrc

首先配置husky方面,在根目录新建一个名为.huskyrc(也可以是.huskyrc.json)的文件,写入以下内容:

//.huskyrc
{
  "hooks": {
    "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
  }
}

上述代码目的是为了在git执行commit和merge之前,先执行commitlint -E HUSKY_GIT_PARAMS。从而使commitlint检测提交信息。

**拓展:**git hooks有很多个周期函数,更多可点击查看githooks。有人会问为什么不用"pre-commit""pre-commit-msg"

我们可以在刚刚附注的githooks网址里查看"pre-commit"的解释:

This hook is invoked by git-commit[1], and can be bypassed with the --no-verify option. It takes no parameters, and is invoked before obtaining the proposed commit log message and making a commit. Exiting with a non-zero status from this script causes the git commit command to abort before creating a commit.

看打粗部分的解释,"pre-commit"是在获取已提交的commit log message之前触发的。因此不能检测提交信息。

再看以下"pre-commit-msg"的解释:

This hook is invoked by git-commit[1] right after preparing the default log message, and before the editor is started.

对比以下"commit-msg"的解释:

This hook is invoked by git-commit[1] and git-merge[1], and can be bypassed with the --no-verifyoption. It takes a single parameter, the name of the file that holds the proposed commit log message. Exiting with a non-zero status causes the command to abort.

可以知道,"pre-commit""pre-commit-msg"都是在因git commit触发的,而"commit-msg"是因git commit和git merge触发的,在多人合作项目中避免不了git merge指令,因此我们选择"commit-msg"周期函数。

(2)commitlint.config.js

其后配置commitlint的参数,写入以下内容:

//commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'scope-case': [0]
  }
}

上面代码意思为指定提交信息的检测规则为'@commitlint/config-conventional'。

检测规则有很多类,最常用的是上面采用的Conventional Commits specification。此规则是根据上文所说的Angular Team Commit Specification衍生出来的。commitlint更多规则可看:Shared configuration

**拓展:**上面网址的规则中有两个比较类似的规则:

自己对比看了一下,'@commitlint/config-angular'是几乎满足了本文第二点中介绍的header、body、footer中所有的规则。然后添加了header中的type和scope必须为小写的规则

'@commitlint/config-conventional'是继承'@commitlint/config-angular'的全部规则上还有一些小的约束,例如:

  • header最大长度为100个字符
  • body和footer每行的最大长度为100个字符,注意这里是每行,即可以换行。

添加了以上两个配置后,每次git commit前就会根据规则检查,如果不符合规则,则会中断,例如:

我提交的信息为:ci(.huskyrC): test,因为scope存在大写字母,所以被截止了不能提交。

5.rules的编写

和eslint的规则一样,rules中定义的规则的优先级是最高的。所以当@commitlint/config-conventional里面的部分检测规则不适用于自己的项目时,就可以通过rules改写。如下所示:

"rules": {
  "header-max-length": [0, "always", 72],
}

rules中定义的每一条规则由名称header-max-length配置数组[0, "always", 72]组成。

配置数组中有三个元素,分别为:

  • Level:为0,1,2三个数的其中一值。0代表让本规则无效;1代表以警告提示,不影响编译运行;2代表以错误提示,阻止编译运行。以上面的例子则代表该规则不会起作用。
  • Applicable :为alwaysnever其中一值。never代表反转规则,相当于!取反。
  • Value:适用于该规则的值。以上面的例子则代表header最大的字数只能为72。

针对我自己的项目,因为scope我用于写修改的文件夹名称,所以会存在大写的情况,所以我针对此处用rules修改如下所示:

rules: {
    'scope-case': [0]
}

意为不再让scope的大小写规则校验生效。如果我这时候再次以ci(.huskyrC): test提交,则不会报错。

6.拓展:添加type

针对实际的开发场景中,Angular Team Commit Specification中定义的type可能满足不了开发者应对的修改类型。就如自己的项目开发,有些修改是因为UI的改动而调整组件css样式。因此,团队商量后添加了一个名为ui的type类型。

改动如下:

//commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'scope-case': [0],
    'type-enum': [
      2,
      'always',
      [
        'build',
        'chore',
        'ci',
        'docs',
        'feat',
        'fix',
        'perf',
        'refactor',
        'revert',
        'style',
        'test',
        'ui' //针对css方面的改变新增的类型
      ]
    ]
  }
}

rules中添加针对type-enum的检测,其中的第三个参数value列表中除了原有的类型外,还添加ui作为新的类型。

针对VSCode的插件Commit Message Editor,添加type类型可以在setting.json中添加下面一段:

"commit-message-editor.tokens": [
        {
            "label": "Type",
            "name": "type",
            "type": "enum",
            "options": [
                {
                    "label": "---",
                    "value": ""
                },
                {
                    "label": "build",
                    "description": "Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)"
                },
                {
                    "label": "chore",
                    "description": "Updating grunt tasks etc; no production code change"
                },
                {
                    "label": "ci",
                    "description": "Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)"
                },
                {
                    "label": "docs",
                    "description": "Documentation only changes"
                },
                {
                    "label": "feat",
                    "description": "A new feature"
                },
                {
                    "label": "fix",
                    "description": "A bug fix"
                },
                {
                    "label": "perf",
                    "description": "A code change that improves performance"
                },
                {
                    "label": "refactor",
                    "description": "A code change that neither fixes a bug nor adds a feature"
                },
                {
                    "label": "revert"
                },
                {
                    "label": "style",
                    "description": "Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)"
                },
                {
                    "label": "test",
                    "description": "Adding missing tests or correcting existing tests"
                },
                {
                    "label": "ui",
                    "description": "Modify css"
                }
            ],
            "description": "Type of changes"
        },
        {
            "label": "Scope",
            "name": "scope",
            "description": "A scope may be provided to a commit’s type, to provide additional contextual information and is contained within parenthesis, e.g., <code>feat(parser): add ability to parse arrays</code>.",
            "type": "text",
            "multiline": false,
            "prefix": "(",
            "suffix": ")"
        },
        {
            "label": "Short description",
            "name": "description",
            "description": "Short description in the subject line.",
            "type": "text",
            "multiline": false
        },
        {
            "label": "Body",
            "name": "body",
            "description": "Optional body",
            "type": "text",
            "multiline": true
        },
        {
            "label": "Breaking change",
            "name": "breaking_change",
            "type": "boolean",
            "value": "BREAKING CHANGE: ",
            "default": false
        },
        {
            "label": "Footer",
            "name": "footer",
            "description": "Optional footer",
            "type": "text",
            "multiline": true
        }
    ]

主要是以下这段红框里的代码:

此时使用Commit Message Editor就可以选择名为ui的type,如下所示: