diff options
Diffstat (limited to 'third_party/git/diff.c')
-rw-r--r-- | third_party/git/diff.c | 415 |
1 files changed, 256 insertions, 159 deletions
diff --git a/third_party/git/diff.c b/third_party/git/diff.c index efe42b341ae1..2bb2f8f57e8b 100644 --- a/third_party/git/diff.c +++ b/third_party/git/diff.c @@ -20,12 +20,12 @@ #include "hashmap.h" #include "ll-merge.h" #include "string-list.h" -#include "argv-array.h" +#include "strvec.h" #include "graph.h" #include "packfile.h" #include "parse-options.h" #include "help.h" -#include "fetch-object.h" +#include "promisor-remote.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -48,6 +48,7 @@ static const char *diff_order_file_cfg; int diff_auto_refresh_index = 1; static int diff_mnemonic_prefix; static int diff_no_prefix; +static int diff_relative; static int diff_stat_graph_width; static int diff_dirstat_permille_default = 30; static struct diff_options default_diff_options; @@ -386,6 +387,10 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) diff_no_prefix = git_config_bool(var, value); return 0; } + if (!strcmp(var, "diff.relative")) { + diff_relative = git_config_bool(var, value); + return 0; + } if (!strcmp(var, "diff.statgraphwidth")) { diff_stat_graph_width = git_config_int(var, value); return 0; @@ -414,14 +419,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb) return 0; } - 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; @@ -450,6 +447,14 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) return color_parse(value, diff_colors[slot]); } + if (!strcmp(var, "diff.wserrorhighlight")) { + int val = parse_ws_error_highlight(value); + if (val < 0) + return -1; + ws_error_highlight_default = val; + return 0; + } + /* like GNU diff's --suppress-blank-empty option */ if (!strcmp(var, "diff.suppressblankempty") || /* for backwards compatibility */ @@ -477,14 +482,14 @@ int git_diff_basic_config(const char *var, const char *value, void *cb) static char *quote_two(const char *one, const char *two) { - int need_one = quote_c_style(one, NULL, NULL, 1); - int need_two = quote_c_style(two, NULL, NULL, 1); + int need_one = quote_c_style(one, NULL, NULL, CQUOTE_NODQ); + int need_two = quote_c_style(two, NULL, NULL, CQUOTE_NODQ); struct strbuf res = STRBUF_INIT; if (need_one + need_two) { strbuf_addch(&res, '"'); - quote_c_style(one, &res, NULL, 1); - quote_c_style(two, &res, NULL, 1); + quote_c_style(one, &res, NULL, CQUOTE_NODQ); + quote_c_style(two, &res, NULL, CQUOTE_NODQ); strbuf_addch(&res, '"'); } else { strbuf_addstr(&res, one); @@ -573,7 +578,7 @@ static int fill_mmfile(struct repository *r, mmfile_t *mf, mf->size = 0; return 0; } - else if (diff_populate_filespec(r, one, 0)) + else if (diff_populate_filespec(r, one, NULL)) return -1; mf->ptr = one->data; @@ -585,9 +590,13 @@ static int fill_mmfile(struct repository *r, mmfile_t *mf, static unsigned long diff_filespec_size(struct repository *r, struct diff_filespec *one) { + struct diff_populate_filespec_options dpf_options = { + .check_size_only = 1, + }; + if (!DIFF_FILE_VALID(one)) return 0; - diff_populate_filespec(r, one, CHECK_SIZE_ONLY); + diff_populate_filespec(r, one, &dpf_options); return one->size; } @@ -933,16 +942,18 @@ static int cmp_in_block_with_wsd(const struct diff_options *o, } static int moved_entry_cmp(const void *hashmap_cmp_fn_data, - const void *entry, - const void *entry_or_key, + const struct hashmap_entry *eptr, + const struct hashmap_entry *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; + const struct moved_entry *a, *b; unsigned flags = diffopt->color_moved_ws_handling & XDF_WHITESPACE_FLAGS; + a = container_of(eptr, const struct moved_entry, ent); + b = container_of(entry_or_key, const struct moved_entry, ent); + if (diffopt->color_moved_ws_handling & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) /* @@ -964,8 +975,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; + unsigned int hash = xdiff_hash_string(l->line, l->len, flags); - ret->ent.hash = xdiff_hash_string(l->line, l->len, flags); + hashmap_entry_init(&ret->ent, hash); ret->es = l; ret->next_line = NULL; @@ -1002,7 +1014,7 @@ static void add_lines_to_move_detection(struct diff_options *o, if (prev_line && prev_line->es->s == o->emitted_symbols->buf[n].s) prev_line->next_line = key; - hashmap_add(hm, key); + hashmap_add(hm, &key->ent); prev_line = key; } } @@ -1018,7 +1030,7 @@ static void pmb_advance_or_null(struct diff_options *o, 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)) { + if (cur && !hm->cmpfn(o, &cur->ent, &match->ent, NULL)) { pmb[i].match = cur; } else { pmb[i].match = NULL; @@ -1035,7 +1047,7 @@ static void pmb_advance_or_null_multi_match(struct diff_options *o, int i; char *got_match = xcalloc(1, pmb_nr); - for (; match; match = hashmap_get_next(hm, match)) { + hashmap_for_each_entry_from(hm, match, ent) { for (i = 0; i < pmb_nr; i++) { struct moved_entry *prev = pmb[i].match; struct moved_entry *cur = (prev && prev->next_line) ? @@ -1143,13 +1155,13 @@ static void mark_color_as_moved(struct diff_options *o, case DIFF_SYMBOL_PLUS: hm = del_lines; key = prepare_entry(o, n); - match = hashmap_get(hm, key, NULL); + match = hashmap_get_entry(hm, key, ent, NULL); free(key); break; case DIFF_SYMBOL_MINUS: hm = add_lines; key = prepare_entry(o, n); - match = hashmap_get(hm, key, NULL); + match = hashmap_get_entry(hm, key, ent, NULL); free(key); break; default: @@ -1188,7 +1200,7 @@ static void mark_color_as_moved(struct diff_options *o, * The current line is the start of a new block. * Setup the set of potential blocks. */ - for (; match; match = hashmap_get_next(hm, match)) { + hashmap_for_each_entry_from(hm, match, ent) { ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc); if (o->color_moved_ws_handling & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) { @@ -2492,22 +2504,6 @@ static void pprint_rename(struct strbuf *name, const char *a, const char *b) } } -struct diffstat_t { - int nr; - int alloc; - struct diffstat_file { - char *from_name; - char *name; - char *print_name; - const char *comments; - unsigned is_unmerged:1; - unsigned is_binary:1; - unsigned is_renamed:1; - unsigned is_interesting:1; - uintmax_t added, deleted; - } **files; -}; - static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat, const char *name_a, const char *name_b) @@ -2548,7 +2544,7 @@ static int scale_linear(int it, int width, int max_change) /* * make sure that at least one '-' or '+' is printed if * there is any change to this path. The easiest way is to - * scale linearly as if the alloted width is one column shorter + * scale linearly as if the allotted width is one column shorter * than it is, and then add 1 to the result. */ return 1 + (it * (width - 1) / max_change); @@ -3033,6 +3029,9 @@ static void show_dirstat(struct diff_options *options) struct diff_filepair *p = q->queue[i]; const char *name; unsigned long copied, added, damage; + struct diff_populate_filespec_options dpf_options = { + .check_size_only = 1, + }; name = p->two->path ? p->two->path : p->one->path; @@ -3060,19 +3059,19 @@ static void show_dirstat(struct diff_options *options) } if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) { - diff_populate_filespec(options->repo, p->one, 0); - diff_populate_filespec(options->repo, p->two, 0); + diff_populate_filespec(options->repo, p->one, NULL); + diff_populate_filespec(options->repo, p->two, NULL); 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(options->repo, p->one, CHECK_SIZE_ONLY); + diff_populate_filespec(options->repo, p->one, &dpf_options); copied = added = 0; diff_free_filespec_data(p->one); } else if (DIFF_FILE_VALID(p->two)) { - diff_populate_filespec(options->repo, p->two, CHECK_SIZE_ONLY); + diff_populate_filespec(options->repo, p->two, &dpf_options); copied = 0; added = p->two->size; diff_free_filespec_data(p->two); @@ -3154,16 +3153,19 @@ static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *o gather_dirstat(options, &dir, changed, "", 0); } -static void free_diffstat_info(struct diffstat_t *diffstat) +static void free_diffstat_file(struct diffstat_file *f) +{ + free(f->print_name); + free(f->name); + free(f->from_name); + free(f); +} + +void free_diffstat_info(struct diffstat_t *diffstat) { int i; - for (i = 0; i < diffstat->nr; i++) { - struct diffstat_file *f = diffstat->files[i]; - free(f->print_name); - free(f->name); - free(f->from_name); - free(f); - } + for (i = 0; i < diffstat->nr; i++) + free_diffstat_file(diffstat->files[i]); free(diffstat->files); } @@ -3193,7 +3195,7 @@ static int is_conflict_marker(const char *line, int marker_size, unsigned long l for (cnt = 1; cnt < marker_size; cnt++) if (line[cnt] != firstchar) return 0; - /* line[1] thru line[marker_size-1] are same as firstchar */ + /* line[1] through line[marker_size-1] are same as firstchar */ if (len < marker_size + 1 || !isspace(line[marker_size])) return 0; return 1; @@ -3352,13 +3354,17 @@ static void emit_binary_diff(struct diff_options *o, int diff_filespec_is_binary(struct repository *r, struct diff_filespec *one) { + struct diff_populate_filespec_options dpf_options = { + .check_binary = 1, + }; + if (one->is_binary == -1) { 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(r, one, CHECK_BINARY); + diff_populate_filespec(r, one, &dpf_options); if (one->is_binary == -1 && one->data) one->is_binary = buffer_is_binary(one->data, one->size); @@ -3426,7 +3432,7 @@ 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))) { - show_submodule_summary(o, one->path ? one->path : two->path, + show_submodule_diff_summary(o, one->path ? one->path : two->path, &one->oid, &two->oid, two->dirty_submodule); return; @@ -3657,7 +3663,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b, { mmfile_t mf1, mf2; struct diffstat_file *data; - int same_contents; + int may_differ; int complete_rewrite = 0; if (!DIFF_PAIR_UNMERGED(p)) { @@ -3675,12 +3681,14 @@ static void builtin_diffstat(const char *name_a, const char *name_b, return; } - same_contents = oideq(&one->oid, &two->oid); + /* saves some reads if true, not a guarantee of diff outcome */ + may_differ = !(one->oid_valid && two->oid_valid && + oideq(&one->oid, &two->oid)); if (diff_filespec_is_binary(o->repo, one) || diff_filespec_is_binary(o->repo, two)) { data->is_binary = 1; - if (same_contents) { + if (!may_differ) { data->added = 0; data->deleted = 0; } else { @@ -3690,13 +3698,13 @@ static void builtin_diffstat(const char *name_a, const char *name_b, } else if (complete_rewrite) { - diff_populate_filespec(o->repo, one, 0); - diff_populate_filespec(o->repo, two, 0); + diff_populate_filespec(o->repo, one, NULL); + diff_populate_filespec(o->repo, two, NULL); data->deleted = count_lines(one->data, one->size); data->added = count_lines(two->data, two->size); } - else if (!same_contents) { + else if (may_differ) { /* Crazy xdl interfaces.. */ xpparam_t xpp; xdemitconf_t xecfg; @@ -3715,6 +3723,27 @@ static void builtin_diffstat(const char *name_a, const char *name_b, if (xdi_diff_outf(&mf1, &mf2, discard_hunk_line, diffstat_consume, diffstat, &xpp, &xecfg)) die("unable to generate diffstat for %s", one->path); + + if (DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two)) { + struct diffstat_file *file = + diffstat->files[diffstat->nr - 1]; + /* + * Omit diffstats of modified files where nothing changed. + * Even if may_differ, this might be the case due to + * ignoring whitespace changes, etc. + * + * But note that we special-case additions, deletions, + * renames, and mode changes as adding an empty file, + * for example is still of interest. + */ + if ((p->status == DIFF_STATUS_MODIFIED) + && !file->added + && !file->deleted + && one->mode == two->mode) { + free_diffstat_file(file); + diffstat->nr--; + } + } } diff_free_filespec_data(one); @@ -3927,9 +3956,10 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only) */ int diff_populate_filespec(struct repository *r, struct diff_filespec *s, - unsigned int flags) + const struct diff_populate_filespec_options *options) { - int size_only = flags & CHECK_SIZE_ONLY; + int size_only = options ? options->check_size_only : 0; + int check_binary = options ? options->check_binary : 0; int err = 0; int conv_flags = global_conv_flags_eol; /* @@ -3999,7 +4029,7 @@ int diff_populate_filespec(struct repository *r, * opening the file and inspecting the contents, this * is probably fine. */ - if ((flags & CHECK_BINARY) && + if (check_binary && s->size > big_file_threshold && s->is_binary == -1) { s->is_binary = 1; return 0; @@ -4024,12 +4054,30 @@ int diff_populate_filespec(struct repository *r, } } else { - enum object_type type; - if (size_only || (flags & CHECK_BINARY)) { - type = oid_object_info(r, &s->oid, &s->size); - if (type < 0) - die("unable to read %s", - oid_to_hex(&s->oid)); + struct object_info info = { + .sizep = &s->size + }; + + if (!(size_only || check_binary)) + /* + * Set contentp, since there is no chance that merely + * the size is sufficient. + */ + info.contentp = &s->data; + + if (options && options->missing_object_cb) { + if (!oid_object_info_extended(r, &s->oid, &info, + OBJECT_INFO_LOOKUP_REPLACE | + OBJECT_INFO_SKIP_FETCH_OBJECT)) + goto object_read; + options->missing_object_cb(options->missing_object_data); + } + if (oid_object_info_extended(r, &s->oid, &info, + OBJECT_INFO_LOOKUP_REPLACE)) + die("unable to read %s", oid_to_hex(&s->oid)); + +object_read: + if (size_only || check_binary) { if (size_only) return 0; if (s->size > big_file_threshold && s->is_binary == -1) { @@ -4037,9 +4085,12 @@ int diff_populate_filespec(struct repository *r, return 0; } } - s->data = read_object_file(&s->oid, &type, &s->size); - if (!s->data) - die("unable to read %s", oid_to_hex(&s->oid)); + if (!info.contentp) { + info.contentp = &s->data; + if (oid_object_info_extended(r, &s->oid, &info, + OBJECT_INFO_LOOKUP_REPLACE)) + die("unable to read %s", oid_to_hex(&s->oid)); + } s->should_free = 1; } return 0; @@ -4075,6 +4126,9 @@ static void prep_temp_blob(struct index_state *istate, 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_"); @@ -4084,7 +4138,7 @@ static void prep_temp_blob(struct index_state *istate, if (!temp->tempfile) die_errno("unable to create temp-file"); if (convert_to_working_tree(istate, path, - (const char *)blob, (size_t)size, &buf)) { + (const char *)blob, (size_t)size, &buf, &meta)) { blob = buf.buf; size = buf.len; } @@ -4154,7 +4208,7 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r, return temp; } else { - if (diff_populate_filespec(r, one, 0)) + if (diff_populate_filespec(r, one, NULL)) die("cannot read data blob for %s", one->path); prep_temp_blob(r->index, name, temp, one->data, one->size, @@ -4164,14 +4218,14 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r, } static void add_external_diff_name(struct repository *r, - struct argv_array *argv, + struct strvec *argv, const char *name, struct diff_filespec *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); + strvec_push(argv, temp->name); + strvec_push(argv, temp->hex); + strvec_push(argv, temp->mode); } /* An external diff command takes: @@ -4188,12 +4242,12 @@ static void run_external_diff(const char *pgm, const char *xfrm_msg, struct diff_options *o) { - struct argv_array argv = ARGV_ARRAY_INIT; - struct argv_array env = ARGV_ARRAY_INIT; + struct strvec argv = STRVEC_INIT; + struct strvec env = STRVEC_INIT; struct diff_queue_struct *q = &diff_queued_diff; - argv_array_push(&argv, pgm); - argv_array_push(&argv, name); + strvec_push(&argv, pgm); + strvec_push(&argv, name); if (one && two) { add_external_diff_name(o->repo, &argv, name, one); @@ -4201,22 +4255,22 @@ static void run_external_diff(const char *pgm, add_external_diff_name(o->repo, &argv, name, two); else { add_external_diff_name(o->repo, &argv, other, two); - argv_array_push(&argv, other); - argv_array_push(&argv, xfrm_msg); + strvec_push(&argv, other); + strvec_push(&argv, xfrm_msg); } } - argv_array_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter); - argv_array_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr); + strvec_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter); + strvec_pushf(&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.argv, RUN_USING_SHELL, NULL, env.argv)) + if (run_command_v_opt_cd_env(argv.v, RUN_USING_SHELL, NULL, env.v)) die(_("external diff died, stopping at %s"), name); remove_tempfile(); - argv_array_clear(&argv); - argv_array_clear(&env); + strvec_clear(&argv); + strvec_clear(&env); } static int similarity_index(struct diff_filepair *p) @@ -4291,7 +4345,10 @@ static void fill_metainfo(struct strbuf *msg, } if (one && two && !oideq(&one->oid, &two->oid)) { const unsigned hexsz = the_hash_algo->hexsz; - int abbrev = o->flags.full_index ? hexsz : DEFAULT_ABBREV; + int abbrev = o->abbrev ? o->abbrev : DEFAULT_ABBREV; + + if (o->flags.full_index) + abbrev = hexsz; if (o->flags.binary) { mmfile_t mf; @@ -4515,6 +4572,7 @@ void repo_diff_setup(struct repository *r, struct diff_options *options) options->interhunkcontext = diff_interhunk_context_default; options->ws_error_highlight = ws_error_highlight_default; options->flags.rename_empty = 1; + options->flags.relative_name = diff_relative; options->objfind = NULL; /* pathchange left =NULL by default */ @@ -5172,8 +5230,7 @@ static int diff_opt_relative(const struct option *opt, { struct diff_options *options = opt->value; - BUG_ON_OPT_NEG(unset); - options->flags.relative_name = 1; + options->flags.relative_name = !unset; if (arg) options->prefix = arg; return 0; @@ -5469,7 +5526,7 @@ static void prep_parse_options(struct diff_options *options) OPT_GROUP(N_("Other diff options")), OPT_CALLBACK_F(0, "relative", options, N_("<prefix>"), N_("when run from subdir, exclude changes outside and show relative paths"), - PARSE_OPT_NONEG | PARSE_OPT_OPTARG, + PARSE_OPT_OPTARG, diff_opt_relative), OPT_BOOL('a', "text", &options->flags.text, N_("treat all files as text")), @@ -5978,7 +6035,7 @@ static void diff_summary(struct diff_options *opt, struct diff_filepair *p) } struct patch_id_t { - git_SHA_CTX *ctx; + git_hash_ctx *ctx; int patchlen; }; @@ -5995,16 +6052,16 @@ static int remove_space(char *line, int len) return dst - line; } -void flush_one_hunk(struct object_id *result, git_SHA_CTX *ctx) +void flush_one_hunk(struct object_id *result, git_hash_ctx *ctx) { unsigned char hash[GIT_MAX_RAWSZ]; unsigned short carry = 0; int i; - git_SHA1_Final(hash, ctx); - git_SHA1_Init(ctx); + the_hash_algo->final_fn(hash, ctx); + the_hash_algo->init_fn(ctx); /* 20-byte sum, with carry */ - for (i = 0; i < GIT_SHA1_RAWSZ; ++i) { + for (i = 0; i < the_hash_algo->rawsz; ++i) { carry += result->hash[i] + hash[i]; result->hash[i] = carry; carry >>= 8; @@ -6016,23 +6073,25 @@ static void patch_id_consume(void *priv, char *line, unsigned long len) struct patch_id_t *data = priv; int new_len; + if (len > 12 && starts_with(line, "\\ ")) + return; new_len = remove_space(line, len); - git_SHA1_Update(data->ctx, line, new_len); + the_hash_algo->update_fn(data->ctx, line, new_len); data->patchlen += new_len; } -static void patch_id_add_string(git_SHA_CTX *ctx, const char *str) +static void patch_id_add_string(git_hash_ctx *ctx, const char *str) { - git_SHA1_Update(ctx, str, strlen(str)); + the_hash_algo->update_fn(ctx, str, strlen(str)); } -static void patch_id_add_mode(git_SHA_CTX *ctx, unsigned mode) +static void patch_id_add_mode(git_hash_ctx *ctx, unsigned mode) { /* large enough for 2^32 in octal */ char buf[12]; int len = xsnprintf(buf, sizeof(buf), "%06o", mode); - git_SHA1_Update(ctx, buf, len); + the_hash_algo->update_fn(ctx, buf, len); } /* returns 0 upon success, and writes result into oid */ @@ -6040,10 +6099,10 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid { struct diff_queue_struct *q = &diff_queued_diff; int i; - git_SHA_CTX ctx; + git_hash_ctx ctx; struct patch_id_t data; - git_SHA1_Init(&ctx); + the_hash_algo->init_fn(&ctx); memset(&data, 0, sizeof(struct patch_id_t)); data.ctx = &ctx; oidclr(oid); @@ -6076,27 +6135,27 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid len2 = remove_space(p->two->path, strlen(p->two->path)); patch_id_add_string(&ctx, "diff--git"); patch_id_add_string(&ctx, "a/"); - git_SHA1_Update(&ctx, p->one->path, len1); + the_hash_algo->update_fn(&ctx, p->one->path, len1); patch_id_add_string(&ctx, "b/"); - git_SHA1_Update(&ctx, p->two->path, len2); + the_hash_algo->update_fn(&ctx, p->two->path, len2); 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/"); - git_SHA1_Update(&ctx, p->two->path, len2); + 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/"); - git_SHA1_Update(&ctx, p->one->path, len1); + the_hash_algo->update_fn(&ctx, p->one->path, len1); patch_id_add_string(&ctx, "+++/dev/null"); } else { patch_id_add_string(&ctx, "---a/"); - git_SHA1_Update(&ctx, p->one->path, len1); + the_hash_algo->update_fn(&ctx, p->one->path, len1); patch_id_add_string(&ctx, "+++b/"); - git_SHA1_Update(&ctx, p->two->path, len2); + the_hash_algo->update_fn(&ctx, p->two->path, len2); } if (diff_header_only) @@ -6108,10 +6167,10 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid 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), - GIT_SHA1_HEXSZ); + 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; } @@ -6128,7 +6187,7 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid } if (!stable) - git_SHA1_Final(oid->hash, &ctx); + the_hash_algo->final_fn(oid->hash, &ctx); return 0; } @@ -6230,8 +6289,10 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o) if (o->color_moved == COLOR_MOVED_ZEBRA_DIM) dim_moved_lines(o); - hashmap_free(&add_lines, 1); - hashmap_free(&del_lines, 1); + hashmap_free_entries(&add_lines, struct moved_entry, + ent); + hashmap_free_entries(&del_lines, struct moved_entry, + ent); } for (i = 0; i < esm.nr; i++) @@ -6278,12 +6339,7 @@ void diff_flush(struct diff_options *options) dirstat_by_line) { struct diffstat_t diffstat; - memset(&diffstat, 0, sizeof(struct diffstat_t)); - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - if (check_pair_status(p)) - diff_flush_stat(p, options, &diffstat); - } + compute_diffstat(options, &diffstat, q); if (output_format & DIFF_FORMAT_NUMSTAT) show_numstat(&diffstat, options); if (output_format & DIFF_FORMAT_DIFFSTAT) @@ -6423,9 +6479,9 @@ static int diff_filespec_is_identical(struct repository *r, { if (S_ISGITLINK(one->mode)) return 0; - if (diff_populate_filespec(r, one, 0)) + if (diff_populate_filespec(r, one, NULL)) return 0; - if (diff_populate_filespec(r, two, 0)) + if (diff_populate_filespec(r, two, NULL)) return 0; return !memcmp(one->data, two->data, one->size); } @@ -6433,6 +6489,12 @@ static int diff_filespec_is_identical(struct repository *r, static int diff_filespec_check_stat_unmatch(struct repository *r, struct diff_filepair *p) { + struct diff_populate_filespec_options dpf_options = { + .check_size_only = 1, + .missing_object_cb = diff_queued_diff_prefetch, + .missing_object_data = r, + }; + if (p->done_skip_stat_unmatch) return p->skip_stat_unmatch_result; @@ -6455,8 +6517,8 @@ static int diff_filespec_check_stat_unmatch(struct repository *r, !DIFF_FILE_VALID(p->two) || (p->one->oid_valid && p->two->oid_valid) || (p->one->mode != p->two->mode) || - diff_populate_filespec(r, p->one, CHECK_SIZE_ONLY) || - diff_populate_filespec(r, p->two, CHECK_SIZE_ONLY) || + diff_populate_filespec(r, p->one, &dpf_options) || + diff_populate_filespec(r, p->two, &dpf_options) || (p->one->size != p->two->size) || !diff_filespec_is_identical(r, p->one, p->two)) /* (2) */ p->skip_stat_unmatch_result = 1; @@ -6507,41 +6569,59 @@ void diffcore_fix_diff_index(void) QSORT(q->queue, q->nr, diffnamecmp); } -static void add_if_missing(struct repository *r, - struct oid_array *to_fetch, - const struct diff_filespec *filespec) +void diff_add_if_missing(struct repository *r, + struct oid_array *to_fetch, + const struct diff_filespec *filespec) { if (filespec && filespec->oid_valid && + !S_ISGITLINK(filespec->mode) && oid_object_info_extended(r, &filespec->oid, NULL, OBJECT_INFO_FOR_PREFETCH)) oid_array_append(to_fetch, &filespec->oid); } -void diffcore_std(struct diff_options *options) +void diff_queued_diff_prefetch(void *repository) { - if (options->repo == the_repository && - repository_format_partial_clone) { - /* - * Prefetch the diff pairs that are about to be flushed. - */ - int i; - struct diff_queue_struct *q = &diff_queued_diff; - struct oid_array to_fetch = OID_ARRAY_INIT; + struct repository *repo = repository; + int i; + struct diff_queue_struct *q = &diff_queued_diff; + struct oid_array to_fetch = OID_ARRAY_INIT; - for (i = 0; i < q->nr; i++) { - struct diff_filepair *p = q->queue[i]; - add_if_missing(options->repo, &to_fetch, p->one); - add_if_missing(options->repo, &to_fetch, p->two); - } - if (to_fetch.nr) - /* - * NEEDSWORK: Consider deduplicating the OIDs sent. - */ - fetch_objects(repository_format_partial_clone, - to_fetch.oid, to_fetch.nr); - oid_array_clear(&to_fetch); + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + diff_add_if_missing(repo, &to_fetch, p->one); + diff_add_if_missing(repo, &to_fetch, p->two); } + /* + * NEEDSWORK: Consider deduplicating the OIDs sent. + */ + promisor_remote_get_direct(repo, to_fetch.oid, to_fetch.nr); + + oid_array_clear(&to_fetch); +} + +void diffcore_std(struct diff_options *options) +{ + int output_formats_to_prefetch = DIFF_FORMAT_DIFFSTAT | + DIFF_FORMAT_NUMSTAT | + DIFF_FORMAT_PATCH | + DIFF_FORMAT_SHORTSTAT | + DIFF_FORMAT_DIRSTAT; + + /* + * Check if the user requested a blob-data-requiring diff output and/or + * break-rewrite detection (which requires blob data). If yes, prefetch + * the diff pairs. + * + * If no prefetching occurs, diffcore_rename() will prefetch if it + * decides that it needs inexact rename detection. + */ + if (options->repo == the_repository && has_promisor_remote() && + (options->output_format & output_formats_to_prefetch || + options->pickaxe_opts & DIFF_PICKAXE_KINDS_MASK)) + diff_queued_diff_prefetch(options->repo); + /* NOTE please keep the following in sync with diff_tree_combined() */ if (options->skip_stat_unmatch) diffcore_skip_stat_unmatch(options); @@ -6616,6 +6696,20 @@ static int is_submodule_ignored(const char *path, struct diff_options *options) return ignored; } +void compute_diffstat(struct diff_options *options, + struct diffstat_t *diffstat, + struct diff_queue_struct *q) +{ + int i; + + memset(diffstat, 0, sizeof(struct diffstat_t)); + for (i = 0; i < q->nr; i++) { + struct diff_filepair *p = q->queue[i]; + if (check_pair_status(p)) + diff_flush_stat(p, options, diffstat); + } +} + void diff_addremove(struct diff_options *options, int addremove, unsigned mode, const struct object_id *oid, @@ -6700,8 +6794,11 @@ void diff_change(struct diff_options *options, return; if (options->flags.quick && options->skip_stat_unmatch && - !diff_filespec_check_stat_unmatch(options->repo, p)) + !diff_filespec_check_stat_unmatch(options->repo, p)) { + diff_free_filespec_data(p->one); + diff_free_filespec_data(p->two); return; + } options->flags.has_changes = 1; } @@ -6773,7 +6870,7 @@ size_t fill_textconv(struct repository *r, *outbuf = ""; return 0; } - if (diff_populate_filespec(r, df, 0)) + if (diff_populate_filespec(r, df, NULL)) die("unable to read files to diff"); *outbuf = df->data; return df->size; |