-
Notifications
You must be signed in to change notification settings - Fork 151
/
git-overwritten
executable file
·130 lines (120 loc) · 2.77 KB
/
git-overwritten
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
120
121
122
123
124
125
126
127
128
129
130
#!/bin/bash
# Usage: git overwritten [--[no-]color] [<head=HEAD>] [<base=origin>]
#
# Aggregates git blame information about original owners of lines changed or
# removed in the '<base>...<head>' diff.
#
# Each line of output represents a past commit, and consists of:
# - number of lines from the commit that this diff affects
# - commit date
# - commit sha
# - author name
# - commit subject message
#
# The commits are listed in the reverse chronological order.
#
# Author: Mislav Marohnić
set -e
unset colorize
unset head
unset base
unset diff_type
# Figure out what argument to use for date as GNU & BSD `date`s are different
[[ $(date -r123 '+%s' 2>/dev/null) -eq 123 ]] && DATEARG='-r' || DATEARG='-d@'
abort() {
"$0" --help | head -1 >&2
exit 1
}
while [ "$#" -gt 0 ]; do
case "$1" in
--color ) colorize=1 ;;
--no-color ) colorize= ;;
--staged ) diff_type='--staged' ;;
--unstaged ) diff_type= ;;
-h | --help )
sed -ne '/^#/!q;s/.\{1,2\}//;1d;p' < "$0"
exit 0
;;
-* | "") abort ;;
* )
if [ -z "$head" ]; then head="$1"
elif [ -z "$base" ]; then base="$1"
else abort
fi
;;
esac
shift 1
done
if [ "${diff_type-no}" = "no" ]; then
head="${head:-HEAD}"
base="${base:-origin}"
else
head=
base=HEAD
fi
[ -t 1 ] && colorize="${colorize-1}"
color() {
if [ -n "$colorize" ]; then
printf "\e[0;%dm%s\e[m" "$1" "$2"
else
echo -n "$2"
fi
}
git diff ${diff_type-"${base}...${head}"} --diff-filter=DM --no-prefix -w -U0 | grep -v '^+' | awk '
function print_range() {
printf "-L %d,%d -- %s\n", start, stop, file
}
/^--- / {
sub(/^--- /, "")
file = $0
next
}
/^-/ {
if (!start) start = stop = diffstart
else stop += 1
next
}
start {
print_range()
start = 0
}
/^@@ / {
sub(/^-/, "", $2)
diffstart = int($2)
start = 0
}
END {
if (start) print_range()
}
' | xargs -L1 git blame --line-porcelain "$base" | awk -v OFS=$'\t' '
BEGIN { num_lines = name_length = 0 }
function val() { sub(/^[a-z-]+ /, ""); return $0 }
/^[0-9a-f]{40} / { sha = $1 }
/^author / {
author = val()
len = length(author)
if (len > name_length) name_length = len
}
/^committer-time / { time = val() }
/^summary / { msg = val() }
/^\t/ {
print time, sha, author, msg
num_lines += 1
}
END { print num_lines, name_length }
' | sort -n | {
read num_lines name_length
if [ "$num_lines" -eq 0 ]; then
color 31 "Warning: " >&2
echo "no changed/removed lines found in the $base...$head diff" >&2
fi
while IFS=$'\t' read time sha author msg; do
date "${DATEARG}${time}" '+%F' | tr -d $'\n'
printf ' '
color 33 "${sha:0:7}"
printf ' '
color 32 "$(printf "%${name_length}s" "$author")"
printf ': '
echo "$msg"
done
} | sort -r | uniq -c