diff: Introduce --diff-algorithm command line option
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index cb1fd9afcc2ece8c5a4123a451a0cb091baae1e3..0724fa6ff6c4312733e520d59f88ea12740d408d 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -36,6 +36,7 @@ static int diff_no_prefix;
 static int diff_stat_graph_width;
 static int diff_dirstat_permille_default = 30;
 static struct diff_options default_diff_options;
+static long diff_algorithm;
 
 static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
@@ -143,6 +144,21 @@ static int git_config_rename(const char *var, const char *value)
        return git_config_bool(var,value) ? DIFF_DETECT_RENAME : 0;
 }
 
+long parse_algorithm_value(const char *value)
+{
+       if (!value)
+               return -1;
+       else if (!strcasecmp(value, "myers") || !strcasecmp(value, "default"))
+               return 0;
+       else if (!strcasecmp(value, "minimal"))
+               return XDF_NEED_MINIMAL;
+       else if (!strcasecmp(value, "patience"))
+               return XDF_PATIENCE_DIFF;
+       else if (!strcasecmp(value, "histogram"))
+               return XDF_HISTOGRAM_DIFF;
+       return -1;
+}
+
 /*
  * These are to give UI layer defaults.
  * The core-level commands such as git-diff-files should
@@ -196,6 +212,13 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp(var, "diff.algorithm")) {
+               diff_algorithm = parse_algorithm_value(value);
+               if (diff_algorithm < 0)
+                       return -1;
+               return 0;
+       }
+
        if (git_color_config(var, value, cb) < 0)
                return -1;
 
@@ -1497,8 +1520,8 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
        for (i = 0; (i < count) && (i < data->nr); i++) {
                struct diffstat_file *file = data->files[i];
                uintmax_t change = file->added + file->deleted;
-               if (!data->files[i]->is_interesting &&
-                        (change == 0)) {
+
+               if (!file->is_interesting && (change == 0)) {
                        count++; /* not shown == room for one more */
                        continue;
                }
@@ -1525,7 +1548,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                if (max_change < change)
                        max_change = change;
        }
-       count = i; /* min(count, data->nr) */
+       count = i; /* where we can stop scanning in data->files[] */
 
        /*
         * We have width = stat_width or term_columns() columns total.
@@ -1613,16 +1636,15 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
         */
        for (i = 0; i < count; i++) {
                const char *prefix = "";
-               char *name = data->files[i]->print_name;
-               uintmax_t added = data->files[i]->added;
-               uintmax_t deleted = data->files[i]->deleted;
+               struct diffstat_file *file = data->files[i];
+               char *name = file->print_name;
+               uintmax_t added = file->added;
+               uintmax_t deleted = file->deleted;
                int name_len;
 
-               if (!data->files[i]->is_interesting &&
-                        (added + deleted == 0)) {
-                       total_files--;
+               if (!file->is_interesting && (added + deleted == 0))
                        continue;
-               }
+
                /*
                 * "scale" the filename
                 */
@@ -1638,7 +1660,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                                name = slash;
                }
 
-               if (data->files[i]->is_binary) {
+               if (file->is_binary) {
                        fprintf(options->file, "%s", line_prefix);
                        show_name(options->file, prefix, name, len);
                        fprintf(options->file, " %*s", number_width, "Bin");
@@ -1655,7 +1677,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                        fprintf(options->file, "\n");
                        continue;
                }
-               else if (data->files[i]->is_unmerged) {
+               else if (file->is_unmerged) {
                        fprintf(options->file, "%s", line_prefix);
                        show_name(options->file, prefix, name, len);
                        fprintf(options->file, " Unmerged\n");
@@ -1667,8 +1689,6 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                 */
                add = added;
                del = deleted;
-               adds += add;
-               dels += del;
 
                if (graph_width <= max_change) {
                        int total = add + del;
@@ -1694,16 +1714,24 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
                show_graph(options->file, '-', del, del_c, reset);
                fprintf(options->file, "\n");
        }
-       for (i = count; i < data->nr; i++) {
-               uintmax_t added = data->files[i]->added;
-               uintmax_t deleted = data->files[i]->deleted;
-               if (!data->files[i]->is_interesting &&
-                        (added + deleted == 0)) {
+
+       for (i = 0; i < data->nr; i++) {
+               struct diffstat_file *file = data->files[i];
+               uintmax_t added = file->added;
+               uintmax_t deleted = file->deleted;
+
+               if (file->is_unmerged ||
+                   (!file->is_interesting && (added + deleted == 0))) {
                        total_files--;
                        continue;
                }
-               adds += added;
-               dels += deleted;
+
+               if (!file->is_binary) {
+                       adds += added;
+                       dels += deleted;
+               }
+               if (i < count)
+                       continue;
                if (!extra_shown)
                        fprintf(options->file, "%s ...\n", line_prefix);
                extra_shown = 1;
@@ -1723,9 +1751,8 @@ static void show_shortstats(struct diffstat_t *data, struct diff_options *option
                int added = data->files[i]->added;
                int deleted= data->files[i]->deleted;
 
-               if (data->files[i]->is_unmerged)
-                       continue;
-               if (!data->files[i]->is_interesting && (added + deleted == 0)) {
+               if (data->files[i]->is_unmerged ||
+                   (!data->files[i]->is_interesting && (added + deleted == 0))) {
                        total_files--;
                } else if (!data->files[i]->is_binary) { /* don't count bytes */
                        adds += added;
@@ -2438,7 +2465,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
        }
 
        data = diffstat_add(diffstat, name_a, name_b);
-       data->is_interesting = p->status != 0;
+       data->is_interesting = p->status != DIFF_STATUS_UNKNOWN;
 
        if (!one || !two) {
                data->is_unmerged = 1;
@@ -3209,6 +3236,7 @@ void diff_setup(struct diff_options *options)
        options->add_remove = diff_addremove;
        options->use_color = diff_use_color_default;
        options->detect_rename = diff_detect_rename_default;
+       options->xdl_opts |= diff_algorithm;
 
        if (diff_no_prefix) {
                options->a_prefix = options->b_prefix = "";
@@ -3606,6 +3634,16 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
        else if (!strcmp(arg, "--histogram"))
                options->xdl_opts = DIFF_WITH_ALG(options, HISTOGRAM_DIFF);
+       else if (!prefixcmp(arg, "--diff-algorithm=")) {
+               long value = parse_algorithm_value(arg+17);
+               if (value < 0)
+                       return error("option diff-algorithm accepts \"myers\", "
+                                    "\"minimal\", \"patience\" and \"histogram\"");
+               /* clear out previous settings */
+               DIFF_XDL_CLR(options, NEED_MINIMAL);
+               options->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
+               options->xdl_opts |= value;
+       }
 
        /* flags options */
        else if (!strcmp(arg, "--binary")) {