What's cooking (2021/06 #06)
[git] / Linus
1 #!/bin/sh
2 # How much of the very original version from Linus survive?
3
4 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
5 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
6
7 summary=
8 while case $# in 0) break ;; esac
9 do
10         case "$1" in
11         -s | --summary)
12                 summary=t
13                 ;;
14         -*)
15                 echo >&2 "$1: unknown option"
16                 exit 1
17                 ;;
18         *)
19                 break
20                 ;;
21         esac
22         shift
23 done
24
25 if test $# = 0
26 then
27         this=HEAD
28 else
29         this=$1
30         shift
31 fi
32
33 if test $# = 0
34 then
35         initial=e83c5163316f89bfbde7d9ab23ca2e25604af290
36         range="..$this"
37 else
38         initial=$1
39         range="$initial..$this"
40         shift
41 fi
42
43 this=$(git rev-parse --verify "$this^0") &&
44 initial=$(git rev-parse --verify "$initial^0") || exit
45
46 tmp="/var/tmp/Linus.$$"
47 trap 'rm -f "$tmp".*' 0
48
49 # We blame each file in the initial revision pretending as if it is a
50 # direct descendant of the given version, and also pretend that the
51 # latter is a root commit.  This way, lines in the initial revision
52 # that survived to the other version can be identified (they will be
53 # attributed to the other version).
54 graft="$tmp.graft" &&
55 {
56         echo "$initial $this"
57         echo "$this"
58 } >"$graft" || exit
59
60 opts='-C -C -C -w'
61
62 show () {
63         s=$1 t=$2 n=$3
64         p=$(($s * 100 / $t))
65         c=$(($s * 10000 / $t - $p * 100))
66         printf "%12d %12d %s (%d.%02d%%)\n" $s $t $n $p $c
67 }
68
69 empty_tree=$(git hash-object -t tree -w --stdin </dev/null)
70
71 git diff-tree -r --raw $empty_tree $initial -- "$@" |
72 while read mode_old mode_new sha1_old sha1_new op name
73 do
74         git blame $opts --porcelain -S "$graft" "$this..$initial" -- "$name" |
75         sed -ne "s/^\($_x40\) .*/\1/p" |
76         sort |
77         uniq -c | {
78                 # There are only two commits in the fake history, so
79                 # there will be at most two output from the above.
80                 read cnt1 commit1
81                 read cnt2 commit2
82                 if test -z "$commit2"
83                 then
84                         cnt2=0
85                 fi
86                 if test "$initial" != "$commit1"
87                 then
88                         cnt_surviving=$cnt1
89                 else
90                         cnt_surviving=$cnt2
91                 fi
92                 cnt_total=$(( $cnt1 + $cnt2 ))
93                 echo "$cnt_surviving $cnt_total $name"
94         }
95 done | {
96         total=0
97         surviving=0
98         test -n "$summary" ||
99         printf "%12s %12s %s (survival%%)\n" surviving original path
100         while read s t n
101         do
102                 total=$(( $total + $t )) surviving=$(( $surviving + $s ))
103                 test -n "$summary" ||
104                 show $s $t $n
105         done
106         if test -n "$summary"
107         then
108                 echo $surviving $total
109         else
110                 label=Total
111                 show $surviving $total $label
112         fi
113
114 }