1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
|
#!/usr/bin/env bash
for param in "$@"
do
case $param in
-h)
echo 'Usage: git-guilt [<options>] <since> <until>'
echo 'Calculates the change in blame between two revisions'
echo 'Example: git guilt HEAD~3 HEAD'
echo
echo 'Options:'
echo
echo ' -h, --help output usage information'
echo ' -e, --email display author emails instead of names'
echo ' -w, --ignore-whitespace ignore whitespace only changes when attributing blame'
echo ' -d, --debug output debug information'
exit 0
;;
-e|--email )
EMAIL='-e'
shift
;;
-w|--ignore-whitespace )
NOT_WHITESPACE='-w'
shift
;;
-d|--debug )
DEBUG=$(git_extra_mktemp)
shift
;;
esac
done
cd "$(git-root)" || exit # cd for git blame
MERGED_LOG=$(git_extra_mktemp)
if [[ $EMAIL == '-e' ]]
then
PATTERN='s/^author-mail <\(.*\)>/\1/p'
else
PATTERN='s/^author //p'
fi
for file in $(git diff --name-only "$@")
do
test -n "$DEBUG" && echo "git blame $file"
# $1 - since $2 - until
# shellcheck disable=SC2086
git blame $NOT_WHITESPACE --line-porcelain "$1" -- "$file" 2> /dev/null |
LC_ALL=C sed -n "$PATTERN" | sort | uniq -c | LC_ALL=C sed 's/^\(.\)/- \1/' >> "$MERGED_LOG"
# if $2 not given, use current commit as "until"
# shellcheck disable=SC2086
git blame $NOT_WHITESPACE --line-porcelain "${2-@}" -- "$file" 2> /dev/null |
LC_ALL=C sed -n "$PATTERN" | sort | uniq -c | LC_ALL=C sed 's/^\(.\)/+ \1/' >> "$MERGED_LOG"
done
DEBUG="$DEBUG" awk '
NR==1 {
# the index of $2 does not change in each line
name_start_at = index($0, $3)
}
/^\+/ {
contributors[substr($0, name_start_at)] += $2
}
/^-/ {
contributors[substr($0, name_start_at)] -= $2
}
END {
for (people in contributors) {
if (ENVIRON["DEBUG"]) {
printf("%d %s\n", contributors[people], people) >> ENVIRON["DEBUG"]
}
if (contributors[people] != 0) {
printf("%d %s\n", contributors[people], people)
}
}
}' "$MERGED_LOG" | sort -nr | # only gawk supports built-in sort function
while read -r line
do
people=${line#* }
num=${line%% *}
if [[ $num -gt 0 ]]
then
printf "%-29s \033[00;32m" "$people"
if [[ $num -ge 50 ]]
then
len=${#num}
for (( i = 0; i < 48 - len; i++ ))
do
printf "+"
done
printf "(%s)" "$num"
else
for (( i = 0; i < num; i++ ))
do
printf "+"
done
fi
else
printf "%-29s \033[00;31m" "$people"
if [[ $num -le -50 ]]
then
len=${#num}
for (( i = 0; i < 48 - len; i++ ))
do
printf "-"
done
printf "(%s)" "$num"
else
for (( i = 0; i > num; i-- ))
do
printf "-"
done
fi
fi
printf "\033[00m\n"
done
test -n "$DEBUG" && sort -nr "$DEBUG"
|