12bfe3f2eSlogwang#! /bin/sh 2*d30ea906Sjfb8856606# SPDX-License-Identifier: BSD-3-Clause 32bfe3f2eSlogwang# Copyright 2016 6WIND S.A. 42bfe3f2eSlogwang 52bfe3f2eSlogwang# Check commit logs (headlines and references) 62bfe3f2eSlogwang# 72bfe3f2eSlogwang# If any doubt about the formatting, please check in the most recent history: 82bfe3f2eSlogwang# git log --format='%>|(15)%cr %s' --reverse | grep -i <pattern> 92bfe3f2eSlogwang 102bfe3f2eSlogwangif [ "$1" = '-h' -o "$1" = '--help' ] ; then 112bfe3f2eSlogwang cat <<- END_OF_HELP 122bfe3f2eSlogwang usage: $(basename $0) [-h] [range] 132bfe3f2eSlogwang 142bfe3f2eSlogwang Check commit log formatting. 152bfe3f2eSlogwang The git range can be specified as a "git log" option, 162bfe3f2eSlogwang e.g. -1 to check only the latest commit. 172bfe3f2eSlogwang The default range starts from origin/master to HEAD. 182bfe3f2eSlogwang END_OF_HELP 192bfe3f2eSlogwang exit 202bfe3f2eSlogwangfi 212bfe3f2eSlogwang 222bfe3f2eSlogwangselfdir=$(dirname $(readlink -f $0)) 232bfe3f2eSlogwangrange=${1:-origin/master..} 242bfe3f2eSlogwang# convert -N to HEAD~N.. in order to comply with git-log-fixes.sh getopts 252bfe3f2eSlogwangif printf -- $range | grep -q '^-[0-9]\+' ; then 262bfe3f2eSlogwang range="HEAD$(printf -- $range | sed 's,^-,~,').." 272bfe3f2eSlogwangfi 282bfe3f2eSlogwang 292bfe3f2eSlogwangcommits=$(git log --format='%h' --reverse $range) 302bfe3f2eSlogwangheadlines=$(git log --format='%s' --reverse $range) 312bfe3f2eSlogwangbodylines=$(git log --format='%b' --reverse $range) 322bfe3f2eSlogwangfixes=$(git log --format='%h %s' --reverse $range | grep -i ': *fix' | cut -d' ' -f1) 332bfe3f2eSlogwangstablefixes=$($selfdir/git-log-fixes.sh $range | sed '/(N\/A)$/d' | cut -d' ' -f2) 342bfe3f2eSlogwangtags=$(git log --format='%b' --reverse $range | grep -i -e 'by *:' -e 'fix.*:') 352bfe3f2eSlogwangbytag='\(Reported\|Suggested\|Signed-off\|Acked\|Reviewed\|Tested\)-by:' 362bfe3f2eSlogwang 372bfe3f2eSlogwang# check headline format (spacing, no punctuation, no code) 382bfe3f2eSlogwangbad=$(echo "$headlines" | grep --color=always \ 392bfe3f2eSlogwang -e ' ' \ 402bfe3f2eSlogwang -e '^ ' \ 412bfe3f2eSlogwang -e ' $' \ 422bfe3f2eSlogwang -e '\.$' \ 432bfe3f2eSlogwang -e '[,;!?&|]' \ 442bfe3f2eSlogwang -e ':.*_' \ 452bfe3f2eSlogwang -e '^[^:]\+$' \ 462bfe3f2eSlogwang -e ':[^ ]' \ 472bfe3f2eSlogwang -e ' :' \ 482bfe3f2eSlogwang | sed 's,^,\t,') 492bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong headline format:\n$bad\n" 502bfe3f2eSlogwang 512bfe3f2eSlogwang# check headline prefix when touching only drivers, e.g. net/<driver name> 522bfe3f2eSlogwangbad=$(for commit in $commits ; do 532bfe3f2eSlogwang headline=$(git log --format='%s' -1 $commit) 542bfe3f2eSlogwang files=$(git diff-tree --no-commit-id --name-only -r $commit) 552bfe3f2eSlogwang [ -z "$(echo "$files" | grep -v '^\(drivers\|doc\|config\)/')" ] || 562bfe3f2eSlogwang continue 572bfe3f2eSlogwang drv=$(echo "$files" | grep '^drivers/' | cut -d "/" -f 2,3 | sort -u) 582bfe3f2eSlogwang drvgrp=$(echo "$drv" | cut -d "/" -f 1 | uniq) 592bfe3f2eSlogwang if [ $(echo "$drvgrp" | wc -l) -gt 1 ] ; then 602bfe3f2eSlogwang echo "$headline" | grep -v '^drivers:' 612bfe3f2eSlogwang elif [ $(echo "$drv" | wc -l) -gt 1 ] ; then 622bfe3f2eSlogwang echo "$headline" | grep -v "^drivers/$drvgrp" 632bfe3f2eSlogwang else 642bfe3f2eSlogwang echo "$headline" | grep -v "^$drv" 652bfe3f2eSlogwang fi 662bfe3f2eSlogwangdone | sed 's,^,\t,') 672bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong headline prefix:\n$bad\n" 682bfe3f2eSlogwang 692bfe3f2eSlogwang# check headline label for common typos 702bfe3f2eSlogwangbad=$(echo "$headlines" | grep --color=always \ 712bfe3f2eSlogwang -e '^example[:/]' \ 722bfe3f2eSlogwang -e '^apps/' \ 732bfe3f2eSlogwang -e '^testpmd' \ 742bfe3f2eSlogwang -e 'test-pmd' \ 752bfe3f2eSlogwang -e '^bond:' \ 762bfe3f2eSlogwang | sed 's,^,\t,') 772bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong headline label:\n$bad\n" 782bfe3f2eSlogwang 792bfe3f2eSlogwang# check headline lowercase for first words 802bfe3f2eSlogwangbad=$(echo "$headlines" | grep --color=always \ 81*d30ea906Sjfb8856606 -e '^.*[[:upper:]].*:' \ 82*d30ea906Sjfb8856606 -e ': *[[:upper:]]' \ 832bfe3f2eSlogwang | sed 's,^,\t,') 842bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong headline uppercase:\n$bad\n" 852bfe3f2eSlogwang 862bfe3f2eSlogwang# check headline uppercase (Rx/Tx, VF, L2, MAC, Linux, ARM...) 872bfe3f2eSlogwangbad=$(echo "$headlines" | grep -E --color=always \ 882bfe3f2eSlogwang -e ':.*\<(rx|tx|RX|TX)\>' \ 892bfe3f2eSlogwang -e ':.*\<[pv]f\>' \ 902bfe3f2eSlogwang -e ':.*\<[hsf]w\>' \ 912bfe3f2eSlogwang -e ':.*\<l[234]\>' \ 922bfe3f2eSlogwang -e ':.*\<api\>' \ 932bfe3f2eSlogwang -e ':.*\<arm\>' \ 942bfe3f2eSlogwang -e ':.*\<armv7\>' \ 952bfe3f2eSlogwang -e ':.*\<armv8\>' \ 962bfe3f2eSlogwang -e ':.*\<crc\>' \ 972bfe3f2eSlogwang -e ':.*\<dma\>' \ 98*d30ea906Sjfb8856606 -e ':.*\<eeprom\>' \ 992bfe3f2eSlogwang -e ':.*\<freebsd\>' \ 100*d30ea906Sjfb8856606 -e ':.*\<iova\>' \ 1012bfe3f2eSlogwang -e ':.*\<linux\>' \ 1022bfe3f2eSlogwang -e ':.*\<lro\>' \ 1032bfe3f2eSlogwang -e ':.*\<lsc\>' \ 1042bfe3f2eSlogwang -e ':.*\<mac\>' \ 105*d30ea906Sjfb8856606 -e ':.*\<mss\>' \ 1062bfe3f2eSlogwang -e ':.*\<mtu\>' \ 1072bfe3f2eSlogwang -e ':.*\<nic\>' \ 1082bfe3f2eSlogwang -e ':.*\<nvm\>' \ 1092bfe3f2eSlogwang -e ':.*\<numa\>' \ 1102bfe3f2eSlogwang -e ':.*\<pci\>' \ 111*d30ea906Sjfb8856606 -e ':.*\<phy\>' \ 1122bfe3f2eSlogwang -e ':.*\<pmd\>' \ 1132bfe3f2eSlogwang -e ':.*\<rss\>' \ 114*d30ea906Sjfb8856606 -e ':.*\<sctp\>' \ 1152bfe3f2eSlogwang -e ':.*\<tso\>' \ 116*d30ea906Sjfb8856606 -e ':.*\<udp\>' \ 1172bfe3f2eSlogwang -e ':.*\<[Vv]lan\>' \ 118*d30ea906Sjfb8856606 -e ':.*\<vdpa\>' \ 1192bfe3f2eSlogwang -e ':.*\<vsi\>' \ 120*d30ea906Sjfb8856606 | grep \ 121*d30ea906Sjfb8856606 -v ':.*\<OCTEON\ TX\>' \ 1222bfe3f2eSlogwang | sed 's,^,\t,') 1232bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong headline lowercase:\n$bad\n" 1242bfe3f2eSlogwang 1252bfe3f2eSlogwang# special case check for VMDq to give good error message 1262bfe3f2eSlogwangbad=$(echo "$headlines" | grep -E --color=always \ 1272bfe3f2eSlogwang -e '\<(vmdq|VMDQ)\>' \ 1282bfe3f2eSlogwang | sed 's,^,\t,') 1292bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong headline capitalization, use 'VMDq':\n$bad\n" 1302bfe3f2eSlogwang 1312bfe3f2eSlogwang# check headline length (60 max) 1322bfe3f2eSlogwangbad=$(echo "$headlines" | 1332bfe3f2eSlogwang awk 'length>60 {print}' | 1342bfe3f2eSlogwang sed 's,^,\t,') 1352bfe3f2eSlogwang[ -z "$bad" ] || printf "Headline too long:\n$bad\n" 1362bfe3f2eSlogwang 1372bfe3f2eSlogwang# check body lines length (75 max) 1382bfe3f2eSlogwangbad=$(echo "$bodylines" | grep -v '^Fixes:' | 1392bfe3f2eSlogwang awk 'length>75 {print}' | 1402bfe3f2eSlogwang sed 's,^,\t,') 1412bfe3f2eSlogwang[ -z "$bad" ] || printf "Line too long:\n$bad\n" 1422bfe3f2eSlogwang 1432bfe3f2eSlogwang# check starting commit message with "It" 1442bfe3f2eSlogwangbad=$(for commit in $commits ; do 1452bfe3f2eSlogwang firstbodyline=$(git log --format='%b' -1 $commit | head -n1) 1462bfe3f2eSlogwang echo "$firstbodyline" | grep --color=always -ie '^It ' 1472bfe3f2eSlogwangdone | sed 's,^,\t,') 1482bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong beginning of commit message:\n$bad\n" 1492bfe3f2eSlogwang 1502bfe3f2eSlogwang# check tags spelling 1512bfe3f2eSlogwangbad=$(echo "$tags" | 1522bfe3f2eSlogwang grep -v "^$bytag [^,]* <.*@.*>$" | 1532bfe3f2eSlogwang grep -v '^Fixes: [0-9a-f]\{7\}[0-9a-f]* (".*")$' | 1542bfe3f2eSlogwang sed 's,^.,\t&,') 1552bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong tag:\n$bad\n" 1562bfe3f2eSlogwang 1572bfe3f2eSlogwang# check missing Fixes: tag 1582bfe3f2eSlogwangbad=$(for fix in $fixes ; do 1592bfe3f2eSlogwang git log --format='%b' -1 $fix | grep -q '^Fixes: ' || 1602bfe3f2eSlogwang git log --format='\t%s' -1 $fix 1612bfe3f2eSlogwangdone) 1622bfe3f2eSlogwang[ -z "$bad" ] || printf "Missing 'Fixes' tag:\n$bad\n" 1632bfe3f2eSlogwang 1642bfe3f2eSlogwang# check Fixes: reference 1652bfe3f2eSlogwangIFS=' 1662bfe3f2eSlogwang' 1672bfe3f2eSlogwangfixtags=$(echo "$tags" | grep '^Fixes: ') 1682bfe3f2eSlogwangbad=$(for fixtag in $fixtags ; do 1692bfe3f2eSlogwang hash=$(echo "$fixtag" | sed 's,^Fixes: \([0-9a-f]*\).*,\1,') 1702bfe3f2eSlogwang if git branch --contains $hash 2>&- | grep -q '^\*' ; then 1712bfe3f2eSlogwang good="Fixes: $hash "$(git log --format='("%s")' -1 $hash 2>&-) 1722bfe3f2eSlogwang else 1732bfe3f2eSlogwang good="reference not in current branch" 1742bfe3f2eSlogwang fi 1752bfe3f2eSlogwang printf "$fixtag" | grep -v "^$good$" 1762bfe3f2eSlogwangdone | sed 's,^,\t,') 1772bfe3f2eSlogwang[ -z "$bad" ] || printf "Wrong 'Fixes' reference:\n$bad\n" 1782bfe3f2eSlogwang 1792bfe3f2eSlogwang# check Cc: [email protected] for fixes 1802bfe3f2eSlogwangbad=$(for fix in $stablefixes ; do 1812bfe3f2eSlogwang git log --format='%b' -1 $fix | grep -qi '^Cc: *[email protected]' || 1822bfe3f2eSlogwang git log --format='\t%s' -1 $fix 1832bfe3f2eSlogwangdone) 1842bfe3f2eSlogwang[ -z "$bad" ] || printf "Is it candidate for Cc: [email protected] backport?\n$bad\n" 185