diff --stat: show bars of same length for paths with same amount of changes
authorJunio C Hamano <gitster@pobox.com>
Tue, 14 Feb 2012 21:49:11 +0000 (13:49 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 14 Feb 2012 22:21:49 +0000 (14:21 -0800)
When commit 3ed74e6 (diff --stat: ensure at least one '-' for deletions,
and one '+' for additions, 2006-09-28) improved the output for files with
tiny modifications, we accidentally broke the logic to ensure that two
equal sized changes are shown with the bars of the same length, even when
rounding errors exist.

Compute the length of the graph bars, using the same "non-zero changes is
shown with at least one column" scaling logic, but by scaling the sum of
additions and deletions to come up with the total length of the bar (this
ensures that two equal sized changes result in bars of the same length),
and then scaling the smaller of the additions or deletions. The other side
is computed as the difference between the two.

This makes the apportioning between additions and deletions less accurate
due to rounding errors, but it is much less noticeable than two files with
the same amount of change showing bars of different length.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff.c

diff --git a/diff.c b/diff.c
index 374ecf3b486e218c52f94ba14481a715c19b80db..74db18846f3149822bb1cd9b988feb953cc309dd 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1267,13 +1267,15 @@ const char mime_boundary_leader[] = "------------";
 
 static int scale_linear(int it, int width, int max_change)
 {
+       if (!it)
+               return 0;
        /*
-        * make sure that at least one '-' is printed if there were deletions,
-        * and likewise for '+'.
+        * make sure that at least one '-' or '+' is printed if
+        * there is any change to this path. The easiest way is to
+        * scale linearly as if the alloted width is one column shorter
+        * than it is, and then add 1 to the result.
         */
-       if (max_change < 2)
-               return it;
-       return ((it - 1) * (width - 1) + max_change - 1) / (max_change - 1);
+       return 1 + (it * (width - 1) / max_change);
 }
 
 static void show_name(FILE *file,
@@ -1440,8 +1442,19 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                dels += del;
 
                if (width <= max_change) {
-                       add = scale_linear(add, width, max_change);
-                       del = scale_linear(del, width, max_change);
+                       int total = add + del;
+
+                       total = scale_linear(add + del, width, max_change);
+                       if (total < 2 && add && del)
+                               /* width >= 2 due to the sanity check */
+                               total = 2;
+                       if (add < del) {
+                               add = scale_linear(add, width, max_change);
+                               del = total - add;
+                       } else {
+                               del = scale_linear(del, width, max_change);
+                               add = total - del;
+                       }
                }
                fprintf(options->file, "%s", line_prefix);
                show_name(options->file, prefix, name, len);