diff options
Diffstat (limited to 'diff.c')
-rw-r--r-- | diff.c | 948 |
1 files changed, 578 insertions, 370 deletions
@@ -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,10 +13,12 @@ #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" +#include "argv-array.h" +#include "graph.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -24,10 +27,12 @@ #endif static int diff_detect_rename_default; +static int diff_indent_heuristic; /* experimental */ static int diff_rename_limit_default = 400; static int diff_suppress_blank_empty; static int diff_use_color_default = -1; static int diff_context_default = 3; +static int diff_interhunk_context_default; static const char *diff_word_regex_cfg; static const char *external_diff_cmd_cfg; static const char *diff_order_file_cfg; @@ -38,10 +43,11 @@ static int diff_stat_graph_width; static int diff_dirstat_permille_default = 30; static struct diff_options default_diff_options; static long diff_algorithm; +static unsigned ws_error_highlight_default = WSEH_NEW; static char diff_colors[][COLOR_MAXLEN] = { GIT_COLOR_RESET, - GIT_COLOR_NORMAL, /* PLAIN */ + GIT_COLOR_NORMAL, /* CONTEXT */ GIT_COLOR_BOLD, /* METAINFO */ GIT_COLOR_CYAN, /* FRAGINFO */ GIT_COLOR_RED, /* OLD */ @@ -51,23 +57,28 @@ static char diff_colors[][COLOR_MAXLEN] = { GIT_COLOR_NORMAL, /* FUNCINFO */ }; -static int parse_diff_color_slot(const char *var, int ofs) +static NORETURN void die_want_option(const char *option_name) { - if (!strcasecmp(var+ofs, "plain")) - return DIFF_PLAIN; - if (!strcasecmp(var+ofs, "meta")) + die(_("option '%s' requires a value"), option_name); +} + +static int parse_diff_color_slot(const char *var) +{ + if (!strcasecmp(var, "context") || !strcasecmp(var, "plain")) + return DIFF_CONTEXT; + if (!strcasecmp(var, "meta")) return DIFF_METAINFO; - if (!strcasecmp(var+ofs, "frag")) + if (!strcasecmp(var, "frag")) return DIFF_FRAGINFO; - if (!strcasecmp(var+ofs, "old")) + if (!strcasecmp(var, "old")) return DIFF_FILE_OLD; - if (!strcasecmp(var+ofs, "new")) + if (!strcasecmp(var, "new")) return DIFF_FILE_NEW; - if (!strcasecmp(var+ofs, "commit")) + if (!strcasecmp(var, "commit")) return DIFF_COMMIT; - if (!strcasecmp(var+ofs, "whitespace")) + if (!strcasecmp(var, "whitespace")) return DIFF_WHITESPACE; - if (!strcasecmp(var+ofs, "func")) + if (!strcasecmp(var, "func")) return DIFF_FUNCINFO; return -1; } @@ -128,9 +139,11 @@ static int parse_dirstat_params(struct diff_options *options, const char *params static int parse_submodule_params(struct diff_options *options, const char *value) { if (!strcmp(value, "log")) - DIFF_OPT_SET(options, SUBMODULE_LOG); + options->submodule_format = DIFF_SUBMODULE_LOG; else if (!strcmp(value, "short")) - DIFF_OPT_CLR(options, SUBMODULE_LOG); + options->submodule_format = DIFF_SUBMODULE_SHORT; + else if (!strcmp(value, "diff")) + options->submodule_format = DIFF_SUBMODULE_INLINE_DIFF; else return -1; return 0; @@ -160,12 +173,61 @@ long parse_algorithm_value(const char *value) return -1; } +static int parse_one_token(const char **arg, const char *token) +{ + const char *rest; + if (skip_prefix(*arg, token, &rest) && (!*rest || *rest == ',')) { + *arg = rest; + return 1; + } + return 0; +} + +static int parse_ws_error_highlight(const char *arg) +{ + const char *orig_arg = arg; + unsigned val = 0; + + while (*arg) { + if (parse_one_token(&arg, "none")) + val = 0; + else if (parse_one_token(&arg, "default")) + val = WSEH_NEW; + else if (parse_one_token(&arg, "all")) + val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT; + else if (parse_one_token(&arg, "new")) + val |= WSEH_NEW; + else if (parse_one_token(&arg, "old")) + val |= WSEH_OLD; + else if (parse_one_token(&arg, "context")) + val |= WSEH_CONTEXT; + else { + return -1 - (int)(arg - orig_arg); + } + if (*arg) + arg++; + } + return val; +} + /* * These are to give UI layer defaults. * The core-level commands such as git-diff-files should * 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_heuristic_config(const char *var, const char *value, void *cb) +{ + if (!strcmp(var, "diff.indentheuristic")) + diff_indent_heuristic = git_config_bool(var, value); + return 0; +} + int git_diff_ui_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) { @@ -178,6 +240,12 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) return -1; return 0; } + if (!strcmp(var, "diff.interhunkcontext")) { + diff_interhunk_context_default = git_config_int(var, value); + if (diff_interhunk_context_default < 0) + return -1; + return 0; + } if (!strcmp(var, "diff.renames")) { diff_detect_rename_default = git_config_rename(var, value); return 0; @@ -222,6 +290,17 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) return 0; } + if (git_diff_heuristic_config(var, value, cb) < 0) + return -1; + + if (!strcmp(var, "diff.wserrorhighlight")) { + int val = parse_ws_error_highlight(value); + if (val < 0) + return -1; + ws_error_highlight_default = val; + return 0; + } + if (git_color_config(var, value, cb) < 0) return -1; @@ -230,6 +309,8 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) int git_diff_basic_config(const char *var, const char *value, void *cb) { + const char *name; + if (!strcmp(var, "diff.renamelimit")) { diff_rename_limit_default = git_config_int(var, value); return 0; @@ -238,14 +319,14 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) if (userdiff_config(var, value) < 0) return -1; - if (starts_with(var, "diff.color.") || starts_with(var, "color.diff.")) { - int slot = parse_diff_color_slot(var, 11); + if (skip_prefix(var, "diff.color.", &name) || + skip_prefix(var, "color.diff.", &name)) { + int slot = parse_diff_color_slot(name); if (slot < 0) return 0; if (!value) return config_error_nonbool(var); - color_parse(value, var, diff_colors[slot]); - return 0; + return color_parse(value, diff_colors[slot]); } /* like GNU diff's --suppress-blank-empty option */ @@ -305,11 +386,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); @@ -325,7 +421,6 @@ struct emit_callback { const char **label_path; struct diff_words_data *diff_words; struct diff_options *opt; - int *found_changesp; struct strbuf *header; }; @@ -372,7 +467,7 @@ static unsigned long diff_filespec_size(struct diff_filespec *one) { if (!DIFF_FILE_VALID(one)) return 0; - diff_populate_filespec(one, 1); + diff_populate_filespec(one, CHECK_SIZE_ONLY); return one->size; } @@ -475,30 +570,63 @@ static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line return ws_blank_line(line, len, ecbdata->ws_rule); } -static void emit_add_line(const char *reset, - struct emit_callback *ecbdata, - const char *line, int len) +static void emit_line_checked(const char *reset, + struct emit_callback *ecbdata, + const char *line, int len, + enum color_diff color, + unsigned ws_error_highlight, + char sign) { - const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE); - const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW); + const char *set = diff_get_color(ecbdata->color_diff, color); + const char *ws = NULL; - if (!*ws) - emit_line_0(ecbdata->opt, set, reset, '+', line, len); - else if (new_blank_line_at_eof(ecbdata, line, len)) + if (ecbdata->opt->ws_error_highlight & ws_error_highlight) { + ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE); + if (!*ws) + ws = NULL; + } + + if (!ws) + emit_line_0(ecbdata->opt, set, reset, sign, line, len); + else if (sign == '+' && new_blank_line_at_eof(ecbdata, line, len)) /* Blank line at EOF - paint '+' as well */ - emit_line_0(ecbdata->opt, ws, reset, '+', line, len); + emit_line_0(ecbdata->opt, ws, reset, sign, line, len); else { /* Emit just the prefix, then the rest. */ - emit_line_0(ecbdata->opt, set, reset, '+', "", 0); + emit_line_0(ecbdata->opt, set, reset, sign, "", 0); ws_check_emit(line, len, ecbdata->ws_rule, ecbdata->opt->file, set, reset, ws); } } +static void emit_add_line(const char *reset, + struct emit_callback *ecbdata, + const char *line, int len) +{ + emit_line_checked(reset, ecbdata, line, len, + DIFF_FILE_NEW, WSEH_NEW, '+'); +} + +static void emit_del_line(const char *reset, + struct emit_callback *ecbdata, + const char *line, int len) +{ + emit_line_checked(reset, ecbdata, line, len, + DIFF_FILE_OLD, WSEH_OLD, '-'); +} + +static void emit_context_line(const char *reset, + struct emit_callback *ecbdata, + const char *line, int len) +{ + emit_line_checked(reset, ecbdata, line, len, + DIFF_CONTEXT, WSEH_CONTEXT, ' '); +} + static void emit_hunk_header(struct emit_callback *ecbdata, const char *line, int len) { - const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN); + const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT); 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); @@ -515,15 +643,15 @@ static void emit_hunk_header(struct emit_callback *ecbdata, if (len < 10 || memcmp(line, atat, 2) || !(ep = memmem(line + 2, len - 2, atat, 2))) { - emit_line(ecbdata->opt, plain, reset, line, len); + emit_line(ecbdata->opt, context, reset, line, len); return; } ep += 2; /* skip over @@ */ /* The hunk header in fraginfo color */ - strbuf_add(&msgbuf, frag, strlen(frag)); + strbuf_addstr(&msgbuf, frag); strbuf_add(&msgbuf, line, ep - line); - strbuf_add(&msgbuf, reset, strlen(reset)); + strbuf_addstr(&msgbuf, reset); /* * trailing "\r\n" @@ -537,15 +665,15 @@ static void emit_hunk_header(struct emit_callback *ecbdata, if (*ep != ' ' && *ep != '\t') break; if (ep != cp) { - strbuf_add(&msgbuf, plain, strlen(plain)); + strbuf_addstr(&msgbuf, context); strbuf_add(&msgbuf, cp, ep - cp); - strbuf_add(&msgbuf, reset, strlen(reset)); + strbuf_addstr(&msgbuf, reset); } if (ep < line + len) { - strbuf_add(&msgbuf, func, strlen(func)); + strbuf_addstr(&msgbuf, func); strbuf_add(&msgbuf, ep, line + len - ep); - strbuf_add(&msgbuf, reset, strlen(reset)); + strbuf_addstr(&msgbuf, reset); } strbuf_add(&msgbuf, line + len, org_len - len); @@ -561,25 +689,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) { @@ -600,7 +719,6 @@ static void emit_rewrite_lines(struct emit_callback *ecb, { const char *endp = NULL; static const char *nneof = " No newline at end of file\n"; - const char *old = diff_get_color(ecb->color_diff, DIFF_FILE_OLD); const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET); while (0 < size) { @@ -610,8 +728,7 @@ static void emit_rewrite_lines(struct emit_callback *ecb, len = endp ? (endp - data + 1) : size; if (prefix != '+') { ecb->lno_in_preimage++; - emit_line_0(ecb->opt, old, reset, '-', - data, len); + emit_del_line(reset, ecb, data, len); } else { ecb->lno_in_postimage++; emit_add_line(reset, ecb, data, len); @@ -620,10 +737,10 @@ static void emit_rewrite_lines(struct emit_callback *ecb, data += len; } if (!endp) { - const char *plain = diff_get_color(ecb->color_diff, - DIFF_PLAIN); + const char *context = diff_get_color(ecb->color_diff, + DIFF_CONTEXT); putc('\n', ecb->opt->file); - emit_line_0(ecb->opt, plain, reset, '\\', + emit_line_0(ecb->opt, context, reset, '\\', nneof, strlen(nneof)); } } @@ -671,7 +788,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) { @@ -900,7 +1016,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; @@ -999,8 +1116,9 @@ static void diff_words_show(struct diff_words_data *diff_words) xpp.flags = 0; /* as only the hunk header will be parsed, we need a 0-context */ xecfg.ctxlen = 0; - xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words, - &xpp, &xecfg); + if (xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words, + &xpp, &xecfg)) + die("unable to generate word diff"); free(minus.ptr); free(plus.ptr); if (diff_words->current_plus != diff_words->plus.text.ptr + @@ -1083,7 +1201,7 @@ static void init_diff_words_data(struct emit_callback *ecbdata, struct diff_words_style *st = ecbdata->diff_words->style; st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD); st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW); - st->ctx.color = diff_get_color_opt(o, DIFF_PLAIN); + st->ctx.color = diff_get_color_opt(o, DIFF_CONTEXT); } } @@ -1159,17 +1277,18 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) { struct emit_callback *ecbdata = priv; const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO); - const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN); + const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT); const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET); 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; @@ -1177,9 +1296,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; } @@ -1197,15 +1316,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; } @@ -1230,8 +1341,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, plain, 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 @@ -1242,22 +1353,31 @@ static void fn_out_consume(void *priv, char *line, unsigned long len) line++; len--; } - emit_line(ecbdata->opt, plain, reset, line, len); + emit_line(o, context, reset, line, len); } return; } - if (line[0] != '+') { - const char *color = - diff_get_color(ecbdata->color_diff, - line[0] == '-' ? DIFF_FILE_OLD : DIFF_PLAIN); - ecbdata->lno_in_preimage++; - if (line[0] == ' ') - ecbdata->lno_in_postimage++; - emit_line(ecbdata->opt, color, reset, line, len); - } else { + switch (line[0]) { + case '+': ecbdata->lno_in_postimage++; emit_add_line(reset, ecbdata, line + 1, len - 1); + break; + case '-': + ecbdata->lno_in_preimage++; + emit_del_line(reset, ecbdata, line + 1, len - 1); + break; + case ' ': + ecbdata->lno_in_postimage++; + ecbdata->lno_in_preimage++; + emit_context_line(reset, ecbdata, line + 1, len - 1); + break; + default: + /* incomplete line at the end */ + ecbdata->lno_in_preimage++; + emit_line(o, diff_get_color(ecbdata->color_diff, DIFF_CONTEXT), + reset, line, len); + break; } } @@ -1360,12 +1480,8 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat, const char *name_b) { struct diffstat_file *x; - x = xcalloc(sizeof (*x), 1); - if (diffstat->nr == diffstat->alloc) { - diffstat->alloc = alloc_nr(diffstat->alloc); - diffstat->files = xrealloc(diffstat->files, - diffstat->alloc * sizeof(x)); - } + x = xcalloc(1, sizeof(*x)); + ALLOC_GROW(diffstat->files, diffstat->nr + 1, diffstat->alloc); diffstat->files[diffstat->nr++] = x; if (name_b) { x->from_name = xstrdup(name_a); @@ -1465,20 +1581,12 @@ int print_stat_summary(FILE *fp, int files, int insertions, int deletions) * but nothing about added/removed lines? Is this a bug in Git?"). */ if (insertions || deletions == 0) { - /* - * TRANSLATORS: "+" in (+) is a line addition marker; - * do not translate it. - */ strbuf_addf(&sb, (insertions == 1) ? ", %d insertion(+)" : ", %d insertions(+)", insertions); } if (deletions || insertions == 0) { - /* - * TRANSLATORS: "-" in (-) is a line removal marker; - * do not translate it. - */ strbuf_addf(&sb, (deletions == 1) ? ", %d deletion(-)" : ", %d deletions(-)", deletions); @@ -1575,7 +1683,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) */ if (options->stat_width == -1) - width = term_columns() - options->output_prefix_length; + width = term_columns() - strlen(line_prefix); else width = options->stat_width ? options->stat_width : 80; number_width = decimal_width(max_change) > number_width ? @@ -1883,8 +1991,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; @@ -1913,16 +2021,16 @@ 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); } else if (DIFF_FILE_VALID(p->one)) { - diff_populate_filespec(p->one, 1); + diff_populate_filespec(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, 1); + diff_populate_filespec(p->two, CHECK_SIZE_ONLY); copied = 0; added = p->two->size; diff_free_filespec_data(p->two); @@ -1956,7 +2064,7 @@ found_damage: return; /* Show all directories with more than x% of the changes */ - qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare); + QSORT(dir.files, dir.nr, dirstat_compare); gather_dirstat(options, &dir, changed, "", 0); } @@ -2000,7 +2108,7 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o return; /* Show all directories with more than x% of the changes */ - qsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare); + QSORT(dir.files, dir.nr, dirstat_compare); gather_dirstat(options, &dir, changed, "", 0); } @@ -2102,7 +2210,6 @@ static unsigned char *deflate_it(char *data, unsigned char *deflated; git_zstream stream; - memset(&stream, 0, sizeof(stream)); git_deflate_init(&stream, zlib_compression_level); bound = git_deflate_bound(&stream, size); deflated = xmalloc(bound); @@ -2196,8 +2303,8 @@ int diff_filespec_is_binary(struct diff_filespec *one) one->is_binary = one->driver->binary; else { if (!one->data && DIFF_FILE_VALID(one)) - diff_populate_filespec(one, 0); - if (one->data) + diff_populate_filespec(one, CHECK_BINARY); + if (one->is_binary == -1 && one->data) one->is_binary = buffer_is_binary(one->data, one->size); if (one->is_binary == -1) @@ -2250,16 +2357,37 @@ static void builtin_diff(const char *name_a, struct strbuf header = STRBUF_INIT; const char *line_prefix = diff_line_prefix(o); - if (DIFF_OPT_TST(o, SUBMODULE_LOG) && - (!one->mode || S_ISGITLINK(one->mode)) && - (!two->mode || S_ISGITLINK(two->mode))) { + diff_set_mnemonic_prefix(o, "a/", "b/"); + if (DIFF_OPT_TST(o, REVERSE_DIFF)) { + a_prefix = o->b_prefix; + b_prefix = o->a_prefix; + } else { + a_prefix = o->a_prefix; + b_prefix = o->b_prefix; + } + + if (o->submodule_format == DIFF_SUBMODULE_LOG && + (!one->mode || S_ISGITLINK(one->mode)) && + (!two->mode || S_ISGITLINK(two->mode))) { const char *del = diff_get_color_opt(o, DIFF_FILE_OLD); const char *add = diff_get_color_opt(o, DIFF_FILE_NEW); show_submodule_summary(o->file, one->path ? one->path : two->path, line_prefix, - one->sha1, two->sha1, two->dirty_submodule, + &one->oid, &two->oid, + two->dirty_submodule, meta, del, add, reset); return; + } else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF && + (!one->mode || S_ISGITLINK(one->mode)) && + (!two->mode || S_ISGITLINK(two->mode))) { + const char *del = diff_get_color_opt(o, DIFF_FILE_OLD); + const char *add = diff_get_color_opt(o, DIFF_FILE_NEW); + show_submodule_inline_diff(o->file, one->path ? one->path : two->path, + line_prefix, + &one->oid, &two->oid, + two->dirty_submodule, + meta, del, add, reset, o); + return; } if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) { @@ -2267,15 +2395,6 @@ static void builtin_diff(const char *name_a, textconv_two = get_textconv(two); } - diff_set_mnemonic_prefix(o, "a/", "b/"); - if (DIFF_OPT_TST(o, REVERSE_DIFF)) { - a_prefix = o->b_prefix; - b_prefix = o->a_prefix; - } else { - a_prefix = o->a_prefix; - b_prefix = o->b_prefix; - } - /* Never use a non-valid filename anywhere if at all possible */ name_a = DIFF_FILE_VALID(one) ? name_a : name_b; name_b = DIFF_FILE_VALID(two) ? name_b : name_a; @@ -2332,6 +2451,19 @@ static void builtin_diff(const char *name_a, } else if (!DIFF_OPT_TST(o, TEXT) && ( (!textconv_one && diff_filespec_is_binary(one)) || (!textconv_two && diff_filespec_is_binary(two)) )) { + if (!one->data && !two->data && + S_ISREG(one->mode) && S_ISREG(two->mode) && + !DIFF_OPT_TST(o, BINARY)) { + if (!oidcmp(&one->oid, &two->oid)) { + if (must_show_header) + fprintf(o->file, "%s", header.buf); + goto free_ab_and_return; + } + fprintf(o->file, "%s", header.buf); + fprintf(o->file, "%sBinary files %s and %s differ\n", + line_prefix, lbl[0], lbl[1]); + goto free_ab_and_return; + } if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); /* Quite common confusing case */ @@ -2352,6 +2484,7 @@ static void builtin_diff(const char *name_a, } else { /* Crazy xdl interfaces.. */ const char *diffopts = getenv("GIT_DIFF_OPTS"); + const char *v; xpparam_t xpp; xdemitconf_t xecfg; struct emit_callback ecbdata; @@ -2374,7 +2507,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); @@ -2390,14 +2522,15 @@ static void builtin_diff(const char *name_a, xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags); if (!diffopts) ; - else if (starts_with(diffopts, "--unified=")) - xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10); - else if (starts_with(diffopts, "-u")) - xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10); + else if (skip_prefix(diffopts, "--unified=", &v)) + xecfg.ctxlen = strtoul(v, NULL, 10); + else if (skip_prefix(diffopts, "-u", &v)) + xecfg.ctxlen = strtoul(v, NULL, 10); if (o->word_diff) init_diff_words_data(&ecbdata, o, one, two); - xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata, - &xpp, &xecfg); + if (xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata, + &xpp, &xecfg)) + die("unable to generate diff for %s", one->path); if (o->word_diff) free_diff_words_data(&ecbdata); if (textconv_one) @@ -2441,7 +2574,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; @@ -2474,8 +2607,9 @@ static void builtin_diffstat(const char *name_a, const char *name_b, xpp.flags = o->xdl_opts; xecfg.ctxlen = o->context; xecfg.interhunkctxlen = o->interhunkcontext; - xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat, - &xpp, &xecfg); + if (xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat, + &xpp, &xecfg)) + die("unable to generate diffstat for %s", one->path); } diff_free_filespec_data(one); @@ -2521,8 +2655,9 @@ static void builtin_checkdiff(const char *name_a, const char *name_b, memset(&xecfg, 0, sizeof(xecfg)); xecfg.ctxlen = 1; /* at least one context line */ xpp.flags = 0; - xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data, - &xpp, &xecfg); + if (xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data, + &xpp, &xecfg)) + die("unable to generate checkdiff for %s", one->path); if (data.ws_rule & WS_BLANK_AT_EOF) { struct emit_callback ecbdata; @@ -2551,12 +2686,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; @@ -2575,8 +2707,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; } } @@ -2619,6 +2751,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) @@ -2629,7 +2768,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int * This is not the sha1 we are looking for, or * unreusable because it is not a regular file. */ - if (hashcmp(sha1, ce->sha1) || !S_ISREG(ce->ce_mode)) + if (hashcmp(sha1, ce->oid.hash) || !S_ISREG(ce->ce_mode)) return 0; /* @@ -2651,21 +2790,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; } @@ -2675,8 +2815,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, int size_only) +int diff_populate_filespec(struct diff_filespec *s, unsigned int flags) { + int size_only = flags & CHECK_SIZE_ONLY; int err = 0; /* * demote FAIL to WARN to allow inspecting the situation @@ -2700,8 +2841,8 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) 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; @@ -2731,6 +2872,11 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) } if (size_only) return 0; + if ((flags & CHECK_BINARY) && + s->size > big_file_threshold && s->is_binary == -1) { + s->is_binary = 1; + return 0; + } fd = open(s->path, O_RDONLY); if (fd < 0) goto err_empty; @@ -2752,16 +2898,22 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only) } else { enum object_type type; - if (size_only) { - type = sha1_object_info(s->sha1, &s->size); + if (size_only || (flags & CHECK_BINARY)) { + type = sha1_object_info(s->oid.hash, &s->size); if (type < 0) - die("unable to read %s", sha1_to_hex(s->sha1)); - } else { - s->data = read_sha1_file(s->sha1, &type, &s->size); - if (!s->data) - die("unable to read %s", sha1_to_hex(s->sha1)); - s->should_free = 1; + 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) { + s->is_binary = 1; + return 0; + } } + s->data = read_sha1_file(s->oid.hash, &type, &s->size); + if (!s->data) + die("unable to read %s", oid_to_hex(&s->oid)); + s->should_free = 1; } return 0; } @@ -2789,7 +2941,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; @@ -2802,8 +2954,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, @@ -2813,11 +2964,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); @@ -2834,19 +2984,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 (!one->sha1_valid || - reuse_worktree_file(name, one->sha1, 1)) { + if (!S_ISGITLINK(one->mode) && + (!one->oid_valid || + reuse_worktree_file(name, one->oid.hash, 1))) { struct stat st; if (lstat(name, &st) < 0) { if (errno == ENOENT) @@ -2858,26 +3003,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)); + oid_to_hex_r(temp->hex, &one->oid); /* 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; } @@ -2885,11 +3030,21 @@ 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; } +static void add_external_diff_name(struct argv_array *argv, + const char *name, + struct diff_filespec *df) +{ + struct diff_tempfile *temp = prepare_temp_file(name, df); + argv_array_push(argv, temp->name); + argv_array_push(argv, temp->hex); + argv_array_push(argv, temp->mode); +} + /* An external diff command takes: * * diff-cmd name infile1 infile1-sha1 infile1-mode \ @@ -2905,50 +3060,33 @@ static void run_external_diff(const char *pgm, int complete_rewrite, struct diff_options *o) { - const char *spawn_arg[10]; - int retval; - const char **arg = &spawn_arg[0]; + struct argv_array argv = ARGV_ARRAY_INIT; + struct argv_array env = ARGV_ARRAY_INIT; struct diff_queue_struct *q = &diff_queued_diff; - const char *env[3] = { NULL }; - char env_counter[50]; - char env_total[50]; + + argv_array_push(&argv, pgm); + argv_array_push(&argv, name); if (one && two) { - struct diff_tempfile *temp_one, *temp_two; - const char *othername = (other ? other : name); - temp_one = prepare_temp_file(name, one); - temp_two = prepare_temp_file(othername, two); - *arg++ = pgm; - *arg++ = name; - *arg++ = temp_one->name; - *arg++ = temp_one->hex; - *arg++ = temp_one->mode; - *arg++ = temp_two->name; - *arg++ = temp_two->hex; - *arg++ = temp_two->mode; - if (other) { - *arg++ = other; - *arg++ = xfrm_msg; + add_external_diff_name(&argv, name, one); + if (!other) + add_external_diff_name(&argv, name, two); + else { + add_external_diff_name(&argv, other, two); + argv_array_push(&argv, other); + argv_array_push(&argv, xfrm_msg); } - } else { - *arg++ = pgm; - *arg++ = name; } - *arg = NULL; - fflush(NULL); - env[0] = env_counter; - snprintf(env_counter, sizeof(env_counter), "GIT_DIFF_PATH_COUNTER=%d", - ++o->diff_path_counter); - env[1] = env_total; - snprintf(env_total, sizeof(env_total), "GIT_DIFF_PATH_TOTAL=%d", q->nr); + argv_array_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter); + argv_array_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr); + + if (run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env.argv)) + die(_("external diff died, stopping at %s"), name); - retval = run_command_v_opt_cd_env(spawn_arg, RUN_USING_SHELL, NULL, env); remove_tempfile(); - if (retval) { - fprintf(stderr, "external diff died, stopping at %s.\n", name); - exit(1); - } + argv_array_clear(&argv); + argv_array_clear(&env); } static int similarity_index(struct diff_filepair *p) @@ -2956,6 +3094,22 @@ static int similarity_index(struct diff_filepair *p) return p->score * 100 / MAX_SCORE; } +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); + 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) + hex[abbrev] = '\0'; + return hex; + } +} + static void fill_metainfo(struct strbuf *msg, const char *name, const char *other, @@ -3005,7 +3159,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)) { @@ -3014,9 +3168,9 @@ static void fill_metainfo(struct strbuf *msg, (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two))) 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)); + strbuf_addf(msg, "%s%sindex %s..%s", line_prefix, set, + diff_abbrev_oid(&one->oid, abbrev), + diff_abbrev_oid(&two->oid, abbrev)); if (one->mode == two->mode) strbuf_addf(msg, " %06o", one->mode); strbuf_addf(msg, "%s\n", reset); @@ -3071,20 +3225,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) @@ -3209,18 +3363,24 @@ void diff_setup(struct diff_options *options) options->file = stdout; + options->abbrev = DEFAULT_ABBREV; options->line_termination = '\n'; options->break_opt = -1; options->rename_limit = -1; options->dirstat_permille = diff_dirstat_permille_default; options->context = diff_context_default; + options->interhunkcontext = diff_interhunk_context_default; + options->ws_error_highlight = ws_error_highlight_default; DIFF_OPT_SET(options, RENAME_EMPTY); + /* pathchange left =NULL by default */ options->change = diff_change; options->add_remove = diff_addremove; options->use_color = diff_use_color_default; options->detect_rename = diff_detect_rename_default; options->xdl_opts |= diff_algorithm; + if (diff_indent_heuristic) + DIFF_XDL_SET(options, INDENT_HEURISTIC); options->orderfile = diff_order_file_cfg; @@ -3248,7 +3408,7 @@ void diff_setup_done(struct diff_options *options) if (options->output_format & DIFF_FORMAT_NO_OUTPUT) count++; if (count > 1) - die("--name-only, --name-status, --check and -s are mutually exclusive"); + die(_("--name-only, --name-status, --check and -s are mutually exclusive")); /* * Most of the time we can say "there are changes" @@ -3322,7 +3482,7 @@ void diff_setup_done(struct diff_options *options) */ read_cache(); } - if (options->abbrev <= 0 || 40 < options->abbrev) + if (40 < options->abbrev) options->abbrev = 40; /* full */ /* @@ -3336,6 +3496,9 @@ void diff_setup_done(struct diff_options *options) } options->diff_path_counter = 0; + + if (DIFF_OPT_TST(options, FOLLOW_RENAMES) && options->pathspec.nr != 1) + die(_("--follow requires exactly one pathspec")); } static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val) @@ -3365,14 +3528,11 @@ static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *va if (c != '-') return 0; arg++; - eq = strchr(arg, '='); - if (eq) - len = eq - arg; - else - len = strlen(arg); + eq = strchrnul(arg, '='); + len = eq - arg; if (!len || strncmp(arg, arg_long, len)) return 0; - if (eq) { + if (*eq) { int n; char *end; if (!isdigit(*++eq)) @@ -3407,12 +3567,10 @@ int parse_long_opt(const char *opt, const char **argv, const char **optarg) { const char *arg = argv[0]; - if (arg[0] != '-' || arg[1] != '-') + if (!skip_prefix(arg, "--", &arg)) return 0; - arg += strlen("--"); - if (!starts_with(arg, opt)) + if (!skip_prefix(arg, opt, &arg)) return 0; - arg += strlen(opt); if (*arg == '=') { /* stuck form: --option=value */ *optarg = arg + 1; return 1; @@ -3436,47 +3594,44 @@ static int stat_opt(struct diff_options *options, const char **av) int count = options->stat_count; int argcount = 1; - arg += strlen("--stat"); + if (!skip_prefix(arg, "--stat", &arg)) + die("BUG: stat option does not begin with --stat: %s", arg); end = (char *)arg; switch (*arg) { case '-': - if (starts_with(arg, "-width")) { - arg += strlen("-width"); + if (skip_prefix(arg, "-width", &arg)) { if (*arg == '=') width = strtoul(arg + 1, &end, 10); else if (!*arg && !av[1]) - die("Option '--stat-width' requires a value"); + die_want_option("--stat-width"); else if (!*arg) { width = strtoul(av[1], &end, 10); argcount = 2; } - } else if (starts_with(arg, "-name-width")) { - arg += strlen("-name-width"); + } else if (skip_prefix(arg, "-name-width", &arg)) { if (*arg == '=') name_width = strtoul(arg + 1, &end, 10); else if (!*arg && !av[1]) - die("Option '--stat-name-width' requires a value"); + die_want_option("--stat-name-width"); else if (!*arg) { name_width = strtoul(av[1], &end, 10); argcount = 2; } - } else if (starts_with(arg, "-graph-width")) { - arg += strlen("-graph-width"); + } else if (skip_prefix(arg, "-graph-width", &arg)) { if (*arg == '=') graph_width = strtoul(arg + 1, &end, 10); else if (!*arg && !av[1]) - die("Option '--stat-graph-width' requires a value"); + die_want_option("--stat-graph-width"); else if (!*arg) { graph_width = strtoul(av[1], &end, 10); argcount = 2; } - } else if (starts_with(arg, "-count")) { - arg += strlen("-count"); + } else if (skip_prefix(arg, "-count", &arg)) { if (*arg == '=') count = strtoul(arg + 1, &end, 10); else if (!*arg && !av[1]) - die("Option '--stat-count' requires a value"); + die_want_option("--stat-count"); else if (!*arg) { count = strtoul(av[1], &end, 10); argcount = 2; @@ -3599,25 +3754,34 @@ static int parse_diff_filter_opt(const char *optarg, struct diff_options *opt) return 0; } -/* Used only by "diff-files" and "diff --no-index" */ -void handle_deprecated_show_diff_q(struct diff_options *opt) -{ - warning("'diff -q' and 'diff-files -q' are deprecated."); - warning("Use 'diff --diff-filter=d' instead to ignore deleted filepairs."); - parse_diff_filter_opt("d", opt); -} - static void enable_patch_output(int *fmt) { *fmt &= ~DIFF_FORMAT_NO_OUTPUT; *fmt |= DIFF_FORMAT_PATCH; } -int diff_opt_parse(struct diff_options *options, const char **av, int ac) +static int parse_ws_error_highlight_opt(struct diff_options *opt, const char *arg) +{ + int val = parse_ws_error_highlight(arg); + + if (val < 0) { + error("unknown value after ws-error-highlight=%.*s", + -1 - val, arg); + return 0; + } + opt->ws_error_highlight = val; + return 1; +} + +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)) @@ -3633,17 +3797,17 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->output_format |= DIFF_FORMAT_SHORTSTAT; else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat")) return parse_dirstat_opt(options, ""); - else if (starts_with(arg, "-X")) - return parse_dirstat_opt(options, arg + 2); - else if (starts_with(arg, "--dirstat=")) - return parse_dirstat_opt(options, arg + 10); + else if (skip_prefix(arg, "-X", &arg)) + return parse_dirstat_opt(options, arg); + else if (skip_prefix(arg, "--dirstat=", &arg)) + return parse_dirstat_opt(options, arg); else if (!strcmp(arg, "--cumulative")) return parse_dirstat_opt(options, "cumulative"); else if (!strcmp(arg, "--dirstat-by-file")) return parse_dirstat_opt(options, "files"); - else if (starts_with(arg, "--dirstat-by-file=")) { + else if (skip_prefix(arg, "--dirstat-by-file=", &arg)) { parse_dirstat_opt(options, "files"); - return parse_dirstat_opt(options, arg + 18); + return parse_dirstat_opt(options, arg); } else if (!strcmp(arg, "--check")) options->output_format |= DIFF_FORMAT_CHECKDIFF; @@ -3693,9 +3857,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_CLR(options, RENAME_EMPTY); else if (!strcmp(arg, "--relative")) DIFF_OPT_SET(options, RELATIVE_NAME); - else if (starts_with(arg, "--relative=")) { + else if (skip_prefix(arg, "--relative=", &arg)) { DIFF_OPT_SET(options, RELATIVE_NAME); - options->prefix = arg + 11; + options->prefix = arg; } /* xdiff options */ @@ -3711,6 +3875,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, "--indent-heuristic")) + DIFF_XDL_SET(options, INDENT_HEURISTIC); + else if (!strcmp(arg, "--no-indent-heuristic")) + DIFF_XDL_CLR(options, INDENT_HEURISTIC); else if (!strcmp(arg, "--patience")) options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF); else if (!strcmp(arg, "--histogram")) @@ -3742,12 +3910,13 @@ 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 (starts_with(arg, "--color=")) { - int value = git_config_colorbool(NULL, arg+8); + else if (skip_prefix(arg, "--color=", &arg)) { + int value = git_config_colorbool(NULL, arg); if (value < 0) return error("option `color' expects \"always\", \"auto\", or \"never\""); options->use_color = value; @@ -3758,29 +3927,28 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->use_color = 1; options->word_diff = DIFF_WORDS_COLOR; } - else if (starts_with(arg, "--color-words=")) { + else if (skip_prefix(arg, "--color-words=", &arg)) { options->use_color = 1; options->word_diff = DIFF_WORDS_COLOR; - options->word_regex = arg + 14; + options->word_regex = arg; } else if (!strcmp(arg, "--word-diff")) { if (options->word_diff == DIFF_WORDS_NONE) options->word_diff = DIFF_WORDS_PLAIN; } - else if (starts_with(arg, "--word-diff=")) { - const char *type = arg + 12; - if (!strcmp(type, "plain")) + else if (skip_prefix(arg, "--word-diff=", &arg)) { + if (!strcmp(arg, "plain")) options->word_diff = DIFF_WORDS_PLAIN; - else if (!strcmp(type, "color")) { + else if (!strcmp(arg, "color")) { options->use_color = 1; options->word_diff = DIFF_WORDS_COLOR; } - else if (!strcmp(type, "porcelain")) + else if (!strcmp(arg, "porcelain")) options->word_diff = DIFF_WORDS_PORCELAIN; - else if (!strcmp(type, "none")) + else if (!strcmp(arg, "none")) options->word_diff = DIFF_WORDS_NONE; else - die("bad --word-diff argument: %s", type); + die("bad --word-diff argument: %s", arg); } else if ((argcount = parse_long_opt("word-diff-regex", av, &optarg))) { if (options->word_diff == DIFF_WORDS_NONE) @@ -3803,13 +3971,19 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) else if (!strcmp(arg, "--ignore-submodules")) { DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG); handle_ignore_submodules_arg(options, "all"); - } else if (starts_with(arg, "--ignore-submodules=")) { + } else if (skip_prefix(arg, "--ignore-submodules=", &arg)) { DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG); - handle_ignore_submodules_arg(options, arg + 20); + handle_ignore_submodules_arg(options, arg); } else if (!strcmp(arg, "--submodule")) - DIFF_OPT_SET(options, SUBMODULE_LOG); - else if (starts_with(arg, "--submodule=")) - return parse_submodule_opt(options, arg + 12); + options->submodule_format = DIFF_SUBMODULE_LOG; + else if (skip_prefix(arg, "--submodule=", &arg)) + return parse_submodule_opt(options, arg); + else if (skip_prefix(arg, "--ws-error-highlight=", &arg)) + return parse_ws_error_highlight_opt(options, arg); + else if (!strcmp(arg, "--ita-invisible-in-index")) + options->ita_invisible_in_index = 1; + else if (!strcmp(arg, "--ita-visible-in-index")) + options->ita_invisible_in_index = 0; /* misc options */ else if (!strcmp(arg, "-z")) @@ -3832,7 +4006,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))) { @@ -3842,10 +4017,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) offending, optarg); return argcount; } + else if (!strcmp(arg, "--no-abbrev")) + options->abbrev = 0; else if (!strcmp(arg, "--abbrev")) options->abbrev = DEFAULT_ABBREV; - else if (starts_with(arg, "--abbrev=")) { - options->abbrev = strtoul(arg + 9, NULL, 10); + else if (skip_prefix(arg, "--abbrev=", &arg)) { + options->abbrev = strtoul(arg, NULL, 10); if (options->abbrev < MINIMUM_ABBREV) options->abbrev = MINIMUM_ABBREV; else if (40 < options->abbrev) @@ -3855,6 +4032,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->a_prefix = optarg; return argcount; } + else if ((argcount = parse_long_opt("line-prefix", av, &optarg))) { + options->line_prefix = optarg; + options->line_prefix_length = strlen(options->line_prefix); + graph_setup_line_prefix(options); + return argcount; + } else if ((argcount = parse_long_opt("dst-prefix", av, &optarg))) { options->b_prefix = optarg; return argcount; @@ -3871,10 +4054,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; @@ -3926,22 +4112,19 @@ static int diff_scoreopt_parse(const char *opt) cmd = *opt++; if (cmd == '-') { /* convert the long-form arguments into short-form versions */ - if (starts_with(opt, "break-rewrites")) { - opt += strlen("break-rewrites"); + if (skip_prefix(opt, "break-rewrites", &opt)) { if (*opt == 0 || *opt++ == '=') cmd = 'B'; - } else if (starts_with(opt, "find-copies")) { - opt += strlen("find-copies"); + } else if (skip_prefix(opt, "find-copies", &opt)) { if (*opt == 0 || *opt++ == '=') cmd = 'C'; - } else if (starts_with(opt, "find-renames")) { - opt += strlen("find-renames"); + } else if (skip_prefix(opt, "find-renames", &opt)) { if (*opt == 0 || *opt++ == '=') cmd = 'M'; } } if (cmd != 'M' && cmd != 'C' && cmd != 'B') - return -1; /* that is not a -M, -C nor -B option */ + return -1; /* that is not a -M, -C, or -B option */ opt1 = parse_rename_score(&opt); if (cmd != 'B') @@ -3965,11 +4148,7 @@ struct diff_queue_struct diff_queued_diff; void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp) { - if (queue->alloc <= queue->nr) { - queue->alloc = alloc_nr(queue->alloc); - queue->queue = xrealloc(queue->queue, - sizeof(dp) * queue->alloc); - } + ALLOC_GROW(queue->queue, queue->nr + 1, queue->alloc); queue->queue[queue->nr++] = dp; } @@ -3992,27 +4171,46 @@ void diff_free_filepair(struct diff_filepair *p) free(p); } -/* 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) +const char *diff_aligned_abbrev(const struct object_id *oid, int len) { int abblen; const char *abbrev; - if (len == 40) - return sha1_to_hex(sha1); - abbrev = find_unique_abbrev(sha1, len); + if (len == GIT_SHA1_HEXSZ) + return oid_to_hex(oid); + + abbrev = diff_abbrev_oid(oid, len); abblen = strlen(abbrev); - if (abblen < 37) { - static char hex[41]; + + /* + * 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 < GIT_SHA1_HEXSZ - 3) { + static char hex[GIT_SHA1_HEXSZ + 1]; 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); + + return oid_to_hex(oid); } static void diff_flush_raw(struct diff_filepair *p, struct diff_options *opt) @@ -4023,8 +4221,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_aligned_abbrev(&p->one->oid, opt->abbrev)); + fprintf(opt->file, "%s ", + diff_aligned_abbrev(&p->two->oid, opt->abbrev)); } if (p->score) { fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p), @@ -4073,11 +4272,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; } @@ -4138,7 +4337,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); @@ -4208,11 +4407,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 @@ -4251,6 +4450,7 @@ static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt) name_a = p->two->path; name_b = NULL; strip_prefix(opt->prefix_length, &name_a, &name_b); + fprintf(opt->file, "%s", diff_line_prefix(opt)); write_name_quoted(name_a, opt->file, opt->line_termination); } } @@ -4354,7 +4554,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; @@ -4389,9 +4589,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)); @@ -4426,29 +4623,40 @@ 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; } xpp.flags = 0; xecfg.ctxlen = 3; xecfg.flags = 0; - xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data, - &xpp, &xecfg); + if (xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data, + &xpp, &xecfg)) + return error("unable to generate patch-id diff for %s", + p->one->path); } git_SHA1_Final(sha1, &ctx); 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]); @@ -4485,25 +4693,25 @@ static int is_summary_empty(const struct diff_queue_struct *q) } static const char rename_limit_warning[] = -"inexact rename detection was skipped due to too many files."; +N_("inexact rename detection was skipped due to too many files."); static const char degrade_cc_to_c_warning[] = -"only found copies from modified paths due to too many files."; +N_("only found copies from modified paths due to too many files."); static const char rename_limit_advice[] = -"you may want to set your %s variable to at least " -"%d and retry the command."; +N_("you may want to set your %s variable to at least " + "%d and retry the command."); void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc) { if (degraded_cc) - warning(degrade_cc_to_c_warning); + warning(_(degrade_cc_to_c_warning)); else if (needed) - warning(rename_limit_warning); + warning(_(rename_limit_warning)); else return; if (0 < needed && needed < 32767) - warning(rename_limit_advice, varname, needed); + warning(_(rename_limit_advice), varname, needed); } void diff_flush(struct diff_options *options) @@ -4551,7 +4759,7 @@ void diff_flush(struct diff_options *options) show_stats(&diffstat, options); if (output_format & DIFF_FORMAT_SHORTSTAT) show_shortstats(&diffstat, options); - if (output_format & DIFF_FORMAT_DIRSTAT) + if (output_format & DIFF_FORMAT_DIRSTAT && dirstat_by_line) show_dirstat_by_line(&diffstat, options); free_diffstat_info(&diffstat); separator++; @@ -4719,10 +4927,10 @@ 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, 1) || - diff_populate_filespec(p->two, 1) || + diff_populate_filespec(p->one, CHECK_SIZE_ONLY) || + diff_populate_filespec(p->two, CHECK_SIZE_ONLY) || (p->one->size != p->two->size) || !diff_filespec_is_identical(p->one, p->two)) /* (2) */ p->skip_stat_unmatch_result = 1; @@ -4770,11 +4978,12 @@ static int diffnamecmp(const void *a_, const void *b_) void diffcore_fix_diff_index(struct diff_options *options) { struct diff_queue_struct *q = &diff_queued_diff; - qsort(q->queue, q->nr, sizeof(q->queue[0]), diffnamecmp); + QSORT(q->queue, q->nr, diffnamecmp); } void diffcore_std(struct diff_options *options) { + /* NOTE please keep the following in sync with diff_tree_combined() */ if (options->skip_stat_unmatch) diffcore_skip_stat_unmatch(options); if (!options->found_follow) { @@ -4909,14 +5118,10 @@ void diff_change(struct diff_options *options, return; if (DIFF_OPT_TST(options, REVERSE_DIFF)) { - unsigned tmp; - const unsigned char *tmp_c; - tmp = old_mode; old_mode = new_mode; new_mode = tmp; - tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c; - tmp = old_sha1_valid; old_sha1_valid = new_sha1_valid; - new_sha1_valid = tmp; - tmp = old_dirty_submodule; old_dirty_submodule = new_dirty_submodule; - new_dirty_submodule = tmp; + SWAP(old_mode, new_mode); + SWAP(old_sha1, new_sha1); + SWAP(old_sha1_valid, new_sha1_valid); + SWAP(old_dirty_submodule, new_dirty_submodule); } if (options->prefix && @@ -4963,7 +5168,7 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec, struct diff_tempfile *temp; const char *argv[3]; const char **arg = argv; - struct child_process child; + struct child_process child = CHILD_PROCESS_INIT; struct strbuf buf = STRBUF_INIT; int err = 0; @@ -4972,7 +5177,6 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec, *arg++ = temp->name; *arg = NULL; - memset(&child, 0, sizeof(child)); child.use_shell = 1; child.argv = argv; child.out = -1; @@ -5001,7 +5205,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; @@ -5012,8 +5216,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; @@ -5023,9 +5231,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, |