diff --stat: ensure at least one '-' for deletions, and one '+' for additions
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index 8d299f4766eb97a19390e5ea3dde6f1c3e115a97..2df085f3c67afd96b5fcea5ade6f45c5e330198b 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -550,22 +550,28 @@ 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)
+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 +580,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 +599,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 +662,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;
                }
@@ -675,22 +687,22 @@ 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);
+               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 {
@@ -1726,15 +1738,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;