Skip to content

Commit 000076f

Browse files
committed
Add Change-Id trailer to commit message hook
Append a deterministic Gerrit-style Change-Id to every commit message: hash of tree, parent, author/committer identity, and cleaned message body. The awk block inserts the trailer in the footer, respecting existing trailer ordering. Handles core.commentChar (including 'auto'), validates the generated hash, and fails loudly on write errors instead of silently proceeding. Change-Id: Id606a2c2b9deae4f6bd10093cfe506fe9203da1a
1 parent 000091d commit 000076f

1 file changed

Lines changed: 98 additions & 1 deletion

File tree

scripts/commit-msg.hook

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ while true; do
364364
validate_commit_message
365365

366366
# if there are no WARNINGS are empty then we're good to break out of here
367-
test ${#WARNINGS[@]} -eq 0 && exit 0
367+
test ${#WARNINGS[@]} -eq 0 && break
368368

369369
display_warnings
370370

@@ -389,3 +389,100 @@ while true; do
389389
esac
390390

391391
done
392+
393+
#
394+
# Append a deterministic Change-Id (Gerrit algorithm) when not already present.
395+
#
396+
397+
if grep -q '^Change-Id: I[0-9a-f]\{40\}' "$COMMIT_MSG_FILE"; then
398+
exit 0
399+
fi
400+
401+
clean_message=$(sed -n -e '/^#/d' -e '/^diff --git /q' \
402+
-e '/^Signed-off-by:/d' -e p "$COMMIT_MSG_FILE" | git stripspace)
403+
404+
# Nothing to sign: skip.
405+
[ -z "$clean_message" ] && exit 0
406+
407+
_gen_changeid_input()
408+
{
409+
local tree parent author committer
410+
411+
tree=$(git write-tree) || return 1
412+
author=$(git var GIT_AUTHOR_IDENT) || return 1
413+
committer=$(git var GIT_COMMITTER_IDENT) || return 1
414+
415+
echo "tree $tree"
416+
if parent=$(git rev-parse "HEAD^0" 2> /dev/null); then
417+
echo "parent $parent"
418+
fi
419+
echo "author $author"
420+
echo "committer $committer"
421+
echo
422+
printf '%s' "$clean_message"
423+
}
424+
425+
if ! change_id_input=$(_gen_changeid_input); then
426+
echo "Failed to build Change-Id input" >&2
427+
exit 1
428+
fi
429+
430+
if ! change_id=$(printf '%s' "$change_id_input" | git hash-object -t commit --stdin); then
431+
echo "Failed to compute Change-Id" >&2
432+
exit 1
433+
fi
434+
435+
if [[ ! "$change_id" =~ ^[0-9a-f]{40}$ ]]; then
436+
echo "Failed to compute valid Change-Id" >&2
437+
exit 1
438+
fi
439+
440+
commentChar=$(git config --get core.commentChar 2> /dev/null || echo '#')
441+
# 'auto' means Git picks dynamically; treat as '#' for our purposes.
442+
[[ "$commentChar" == "auto" || -z "$commentChar" ]] && commentChar='#'
443+
444+
awk -v id="$change_id" -v cc="$commentChar" '
445+
BEGIN {
446+
isFooter = 0; footerComment = 0; blankLines = 0
447+
}
448+
index($0, cc) == 1 { next }
449+
/^diff --git / { blankLines = 0; while (getline) {}; next }
450+
/^$/ && (footerComment == 0) { blankLines++; next }
451+
/^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) { footerComment = 1 }
452+
/]$/ && (footerComment == 1) { footerComment = 2 }
453+
(blankLines > 0) {
454+
print lines
455+
for (i = 0; i < blankLines; i++) print ""
456+
lines = ""; blankLines = 0; isFooter = 1; footerComment = 0
457+
}
458+
(footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) {
459+
isFooter = 0
460+
}
461+
{
462+
if (footerComment == 2) footerComment = 0
463+
if (lines != "") lines = lines "\n"
464+
lines = lines $0
465+
}
466+
END {
467+
unprinted = 1
468+
if (isFooter == 0) { print lines "\n"; lines = "" }
469+
numlines = split(lines, footer, "\n")
470+
trailers = ""; other = ""
471+
for (i = 1; i <= numlines; i++) {
472+
low = tolower(footer[i])
473+
if (low ~ /^(signed-off|reviewed|co-authored|acked|suggested|tested|reported)-by:/) {
474+
trailers = trailers footer[i] "\n"
475+
} else {
476+
other = other footer[i] "\n"
477+
}
478+
}
479+
if (other != "") printf "%s", other
480+
if (trailers != "") printf "%s", trailers
481+
printf "Change-Id: I%s\n", id
482+
}' "$COMMIT_MSG_FILE" > "${COMMIT_MSG_FILE}.tmp"
483+
484+
if ! mv "${COMMIT_MSG_FILE}.tmp" "$COMMIT_MSG_FILE"; then
485+
rm -f "${COMMIT_MSG_FILE}.tmp"
486+
echo "Failed to write Change-Id to commit message" >&2
487+
exit 1
488+
fi

0 commit comments

Comments
 (0)