From a2540023dcf833ca02d87c28052e8b7135d56b60 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 26 Sep 2006 18:53:02 -0700 Subject: diff --stat: allow custom diffstat output width. This adds two parameters to "diff --stat". . --stat-width=72 tells that the page should fit on 72-column output. . --stat-name-width=30 tells that the filename part is limited to 30 columns. Signed-off-by: Junio C Hamano --- diff.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++------------------- diff.h | 3 ++ 2 files changed, 96 insertions(+), 38 deletions(-) diff --git a/diff.c b/diff.c index 443e24861..8d299f476 100644 --- a/diff.c +++ b/diff.c @@ -545,21 +545,64 @@ static void diffstat_consume(void *priv, char *line, unsigned long len) x->deleted++; } -static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; -static const char minuses[]= "----------------------------------------------------------------------"; const char mime_boundary_leader[] = "------------"; -static void show_stats(struct diffstat_t* data) +static int scale_linear(int it, int width, int max_change) +{ + /* + * round(width * it / max_change); + */ + return (it * width * 2 + max_change) / (max_change * 2); +} + +static void show_name(const char *prefix, const char *name, int len) +{ + printf(" %s%-*s |", prefix, len, name); +} + +static void show_graph(char ch, int cnt) +{ + if (cnt <= 0) + return; + while (cnt--) + putchar(ch); +} + +static void show_stats(struct diffstat_t* data, struct diff_options *options) { int i, len, add, del, total, adds = 0, dels = 0; - int max, max_change = 0, max_len = 0; + int max_change = 0, max_len = 0; int total_files = data->nr; + int width, name_width; if (data->nr == 0) return; + width = options->stat_width ? options->stat_width : 80; + name_width = options->stat_name_width ? options->stat_name_width : 50; + + /* Sanity: give at least 5 columns to the graph, + * but leave at least 10 columns for the name. + */ + if (width < name_width + 15) { + if (name_width <= 25) + width = name_width + 15; + else + name_width = width - 15; + } + + /* Find the longest filename and max number of changes */ for (i = 0; i < data->nr; i++) { struct diffstat_file *file = data->files[i]; + int change = file->added + file->deleted; + + len = quote_c_style(file->name, NULL, NULL, 0); + if (len) { + char *qname = xmalloc(len + 1); + quote_c_style(file->name, qname, NULL, 0); + free(file->name); + file->name = qname; + } len = strlen(file->name); if (max_len < len) @@ -567,54 +610,53 @@ static void show_stats(struct diffstat_t* data) if (file->is_binary || file->is_unmerged) continue; - if (max_change < file->added + file->deleted) - max_change = file->added + file->deleted; + if (max_change < change) + max_change = change; } + /* 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. + * + * From here on, name_width is the width of the name area, + * and width is the width of the graph area. + */ + 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; + for (i = 0; i < data->nr; i++) { const char *prefix = ""; char *name = data->files[i]->name; int added = data->files[i]->added; int deleted = data->files[i]->deleted; - - if (0 < (len = quote_c_style(name, NULL, NULL, 0))) { - char *qname = xmalloc(len + 1); - quote_c_style(name, qname, NULL, 0); - free(name); - data->files[i]->name = name = qname; - } + int name_len; /* * "scale" the filename */ - len = strlen(name); - max = max_len; - if (max > 50) - max = 50; - if (len > max) { + len = name_width; + name_len = strlen(name); + if (name_width < name_len) { char *slash; prefix = "..."; - max -= 3; - name += len - max; + len -= 3; + name += name_len - len; slash = strchr(name, '/'); if (slash) name = slash; } - len = max; - - /* - * scale the add/delete - */ - max = max_change; - if (max + len > 70) - max = 70 - len; if (data->files[i]->is_binary) { - printf(" %s%-*s | Bin\n", prefix, len, name); + show_name(prefix, name, len); + printf(" Bin\n"); goto free_diffstat_file; } else if (data->files[i]->is_unmerged) { - printf(" %s%-*s | Unmerged\n", prefix, len, name); + show_name(prefix, name, len); + printf(" Unmerged\n"); goto free_diffstat_file; } else if (!data->files[i]->is_renamed && @@ -623,27 +665,32 @@ static void show_stats(struct diffstat_t* data) goto free_diffstat_file; } + /* + * scale the add/delete + */ add = added; del = deleted; total = add + del; adds += add; dels += del; - if (max_change > 0) { - total = (total * max + max_change / 2) / max_change; - add = (add * max + max_change / 2) / max_change; + if (width <= max_change) { + total = scale_linear(total, width, max_change); + add = scale_linear(add, width, max_change); del = total - add; } - printf(" %s%-*s |%5d %.*s%.*s\n", prefix, - len, name, added + deleted, - add, pluses, del, minuses); + show_name(prefix, name, len); + printf("%5d ", added + deleted); + show_graph('+', add); + show_graph('-', del); + putchar('\n'); free_diffstat_file: free(data->files[i]->name); free(data->files[i]); } free(data->files); printf(" %d files changed, %d insertions(+), %d deletions(-)\n", - total_files, adds, dels); + total_files, adds, dels); } struct checkdiff_t { @@ -1681,6 +1728,14 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) } else if (!strcmp(arg, "--stat")) options->output_format |= DIFF_FORMAT_DIFFSTAT; + else if (!strncmp(arg, "--stat-width=", 13)) { + options->stat_width = strtoul(arg + 13, NULL, 10); + options->output_format |= DIFF_FORMAT_DIFFSTAT; + } + else if (!strncmp(arg, "--stat-name-width=", 18)) { + options->stat_name_width = strtoul(arg + 18, NULL, 10); + options->output_format |= DIFF_FORMAT_DIFFSTAT; + } else if (!strcmp(arg, "--check")) options->output_format |= DIFF_FORMAT_CHECKDIFF; else if (!strcmp(arg, "--summary")) @@ -2438,7 +2493,7 @@ void diff_flush(struct diff_options *options) if (check_pair_status(p)) diff_flush_stat(p, options, &diffstat); } - show_stats(&diffstat); + show_stats(&diffstat, options); separator++; } diff --git a/diff.h b/diff.h index b60a02e62..e06d0f418 100644 --- a/diff.h +++ b/diff.h @@ -69,6 +69,9 @@ struct diff_options { const char *stat_sep; long xdl_opts; + int stat_width; + int stat_name_width; + int nr_paths; const char **paths; int *pathlens; -- cgit v1.2.1 From 785f743276991567a36420f069c228e4dc9d0df3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 26 Sep 2006 18:59:41 -0700 Subject: diff --stat: color output. Under --color option, diffstat shows '+' and '-' in the graph the same color as added and deleted lines. Signed-off-by: Junio C Hamano --- diff.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/diff.c b/diff.c index 8d299f476..3fd7a5220 100644 --- a/diff.c +++ b/diff.c @@ -555,17 +555,20 @@ static int scale_linear(int it, int width, int max_change) return (it * width * 2 + max_change) / (max_change * 2); } -static void show_name(const char *prefix, const char *name, int len) +static void show_name(const char *prefix, const char *name, int len, + const char *reset, const char *set) { - printf(" %s%-*s |", prefix, len, name); + printf(" %s%s%-*s%s |", set, prefix, len, name, reset); } -static void show_graph(char ch, int cnt) +static void show_graph(char ch, int cnt, const char *set, const char *reset) { if (cnt <= 0) return; + printf("%s", set); while (cnt--) putchar(ch); + printf("%s", reset); } static void show_stats(struct diffstat_t* data, struct diff_options *options) @@ -574,6 +577,7 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) int max_change = 0, max_len = 0; int total_files = data->nr; int width, name_width; + const char *reset, *set, *add_c, *del_c; if (data->nr == 0) return; @@ -592,6 +596,11 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) } /* Find the longest filename and max number of changes */ + reset = diff_get_color(options->color_diff, DIFF_RESET); + set = diff_get_color(options->color_diff, DIFF_PLAIN); + add_c = diff_get_color(options->color_diff, DIFF_FILE_NEW); + del_c = diff_get_color(options->color_diff, DIFF_FILE_OLD); + for (i = 0; i < data->nr; i++) { struct diffstat_file *file = data->files[i]; int change = file->added + file->deleted; @@ -650,12 +659,12 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) } if (data->files[i]->is_binary) { - show_name(prefix, name, len); + show_name(prefix, name, len, reset, set); printf(" Bin\n"); goto free_diffstat_file; } else if (data->files[i]->is_unmerged) { - show_name(prefix, name, len); + show_name(prefix, name, len, reset, set); printf(" Unmerged\n"); goto free_diffstat_file; } @@ -679,18 +688,18 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) add = scale_linear(add, width, max_change); del = total - add; } - show_name(prefix, name, len); + show_name(prefix, name, len, reset, set); printf("%5d ", added + deleted); - show_graph('+', add); - show_graph('-', del); + show_graph('+', add, add_c, reset); + show_graph('-', del, del_c, reset); putchar('\n'); free_diffstat_file: free(data->files[i]->name); free(data->files[i]); } free(data->files); - printf(" %d files changed, %d insertions(+), %d deletions(-)\n", - total_files, adds, dels); + printf("%s %d files changed, %d insertions(+), %d deletions(-)%s\n", + set, total_files, adds, dels, reset); } struct checkdiff_t { -- cgit v1.2.1 From 5c5b2ea9ab95f71ac155f12d75d1432b5c93c2bb Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 28 Sep 2006 15:07:16 -0700 Subject: diff --stat=width[,name-width]: allow custom diffstat output width. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- Documentation/diff-options.txt | 7 +++++-- diff.c | 33 +++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index b5d976359..7b7b9e8ce 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -10,8 +10,11 @@ --patch-with-raw:: Synonym for "-p --raw". ---stat:: - Generate a diffstat. +--stat[=width[,name-width]]:: + Generate a diffstat. You can override the default + output width for 80-column terminal by "--stat=width". + The width of the filename part can be controlled by + giving another width to it separated by a comma. --summary:: Output a condensed summary of extended header information diff --git a/diff.c b/diff.c index 3fd7a5220..90e084410 100644 --- a/diff.c +++ b/diff.c @@ -1735,15 +1735,32 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--patch-with-raw")) { options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW; } - else if (!strcmp(arg, "--stat")) - options->output_format |= DIFF_FORMAT_DIFFSTAT; - else if (!strncmp(arg, "--stat-width=", 13)) { - options->stat_width = strtoul(arg + 13, NULL, 10); - options->output_format |= DIFF_FORMAT_DIFFSTAT; - } - else if (!strncmp(arg, "--stat-name-width=", 18)) { - options->stat_name_width = strtoul(arg + 18, NULL, 10); + else if (!strncmp(arg, "--stat", 6)) { + char *end; + int width = options->stat_width; + int name_width = options->stat_name_width; + arg += 6; + end = (char *)arg; + + switch (*arg) { + case '-': + if (!strncmp(arg, "-width=", 7)) + width = strtoul(arg + 7, &end, 10); + else if (!strncmp(arg, "-name-width=", 12)) + name_width = strtoul(arg + 12, &end, 10); + break; + case '=': + width = strtoul(arg+1, &end, 10); + if (*end == ',') + name_width = strtoul(end+1, &end, 10); + } + + /* Important! This checks all the error cases! */ + if (*end) + return 0; options->output_format |= DIFF_FORMAT_DIFFSTAT; + options->stat_name_width = name_width; + options->stat_width = width; } else if (!strcmp(arg, "--check")) options->output_format |= DIFF_FORMAT_CHECKDIFF; -- cgit v1.2.1 From 3ed74e608a69ce0f10bfad2e4ef6cf99eec04613 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 28 Sep 2006 17:37:39 +0200 Subject: diff --stat: ensure at least one '-' for deletions, and one '+' for additions The number of '-' and '+' is still linear. The idea is that scaled-length := floor(a * length + b) with the following constraints: if length == 1, scaled-length == 1, and the combined length of plusses and minusses should not be larger than the width by a small margin. Thus, a + b == 1 and a * max_plusses + b + a * max_minusses + b = width + 1 The solution is a * x + b = ((width - 1) * (x - 1) + max_change - 1) / (max_change - 1) Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- diff.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/diff.c b/diff.c index 90e084410..2df085f3c 100644 --- a/diff.c +++ b/diff.c @@ -550,9 +550,12 @@ const char mime_boundary_leader[] = "------------"; static int scale_linear(int it, int width, int max_change) { /* - * round(width * it / max_change); + * make sure that at least one '-' is printed if there were deletions, + * and likewise for '+'. */ - return (it * width * 2 + max_change) / (max_change * 2); + if (max_change < 2) + return it; + return ((it - 1) * (width - 1) + max_change - 1) / (max_change - 1); } static void show_name(const char *prefix, const char *name, int len, @@ -684,9 +687,9 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options) dels += del; if (width <= max_change) { - total = scale_linear(total, width, max_change); add = scale_linear(add, width, max_change); - del = total - add; + del = scale_linear(del, width, max_change); + total = add + del; } show_name(prefix, name, len, reset, set); printf("%5d ", added + deleted); -- cgit v1.2.1