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