@@ -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.
145239validate_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 # ------------------------------------------------------------------------------
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
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).
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
563662set_editor
564663
664+ build_commit_trailer_regex
665+
565666if tty > /dev/null 2>&1 ; then
566667 TTY=$( tty)
567668else
0 commit comments