diff options
Diffstat (limited to 'diff.c')
-rw-r--r-- | diff.c | 250 |
1 files changed, 146 insertions, 104 deletions
@@ -264,7 +264,8 @@ void init_diff_ui_defaults(void) diff_detect_rename_default = DIFF_DETECT_RENAME; } -int git_diff_heuristic_config(const char *var, const char *value, void *cb) +int git_diff_heuristic_config(const char *var, const char *value, + void *cb UNUSED) { if (!strcmp(var, "diff.indentheuristic")) diff_indent_heuristic = git_config_bool(var, value); @@ -603,7 +604,7 @@ static unsigned long diff_filespec_size(struct repository *r, return one->size; } -static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule) +static int count_trailing_blank(mmfile_t *mf) { char *ptr = mf->ptr; long size = mf->size; @@ -621,7 +622,7 @@ static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule) for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--) if (*prev_eol == '\n') break; - if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule)) + if (!ws_blank_line(prev_eol + 1, ptr - prev_eol)) break; cnt++; ptr = prev_eol - 1; @@ -633,9 +634,8 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2, struct emit_callback *ecbdata) { int l1, l2, at; - unsigned ws_rule = ecbdata->ws_rule; - l1 = count_trailing_blank(mf1, ws_rule); - l2 = count_trailing_blank(mf2, ws_rule); + l1 = count_trailing_blank(mf1); + l2 = count_trailing_blank(mf2); if (l2 <= l1) { ecbdata->blank_at_eof_in_preimage = 0; ecbdata->blank_at_eof_in_postimage = 0; @@ -916,7 +916,7 @@ struct interned_diff_symbol { static int interned_diff_symbol_cmp(const void *hashmap_cmp_fn_data, const struct hashmap_entry *eptr, const struct hashmap_entry *entry_or_key, - const void *keydata) + const void *keydata UNUSED) { const struct diff_options *diffopt = hashmap_cmp_fn_data; const struct emitted_diff_symbol *a, *b; @@ -1289,7 +1289,6 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, { static const char *nneof = " No newline at end of file\n"; const char *context, *reset, *set, *set_sign, *meta, *fraginfo; - struct strbuf sb = STRBUF_INIT; enum diff_symbol s = eds->s; const char *line = eds->line; @@ -1521,7 +1520,6 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, default: BUG("unknown diff symbol"); } - strbuf_release(&sb); } static void emit_diff_symbol(struct diff_options *o, enum diff_symbol s, @@ -1584,7 +1582,7 @@ static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage && ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage)) return 0; - return ws_blank_line(line, len, ecbdata->ws_rule); + return ws_blank_line(line, len); } static void emit_add_line(struct emit_callback *ecbdata, @@ -1956,7 +1954,7 @@ static int color_words_output_graph_prefix(struct diff_words_data *diff_words) static void fn_out_diff_words_aux(void *priv, long minus_first, long minus_len, long plus_first, long plus_len, - const char *func, long funclen) + const char *func UNUSED, long funclen UNUSED) { struct diff_words_data *diff_words = priv; struct diff_words_style *style = diff_words->style; @@ -2489,6 +2487,9 @@ static int diffstat_consume(void *priv, char *line, unsigned long len) struct diffstat_t *diffstat = priv; struct diffstat_file *x = diffstat->files[diffstat->nr - 1]; + if (!len) + BUG("xdiff fed us an empty line"); + if (line[0] == '+') x->added++; else if (line[0] == '-') @@ -2675,6 +2676,11 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) * making the line longer than the maximum width. */ + /* + * NEEDSWORK: line_prefix is often used for "log --graph" output + * and contains ANSI-colored string. utf8_strnwidth() should be + * used to correctly count the display width instead of strlen(). + */ if (options->stat_width == -1) width = term_columns() - strlen(line_prefix); else @@ -2750,6 +2756,16 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options) char *slash; prefix = "..."; len -= 3; + /* + * NEEDSWORK: (name_len - len) counts the display + * width, which would be shorter than the byte + * length of the corresponding substring. + * Advancing "name" by that number of bytes does + * *NOT* skip over that many columns, so it is + * very likely that chomping the pathname at the + * slash we will find starting from "name" will + * leave the resulting string still too long. + */ name += name_len - len; slash = strchr(name, '/'); if (slash) @@ -3168,8 +3184,9 @@ static int is_conflict_marker(const char *line, int marker_size, unsigned long l } static void checkdiff_consume_hunk(void *priv, - long ob, long on, long nb, long nn, - const char *func, long funclen) + long ob UNUSED, long on UNUSED, + long nb, long nn UNUSED, + const char *func UNUSED, long funclen UNUSED) { struct checkdiff_t *data = priv; @@ -3367,23 +3384,23 @@ struct userdiff_driver *get_textconv(struct repository *r, return userdiff_get_textconv(r, one->driver); } -static struct strbuf *additional_headers(struct diff_options *o, - const char *path) +static struct string_list *additional_headers(struct diff_options *o, + const char *path) { if (!o->additional_path_headers) return NULL; return strmap_get(o->additional_path_headers, path); } -static void add_formatted_headers(struct strbuf *msg, - struct strbuf *more_headers, +static void add_formatted_header(struct strbuf *msg, + const char *header, const char *line_prefix, const char *meta, const char *reset) { - char *next, *newline; + const char *next, *newline; - for (next = more_headers->buf; *next; next = newline) { + for (next = header; *next; next = newline) { newline = strchrnul(next, '\n'); strbuf_addf(msg, "%s%s%.*s%s\n", line_prefix, meta, (int)(newline - next), next, reset); @@ -3392,6 +3409,34 @@ static void add_formatted_headers(struct strbuf *msg, } } +static void add_formatted_headers(struct strbuf *msg, + struct string_list *more_headers, + const char *line_prefix, + const char *meta, + const char *reset) +{ + int i; + + for (i = 0; i < more_headers->nr; i++) + add_formatted_header(msg, more_headers->items[i].string, + line_prefix, meta, reset); +} + +static int diff_filepair_is_phoney(struct diff_filespec *one, + struct diff_filespec *two) +{ + /* + * This function specifically looks for pairs injected by + * create_filepairs_for_header_only_notifications(). Such + * pairs are "phoney" in that they do not represent any + * content or even mode difference, but were inserted because + * diff_queued_diff previously had no pair associated with + * that path but we needed some pair to avoid losing the + * "remerge CONFLICT" header associated with the path. + */ + return !DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two); +} + static void builtin_diff(const char *name_a, const char *name_b, struct diff_filespec *one, @@ -3423,14 +3468,16 @@ static void builtin_diff(const char *name_a, if (o->submodule_format == DIFF_SUBMODULE_LOG && (!one->mode || S_ISGITLINK(one->mode)) && - (!two->mode || S_ISGITLINK(two->mode))) { + (!two->mode || S_ISGITLINK(two->mode)) && + (!diff_filepair_is_phoney(one, two))) { show_submodule_diff_summary(o, one->path ? one->path : two->path, &one->oid, &two->oid, two->dirty_submodule); return; } else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF && (!one->mode || S_ISGITLINK(one->mode)) && - (!two->mode || S_ISGITLINK(two->mode))) { + (!two->mode || S_ISGITLINK(two->mode)) && + (!diff_filepair_is_phoney(one, two))) { show_submodule_inline_diff(o, one->path ? one->path : two->path, &one->oid, &two->oid, two->dirty_submodule); @@ -3450,12 +3497,12 @@ static void builtin_diff(const char *name_a, b_two = quote_two(b_prefix, name_b + (*name_b == '/')); lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null"; lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null"; - if (!DIFF_FILE_VALID(one) && !DIFF_FILE_VALID(two)) { + if (diff_filepair_is_phoney(one, two)) { /* - * We should only reach this point for pairs from + * We should only reach this point for pairs generated from * create_filepairs_for_header_only_notifications(). For - * these, we should avoid the "/dev/null" special casing - * above, meaning we avoid showing such pairs as either + * these, we want to avoid the "/dev/null" special casing + * above, because we do not want such pairs shown as either * "new file" or "deleted file" below. */ lbl[0] = a_one; @@ -4141,18 +4188,13 @@ static void prep_temp_blob(struct index_state *istate, int mode) { struct strbuf buf = STRBUF_INIT; - struct strbuf tempfile = STRBUF_INIT; char *path_dup = xstrdup(path); const char *base = basename(path_dup); struct checkout_metadata meta; init_checkout_metadata(&meta, NULL, NULL, oid); - /* Generate "XXXXXX_basename.ext" */ - strbuf_addstr(&tempfile, "XXXXXX_"); - strbuf_addstr(&tempfile, base); - - temp->tempfile = mks_tempfile_ts(tempfile.buf, strlen(base) + 1); + temp->tempfile = mks_tempfile_dt("git-blob-XXXXXX", base); if (!temp->tempfile) die_errno("unable to create temp-file"); if (convert_to_working_tree(istate, path, @@ -4167,7 +4209,6 @@ static void prep_temp_blob(struct index_state *istate, oid_to_hex_r(temp->hex, oid); xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode); strbuf_release(&buf); - strbuf_release(&tempfile); free(path_dup); } @@ -4260,35 +4301,34 @@ static void run_external_diff(const char *pgm, const char *xfrm_msg, struct diff_options *o) { - struct strvec argv = STRVEC_INIT; - struct strvec env = STRVEC_INIT; + struct child_process cmd = CHILD_PROCESS_INIT; struct diff_queue_struct *q = &diff_queued_diff; - strvec_push(&argv, pgm); - strvec_push(&argv, name); + strvec_push(&cmd.args, pgm); + strvec_push(&cmd.args, name); if (one && two) { - add_external_diff_name(o->repo, &argv, name, one); + add_external_diff_name(o->repo, &cmd.args, name, one); if (!other) - add_external_diff_name(o->repo, &argv, name, two); + add_external_diff_name(o->repo, &cmd.args, name, two); else { - add_external_diff_name(o->repo, &argv, other, two); - strvec_push(&argv, other); - strvec_push(&argv, xfrm_msg); + add_external_diff_name(o->repo, &cmd.args, other, two); + strvec_push(&cmd.args, other); + strvec_push(&cmd.args, xfrm_msg); } } - strvec_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter); - strvec_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr); + strvec_pushf(&cmd.env, "GIT_DIFF_PATH_COUNTER=%d", + ++o->diff_path_counter); + strvec_pushf(&cmd.env, "GIT_DIFF_PATH_TOTAL=%d", q->nr); diff_free_filespec_data(one); diff_free_filespec_data(two); - if (run_command_v_opt_cd_env(argv.v, RUN_USING_SHELL, NULL, env.v)) + cmd.use_shell = 1; + if (run_command(&cmd)) die(_("external diff died, stopping at %s"), name); remove_tempfile(); - strvec_clear(&argv); - strvec_clear(&env); } static int similarity_index(struct diff_filepair *p) @@ -4325,7 +4365,7 @@ static void fill_metainfo(struct strbuf *msg, const char *set = diff_get_color(use_color, DIFF_METAINFO); const char *reset = diff_get_color(use_color, DIFF_RESET); const char *line_prefix = diff_line_prefix(o); - struct strbuf *more_headers = NULL; + struct string_list *more_headers = NULL; *must_show_header = 1; strbuf_init(msg, PATH_MAX * 2 + 300); @@ -4575,8 +4615,6 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o) builtin_checkdiff(name, other, attr_path, p->one, p->two, o); } -static void prep_parse_options(struct diff_options *options); - void repo_diff_setup(struct repository *r, struct diff_options *options) { memcpy(options, &default_diff_options, sizeof(*options)); @@ -4622,8 +4660,6 @@ void repo_diff_setup(struct repository *r, struct diff_options *options) options->color_moved = diff_color_moved_default; options->color_moved_ws_handling = diff_color_moved_ws_default; - - prep_parse_options(options); } static const char diff_status_letters[] = { @@ -4781,8 +4817,6 @@ void diff_setup_done(struct diff_options *options) options->filter = ~filter_bit[DIFF_STATUS_FILTER_AON]; options->filter &= ~options->filter_not; } - - FREE_AND_NULL(options->parseopts); } int parse_long_opt(const char *opt, const char **argv, @@ -5379,7 +5413,8 @@ static int diff_opt_rotate_to(const struct option *opt, const char *arg, int uns return 0; } -static void prep_parse_options(struct diff_options *options) +struct option *add_diff_options(const struct option *opts, + struct diff_options *options) { struct option parseopts[] = { OPT_GROUP(N_("Diff output format options")), @@ -5649,22 +5684,25 @@ static void prep_parse_options(struct diff_options *options) OPT_END() }; - ALLOC_ARRAY(options->parseopts, ARRAY_SIZE(parseopts)); - memcpy(options->parseopts, parseopts, sizeof(parseopts)); + return parse_options_concat(opts, parseopts); } int diff_opt_parse(struct diff_options *options, const char **av, int ac, const char *prefix) { + struct option no_options[] = { OPT_END() }; + struct option *parseopts = add_diff_options(no_options, options); + if (!prefix) prefix = ""; - ac = parse_options(ac, av, prefix, options->parseopts, NULL, + ac = parse_options(ac, av, prefix, parseopts, NULL, PARSE_OPT_KEEP_DASHDASH | - PARSE_OPT_KEEP_UNKNOWN | + PARSE_OPT_KEEP_UNKNOWN_OPT | PARSE_OPT_NO_INTERNAL_HELP | PARSE_OPT_ONE_SHOT | PARSE_OPT_STOP_AT_NON_OPTION); + free(parseopts); return ac; } @@ -5732,6 +5770,13 @@ void diff_free_filepair(struct diff_filepair *p) free(p); } +void diff_free_queue(struct diff_queue_struct *q) +{ + for (int i = 0; i < q->nr; i++) + diff_free_filepair(q->queue[i]); + free(q->queue); +} + const char *diff_aligned_abbrev(const struct object_id *oid, int len) { int abblen; @@ -5852,6 +5897,7 @@ static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o) { int include_conflict_headers = (additional_headers(o, p->one->path) && + !o->pickaxe_opts && (!o->filter || filter_bit_tst(DIFF_STATUS_UNMERGED, o))); /* @@ -5907,6 +5953,8 @@ int diff_queue_is_empty(struct diff_options *o) int i; int include_conflict_headers = (o->additional_path_headers && + strmap_get_size(o->additional_path_headers) && + !o->pickaxe_opts && (!o->filter || filter_bit_tst(DIFF_STATUS_UNMERGED, o))); if (include_conflict_headers) @@ -6185,7 +6233,7 @@ static void patch_id_add_mode(git_hash_ctx *ctx, unsigned mode) } /* returns 0 upon success, and writes result into oid */ -static int diff_get_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only, int stable) +static int diff_get_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only) { struct diff_queue_struct *q = &diff_queued_diff; int i; @@ -6232,66 +6280,63 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid if (p->one->mode == 0) { patch_id_add_string(&ctx, "newfilemode"); patch_id_add_mode(&ctx, p->two->mode); - patch_id_add_string(&ctx, "---/dev/null"); - patch_id_add_string(&ctx, "+++b/"); - the_hash_algo->update_fn(&ctx, p->two->path, len2); } else if (p->two->mode == 0) { patch_id_add_string(&ctx, "deletedfilemode"); patch_id_add_mode(&ctx, p->one->mode); - patch_id_add_string(&ctx, "---a/"); - the_hash_algo->update_fn(&ctx, p->one->path, len1); - patch_id_add_string(&ctx, "+++/dev/null"); - } else { - patch_id_add_string(&ctx, "---a/"); - the_hash_algo->update_fn(&ctx, p->one->path, len1); - patch_id_add_string(&ctx, "+++b/"); - the_hash_algo->update_fn(&ctx, p->two->path, len2); + } else if (p->one->mode != p->two->mode) { + patch_id_add_string(&ctx, "oldmode"); + patch_id_add_mode(&ctx, p->one->mode); + patch_id_add_string(&ctx, "newmode"); + patch_id_add_mode(&ctx, p->two->mode); } - if (diff_header_only) - continue; - - 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(options->repo, p->one) || + if (diff_header_only) { + /* don't do anything since we're only populating header info */ + } else if (diff_filespec_is_binary(options->repo, p->one) || diff_filespec_is_binary(options->repo, p->two)) { the_hash_algo->update_fn(&ctx, oid_to_hex(&p->one->oid), the_hash_algo->hexsz); the_hash_algo->update_fn(&ctx, oid_to_hex(&p->two->oid), the_hash_algo->hexsz); - continue; - } - - xpp.flags = 0; - xecfg.ctxlen = 3; - xecfg.flags = XDL_EMIT_NO_HUNK_HDR; - if (xdi_diff_outf(&mf1, &mf2, NULL, - patch_id_consume, &data, &xpp, &xecfg)) - return error("unable to generate patch-id diff for %s", - p->one->path); + } else { + if (p->one->mode == 0) { + patch_id_add_string(&ctx, "---/dev/null"); + patch_id_add_string(&ctx, "+++b/"); + the_hash_algo->update_fn(&ctx, p->two->path, len2); + } else if (p->two->mode == 0) { + patch_id_add_string(&ctx, "---a/"); + the_hash_algo->update_fn(&ctx, p->one->path, len1); + patch_id_add_string(&ctx, "+++/dev/null"); + } else { + patch_id_add_string(&ctx, "---a/"); + the_hash_algo->update_fn(&ctx, p->one->path, len1); + patch_id_add_string(&ctx, "+++b/"); + the_hash_algo->update_fn(&ctx, p->two->path, len2); + } - if (stable) - flush_one_hunk(oid, &ctx); + 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"); + xpp.flags = 0; + xecfg.ctxlen = 3; + xecfg.flags = XDL_EMIT_NO_HUNK_HDR; + if (xdi_diff_outf(&mf1, &mf2, NULL, + patch_id_consume, &data, &xpp, &xecfg)) + return error("unable to generate patch-id diff for %s", + p->one->path); + } + flush_one_hunk(oid, &ctx); } - if (!stable) - the_hash_algo->final_oid_fn(oid, &ctx); - return 0; } -int diff_flush_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only, int stable) +int diff_flush_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only) { struct diff_queue_struct *q = &diff_queued_diff; - int i; - int result = diff_get_patch_id(options, oid, diff_header_only, stable); + int result = diff_get_patch_id(options, oid, diff_header_only); - for (i = 0; i < q->nr; i++) - diff_free_filepair(q->queue[i]); - - free(q->queue); + diff_free_queue(q); DIFF_QUEUE_CLEAR(q); return result; @@ -6466,7 +6511,6 @@ void diff_free(struct diff_options *options) diff_free_file(options); diff_free_ignore_regex(options); clear_pathspec(&options->pathspec); - FREE_AND_NULL(options->parseopts); } void diff_flush(struct diff_options *options) @@ -6560,10 +6604,8 @@ void diff_flush(struct diff_options *options) if (output_format & DIFF_FORMAT_CALLBACK) options->format_callback(q, options, options->format_callback_data); - for (i = 0; i < q->nr; i++) - diff_free_filepair(q->queue[i]); free_queue: - free(q->queue); + diff_free_queue(q); DIFF_QUEUE_CLEAR(q); diff_free(options); |