1*2bfe3f2eSlogwang#! /bin/sh -e 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*2bfe3f2eSlogwangprint_usage () 34*2bfe3f2eSlogwang{ 35*2bfe3f2eSlogwang echo "usage: $(basename $0) [-h] <git_range>" 36*2bfe3f2eSlogwang} 37*2bfe3f2eSlogwang 38*2bfe3f2eSlogwangprint_help () 39*2bfe3f2eSlogwang{ 40*2bfe3f2eSlogwang print_usage 41*2bfe3f2eSlogwang cat <<- END_OF_HELP 42*2bfe3f2eSlogwang 43*2bfe3f2eSlogwang Find fixes to backport on previous versions. 44*2bfe3f2eSlogwang It looks for the word "fix" in the headline or a tag "Fixes" or "Reverts". 45*2bfe3f2eSlogwang The oldest bug origin is printed as well as partially fixed versions. 46*2bfe3f2eSlogwang END_OF_HELP 47*2bfe3f2eSlogwang} 48*2bfe3f2eSlogwang 49*2bfe3f2eSlogwangusage_error () # <message> 50*2bfe3f2eSlogwang{ 51*2bfe3f2eSlogwang echo "$*" >&2 52*2bfe3f2eSlogwang print_usage >&2 53*2bfe3f2eSlogwang exit 1 54*2bfe3f2eSlogwang} 55*2bfe3f2eSlogwang 56*2bfe3f2eSlogwangwhile getopts h ARG ; do 57*2bfe3f2eSlogwang case $ARG in 58*2bfe3f2eSlogwang h ) print_help ; exit 0 ;; 59*2bfe3f2eSlogwang ? ) print_usage >&2 ; exit 1 ;; 60*2bfe3f2eSlogwang esac 61*2bfe3f2eSlogwangdone 62*2bfe3f2eSlogwangshift $(($OPTIND - 1)) 63*2bfe3f2eSlogwang[ $# -ge 1 ] || usage_error 'range argument required' 64*2bfe3f2eSlogwangrange="$*" 65*2bfe3f2eSlogwang 66*2bfe3f2eSlogwang# get major release version of a commit 67*2bfe3f2eSlogwangcommit_version () # <hash> 68*2bfe3f2eSlogwang{ 69*2bfe3f2eSlogwang # use current branch as history reference 70*2bfe3f2eSlogwang local refbranch=$(git rev-parse --abbrev-ref HEAD) 71*2bfe3f2eSlogwang local tag=$( (git tag -l --contains $1 --merged $refbranch 2>&- || 72*2bfe3f2eSlogwang # tag --merged option has been introduced in git 2.7.0 73*2bfe3f2eSlogwang # below is a fallback in case of old git version 74*2bfe3f2eSlogwang for t in $(git tag -l --contains $1) ; do 75*2bfe3f2eSlogwang git branch $refbranch --contains $t | 76*2bfe3f2eSlogwang sed "s,.\+,$t," 77*2bfe3f2eSlogwang done) | 78*2bfe3f2eSlogwang head -n1) 79*2bfe3f2eSlogwang if [ -z "$tag" ] ; then 80*2bfe3f2eSlogwang # before -rc1 tag of release in progress 81*2bfe3f2eSlogwang make showversion | cut -d'.' -f-2 82*2bfe3f2eSlogwang else 83*2bfe3f2eSlogwang echo $tag | sed 's,^v,,' | sed 's,-rc.*,,' 84*2bfe3f2eSlogwang fi 85*2bfe3f2eSlogwang} 86*2bfe3f2eSlogwang 87*2bfe3f2eSlogwang# get bug origin hashes of a fix 88*2bfe3f2eSlogwangorigin_filter () # <hash> 89*2bfe3f2eSlogwang{ 90*2bfe3f2eSlogwang git log --format='%b' -1 $1 | 91*2bfe3f2eSlogwang sed -n 's,^ *\([Ff]ixes\|[Rr]everts\): *\([0-9a-f]*\).*,\2,p' 92*2bfe3f2eSlogwang} 93*2bfe3f2eSlogwang 94*2bfe3f2eSlogwang# get oldest major release version of bug origins 95*2bfe3f2eSlogwangorigin_version () # <origin_hash> ... 96*2bfe3f2eSlogwang{ 97*2bfe3f2eSlogwang for origin in $* ; do 98*2bfe3f2eSlogwang # check hash is valid 99*2bfe3f2eSlogwang git rev-parse -q --verify $1 >&- || continue 100*2bfe3f2eSlogwang # get version of this bug origin 101*2bfe3f2eSlogwang local origver=$(commit_version $origin) 102*2bfe3f2eSlogwang local roothashes="$(origin_filter $origin)" 103*2bfe3f2eSlogwang if [ -n "$roothashes" ] ; then 104*2bfe3f2eSlogwang # look chained fix of fix recursively 105*2bfe3f2eSlogwang local rootver="$(origin_version $roothashes)" 106*2bfe3f2eSlogwang [ -n "$rootver" ] || continue 107*2bfe3f2eSlogwang echo "$rootver (partially fixed in $origver)" 108*2bfe3f2eSlogwang else 109*2bfe3f2eSlogwang echo "$origver" 110*2bfe3f2eSlogwang fi 111*2bfe3f2eSlogwang # filter the oldest origin 112*2bfe3f2eSlogwang done | sort -uV | head -n1 113*2bfe3f2eSlogwang} 114*2bfe3f2eSlogwang 115*2bfe3f2eSlogwang# print a marker for stable tag presence 116*2bfe3f2eSlogwangstable_tag () # <hash> 117*2bfe3f2eSlogwang{ 118*2bfe3f2eSlogwang if git log --format='%b' -1 $1 | grep -qi '^Cc: *[email protected]' ; then 119*2bfe3f2eSlogwang echo 'S' 120*2bfe3f2eSlogwang else 121*2bfe3f2eSlogwang echo '-' 122*2bfe3f2eSlogwang fi 123*2bfe3f2eSlogwang} 124*2bfe3f2eSlogwang 125*2bfe3f2eSlogwanggit log --oneline --reverse $range | 126*2bfe3f2eSlogwangwhile read id headline ; do 127*2bfe3f2eSlogwang origins=$(origin_filter $id) 128*2bfe3f2eSlogwang stable=$(stable_tag $id) 129*2bfe3f2eSlogwang [ "$stable" = "S" ] || [ -n "$origins" ] || echo "$headline" | grep -q fix || continue 130*2bfe3f2eSlogwang version=$(commit_version $id) 131*2bfe3f2eSlogwang if [ -n "$origins" ] ; then 132*2bfe3f2eSlogwang origver="$(origin_version $origins)" 133*2bfe3f2eSlogwang [ -n "$origver" ] || continue 134*2bfe3f2eSlogwang # ignore fix of bug introduced in the same release 135*2bfe3f2eSlogwang ! echo "$origver" | grep -q "^$version" || continue 136*2bfe3f2eSlogwang else 137*2bfe3f2eSlogwang origver='N/A' 138*2bfe3f2eSlogwang fi 139*2bfe3f2eSlogwang printf '%s %7s %s %s (%s)\n' $version $id $stable "$headline" "$origver" 140*2bfe3f2eSlogwangdone 141