Skip to content

Commit eea1c6d

Browse files
authored
Merge pull request #214 from sysprog21/refine-commit-hook
Refine commit hook
2 parents 1d68fae + 4a8b8d5 commit eea1c6d

File tree

1 file changed

+128
-51
lines changed

1 file changed

+128
-51
lines changed

scripts/commit-msg.hook

+128-51
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ WHITE=
1818
CYAN=
1919
NC=
2020

21-
#
2221
# Set colour variables if the output should be coloured.
23-
#
24-
2522
set_colors() {
2623
local default_color=$(git config --get hooks.goodcommit.color || git config --get color.ui || echo 'auto')
2724
if [[ $default_color == 'always' ]] || [[ $default_color == 'auto' && -t 1 ]]; then
@@ -34,10 +31,7 @@ set_colors() {
3431
fi
3532
}
3633

37-
#
3834
# Set the hook editor, using the same approach as git.
39-
#
40-
4135
set_editor() {
4236
# $GIT_EDITOR appears to always be set to `:` when the hook is executed by Git?
4337
# ref: http://stackoverflow.com/q/41468839/885540
@@ -49,10 +43,7 @@ set_editor() {
4943
test -z "${HOOK_EDITOR}" && HOOK_EDITOR='vi'
5044
}
5145

52-
#
5346
# Output prompt help information.
54-
#
55-
5647
prompt_help() {
5748
echo -e "${RED}$(cat <<-EOF
5849
e - edit commit message
@@ -62,20 +53,14 @@ EOF
6253
)${NC}"
6354
}
6455

65-
#
6656
# Add a warning with <line_number> and <msg>.
67-
#
68-
6957
add_warning() {
7058
local line_number=$1
7159
local warning=$2
7260
WARNINGS[$line_number]="${WARNINGS[$line_number]}$warning;"
7361
}
7462

75-
#
7663
# Output warnings.
77-
#
78-
7964
display_warnings() {
8065
if [ $SKIP_DISPLAY_WARNINGS -eq 1 ]; then
8166
# if the warnings were skipped then they should be displayed next time
@@ -98,10 +83,7 @@ EOF
9883
)${NC}"
9984
}
10085

101-
#
10286
# Read the contents of the commit msg into an array of lines.
103-
#
104-
10587
read_commit_message() {
10688
# reset commit_msg_lines
10789
COMMIT_MSG_LINES=()
@@ -159,10 +141,101 @@ get_all_match_positions() {
159141
done <<< "$targets"
160142
}
161143

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')
165192

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.
166239
validate_commit_message() {
167240
# reset warnings
168241
WARNINGS=()
@@ -286,31 +359,35 @@ validate_commit_message() {
286359

287360
URL_REGEX='^[[:blank:]]*(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]*[-A-Za-z0-9+&@#/%=~_|]'
288361

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"
291364

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
297370

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]}"
304372

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
314391

315392
# 7. Ensure the commit subject has more than one word.
316393
# ------------------------------------------------------------------------------
@@ -343,8 +420,9 @@ done
343420
# 8. Use the body to explain what and why vs. how
344421
# ------------------------------------------------------------------------------
345422

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)
348426

349427
# If queue.c is modified and the commit message is oversimplified, forbid generic subjects.
350428
if git diff --cached --name-only | grep -Eq '(^|/)queue\.c$'; then
@@ -383,8 +461,9 @@ done
383461
# 12. Avoid abusive language in commit message content
384462
# ------------------------------------------------------------------------------
385463

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")
388467
FULL_COMMIT_MSG=$(echo "$FULL_COMMIT_MSG_WITH_SPACE" | sed '/^[[:space:]]*$/d')
389468

390469
# Extended list of abusive words (case-insensitive).
@@ -409,7 +488,6 @@ done
409488
-e "s/\bcommit[[:space:]]+[0-9a-fA-F]{7,40}\b/commit/g")
410489
MSG_FOR_SPELLCHECK=$(echo "$MSG_FOR_SPELLCHECK_LINE_FINDING" | sed '/^[[:space:]]*$/d')
411490

412-
413491
# Use aspell to list misspelled words according to American English, ignoring quoted text.
414492
MISSPELLED_WORDS=$(echo "$MSG_FOR_SPELLCHECK" | $ASPELL --lang=en --list --home-dir=scripts --personal=aspell-pws)
415493
if [ -n "$MISSPELLED_WORDS" ]; then
@@ -427,7 +505,6 @@ CHANGE_ID_AFTER="Bug|Issue|Test"
427505
MSG="$1"
428506

429507
# Ensure that a unique Change-Id is present, and generate one if it is not.
430-
#
431508
# Partially taken from Gerrit Code Review 3.3.0-56-gbcecc47463
432509
add_change_id() {
433510
clean_message=`sed -e '
@@ -578,14 +655,14 @@ _gen_changeid() {
578655
git hash-object -t commit --stdin
579656
}
580657

581-
#
582658
# It's showtime.
583-
#
584659

585660
set_colors
586661

587662
set_editor
588663

664+
build_commit_trailer_regex
665+
589666
if tty >/dev/null 2>&1; then
590667
TTY=$(tty)
591668
else

0 commit comments

Comments
 (0)