diff --stat: use a maximum of 5/8 for the filename part

The way that available columns are divided between the filename part and the graph part is modified to use as many columns as necessary for the filenames and the rest for the graph. If there isn't enough columns to print both the filename and the graph, at least 5/8 of available space is devoted to filenames. On a standard 80 column terminal, or if not connected to a terminal and using the default of 80 columns, this gives the same partition as before. The effect of this change is visible in the patch to the test vector in t4052; with a small change with long filename, it stops truncating the name part too short, and also allocates a bit more columns to the graph for larger changes. Signed-off-by: Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Zbigniew Jędrzejewski-Szmek committed Mar 1, 2012 at 13:26 UTC 1b058bc30df5f79fe7449cacd8ae7128944327f7
3 files changed +76 -44
Documentation/diff-options.txt
+8 -6
@@ -53,13 +53,15 @@ endif::git-format-patch[]
53 Generate a diff using the "patience diff" algorithm.
54
55 --stat[=<width>[,<name-width>[,<count>]]]::
56 - Generate a diffstat. You can override the default
57 - output width for 80-column terminal by `--stat=<width>`.
58 - The width of the filename part can be controlled by
59 - giving another width to it separated by a comma.
56 + Generate a diffstat. By default, as much space as necessary
57 + will be used for the filename part, and the rest for
58 + the graph part. Maximum width defaults to terminal width,
59 + or 80 columns if not connected to a terminal, and can be
60 + overriden by `<width>`. The width of the filename part can be
61 + limited by giving another width `<name-width>` after a comma.
62 By giving a third parameter `<count>`, you can limit the
61 - output to the first `<count>` lines, followed by
62 - `...` if there are more.
63 + output to the first `<count>` lines, followed by `...` if
64 + there are more.
65 +
66 These parameters can also be set individually with `--stat-width=<width>`,
67 `--stat-name-width=<name-width>` and `--stat-count=<count>`.
diff.c
+60 -30
@@ -1329,7 +1329,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
1329 int i, len, add, del, adds = 0, dels = 0;
1330 uintmax_t max_change = 0, max_len = 0;
1331 int total_files = data->nr;
1332 - int width, name_width, count;
1332 + int width, name_width, graph_width, number_width = 4, count;
1333 const char *reset, *add_c, *del_c;
1334 const char *line_prefix = "";
1335 int extra_shown = 0;
@@ -1343,28 +1343,15 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
1343 line_prefix = msg->buf;
1344 }
1345
1346 - if (options->stat_width == -1)
1347 - width = term_columns();
1348 - else
1349 - width = options->stat_width ? options->stat_width : 80;
1350 - name_width = options->stat_name_width ? options->stat_name_width : 50;
1346 count = options->stat_count ? options->stat_count : data->nr;
1347
1353 - /* Sanity: give at least 5 columns to the graph,
1354 - * but leave at least 10 columns for the name.
1355 - */
1356 - if (width < 25)
1357 - width = 25;
1358 - if (name_width < 10)
1359 - name_width = 10;
1360 - else if (width < name_width + 15)
1361 - name_width = width - 15;
1362 -
1363 - /* Find the longest filename and max number of changes */
1348 reset = diff_get_color_opt(options, DIFF_RESET);
1349 add_c = diff_get_color_opt(options, DIFF_FILE_NEW);
1350 del_c = diff_get_color_opt(options, DIFF_FILE_OLD);
1351
1352 + /*
1353 + * Find the longest filename and max number of changes
1354 + */
1355 for (i = 0; (i < count) && (i < data->nr); i++) {
1356 struct diffstat_file *file = data->files[i];
1357 uintmax_t change = file->added + file->deleted;
@@ -1385,19 +1372,62 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
1372 }
1373 count = i; /* min(count, data->nr) */
1374
1388 - /* Compute the width of the graph part;
1389 - * 10 is for one blank at the beginning of the line plus
1390 - * " | count " between the name and the graph.
1375 + /*
1376 + * We have width = stat_width or term_columns() columns total.
1377 + * We want a maximum of min(max_len, stat_name_width) for the name part.
1378 + * We also need 1 for " " and 4 + decimal_width(max_change)
1379 + * for " | NNNN " and one the empty column at the end, altogether
1380 + * 6 + decimal_width(max_change).
1381 + *
1382 + * If there's not enough space, we will use the smaller of
1383 + * stat_name_width (if set) and 5/8*width for the filename,
1384 + * and the rest for constant elements + graph part.
1385 + * (5/8 gives 50 for filename and 30 for the constant parts + graph
1386 + * for the standard terminal size).
1387 *
1392 - * From here on, name_width is the width of the name area,
1393 - * and width is the width of the graph area.
1388 + * In other words: stat_width limits the maximum width, and
1389 + * stat_name_width fixes the maximum width of the filename,
1390 + * and is also used to divide available columns if there
1391 + * aren't enough.
1392 */
1395 - name_width = (name_width < max_len) ? name_width : max_len;
1396 - if (width < (name_width + 10) + max_change)
1397 - width = width - (name_width + 10);
1393 +
1394 + if (options->stat_width == -1)
1395 + width = term_columns();
1396 else
1399 - width = max_change;
1397 + width = options->stat_width ? options->stat_width : 80;
1398
1399 + /*
1400 + * Guarantee 3/8*16==6 for the graph part
1401 + * and 5/8*16==10 for the filename part
1402 + */
1403 + if (width < 16 + 6 + number_width)
1404 + width = 16 + 6 + number_width;
1405 +
1406 + /*
1407 + * First assign sizes that are wanted, ignoring available width.
1408 + */
1409 + graph_width = max_change;
1410 + name_width = (options->stat_name_width > 0 &&
1411 + options->stat_name_width < max_len) ?
1412 + options->stat_name_width : max_len;
1413 +
1414 + /*
1415 + * Adjust adjustable widths not to exceed maximum width
1416 + */
1417 + if (name_width + number_width + 6 + graph_width > width) {
1418 + if (graph_width > width * 3/8 - number_width - 6)
1419 + graph_width = width * 3/8 - number_width - 6;
1420 + if (name_width > width - number_width - 6 - graph_width)
1421 + name_width = width - number_width - 6 - graph_width;
1422 + else
1423 + graph_width = width - number_width - 6 - name_width;
1424 + }
1425 +
1426 + /*
1427 + * From here name_width is the width of the name area,
1428 + * and graph_width is the width of the graph area.
1429 + * max_change is used to scale graph properly.
1430 + */
1431 for (i = 0; i < count; i++) {
1432 const char *prefix = "";
1433 char *name = data->files[i]->print_name;
@@ -1453,18 +1483,18 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
1483 adds += add;
1484 dels += del;
1485
1456 - if (width <= max_change) {
1486 + if (graph_width <= max_change) {
1487 int total = add + del;
1488
1459 - total = scale_linear(add + del, width, max_change);
1489 + total = scale_linear(add + del, graph_width, max_change);
1490 if (total < 2 && add && del)
1491 /* width >= 2 due to the sanity check */
1492 total = 2;
1493 if (add < del) {
1464 - add = scale_linear(add, width, max_change);
1494 + add = scale_linear(add, graph_width, max_change);
1495 del = total - add;
1496 } else {
1467 - del = scale_linear(del, width, max_change);
1497 + del = scale_linear(del, graph_width, max_change);
1498 add = total - del;
1499 }
1500 }
t/t4052-stat-output.sh
+8 -8
@@ -22,18 +22,18 @@ test_expect_success 'preparation' '
22 while read cmd args
23 do
24 cat >expect <<-'EOF'
25 - ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
25 + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
26 EOF
27 - test_expect_success "$cmd: a short graph bar does not extend to the full width" '
27 + test_expect_success "$cmd: small change with long name gives more space to the name" '
28 git $cmd $args >output &&
29 grep " | " output >actual &&
30 test_cmp expect actual
31 '
32
33 cat >expect <<-'EOF'
34 - ...aaaaaaaaaaaaaaaaaaaaaa | 1 +
34 + ...aaaaaaaaaaaaaaaaaaaaaaaaaa | 1 +
35 EOF
36 - test_expect_success "$cmd --stat=width: name is chopped to leave room to the right of a short bar" '
36 + test_expect_success "$cmd --stat=width: a long name is given more room when the bar is short" '
37 git $cmd $args --stat=40 >output &&
38 grep " | " output >actual &&
39 test_cmp expect actual
@@ -131,11 +131,11 @@ test_expect_success 'preparation for long filename tests' '
131 '
132
133 cat >expect <<'EOF'
134 - ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++
134 + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++
135 EOF
136 while read cmd args
137 do
138 - test_expect_success "$cmd --stat=width with big change and long name favors name part" '
138 + test_expect_success "$cmd --stat=width with big change is more balanced" '
139 git $cmd $args --stat-width=60 >output &&
140 grep " | " output >actual &&
141 test_cmp expect actual
@@ -151,7 +151,7 @@ cat >expect80 <<'EOF'
151 ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++
152 EOF
153 cat >expect200 <<'EOF'
154 - ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
154 + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
155 EOF
156 while read verb expect cmd args
157 do
@@ -178,7 +178,7 @@ test_expect_success 'merge --stat respects COLUMNS (big change)' '
178 '
179
180 cat >expect <<'EOF'
181 - ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++++++++++++++++++++++
181 + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++
182 EOF
183 test_expect_success 'merge --stat respects COLUMNS (long filename)' '
184 COLUMNS=100 git merge --stat --no-ff master >output &&