本文包含以下内容:
- 配置Git commitizen规范Git提交内容
1. 为什么需要commitizen规范
在Git commit时,必要的message是用以描述本次提交的内容的,可以让团队成员清楚的知道本次提交的内容,也可以清晰的表现出不同的分支间的差异,甚至在版本回退时可以简洁明了的找出需要回退的版本,试想如果在版本回退的时候看到一大段糟心的 Commit,是不是会难受。
所以,不以规矩,不成方圆。
3.公式
先来看看提交的公式
1 | <type>(<scope>): <subject> |
- type用以描述commit的类别
- scope用以描述影响的范围
- subject是本次提交的描述信息
2.如何配置
brew install npm 安装npm
sudo npm install -g commitizen 全局安装commitizen
项目中执行初始化 npm init 生成package.json
项目中执行安装cz-customizable , npm install cz-customizable –save-dev
项目目录下创建自定义cz-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'use strict';
module.exports = {
types: [
{value: 'feat', name: 'feat: 新功能'},
{value: 'refactor', name: 'refactor: 代码重构'},
{value: 'WIP', name: 'WIP: 暂时提交'},
{value: 'fix', name: 'fix: bug修复'},
{value: 'perf', name: 'perf: 性能优化'},
{value: 'docs', name: 'docs: 文档变更'},
{value: 'style', name: 'style: 代码格式化'},
{value: 'test', name: 'test: 添加测试用例'},
{value: 'chore', name: 'chore: 构建过程或辅助工具的变动'},
{value: 'revert', name: 'revert: 回滚'}
],
scopes: [
{name: 'api'},
{name: 'service'},
{name: 'dao'}
],
// 比较适合db项目
// allowTicketNumber: true,
// isTicketNumberRequired: true,
// ticketNumberPrefix: 'JIRA-',
// ticketNumberRegExp: '\\d{1,5}',
// it needs to match the value for field type. Eg.: 'fix'
/*
scopeOverrides: {
fix: [
{name: 'merge'},
{name: 'style'},
{name: 'e2eTest'},
{name: 'unitTest'}
]
},
*/
// override the messages, defaults are as follows
messages: {
type: '选择当前的提交类型:',
scope: '\n影响面:',
// used if allowCustomScopes is true
customScope: '自定义影响面:',
subject: '简单描述这次提交:\n',
body: '详细描述这次提交. 试用 "|" 换行:\n',
breaking: '列举主要变化点(可选):\n',
footer: 'List any ISSUES CLOSED by this change (optional). E.g.: #31, #34:\n',
confirmCommit: '确认提交?'
},
allowCustomScopes: true,
allowBreakingChanges: ['fix'],
// skip any questions you want
skipQuestions: ['body', 'footer'],
// limit subject length
subjectLimit: 100,
// min subject length
subjectMin:1
};修改package.json中的配置信息
1
2
3
4
5
6
7
8"config": {
"commitizen": {
"path": "node_modules/cz-customizable"
},
"cz-customizable": {
"config": "cz-config.js"
}
}替换node_modules/cz-customizable中questions.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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178'use strict';
var fs = require('fs');
var path = require('path');
var buildCommit = require('./buildCommit');
var log = require('./logger');
var isNotWip = function(answers) {
return answers.type.toLowerCase() !== 'wip';
};
var cwd = fs.realpathSync(process.cwd());
var packageData = require(path.join(cwd, 'package.json'));
function isValidateTicketNo(value, config) {
if (!value) {
return config.isTicketNumberRequired ? false : true;
}
if (!config.ticketNumberRegExp) {
return true;
}
var reg = new RegExp(config.ticketNumberRegExp);
if (value.replace(reg, '') !== '') {
return false;
}
return true;
}
module.exports = {
getQuestions: function(config, cz) {
// normalize config optional options
var scopeOverrides = config.scopeOverrides || {};
var messages = config.messages || {};
var skipQuestions = config.skipQuestions || [];
messages.type = messages.type || 'Select the type of change that you\'re committing:';
messages.scope = messages.scope || '\nDenote the SCOPE of this change (optional):';
messages.customScope = messages.customScope || 'Denote the SCOPE of this change:';
if (!messages.ticketNumber) {
if (config.ticketNumberRegExp) {
messages.ticketNumber = messages.ticketNumberPattern || 'Enter the ticket number following this pattern (' + config.ticketNumberRegExp + ')\n';
} else {
messages.ticketNumber = 'Enter the ticket number:\n';
}
}
messages.subject = messages.subject || 'Write a SHORT, IMPERATIVE tense description of the change:\n';
messages.body = messages.body || 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n';
messages.breaking = messages.breaking || 'List any BREAKING CHANGES (optional):\n';
messages.footer = messages.footer || 'List any ISSUES CLOSED by this change (optional). E.g.: #31, #34:\n';
messages.confirmCommit = messages.confirmCommit || 'Are you sure you want to proceed with the commit above?';
var questions = [
{
type: 'list',
name: 'type',
message: messages.type,
choices: config.types
},
{
type: 'list',
name: 'scope',
message: messages.scope,
choices: function(answers) {
var scopes = [];
if (scopeOverrides[answers.type]) {
scopes = scopes.concat(scopeOverrides[answers.type]);
} else {
scopes = scopes.concat(config.scopes);
}
if (config.allowCustomScopes || scopes.length === 0) {
scopes = scopes.concat([
new cz.Separator(),
{ name: 'empty', value: false },
{ name: 'custom', value: 'custom' }
]);
}
return scopes;
},
when: function(answers) {
var hasScope = false;
if (scopeOverrides[answers.type]) {
hasScope = !!(scopeOverrides[answers.type].length > 0);
} else {
hasScope = !!(config.scopes && (config.scopes.length > 0));
}
if (!hasScope) {
answers.scope = 'custom';
return false;
} else {
return isNotWip(answers);
}
}
},
{
type: 'input',
name: 'scope',
message: messages.customScope,
when: function(answers) {
return answers.scope === 'custom';
}
},
{
type: 'input',
name: 'ticketNumber',
message: messages.ticketNumber,
when: function() {
return !!config.allowTicketNumber; // no ticket numbers allowed unless specifed
},
validate: function (value) {
return isValidateTicketNo(value, config);
}
},
{
type: 'input',
name: 'subject',
message: messages.subject,
validate: function(value) {
var limit = config.subjectLimit || 100;
if (value.length > limit) {
return 'Exceed limit: ' + limit;
}
const minLength = config.subjectMin || 0;
if (value.length < minLength) {
return 'Less than minimum length:' + minLength;
}
return true;
},
filter: function(value) {
return value.charAt(0).toLowerCase() + value.slice(1);
}
},
{
type: 'input',
name: 'body',
message: messages.body
},
{
type: 'input',
name: 'breaking',
message: messages.breaking,
when: function(answers) {
if (config.allowBreakingChanges && config.allowBreakingChanges.indexOf(answers.type.toLowerCase()) >= 0) {
return true;
}
return false; // no breaking changes allowed unless specifed
}
},
{
type: 'input',
name: 'footer',
message: messages.footer,
when: isNotWip
},
{
type: 'expand',
name: 'confirmCommit',
choices: [
{ key: 'y', name: 'Yes', value: 'yes' },
{ key: 'n', name: 'Abort commit', value: 'no' },
{ key: 'e', name: 'Edit message', value: 'edit' }
],
message: function(answers) {
var SEP = '###--------------------------------------------------------###';
log.info('\n' + SEP + '\n' + buildCommit(answers, config) + '\n' + SEP + '\n');
return messages.confirmCommit;
}
}
];
questions = questions.filter(function(item) {
return !skipQuestions.includes(item.name);
});
return questions;
}
};使用git cz取代git commit -m 进行commit message编辑;