Skip to content

Commit 4a8b8d5

Browse files
committed
Ignore generated trailers in commit message
Auto-generated trailers (e.g. 'Signed-off-by') should be excluded from validation. This commit builds proper regular expressions and removes predefined 'Change-Id:' detection accordingly. Change-Id: Ide1ac959fe41f0f019db92c7ae6e682fa1d17615
1 parent c26a705 commit 4a8b8d5

File tree

1 file changed

+128
-27
lines changed

1 file changed

+128
-27
lines changed

scripts/commit-msg.hook

+128-27
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,100 @@ get_all_match_positions() {
141141
done <<< "$targets"
142142
}
143143

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')
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+
144238
# Validate the contents of the commmit msg agains the good commit guidelines.
145239
validate_commit_message() {
146240
# reset warnings
@@ -265,31 +359,35 @@ validate_commit_message() {
265359

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

268-
# Ensure the commit message lines are loaded into an array.
269-
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"
270364

271-
for i in "${!COMMIT_MSG_LINES[@]}"; do
272-
# Skip the first line (the subject) because the limit applies to the body.
273-
if [ "$i" -eq 0 ]; then
274-
continue
275-
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
276370

277-
LINE="${COMMIT_MSG_LINES[$i]}"
278-
279-
# Skip the line if it is a comment.
280-
if [[ "$LINE" =~ ^[[:space:]]*# ]]; then
281-
continue
282-
fi
371+
line="${commit_msg_lines[$i]}"
283372

284-
# Trim leading and trailing whitespace.
285-
TRIMMED_LINE="${LINE#"${LINE%%[![:space:]]*}"}"
286-
TRIMMED_LINE="${TRIMMED_LINE%"${TRIMMED_LINE##*[![:space:]]}"}"
287-
LINE_NUMBER=$((i+1))
288-
289-
if [ "${#TRIMMED_LINE}" -gt 72 ] && ! [[ "$TRIMMED_LINE" =~ $URL_REGEX ]]; then
290-
add_warning "$LINE_NUMBER" "Wrap the body at 72 characters (${#TRIMMED_LINE} chars)"
291-
fi
292-
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
293391

294392
# 7. Ensure the commit subject has more than one word.
295393
# ------------------------------------------------------------------------------
@@ -322,8 +420,9 @@ done
322420
# 8. Use the body to explain what and why vs. how
323421
# ------------------------------------------------------------------------------
324422

325-
# Count non-comment, non-blank lines excluding "Change-Id:".
326-
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)
327426

328427
# If queue.c is modified and the commit message is oversimplified, forbid generic subjects.
329428
if git diff --cached --name-only | grep -Eq '(^|/)queue\.c$'; then
@@ -362,8 +461,9 @@ done
362461
# 12. Avoid abusive language in commit message content
363462
# ------------------------------------------------------------------------------
364463

365-
FULL_COMMIT_MSG_WITH_SPACE=$(sed '/^#/d;/^[[:space:]]*Change-Id:/d' "$COMMIT_MSG_FILE" | \
366-
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")
367467
FULL_COMMIT_MSG=$(echo "$FULL_COMMIT_MSG_WITH_SPACE" | sed '/^[[:space:]]*$/d')
368468

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

391-
392491
# Use aspell to list misspelled words according to American English, ignoring quoted text.
393492
MISSPELLED_WORDS=$(echo "$MSG_FOR_SPELLCHECK" | $ASPELL --lang=en --list --home-dir=scripts --personal=aspell-pws)
394493
if [ -n "$MISSPELLED_WORDS" ]; then
@@ -562,6 +661,8 @@ set_colors
562661

563662
set_editor
564663

664+
build_commit_trailer_regex
665+
565666
if tty >/dev/null 2>&1; then
566667
TTY=$(tty)
567668
else

0 commit comments

Comments
 (0)