Merge branch 'sb/diff-color-move-more'
authorJunio C Hamano <gitster@pobox.com>
Mon, 24 Sep 2018 17:30:48 +0000 (10:30 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 24 Sep 2018 17:30:48 +0000 (10:30 -0700)
Bugfix.

* sb/diff-color-move-more:
diff: fix --color-moved-ws=allow-indentation-change

1  2 
diff.c
diff --combined diff.c
index 71ff247702048f70dbfaec59090132461c70642d,9393993e337493e19afd1f87e8a644a6fe34a716..f0c7557b40443da060c3070c602dd03f49b0d689
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -13,7 -13,6 +13,7 @@@
  #include "attr.h"
  #include "run-command.h"
  #include "utf8.h"
 +#include "object-store.h"
  #include "userdiff.h"
  #include "submodule-config.h"
  #include "submodule.h"
@@@ -23,7 -22,6 +23,7 @@@
  #include "argv-array.h"
  #include "graph.h"
  #include "packfile.h"
 +#include "help.h"
  
  #ifdef NO_FAST_WORKING_DIRECTORY
  #define FAST_WORKING_DIRECTORY 0
@@@ -70,37 -68,6 +70,37 @@@ static char diff_colors[][COLOR_MAXLEN
        GIT_COLOR_BOLD_YELLOW,  /* NEW_MOVED ALTERNATIVE */
        GIT_COLOR_FAINT,        /* NEW_MOVED_DIM */
        GIT_COLOR_FAINT_ITALIC, /* NEW_MOVED_ALTERNATIVE_DIM */
 +      GIT_COLOR_FAINT,        /* CONTEXT_DIM */
 +      GIT_COLOR_FAINT_RED,    /* OLD_DIM */
 +      GIT_COLOR_FAINT_GREEN,  /* NEW_DIM */
 +      GIT_COLOR_BOLD,         /* CONTEXT_BOLD */
 +      GIT_COLOR_BOLD_RED,     /* OLD_BOLD */
 +      GIT_COLOR_BOLD_GREEN,   /* NEW_BOLD */
 +};
 +
 +static const char *color_diff_slots[] = {
 +      [DIFF_CONTEXT]                = "context",
 +      [DIFF_METAINFO]               = "meta",
 +      [DIFF_FRAGINFO]               = "frag",
 +      [DIFF_FILE_OLD]               = "old",
 +      [DIFF_FILE_NEW]               = "new",
 +      [DIFF_COMMIT]                 = "commit",
 +      [DIFF_WHITESPACE]             = "whitespace",
 +      [DIFF_FUNCINFO]               = "func",
 +      [DIFF_FILE_OLD_MOVED]         = "oldMoved",
 +      [DIFF_FILE_OLD_MOVED_ALT]     = "oldMovedAlternative",
 +      [DIFF_FILE_OLD_MOVED_DIM]     = "oldMovedDimmed",
 +      [DIFF_FILE_OLD_MOVED_ALT_DIM] = "oldMovedAlternativeDimmed",
 +      [DIFF_FILE_NEW_MOVED]         = "newMoved",
 +      [DIFF_FILE_NEW_MOVED_ALT]     = "newMovedAlternative",
 +      [DIFF_FILE_NEW_MOVED_DIM]     = "newMovedDimmed",
 +      [DIFF_FILE_NEW_MOVED_ALT_DIM] = "newMovedAlternativeDimmed",
 +      [DIFF_CONTEXT_DIM]            = "contextDimmed",
 +      [DIFF_FILE_OLD_DIM]           = "oldDimmed",
 +      [DIFF_FILE_NEW_DIM]           = "newDimmed",
 +      [DIFF_CONTEXT_BOLD]           = "contextBold",
 +      [DIFF_FILE_OLD_BOLD]          = "oldBold",
 +      [DIFF_FILE_NEW_BOLD]          = "newBold",
  };
  
  static NORETURN void die_want_option(const char *option_name)
        die(_("option '%s' requires a value"), option_name);
  }
  
 +define_list_config_array_extra(color_diff_slots, {"plain"});
 +
  static int parse_diff_color_slot(const char *var)
  {
 -      if (!strcasecmp(var, "context") || !strcasecmp(var, "plain"))
 +      if (!strcasecmp(var, "plain"))
                return DIFF_CONTEXT;
 -      if (!strcasecmp(var, "meta"))
 -              return DIFF_METAINFO;
 -      if (!strcasecmp(var, "frag"))
 -              return DIFF_FRAGINFO;
 -      if (!strcasecmp(var, "old"))
 -              return DIFF_FILE_OLD;
 -      if (!strcasecmp(var, "new"))
 -              return DIFF_FILE_NEW;
 -      if (!strcasecmp(var, "commit"))
 -              return DIFF_COMMIT;
 -      if (!strcasecmp(var, "whitespace"))
 -              return DIFF_WHITESPACE;
 -      if (!strcasecmp(var, "func"))
 -              return DIFF_FUNCINFO;
 -      if (!strcasecmp(var, "oldmoved"))
 -              return DIFF_FILE_OLD_MOVED;
 -      if (!strcasecmp(var, "oldmovedalternative"))
 -              return DIFF_FILE_OLD_MOVED_ALT;
 -      if (!strcasecmp(var, "oldmoveddimmed"))
 -              return DIFF_FILE_OLD_MOVED_DIM;
 -      if (!strcasecmp(var, "oldmovedalternativedimmed"))
 -              return DIFF_FILE_OLD_MOVED_ALT_DIM;
 -      if (!strcasecmp(var, "newmoved"))
 -              return DIFF_FILE_NEW_MOVED;
 -      if (!strcasecmp(var, "newmovedalternative"))
 -              return DIFF_FILE_NEW_MOVED_ALT;
 -      if (!strcasecmp(var, "newmoveddimmed"))
 -              return DIFF_FILE_NEW_MOVED_DIM;
 -      if (!strcasecmp(var, "newmovedalternativedimmed"))
 -              return DIFF_FILE_NEW_MOVED_ALT_DIM;
 -      return -1;
 +      return LOOKUP_CONFIG(color_diff_slots, var);
  }
  
  static int parse_dirstat_params(struct diff_options *options, const char *params_string,
@@@ -183,7 -178,7 +183,7 @@@ static int parse_submodule_params(struc
        return 0;
  }
  
 -static int git_config_rename(const char *var, const char *value)
 +int git_config_rename(const char *var, const char *value)
  {
        if (!value)
                return DIFF_DETECT_RENAME;
@@@ -283,12 -278,10 +283,12 @@@ static int parse_color_moved(const cha
                return COLOR_MOVED_ZEBRA;
        else if (!strcmp(arg, "default"))
                return COLOR_MOVED_DEFAULT;
 +      else if (!strcmp(arg, "dimmed-zebra"))
 +              return COLOR_MOVED_ZEBRA_DIM;
        else if (!strcmp(arg, "dimmed_zebra"))
                return COLOR_MOVED_ZEBRA_DIM;
        else
 -              return error(_("color moved setting must be one of 'no', 'default', 'blocks', 'zebra', 'dimmed_zebra', 'plain'"));
 +              return error(_("color moved setting must be one of 'no', 'default', 'blocks', 'zebra', 'dimmed-zebra', 'plain'"));
  }
  
  static int parse_color_moved_ws(const char *arg)
@@@ -623,55 -616,37 +623,55 @@@ static void check_blank_at_eof(mmfile_
        ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
  }
  
 -static void emit_line_0(struct diff_options *o, const char *set, const char *reset,
 +static void emit_line_0(struct diff_options *o,
 +                      const char *set_sign, const char *set, unsigned reverse, const char *reset,
                        int first, const char *line, int len)
  {
        int has_trailing_newline, has_trailing_carriage_return;
 -      int nofirst;
 +      int needs_reset = 0; /* at the end of the line */
        FILE *file = o->file;
  
        fputs(diff_line_prefix(o), file);
  
 -      if (len == 0) {
 -              has_trailing_newline = (first == '\n');
 -              has_trailing_carriage_return = (!has_trailing_newline &&
 -                                              (first == '\r'));
 -              nofirst = has_trailing_newline || has_trailing_carriage_return;
 -      } else {
 -              has_trailing_newline = (len > 0 && line[len-1] == '\n');
 -              if (has_trailing_newline)
 -                      len--;
 -              has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
 -              if (has_trailing_carriage_return)
 -                      len--;
 -              nofirst = 0;
 +      has_trailing_newline = (len > 0 && line[len-1] == '\n');
 +      if (has_trailing_newline)
 +              len--;
 +
 +      has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
 +      if (has_trailing_carriage_return)
 +              len--;
 +
 +      if (!len && !first)
 +              goto end_of_line;
 +
 +      if (reverse && want_color(o->use_color)) {
 +              fputs(GIT_COLOR_REVERSE, file);
 +              needs_reset = 1;
        }
  
 -      if (len || !nofirst) {
 +      if (set_sign) {
 +              fputs(set_sign, file);
 +              needs_reset = 1;
 +      }
 +
 +      if (first)
 +              fputc(first, file);
 +
 +      if (!len)
 +              goto end_of_line;
 +
 +      if (set) {
 +              if (set_sign && set != set_sign)
 +                      fputs(reset, file);
                fputs(set, file);
 -              if (!nofirst)
 -                      fputc(first, file);
 -              fwrite(line, len, 1, file);
 -              fputs(reset, file);
 +              needs_reset = 1;
        }
 +      fwrite(line, len, 1, file);
 +      needs_reset = 1; /* 'line' may contain color codes. */
 +
 +end_of_line:
 +      if (needs_reset)
 +              fputs(reset, file);
        if (has_trailing_carriage_return)
                fputc('\r', file);
        if (has_trailing_newline)
  static void emit_line(struct diff_options *o, const char *set, const char *reset,
                      const char *line, int len)
  {
 -      emit_line_0(o, set, reset, line[0], line+1, len-1);
 +      emit_line_0(o, set, NULL, 0, reset, 0, line, len);
  }
  
  enum diff_symbol {
@@@ -980,8 -955,13 +980,13 @@@ static void pmb_advance_or_null_multi_m
                        /* Carry the white space delta forward */
                        pmb[i]->next_line->wsd = pmb[i]->wsd;
                        pmb[i] = pmb[i]->next_line;
-               } else
+               } else {
+                       if (pmb[i]->wsd) {
+                               free(pmb[i]->wsd->string);
+                               FREE_AND_NULL(pmb[i]->wsd);
+                       }
                        pmb[i] = NULL;
+               }
        }
  }
  
@@@ -1002,10 -982,6 +1007,6 @@@ static int shrink_potential_moved_block
  
                if (lp < pmb_nr && rp > -1 && lp < rp) {
                        pmb[lp] = pmb[rp];
-                       if (pmb[rp]->wsd) {
-                               free(pmb[rp]->wsd->string);
-                               FREE_AND_NULL(pmb[rp]->wsd);
-                       }
                        pmb[rp] = NULL;
                        rp--;
                        lp++;
@@@ -1199,9 -1175,8 +1200,9 @@@ static void dim_moved_lines(struct diff
  }
  
  static void emit_line_ws_markup(struct diff_options *o,
 -                              const char *set, const char *reset,
 -                              const char *line, int len, char sign,
 +                              const char *set_sign, const char *set,
 +                              const char *reset,
 +                              char sign, const char *line, int len,
                                unsigned ws_rule, int blank_at_eof)
  {
        const char *ws = NULL;
                        ws = NULL;
        }
  
 -      if (!ws)
 -              emit_line_0(o, set, reset, sign, line, len);
 -      else if (blank_at_eof)
 +      if (!ws && !set_sign)
 +              emit_line_0(o, set, NULL, 0, reset, sign, line, len);
 +      else if (!ws) {
 +              emit_line_0(o, set_sign, set, !!set_sign, reset, sign, line, len);
 +      } else if (blank_at_eof)
                /* Blank line at EOF - paint '+' as well */
 -              emit_line_0(o, ws, reset, sign, line, len);
 +              emit_line_0(o, ws, NULL, 0, reset, sign, line, len);
        else {
                /* Emit just the prefix, then the rest. */
 -              emit_line_0(o, set, reset, sign, "", 0);
 +              emit_line_0(o, set_sign ? set_sign : set, NULL, !!set_sign, reset,
 +                          sign, "", 0);
                ws_check_emit(line, len, ws_rule,
                              o->file, set, reset, ws);
        }
@@@ -1232,7 -1204,7 +1233,7 @@@ static void emit_diff_symbol_from_struc
                                         struct emitted_diff_symbol *eds)
  {
        static const char *nneof = " No newline at end of file\n";
 -      const char *context, *reset, *set, *meta, *fraginfo;
 +      const char *context, *reset, *set, *set_sign, *meta, *fraginfo;
        struct strbuf sb = STRBUF_INIT;
  
        enum diff_symbol s = eds->s;
                context = diff_get_color_opt(o, DIFF_CONTEXT);
                reset = diff_get_color_opt(o, DIFF_RESET);
                putc('\n', o->file);
 -              emit_line_0(o, context, reset, '\\',
 +              emit_line_0(o, context, NULL, 0, reset, '\\',
                            nneof, strlen(nneof));
                break;
        case DIFF_SYMBOL_SUBMODULE_HEADER:
        case DIFF_SYMBOL_CONTEXT:
                set = diff_get_color_opt(o, DIFF_CONTEXT);
                reset = diff_get_color_opt(o, DIFF_RESET);
 -              emit_line_ws_markup(o, set, reset, line, len, ' ',
 +              set_sign = NULL;
 +              if (o->flags.dual_color_diffed_diffs) {
 +                      char c = !len ? 0 : line[0];
 +
 +                      if (c == '+')
 +                              set = diff_get_color_opt(o, DIFF_FILE_NEW);
 +                      else if (c == '@')
 +                              set = diff_get_color_opt(o, DIFF_FRAGINFO);
 +                      else if (c == '-')
 +                              set = diff_get_color_opt(o, DIFF_FILE_OLD);
 +              }
 +              emit_line_ws_markup(o, set_sign, set, reset,
 +                                  o->output_indicators[OUTPUT_INDICATOR_CONTEXT],
 +                                  line, len,
                                    flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
                break;
        case DIFF_SYMBOL_PLUS:
                        set = diff_get_color_opt(o, DIFF_FILE_NEW);
                }
                reset = diff_get_color_opt(o, DIFF_RESET);
 -              emit_line_ws_markup(o, set, reset, line, len, '+',
 +              if (!o->flags.dual_color_diffed_diffs)
 +                      set_sign = NULL;
 +              else {
 +                      char c = !len ? 0 : line[0];
 +
 +                      set_sign = set;
 +                      if (c == '-')
 +                              set = diff_get_color_opt(o, DIFF_FILE_OLD_BOLD);
 +                      else if (c == '@')
 +                              set = diff_get_color_opt(o, DIFF_FRAGINFO);
 +                      else if (c == '+')
 +                              set = diff_get_color_opt(o, DIFF_FILE_NEW_BOLD);
 +                      else
 +                              set = diff_get_color_opt(o, DIFF_CONTEXT_BOLD);
 +                      flags &= ~DIFF_SYMBOL_CONTENT_WS_MASK;
 +              }
 +              emit_line_ws_markup(o, set_sign, set, reset,
 +                                  o->output_indicators[OUTPUT_INDICATOR_NEW],
 +                                  line, len,
                                    flags & DIFF_SYMBOL_CONTENT_WS_MASK,
                                    flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
                break;
                        set = diff_get_color_opt(o, DIFF_FILE_OLD);
                }
                reset = diff_get_color_opt(o, DIFF_RESET);
 -              emit_line_ws_markup(o, set, reset, line, len, '-',
 +              if (!o->flags.dual_color_diffed_diffs)
 +                      set_sign = NULL;
 +              else {
 +                      char c = !len ? 0 : line[0];
 +
 +                      set_sign = set;
 +                      if (c == '+')
 +                              set = diff_get_color_opt(o, DIFF_FILE_NEW_DIM);
 +                      else if (c == '@')
 +                              set = diff_get_color_opt(o, DIFF_FRAGINFO);
 +                      else if (c == '-')
 +                              set = diff_get_color_opt(o, DIFF_FILE_OLD_DIM);
 +                      else
 +                              set = diff_get_color_opt(o, DIFF_CONTEXT_DIM);
 +              }
 +              emit_line_ws_markup(o, set_sign, set, reset,
 +                                  o->output_indicators[OUTPUT_INDICATOR_OLD],
 +                                  line, len,
                                    flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
                break;
        case DIFF_SYMBOL_WORDS_PORCELAIN:
                fputs(o->stat_sep, o->file);
                break;
        default:
 -              die("BUG: unknown diff symbol");
 +              BUG("unknown diff symbol");
        }
        strbuf_release(&sb);
  }
@@@ -1566,7 -1490,6 +1567,7 @@@ static void emit_hunk_header(struct emi
        const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
        const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
 +      const char *reverse = ecbdata->color_diff ? GIT_COLOR_REVERSE : "";
        static const char atat[2] = { '@', '@' };
        const char *cp, *ep;
        struct strbuf msgbuf = STRBUF_INIT;
        ep += 2; /* skip over @@ */
  
        /* The hunk header in fraginfo color */
 +      if (ecbdata->opt->flags.dual_color_diffed_diffs)
 +              strbuf_addstr(&msgbuf, reverse);
        strbuf_addstr(&msgbuf, frag);
        strbuf_add(&msgbuf, line, ep - line);
        strbuf_addstr(&msgbuf, reset);
@@@ -1628,7 -1549,7 +1629,7 @@@ static struct diff_tempfile *claim_diff
        for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
                if (!diff_temp[i].name)
                        return diff_temp + i;
 -      die("BUG: diff is failing to clean up its tempfiles");
 +      BUG("diff is failing to clean up its tempfiles");
  }
  
  static void remove_tempfile(void)
@@@ -2156,8 -2077,8 +2157,8 @@@ static void init_diff_words_data(struc
                if (regcomp(ecbdata->diff_words->word_regex,
                            o->word_regex,
                            REG_EXTENDED | REG_NEWLINE))
 -                      die ("Invalid regular expression: %s",
 -                           o->word_regex);
 +                      die("invalid regular expression: %s",
 +                          o->word_regex);
        }
        for (i = 0; i < ARRAY_SIZE(diff_words_styles); i++) {
                if (o->word_diff == diff_words_styles[i].type) {
@@@ -2948,11 -2869,16 +2949,11 @@@ static void show_dirstat(struct diff_op
                struct diff_filepair *p = q->queue[i];
                const char *name;
                unsigned long copied, added, damage;
 -              int content_changed;
  
                name = p->two->path ? p->two->path : p->one->path;
  
 -              if (p->one->oid_valid && p->two->oid_valid)
 -                      content_changed = oidcmp(&p->one->oid, &p->two->oid);
 -              else
 -                      content_changed = 1;
 -
 -              if (!content_changed) {
 +              if (p->one->oid_valid && p->two->oid_valid &&
 +                  oideq(&p->one->oid, &p->two->oid)) {
                        /*
                         * The SHA1 has not changed, so pre-/post-content is
                         * identical. We can therefore skip looking at the
                 * made to the preimage.
                 * If the resulting damage is zero, we know that
                 * diffcore_count_changes() considers the two entries to
 -               * be identical, but since content_changed is true, we
 +               * be identical, but since the oid changed, we
                 * know that there must have been _some_ kind of change,
                 * so we force all entries to have damage > 0.
                 */
@@@ -3414,7 -3340,7 +3415,7 @@@ static void builtin_diff(const char *na
                if (!one->data && !two->data &&
                    S_ISREG(one->mode) && S_ISREG(two->mode) &&
                    !o->flags.binary) {
 -                      if (!oidcmp(&one->oid, &two->oid)) {
 +                      if (oideq(&one->oid, &two->oid)) {
                                if (must_show_header)
                                        emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
                                                         header.buf, header.len,
                memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
                memset(&ecbdata, 0, sizeof(ecbdata));
 +              if (o->flags.suppress_diff_headers)
 +                      lbl[0] = NULL;
                ecbdata.label_path = lbl;
                ecbdata.color_diff = want_color(o->use_color);
                ecbdata.ws_rule = whitespace_rule(name_b);
                if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
                        check_blank_at_eof(&mf1, &mf2, &ecbdata);
                ecbdata.opt = o;
 -              ecbdata.header = header.len ? &header : NULL;
 +              if (header.len && !o->flags.suppress_diff_headers)
 +                      ecbdata.header = &header;
                xpp.flags = o->xdl_opts;
                xpp.anchors = o->anchors;
                xpp.anchors_nr = o->anchors_nr;
@@@ -3579,7 -3502,7 +3580,7 @@@ static void builtin_diffstat(const cha
                return;
        }
  
 -      same_contents = !oidcmp(&one->oid, &two->oid);
 +      same_contents = oideq(&one->oid, &two->oid);
  
        if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
                data->is_binary = 1;
@@@ -3755,7 -3678,7 +3756,7 @@@ static int reuse_worktree_file(const ch
         * objects however would tend to be slower as they need
         * to be individually opened and inflated.
         */
 -      if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(oid->hash))
 +      if (!FAST_WORKING_DIRECTORY && !want_file && has_object_pack(oid))
                return 0;
  
        /*
         * This is not the sha1 we are looking for, or
         * unreusable because it is not a regular file.
         */
 -      if (oidcmp(oid, &ce->oid) || !S_ISREG(ce->ce_mode))
 +      if (!oideq(oid, &ce->oid) || !S_ISREG(ce->ce_mode))
                return 0;
  
        /*
@@@ -3921,8 -3844,7 +3922,8 @@@ int diff_populate_filespec(struct diff_
        else {
                enum object_type type;
                if (size_only || (flags & CHECK_BINARY)) {
 -                      type = oid_object_info(&s->oid, &s->size);
 +                      type = oid_object_info(the_repository, &s->oid,
 +                                             &s->size);
                        if (type < 0)
                                die("unable to read %s",
                                    oid_to_hex(&s->oid));
@@@ -3978,7 -3900,7 +3979,7 @@@ static void prep_temp_blob(const char *
        temp->tempfile = mks_tempfile_ts(tempfile.buf, strlen(base) + 1);
        if (!temp->tempfile)
                die_errno("unable to create temp-file");
 -      if (convert_to_working_tree(path,
 +      if (convert_to_working_tree(&the_index, path,
                        (const char *)blob, (size_t)size, &buf)) {
                blob = buf.buf;
                size = buf.len;
@@@ -4123,8 -4045,8 +4124,8 @@@ static const char *diff_abbrev_oid(cons
                char *hex = oid_to_hex(oid);
                if (abbrev < 0)
                        abbrev = FALLBACK_DEFAULT_ABBREV;
 -              if (abbrev > GIT_SHA1_HEXSZ)
 -                      die("BUG: oid abbreviation out of range: %d", abbrev);
 +              if (abbrev > the_hash_algo->hexsz)
 +                      BUG("oid abbreviation out of range: %d", abbrev);
                if (abbrev)
                        hex[abbrev] = '\0';
                return hex;
@@@ -4180,15 -4102,14 +4181,15 @@@ static void fill_metainfo(struct strbu
        default:
                *must_show_header = 0;
        }
 -      if (one && two && oidcmp(&one->oid, &two->oid)) {
 -              int abbrev = o->flags.full_index ? 40 : DEFAULT_ABBREV;
 +      if (one && two && !oideq(&one->oid, &two->oid)) {
 +              const unsigned hexsz = the_hash_algo->hexsz;
 +              int abbrev = o->flags.full_index ? hexsz : DEFAULT_ABBREV;
  
                if (o->flags.binary) {
                        mmfile_t mf;
                        if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
                            (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
 -                              abbrev = 40;
 +                              abbrev = hexsz;
                }
                strbuf_addf(msg, "%s%sindex %s..%s", line_prefix, set,
                            diff_abbrev_oid(&one->oid, abbrev),
@@@ -4385,9 -4306,6 +4386,9 @@@ void diff_setup(struct diff_options *op
  
        options->file = stdout;
  
 +      options->output_indicators[OUTPUT_INDICATOR_NEW] = '+';
 +      options->output_indicators[OUTPUT_INDICATOR_OLD] = '-';
 +      options->output_indicators[OUTPUT_INDICATOR_CONTEXT] = ' ';
        options->abbrev = DEFAULT_ABBREV;
        options->line_termination = '\n';
        options->break_opt = -1;
@@@ -4427,11 -4345,6 +4428,11 @@@ void diff_setup_done(struct diff_option
                              DIFF_FORMAT_NAME_STATUS |
                              DIFF_FORMAT_CHECKDIFF |
                              DIFF_FORMAT_NO_OUTPUT;
 +      /*
 +       * This must be signed because we're comparing against a potentially
 +       * negative value.
 +       */
 +      const int hexsz = the_hash_algo->hexsz;
  
        if (options->set_default)
                options->set_default(options);
  
        if (options->detect_rename && options->rename_limit < 0)
                options->rename_limit = diff_rename_limit_default;
 -      if (options->setup & DIFF_SETUP_USE_CACHE) {
 -              if (!active_cache)
 -                      /* read-cache does not die even when it fails
 -                       * so it is safe for us to do this here.  Also
 -                       * it does not smudge active_cache or active_nr
 -                       * when it fails, so we do not have to worry about
 -                       * cleaning it up ourselves either.
 -                       */
 -                      read_cache();
 -      }
 -      if (40 < options->abbrev)
 -              options->abbrev = 40; /* full */
 +      if (hexsz < options->abbrev)
 +              options->abbrev = hexsz; /* full */
  
        /*
         * It does not make sense to show the first hit we happened
@@@ -4618,7 -4541,7 +4619,7 @@@ static int stat_opt(struct diff_option
        int argcount = 1;
  
        if (!skip_prefix(arg, "--stat", &arg))
 -              die("BUG: stat option does not begin with --stat: %s", arg);
 +              BUG("stat option does not begin with --stat: %s", arg);
        end = (char *)arg;
  
        switch (*arg) {
@@@ -4865,12 -4788,6 +4866,12 @@@ int diff_opt_parse(struct diff_options 
                 options->output_format |= DIFF_FORMAT_DIFFSTAT;
        } else if (!strcmp(arg, "--no-compact-summary"))
                 options->flags.stat_with_summary = 0;
 +      else if (skip_prefix(arg, "--output-indicator-new=", &arg))
 +              options->output_indicators[OUTPUT_INDICATOR_NEW] = arg[0];
 +      else if (skip_prefix(arg, "--output-indicator-old=", &arg))
 +              options->output_indicators[OUTPUT_INDICATOR_OLD] = arg[0];
 +      else if (skip_prefix(arg, "--output-indicator-context=", &arg))
 +              options->output_indicators[OUTPUT_INDICATOR_CONTEXT] = arg[0];
  
        /* renames options */
        else if (starts_with(arg, "-B") ||
                options->abbrev = strtoul(arg, NULL, 10);
                if (options->abbrev < MINIMUM_ABBREV)
                        options->abbrev = MINIMUM_ABBREV;
 -              else if (40 < options->abbrev)
 -                      options->abbrev = 40;
 +              else if (the_hash_algo->hexsz < options->abbrev)
 +                      options->abbrev = the_hash_algo->hexsz;
        }
        else if ((argcount = parse_long_opt("src-prefix", av, &optarg))) {
                options->a_prefix = optarg;
@@@ -5240,7 -5157,7 +5241,7 @@@ const char *diff_aligned_abbrev(const s
        const char *abbrev;
  
        /* Do we want all 40 hex characters? */
 -      if (len == GIT_SHA1_HEXSZ)
 +      if (len == the_hash_algo->hexsz)
                return oid_to_hex(oid);
  
        /* An abbreviated value is fine, possibly followed by an ellipsis. */
         * the automatic sizing is supposed to give abblen that ensures
         * uniqueness across all objects (statistically speaking).
         */
 -      if (abblen < GIT_SHA1_HEXSZ - 3) {
 +      if (abblen < the_hash_algo->hexsz - 3) {
                static char hex[GIT_MAX_HEXSZ + 1];
                if (len < abblen && abblen <= len + 2)
                        xsnprintf(hex, sizeof(hex), "%s%.*s", abbrev, len+3-abblen, "..");
@@@ -5342,7 -5259,7 +5343,7 @@@ int diff_unmodified_pair(struct diff_fi
         * dealing with a change.
         */
        if (one->oid_valid && two->oid_valid &&
 -          !oidcmp(&one->oid, &two->oid) &&
 +          oideq(&one->oid, &two->oid) &&
            !one->dirty_submodule && !two->dirty_submodule)
                return 1; /* no change */
        if (!one->oid_valid && !two->oid_valid)
@@@ -5476,7 -5393,7 +5477,7 @@@ static void diff_resolve_rename_copy(vo
                        else
                                p->status = DIFF_STATUS_RENAMED;
                }
 -              else if (oidcmp(&p->one->oid, &p->two->oid) ||
 +              else if (!oideq(&p->one->oid, &p->two->oid) ||
                         p->one->mode != p->two->mode ||
                         p->one->dirty_submodule ||
                         p->two->dirty_submodule ||
@@@ -5811,7 -5728,7 +5812,7 @@@ static void diff_flush_patch_all_file_p
        struct diff_queue_struct *q = &diff_queued_diff;
  
        if (WSEH_NEW & WS_RULE_MASK)
 -              die("BUG: WS rules bit mask overlaps with diff symbol flags");
 +              BUG("WS rules bit mask overlaps with diff symbol flags");
  
        if (o->color_moved)
                o->emitted_symbols = &esm;
@@@ -6347,7 -6264,7 +6348,7 @@@ size_t fill_textconv(struct userdiff_dr
        }
  
        if (!driver->textconv)
 -              die("BUG: fill_textconv called with non-textconv driver");
 +              BUG("fill_textconv called with non-textconv driver");
  
        if (driver->textconv_cache && df->oid_valid) {
                *outbuf = notes_cache_get(driver->textconv_cache,