blob: 0eb8c974bc0fa95ce66d0044f4a0dc905230c9fb (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
|
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Major parts from verify_fixes.sh and verify_signedoff.sh by
# Stephen and Greg. Only integrated the checks into pw-check.
#
# Copyright (C) 2019 Stephen Rothwell <sfr@canb.auug.org.au>
# Copyright (C) 2019 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
# Copyright (C) 2019 Daniel Borkmann <daniel@iogearbox.net>
usage()
{
cat <<-EOF
usage: pw-check [-h] [-s START_COMMIT] [-e END_COMMIT]
EOF
exit
}
split_re='^([Cc][Oo][Mm][Mm][Ii][Tt])?[[:space:]]*([[:xdigit:]]{5,})([[:space:]]*)(.*)$'
nl=$'\n'
tab=$'\t'
strip_spaces()
{
[[ "$1" =~ ^[[:space:]]*(.*[^[:space:]])[[:space:]]*$ ]]
echo "${BASH_REMATCH[1]}"
}
verify_fixes()
{
git_range=$1
error=0
commits=$(git rev-list --no-merges -i --grep='^[[:space:]]*Fixes:' "${git_range}")
if [ -z "$commits" ]; then
return 0
fi
for c in $commits; do
commit_log=$(git log -1 --format='%h ("%s")' "$c")
commit_msg="Commit: $commit_log
"
fixes_lines=$(git log -1 --format='%B' "$c" |
grep -i '^[[:space:]]*Fixes:')
while read -r fline; do
[[ "$fline" =~ ^[[:space:]]*[Ff][Ii][Xx][Ee][Ss]:[[:space:]]*(.*)$ ]]
f="${BASH_REMATCH[1]}"
fixes_msg=" Fixes tag: $fline
Has these problem(s):
"
sha=
subject=
msg=
if [[ "$f" =~ $split_re ]]; then
first="${BASH_REMATCH[1]}"
sha="${BASH_REMATCH[2]}"
spaces="${BASH_REMATCH[3]}"
subject="${BASH_REMATCH[4]}"
if [ "$first" ]; then
msg="${msg:+${msg}${nl}}${tab}${tab}- leading word '$first' unexpected"
fi
if [ -z "$subject" ]; then
msg="${msg:+${msg}${nl}}${tab}${tab}- missing subject"
elif [ -z "$spaces" ]; then
msg="${msg:+${msg}${nl}}${tab}${tab}- missing space between the SHA1 and the subject"
fi
else
printf '%s%s\t\t- %s\n' "$commit_msg" "$fixes_msg" 'No SHA1 recognised'
commit_msg=''
error=1
continue
fi
if ! git rev-parse -q --verify "$sha" >/dev/null; then
printf '%s%s\t\t- %s\n' "$commit_msg" "$fixes_msg" 'Target SHA1 does not exist'
commit_msg=''
error=1
continue
fi
if [ "${#sha}" -lt 12 ]; then
msg="${msg:+${msg}${nl}}${tab}${tab}- SHA1 should be at least 12 digits long${nl}${tab}${tab} Can be fixed by setting core.abbrev to 12 (or more) or (for git v2.11${nl}${tab}${tab} or later) just making sure it is not set (or set to \"auto\")."
fi
# reduce the subject to the part between () if there
if [[ "$subject" =~ ^\((.*)\) ]]; then
subject="${BASH_REMATCH[1]}"
elif [[ "$subject" =~ ^\((.*) ]]; then
subject="${BASH_REMATCH[1]}"
msg="${msg:+${msg}${nl}}${tab}${tab}- Subject has leading but no trailing parentheses"
fi
# strip matching quotes at the start and end of the subject
# the unicode characters in the classes are
# U+201C LEFT DOUBLE QUOTATION MARK
# U+201D RIGHT DOUBLE QUOTATION MARK
# U+2018 LEFT SINGLE QUOTATION MARK
# U+2019 RIGHT SINGLE QUOTATION MARK
re1=$'^[\"\u201C](.*)[\"\u201D]$'
re2=$'^[\'\u2018](.*)[\'\u2019]$'
re3=$'^[\"\'\u201C\u2018](.*)$'
if [[ "$subject" =~ $re1 ]]; then
subject="${BASH_REMATCH[1]}"
elif [[ "$subject" =~ $re2 ]]; then
subject="${BASH_REMATCH[1]}"
elif [[ "$subject" =~ $re3 ]]; then
subject="${BASH_REMATCH[1]}"
msg="${msg:+${msg}${nl}}${tab}${tab}- Subject has leading but no trailing quotes"
fi
subject=$(strip_spaces "$subject")
target_subject=$(git log -1 --format='%s' "$sha")
target_subject=$(strip_spaces "$target_subject")
# match with ellipses
case "$subject" in
*...) subject="${subject%...}"
target_subject="${target_subject:0:${#subject}}"
;;
...*) subject="${subject#...}"
target_subject="${target_subject: -${#subject}}"
;;
*\ ...\ *)
s1="${subject% ... *}"
s2="${subject#* ... }"
subject="$s1 $s2"
t1="${target_subject:0:${#s1}}"
t2="${target_subject: -${#s2}}"
target_subject="$t1 $t2"
;;
esac
subject=$(strip_spaces "$subject")
target_subject=$(strip_spaces "$target_subject")
if [ "$subject" != "${target_subject:0:${#subject}}" ]; then
msg="${msg:+${msg}${nl}}${tab}${tab}- Subject does not match target commit subject${nl}${tab}${tab} Just use${nl}${tab}${tab}${tab}git log -1 --format='Fixes: %h (\"%s\")'"
fi
lsha=$(git rev-parse -q --verify "$sha")
if [ -z "$lsha" ]; then
count=$(git rev-list --count "$sha".."$c")
if [ "$count" -eq 0 ]; then
msg="${msg:+${msg}${nl}}${tab}${tab}- Target is not an ancestor of this commit"
fi
fi
if [ "$msg" ]; then
printf '%s%s%s\n' "$commit_msg" "$fixes_msg" "$msg"
commit_msg=''
error=1
fi
done <<< "$fixes_lines"
done
if [ ${error} -eq 1 ] ; then
exit 1
fi
}
verify_signedoff()
{
git_range=$1
error=false
for c in $(git rev-list --no-merges "${git_range}"); do
ae=$(git log -1 --format='%ae' "$c")
aE=$(git log -1 --format='%aE' "$c")
an=$(git log -1 --format='%an' "$c")
aN=$(git log -1 --format='%aN' "$c")
ce=$(git log -1 --format='%ce' "$c")
cE=$(git log -1 --format='%cE' "$c")
cn=$(git log -1 --format='%cn' "$c")
cN=$(git log -1 --format='%cN' "$c")
sob=$(git log -1 --format='%b' "$c" | grep -i '^[[:space:]]*Signed-off-by:')
am=false
cm=false
grep -i -q "<$ae>" <<<"$sob" ||
grep -i -q "<$aE>" <<<"$sob" ||
grep -i -q ":[[:space:]]*${an}[[:space:]]*<" <<<"$sob" ||
grep -i -q ":[[:space:]]*${aN}[[:space:]]*<" <<<"$sob" ||
am=true
grep -i -q "<$ce>" <<<"$sob" ||
grep -i -q "<$cE>" <<<"$sob" ||
grep -i -q ":[[:space:]]*${cn}[[:space:]]*<" <<<"$sob" ||
grep -i -q ":[[:space:]]*${cN}[[:space:]]*<" <<<"$sob" ||
cm=true
if "$am" || "$cm"; then
printf "Commit %s\n" "$(git show -s --abbrev-commit --abbrev=12 --pretty=format:"%h (\"%s\")%n" "${c}")"
"$am" && printf "\tauthor Signed-off-by missing\n"
"$cm" && printf "\tcommitter Signed-off-by missing\n"
printf "\tauthor email: %s\n" "$ae"
printf "\tcommitter email: %s\n" "$ce"
readarray -t s <<< "${sob}"
printf "\t%s\n" "${s[@]}"
printf "\n"
error=true
fi
done
if "$error"; then
echo "Errors in tree with Signed-off-by, please fix!"
exit 1
fi
}
from=""
to=""
while true; do
case "$1" in
-s | --start ) from="$2"; shift 2 ;;
-e | --end ) to="$2"; shift 2 ;;
-h | --help ) usage; break ;;
* ) break ;;
esac
done
[ -z "$from" ] && usage
[ -z "$to" ] && usage
verify_signedoff "$from..$to"
verify_fixes "$from..$to"
echo "Checked $from -> $to: OK"
|