Blob


1 # diffstat
3 Show diff statistics.
5 #!/usr/bin/awk -f
7 AWK is great. All hail AWK!
9 Now, some utility functions. parsehdr parse extracts the number of
10 lines (old or new) in the following hunk.
12 function parsehdr(s) {
13 s = gensub(".*,", "", 1, s)
14 s = gensub("^-", "", 1, s)
15 return s + 0
16 }
18 Extracts the name of the file from a "+++ path" or "--- path" line.
20 function filename(s) {
21 s = gensub("^... ", "", 1, s)
23 These lines have an optional tab followed by extra informations (the
24 date for example) that needs to be removed too.
26 s = gensub("\t.*", "", 1, s)
27 return s
28 }
30 Switches the current file to the one provided. It's a great place where
31 accumulate part of the summary showed at the end and to reset the
32 per-file counters.
34 function switchfile(newfile) {
35 if (file != "") {
36 summary = sprintf("%s+%d -%d\t%s\n",
37 summary, add, rem, file)
38 }
40 add = 0
41 rem = 0
42 file = newfile
43 }
45 Now, the real "parser". It start in the "out" state
47 BEGIN {
48 state = "out"
49 }
51 Match the start of a diff on the "+++" line.
53 state == "out" && /^\+\+\+ / {
54 nfile = filename($0)
55 if (nfile == "/dev/null") {
57 When deleting a file, the name will be "/dev/null", but that's not a
58 great name for the stats. Let's use the "old" name instead.
60 nfile = delfile
61 }
63 switchfile(nfile)
64 delfile = ""
65 }
67 Let's save the old name in case it's needed.
69 state == "out" && /^--- / && file == "" {
70 delfile = filename($0)
71 }
73 Match the start of a hunk and switch the state to "in"
75 state == "out" && /^@@ / {
77 This part is a bit complicated, but all it does is extracting the number
78 of "new" and "old" lines showed in the hunk. A hunk header looks like this
79 (except for the initial '#' character)
81 # @@ -55,7 +55,19 @@ ...
83 So first extract the text inside the pair of "@@"
85 s = gensub("@@ ", "", 1)
86 s = gensub(" @@.*", "", 1, s)
88 and then parse each number.
90 old = gensub(" .*", "", 1, s)
91 old = parsehdr(old)
93 new = gensub(".* ", "", 1, s)
94 new = parsehdr(new)
96 Don't forget to switch the state of the parser, now we're reading a
97 hunk.
99 state = "in"
102 Keep count of the added and removed line. Also, decrement the "old" and
103 "new" lines when needed, to know when we're done with the hunk.
105 state == "in" && /^ / {
106 old--
107 new--
110 state == "in" && /^-/ {
111 old--
112 rem++
113 totrem++
116 state == "in" && /^\+/ {
117 new--
118 add++
119 totadd++
122 When there are no more "new" and "old" lines to read, go back to the
123 "out" state, ready to read another hunk or another file.
125 state == "in" && old <= 0 && new <= 0 {
126 state = "out"
129 Don't be a sink! Continue the pipeline so we can further save or apply
130 the diff.
132 // { print $0 }
134 At the end, print the stats to standard error to avoid mangling the
135 input. Unfortunately, there doesn't seem to be a "built-in" way of
136 printing to stderr other than using the pseudo-device.
138 END {
139 fflush()
140 switchfile("")
142 printf("%s", summary) > "/dev/stderr"
143 printf("+%d -%d\ttotal\n", totadd, totrem) > "/dev/stderr"
146 some example usages:
148 * cvs -q di | diffstat | tee /tmp/diff | less
149 * git diff | diffstat > /tmp/diff
150 * got di | diffstat | ssh foo 'cd xyz && got patch'