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
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
|
#!/usr/bin/awk
# Scan a patch for correctness, according to XFS/iomap rules.
#
# Pass the commit id as the variable "commit_id" and the git log --oneline
# output as "oneline" for report generation.
function getval(key) {
realkey = sprintf("%s: ", key)
keylen = length(realkey)
if (substr($0, 0, keylen) != realkey)
return 0
return substr($0, keylen + 1)
}
function is_me(val, my_signature, dead_signature)
{
if (val == my_signature)
return 1
if (dead_signature != 0 && val == dead_signature)
return 1
return 0
}
function commit_error(why)
{
if (!commit_errored)
printf("%s\n", oneline) > "/dev/stderr"
printf(" - %s\n", why) > "/dev/stderr"
commit_errored = 1
}
function commit_error_end(why)
{
if (commit_errored)
printf("\n") > "/dev/stderr"
}
function prog_error(why)
{
printf("%s: %s\n", commit_id, why) > "/dev/stderr"
}
BEGIN {
ret = 0
commit_errored = 0
if (length(commit_id) == 0) {
printf("Commit id not supplied.") > "/dev/stderr"
ret = 2
exit
}
if (length(oneline) == 0) {
prog_error("Commit oneline not supplied.")
ret = 2
exit
}
if (("git config user.name" | getline name) < 0) {
prog_error("%s: Can't get git user.name.")
ret = 2
exit
}
if (("git config user.email" | getline email) < 0) {
prog_error("%s: Can't get git user.email.")
ret = 2
exit
}
if (("git config core.abbrev" | getline abbrev_len) < 0) {
prog_error("%s: Can't get git core.abbrev.")
ret = 2
exit
}
if (abbrev_len <= 0)
abbrev_len = 12
("git config user.deadname" | getline dead_name)
("git config user.deademail" | getline dead_email)
if (length(dead_name) > 0 && length(dead_email) == 0) {
dead_email = email
}
if (length(dead_email) > 0 && length(dead_name) == 0) {
dead_name = name
}
if (("git config --type bool user.devtree" | getline dev_tree) < 0) {
dev_tree = "false"
}
dev_tree = (dev_tree == "true")
my_signature = sprintf("%s <%s>", name, email)
if (length(dead_email) > 0)
dead_signature = sprintf("%s <%s>", dead_name, dead_email)
else
dead_signature = 0;
you_are_author = 0
has_other_review = 0
has_self_review = 0
has_self_signoff = 0
has_committer_signoff = 0
bad_commit = 0
in_header = 1
in_body = 0
subject = 0
committer_signature = 0
committer_name = 0
author_signature = 0
author_name = 0
merge = 0
}
{
# Trim leading and trailing whitespace so that we can pick up tags that
# are in the commit message.
gsub(/^[[:space:]]+|[[:space:]]+$/, "", $0)
if (debug)
printf(":%s:%s:%s:\n", in_header, $1, $0)
if (in_header && $0 == "") {
in_header = 0
in_body = 1
} else if (in_body && subject == 0) {
# Pick up the subject line for error reporting
subject = $0
if (debug)
printf("subj:%s:%s:\n", $0, subject)
} else if ((val = getval("Merge")) != 0) {
# We don't process merge commits
merge = 1
if (debug)
printf("Merge:%s:%s:\n", $0, val);
nextfile
} else if ((val = getval("Author")) != 0) {
# Pick up the author header
author_signature = val
author_name = gensub(/^[[:space:]]*(.+) <.*$/, "\\1", "g",
author_signature);
if (debug)
printf("author:%s:%s:\n", val, author_signature);
if (is_me(val, my_signature, dead_signature))
you_are_author = 1
} else if ((val = getval("Commit")) != 0) {
# Pick up the committer header
committer_signature = val;
committer_name = gensub(/^[[:space:]]*(.+) <.*$/, "\\1", "g",
committer_signature);
if (debug)
printf("committer:%s:%s:\n", val, committer_name);
} else if ((val = getval("Signed-off-by")) != 0) {
# Pick up signoffs
sob_name = gensub(/^[[:space:]]*(.+) <.*$/, "\\1", "g", val);
if (is_me(val, my_signature, dead_signature))
has_self_signoff = 1
if (val == author_signature || sob_name == author_name)
has_author_signoff = 1
if (val == committer_signature || sob_name == committer_name)
has_committer_signoff = 1
} else if ((val = getval("Reviewed-by")) != 0) {
# Pick up reviews
if (is_me(val, my_signature, dead_signature))
has_self_review = 1
else
has_other_review = 1
} else if ((val = getval("Fixes")) != 0) {
# Fixes tags must be of a certain length and exist in the git
# history.
split(val, cols)
fixes_id = cols[1]
if (debug)
printf("fixes:%s:\n", fixes_id)
if (length(fixes_id) < abbrev_len) {
msg = sprintf("Fixes tag commit \"%s\" needs to be longer.", fixes_id)
commit_error(msg)
bad_commit = 1
}
cmd = sprintf("git log -n 1 \"%s\" >/dev/null 2>&1", fixes_id)
if (system(cmd) != 0) {
msg = sprintf("Fixes tag commit \"%s\" does not exist.", fixes_id)
commit_error(msg)
bad_commit = 1
}
} else if ($0 ~ /scanned for virus detection/) {
commit_error("Email virus scanner garbage in commit message.")
bad_commit = 1
}
}
END {
if (ret != 0 || merge != 0)
exit ret
if (subject == 0) {
commit_error("Patch subject not found.")
ret = 1
}
if (bad_commit)
ret = 1
if (you_are_author) {
# If you wrote the patch, you must have your own signoff and a
# review by someone else (if this isn't your development tree).
# You can't review your own patches.
if (!has_self_signoff) {
commit_error("Your patch needs your signoff.")
ret = 1
}
if (!has_other_review && !dev_tree) {
commit_error("Your patch needs review by someone else.")
ret = 1
}
if (has_self_review) {
commit_error("You cannot review your own patch.")
ret = 1
}
} else {
# If you did not write the patch, it must have a signoff from
# the author, a review by you, and a signoff by you unless the
# author is the committer.
if (!has_author_signoff) {
commit_error("Patch needs to be signed off by the author.")
ret = 1
}
if (!has_self_review) {
commit_error("Patch needs your review.")
ret = 1
}
if (!has_committer_signoff && !has_self_signoff) {
commit_error("Patch needs your (or the committer's) signoff.")
has_committer_signoff = 1
ret = 1
}
}
# All commits must have a signoff from the committer.
if (!has_committer_signoff) {
commit_error("This patch needs to be signed off by the committer.")
ret = 1
}
commit_error_end()
exit ret
}
|