xref: /aosp_15_r20/external/coreboot/util/gitconfig/commit-msg (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1#!/usr/bin/env sh
2# Part of Gerrit Code Review (http://code.google.com/p/gerrit/)
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17CHANGE_ID_AFTER="Bug|Issue"
18MSG="$1"
19
20# Check for, and add if missing, a unique Change-Id
21#
22add_ChangeId() {
23	clean_message=`sed -e '
24		/^diff --git a\/.*/{
25			s///
26			q
27		}
28		/^Signed-off-by:/d
29		/^#/d
30	' "$MSG" | git stripspace`
31	if test -z "$clean_message"
32	then
33		return
34	fi
35
36	# Does Change-Id: already exist? if so, exit (no change).
37	if grep -i '^Change-Id: I[0-9a-f]\{40\}$' "$MSG" >/dev/null
38	then
39		return
40	fi
41
42	id=`_gen_ChangeId`
43	T="$MSG.tmp.$$"
44	AWK=awk
45	if [ -x /usr/xpg4/bin/awk ]; then
46		# Solaris AWK is just too broken
47		AWK=/usr/xpg4/bin/awk
48	fi
49
50	# How this works:
51	# - parse the commit message as (textLine+ blankLine*)*
52	# - assume textLine+ to be a footer until proven otherwise
53	# - exception: the first block is not footer (as it is the title)
54	# - read textLine+ into a variable
55	# - then count blankLines
56	# - once the next textLine appears, print textLine+ blankLine* as these
57	#   aren't footer
58	# - in END, the last textLine+ block is available for footer parsing
59	$AWK '
60	BEGIN {
61		# while we start with the assumption that textLine+
62		# is a footer, the first block is not.
63		isFooter = 0
64		footerComment = 0
65		blankLines = 0
66	}
67
68	# Skip lines starting with "#" without any spaces before it.
69	/^#/ { next }
70
71	# Skip the line starting with the diff command and everything after it,
72	# up to the end of the file, assuming it is only patch data.
73	# If more than one line before the diff was empty, strip all but one.
74	/^diff --git a/ {
75		blankLines = 0
76		while (getline) { }
77		next
78	}
79
80	# Count blank lines outside footer comments
81	/^$/ && (footerComment == 0) {
82		blankLines++
83		next
84	}
85
86	# Catch footer comment
87	/^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) {
88		footerComment = 1
89	}
90
91	/]$/ && (footerComment == 1) {
92		footerComment = 2
93	}
94
95	# We have a non-blank line after blank lines. Handle this.
96	(blankLines > 0) {
97		print lines
98		for (i = 0; i < blankLines; i++) {
99			print ""
100		}
101
102		lines = ""
103		blankLines = 0
104		isFooter = 1
105		footerComment = 0
106	}
107
108	# Detect that the current block is not the footer
109	(footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) {
110		isFooter = 0
111	}
112
113	{
114		# We need this information about the current last comment line
115		if (footerComment == 2) {
116			footerComment = 0
117		}
118		if (lines != "") {
119			lines = lines "\n";
120		}
121		lines = lines $0
122	}
123
124	# Footer handling:
125	# If the last block is considered a footer, splice in the Change-Id at the
126	# right place.
127	# Look for the right place to inject Change-Id by considering
128	# CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first,
129	# then Change-Id, then everything else (eg. Signed-off-by:).
130	#
131	# Otherwise just print the last block, a new line and the Change-Id as a
132	# block of its own.
133	END {
134		unprinted = 1
135		if (isFooter == 0) {
136			print lines "\n"
137			lines = ""
138		}
139		changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):"
140		numlines = split(lines, footer, "\n")
141		for (line = 1; line <= numlines; line++) {
142			if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) {
143				unprinted = 0
144				print "Change-Id: I'"$id"'"
145			}
146			print footer[line]
147		}
148		if (unprinted) {
149			print "Change-Id: I'"$id"'"
150		}
151	}' "$MSG" > $T && mv $T "$MSG" || rm -f $T
152}
153_gen_ChangeIdInput() {
154	echo "tree `git write-tree`"
155	if parent=`git rev-parse "HEAD^0" 2>/dev/null`
156	then
157		echo "parent $parent"
158	fi
159	echo "author `git var GIT_AUTHOR_IDENT`"
160	echo "committer `git var GIT_COMMITTER_IDENT`"
161	echo
162	printf '%s' "$clean_message"
163}
164_gen_ChangeId() {
165	_gen_ChangeIdInput |
166	git hash-object -t commit --stdin
167}
168
169if ! grep -qi '^[[:space:]]*Signed-off-by:.\+<.\+@.\+>' "$MSG"; then
170	printf "\nError: No Signed-off-by line in the commit message.\n"
171	exit 1
172fi
173add_ChangeId
174