]> git.ipfire.org Git - thirdparty/git.git/blobdiff - diff.c
diff: add --ita-[in]visible-in-index
[thirdparty/git.git] / diff.c
diff --git a/diff.c b/diff.c
index abff2eec2b0604786b09ff0ac7fae24e1df6677f..297e0340e93cdf3e14be14f03d41506756d48511 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -18,6 +18,7 @@
 #include "ll-merge.h"
 #include "string-list.h"
 #include "argv-array.h"
+#include "graph.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -26,6 +27,7 @@
 #endif
 
 static int diff_detect_rename_default;
+static int diff_indent_heuristic; /* experimental */
 static int diff_compaction_heuristic; /* experimental */
 static int diff_rename_limit_default = 400;
 static int diff_suppress_blank_empty;
@@ -54,6 +56,11 @@ static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_NORMAL,       /* FUNCINFO */
 };
 
+static NORETURN void die_want_option(const char *option_name)
+{
+       die(_("option '%s' requires a value"), option_name);
+}
+
 static int parse_diff_color_slot(const char *var)
 {
        if (!strcasecmp(var, "context") || !strcasecmp(var, "plain"))
@@ -131,9 +138,11 @@ static int parse_dirstat_params(struct diff_options *options, const char *params
 static int parse_submodule_params(struct diff_options *options, const char *value)
 {
        if (!strcmp(value, "log"))
-               DIFF_OPT_SET(options, SUBMODULE_LOG);
+               options->submodule_format = DIFF_SUBMODULE_LOG;
        else if (!strcmp(value, "short"))
-               DIFF_OPT_CLR(options, SUBMODULE_LOG);
+               options->submodule_format = DIFF_SUBMODULE_SHORT;
+       else if (!strcmp(value, "diff"))
+               options->submodule_format = DIFF_SUBMODULE_INLINE_DIFF;
        else
                return -1;
        return 0;
@@ -174,6 +183,21 @@ void init_diff_ui_defaults(void)
        diff_detect_rename_default = 1;
 }
 
+int git_diff_heuristic_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "diff.indentheuristic")) {
+               diff_indent_heuristic = git_config_bool(var, value);
+               if (diff_indent_heuristic)
+                       diff_compaction_heuristic = 0;
+       }
+       if (!strcmp(var, "diff.compactionheuristic")) {
+               diff_compaction_heuristic = git_config_bool(var, value);
+               if (diff_compaction_heuristic)
+                       diff_indent_heuristic = 0;
+       }
+       return 0;
+}
+
 int git_diff_ui_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
@@ -190,10 +214,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                diff_detect_rename_default = git_config_rename(var, value);
                return 0;
        }
-       if (!strcmp(var, "diff.compactionheuristic")) {
-               diff_compaction_heuristic = git_config_bool(var, value);
-               return 0;
-       }
        if (!strcmp(var, "diff.autorefreshindex")) {
                diff_auto_refresh_index = git_config_bool(var, value);
                return 0;
@@ -234,6 +254,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (git_diff_heuristic_config(var, value, cb) < 0)
+               return -1;
        if (git_color_config(var, value, cb) < 0)
                return -1;
 
@@ -354,7 +376,6 @@ struct emit_callback {
        const char **label_path;
        struct diff_words_data *diff_words;
        struct diff_options *opt;
-       int *found_changesp;
        struct strbuf *header;
 };
 
@@ -722,7 +743,6 @@ static void emit_rewrite_diff(const char *name_a,
 
        memset(&ecbdata, 0, sizeof(ecbdata));
        ecbdata.color_diff = want_color(o->use_color);
-       ecbdata.found_changesp = &o->found_changes;
        ecbdata.ws_rule = whitespace_rule(name_b);
        ecbdata.opt = o;
        if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
@@ -951,7 +971,8 @@ static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,
 {
        if (word_regex && *begin < buffer->size) {
                regmatch_t match[1];
-               if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
+               if (!regexec_buf(word_regex, buffer->ptr + *begin,
+                                buffer->size - *begin, 1, match, 0)) {
                        char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
                                        '\n', match[0].rm_eo - match[0].rm_so);
                        *end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
@@ -1216,12 +1237,13 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        struct diff_options *o = ecbdata->opt;
        const char *line_prefix = diff_line_prefix(o);
 
+       o->found_changes = 1;
+
        if (ecbdata->header) {
-               fprintf(ecbdata->opt->file, "%s", ecbdata->header->buf);
+               fprintf(o->file, "%s", ecbdata->header->buf);
                strbuf_reset(ecbdata->header);
                ecbdata->header = NULL;
        }
-       *(ecbdata->found_changesp) = 1;
 
        if (ecbdata->label_path[0]) {
                const char *name_a_tab, *name_b_tab;
@@ -1229,9 +1251,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                name_a_tab = strchr(ecbdata->label_path[0], ' ') ? "\t" : "";
                name_b_tab = strchr(ecbdata->label_path[1], ' ') ? "\t" : "";
 
-               fprintf(ecbdata->opt->file, "%s%s--- %s%s%s\n",
+               fprintf(o->file, "%s%s--- %s%s%s\n",
                        line_prefix, meta, ecbdata->label_path[0], reset, name_a_tab);
-               fprintf(ecbdata->opt->file, "%s%s+++ %s%s%s\n",
+               fprintf(o->file, "%s%s+++ %s%s%s\n",
                        line_prefix, meta, ecbdata->label_path[1], reset, name_b_tab);
                ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
        }
@@ -1249,15 +1271,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                find_lno(line, ecbdata);
                emit_hunk_header(ecbdata, line, len);
                if (line[len-1] != '\n')
-                       putc('\n', ecbdata->opt->file);
-               return;
-       }
-
-       if (len < 1) {
-               emit_line(ecbdata->opt, reset, reset, line, len);
-               if (ecbdata->diff_words
-                   && ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN)
-                       fputs("~\n", ecbdata->opt->file);
+                       putc('\n', o->file);
                return;
        }
 
@@ -1282,8 +1296,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                }
                diff_words_flush(ecbdata);
                if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
-                       emit_line(ecbdata->opt, context, reset, line, len);
-                       fputs("~\n", ecbdata->opt->file);
+                       emit_line(o, context, reset, line, len);
+                       fputs("~\n", o->file);
                } else {
                        /*
                         * Skip the prefix character, if any.  With
@@ -1294,7 +1308,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
                              line++;
                              len--;
                        }
-                       emit_line(ecbdata->opt, context, reset, line, len);
+                       emit_line(o, context, reset, line, len);
                }
                return;
        }
@@ -1316,8 +1330,7 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
        default:
                /* incomplete line at the end */
                ecbdata->lno_in_preimage++;
-               emit_line(ecbdata->opt,
-                         diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
+               emit_line(o, diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
                          reset, line, len);
                break;
        }
@@ -1625,7 +1638,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
         */
 
        if (options->stat_width == -1)
-               width = term_columns() - options->output_prefix_length;
+               width = term_columns() - strlen(line_prefix);
        else
                width = options->stat_width ? options->stat_width : 80;
        number_width = decimal_width(max_change) > number_width ?
@@ -1933,8 +1946,8 @@ static void show_dirstat(struct diff_options *options)
 
                name = p->two->path ? p->two->path : p->one->path;
 
-               if (p->one->sha1_valid && p->two->sha1_valid)
-                       content_changed = hashcmp(p->one->sha1, p->two->sha1);
+               if (p->one->oid_valid && p->two->oid_valid)
+                       content_changed = oidcmp(&p->one->oid, &p->two->oid);
                else
                        content_changed = 1;
 
@@ -2299,16 +2312,37 @@ static void builtin_diff(const char *name_a,
        struct strbuf header = STRBUF_INIT;
        const char *line_prefix = diff_line_prefix(o);
 
-       if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
-                       (!one->mode || S_ISGITLINK(one->mode)) &&
-                       (!two->mode || S_ISGITLINK(two->mode))) {
+       diff_set_mnemonic_prefix(o, "a/", "b/");
+       if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+               a_prefix = o->b_prefix;
+               b_prefix = o->a_prefix;
+       } else {
+               a_prefix = o->a_prefix;
+               b_prefix = o->b_prefix;
+       }
+
+       if (o->submodule_format == DIFF_SUBMODULE_LOG &&
+           (!one->mode || S_ISGITLINK(one->mode)) &&
+           (!two->mode || S_ISGITLINK(two->mode))) {
                const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
                const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
                show_submodule_summary(o->file, one->path ? one->path : two->path,
                                line_prefix,
-                               one->sha1, two->sha1, two->dirty_submodule,
+                               &one->oid, &two->oid,
+                               two->dirty_submodule,
                                meta, del, add, reset);
                return;
+       } else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF &&
+                  (!one->mode || S_ISGITLINK(one->mode)) &&
+                  (!two->mode || S_ISGITLINK(two->mode))) {
+               const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
+               const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
+               show_submodule_inline_diff(o->file, one->path ? one->path : two->path,
+                               line_prefix,
+                               &one->oid, &two->oid,
+                               two->dirty_submodule,
+                               meta, del, add, reset, o);
+               return;
        }
 
        if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
@@ -2316,15 +2350,6 @@ static void builtin_diff(const char *name_a,
                textconv_two = get_textconv(two);
        }
 
-       diff_set_mnemonic_prefix(o, "a/", "b/");
-       if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
-               a_prefix = o->b_prefix;
-               b_prefix = o->a_prefix;
-       } else {
-               a_prefix = o->a_prefix;
-               b_prefix = o->b_prefix;
-       }
-
        /* Never use a non-valid filename anywhere if at all possible */
        name_a = DIFF_FILE_VALID(one) ? name_a : name_b;
        name_b = DIFF_FILE_VALID(two) ? name_b : name_a;
@@ -2384,7 +2409,7 @@ static void builtin_diff(const char *name_a,
                if (!one->data && !two->data &&
                    S_ISREG(one->mode) && S_ISREG(two->mode) &&
                    !DIFF_OPT_TST(o, BINARY)) {
-                       if (!hashcmp(one->sha1, two->sha1)) {
+                       if (!oidcmp(&one->oid, &two->oid)) {
                                if (must_show_header)
                                        fprintf(o->file, "%s", header.buf);
                                goto free_ab_and_return;
@@ -2437,7 +2462,6 @@ static void builtin_diff(const char *name_a,
                memset(&ecbdata, 0, sizeof(ecbdata));
                ecbdata.label_path = lbl;
                ecbdata.color_diff = want_color(o->use_color);
-               ecbdata.found_changesp = &o->found_changes;
                ecbdata.ws_rule = whitespace_rule(name_b);
                if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
                        check_blank_at_eof(&mf1, &mf2, &ecbdata);
@@ -2505,7 +2529,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
                return;
        }
 
-       same_contents = !hashcmp(one->sha1, two->sha1);
+       same_contents = !oidcmp(&one->oid, &two->oid);
 
        if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
                data->is_binary = 1;
@@ -2638,8 +2662,8 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
 {
        if (mode) {
                spec->mode = canon_mode(mode);
-               hashcpy(spec->sha1, sha1);
-               spec->sha1_valid = sha1_valid;
+               hashcpy(spec->oid.hash, sha1);
+               spec->oid_valid = sha1_valid;
        }
 }
 
@@ -2699,7 +2723,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
         * This is not the sha1 we are looking for, or
         * unreusable because it is not a regular file.
         */
-       if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode))
+       if (hashcmp(sha1, ce->oid.hash) || !S_ISREG(ce->ce_mode))
                return 0;
 
        /*
@@ -2728,7 +2752,8 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
        if (s->dirty_submodule)
                dirty = "-dirty";
 
-       strbuf_addf(&buf, "Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty);
+       strbuf_addf(&buf, "Subproject commit %s%s\n",
+                   oid_to_hex(&s->oid), dirty);
        s->size = buf.len;
        if (size_only) {
                s->data = NULL;
@@ -2771,8 +2796,8 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
        if (S_ISGITLINK(s->mode))
                return diff_populate_gitlink(s, size_only);
 
-       if (!s->sha1_valid ||
-           reuse_worktree_file(s->path, s->sha1, 0)) {
+       if (!s->oid_valid ||
+           reuse_worktree_file(s->path, s->oid.hash, 0)) {
                struct strbuf buf = STRBUF_INIT;
                struct stat st;
                int fd;
@@ -2829,9 +2854,10 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
        else {
                enum object_type type;
                if (size_only || (flags & CHECK_BINARY)) {
-                       type = sha1_object_info(s->sha1, &s->size);
+                       type = sha1_object_info(s->oid.hash, &s->size);
                        if (type < 0)
-                               die("unable to read %s", sha1_to_hex(s->sha1));
+                               die("unable to read %s",
+                                   oid_to_hex(&s->oid));
                        if (size_only)
                                return 0;
                        if (s->size > big_file_threshold && s->is_binary == -1) {
@@ -2839,9 +2865,9 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
                                return 0;
                        }
                }
-               s->data = read_sha1_file(s->sha1, &type, &s->size);
+               s->data = read_sha1_file(s->oid.hash, &type, &s->size);
                if (!s->data)
-                       die("unable to read %s", sha1_to_hex(s->sha1));
+                       die("unable to read %s", oid_to_hex(&s->oid));
                s->should_free = 1;
        }
        return 0;
@@ -2870,7 +2896,7 @@ void diff_free_filespec_data(struct diff_filespec *s)
 static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
                           void *blob,
                           unsigned long size,
-                          const unsigned char *sha1,
+                          const struct object_id *oid,
                           int mode)
 {
        int fd;
@@ -2895,7 +2921,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
                die_errno("unable to write temp-file");
        close_tempfile(&temp->tempfile);
        temp->name = get_tempfile_path(&temp->tempfile);
-       sha1_to_hex_r(temp->hex, sha1);
+       oid_to_hex_r(temp->hex, oid);
        xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
        strbuf_release(&buf);
        strbuf_release(&template);
@@ -2919,8 +2945,8 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
        }
 
        if (!S_ISGITLINK(one->mode) &&
-           (!one->sha1_valid ||
-            reuse_worktree_file(name, one->sha1, 1))) {
+           (!one->oid_valid ||
+            reuse_worktree_file(name, one->oid.hash, 1))) {
                struct stat st;
                if (lstat(name, &st) < 0) {
                        if (errno == ENOENT)
@@ -2932,19 +2958,19 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
                        if (strbuf_readlink(&sb, name, st.st_size) < 0)
                                die_errno("readlink(%s)", name);
                        prep_temp_blob(name, temp, sb.buf, sb.len,
-                                      (one->sha1_valid ?
-                                       one->sha1 : null_sha1),
-                                      (one->sha1_valid ?
+                                      (one->oid_valid ?
+                                       &one->oid : &null_oid),
+                                      (one->oid_valid ?
                                        one->mode : S_IFLNK));
                        strbuf_release(&sb);
                }
                else {
                        /* we can borrow from the file in the work tree */
                        temp->name = name;
-                       if (!one->sha1_valid)
+                       if (!one->oid_valid)
                                sha1_to_hex_r(temp->hex, null_sha1);
                        else
-                               sha1_to_hex_r(temp->hex, one->sha1);
+                               sha1_to_hex_r(temp->hex, one->oid.hash);
                        /* Even though we may sometimes borrow the
                         * contents from the work tree, we always want
                         * one->mode.  mode is trustworthy even when
@@ -2959,7 +2985,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
                if (diff_populate_filespec(one, 0))
                        die("cannot read data blob for %s", one->path);
                prep_temp_blob(name, temp, one->data, one->size,
-                              one->sha1, one->mode);
+                              &one->oid, one->mode);
        }
        return temp;
 }
@@ -3072,7 +3098,7 @@ static void fill_metainfo(struct strbuf *msg,
        default:
                *must_show_header = 0;
        }
-       if (one && two && hashcmp(one->sha1, two->sha1)) {
+       if (one && two && oidcmp(&one->oid, &two->oid)) {
                int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
 
                if (DIFF_OPT_TST(o, BINARY)) {
@@ -3082,8 +3108,8 @@ static void fill_metainfo(struct strbuf *msg,
                                abbrev = 40;
                }
                strbuf_addf(msg, "%s%sindex %s..", line_prefix, set,
-                           find_unique_abbrev(one->sha1, abbrev));
-               strbuf_addstr(msg, find_unique_abbrev(two->sha1, abbrev));
+                           find_unique_abbrev(one->oid.hash, abbrev));
+               strbuf_addstr(msg, find_unique_abbrev(two->oid.hash, abbrev));
                if (one->mode == two->mode)
                        strbuf_addf(msg, " %06o", one->mode);
                strbuf_addf(msg, "%s\n", reset);
@@ -3138,20 +3164,20 @@ static void run_diff_cmd(const char *pgm,
 static void diff_fill_sha1_info(struct diff_filespec *one)
 {
        if (DIFF_FILE_VALID(one)) {
-               if (!one->sha1_valid) {
+               if (!one->oid_valid) {
                        struct stat st;
                        if (one->is_stdin) {
-                               hashcpy(one->sha1, null_sha1);
+                               oidclr(&one->oid);
                                return;
                        }
                        if (lstat(one->path, &st) < 0)
                                die_errno("stat '%s'", one->path);
-                       if (index_path(one->sha1, one->path, &st, 0))
+                       if (index_path(one->oid.hash, one->path, &st, 0))
                                die("cannot hash %s", one->path);
                }
        }
        else
-               hashclr(one->sha1);
+               oidclr(&one->oid);
 }
 
 static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
@@ -3290,7 +3316,9 @@ void diff_setup(struct diff_options *options)
        options->use_color = diff_use_color_default;
        options->detect_rename = diff_detect_rename_default;
        options->xdl_opts |= diff_algorithm;
-       if (diff_compaction_heuristic)
+       if (diff_indent_heuristic)
+               DIFF_XDL_SET(options, INDENT_HEURISTIC);
+       else if (diff_compaction_heuristic)
                DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
 
        options->orderfile = diff_order_file_cfg;
@@ -3319,7 +3347,7 @@ void diff_setup_done(struct diff_options *options)
        if (options->output_format & DIFF_FORMAT_NO_OUTPUT)
                count++;
        if (count > 1)
-               die("--name-only, --name-status, --check and -s are mutually exclusive");
+               die(_("--name-only, --name-status, --check and -s are mutually exclusive"));
 
        /*
         * Most of the time we can say "there are changes"
@@ -3515,7 +3543,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                        if (*arg == '=')
                                width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
-                               die("Option '--stat-width' requires a value");
+                               die_want_option("--stat-width");
                        else if (!*arg) {
                                width = strtoul(av[1], &end, 10);
                                argcount = 2;
@@ -3524,7 +3552,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                        if (*arg == '=')
                                name_width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
-                               die("Option '--stat-name-width' requires a value");
+                               die_want_option("--stat-name-width");
                        else if (!*arg) {
                                name_width = strtoul(av[1], &end, 10);
                                argcount = 2;
@@ -3533,7 +3561,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                        if (*arg == '=')
                                graph_width = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
-                               die("Option '--stat-graph-width' requires a value");
+                               die_want_option("--stat-graph-width");
                        else if (!*arg) {
                                graph_width = strtoul(av[1], &end, 10);
                                argcount = 2;
@@ -3542,7 +3570,7 @@ static int stat_opt(struct diff_options *options, const char **av)
                        if (*arg == '=')
                                count = strtoul(arg + 1, &end, 10);
                        else if (!*arg && !av[1])
-                               die("Option '--stat-count' requires a value");
+                               die_want_option("--stat-count");
                        else if (!*arg) {
                                count = strtoul(av[1], &end, 10);
                                argcount = 2;
@@ -3812,9 +3840,15 @@ int diff_opt_parse(struct diff_options *options,
                DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
        else if (!strcmp(arg, "--ignore-blank-lines"))
                DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
-       else if (!strcmp(arg, "--compaction-heuristic"))
+       else if (!strcmp(arg, "--indent-heuristic")) {
+               DIFF_XDL_SET(options, INDENT_HEURISTIC);
+               DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
+       } else if (!strcmp(arg, "--no-indent-heuristic"))
+               DIFF_XDL_CLR(options, INDENT_HEURISTIC);
+       else if (!strcmp(arg, "--compaction-heuristic")) {
                DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
-       else if (!strcmp(arg, "--no-compaction-heuristic"))
+               DIFF_XDL_CLR(options, INDENT_HEURISTIC);
+       } else if (!strcmp(arg, "--no-compaction-heuristic"))
                DIFF_XDL_CLR(options, COMPACTION_HEURISTIC);
        else if (!strcmp(arg, "--patience"))
                options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
@@ -3912,11 +3946,15 @@ int diff_opt_parse(struct diff_options *options,
                DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, arg);
        } else if (!strcmp(arg, "--submodule"))
-               DIFF_OPT_SET(options, SUBMODULE_LOG);
+               options->submodule_format = DIFF_SUBMODULE_LOG;
        else if (skip_prefix(arg, "--submodule=", &arg))
                return parse_submodule_opt(options, arg);
        else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
                return parse_ws_error_highlight(options, arg);
+       else if (!strcmp(arg, "--ita-invisible-in-index"))
+               options->ita_invisible_in_index = 1;
+       else if (!strcmp(arg, "--ita-visible-in-index"))
+               options->ita_invisible_in_index = 0;
 
        /* misc options */
        else if (!strcmp(arg, "-z"))
@@ -3963,6 +4001,12 @@ int diff_opt_parse(struct diff_options *options,
                options->a_prefix = optarg;
                return argcount;
        }
+       else if ((argcount = parse_long_opt("line-prefix", av, &optarg))) {
+               options->line_prefix = optarg;
+               options->line_prefix_length = strlen(options->line_prefix);
+               graph_setup_line_prefix(options);
+               return argcount;
+       }
        else if ((argcount = parse_long_opt("dst-prefix", av, &optarg))) {
                options->b_prefix = optarg;
                return argcount;
@@ -3984,6 +4028,8 @@ int diff_opt_parse(struct diff_options *options,
                if (!options->file)
                        die_errno("Could not open '%s'", path);
                options->close_file = 1;
+               if (options->use_color != GIT_COLOR_ALWAYS)
+                       options->use_color = GIT_COLOR_NEVER;
                return argcount;
        } else
                return 0;
@@ -4125,8 +4171,9 @@ static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt)
        fprintf(opt->file, "%s", diff_line_prefix(opt));
        if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
                fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode,
-                       diff_unique_abbrev(p->one->sha1, opt->abbrev));
-               fprintf(opt->file, "%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev));
+                       diff_unique_abbrev(p->one->oid.hash, opt->abbrev));
+               fprintf(opt->file, "%s ",
+                       diff_unique_abbrev(p->two->oid.hash, opt->abbrev));
        }
        if (p->score) {
                fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p),
@@ -4175,11 +4222,11 @@ int diff_unmodified_pair(struct diff_filepair *p)
        /* both are valid and point at the same path.  that is, we are
         * dealing with a change.
         */
-       if (one->sha1_valid && two->sha1_valid &&
-           !hashcmp(one->sha1, two->sha1) &&
+       if (one->oid_valid && two->oid_valid &&
+           !oidcmp(&one->oid, &two->oid) &&
            !one->dirty_submodule && !two->dirty_submodule)
                return 1; /* no change */
-       if (!one->sha1_valid && !two->sha1_valid)
+       if (!one->oid_valid && !two->oid_valid)
                return 1; /* both look at the same file on the filesystem. */
        return 0;
 }
@@ -4240,7 +4287,7 @@ void diff_debug_filespec(struct diff_filespec *s, int x, const char *one)
                s->path,
                DIFF_FILE_VALID(s) ? "valid" : "invalid",
                s->mode,
-               s->sha1_valid ? sha1_to_hex(s->sha1) : "");
+               s->oid_valid ? oid_to_hex(&s->oid) : "");
        fprintf(stderr, "queue[%d] %s size %lu\n",
                x, one ? one : "",
                s->size);
@@ -4310,11 +4357,11 @@ static void diff_resolve_rename_copy(void)
                        else
                                p->status = DIFF_STATUS_RENAMED;
                }
-               else if (hashcmp(p->one->sha1, p->two->sha1) ||
+               else if (oidcmp(&p->one->oid, &p->two->oid) ||
                         p->one->mode != p->two->mode ||
                         p->one->dirty_submodule ||
                         p->two->dirty_submodule ||
-                        is_null_sha1(p->one->sha1))
+                        is_null_oid(&p->one->oid))
                        p->status = DIFF_STATUS_MODIFIED;
                else {
                        /* This is a "no-change" entry and should not
@@ -4456,7 +4503,7 @@ static void patch_id_consume(void *priv, char *line, unsigned long len)
 }
 
 /* returns 0 upon success, and writes result into sha1 */
-static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
+static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
@@ -4491,9 +4538,6 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
 
                diff_fill_sha1_info(p->one);
                diff_fill_sha1_info(p->two);
-               if (fill_mmfile(&mf1, p->one) < 0 ||
-                               fill_mmfile(&mf2, p->two) < 0)
-                       return error("unable to read files to diff");
 
                len1 = remove_space(p->one->path, strlen(p->one->path));
                len2 = remove_space(p->two->path, strlen(p->two->path));
@@ -4528,10 +4572,19 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
                                        len2, p->two->path);
                git_SHA1_Update(&ctx, buffer, len1);
 
+               if (diff_header_only)
+                       continue;
+
+               if (fill_mmfile(&mf1, p->one) < 0 ||
+                   fill_mmfile(&mf2, p->two) < 0)
+                       return error("unable to read files to diff");
+
                if (diff_filespec_is_binary(p->one) ||
                    diff_filespec_is_binary(p->two)) {
-                       git_SHA1_Update(&ctx, sha1_to_hex(p->one->sha1), 40);
-                       git_SHA1_Update(&ctx, sha1_to_hex(p->two->sha1), 40);
+                       git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid),
+                                       40);
+                       git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid),
+                                       40);
                        continue;
                }
 
@@ -4548,11 +4601,11 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
        return 0;
 }
 
-int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1)
+int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
-       int result = diff_get_patch_id(options, sha1);
+       int result = diff_get_patch_id(options, sha1, diff_header_only);
 
        for (i = 0; i < q->nr; i++)
                diff_free_filepair(q->queue[i]);
@@ -4823,7 +4876,7 @@ static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
         */
        if (!DIFF_FILE_VALID(p->one) || /* (1) */
            !DIFF_FILE_VALID(p->two) ||
-           (p->one->sha1_valid && p->two->sha1_valid) ||
+           (p->one->oid_valid && p->two->oid_valid) ||
            (p->one->mode != p->two->mode) ||
            diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
            diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
@@ -5119,8 +5172,9 @@ size_t fill_textconv(struct userdiff_driver *driver,
        if (!driver->textconv)
                die("BUG: fill_textconv called with non-textconv driver");
 
-       if (driver->textconv_cache && df->sha1_valid) {
-               *outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
+       if (driver->textconv_cache && df->oid_valid) {
+               *outbuf = notes_cache_get(driver->textconv_cache,
+                                         df->oid.hash,
                                          &size);
                if (*outbuf)
                        return size;
@@ -5130,9 +5184,9 @@ size_t fill_textconv(struct userdiff_driver *driver,
        if (!*outbuf)
                die("unable to read files to diff");
 
-       if (driver->textconv_cache && df->sha1_valid) {
+       if (driver->textconv_cache && df->oid_valid) {
                /* ignore errors, as we might be in a readonly repository */
-               notes_cache_put(driver->textconv_cache, df->sha1, *outbuf,
+               notes_cache_put(driver->textconv_cache, df->oid.hash, *outbuf,
                                size);
                /*
                 * we could save up changes and flush them all at the end,