diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 9f7cba2be6..6b9408fdd9 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -53,13 +53,15 @@ endif::git-format-patch[] Generate a diff using the "patience diff" algorithm. --stat[=[,[,]]]:: - Generate a diffstat. You can override the default - output width for 80-column terminal by `--stat=`. - The width of the filename part can be controlled by - giving another width to it separated by a comma. + Generate a diffstat. By default, as much space as necessary + will be used for the filename part, and the rest for + the graph part. Maximum width defaults to terminal width, + or 80 columns if not connected to a terminal, and can be + overriden by ``. The width of the filename part can be + limited by giving another width `` after a comma. By giving a third parameter ``, you can limit the - output to the first `` lines, followed by - `...` if there are more. + output to the first `` lines, followed by `...` if + there are more. + These parameters can also be set individually with `--stat-width=`, `--stat-name-width=` and `--stat-count=`. diff --git a/diff.c b/diff.c index 74782770bc..1656310ddc 100644 --- a/diff.c +++ b/diff.c @@ -1329,7 +1329,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) int i, len, add, del, adds = 0, dels = 0; uintmax_t max_change = 0, max_len = 0; int total_files = data->nr; - int width, name_width, count; + int width, name_width, graph_width, number_width = 4, count; const char *reset, *add_c, *del_c; const char *line_prefix = ""; int extra_shown = 0; @@ -1343,28 +1343,15 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) line_prefix = msg->buf; } - if (options->stat_width == -1) - width = term_columns(); - else - width = options->stat_width ? options->stat_width : 80; - name_width = options->stat_name_width ? options->stat_name_width : 50; count = options->stat_count ? options->stat_count : data->nr; - /* Sanity: give at least 5 columns to the graph, - * but leave at least 10 columns for the name. - */ - if (width < 25) - width = 25; - if (name_width < 10) - name_width = 10; - else if (width < name_width + 15) - name_width = width - 15; - - /* Find the longest filename and max number of changes */ reset = diff_get_color_opt(options, DIFF_RESET); add_c = diff_get_color_opt(options, DIFF_FILE_NEW); del_c = diff_get_color_opt(options, DIFF_FILE_OLD); + /* + * Find the longest filename and max number of changes + */ for (i = 0; (i < count) && (i < data->nr); i++) { struct diffstat_file *file = data->files[i]; uintmax_t change = file->added + file->deleted; @@ -1385,19 +1372,62 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) } count = i; /* min(count, data->nr) */ - /* Compute the width of the graph part; - * 10 is for one blank at the beginning of the line plus - * " | count " between the name and the graph. + /* + * We have width = stat_width or term_columns() columns total. + * We want a maximum of min(max_len, stat_name_width) for the name part. + * We also need 1 for " " and 4 + decimal_width(max_change) + * for " | NNNN " and one the empty column at the end, altogether + * 6 + decimal_width(max_change). * - * From here on, name_width is the width of the name area, - * and width is the width of the graph area. + * If there's not enough space, we will use the smaller of + * stat_name_width (if set) and 5/8*width for the filename, + * and the rest for constant elements + graph part. + * (5/8 gives 50 for filename and 30 for the constant parts + graph + * for the standard terminal size). + * + * In other words: stat_width limits the maximum width, and + * stat_name_width fixes the maximum width of the filename, + * and is also used to divide available columns if there + * aren't enough. */ - name_width = (name_width < max_len) ? name_width : max_len; - if (width < (name_width + 10) + max_change) - width = width - (name_width + 10); - else - width = max_change; + if (options->stat_width == -1) + width = term_columns(); + else + width = options->stat_width ? options->stat_width : 80; + + /* + * Guarantee 3/8*16==6 for the graph part + * and 5/8*16==10 for the filename part + */ + if (width < 16 + 6 + number_width) + width = 16 + 6 + number_width; + + /* + * First assign sizes that are wanted, ignoring available width. + */ + graph_width = max_change; + name_width = (options->stat_name_width > 0 && + options->stat_name_width < max_len) ? + options->stat_name_width : max_len; + + /* + * Adjust adjustable widths not to exceed maximum width + */ + if (name_width + number_width + 6 + graph_width > width) { + if (graph_width > width * 3/8 - number_width - 6) + graph_width = width * 3/8 - number_width - 6; + if (name_width > width - number_width - 6 - graph_width) + name_width = width - number_width - 6 - graph_width; + else + graph_width = width - number_width - 6 - name_width; + } + + /* + * From here name_width is the width of the name area, + * and graph_width is the width of the graph area. + * max_change is used to scale graph properly. + */ for (i = 0; i < count; i++) { const char *prefix = ""; char *name = data->files[i]->print_name; @@ -1453,18 +1483,18 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) adds += add; dels += del; - if (width <= max_change) { + if (graph_width <= max_change) { int total = add + del; - total = scale_linear(add + del, width, max_change); + total = scale_linear(add + del, graph_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); + add = scale_linear(add, graph_width, max_change); del = total - add; } else { - del = scale_linear(del, width, max_change); + del = scale_linear(del, graph_width, max_change); add = total - del; } } diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh index 954c16f0ac..d0ed0dc403 100755 --- a/t/t4052-stat-output.sh +++ b/t/t4052-stat-output.sh @@ -22,18 +22,18 @@ test_expect_success 'preparation' ' while read cmd args do cat >expect <<-'EOF' - ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + EOF - test_expect_success "$cmd: a short graph bar does not extend to the full width" ' + test_expect_success "$cmd: small change with long name gives more space to the name" ' git $cmd $args >output && grep " | " output >actual && test_cmp expect actual ' cat >expect <<-'EOF' - ...aaaaaaaaaaaaaaaaaaaaaa | 1 + + ...aaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + EOF - test_expect_success "$cmd --stat=width: name is chopped to leave room to the right of a short bar" ' + test_expect_success "$cmd --stat=width: a long name is given more room when the bar is short" ' git $cmd $args --stat=40 >output && grep " | " output >actual && test_cmp expect actual @@ -131,11 +131,11 @@ test_expect_success 'preparation for long filename tests' ' ' cat >expect <<'EOF' - ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++ + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++ EOF while read cmd args do - test_expect_success "$cmd --stat=width with big change and long name favors name part" ' + test_expect_success "$cmd --stat=width with big change is more balanced" ' git $cmd $args --stat-width=60 >output && grep " | " output >actual && test_cmp expect actual @@ -151,7 +151,7 @@ cat >expect80 <<'EOF' ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++ EOF cat >expect200 <<'EOF' - ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ EOF while read verb expect cmd args do @@ -178,7 +178,7 @@ test_expect_success 'merge --stat respects COLUMNS (big change)' ' ' cat >expect <<'EOF' - ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++++++++++++++++++++++ + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++ EOF test_expect_success 'merge --stat respects COLUMNS (long filename)' ' COLUMNS=100 git merge --stat --no-ff master >output &&