File: git-guilt

package info (click to toggle)
git-extras 7.4.0-1
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 2,120 kB
  • sloc: sh: 4,312; python: 994; makefile: 146
file content (119 lines) | stat: -rwxr-xr-x 3,273 bytes parent folder | download
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"