xref: /f-stack/dpdk/devtools/check-git-log.sh (revision 2bfe3f2e)
1*2bfe3f2eSlogwang#! /bin/sh
2*2bfe3f2eSlogwang
3*2bfe3f2eSlogwang# BSD LICENSE
4*2bfe3f2eSlogwang#
5*2bfe3f2eSlogwang# Copyright 2016 6WIND S.A.
6*2bfe3f2eSlogwang#
7*2bfe3f2eSlogwang# Redistribution and use in source and binary forms, with or without
8*2bfe3f2eSlogwang# modification, are permitted provided that the following conditions
9*2bfe3f2eSlogwang# are met:
10*2bfe3f2eSlogwang#
11*2bfe3f2eSlogwang#   * Redistributions of source code must retain the above copyright
12*2bfe3f2eSlogwang#     notice, this list of conditions and the following disclaimer.
13*2bfe3f2eSlogwang#   * Redistributions in binary form must reproduce the above copyright
14*2bfe3f2eSlogwang#     notice, this list of conditions and the following disclaimer in
15*2bfe3f2eSlogwang#     the documentation and/or other materials provided with the
16*2bfe3f2eSlogwang#     distribution.
17*2bfe3f2eSlogwang#   * Neither the name of 6WIND S.A. nor the names of its
18*2bfe3f2eSlogwang#     contributors may be used to endorse or promote products derived
19*2bfe3f2eSlogwang#     from this software without specific prior written permission.
20*2bfe3f2eSlogwang#
21*2bfe3f2eSlogwang# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22*2bfe3f2eSlogwang# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23*2bfe3f2eSlogwang# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24*2bfe3f2eSlogwang# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25*2bfe3f2eSlogwang# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26*2bfe3f2eSlogwang# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27*2bfe3f2eSlogwang# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28*2bfe3f2eSlogwang# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29*2bfe3f2eSlogwang# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30*2bfe3f2eSlogwang# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31*2bfe3f2eSlogwang# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32*2bfe3f2eSlogwang
33*2bfe3f2eSlogwang# Check commit logs (headlines and references)
34*2bfe3f2eSlogwang#
35*2bfe3f2eSlogwang# If any doubt about the formatting, please check in the most recent history:
36*2bfe3f2eSlogwang#	git log --format='%>|(15)%cr   %s' --reverse | grep -i <pattern>
37*2bfe3f2eSlogwang
38*2bfe3f2eSlogwangif [ "$1" = '-h' -o "$1" = '--help' ] ; then
39*2bfe3f2eSlogwang	cat <<- END_OF_HELP
40*2bfe3f2eSlogwang	usage: $(basename $0) [-h] [range]
41*2bfe3f2eSlogwang
42*2bfe3f2eSlogwang	Check commit log formatting.
43*2bfe3f2eSlogwang	The git range can be specified as a "git log" option,
44*2bfe3f2eSlogwang	e.g. -1 to check only the latest commit.
45*2bfe3f2eSlogwang	The default range starts from origin/master to HEAD.
46*2bfe3f2eSlogwang	END_OF_HELP
47*2bfe3f2eSlogwang	exit
48*2bfe3f2eSlogwangfi
49*2bfe3f2eSlogwang
50*2bfe3f2eSlogwangselfdir=$(dirname $(readlink -f $0))
51*2bfe3f2eSlogwangrange=${1:-origin/master..}
52*2bfe3f2eSlogwang# convert -N to HEAD~N.. in order to comply with git-log-fixes.sh getopts
53*2bfe3f2eSlogwangif printf -- $range | grep -q '^-[0-9]\+' ; then
54*2bfe3f2eSlogwang	range="HEAD$(printf -- $range | sed 's,^-,~,').."
55*2bfe3f2eSlogwangfi
56*2bfe3f2eSlogwang
57*2bfe3f2eSlogwangcommits=$(git log --format='%h' --reverse $range)
58*2bfe3f2eSlogwangheadlines=$(git log --format='%s' --reverse $range)
59*2bfe3f2eSlogwangbodylines=$(git log --format='%b' --reverse $range)
60*2bfe3f2eSlogwangfixes=$(git log --format='%h %s' --reverse $range | grep -i ': *fix' | cut -d' ' -f1)
61*2bfe3f2eSlogwangstablefixes=$($selfdir/git-log-fixes.sh $range | sed '/(N\/A)$/d'  | cut -d' ' -f2)
62*2bfe3f2eSlogwangtags=$(git log --format='%b' --reverse $range | grep -i -e 'by *:' -e 'fix.*:')
63*2bfe3f2eSlogwangbytag='\(Reported\|Suggested\|Signed-off\|Acked\|Reviewed\|Tested\)-by:'
64*2bfe3f2eSlogwang
65*2bfe3f2eSlogwang# check headline format (spacing, no punctuation, no code)
66*2bfe3f2eSlogwangbad=$(echo "$headlines" | grep --color=always \
67*2bfe3f2eSlogwang	-e '	' \
68*2bfe3f2eSlogwang	-e '^ ' \
69*2bfe3f2eSlogwang	-e ' $' \
70*2bfe3f2eSlogwang	-e '\.$' \
71*2bfe3f2eSlogwang	-e '[,;!?&|]' \
72*2bfe3f2eSlogwang	-e ':.*_' \
73*2bfe3f2eSlogwang	-e '^[^:]\+$' \
74*2bfe3f2eSlogwang	-e ':[^ ]' \
75*2bfe3f2eSlogwang	-e ' :' \
76*2bfe3f2eSlogwang	| sed 's,^,\t,')
77*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong headline format:\n$bad\n"
78*2bfe3f2eSlogwang
79*2bfe3f2eSlogwang# check headline prefix when touching only drivers, e.g. net/<driver name>
80*2bfe3f2eSlogwangbad=$(for commit in $commits ; do
81*2bfe3f2eSlogwang	headline=$(git log --format='%s' -1 $commit)
82*2bfe3f2eSlogwang	files=$(git diff-tree --no-commit-id --name-only -r $commit)
83*2bfe3f2eSlogwang	[ -z "$(echo "$files" | grep -v '^\(drivers\|doc\|config\)/')" ] ||
84*2bfe3f2eSlogwang		continue
85*2bfe3f2eSlogwang	drv=$(echo "$files" | grep '^drivers/' | cut -d "/" -f 2,3 | sort -u)
86*2bfe3f2eSlogwang	drvgrp=$(echo "$drv" | cut -d "/" -f 1 | uniq)
87*2bfe3f2eSlogwang	if [ $(echo "$drvgrp" | wc -l) -gt 1 ] ; then
88*2bfe3f2eSlogwang		echo "$headline" | grep -v '^drivers:'
89*2bfe3f2eSlogwang	elif [ $(echo "$drv" | wc -l) -gt 1 ] ; then
90*2bfe3f2eSlogwang		echo "$headline" | grep -v "^drivers/$drvgrp"
91*2bfe3f2eSlogwang	else
92*2bfe3f2eSlogwang		echo "$headline" | grep -v "^$drv"
93*2bfe3f2eSlogwang	fi
94*2bfe3f2eSlogwangdone | sed 's,^,\t,')
95*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong headline prefix:\n$bad\n"
96*2bfe3f2eSlogwang
97*2bfe3f2eSlogwang# check headline label for common typos
98*2bfe3f2eSlogwangbad=$(echo "$headlines" | grep --color=always \
99*2bfe3f2eSlogwang	-e '^example[:/]' \
100*2bfe3f2eSlogwang	-e '^apps/' \
101*2bfe3f2eSlogwang	-e '^testpmd' \
102*2bfe3f2eSlogwang	-e 'test-pmd' \
103*2bfe3f2eSlogwang	-e '^bond:' \
104*2bfe3f2eSlogwang	| sed 's,^,\t,')
105*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong headline label:\n$bad\n"
106*2bfe3f2eSlogwang
107*2bfe3f2eSlogwang# check headline lowercase for first words
108*2bfe3f2eSlogwangbad=$(echo "$headlines" | grep --color=always \
109*2bfe3f2eSlogwang	-e '^.*[A-Z].*:' \
110*2bfe3f2eSlogwang	-e ': *[A-Z]' \
111*2bfe3f2eSlogwang	| sed 's,^,\t,')
112*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong headline uppercase:\n$bad\n"
113*2bfe3f2eSlogwang
114*2bfe3f2eSlogwang# check headline uppercase (Rx/Tx, VF, L2, MAC, Linux, ARM...)
115*2bfe3f2eSlogwangbad=$(echo "$headlines" | grep -E --color=always \
116*2bfe3f2eSlogwang	-e ':.*\<(rx|tx|RX|TX)\>' \
117*2bfe3f2eSlogwang	-e ':.*\<[pv]f\>' \
118*2bfe3f2eSlogwang	-e ':.*\<[hsf]w\>' \
119*2bfe3f2eSlogwang	-e ':.*\<l[234]\>' \
120*2bfe3f2eSlogwang	-e ':.*\<api\>' \
121*2bfe3f2eSlogwang	-e ':.*\<arm\>' \
122*2bfe3f2eSlogwang	-e ':.*\<armv7\>' \
123*2bfe3f2eSlogwang	-e ':.*\<armv8\>' \
124*2bfe3f2eSlogwang	-e ':.*\<crc\>' \
125*2bfe3f2eSlogwang	-e ':.*\<dma\>' \
126*2bfe3f2eSlogwang	-e ':.*\<freebsd\>' \
127*2bfe3f2eSlogwang	-e ':.*\<linux\>' \
128*2bfe3f2eSlogwang	-e ':.*\<lro\>' \
129*2bfe3f2eSlogwang	-e ':.*\<lsc\>' \
130*2bfe3f2eSlogwang	-e ':.*\<mac\>' \
131*2bfe3f2eSlogwang	-e ':.*\<mtu\>' \
132*2bfe3f2eSlogwang	-e ':.*\<nic\>' \
133*2bfe3f2eSlogwang	-e ':.*\<nvm\>' \
134*2bfe3f2eSlogwang	-e ':.*\<numa\>' \
135*2bfe3f2eSlogwang	-e ':.*\<pci\>' \
136*2bfe3f2eSlogwang	-e ':.*\<pmd\>' \
137*2bfe3f2eSlogwang	-e ':.*\<rss\>' \
138*2bfe3f2eSlogwang	-e ':.*\<tso\>' \
139*2bfe3f2eSlogwang	-e ':.*\<[Vv]lan\>' \
140*2bfe3f2eSlogwang	-e ':.*\<vsi\>' \
141*2bfe3f2eSlogwang	| sed 's,^,\t,')
142*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong headline lowercase:\n$bad\n"
143*2bfe3f2eSlogwang
144*2bfe3f2eSlogwang# special case check for VMDq to give good error message
145*2bfe3f2eSlogwangbad=$(echo "$headlines" | grep -E --color=always \
146*2bfe3f2eSlogwang	-e '\<(vmdq|VMDQ)\>' \
147*2bfe3f2eSlogwang	| sed 's,^,\t,')
148*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong headline capitalization, use 'VMDq':\n$bad\n"
149*2bfe3f2eSlogwang
150*2bfe3f2eSlogwang# check headline length (60 max)
151*2bfe3f2eSlogwangbad=$(echo "$headlines" |
152*2bfe3f2eSlogwang	awk 'length>60 {print}' |
153*2bfe3f2eSlogwang	sed 's,^,\t,')
154*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Headline too long:\n$bad\n"
155*2bfe3f2eSlogwang
156*2bfe3f2eSlogwang# check body lines length (75 max)
157*2bfe3f2eSlogwangbad=$(echo "$bodylines" | grep -v '^Fixes:' |
158*2bfe3f2eSlogwang	awk 'length>75 {print}' |
159*2bfe3f2eSlogwang	sed 's,^,\t,')
160*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Line too long:\n$bad\n"
161*2bfe3f2eSlogwang
162*2bfe3f2eSlogwang# check starting commit message with "It"
163*2bfe3f2eSlogwangbad=$(for commit in $commits ; do
164*2bfe3f2eSlogwang	firstbodyline=$(git log --format='%b' -1 $commit | head -n1)
165*2bfe3f2eSlogwang	echo "$firstbodyline" | grep --color=always -ie '^It '
166*2bfe3f2eSlogwangdone | sed 's,^,\t,')
167*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong beginning of commit message:\n$bad\n"
168*2bfe3f2eSlogwang
169*2bfe3f2eSlogwang# check tags spelling
170*2bfe3f2eSlogwangbad=$(echo "$tags" |
171*2bfe3f2eSlogwang	grep -v "^$bytag [^,]* <.*@.*>$" |
172*2bfe3f2eSlogwang	grep -v '^Fixes: [0-9a-f]\{7\}[0-9a-f]* (".*")$' |
173*2bfe3f2eSlogwang	sed 's,^.,\t&,')
174*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong tag:\n$bad\n"
175*2bfe3f2eSlogwang
176*2bfe3f2eSlogwang# check missing Fixes: tag
177*2bfe3f2eSlogwangbad=$(for fix in $fixes ; do
178*2bfe3f2eSlogwang	git log --format='%b' -1 $fix | grep -q '^Fixes: ' ||
179*2bfe3f2eSlogwang		git log --format='\t%s' -1 $fix
180*2bfe3f2eSlogwangdone)
181*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Missing 'Fixes' tag:\n$bad\n"
182*2bfe3f2eSlogwang
183*2bfe3f2eSlogwang# check Fixes: reference
184*2bfe3f2eSlogwangIFS='
185*2bfe3f2eSlogwang'
186*2bfe3f2eSlogwangfixtags=$(echo "$tags" | grep '^Fixes: ')
187*2bfe3f2eSlogwangbad=$(for fixtag in $fixtags ; do
188*2bfe3f2eSlogwang	hash=$(echo "$fixtag" | sed 's,^Fixes: \([0-9a-f]*\).*,\1,')
189*2bfe3f2eSlogwang	if git branch --contains $hash 2>&- | grep -q '^\*' ; then
190*2bfe3f2eSlogwang		good="Fixes: $hash "$(git log --format='("%s")' -1 $hash 2>&-)
191*2bfe3f2eSlogwang	else
192*2bfe3f2eSlogwang		good="reference not in current branch"
193*2bfe3f2eSlogwang	fi
194*2bfe3f2eSlogwang	printf "$fixtag" | grep -v "^$good$"
195*2bfe3f2eSlogwangdone | sed 's,^,\t,')
196*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong 'Fixes' reference:\n$bad\n"
197*2bfe3f2eSlogwang
198*2bfe3f2eSlogwang# check Cc: [email protected] for fixes
199*2bfe3f2eSlogwangbad=$(for fix in $stablefixes ; do
200*2bfe3f2eSlogwang	git log --format='%b' -1 $fix | grep -qi '^Cc: *[email protected]' ||
201*2bfe3f2eSlogwang		git log --format='\t%s' -1 $fix
202*2bfe3f2eSlogwangdone)
203*2bfe3f2eSlogwang[ -z "$bad" ] || printf "Is it candidate for Cc: [email protected] backport?\n$bad\n"
204