dtc/redhat/scripts/git-backport-diff
2017-03-06 10:48:03 +01:00

302 lines
8.6 KiB
Bash
Executable file

#!/bin/bash
#
# See $desc, below, for program description
#
# Copyright (c) 2013 Red Hat, Inc.
#
# Author(s):
# Jeff Cody <jcody@redhat.com>
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; under version 2 of the license
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
#
set -C -u -e
set -o pipefail
desc="
$0 compares the commits in a specified git range, with the corresponding
upstream commit. This assumes that you have both the downstream and
upstream repositories added as remotes in your git repo.
Example usage:
$0 -r qemu-kvm-0.12.1.2-2.370.el6..my_feature_branch -u v1.5.0
Ouput key:
[----] Indicates the patches upstream and downstream are identical
[####] Indicates the number of differences (#### is substituted)
[down] Indicates the patch only exists downstream"
def_upstream="v1.7.0"
def_diffprog=meld
def_range="HEAD^!"
def_color='y'
def_pause='y'
def_sensitivity=0
upstream=`git config backport-diff.upstream || true`
diffprog=`git config backport-diff.diffprog || true`
range=`git config backport-diff.range || true`
color=`git config backport-diff.color || true`
pause=`git config backport-diff.pause || true`
sensitivity=`git config backport-diff.sensitivity || true`
if [[ -z "$upstream" ]]
then
upstream=$def_upstream
git config backport-diff.upstream $upstream || true
fi
if [[ -z "$diffprog" ]]
then
diffprog=$def_diffprog
git config backport-diff.diffprog $diffprog || true
fi
if [[ -z "$range" ]]
then
range=$def_range
git config backport-diff.range $range || true
fi
if [[ -z "$color" ]]
then
color=$def_color
git config backport-diff.color $color || true
fi
if [[ -z "$pause" ]]
then
pause=$def_pause
git config backport-diff.pause $pause || true
fi
if [[ -z "$sensitivity" ]]
then
sensitivity=$def_sensitivity
git config backport-diff.sensitivity $sensitivity || true
fi
usage() {
echo ""
echo "$0 [OPTIONS]"
echo "$desc"
echo ""
echo "OPTIONS:"
echo " -r git range
optional; default is '$range'
"
echo " -u upstream git tag / branch / id
optional; default is '$upstream'
"
echo " -n do not use colors
"
echo " -d diff program to use
optional; default is '$diffprog'
"
echo " -p do not pause when viewing diffs
"
echo " -s sensitivity level of diff compares
0: only show functional code differences
1: show (0) + contextual differences
2+: offer to compare all patches, regardless of any differences
"
echo " -h help"
echo ""
echo "You can set each of the default options using git-config:"
echo " git config backport-diff.upstream"
echo " git config backport-diff.diffprog"
echo " git config backport-diff.range"
echo " git config backport-diff.color"
echo " git config backport-diff.pause"
}
while getopts ":r:u:nd:phs:" opt
do
case $opt in
r) range=$OPTARG
;;
u) upstream=$OPTARG
;;
n) color='n'
;;
d) diffprog=$OPTARG
;;
p) pause='n'
;;
s) sensitivity=$OPTARG
if ! [[ "$sensitivity" =~ ^[0-9]+$ ]]
then
echo "Invalid argument for -s" >&2
usage
exit 1
fi
;;
h) usage
exit
;;
\?) echo "Unknown option: -$OPTARG" >&2
usage
exit 1
;;
esac
done
if [[ $color == 'y' ]]
then
bold=$(tput bold)
color1=$(tput setaf 1)
color2=$(tput setaf 2)
color3=$(tput setaf 3)
color4=$(tput setaf 4)
color5=$(tput setaf 5)
color6=$(tput setaf 6)
color7=$(tput setaf 7)
reset=$(tput sgr0)
else
bold=
color1=
color2=
color3=
color4=
color5=
color6=
color7=
reset=
fi
trap cleanup EXIT
cleanup() {
echo $reset
}
total=`git rev-list "$range" |wc -l`
# verify the upstream exists
upstream_valid='n'
# branch
if git show-ref --quiet --verify refs/heads/${upstream}
then
upstream_valid='y'
# tag
elif git show-ref --quiet --verify refs/tags/${upstream}
then
upstream_valid='y'
# remote branch
elif git show-ref --quiet --verify refs/remotes/${upstream}
then
upstream_valid='y'
# commit id
elif git rev-list --max-count=1 --quiet $upstream >/dev/null 2>&1
then
upstream_valid='y'
fi
numdiff=0
label=
subjlist=
cnt=0
compare_git()
{
echo "Key:"
printf "[----] : patches are identical\n"
printf "[${bold}####${reset}] : number of functional differences between upstream/downstream patch\n"
printf "[${bold}${color1}down${reset}] : patch is downstream-only\n"
printf "The flags [${bold}FC${reset}] indicate (F)unctional and (C)ontextual differences, respectively\n\n"
# don't pipe the git job into read, to avoid subshells
while read hashsubj
do
let cnt=$cnt+1;
subj=${hashsubj:40}
downhash=${hashsubj:0:40}
# A little bit hackish, but find the match by looking at upstream
# subject lines, and using the last one. Not all backports contain
# the phrase "cherry-pick", so we can't really try and find the
# upstream hash from that...
uphash=`git log $upstream --pretty=format:"%H" --perl-regexp --grep="^\\Q${subj}\\E$"|tail -n 1`
if [[ -n "$uphash" ]]
then
numdiff=`diff -u <(git diff $uphash^! |egrep ^[-+])\
<(git diff $downhash^! |egrep ^[-+])\
| egrep '^[-+]' | egrep -v '^[-+]{3}' |wc -l || true`
# for contextual diff checking, we will ignore hashes and line number offsets
condiff=`diff -u <(git diff $uphash^\! |sed -e s/^@@.*@@//g |egrep -v ^index |egrep -v ^diff)\
<(git diff $downhash^\!|sed -e s/^@@.*@@//g |egrep -v ^index |egrep -v ^diff)\
| egrep '^[-+]' | egrep -v '^[-+]{3}'|wc -l || true`
f="-"
c="-"
if [[ $sensitivity -gt 1 ]]
then
showdiff=1
else
showdiff=0
fi
if [[ $condiff -ne 0 ]]
then
c=${bold}C${reset}
if [[ $sensitivity -gt 0 ]]
then
showdiff=1
fi
fi
if [[ $numdiff -ne 0 ]]
then
f=${bold}F${reset}
showdiff=1
printf "%03d/${total}:[${bold}%04d${reset}] [${f}${c}] ${bold}${color4}'${subj}'${reset}\n" $cnt $numdiff
else
printf "%03d/$total:[----] [${f}${c}] '$subj'\n" $cnt
fi
if [[ $showdiff -eq 1 ]]
then
if [[ diffprog == "meld" ]]
then
label="--label=\"#$cnt: $subj ( <-upstream, downstream-> )\""
fi
subjlist[$cnt]=$subj
exe[$cnt]="${label} <(git show $uphash^!) <(git show $downhash^!) 2>/dev/null"
shortexe[$cnt]="<(git show ${uphash:0:7}^\!) <(git show ${downhash:0:7}^\!)"
fi
else
printf "%03d/$total:[${bold}${color1}down${reset}] ${bold}${color4}'$subj'${reset}\n" $cnt
fi
done < <(git log --pretty=tformat:"%H%s" --reverse $range)
}
if [[ $upstream_valid != 'y' ]]
then
echo "Upstream $upstream is not valid (does not exist)!"
echo "Do you need to add the remote upstream repo?"
exit 2
fi >&2
compare_git
echo "Do you want to view the diffs using ${bold}${diffprog}${reset}? y/[n]: "
read -n 1 view
echo ""
if [[ "$view" == "y" ]]
then
for idx in ${!exe[*]}
do
if [[ $pause == 'y' ]]
then
echo "Press [Enter] to view diff(diff) of ${idx}/${total}: ${bold}${color4}${subjlist[$idx]}${reset}"
read
else
echo "Viewing diff(diff) of ${idx}/${total}: ${bold}${color4}${subjlist[$idx]}${reset}"
fi
eval ${diffprog} ${exe[$idx]} || true
done
fi
echo "To view diffs later, you may run:"
for idx in ${!shortexe[*]}
do
printf "%03d/$total: '${diffprog} ${shortexe[$idx]}'\n" $idx
done