Git相关依赖介绍

依赖 作用描述
husky 操作 git 钩子的工具(在 git xx 之前执行某些命令)
lint-staged 在提交之前进行 eslint 校验,并使用 prettier 格式化本地暂存区的代码
@commitlint/cli 校验 git commit 信息是否符合规范,保证团队的一致性
@commitlint/config-conventional Anglar 的提交规范
commitizen 基于 Node.js 的 git commit 命令行工具,生成标准化的 commit message
cz-git 一款工程性更强,轻量级,高度自定义,标准输出格式的 commitize 适配器

commit拦截(husky + lint-staged)

我们的目的是在 git commit 之前,先强制执行prettier格式化(防止某些成员开发期间不开启编辑器格式化)、再检查代码规范,如果检查不通过、阻止提交。

1. 新建 .eslintignore + .prettierignore

.eslintignore.prettierignore,内容参考如下:(没使用eslint和prettier可跳过)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.DS_Store
node_modules
dist
.gitignore
.eslintignore
.prettierignore
LICENSE
README.md
yarn.lock

# local env files
.env.local
.env.*.local

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

2. husky:操作git钩子的工具

(1)安装:

1
npm install husky -D

(2)使用(为了添加.husky 文件夹):

1
2
3
4
5
// 1、打开package.json文件,在scripts中添加
"prepare": "husky install"

// 2、添加完成之后,执行如下命令
npm run prepare

3. lint-staged:本地暂存代码检查工具

git commit 之前,会自动使用 prettier 格式化 ignore 之外的代码。格式化后,自动检查所有文件,是否全部符合 eslint 规范。存在不符合规范的代码,git commit 将被终止。

(1)安装:

1
npm install lint-staged -D

(2)添加 ESlint Hook(在.husky 文件夹下添加 pre-commit 文件)

执行以下命令,在husky文件夹下会自动生成pre-commit文件:

1
npx husky add .husky/pre-commit "npm run lint:lint-staged"

作用是通过钩子函数,判断提交的代码是否符合规范,并使用 prettier 格式化代码

(3)项目根目录下手动新增 lint-staged.config.js 文件:

1
2
3
4
5
6
7
8
module.exports = {
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": ["prettier --write--parser json"],
"package.json": ["prettier --write"],
"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
"*.{scss,less,styl,html}": ["stylelint --fix", "prettier --write"],
"*.md": ["prettier --write"]
};

规范commit内容(husky + commit-msg)

commitlint是一个开源的msg的提交规范库,使用 commitlint 可以规范我们每一次的 commit,我们也可以用来自动生成 changeLog 等文件,方便代码管理。

1. commitlint: 制定提交规范

(1)安装:

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

(2)配置命令(在.husky 文件夹下自动生成 commit-msg 文件,执行如下代码自动生成):

1
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

2. commitizen:基于 Node.js 的 git commit 命令行工具,生成标准化的 message

1
2
// 安装 commitizen,如此一来可以快速使用 cz 或 git cz 命令进行启动。
npm install commitizen -D

3. cz-git

(1)指定提交文字规范,一款工程性更强,高度自定义,标准输出格式的 commitizen 适配器

1
npm install cz-git -D

(2)配置 package.json:

1
2
3
4
5
"config": {
"commitizen": {
"path": "node_modules/cz-git"
}
}

(3)新建 commitlint.config.js 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// @see: https://cz-git.qbenben.com/zh/guide
/** @type {import('cz-git').UserConfig} */
/**
* feature:新功能
* update:更新某功能
* fixbug:修补某功能的bug
* refactor:重构某个功能
* optimize: 优化构建工具或运行时性能
* style:仅样式改动
* docs:仅文档新增/改动
* chore:构建过程或辅助工具的变动
*/
module.exports = {
ignores: [commit => commit === "init"],
// commitlint 推荐我们使用 config-conventional 配置去写 commit
extends: ["@commitlint/config-conventional"],
rules: {
// @see: https://commitlint.js.org/#/reference-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",
"docs",
"style",
"refactor",
"perf",
"test",
"build",
"ci",
"chore",
"revert",
"wip",
"workflow",
"types",
"release"
]
]
},
prompt: {
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: "特性: 🚀 新增功能", emoji: "🚀" },
{ value: "fix: 修复", name: "修复: 🧩 修复缺陷", emoji: "🧩" },
{ value: "docs: 文档", name: "文档: 📚 文档变更", emoji: "📚" },
{ value: "style: 格式", name: "格式: 🎨 代码格式(不影响功能,例如空格、分号等格式修正)", emoji: "🎨" },
{ value: "refactor: 重构", name: "重构: ♻️ 代码重构(不包括 bug 修复、功能新增)", emoji: "♻️" },
{ value: "perf: 性能", name: "性能: ⚡️ 性能优化", emoji: "⚡️" },
{ value: "test: 测试", name: "测试: ✅ 添加疏漏测试或已有测试改动", emoji: "✅" },
{ value: "chore: 构建", name: "构建: 📦️ 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)", emoji: "📦️" },
{ value: "ci: 集成", name: "集成: 🎡 修改 CI 配置、脚本", emoji: "🎡" },
{ value: "revert: 回退", name: "回退: ⏪️ 回滚 commit", emoji: "⏪️" },
{ value: "build: 打包", name: "打包: 🔨 项目打包发布", emoji: "🔨" }
],
useEmoji: true,
themeColorCode: "",
scopes: [],
allowCustomScopes: true,
allowEmptyScopes: true,
customScopesAlign: "bottom",
customScopesAlias: "custom",
emptyScopesAlias: "empty",
upperCaseSubject: false,
allowBreakingChanges: ["feat", "fix"],
breaklineNumber: 100,
breaklineChar: "|",
skipQuestions: [],
issuePrefixs: [{ value: "closed", name: "closed: ISSUES has been processed" }],
customIssuePrefixsAlign: "top",
emptyIssuePrefixsAlias: "skip",
customIssuePrefixsAlias: "custom",
allowCustomIssuePrefixs: true,
allowEmptyIssuePrefixs: true,
confirmColorize: true,
maxHeaderLength: Infinity,
maxSubjectLength: Infinity,
minSubjectLength: 0,
scopeOverrides: undefined,
defaultBody: "",
defaultIssues: "",
defaultScope: "",
defaultSubject: ""
}
};

验证

1. 配置package.json命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"scripts": {
"dev": "vite",
"serve": "vite",
"build:dev": "vue-tsc --noEmit && vite build --mode development",
"build:test": "vue-tsc --noEmit && vite build --mode test",
"build:pro": "vue-tsc --noEmit && vite build --mode production",
"preview": "vite preview",
// 以下为必配置
"lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
"lint:prettier": "prettier --write --loglevel warn \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged",
"prepare": "husky install",
"release": "standard-version",
"commit": "git pull && git add -A && git-cz && git push"
}
}

2. 配置完成,提交代码

1
npm run commit

3. 效果如下

img
img


根据 commit message 自动生成 changelog

img

使用参考 conventional-changelog

1. 安装依赖

1
npm install conventional-changelog conventional-changelog-cli --save-dev

2. 添加脚本

1
2
3
4
5
{
"scripts": {
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0"
}
}

3. 生成CHANGELOG.md文件

直接运行下面的命令即可

1
npm run changelog

img

如果type为feat、fix、perf、revert,则该 commit 将肯定出现在 Change log 之中。其他情况(docs、chore、style、refactor、test)由你决定,要不要放入 Change log,建议是不要。

注意:版本的log使用 npm version 才会生成新的log,手动修改 package.json version 不会有新的版本log。

4. package version

npm version [ | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]

参考npm version

npm version 命令用于更新和设置包的版本号,并可以自动创建并提交Git标签以及更新package.json文件。该命令的语法如下:

1
npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease | from-git]

每个参数的含义如下:

  • <newversion>:这是一个可选的参数,表示你要将包的版本号更新为的具体版本号。可以是任何有效的版本号,如1.2.3。如果提供了这个参数,它将直接设置包的版本号为指定的值。

  • major:这是一个可选的参数,表示要执行主版本号升级。执行此命令后,包的主版本号将加1,次版本号和修订号将重置为0。

  • minor:这是一个可选的参数,表示要执行次版本号升级。执行此命令后,包的次版本号将加1,修订号将重置为0。

  • patch:这是一个可选的参数,表示要执行修订号升级。执行此命令后,包的修订号将加1。

  • premajor:这是一个可选的参数,表示要执行主版本号升级,并在版本号后添加一个预发布标签(如-alpha.1)。

  • preminor:这是一个可选的参数,表示要执行次版本号升级,并在版本号后添加一个预发布标签。

  • prepatch:这是一个可选的参数,表示要执行修订号升级,并在版本号后添加一个预发布标签。

  • prerelease:这是一个可选的参数,表示要在当前版本号的基础上添加一个预发布标签。

  • from-git:这是一个可选的参数,用于根据最新的Git提交信息自动确定新版本号。这可以用于生成一个语义版本号,根据Git提交历史中的特性和修复来决定。

  • npm version 命令通常用于升级包的版本号,并且在升级后,它会自动创建一个Git标签,以及在package.json中更新新的版本号。你可以根据项目需求选择不同的参数来执行不同类型的版本升级和预发布标签。

通过使用npm 钩子,可以做一些自动化的东西,例如:

1
2
3
4
5
6
7
{
"scripts": {
"preversion": "npm test",
"version": "npm run build && git add -A dist",
"postversion": "git push && git push --tags && rm -rf build/temp"
}
}

5. 自定义conventional-changelog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
const compareFunc = require('compare-func')
module.exports = {
writerOpts: {
transform: (commit, context) => {
let discard = true
const issues = []

commit.notes.forEach(note => {
note.title = 'BREAKING CHANGES'
discard = false
})
if (commit.type === 'feat') {
commit.type = '✨ Features | 新功能'
} else if (commit.type === 'fix') {
commit.type = '🐛 Bug Fixes | Bug 修复'
} else if (commit.type === 'perf') {
commit.type = '⚡ Performance Improvements | 性能优化'
} else if (commit.type === 'revert' || commit.revert) {
commit.type = '⏪ Reverts | 回退'
} else if (discard) {
return
} else if (commit.type === 'docs') {
commit.type = '📝 Documentation | 文档'
} else if (commit.type === 'style') {
commit.type = '💄 Styles | 风格'
} else if (commit.type === 'refactor') {
commit.type = '♻ Code Refactoring | 代码重构'
} else if (commit.type === 'test') {
commit.type = '✅ Tests | 测试'
} else if (commit.type === 'build') {
commit.type = '👷‍ Build System | 构建'
} else if (commit.type === 'ci') {
commit.type = '🔧 Continuous Integration | CI 配置'
} else if (commit.type === 'chore') {
commit.type = '🎫 Chores | 其他更新'
}


if (commit.scope === '*') {
commit.scope = ''
}
if (typeof commit.hash === 'string') {
commit.hash = commit.hash.substring(0, 7)

}
if (typeof commit.subject === 'string') {
let url = context.repository
? `${context.host}/${context.owner}/${context.repository}`
: context.repoUrl
if (url) {
url = `${url}/issues/`
// Issue URLs.
commit.subject = commit.subject.replace(/#([0-9]+)/g, (_, issue) => {
issues.push(issue)
return `[#${issue}](${url}${issue})`
})
}
if (context.host) {
// User URLs.
commit.subject = commit.subject.replace(/\B@([a-z0-9](?:-?[a-z0-9/]){0,38})/g, (_, username) => {
if (username.includes('/')) {
return `@${username}`
}

return `[@${username}](${context.host}/${username})`
})
}
}

// remove references that already appear in the subject
commit.references = commit.references.filter(reference => {
if (issues.indexOf(reference.issue) === -1) {
return true
}

return false
})
return commit
},
groupBy: 'type',
commitGroupsSort: 'title',
commitsSort: ['scope', 'subject'],
noteGroupsSort: 'title',
notesSort: compareFunc
}
}

最后在 package.json 中的 scripts 添加一条语句

1
2
3
4
5
6
7
8
 "changelog": "conventional-changelog -p custom-config -i CHANGELOG.md -s -r 0  -n ./changelog-option.js"
//在这指定配置文件位置,本人放在了根目录,也可以指定其他地方
/*配置项说明:
-p custom-config 指定风格
-i CHANGELOG.md 指定输出的文件名称
-s -r 0 指定增量更新,不会覆盖以前的更新日志
-n ./changelog-option.js 指定自定义配置
*/

注意:本项目中用到了 custom-config,因此需要先安装 custom-config【单纯的 changelog-option.js 是不完整的配置,在此只修改了自己需要的部分,其他部分默认】,如果不加这个插件,是不生效的。

1
npm i conventional-changelog-custom-config compare-func -D

最后再运行npm run changelog