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