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