diff options
Diffstat (limited to 'diff.c')
-rw-r--r-- | diff.c | 857 |
1 files changed, 599 insertions, 258 deletions
@@ -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" @@ -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 @@ -35,6 +37,7 @@ static int diff_rename_limit_default = 400; static int diff_suppress_blank_empty; static int diff_use_color_default = -1; static int diff_color_moved_default; +static int diff_color_moved_ws_default; static int diff_context_default = 3; static int diff_interhunk_context_default; static const char *diff_word_regex_cfg; @@ -67,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) @@ -74,41 +108,13 @@ 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, @@ -177,7 +183,7 @@ static int parse_submodule_params(struct diff_options *options, const char *valu 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; @@ -271,14 +277,54 @@ static int parse_color_moved(const char *arg) return COLOR_MOVED_NO; else if (!strcmp(arg, "plain")) return COLOR_MOVED_PLAIN; + else if (!strcmp(arg, "blocks")) + return COLOR_MOVED_BLOCKS; else if (!strcmp(arg, "zebra")) 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', '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) +{ + int ret = 0; + struct string_list l = STRING_LIST_INIT_DUP; + struct string_list_item *i; + + string_list_split(&l, arg, ',', -1); + + for_each_string_list_item(i, &l) { + struct strbuf sb = STRBUF_INIT; + strbuf_addstr(&sb, i->string); + strbuf_trim(&sb); + + if (!strcmp(sb.buf, "ignore-space-change")) + ret |= XDF_IGNORE_WHITESPACE_CHANGE; + else if (!strcmp(sb.buf, "ignore-space-at-eol")) + ret |= XDF_IGNORE_WHITESPACE_AT_EOL; + else if (!strcmp(sb.buf, "ignore-all-space")) + ret |= XDF_IGNORE_WHITESPACE; + else if (!strcmp(sb.buf, "allow-indentation-change")) + ret |= COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE; + else + error(_("ignoring unknown color-moved-ws mode '%s'"), sb.buf); + + strbuf_release(&sb); + } + + if ((ret & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) && + (ret & XDF_WHITESPACE_FLAGS)) + die(_("color-moved-ws: allow-indentation-change cannot be combined with other white space modes")); + + string_list_clear(&l, 0); + + return ret; } int git_diff_ui_config(const char *var, const char *value, void *cb) @@ -294,6 +340,13 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) diff_color_moved_default = cm; return 0; } + if (!strcmp(var, "diff.colormovedws")) { + int cm = parse_color_moved_ws(value); + if (cm < 0) + return -1; + diff_color_moved_ws_default = cm; + return 0; + } if (!strcmp(var, "diff.context")) { diff_context_default = git_config_int(var, value); if (diff_context_default < 0) @@ -501,14 +554,15 @@ static int count_lines(const char *data, int size) return count; } -static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) +static int fill_mmfile(struct repository *r, mmfile_t *mf, + struct diff_filespec *one) { if (!DIFF_FILE_VALID(one)) { mf->ptr = (char *)""; /* does not matter */ mf->size = 0; return 0; } - else if (diff_populate_filespec(one, 0)) + else if (diff_populate_filespec(r, one, 0)) return -1; mf->ptr = one->data; @@ -517,11 +571,12 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one) } /* like fill_mmfile, but only for size, so we can avoid retrieving blob */ -static unsigned long diff_filespec_size(struct diff_filespec *one) +static unsigned long diff_filespec_size(struct repository *r, + struct diff_filespec *one) { if (!DIFF_FILE_VALID(one)) return 0; - diff_populate_filespec(one, CHECK_SIZE_ONLY); + diff_populate_filespec(r, one, CHECK_SIZE_ONLY); return one->size; } @@ -570,37 +625,55 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2, 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) @@ -610,7 +683,7 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res 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 { @@ -707,14 +780,126 @@ struct moved_entry { struct moved_entry *next_line; }; -static int moved_entry_cmp(const struct diff_options *diffopt, - const struct moved_entry *a, - const struct moved_entry *b, +/** + * The struct ws_delta holds white space differences between moved lines, i.e. + * between '+' and '-' lines that have been detected to be a move. + * The string contains the difference in leading white spaces, before the + * rest of the line is compared using the white space config for move + * coloring. The current_longer indicates if the first string in the + * comparision is longer than the second. + */ +struct ws_delta { + char *string; + unsigned int current_longer : 1; +}; +#define WS_DELTA_INIT { NULL, 0 } + +struct moved_block { + struct moved_entry *match; + struct ws_delta wsd; +}; + +static void moved_block_clear(struct moved_block *b) +{ + FREE_AND_NULL(b->wsd.string); + b->match = NULL; +} + +static int compute_ws_delta(const struct emitted_diff_symbol *a, + const struct emitted_diff_symbol *b, + struct ws_delta *out) +{ + const struct emitted_diff_symbol *longer = a->len > b->len ? a : b; + const struct emitted_diff_symbol *shorter = a->len > b->len ? b : a; + int d = longer->len - shorter->len; + + if (strncmp(longer->line + d, shorter->line, shorter->len)) + return 0; + + out->string = xmemdupz(longer->line, d); + out->current_longer = (a == longer); + + return 1; +} + +static int cmp_in_block_with_wsd(const struct diff_options *o, + const struct moved_entry *cur, + const struct moved_entry *match, + struct moved_block *pmb, + int n) +{ + struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n]; + int al = cur->es->len, cl = l->len; + const char *a = cur->es->line, + *b = match->es->line, + *c = l->line; + + int wslen; + + /* + * We need to check if 'cur' is equal to 'match'. + * As those are from the same (+/-) side, we do not need to adjust for + * indent changes. However these were found using fuzzy matching + * so we do have to check if they are equal. + */ + if (strcmp(a, b)) + return 1; + + if (!pmb->wsd.string) + /* + * The white space delta is not active? This can happen + * when we exit early in this function. + */ + return 1; + + /* + * The indent changes of the block are known and stored in + * pmb->wsd; however we need to check if the indent changes of the + * current line are still the same as before. + * + * To do so we need to compare 'l' to 'cur', adjusting the + * one of them for the white spaces, depending which was longer. + */ + + wslen = strlen(pmb->wsd.string); + if (pmb->wsd.current_longer) { + c += wslen; + cl -= wslen; + } else { + a += wslen; + al -= wslen; + } + + if (al != cl || memcmp(a, c, al)) + return 1; + + return 0; +} + +static int moved_entry_cmp(const void *hashmap_cmp_fn_data, + const void *entry, + const void *entry_or_key, const void *keydata) { + const struct diff_options *diffopt = hashmap_cmp_fn_data; + const struct moved_entry *a = entry; + const struct moved_entry *b = entry_or_key; + unsigned flags = diffopt->color_moved_ws_handling + & XDF_WHITESPACE_FLAGS; + + if (diffopt->color_moved_ws_handling & + COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) + /* + * As there is not specific white space config given, + * we'd need to check for a new block, so ignore all + * white space. The setup of the white space + * configuration for the next block is done else where + */ + flags |= XDF_IGNORE_WHITESPACE; + return !xdiff_compare_lines(a->es->line, a->es->len, b->es->line, b->es->len, - diffopt->xdl_opts); + flags); } static struct moved_entry *prepare_entry(struct diff_options *o, @@ -722,8 +907,9 @@ static struct moved_entry *prepare_entry(struct diff_options *o, { struct moved_entry *ret = xmalloc(sizeof(*ret)); struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no]; + unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS; - ret->ent.hash = xdiff_hash_string(l->line, l->len, o->xdl_opts); + ret->ent.hash = xdiff_hash_string(l->line, l->len, flags); ret->es = l; ret->next_line = NULL; @@ -762,24 +948,77 @@ static void add_lines_to_move_detection(struct diff_options *o, } } -static int shrink_potential_moved_blocks(struct moved_entry **pmb, +static void pmb_advance_or_null(struct diff_options *o, + struct moved_entry *match, + struct hashmap *hm, + struct moved_block *pmb, + int pmb_nr) +{ + int i; + for (i = 0; i < pmb_nr; i++) { + struct moved_entry *prev = pmb[i].match; + struct moved_entry *cur = (prev && prev->next_line) ? + prev->next_line : NULL; + if (cur && !hm->cmpfn(o, cur, match, NULL)) { + pmb[i].match = cur; + } else { + pmb[i].match = NULL; + } + } +} + +static void pmb_advance_or_null_multi_match(struct diff_options *o, + struct moved_entry *match, + struct hashmap *hm, + struct moved_block *pmb, + int pmb_nr, int n) +{ + int i; + char *got_match = xcalloc(1, pmb_nr); + + for (; match; match = hashmap_get_next(hm, match)) { + for (i = 0; i < pmb_nr; i++) { + struct moved_entry *prev = pmb[i].match; + struct moved_entry *cur = (prev && prev->next_line) ? + prev->next_line : NULL; + if (!cur) + continue; + if (!cmp_in_block_with_wsd(o, cur, match, &pmb[i], n)) + got_match[i] |= 1; + } + } + + for (i = 0; i < pmb_nr; i++) { + if (got_match[i]) { + /* Advance to the next line */ + pmb[i].match = pmb[i].match->next_line; + } else { + moved_block_clear(&pmb[i]); + } + } + + free(got_match); +} + +static int shrink_potential_moved_blocks(struct moved_block *pmb, int pmb_nr) { int lp, rp; /* Shrink the set of potential block to the remaining running */ for (lp = 0, rp = pmb_nr - 1; lp <= rp;) { - while (lp < pmb_nr && pmb[lp]) + while (lp < pmb_nr && pmb[lp].match) lp++; /* lp points at the first NULL now */ - while (rp > -1 && !pmb[rp]) + while (rp > -1 && !pmb[rp].match) rp--; /* rp points at the last non-NULL */ if (lp < pmb_nr && rp > -1 && lp < rp) { pmb[lp] = pmb[rp]; - pmb[rp] = NULL; + pmb[rp].match = NULL; + pmb[rp].wsd.string = NULL; rp--; lp++; } @@ -826,7 +1065,7 @@ static void mark_color_as_moved(struct diff_options *o, struct hashmap *add_lines, struct hashmap *del_lines) { - struct moved_entry **pmb = NULL; /* potentially moved blocks */ + struct moved_block *pmb = NULL; /* potentially moved blocks */ int pmb_nr = 0, pmb_alloc = 0; int n, flipped_block = 1, block_length = 0; @@ -836,19 +1075,18 @@ static void mark_color_as_moved(struct diff_options *o, struct moved_entry *key; struct moved_entry *match = NULL; struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n]; - int i; switch (l->s) { case DIFF_SYMBOL_PLUS: hm = del_lines; key = prepare_entry(o, n); - match = hashmap_get(hm, key, o); + match = hashmap_get(hm, key, NULL); free(key); break; case DIFF_SYMBOL_MINUS: hm = add_lines; key = prepare_entry(o, n); - match = hashmap_get(hm, key, o); + match = hashmap_get(hm, key, NULL); free(key); break; default: @@ -856,7 +1094,11 @@ static void mark_color_as_moved(struct diff_options *o, } if (!match) { + int i; + adjust_last_block(o, n, block_length); + for(i = 0; i < pmb_nr; i++) + moved_block_clear(&pmb[i]); pmb_nr = 0; block_length = 0; continue; @@ -867,17 +1109,11 @@ static void mark_color_as_moved(struct diff_options *o, if (o->color_moved == COLOR_MOVED_PLAIN) continue; - /* Check any potential block runs, advance each or nullify */ - for (i = 0; i < pmb_nr; i++) { - struct moved_entry *p = pmb[i]; - struct moved_entry *pnext = (p && p->next_line) ? - p->next_line : NULL; - if (pnext && !hm->cmpfn(o, pnext, match, NULL)) { - pmb[i] = p->next_line; - } else { - pmb[i] = NULL; - } - } + if (o->color_moved_ws_handling & + COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) + pmb_advance_or_null_multi_match(o, match, hm, pmb, pmb_nr, n); + else + pmb_advance_or_null(o, match, hm, pmb, pmb_nr); pmb_nr = shrink_potential_moved_blocks(pmb, pmb_nr); @@ -888,7 +1124,15 @@ static void mark_color_as_moved(struct diff_options *o, */ for (; match; match = hashmap_get_next(hm, match)) { ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc); - pmb[pmb_nr++] = match; + if (o->color_moved_ws_handling & + COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) { + if (compute_ws_delta(l, match->es, + &pmb[pmb_nr].wsd)) + pmb[pmb_nr++].match = match; + } else { + pmb[pmb_nr].wsd.string = NULL; + pmb[pmb_nr++].match = match; + } } flipped_block = (flipped_block + 1) % 2; @@ -899,11 +1143,13 @@ static void mark_color_as_moved(struct diff_options *o, block_length++; - if (flipped_block) + if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS) l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT; } adjust_last_block(o, n, block_length); + for(n = 0; n < pmb_nr; n++) + moved_block_clear(&pmb[n]); free(pmb); } @@ -969,11 +1215,13 @@ static void dim_moved_lines(struct diff_options *o) } 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, + int sign_index, const char *line, int len, unsigned ws_rule, int blank_at_eof) { const char *ws = NULL; + int sign = o->output_indicators[sign_index]; if (o->ws_error_highlight & ws_rule) { ws = diff_get_color_opt(o, DIFF_WHITESPACE); @@ -981,14 +1229,17 @@ static void emit_line_ws_markup(struct diff_options *o, 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); } @@ -998,7 +1249,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, 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; @@ -1011,7 +1262,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, 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: @@ -1038,7 +1289,19 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, 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, + OUTPUT_INDICATOR_CONTEXT, line, len, flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0); break; case DIFF_SYMBOL_PLUS: @@ -1065,7 +1328,24 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, 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, + OUTPUT_INDICATOR_NEW, line, len, flags & DIFF_SYMBOL_CONTENT_WS_MASK, flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF); break; @@ -1093,7 +1373,23 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, 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, + OUTPUT_INDICATOR_OLD, line, len, flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0); break; case DIFF_SYMBOL_WORDS_PORCELAIN: @@ -1184,7 +1480,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, fputs(o->stat_sep, o->file); break; default: - die("BUG: unknown diff symbol"); + BUG("unknown diff symbol"); } strbuf_release(&sb); } @@ -1284,6 +1580,7 @@ static void emit_hunk_header(struct emit_callback *ecbdata, 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; @@ -1304,6 +1601,8 @@ static void emit_hunk_header(struct emit_callback *ecbdata, 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); @@ -1343,7 +1642,7 @@ static struct diff_tempfile *claim_diff_tempfile(void) { 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) @@ -1428,12 +1727,12 @@ static void emit_rewrite_diff(const char *name_a, quote_two_c_style(&a_name, a_prefix, name_a, 0); quote_two_c_style(&b_name, b_prefix, name_b, 0); - size_one = fill_textconv(textconv_one, one, &data_one); - size_two = fill_textconv(textconv_two, two, &data_two); + size_one = fill_textconv(o->repo, textconv_one, one, &data_one); + size_two = fill_textconv(o->repo, textconv_two, two, &data_two); memset(&ecbdata, 0, sizeof(ecbdata)); ecbdata.color_diff = want_color(o->use_color); - ecbdata.ws_rule = whitespace_rule(name_b); + ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b); ecbdata.opt = o; if (ecbdata.ws_rule & WS_BLANK_AT_EOF) { mmfile_t mf1, mf2; @@ -1821,23 +2120,25 @@ static void diff_words_flush(struct emit_callback *ecbdata) } } -static void diff_filespec_load_driver(struct diff_filespec *one) +static void diff_filespec_load_driver(struct diff_filespec *one, + struct index_state *istate) { /* Use already-loaded driver */ if (one->driver) return; if (S_ISREG(one->mode)) - one->driver = userdiff_find_by_path(one->path); + one->driver = userdiff_find_by_path(istate, one->path); /* Fallback to default settings */ if (!one->driver) one->driver = userdiff_find_by_name("default"); } -static const char *userdiff_word_regex(struct diff_filespec *one) +static const char *userdiff_word_regex(struct diff_filespec *one, + struct index_state *istate) { - diff_filespec_load_driver(one); + diff_filespec_load_driver(one, istate); return one->driver->word_regex; } @@ -1860,9 +2161,9 @@ static void init_diff_words_data(struct emit_callback *ecbdata, xcalloc(1, sizeof(struct emitted_diff_symbols)); if (!o->word_regex) - o->word_regex = userdiff_word_regex(one); + o->word_regex = userdiff_word_regex(one, o->repo->index); if (!o->word_regex) - o->word_regex = userdiff_word_regex(two); + o->word_regex = userdiff_word_regex(two, o->repo->index); if (!o->word_regex) o->word_regex = diff_word_regex_cfg; if (o->word_regex) { @@ -1871,8 +2172,8 @@ static void init_diff_words_data(struct emit_callback *ecbdata, 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) { @@ -2663,16 +2964,11 @@ static void show_dirstat(struct diff_options *options) 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 @@ -2695,18 +2991,19 @@ 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, + diff_populate_filespec(options->repo, p->one, 0); + diff_populate_filespec(options->repo, p->two, 0); + diffcore_count_changes(options->repo, + p->one, p->two, NULL, NULL, &copied, &added); diff_free_filespec_data(p->one); diff_free_filespec_data(p->two); } else if (DIFF_FILE_VALID(p->one)) { - diff_populate_filespec(p->one, CHECK_SIZE_ONLY); + diff_populate_filespec(options->repo, p->one, CHECK_SIZE_ONLY); copied = added = 0; diff_free_filespec_data(p->one); } else if (DIFF_FILE_VALID(p->two)) { - diff_populate_filespec(p->two, CHECK_SIZE_ONLY); + diff_populate_filespec(options->repo, p->two, CHECK_SIZE_ONLY); copied = 0; added = p->two->size; diff_free_filespec_data(p->two); @@ -2719,7 +3016,7 @@ static void show_dirstat(struct diff_options *options) * 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. */ @@ -2980,15 +3277,16 @@ static void emit_binary_diff(struct diff_options *o, emit_binary_diff_body(o, two, one); } -int diff_filespec_is_binary(struct diff_filespec *one) +int diff_filespec_is_binary(struct repository *r, + struct diff_filespec *one) { if (one->is_binary == -1) { - diff_filespec_load_driver(one); + diff_filespec_load_driver(one, r->index); if (one->driver->binary != -1) one->is_binary = one->driver->binary; else { if (!one->data && DIFF_FILE_VALID(one)) - diff_populate_filespec(one, CHECK_BINARY); + diff_populate_filespec(r, one, CHECK_BINARY); if (one->is_binary == -1 && one->data) one->is_binary = buffer_is_binary(one->data, one->size); @@ -2999,9 +3297,10 @@ int diff_filespec_is_binary(struct diff_filespec *one) return one->is_binary; } -static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one) +static const struct userdiff_funcname * +diff_funcname_pattern(struct diff_options *o, struct diff_filespec *one) { - diff_filespec_load_driver(one); + diff_filespec_load_driver(one, o->repo->index); return one->driver->funcname.pattern ? &one->driver->funcname : NULL; } @@ -3013,12 +3312,13 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const options->b_prefix = b; } -struct userdiff_driver *get_textconv(struct diff_filespec *one) +struct userdiff_driver *get_textconv(struct index_state *istate, + struct diff_filespec *one) { if (!DIFF_FILE_VALID(one)) return NULL; - diff_filespec_load_driver(one); + diff_filespec_load_driver(one, istate); return userdiff_get_textconv(one->driver); } @@ -3068,8 +3368,8 @@ static void builtin_diff(const char *name_a, } if (o->flags.allow_textconv) { - textconv_one = get_textconv(one); - textconv_two = get_textconv(two); + textconv_one = get_textconv(o->repo->index, one); + textconv_two = get_textconv(o->repo->index, two); } /* Never use a non-valid filename anywhere if at all possible */ @@ -3110,13 +3410,13 @@ static void builtin_diff(const char *name_a, if ((one->mode ^ two->mode) & S_IFMT) goto free_ab_and_return; if (complete_rewrite && - (textconv_one || !diff_filespec_is_binary(one)) && - (textconv_two || !diff_filespec_is_binary(two))) { + (textconv_one || !diff_filespec_is_binary(o->repo, one)) && + (textconv_two || !diff_filespec_is_binary(o->repo, two))) { emit_diff_symbol(o, DIFF_SYMBOL_HEADER, header.buf, header.len, 0); strbuf_reset(&header); emit_rewrite_diff(name_a, name_b, one, two, - textconv_one, textconv_two, o); + textconv_one, textconv_two, o); o->found_changes = 1; goto free_ab_and_return; } @@ -3128,13 +3428,13 @@ static void builtin_diff(const char *name_a, strbuf_reset(&header); goto free_ab_and_return; } else if (!o->flags.text && - ( (!textconv_one && diff_filespec_is_binary(one)) || - (!textconv_two && diff_filespec_is_binary(two)) )) { + ( (!textconv_one && diff_filespec_is_binary(o->repo, one)) || + (!textconv_two && diff_filespec_is_binary(o->repo, two)) )) { struct strbuf sb = STRBUF_INIT; 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, @@ -3150,7 +3450,8 @@ static void builtin_diff(const char *name_a, strbuf_release(&sb); goto free_ab_and_return; } - if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) + if (fill_mmfile(o->repo, &mf1, one) < 0 || + fill_mmfile(o->repo, &mf2, two) < 0) die("unable to read files to diff"); /* Quite common confusing case */ if (mf1.size == mf2.size && @@ -3187,23 +3488,26 @@ static void builtin_diff(const char *name_a, strbuf_reset(&header); } - mf1.size = fill_textconv(textconv_one, one, &mf1.ptr); - mf2.size = fill_textconv(textconv_two, two, &mf2.ptr); + mf1.size = fill_textconv(o->repo, textconv_one, one, &mf1.ptr); + mf2.size = fill_textconv(o->repo, textconv_two, two, &mf2.ptr); - pe = diff_funcname_pattern(one); + pe = diff_funcname_pattern(o, one); if (!pe) - pe = diff_funcname_pattern(two); + pe = diff_funcname_pattern(o, two); 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); + ecbdata.ws_rule = whitespace_rule(o->repo->index, 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; @@ -3296,22 +3600,23 @@ static void builtin_diffstat(const char *name_a, const char *name_b, 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)) { + if (diff_filespec_is_binary(o->repo, one) || + diff_filespec_is_binary(o->repo, two)) { data->is_binary = 1; if (same_contents) { data->added = 0; data->deleted = 0; } else { - data->added = diff_filespec_size(two); - data->deleted = diff_filespec_size(one); + data->added = diff_filespec_size(o->repo, two); + data->deleted = diff_filespec_size(o->repo, one); } } else if (complete_rewrite) { - diff_populate_filespec(one, 0); - diff_populate_filespec(two, 0); + diff_populate_filespec(o->repo, one, 0); + diff_populate_filespec(o->repo, two, 0); data->deleted = count_lines(one->data, one->size); data->added = count_lines(two->data, two->size); } @@ -3321,7 +3626,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b, xpparam_t xpp; xdemitconf_t xecfg; - if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) + if (fill_mmfile(o->repo, &mf1, one) < 0 || + fill_mmfile(o->repo, &mf2, two) < 0) die("unable to read files to diff"); memset(&xpp, 0, sizeof(xpp)); @@ -3356,10 +3662,11 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, data.filename = name_b ? name_b : name_a; data.lineno = 0; data.o = o; - data.ws_rule = whitespace_rule(attr_path); - data.conflict_marker_size = ll_merge_marker_size(attr_path); + data.ws_rule = whitespace_rule(o->repo->index, attr_path); + data.conflict_marker_size = ll_merge_marker_size(o->repo->index, attr_path); - if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) + if (fill_mmfile(o->repo, &mf1, one) < 0 || + fill_mmfile(o->repo, &mf2, two) < 0) die("unable to read files to diff"); /* @@ -3368,7 +3675,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, * introduced changes, and as long as the "new" side is text, we * can and should check what it introduces. */ - if (diff_filespec_is_binary(two)) + if (diff_filespec_is_binary(o->repo, two)) goto free_and_return; else { /* Crazy xdl interfaces.. */ @@ -3441,7 +3748,10 @@ void fill_filespec(struct diff_filespec *spec, const struct object_id *oid, * the work tree has that object contents, return true, so that * prepare_temp_file() does not have to inflate and extract. */ -static int reuse_worktree_file(const char *name, const struct object_id *oid, int want_file) +static int reuse_worktree_file(struct index_state *istate, + const char *name, + const struct object_id *oid, + int want_file) { const struct cache_entry *ce; struct stat st; @@ -3460,7 +3770,7 @@ static int reuse_worktree_file(const char *name, const struct object_id *oid, in * by diff-cache --cached, which does read the cache before * calling us. */ - if (!active_cache) + if (!istate->cache) return 0; /* We want to avoid the working directory if our caller @@ -3472,27 +3782,27 @@ static int reuse_worktree_file(const char *name, const struct object_id *oid, in * 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; /* * Similarly, if we'd have to convert the file contents anyway, that * makes the optimization not worthwhile. */ - if (!want_file && would_convert_to_git(&the_index, name)) + if (!want_file && would_convert_to_git(istate, name)) return 0; len = strlen(name); - pos = cache_name_pos(name, len); + pos = index_name_pos(istate, name, len); if (pos < 0) return 0; - ce = active_cache[pos]; + ce = istate->cache[pos]; /* * 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; /* @@ -3506,7 +3816,7 @@ static int reuse_worktree_file(const char *name, const struct object_id *oid, in * If ce matches the file in the work tree, we can reuse it. */ if (ce_uptodate(ce) || - (!lstat(name, &st) && !ce_match_stat(ce, &st, 0))) + (!lstat(name, &st) && !ie_match_stat(istate, ce, &st, 0))) return 1; return 0; @@ -3539,7 +3849,9 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only) * grab the data for the blob (or file) for our own in-core comparison. * diff_filespec has data and size fields for this purpose. */ -int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) +int diff_populate_filespec(struct repository *r, + struct diff_filespec *s, + unsigned int flags) { int size_only = flags & CHECK_SIZE_ONLY; int err = 0; @@ -3566,7 +3878,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) return diff_populate_gitlink(s, size_only); if (!s->oid_valid || - reuse_worktree_file(s->path, &s->oid, 0)) { + reuse_worktree_file(r->index, s->path, &s->oid, 0)) { struct strbuf buf = STRBUF_INIT; struct stat st; int fd; @@ -3599,7 +3911,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) * point if the path requires us to run the content * conversion. */ - if (size_only && !would_convert_to_git(&the_index, s->path)) + if (size_only && !would_convert_to_git(r->index, s->path)) return 0; /* @@ -3626,7 +3938,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) /* * Convert from working tree format to canonical git format */ - if (convert_to_git(&the_index, s->path, s->data, s->size, &buf, conv_flags)) { + if (convert_to_git(r->index, s->path, s->data, s->size, &buf, conv_flags)) { size_t size = 0; munmap(s->data, s->size); s->should_munmap = 0; @@ -3638,7 +3950,7 @@ 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->oid.hash, &s->size); + type = oid_object_info(r, &s->oid, &s->size); if (type < 0) die("unable to read %s", oid_to_hex(&s->oid)); @@ -3649,7 +3961,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) return 0; } } - s->data = read_sha1_file(s->oid.hash, &type, &s->size); + s->data = read_object_file(&s->oid, &type, &s->size); if (!s->data) die("unable to read %s", oid_to_hex(&s->oid)); s->should_free = 1; @@ -3676,7 +3988,8 @@ void diff_free_filespec_data(struct diff_filespec *s) FREE_AND_NULL(s->cnt_data); } -static void prep_temp_blob(const char *path, struct diff_tempfile *temp, +static void prep_temp_blob(struct index_state *istate, + const char *path, struct diff_tempfile *temp, void *blob, unsigned long size, const struct object_id *oid, @@ -3694,7 +4007,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp, 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(istate, path, (const char *)blob, (size_t)size, &buf)) { blob = buf.buf; size = buf.len; @@ -3710,8 +4023,9 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp, free(path_dup); } -static struct diff_tempfile *prepare_temp_file(const char *name, - struct diff_filespec *one) +static struct diff_tempfile *prepare_temp_file(struct repository *r, + const char *name, + struct diff_filespec *one) { struct diff_tempfile *temp = claim_diff_tempfile(); @@ -3728,7 +4042,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name, if (!S_ISGITLINK(one->mode) && (!one->oid_valid || - reuse_worktree_file(name, &one->oid, 1))) { + reuse_worktree_file(r->index, name, &one->oid, 1))) { struct stat st; if (lstat(name, &st) < 0) { if (errno == ENOENT) @@ -3739,7 +4053,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name, struct strbuf sb = STRBUF_INIT; if (strbuf_readlink(&sb, name, st.st_size) < 0) die_errno("readlink(%s)", name); - prep_temp_blob(name, temp, sb.buf, sb.len, + prep_temp_blob(r->index, name, temp, sb.buf, sb.len, (one->oid_valid ? &one->oid : &null_oid), (one->oid_valid ? @@ -3764,19 +4078,21 @@ static struct diff_tempfile *prepare_temp_file(const char *name, return temp; } else { - if (diff_populate_filespec(one, 0)) + if (diff_populate_filespec(r, one, 0)) die("cannot read data blob for %s", one->path); - prep_temp_blob(name, temp, one->data, one->size, + prep_temp_blob(r->index, name, temp, + one->data, one->size, &one->oid, one->mode); } return temp; } -static void add_external_diff_name(struct argv_array *argv, +static void add_external_diff_name(struct repository *r, + struct argv_array *argv, const char *name, struct diff_filespec *df) { - struct diff_tempfile *temp = prepare_temp_file(name, df); + struct diff_tempfile *temp = prepare_temp_file(r, name, df); argv_array_push(argv, temp->name); argv_array_push(argv, temp->hex); argv_array_push(argv, temp->mode); @@ -3805,11 +4121,11 @@ static void run_external_diff(const char *pgm, argv_array_push(&argv, name); if (one && two) { - add_external_diff_name(&argv, name, one); + add_external_diff_name(o->repo, &argv, name, one); if (!other) - add_external_diff_name(&argv, name, two); + add_external_diff_name(o->repo, &argv, name, two); else { - add_external_diff_name(&argv, other, two); + add_external_diff_name(o->repo, &argv, other, two); argv_array_push(&argv, other); argv_array_push(&argv, xfrm_msg); } @@ -3834,13 +4150,13 @@ static int similarity_index(struct diff_filepair *p) static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev) { if (startup_info->have_repository) - return find_unique_abbrev(oid->hash, abbrev); + return find_unique_abbrev(oid, abbrev); else { 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; @@ -3896,14 +4212,17 @@ static void fill_metainfo(struct strbuf *msg, 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; + if ((!fill_mmfile(o->repo, &mf, one) && + diff_filespec_is_binary(o->repo, one)) || + (!fill_mmfile(o->repo, &mf, two) && + diff_filespec_is_binary(o->repo, two))) + abbrev = hexsz; } strbuf_addf(msg, "%s%sindex %s..%s", line_prefix, set, diff_abbrev_oid(&one->oid, abbrev), @@ -3930,7 +4249,9 @@ static void run_diff_cmd(const char *pgm, if (o->flags.allow_external) { - struct userdiff_driver *drv = userdiff_find_by_path(attr_path); + struct userdiff_driver *drv; + + drv = userdiff_find_by_path(o->repo->index, attr_path); if (drv && drv->external) pgm = drv->external; } @@ -3959,7 +4280,7 @@ static void run_diff_cmd(const char *pgm, fprintf(o->file, "* Unmerged path %s\n", name); } -static void diff_fill_oid_info(struct diff_filespec *one) +static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *istate) { if (DIFF_FILE_VALID(one)) { if (!one->oid_valid) { @@ -3970,7 +4291,7 @@ static void diff_fill_oid_info(struct diff_filespec *one) } if (lstat(one->path, &st) < 0) die_errno("stat '%s'", one->path); - if (index_path(&one->oid, one->path, &st, 0)) + if (index_path(istate, &one->oid, one->path, &st, 0)) die("cannot hash %s", one->path); } } @@ -3981,12 +4302,12 @@ static void diff_fill_oid_info(struct diff_filespec *one) static void strip_prefix(int prefix_length, const char **namep, const char **otherp) { /* Strip the prefix but do not molest /dev/null and absolute paths */ - if (*namep && **namep != '/') { + if (*namep && !is_absolute_path(*namep)) { *namep += prefix_length; if (**namep == '/') ++*namep; } - if (*otherp && **otherp != '/') { + if (*otherp && !is_absolute_path(*otherp)) { *otherp += prefix_length; if (**otherp == '/') ++*otherp; @@ -4018,8 +4339,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) return; } - diff_fill_oid_info(one); - diff_fill_oid_info(two); + diff_fill_oid_info(one, o->repo->index); + diff_fill_oid_info(two, o->repo->index); if (!pgm && DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) && @@ -4030,7 +4351,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) */ struct diff_filespec *null = alloc_filespec(two->path); run_diff_cmd(NULL, name, other, attr_path, - one, null, &msg, o, p); + one, null, &msg, + o, p); free(null); strbuf_release(&msg); @@ -4054,7 +4376,8 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o, if (DIFF_PAIR_UNMERGED(p)) { /* unmerged */ - builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, o, p); + builtin_diffstat(p->one->path, NULL, NULL, NULL, + diffstat, o, p); return; } @@ -4064,10 +4387,11 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o, if (o->prefix_length) strip_prefix(o->prefix_length, &name, &other); - diff_fill_oid_info(p->one); - diff_fill_oid_info(p->two); + diff_fill_oid_info(p->one, o->repo->index); + diff_fill_oid_info(p->two, o->repo->index); - builtin_diffstat(name, other, p->one, p->two, diffstat, o, p); + builtin_diffstat(name, other, p->one, p->two, + diffstat, o, p); } static void run_checkdiff(struct diff_filepair *p, struct diff_options *o) @@ -4088,18 +4412,22 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o) if (o->prefix_length) strip_prefix(o->prefix_length, &name, &other); - diff_fill_oid_info(p->one); - diff_fill_oid_info(p->two); + diff_fill_oid_info(p->one, o->repo->index); + diff_fill_oid_info(p->two, o->repo->index); builtin_checkdiff(name, other, attr_path, p->one, p->two, o); } -void diff_setup(struct diff_options *options) +void repo_diff_setup(struct repository *r, struct diff_options *options) { memcpy(options, &default_diff_options, sizeof(*options)); options->file = stdout; + options->repo = r; + 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; @@ -4130,6 +4458,7 @@ void diff_setup(struct diff_options *options) } options->color_moved = diff_color_moved_default; + options->color_moved_ws_handling = diff_color_moved_ws_default; } void diff_setup_done(struct diff_options *options) @@ -4138,6 +4467,11 @@ void diff_setup_done(struct diff_options *options) 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); @@ -4208,18 +4542,8 @@ void diff_setup_done(struct diff_options *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 @@ -4334,7 +4658,7 @@ static int stat_opt(struct diff_options *options, const char **av) 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) { @@ -4581,6 +4905,12 @@ int diff_opt_parse(struct diff_options *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") || @@ -4704,6 +5034,8 @@ int diff_opt_parse(struct diff_options *options, if (cm < 0) die("bad --color-moved argument: %s", arg); options->color_moved = cm; + } else if (skip_prefix(arg, "--color-moved-ws=", &arg)) { + options->color_moved_ws_handling = parse_color_moved_ws(arg); } else if (skip_to_optional_arg_default(arg, "--color-words", &options->word_regex, NULL)) { options->use_color = 1; options->word_diff = DIFF_WORDS_COLOR; @@ -4797,8 +5129,8 @@ int diff_opt_parse(struct diff_options *options, 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; @@ -4948,7 +5280,7 @@ const char *diff_aligned_abbrev(const struct object_id *oid, int len) 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. */ @@ -4978,7 +5310,7 @@ const char *diff_aligned_abbrev(const struct object_id *oid, int len) * 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, ".."); @@ -5050,7 +5382,7 @@ int diff_unmodified_pair(struct diff_filepair *p) * 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) @@ -5184,7 +5516,7 @@ static void diff_resolve_rename_copy(void) 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 || @@ -5390,8 +5722,8 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid if (DIFF_PAIR_UNMERGED(p)) continue; - diff_fill_oid_info(p->one); - diff_fill_oid_info(p->two); + diff_fill_oid_info(p->one, options->repo->index); + diff_fill_oid_info(p->two, options->repo->index); len1 = remove_space(p->one->path, strlen(p->one->path)); len2 = remove_space(p->two->path, strlen(p->two->path)); @@ -5423,12 +5755,12 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid if (diff_header_only) continue; - if (fill_mmfile(&mf1, p->one) < 0 || - fill_mmfile(&mf2, p->two) < 0) + if (fill_mmfile(options->repo, &mf1, p->one) < 0 || + fill_mmfile(options->repo, &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)) { + if (diff_filespec_is_binary(options->repo, p->one) || + diff_filespec_is_binary(options->repo, p->two)) { git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid), GIT_SHA1_HEXSZ); git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid), @@ -5519,7 +5851,7 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o) 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; @@ -5534,18 +5866,20 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o) if (o->color_moved) { struct hashmap add_lines, del_lines; - hashmap_init(&del_lines, - (hashmap_cmp_fn)moved_entry_cmp, o, 0); - hashmap_init(&add_lines, - (hashmap_cmp_fn)moved_entry_cmp, o, 0); + if (o->color_moved_ws_handling & + COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) + o->color_moved_ws_handling |= XDF_IGNORE_WHITESPACE; + + hashmap_init(&del_lines, moved_entry_cmp, o, 0); + hashmap_init(&add_lines, moved_entry_cmp, o, 0); add_lines_to_move_detection(o, &add_lines, &del_lines); mark_color_as_moved(o, &add_lines, &del_lines); if (o->color_moved == COLOR_MOVED_ZEBRA_DIM) dim_moved_lines(o); - hashmap_free(&add_lines, 0); - hashmap_free(&del_lines, 0); + hashmap_free(&add_lines, 1); + hashmap_free(&del_lines, 1); } for (i = 0; i < esm.nr; i++) @@ -5729,19 +6063,21 @@ static void diffcore_apply_filter(struct diff_options *options) } /* Check whether two filespecs with the same mode and size are identical */ -static int diff_filespec_is_identical(struct diff_filespec *one, +static int diff_filespec_is_identical(struct repository *r, + struct diff_filespec *one, struct diff_filespec *two) { if (S_ISGITLINK(one->mode)) return 0; - if (diff_populate_filespec(one, 0)) + if (diff_populate_filespec(r, one, 0)) return 0; - if (diff_populate_filespec(two, 0)) + if (diff_populate_filespec(r, two, 0)) return 0; return !memcmp(one->data, two->data, one->size); } -static int diff_filespec_check_stat_unmatch(struct diff_filepair *p) +static int diff_filespec_check_stat_unmatch(struct repository *r, + struct diff_filepair *p) { if (p->done_skip_stat_unmatch) return p->skip_stat_unmatch_result; @@ -5765,10 +6101,10 @@ static int diff_filespec_check_stat_unmatch(struct diff_filepair *p) !DIFF_FILE_VALID(p->two) || (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) || + diff_populate_filespec(r, p->one, CHECK_SIZE_ONLY) || + diff_populate_filespec(r, p->two, CHECK_SIZE_ONLY) || (p->one->size != p->two->size) || - !diff_filespec_is_identical(p->one, p->two)) /* (2) */ + !diff_filespec_is_identical(r, p->one, p->two)) /* (2) */ p->skip_stat_unmatch_result = 1; return p->skip_stat_unmatch_result; } @@ -5783,7 +6119,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; - if (diff_filespec_check_stat_unmatch(p)) + if (diff_filespec_check_stat_unmatch(diffopt->repo, p)) diff_q(&outq, p); else { /* @@ -5825,7 +6161,8 @@ void diffcore_std(struct diff_options *options) if (!options->found_follow) { /* See try_to_follow_renames() in tree-diff.c */ if (options->break_opt != -1) - diffcore_break(options->break_opt); + diffcore_break(options->repo, + options->break_opt); if (options->detect_rename) diffcore_rename(options); if (options->break_opt != -1) @@ -5976,7 +6313,7 @@ void diff_change(struct diff_options *options, return; if (options->flags.quick && options->skip_stat_unmatch && - !diff_filespec_check_stat_unmatch(p)) + !diff_filespec_check_stat_unmatch(options->repo, p)) return; options->flags.has_changes = 1; @@ -5998,8 +6335,10 @@ struct diff_filepair *diff_unmerge(struct diff_options *options, const char *pat return pair; } -static char *run_textconv(const char *pgm, struct diff_filespec *spec, - size_t *outsize) +static char *run_textconv(struct repository *r, + const char *pgm, + struct diff_filespec *spec, + size_t *outsize) { struct diff_tempfile *temp; const char *argv[3]; @@ -6008,7 +6347,7 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec, struct strbuf buf = STRBUF_INIT; int err = 0; - temp = prepare_temp_file(spec->path, spec); + temp = prepare_temp_file(r, spec->path, spec); *arg++ = pgm; *arg++ = temp->name; *arg = NULL; @@ -6035,7 +6374,8 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec, return strbuf_detach(&buf, outsize); } -size_t fill_textconv(struct userdiff_driver *driver, +size_t fill_textconv(struct repository *r, + struct userdiff_driver *driver, struct diff_filespec *df, char **outbuf) { @@ -6046,14 +6386,14 @@ size_t fill_textconv(struct userdiff_driver *driver, *outbuf = ""; return 0; } - if (diff_populate_filespec(df, 0)) + if (diff_populate_filespec(r, df, 0)) die("unable to read files to diff"); *outbuf = df->data; return df->size; } 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, @@ -6063,7 +6403,7 @@ size_t fill_textconv(struct userdiff_driver *driver, return size; } - *outbuf = run_textconv(driver->textconv, df, &size); + *outbuf = run_textconv(r, driver->textconv, df, &size); if (!*outbuf) die("unable to read files to diff"); @@ -6083,7 +6423,8 @@ size_t fill_textconv(struct userdiff_driver *driver, return size; } -int textconv_object(const char *path, +int textconv_object(struct repository *r, + const char *path, unsigned mode, const struct object_id *oid, int oid_valid, @@ -6095,13 +6436,13 @@ int textconv_object(const char *path, df = alloc_filespec(path); fill_filespec(df, oid, oid_valid, mode); - textconv = get_textconv(df); + textconv = get_textconv(r->index, df); if (!textconv) { free_filespec(df); return 0; } - *buf_size = fill_textconv(textconv, df, buf); + *buf_size = fill_textconv(r, textconv, df, buf); free_filespec(df); return 1; } |