@@ -18,10 +18,7 @@ WHITE=
18
18
CYAN=
19
19
NC=
20
20
21
- #
22
21
# Set colour variables if the output should be coloured.
23
- #
24
-
25
22
set_colors () {
26
23
local default_color=$( git config --get hooks.goodcommit.color || git config --get color.ui || echo ' auto' )
27
24
if [[ $default_color == ' always' ]] || [[ $default_color == ' auto' && -t 1 ]]; then
@@ -34,10 +31,7 @@ set_colors() {
34
31
fi
35
32
}
36
33
37
- #
38
34
# Set the hook editor, using the same approach as git.
39
- #
40
-
41
35
set_editor () {
42
36
# $GIT_EDITOR appears to always be set to `:` when the hook is executed by Git?
43
37
# ref: http://stackoverflow.com/q/41468839/885540
@@ -49,10 +43,7 @@ set_editor() {
49
43
test -z " ${HOOK_EDITOR} " && HOOK_EDITOR=' vi'
50
44
}
51
45
52
- #
53
46
# Output prompt help information.
54
- #
55
-
56
47
prompt_help () {
57
48
echo -e " ${RED} $( cat << -EOF
58
49
e - edit commit message
62
53
) ${NC} "
63
54
}
64
55
65
- #
66
56
# Add a warning with <line_number> and <msg>.
67
- #
68
-
69
57
add_warning () {
70
58
local line_number=$1
71
59
local warning=$2
72
60
WARNINGS[$line_number ]=" ${WARNINGS[$line_number]} $warning ;"
73
61
}
74
62
75
- #
76
63
# Output warnings.
77
- #
78
-
79
64
display_warnings () {
80
65
if [ $SKIP_DISPLAY_WARNINGS -eq 1 ]; then
81
66
# if the warnings were skipped then they should be displayed next time
98
83
) ${NC} "
99
84
}
100
85
101
- #
102
86
# Read the contents of the commit msg into an array of lines.
103
- #
104
-
105
87
read_commit_message () {
106
88
# reset commit_msg_lines
107
89
COMMIT_MSG_LINES=()
@@ -159,10 +141,101 @@ get_all_match_positions() {
159
141
done <<< " $targets"
160
142
}
161
143
162
- #
163
- # Validate the contents of the commmit msg agains the good commit guidelines.
164
- #
144
+ # Build regex for detecting commit trailers.
145
+ build_commit_trailer_regex () {
146
+ local -a keys specials standalones trailers
147
+ local _ each key separators
148
+
149
+ # Get the trailer separators from git config (default to ':' if not set)
150
+ separators=$( git config --get trailer.separators || echo ' :' )
151
+
152
+ # Predefined trailer keys.
153
+ trailers=(
154
+ ' CC' ' Change-Id'
155
+ ' Bug' ' Close' ' Closes'
156
+ ' Acked-by' ' Co-Authored-By' ' Reported-by' ' Reviewed-by'
157
+ ' Signed-off-by' ' Suggested-by' ' Tested-by'
158
+ )
159
+
160
+ # Standalone keys (those that do not require a value).
161
+ standalones=(
162
+ ' (Doc|Upgrade|Security)Impact'
163
+ " Git-Dch[$separators ] (Ignore|Short|Full)"
164
+ )
165
+
166
+ # Read custom trailer keys from git config and add them either to specials or trailers.
167
+ # This loop reads lines matching 'trailer.*.key'.
168
+ while read -r _ key; do
169
+ # Skip if key already exists in trailers or specials.
170
+ for each in " ${trailers[@]} " " ${specials[@]} " ; do
171
+ if [ " $key " = " $each " ]; then
172
+ continue 2
173
+ fi
174
+ done
175
+ # If key ends with a separator character, add to specials; otherwise, to trailers.
176
+ if [[ $key =~ [${separators} ]$ ]]; then
177
+ specials+=(" $key " )
178
+ else
179
+ trailers+=(" $key " )
180
+ fi
181
+ done < <( git config --get-regexp ' trailer.*.key' )
182
+
183
+ # Read custom trailer keys again into the 'keys' array (if needed).
184
+ while IFS=. read -r _ key _; do
185
+ for each in " ${keys[@]} " ; do
186
+ if [ " $key " = " $each " ]; then
187
+ continue 2
188
+ fi
189
+ done
190
+ keys+=(" $key " )
191
+ done < <( git config --get-regexp ' trailer.*.key' )
165
192
193
+ # Begin constructing the regex.
194
+ TRAILER_REGEX=' ^('
195
+
196
+ # Append trailer keys (with values).
197
+ if (( ${# trailers[@]} > 0 )) ; then
198
+ TRAILER_REGEX+=' (('
199
+ for each in " ${trailers[@]} " ; do
200
+ TRAILER_REGEX+=" $each |"
201
+ done
202
+ # Remove the trailing pipe, then add a separator and blank space pattern.
203
+ TRAILER_REGEX=" ${TRAILER_REGEX% |} )[$separators ][[:blank:]]*)"
204
+ fi
205
+
206
+ # Append standalone trailer keys.
207
+ if (( ${# standalones[@]} > 0 )) ; then
208
+ TRAILER_REGEX+=' |(('
209
+ for each in " ${standalones[@]} " ; do
210
+ TRAILER_REGEX+=" $each |"
211
+ done
212
+ TRAILER_REGEX=" ${TRAILER_REGEX% |} )$)"
213
+ fi
214
+
215
+ # Append specials.
216
+ if (( ${# specials[@]} > 0 )) ; then
217
+ TRAILER_REGEX+=' |('
218
+ for each in " ${specials[@]} " ; do
219
+ TRAILER_REGEX+=" $each |"
220
+ done
221
+ TRAILER_REGEX=" ${TRAILER_REGEX% |} )"
222
+ fi
223
+
224
+ # Append additional keys.
225
+ if (( ${# keys[@]} > 0 )) ; then
226
+ TRAILER_REGEX+=' |(('
227
+ for each in " ${keys[@]} " ; do
228
+ TRAILER_REGEX+=" $each |"
229
+ done
230
+ # Use the second character of separators (if available) as a separator for keys.
231
+ TRAILER_REGEX=" ${TRAILER_REGEX% |} )[${separators: 1: 1} [:blank:]])"
232
+ fi
233
+
234
+ # End the regex.
235
+ TRAILER_REGEX+=" )"
236
+ }
237
+
238
+ # Validate the contents of the commmit msg agains the good commit guidelines.
166
239
validate_commit_message () {
167
240
# reset warnings
168
241
WARNINGS=()
@@ -286,31 +359,35 @@ validate_commit_message() {
286
359
287
360
URL_REGEX=' ^[[:blank:]]*(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]*[-A-Za-z0-9+&@#/%=~_|]'
288
361
289
- # Ensure the commit message lines are loaded into an array.
290
- readarray -t COMMIT_MSG_LINES < " $COMMIT_MSG_FILE "
362
+ # Ensure the commit message lines are loaded into an array.
363
+ readarray -t commit_msg_lines < " $COMMIT_MSG_FILE "
291
364
292
- for i in " ${! COMMIT_MSG_LINES [@]} " ; do
293
- # Skip the first line (the subject) because the limit applies to the body.
294
- if [ " $i " -eq 0 ]; then
295
- continue
296
- fi
365
+ for i in " ${! commit_msg_lines [@]} " ; do
366
+ # Skip the first line (the subject) since the limit applies to the body.
367
+ if [ " $i " -eq 0 ]; then
368
+ continue
369
+ fi
297
370
298
- LINE=" ${COMMIT_MSG_LINES[$i]} "
299
-
300
- # Skip the line if it is a comment.
301
- if [[ " $LINE " =~ ^[[:space:]]* # ]]; then
302
- continue
303
- fi
371
+ line=" ${commit_msg_lines[$i]} "
304
372
305
- # Trim leading and trailing whitespace.
306
- TRIMMED_LINE= " ${LINE# " ${LINE%% [![:space:]]* } " } "
307
- TRIMMED_LINE= " ${TRIMMED_LINE% " ${TRIMMED_LINE##* [![:space:]]} " } "
308
- LINE_NUMBER= $(( i+ 1 ))
309
-
310
- if [ " ${# TRIMMED_LINE} " -gt 72 ] && ! [[ " $TRIMMED_LINE " =~ $URL_REGEX ]]; then
311
- add_warning " $LINE_NUMBER " " Wrap the body at 72 characters (${# TRIMMED_LINE} chars)"
312
- fi
313
- done
373
+ # Skip lines that are comments.
374
+ if [[ " $line " =~ ^[[:space:]]* # ]]; then
375
+ continue
376
+ fi
377
+
378
+ # Trim leading and trailing whitespace.
379
+ trimmed_line= " ${line# " ${line%% [![:space:]]* } " } "
380
+ trimmed_line= " ${trimmed_line% " ${trimmed_line##* [![:space:]]} " } "
381
+ line_number= $(( i+ 1 ))
382
+
383
+ # Check if the trimmed line is longer than 72 characters and does not match a URL
384
+ # or commit trailer. The URL regex is used inline by stripping its leading caret.
385
+ if [ " ${# trimmed_line} " -gt 72 ] && \
386
+ ! [[ " $trimmed_line " =~ ${URL_REGEX# ^} ]] && \
387
+ ! [[ " $trimmed_line " =~ $TRAILER_REGEX ]]; then
388
+ add_warning " $line_number " " Wrap the body at 72 characters (${# trimmed_line} chars)"
389
+ fi
390
+ done
314
391
315
392
# 7. Ensure the commit subject has more than one word.
316
393
# ------------------------------------------------------------------------------
343
420
# 8. Use the body to explain what and why vs. how
344
421
# ------------------------------------------------------------------------------
345
422
346
- # Count non-comment, non-blank lines excluding "Change-Id:".
347
- NON_COMMENT_COUNT= $( sed ' /^[[:space:]]*#/d;/^[[:space:]]*$/d;/^[[:space:]]*Change-Id:/d' " ${COMMIT_MSG_FILE} " | wc -l | xargs)
423
+ # Count non-comment, non-blank lines, excluding lines that match the trailer regex.
424
+ NON_COMMENT_COUNT= $( sed ' /^[[:space:]]*#/d;/^[[:space:]]*$/d' " ${COMMIT_MSG_FILE} " | \
425
+ sed -E " /$TRAILER_REGEX /d" | wc -l | xargs)
348
426
349
427
# If queue.c is modified and the commit message is oversimplified, forbid generic subjects.
350
428
if git diff --cached --name-only | grep -Eq ' (^|/)queue\.c$' ; then
383
461
# 12. Avoid abusive language in commit message content
384
462
# ------------------------------------------------------------------------------
385
463
386
- FULL_COMMIT_MSG_WITH_SPACE=$( sed ' /^#/d;/^[[:space:]]*Change-Id:/d' " $COMMIT_MSG_FILE " | \
387
- sed -E " s@${URL_REGEX# ^} @@g" )
464
+ # Remove comment lines, trailer lines, and URLs.
465
+ FULL_COMMIT_MSG_WITH_SPACE=$( sed ' /^[[:space:]]*#/d' " $COMMIT_MSG_FILE " | \
466
+ sed -E " /$TRAILER_REGEX /d" | sed -E " s@${URL_REGEX# ^} @@g" )
388
467
FULL_COMMIT_MSG=$( echo " $FULL_COMMIT_MSG_WITH_SPACE " | sed ' /^[[:space:]]*$/d' )
389
468
390
469
# Extended list of abusive words (case-insensitive).
409
488
-e " s/\bcommit[[:space:]]+[0-9a-fA-F]{7,40}\b/commit/g" )
410
489
MSG_FOR_SPELLCHECK=$( echo " $MSG_FOR_SPELLCHECK_LINE_FINDING " | sed ' /^[[:space:]]*$/d' )
411
490
412
-
413
491
# Use aspell to list misspelled words according to American English, ignoring quoted text.
414
492
MISSPELLED_WORDS=$( echo " $MSG_FOR_SPELLCHECK " | $ASPELL --lang=en --list --home-dir=scripts --personal=aspell-pws)
415
493
if [ -n " $MISSPELLED_WORDS " ]; then
@@ -427,7 +505,6 @@ CHANGE_ID_AFTER="Bug|Issue|Test"
427
505
MSG=" $1 "
428
506
429
507
# Ensure that a unique Change-Id is present, and generate one if it is not.
430
- #
431
508
# Partially taken from Gerrit Code Review 3.3.0-56-gbcecc47463
432
509
add_change_id () {
433
510
clean_message=` sed -e '
@@ -578,14 +655,14 @@ _gen_changeid() {
578
655
git hash-object -t commit --stdin
579
656
}
580
657
581
- #
582
658
# It's showtime.
583
- #
584
659
585
660
set_colors
586
661
587
662
set_editor
588
663
664
+ build_commit_trailer_regex
665
+
589
666
if tty > /dev/null 2>&1 ; then
590
667
TTY=$( tty)
591
668
else
0 commit comments