here's my one-liner:
function gitfilecontributors() { local perfile="false" ; if [[ $1 = "-f" ]]; then perfile="true" ; shift ; fi ; if [[ $# -eq 0 ]]; then echo "no files given!" >&2 ; return 1 ; else local f ; { for f in "$@"; do echo "$f" ; git blame --show-email "$f" | sed -nE 's/^[^ ]* *.<([^>]*)>.*$/: \1/p' | sort | uniq -c | sort -r -nk1 ; done } | if [[ "$perfile" = "true" ]]; then tee /tmp/gitblamestats.txt ; else tee /tmp/gitblamestats.txt >/dev/null ; fi ; echo ; echo "total:" ; awk -v FS=' *: *' '/^ *[0-9]/{sums[$2] += $1} END { for (i in sums) printf("%7s : %s\n", sums[i], i)}' /tmp/gitblamestats.txt | sort -r -nk1 ; fi ; }
or with line breaks:
gitfilecontributors ()
{
local perfile="false";
if [[ $1 = "-f" ]]; then
perfile="true";
shift;
fi;
if [[ $# -eq 0 ]]; then
echo "no files given!" 1>&2;
return 1;
else
local f;
{
for f in "$@";
do
echo "$f";
git blame --show-email "$f" | sed -nE 's/^[^ ]* *.<([^>]*)>.*$/: \1/p' | sort | uniq -c | sort -r -nk1;
done
} | if [[ "$perfile" = "true" ]]; then
tee /tmp/gitblamestats.txt;
else
tee /tmp/gitblamestats.txt > /dev/null;
fi;
echo;
echo "total:";
awk -v FS=' *: *' '/^ *[0-9]/{sums[$2] += $1} END { for (i in sums) printf("%7s : %s\n", sums[i], i)}' /tmp/gitblamestats.txt | sort -r -nk1;
fi
}
usage possible four folder(s) of your choice.
option -f to show per file, otherwise totals only:
$ gitfilecontributors $(fd --type f '.*' source)
total:
139 : somebody@somewhere.de
29 : else.user@somewhere.de
9 : just.another@somewhere.de
gitfilecontributors -f $(fd --type f '.*' source)
source/040_InitialSetup.md
80 : somebody@somewhere.de
29 : else.user@somewhere.de
6 : just.another@somewhere.de
README.md
59 : somebody@somewhere.de
5 : whosthat@somewhere.de
3 : just.another@somewhere.de
total:
139 : somebody@somewhere.de
29 : else.user@somewhere.de
9 : just.another@somewhere.de
5 : whosthat@somewhere.de