diff.c: flush stdout before printing rename warnings
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index 71f0274ce0df2e6af8cd144c78c64dbb694c1ab7..bfd1bd2410f9d0b920fda58cb0caae33a94111e7 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -2,6 +2,7 @@
  * Copyright (C) 2005 Junio C Hamano
  */
 #include "cache.h"
+#include "tempfile.h"
 #include "quote.h"
 #include "diff.h"
 #include "diffcore.h"
@@ -12,7 +13,7 @@
 #include "run-command.h"
 #include "utf8.h"
 #include "userdiff.h"
-#include "sigchain.h"
+#include "submodule-config.h"
 #include "submodule.h"
 #include "ll-merge.h"
 #include "string-list.h"
@@ -25,6 +26,7 @@
 #endif
 
 static int diff_detect_rename_default;
+static int diff_compaction_heuristic; /* experimental */
 static int diff_rename_limit_default = 400;
 static int diff_suppress_blank_empty;
 static int diff_use_color_default = -1;
@@ -167,6 +169,11 @@ long parse_algorithm_value(const char *value)
  * never be affected by the setting of diff.renames
  * the user happens to have in the configuration file.
  */
+void init_diff_ui_defaults(void)
+{
+       diff_detect_rename_default = 1;
+}
+
 int git_diff_ui_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
@@ -183,6 +190,10 @@ 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;
@@ -308,11 +319,26 @@ static const char *external_diff(void)
        return external_diff_cmd;
 }
 
+/*
+ * Keep track of files used for diffing. Sometimes such an entry
+ * refers to a temporary file, sometimes to an existing file, and
+ * sometimes to "/dev/null".
+ */
 static struct diff_tempfile {
-       const char *name; /* filename external diff should read from */
-       char hex[41];
+       /*
+        * filename external diff should read from, or NULL if this
+        * entry is currently not in use:
+        */
+       const char *name;
+
+       char hex[GIT_SHA1_HEXSZ + 1];
        char mode[10];
-       char tmp_path[PATH_MAX];
+
+       /*
+        * If this diff_tempfile instance refers to a temporary file,
+        * this tempfile object is used to manage its lifetime.
+        */
+       struct tempfile tempfile;
 } diff_temp[2];
 
 typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
@@ -328,7 +354,6 @@ struct emit_callback {
        const char **label_path;
        struct diff_words_data *diff_words;
        struct diff_options *opt;
-       int *found_changesp;
        struct strbuf *header;
 };
 
@@ -597,25 +622,16 @@ static struct diff_tempfile *claim_diff_tempfile(void) {
        die("BUG: diff is failing to clean up its tempfiles");
 }
 
-static int remove_tempfile_installed;
-
 static void remove_tempfile(void)
 {
        int i;
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
-               if (diff_temp[i].name == diff_temp[i].tmp_path)
-                       unlink_or_warn(diff_temp[i].name);
+               if (is_tempfile_active(&diff_temp[i].tempfile))
+                       delete_tempfile(&diff_temp[i].tempfile);
                diff_temp[i].name = NULL;
        }
 }
 
-static void remove_tempfile_on_signal(int signo)
-{
-       remove_tempfile();
-       sigchain_pop(signo);
-       raise(signo);
-}
-
 static void print_line_count(FILE *file, int count)
 {
        switch (count) {
@@ -705,7 +721,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) {
@@ -934,7 +949,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;
@@ -1199,12 +1215,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;
@@ -1212,9 +1229,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;
        }
@@ -1232,15 +1249,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;
        }
 
@@ -1265,8 +1274,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
@@ -1277,7 +1286,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;
        }
@@ -1299,8 +1308,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;
        }
@@ -1916,8 +1924,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;
 
@@ -1946,7 +1954,7 @@ static void show_dirstat(struct diff_options *options)
                if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
                        diff_populate_filespec(p->one, 0);
                        diff_populate_filespec(p->two, 0);
-                       diffcore_count_changes(p->one, p->two, NULL, NULL, 0,
+                       diffcore_count_changes(p->one, p->two, NULL, NULL,
                                               &copied, &added);
                        diff_free_filespec_data(p->one);
                        diff_free_filespec_data(p->two);
@@ -2289,7 +2297,8 @@ static void builtin_diff(const char *name_a,
                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.hash, two->oid.hash,
+                               two->dirty_submodule,
                                meta, del, add, reset);
                return;
        }
@@ -2367,7 +2376,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;
@@ -2420,7 +2429,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);
@@ -2488,7 +2496,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;
@@ -2600,12 +2608,9 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
 
 struct diff_filespec *alloc_filespec(const char *path)
 {
-       int namelen = strlen(path);
-       struct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1);
+       struct diff_filespec *spec;
 
-       memset(spec, 0, sizeof(*spec));
-       spec->path = (char *)(spec + 1);
-       memcpy(spec->path, path, namelen+1);
+       FLEXPTR_ALLOC_STR(spec, path, path);
        spec->count = 1;
        spec->is_binary = -1;
        return spec;
@@ -2624,8 +2629,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;
        }
 }
 
@@ -2668,6 +2673,13 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
        if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1))
                return 0;
 
+       /*
+        * Similarly, if we'd have to convert the file contents anyway, that
+        * makes the optimization not worthwhile.
+        */
+       if (!want_file && would_convert_to_git(name))
+               return 0;
+
        len = strlen(name);
        pos = cache_name_pos(name, len);
        if (pos < 0)
@@ -2700,21 +2712,22 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
 
 static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
 {
-       int len;
-       char *data = xmalloc(100), *dirty = "";
+       struct strbuf buf = STRBUF_INIT;
+       char *dirty = "";
 
        /* Are we looking at the work tree? */
        if (s->dirty_submodule)
                dirty = "-dirty";
 
-       len = snprintf(data, 100,
-                      "Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty);
-       s->data = data;
-       s->size = len;
-       s->should_free = 1;
+       strbuf_addf(&buf, "Subproject commit %s%s\n",
+                   oid_to_hex(&s->oid), dirty);
+       s->size = buf.len;
        if (size_only) {
                s->data = NULL;
-               free(data);
+               strbuf_release(&buf);
+       } else {
+               s->data = strbuf_detach(&buf, NULL);
+               s->should_free = 1;
        }
        return 0;
 }
@@ -2750,8 +2763,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;
@@ -2808,9 +2821,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) {
@@ -2818,9 +2832,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;
@@ -2849,7 +2863,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;
@@ -2862,8 +2876,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
        strbuf_addstr(&template, "XXXXXX_");
        strbuf_addstr(&template, base);
 
-       fd = git_mkstemps(temp->tmp_path, PATH_MAX, template.buf,
-                       strlen(base) + 1);
+       fd = mks_tempfile_ts(&temp->tempfile, template.buf, strlen(base) + 1);
        if (fd < 0)
                die_errno("unable to create temp-file");
        if (convert_to_working_tree(path,
@@ -2873,11 +2886,10 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
        }
        if (write_in_full(fd, blob, size) != size)
                die_errno("unable to write temp-file");
-       close(fd);
-       temp->name = temp->tmp_path;
-       strcpy(temp->hex, sha1_to_hex(sha1));
-       temp->hex[40] = 0;
-       sprintf(temp->mode, "%06o", mode);
+       close_tempfile(&temp->tempfile);
+       temp->name = get_tempfile_path(&temp->tempfile);
+       oid_to_hex_r(temp->hex, oid);
+       xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
        strbuf_release(&buf);
        strbuf_release(&template);
        free(path_dup);
@@ -2894,20 +2906,14 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
                 * a '+' entry produces this for file-1.
                 */
                temp->name = "/dev/null";
-               strcpy(temp->hex, ".");
-               strcpy(temp->mode, ".");
+               xsnprintf(temp->hex, sizeof(temp->hex), ".");
+               xsnprintf(temp->mode, sizeof(temp->mode), ".");
                return temp;
        }
 
-       if (!remove_tempfile_installed) {
-               atexit(remove_tempfile);
-               sigchain_push_common(remove_tempfile_on_signal);
-               remove_tempfile_installed = 1;
-       }
-
        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)
@@ -2919,26 +2925,26 @@ 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)
-                               strcpy(temp->hex, sha1_to_hex(null_sha1));
+                       if (!one->oid_valid)
+                               sha1_to_hex_r(temp->hex, null_sha1);
                        else
-                               strcpy(temp->hex, sha1_to_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
                         * !(one->sha1_valid), as long as
                         * DIFF_FILE_VALID(one).
                         */
-                       sprintf(temp->mode, "%06o", one->mode);
+                       xsnprintf(temp->mode, sizeof(temp->mode), "%06o", one->mode);
                }
                return temp;
        }
@@ -2946,7 +2952,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;
 }
@@ -3059,7 +3065,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)) {
@@ -3069,8 +3075,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_add_unique_abbrev(msg, two->oid.hash, abbrev);
                if (one->mode == two->mode)
                        strbuf_addf(msg, " %06o", one->mode);
                strbuf_addf(msg, "%s\n", reset);
@@ -3125,20 +3131,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)
@@ -3277,6 +3283,8 @@ 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)
+               DIFF_XDL_SET(options, COMPACTION_HEURISTIC);
 
        options->orderfile = diff_order_file_cfg;
 
@@ -3694,12 +3702,16 @@ static int parse_ws_error_highlight(struct diff_options *opt, const char *arg)
        return 1;
 }
 
-int diff_opt_parse(struct diff_options *options, const char **av, int ac)
+int diff_opt_parse(struct diff_options *options,
+                  const char **av, int ac, const char *prefix)
 {
        const char *arg = av[0];
        const char *optarg;
        int argcount;
 
+       if (!prefix)
+               prefix = "";
+
        /* Output format options */
        if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch")
            || opt_arg(arg, 'U', "unified", &options->context))
@@ -3793,6 +3805,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                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"))
+               DIFF_XDL_SET(options, COMPACTION_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);
        else if (!strcmp(arg, "--histogram"))
@@ -3824,9 +3840,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_OPT_SET(options, FIND_COPIES_HARDER);
        else if (!strcmp(arg, "--follow"))
                DIFF_OPT_SET(options, FOLLOW_RENAMES);
-       else if (!strcmp(arg, "--no-follow"))
+       else if (!strcmp(arg, "--no-follow")) {
                DIFF_OPT_CLR(options, FOLLOW_RENAMES);
-       else if (!strcmp(arg, "--color"))
+               DIFF_OPT_CLR(options, DEFAULT_FOLLOW_RENAMES);
+       } else if (!strcmp(arg, "--color"))
                options->use_color = 1;
        else if (skip_prefix(arg, "--color=", &arg)) {
                int value = git_config_colorbool(NULL, arg);
@@ -3915,7 +3932,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "--pickaxe-regex"))
                options->pickaxe_opts |= DIFF_PICKAXE_REGEX;
        else if ((argcount = short_opt('O', av, &optarg))) {
-               options->orderfile = optarg;
+               const char *path = prefix_filename(prefix, strlen(prefix), optarg);
+               options->orderfile = xstrdup(path);
                return argcount;
        }
        else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) {
@@ -3954,10 +3972,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "--no-function-context"))
                DIFF_OPT_CLR(options, FUNCCONTEXT);
        else if ((argcount = parse_long_opt("output", av, &optarg))) {
-               options->file = fopen(optarg, "w");
+               const char *path = prefix_filename(prefix, strlen(prefix), optarg);
+               options->file = fopen(path, "w");
                if (!options->file)
-                       die_errno("Could not open '%s'", optarg);
+                       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;
@@ -4068,7 +4089,8 @@ void diff_free_filepair(struct diff_filepair *p)
        free(p);
 }
 
-/* This is different from find_unique_abbrev() in that
+/*
+ * This is different from find_unique_abbrev() in that
  * it stuffs the result with dots for alignment.
  */
 const char *diff_unique_abbrev(const unsigned char *sha1, int len)
@@ -4080,12 +4102,32 @@ const char *diff_unique_abbrev(const unsigned char *sha1, int len)
 
        abbrev = find_unique_abbrev(sha1, len);
        abblen = strlen(abbrev);
+
+       /*
+        * In well-behaved cases, where the abbbreviated result is the
+        * same as the requested length, append three dots after the
+        * abbreviation (hence the whole logic is limited to the case
+        * where abblen < 37); when the actual abbreviated result is a
+        * bit longer than the requested length, we reduce the number
+        * of dots so that they match the well-behaved ones.  However,
+        * if the actual abbreviation is longer than the requested
+        * length by more than three, we give up on aligning, and add
+        * three dots anyway, to indicate that the output is not the
+        * full object name.  Yes, this may be suboptimal, but this
+        * appears only in "diff --raw --abbrev" output and it is not
+        * worth the effort to change it now.  Note that this would
+        * likely to work fine when the automatic sizing of default
+        * abbreviation length is used--we would be fed -1 in "len" in
+        * that case, and will end up always appending three-dots, but
+        * the automatic sizing is supposed to give abblen that ensures
+        * uniqueness across all objects (statistically speaking).
+        */
        if (abblen < 37) {
                static char hex[41];
                if (len < abblen && abblen <= len + 2)
-                       sprintf(hex, "%s%.*s", abbrev, len+3-abblen, "..");
+                       xsnprintf(hex, sizeof(hex), "%s%.*s", abbrev, len+3-abblen, "..");
                else
-                       sprintf(hex, "%s...", abbrev);
+                       xsnprintf(hex, sizeof(hex), "%s...", abbrev);
                return hex;
        }
        return sha1_to_hex(sha1);
@@ -4099,8 +4141,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),
@@ -4149,11 +4192,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;
 }
@@ -4214,7 +4257,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);
@@ -4284,11 +4327,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
@@ -4430,7 +4473,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;
@@ -4465,9 +4508,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));
@@ -4502,10 +4542,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;
                }
 
@@ -4522,11 +4571,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]);
@@ -4574,6 +4623,7 @@ static const char rename_limit_advice[] =
 
 void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
 {
+       fflush(stdout);
        if (degraded_cc)
                warning(degrade_cc_to_c_warning);
        else if (needed)
@@ -4797,7 +4847,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) ||
@@ -5079,7 +5129,7 @@ size_t fill_textconv(struct userdiff_driver *driver,
 {
        size_t size;
 
-       if (!driver || !driver->textconv) {
+       if (!driver) {
                if (!DIFF_FILE_VALID(df)) {
                        *outbuf = "";
                        return 0;
@@ -5090,8 +5140,12 @@ size_t fill_textconv(struct userdiff_driver *driver,
                return df->size;
        }
 
-       if (driver->textconv_cache && df->sha1_valid) {
-               *outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
+       if (!driver->textconv)
+               die("BUG: fill_textconv called with non-textconv driver");
+
+       if (driver->textconv_cache && df->oid_valid) {
+               *outbuf = notes_cache_get(driver->textconv_cache,
+                                         df->oid.hash,
                                          &size);
                if (*outbuf)
                        return size;
@@ -5101,9 +5155,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,