From: Junio C Hamano Date: Wed, 8 May 2019 15:37:24 +0000 (+0900) Subject: Merge branch 'nd/sha1-name-c-wo-the-repository' X-Git-Tag: v2.22.0-rc0~25 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/0b179f3175d1a152b1d22ce8352efda34b258ce2?hp=-c Merge branch 'nd/sha1-name-c-wo-the-repository' Further code clean-up to allow the lowest level of name-to-object mapping layer to work with a passed-in repository other than the default one. * nd/sha1-name-c-wo-the-repository: (34 commits) sha1-name.c: remove the_repo from get_oid_mb() sha1-name.c: remove the_repo from other get_oid_* sha1-name.c: remove the_repo from maybe_die_on_misspelt_object_name submodule-config.c: use repo_get_oid for reading .gitmodules sha1-name.c: add repo_get_oid() sha1-name.c: remove the_repo from get_oid_with_context_1() sha1-name.c: remove the_repo from resolve_relative_path() sha1-name.c: remove the_repo from diagnose_invalid_index_path() sha1-name.c: remove the_repo from handle_one_ref() sha1-name.c: remove the_repo from get_oid_1() sha1-name.c: remove the_repo from get_oid_basic() sha1-name.c: remove the_repo from get_describe_name() sha1-name.c: remove the_repo from get_oid_oneline() sha1-name.c: add repo_interpret_branch_name() sha1-name.c: remove the_repo from interpret_branch_mark() sha1-name.c: remove the_repo from interpret_nth_prior_checkout() sha1-name.c: remove the_repo from get_short_oid() sha1-name.c: add repo_for_each_abbrev() sha1-name.c: store and use repo in struct disambiguate_state sha1-name.c: add repo_find_unique_abbrev_r() ... --- 0b179f3175d1a152b1d22ce8352efda34b258ce2 diff --combined builtin/rebase.c index 2e41ad5644,c064909329..966b953e86 --- a/builtin/rebase.c +++ b/builtin/rebase.c @@@ -46,6 -46,29 +46,6 @@@ enum rebase_type REBASE_PRESERVE_MERGES }; -static int use_builtin_rebase(void) -{ - struct child_process cp = CHILD_PROCESS_INIT; - struct strbuf out = STRBUF_INIT; - int ret, env = git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1); - - if (env != -1) - return env; - - argv_array_pushl(&cp.args, - "config", "--bool", "rebase.usebuiltin", NULL); - cp.git_cmd = 1; - if (capture_command(&cp, &out, 6)) { - strbuf_release(&out); - return 1; - } - - strbuf_trim(&out); - ret = !strcmp("true", out.buf); - strbuf_release(&out); - return ret; -} - struct rebase_options { enum rebase_type type; const char *state_dir; @@@ -83,7 -106,6 +83,7 @@@ char *strategy, *strategy_opts; struct strbuf git_format_patch_opt; int reschedule_failed_exec; + int use_legacy_rebase; }; static int is_interactive(struct rebase_options *opts) @@@ -852,11 -874,6 +852,11 @@@ static int rebase_config(const char *va return 0; } + if (!strcmp(var, "rebase.usebuiltin")) { + opts->use_legacy_rebase = !git_config_bool(var, value); + return 0; + } + return git_default_config(var, value, data); } @@@ -1088,8 -1105,8 +1088,8 @@@ int cmd_rebase(int argc, const char **a PARSE_OPT_NOARG | PARSE_OPT_NONEG, parse_opt_interactive }, OPT_SET_INT('p', "preserve-merges", &options.type, - N_("try to recreate merges instead of ignoring " - "them"), REBASE_PRESERVE_MERGES), + N_("(DEPRECATED) try to recreate merges instead of " + "ignoring them"), REBASE_PRESERVE_MERGES), OPT_BOOL(0, "rerere-autoupdate", &options.allow_rerere_autoupdate, N_("allow rerere to update index with resolved " @@@ -1131,6 -1148,22 +1131,6 @@@ }; int i; - /* - * NEEDSWORK: Once the builtin rebase has been tested enough - * and git-legacy-rebase.sh is retired to contrib/, this preamble - * can be removed. - */ - - if (!use_builtin_rebase()) { - const char *path = mkpath("%s/git-legacy-rebase", - git_exec_path()); - - if (sane_execvp(path, (char **)argv) < 0) - die_errno(_("could not exec %s"), path); - else - BUG("sane_execvp() returned???"); - } - if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_rebase_usage, builtin_rebase_options); @@@ -1141,11 -1174,6 +1141,11 @@@ git_config(rebase_config, &options); + if (options.use_legacy_rebase || + !git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1)) + warning(_("the rebase.useBuiltin support has been removed!\n" + "See its entry in 'git help config' for details.")); + strbuf_reset(&buf); strbuf_addf(&buf, "%s/applying", apply_dir()); if(file_exists(buf.buf)) @@@ -1189,10 -1217,6 +1189,10 @@@ usage_with_options(builtin_rebase_usage, builtin_rebase_options); + if (options.type == REBASE_PRESERVE_MERGES) + warning(_("git rebase --preserve-merges is deprecated. " + "Use --rebase-merges instead.")); + if (action != NO_ACTION && !in_progress) die(_("No rebase in progress?")); setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0); @@@ -1568,8 -1592,7 +1568,7 @@@ branch_name = options.head_name; } else { - free(options.head_name); - options.head_name = NULL; + FREE_AND_NULL(options.head_name); branch_name = "HEAD"; } if (get_oid("HEAD", &options.orig_head)) @@@ -1769,7 -1792,7 +1768,7 @@@ * we just fast-forwarded. */ strbuf_reset(&msg); - if (!oidcmp(&merge_base, &options.orig_head)) { + if (oideq(&merge_base, &options.orig_head)) { printf(_("Fast-forwarded %s to %s.\n"), branch_name, options.onto_name); strbuf_addf(&msg, "rebase finished: %s onto %s", diff --combined cache.h index 9f2fb7b2ea,9a600a8b50..67cc2e1806 --- a/cache.h +++ b/cache.h @@@ -339,9 -339,7 +339,9 @@@ struct index_state struct cache_time timestamp; unsigned name_hash_initialized : 1, initialized : 1, - drop_cache_tree : 1; + drop_cache_tree : 1, + updated_workdir : 1, + updated_skipworktree : 1; struct hashmap name_hash; struct hashmap dir_hash; struct object_id oid; @@@ -1047,8 -1045,10 +1047,10 @@@ extern void check_repository_format(voi * Note that while this version avoids the static buffer, it is not fully * reentrant, as it calls into other non-reentrant git code. */ - extern const char *find_unique_abbrev(const struct object_id *oid, int len); - extern int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len); + const char *repo_find_unique_abbrev(struct repository *r, const struct object_id *oid, int len); + #define find_unique_abbrev(oid, len) repo_find_unique_abbrev(the_repository, oid, len) + int repo_find_unique_abbrev_r(struct repository *r, char *hex, const struct object_id *oid, int len); + #define find_unique_abbrev_r(hex, oid, len) repo_find_unique_abbrev_r(the_repository, hex, oid, len) extern const unsigned char null_sha1[GIT_MAX_RAWSZ]; extern const struct object_id null_oid; @@@ -1333,7 -1333,7 +1335,7 @@@ static inline int hex2chr(const char *s #define FALLBACK_DEFAULT_ABBREV 7 struct object_context { - unsigned mode; + unsigned short mode; /* * symlink_path is only used by get_tree_entry_follow_symlinks, * and only for symlinks that point outside the repository. @@@ -1380,20 -1380,31 +1382,32 @@@ enum get_oid_result */ }; - extern int get_oid(const char *str, struct object_id *oid); - extern int get_oidf(struct object_id *oid, const char *fmt, ...); - extern int get_oid_commit(const char *str, struct object_id *oid); - extern int get_oid_committish(const char *str, struct object_id *oid); - extern int get_oid_tree(const char *str, struct object_id *oid); - extern int get_oid_treeish(const char *str, struct object_id *oid); - extern int get_oid_blob(const char *str, struct object_id *oid); - extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix); + int repo_get_oid(struct repository *r, const char *str, struct object_id *oid); ++int get_oidf(struct object_id *oid, const char *fmt, ...); + int repo_get_oid_commit(struct repository *r, const char *str, struct object_id *oid); + int repo_get_oid_committish(struct repository *r, const char *str, struct object_id *oid); + int repo_get_oid_tree(struct repository *r, const char *str, struct object_id *oid); + int repo_get_oid_treeish(struct repository *r, const char *str, struct object_id *oid); + int repo_get_oid_blob(struct repository *r, const char *str, struct object_id *oid); + int repo_get_oid_mb(struct repository *r, const char *str, struct object_id *oid); + void maybe_die_on_misspelt_object_name(struct repository *repo, + const char *name, + const char *prefix); extern enum get_oid_result get_oid_with_context(struct repository *repo, const char *str, unsigned flags, struct object_id *oid, struct object_context *oc); + #define get_oid(str, oid) repo_get_oid(the_repository, str, oid) + #define get_oid_commit(str, oid) repo_get_oid_commit(the_repository, str, oid) + #define get_oid_committish(str, oid) repo_get_oid_committish(the_repository, str, oid) + #define get_oid_tree(str, oid) repo_get_oid_tree(the_repository, str, oid) + #define get_oid_treeish(str, oid) repo_get_oid_treeish(the_repository, str, oid) + #define get_oid_blob(str, oid) repo_get_oid_blob(the_repository, str, oid) + #define get_oid_mb(str, oid) repo_get_oid_mb(the_repository, str, oid) + typedef int each_abbrev_fn(const struct object_id *oid, void *); - extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *); + int repo_for_each_abbrev(struct repository *r, const char *prefix, each_abbrev_fn, void *); + #define for_each_abbrev(prefix, fn, data) repo_for_each_abbrev(the_repository, prefix, fn, data) extern int set_disambiguate_hint_config(const char *var, const char *value); @@@ -1471,9 -1482,12 +1485,12 @@@ extern int parse_oid_hex(const char *he #define INTERPRET_BRANCH_LOCAL (1<<0) #define INTERPRET_BRANCH_REMOTE (1<<1) #define INTERPRET_BRANCH_HEAD (1<<2) - extern int interpret_branch_name(const char *str, int len, struct strbuf *, - unsigned allowed); - extern int get_oid_mb(const char *str, struct object_id *oid); + int repo_interpret_branch_name(struct repository *r, + const char *str, int len, + struct strbuf *buf, + unsigned allowed); + #define interpret_branch_name(str, len, buf, allowed) \ + repo_interpret_branch_name(the_repository, str, len, buf, allowed) extern int validate_headref(const char *ref); @@@ -1487,8 -1501,11 +1504,11 @@@ extern void *read_object_with_reference unsigned long *size, struct object_id *oid_ret); - extern struct object *peel_to_type(const char *name, int namelen, - struct object *o, enum object_type); + struct object *repo_peel_to_type(struct repository *r, + const char *name, int namelen, + struct object *o, enum object_type); + #define peel_to_type(name, namelen, obj, type) \ + repo_peel_to_type(the_repository, name, namelen, obj, type) enum date_mode_type { DATE_NORMAL = 0, @@@ -1555,10 -1572,6 +1575,10 @@@ extern const char *git_sequence_editor( extern const char *git_pager(int stdout_is_tty); extern int is_terminal_dumb(void); extern int git_ident_config(const char *, const char *, void *); +/* + * Prepare an ident to fall back on if the user didn't configure it. + */ +void prepare_fallback_ident(const char *name, const char *email); extern void reset_ident_date(void); struct ident_split { diff --combined commit-graph.c index 66865acbd7,155a270457..7bcc9bb7e0 --- a/commit-graph.c +++ b/commit-graph.c @@@ -80,30 -80,25 +80,30 @@@ static int commit_graph_compatible(stru return 1; } -struct commit_graph *load_commit_graph_one(const char *graph_file) +int open_commit_graph(const char *graph_file, int *fd, struct stat *st) +{ + *fd = git_open(graph_file); + if (*fd < 0) + return 0; + if (fstat(*fd, st)) { + close(*fd); + return 0; + } + return 1; +} + +struct commit_graph *load_commit_graph_one_fd_st(int fd, struct stat *st) { void *graph_map; size_t graph_size; - struct stat st; struct commit_graph *ret; - int fd = git_open(graph_file); - if (fd < 0) - return NULL; - if (fstat(fd, &st)) { - close(fd); - return NULL; - } - graph_size = xsize_t(st.st_size); + graph_size = xsize_t(st->st_size); if (graph_size < GRAPH_MIN_SIZE) { close(fd); - die(_("graph file %s is too small"), graph_file); + error(_("commit-graph file is too small")); + return NULL; } graph_map = xmmap(NULL, graph_size, PROT_READ, MAP_PRIVATE, fd, 0); ret = parse_commit_graph(graph_map, fd, graph_size); @@@ -111,41 -106,12 +111,41 @@@ if (!ret) { munmap(graph_map, graph_size); close(fd); - exit(1); } return ret; } +static int verify_commit_graph_lite(struct commit_graph *g) +{ + /* + * Basic validation shared between parse_commit_graph() + * which'll be called every time the graph is used, and the + * much more expensive verify_commit_graph() used by + * "commit-graph verify". + * + * There should only be very basic checks here to ensure that + * we don't e.g. segfault in fill_commit_in_graph(), but + * because this is a very hot codepath nothing that e.g. loops + * over g->num_commits, or runs a checksum on the commit-graph + * itself. + */ + if (!g->chunk_oid_fanout) { + error("commit-graph is missing the OID Fanout chunk"); + return 1; + } + if (!g->chunk_oid_lookup) { + error("commit-graph is missing the OID Lookup chunk"); + return 1; + } + if (!g->chunk_commit_data) { + error("commit-graph is missing the Commit Data chunk"); + return 1; + } + + return 0; +} + struct commit_graph *parse_commit_graph(void *graph_map, int fd, size_t graph_size) { @@@ -167,21 -133,21 +167,21 @@@ graph_signature = get_be32(data); if (graph_signature != GRAPH_SIGNATURE) { - error(_("graph signature %X does not match signature %X"), + error(_("commit-graph signature %X does not match signature %X"), graph_signature, GRAPH_SIGNATURE); return NULL; } graph_version = *(unsigned char*)(data + 4); if (graph_version != GRAPH_VERSION) { - error(_("graph version %X does not match version %X"), + error(_("commit-graph version %X does not match version %X"), graph_version, GRAPH_VERSION); return NULL; } hash_version = *(unsigned char*)(data + 5); if (hash_version != oid_version()) { - error(_("hash version %X does not match version %X"), + error(_("commit-graph hash version %X does not match version %X"), hash_version, oid_version()); return NULL; } @@@ -204,7 -170,7 +204,7 @@@ if (data + graph_size - chunk_lookup < GRAPH_CHUNKLOOKUP_WIDTH) { - error(_("chunk lookup table entry missing; graph file may be incomplete")); + error(_("commit-graph chunk lookup table entry missing; file may be incomplete")); free(graph); return NULL; } @@@ -215,7 -181,7 +215,7 @@@ chunk_lookup += GRAPH_CHUNKLOOKUP_WIDTH; if (chunk_offset > graph_size - the_hash_algo->rawsz) { - error(_("improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32), + error(_("commit-graph improper chunk offset %08x%08x"), (uint32_t)(chunk_offset >> 32), (uint32_t)chunk_offset); free(graph); return NULL; @@@ -252,7 -218,7 +252,7 @@@ } if (chunk_repeated) { - error(_("chunk id %08x appears multiple times"), chunk_id); + error(_("commit-graph chunk id %08x appears multiple times"), chunk_id); free(graph); return NULL; } @@@ -267,25 -233,9 +267,25 @@@ last_chunk_offset = chunk_offset; } + if (verify_commit_graph_lite(graph)) + return NULL; + return graph; } +static struct commit_graph *load_commit_graph_one(const char *graph_file) +{ + + struct stat st; + int fd; + int open_ok = open_commit_graph(graph_file, &fd, &st); + + if (!open_ok) + return NULL; + + return load_commit_graph_one_fd_st(fd, &st); +} + static void prepare_commit_graph_one(struct repository *r, const char *obj_dir) { char *graph_name; @@@ -311,10 -261,6 +311,10 @@@ static int prepare_commit_graph(struct struct object_directory *odb; int config_value; + if (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0)) + die("dying as requested by the '%s' variable on commit-graph load!", + GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD); + if (r->objects->commit_graph_attempted) return !!r->objects->commit_graph; r->objects->commit_graph_attempted = 1; @@@ -397,6 -343,11 +397,11 @@@ static void fill_commit_graph_info(stru item->generation = get_be32(commit_data + g->hash_len + 8) >> 2; } + static inline void set_commit_tree(struct commit *c, struct tree *t) + { + c->maybe_tree = t; + } + static int fill_commit_in_graph(struct repository *r, struct commit *item, struct commit_graph *g, uint32_t pos) @@@ -410,7 -361,7 +415,7 @@@ item->object.parsed = 1; item->graph_pos = pos; - item->maybe_tree = NULL; + set_commit_tree(item, NULL); date_high = get_be32(commit_data + g->hash_len + 8) & 0x3; date_low = get_be32(commit_data + g->hash_len + 12); @@@ -496,7 -447,7 +501,7 @@@ static struct tree *load_tree_for_commi GRAPH_DATA_WIDTH * (c->graph_pos); hashcpy(oid.hash, commit_data); - c->maybe_tree = lookup_tree(r, &oid); + set_commit_tree(c, lookup_tree(r, &oid)); return c->maybe_tree; } @@@ -579,7 -530,7 +584,7 @@@ static void write_graph_chunk_data(stru uint32_t packedDate[2]; display_progress(progress, ++*progress_cnt); - parse_commit(*list); + parse_commit_no_graph(*list); hashwrite(f, get_commit_tree_oid(*list)->hash, hash_len); parent = (*list)->parents; @@@ -776,7 -727,7 +781,7 @@@ static void close_reachable(struct pack display_progress(progress, i + 1); commit = lookup_commit(the_repository, &oids->list[i]); - if (commit && !parse_commit(commit)) + if (commit && !parse_commit_no_graph(commit)) add_missing_parents(oids, commit); } stop_progress(&progress); @@@ -1025,7 -976,7 +1030,7 @@@ void write_commit_graph(const char *obj continue; commits.list[commits.nr] = lookup_commit(the_repository, &oids.list[i]); - parse_commit(commits.list[commits.nr]); + parse_commit_no_graph(commits.list[commits.nr]); for (parent = commits.list[commits.nr]->parents; parent; parent = parent->next) @@@ -1143,7 -1094,15 +1148,7 @@@ int verify_commit_graph(struct reposito return 1; } - verify_commit_graph_error = 0; - - if (!g->chunk_oid_fanout) - graph_report("commit-graph is missing the OID Fanout chunk"); - if (!g->chunk_oid_lookup) - graph_report("commit-graph is missing the OID Lookup chunk"); - if (!g->chunk_commit_data) - graph_report("commit-graph is missing the Commit Data chunk"); - + verify_commit_graph_error = verify_commit_graph_lite(g); if (verify_commit_graph_error) return verify_commit_graph_error; @@@ -1162,7 -1121,7 +1167,7 @@@ hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i); if (i && oidcmp(&prev_oid, &cur_oid) >= 0) - graph_report("commit-graph has incorrect OID order: %s then %s", + graph_report(_("commit-graph has incorrect OID order: %s then %s"), oid_to_hex(&prev_oid), oid_to_hex(&cur_oid)); @@@ -1172,14 -1131,14 +1177,14 @@@ uint32_t fanout_value = get_be32(g->chunk_oid_fanout + cur_fanout_pos); if (i != fanout_value) - graph_report("commit-graph has incorrect fanout value: fanout[%d] = %u != %u", + graph_report(_("commit-graph has incorrect fanout value: fanout[%d] = %u != %u"), cur_fanout_pos, fanout_value, i); cur_fanout_pos++; } graph_commit = lookup_commit(r, &cur_oid); if (!parse_commit_in_graph_one(r, g, graph_commit)) - graph_report("failed to parse %s from commit-graph", + graph_report(_("failed to parse commit %s from commit-graph"), oid_to_hex(&cur_oid)); } @@@ -1187,7 -1146,7 +1192,7 @@@ uint32_t fanout_value = get_be32(g->chunk_oid_fanout + cur_fanout_pos); if (g->num_commits != fanout_value) - graph_report("commit-graph has incorrect fanout value: fanout[%d] = %u != %u", + graph_report(_("commit-graph has incorrect fanout value: fanout[%d] = %u != %u"), cur_fanout_pos, fanout_value, i); cur_fanout_pos++; @@@ -1209,14 -1168,14 +1214,14 @@@ graph_commit = lookup_commit(r, &cur_oid); odb_commit = (struct commit *)create_object(r, cur_oid.hash, alloc_commit_node(r)); if (parse_commit_internal(odb_commit, 0, 0)) { - graph_report("failed to parse %s from object database", + graph_report(_("failed to parse commit %s from object database for commit-graph"), oid_to_hex(&cur_oid)); continue; } if (!oideq(&get_commit_tree_in_graph_one(r, g, graph_commit)->object.oid, get_commit_tree_oid(odb_commit))) - graph_report("root tree OID for commit %s in commit-graph is %s != %s", + graph_report(_("root tree OID for commit %s in commit-graph is %s != %s"), oid_to_hex(&cur_oid), oid_to_hex(get_commit_tree_oid(graph_commit)), oid_to_hex(get_commit_tree_oid(odb_commit))); @@@ -1226,13 -1185,13 +1231,13 @@@ while (graph_parents) { if (odb_parents == NULL) { - graph_report("commit-graph parent list for commit %s is too long", + graph_report(_("commit-graph parent list for commit %s is too long"), oid_to_hex(&cur_oid)); break; } if (!oideq(&graph_parents->item->object.oid, &odb_parents->item->object.oid)) - graph_report("commit-graph parent for %s is %s != %s", + graph_report(_("commit-graph parent for %s is %s != %s"), oid_to_hex(&cur_oid), oid_to_hex(&graph_parents->item->object.oid), oid_to_hex(&odb_parents->item->object.oid)); @@@ -1245,16 -1204,16 +1250,16 @@@ } if (odb_parents != NULL) - graph_report("commit-graph parent list for commit %s terminates early", + graph_report(_("commit-graph parent list for commit %s terminates early"), oid_to_hex(&cur_oid)); if (!graph_commit->generation) { if (generation_zero == GENERATION_NUMBER_EXISTS) - graph_report("commit-graph has generation number zero for commit %s, but non-zero elsewhere", + graph_report(_("commit-graph has generation number zero for commit %s, but non-zero elsewhere"), oid_to_hex(&cur_oid)); generation_zero = GENERATION_ZERO_EXISTS; } else if (generation_zero == GENERATION_ZERO_EXISTS) - graph_report("commit-graph has non-zero generation number for commit %s, but zero elsewhere", + graph_report(_("commit-graph has non-zero generation number for commit %s, but zero elsewhere"), oid_to_hex(&cur_oid)); if (generation_zero == GENERATION_ZERO_EXISTS) @@@ -1269,13 -1228,13 +1274,13 @@@ max_generation--; if (graph_commit->generation != max_generation + 1) - graph_report("commit-graph generation for commit %s is %u != %u", + graph_report(_("commit-graph generation for commit %s is %u != %u"), oid_to_hex(&cur_oid), graph_commit->generation, max_generation + 1); if (graph_commit->date != odb_commit->date) - graph_report("commit date for commit %s in commit-graph is %"PRItime" != %"PRItime, + graph_report(_("commit date for commit %s in commit-graph is %"PRItime" != %"PRItime), oid_to_hex(&cur_oid), graph_commit->date, odb_commit->date); diff --combined commit.h index 8f1f39f4c3,f1aa4c0472..cc19cb5314 --- a/commit.h +++ b/commit.h @@@ -32,7 -32,7 +32,7 @@@ struct commit /* * If the commit is loaded from the commit-graph file, then this - * member may be NULL. Only access it through get_commit_tree() + * member may be NULL. Only access it through repo_get_commit_tree() * or get_commit_tree_oid(). */ struct tree *maybe_tree; @@@ -89,12 -89,6 +89,12 @@@ static inline int repo_parse_commit(str { return repo_parse_commit_gently(r, item, 0); } + +static inline int parse_commit_no_graph(struct commit *commit) +{ + return repo_parse_commit_internal(the_repository, commit, 0, 0); +} + #ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS #define parse_commit_internal(item, quiet, use) repo_parse_commit_internal(the_repository, item, quiet, use) #define parse_commit_gently(item, quiet) repo_parse_commit_gently(the_repository, item, quiet) @@@ -149,7 -143,8 +149,8 @@@ void repo_unuse_commit_buffer(struct re */ void free_commit_buffer(struct parsed_object_pool *pool, struct commit *); - struct tree *get_commit_tree(const struct commit *); + struct tree *repo_get_commit_tree(struct repository *, const struct commit *); + #define get_commit_tree(c) repo_get_commit_tree(the_repository, c) struct object_id *get_commit_tree_oid(const struct commit *); /* @@@ -263,10 -258,6 +264,10 @@@ extern void setup_alternate_shallow(str extern const char *setup_temporary_shallow(const struct oid_array *extra); extern void advertise_shallow_grafts(int); +/* + * Initialize with prepare_shallow_info() or zero-initialize (equivalent to + * prepare_shallow_info with a NULL oid_array). + */ struct shallow_info { struct oid_array *shallow; int *ours, nr_ours; diff --combined dir.c index 0a3ddcf2bc,e6d97343f4..2ccd4db664 --- a/dir.c +++ b/dir.c @@@ -502,7 -502,8 +502,7 @@@ int submodule_path_match(const struct i } int report_path_error(const char *ps_matched, - const struct pathspec *pathspec, - const char *prefix) + const struct pathspec *pathspec) { /* * Make sure all pathspec matched; otherwise it is an error. @@@ -1466,11 -1467,9 +1466,11 @@@ static enum path_treatment treat_direct return path_none; } if (!(dir->flags & DIR_NO_GITLINKS)) { - struct object_id oid; - if (resolve_gitlink_ref(dirname, "HEAD", &oid) == 0) + struct strbuf sb = STRBUF_INIT; + strbuf_addstr(&sb, dirname); + if (is_nonbare_repository_dir(&sb)) return exclude ? path_excluded : path_untracked; + strbuf_release(&sb); } return path_recurse; } @@@ -2316,6 -2315,14 +2316,14 @@@ int file_exists(const char *f return lstat(f, &sb) == 0; } + int repo_file_exists(struct repository *repo, const char *path) + { + if (repo != the_repository) + BUG("do not know how to check file existence in arbitrary repo"); + + return file_exists(path); + } + static int cmp_icase(char a, char b) { if (a == b) @@@ -2546,9 -2553,13 +2554,9 @@@ struct ondisk_untracked_cache struct stat_data info_exclude_stat; struct stat_data excludes_file_stat; uint32_t dir_flags; - unsigned char info_exclude_sha1[20]; - unsigned char excludes_file_sha1[20]; - char exclude_per_dir[FLEX_ARRAY]; }; #define ouc_offset(x) offsetof(struct ondisk_untracked_cache, x) -#define ouc_size(len) (ouc_offset(exclude_per_dir) + len + 1) struct write_data { int index; /* number of written untracked_cache_dir */ @@@ -2631,21 -2642,20 +2639,21 @@@ void write_untracked_extension(struct s struct write_data wd; unsigned char varbuf[16]; int varint_len; - size_t len = strlen(untracked->exclude_per_dir); + const unsigned hashsz = the_hash_algo->rawsz; - FLEX_ALLOC_MEM(ouc, exclude_per_dir, untracked->exclude_per_dir, len); + ouc = xcalloc(1, sizeof(*ouc)); stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat); stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat); - hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.oid.hash); - hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.oid.hash); ouc->dir_flags = htonl(untracked->dir_flags); varint_len = encode_varint(untracked->ident.len, varbuf); strbuf_add(out, varbuf, varint_len); strbuf_addbuf(out, &untracked->ident); - strbuf_add(out, ouc, ouc_size(len)); + strbuf_add(out, ouc, sizeof(*ouc)); + strbuf_add(out, untracked->ss_info_exclude.oid.hash, hashsz); + strbuf_add(out, untracked->ss_excludes_file.oid.hash, hashsz); + strbuf_add(out, untracked->exclude_per_dir, strlen(untracked->exclude_per_dir) + 1); FREE_AND_NULL(ouc); if (!untracked->root) { @@@ -2758,7 -2768,7 +2766,7 @@@ static int read_one_dir(struct untracke next = data + len + 1; if (next > rd->end) return -1; - *untracked_ = untracked = xmalloc(st_add(sizeof(*untracked), len)); + *untracked_ = untracked = xmalloc(st_add3(sizeof(*untracked), len, 1)); memcpy(untracked, &ud, sizeof(ud)); memcpy(untracked->name, data, len + 1); data = next; @@@ -2832,9 -2842,6 +2840,9 @@@ struct untracked_cache *read_untracked_ int ident_len; ssize_t len; const char *exclude_per_dir; + const unsigned hashsz = the_hash_algo->rawsz; + const unsigned offset = sizeof(struct ondisk_untracked_cache); + const unsigned exclude_per_dir_offset = offset + 2 * hashsz; if (sz <= 1 || end[-1] != '\0') return NULL; @@@ -2846,7 -2853,7 +2854,7 @@@ ident = (const char *)next; next += ident_len; - if (next + ouc_size(0) > end) + if (next + exclude_per_dir_offset + 1 > end) return NULL; uc = xcalloc(1, sizeof(*uc)); @@@ -2854,15 -2861,15 +2862,15 @@@ strbuf_add(&uc->ident, ident, ident_len); load_oid_stat(&uc->ss_info_exclude, next + ouc_offset(info_exclude_stat), - next + ouc_offset(info_exclude_sha1)); + next + offset); load_oid_stat(&uc->ss_excludes_file, next + ouc_offset(excludes_file_stat), - next + ouc_offset(excludes_file_sha1)); + next + offset + hashsz); uc->dir_flags = get_be32(next + ouc_offset(dir_flags)); - exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir); + exclude_per_dir = (const char *)next + exclude_per_dir_offset; uc->exclude_per_dir = xstrdup(exclude_per_dir); /* NUL after exclude_per_dir is covered by sizeof(*ouc) */ - next += ouc_size(strlen(exclude_per_dir)); + next += exclude_per_dir_offset + strlen(exclude_per_dir) + 1; if (next >= end) goto done2; diff --combined dir.h index 823bae628b,47c5409ced..c23313b021 --- a/dir.h +++ b/dir.h @@@ -220,7 -220,7 +220,7 @@@ extern int match_pathspec(const struct const struct pathspec *pathspec, const char *name, int namelen, int prefix, char *seen, int is_dir); -extern int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix); +extern int report_path_error(const char *ps_matched, const struct pathspec *pathspec); extern int within_depth(const char *name, int namelen, int depth, int max_depth); extern int fill_directory(struct dir_struct *dir, @@@ -269,7 -269,9 +269,9 @@@ extern void add_exclude(const char *str int baselen, struct exclude_list *el, int srcpos); extern void clear_exclude_list(struct exclude_list *el); extern void clear_directory(struct dir_struct *dir); - extern int file_exists(const char *); + + int repo_file_exists(struct repository *repo, const char *path); + int file_exists(const char *); extern int is_inside_dir(const char *dir); extern int dir_inside_of(const char *subdir, const char *dir); diff --combined merge-recursive.c index 69abd6edec,ca4731a719..a7bcfcbeb4 --- a/merge-recursive.c +++ b/merge-recursive.c @@@ -115,32 -115,32 +115,32 @@@ static void collision_init(struct hashm hashmap_init(map, (hashmap_cmp_fn) collision_cmp, NULL, 0); } -static void flush_output(struct merge_options *o) +static void flush_output(struct merge_options *opt) { - if (o->buffer_output < 2 && o->obuf.len) { - fputs(o->obuf.buf, stdout); - strbuf_reset(&o->obuf); + if (opt->buffer_output < 2 && opt->obuf.len) { + fputs(opt->obuf.buf, stdout); + strbuf_reset(&opt->obuf); } } -static int err(struct merge_options *o, const char *err, ...) +static int err(struct merge_options *opt, const char *err, ...) { va_list params; - if (o->buffer_output < 2) - flush_output(o); + if (opt->buffer_output < 2) + flush_output(opt); else { - strbuf_complete(&o->obuf, '\n'); - strbuf_addstr(&o->obuf, "error: "); + strbuf_complete(&opt->obuf, '\n'); + strbuf_addstr(&opt->obuf, "error: "); } va_start(params, err); - strbuf_vaddf(&o->obuf, err, params); + strbuf_vaddf(&opt->obuf, err, params); va_end(params); - if (o->buffer_output > 1) - strbuf_addch(&o->obuf, '\n'); + if (opt->buffer_output > 1) + strbuf_addch(&opt->obuf, '\n'); else { - error("%s", o->obuf.buf); - strbuf_reset(&o->obuf); + error("%s", opt->obuf.buf); + strbuf_reset(&opt->obuf); } return -1; @@@ -163,6 -163,11 +163,11 @@@ static struct tree *shift_tree_object(s return lookup_tree(repo, &shifted); } + static inline void set_commit_tree(struct commit *c, struct tree *t) + { + c->maybe_tree = t; + } + static struct commit *make_virtual_commit(struct repository *repo, struct tree *tree, const char *comment) @@@ -170,7 -175,7 +175,7 @@@ struct commit *commit = alloc_commit_node(repo); set_merge_remote_desc(commit, comment, (struct object *)commit); - commit->maybe_tree = tree; + set_commit_tree(commit, tree); commit->object.parsed = 1; return commit; } @@@ -196,148 -201,163 +201,148 @@@ enum rename_type RENAME_TWO_FILES_TO_ONE }; -struct rename_conflict_info { - enum rename_type rename_type; - struct diff_filepair *pair1; - struct diff_filepair *pair2; - const char *branch1; - const char *branch2; - struct stage_data *dst_entry1; - struct stage_data *dst_entry2; - struct diff_filespec ren1_other; - struct diff_filespec ren2_other; -}; - /* * Since we want to write the index eventually, we cannot reuse the index * for these (temporary) data. */ struct stage_data { - struct { - unsigned mode; - struct object_id oid; - } stages[4]; + struct diff_filespec stages[4]; /* mostly for oid & mode; maybe path */ struct rename_conflict_info *rename_conflict_info; unsigned processed:1; }; +struct rename { + unsigned processed:1; + struct diff_filepair *pair; + const char *branch; /* branch that the rename occurred on */ + /* + * If directory rename detection affected this rename, what was its + * original type ('A' or 'R') and it's original destination before + * the directory rename (otherwise, '\0' and NULL for these two vars). + */ + char dir_rename_original_type; + char *dir_rename_original_dest; + /* + * Purpose of src_entry and dst_entry: + * + * If 'before' is renamed to 'after' then src_entry will contain + * the versions of 'before' from the merge_base, HEAD, and MERGE in + * stages 1, 2, and 3; dst_entry will contain the respective + * versions of 'after' in corresponding locations. Thus, we have a + * total of six modes and oids, though some will be null. (Stage 0 + * is ignored; we're interested in handling conflicts.) + * + * Since we don't turn on break-rewrites by default, neither + * src_entry nor dst_entry can have all three of their stages have + * non-null oids, meaning at most four of the six will be non-null. + * Also, since this is a rename, both src_entry and dst_entry will + * have at least one non-null oid, meaning at least two will be + * non-null. Of the six oids, a typical rename will have three be + * non-null. Only two implies a rename/delete, and four implies a + * rename/add. + */ + struct stage_data *src_entry; + struct stage_data *dst_entry; +}; + +struct rename_conflict_info { + enum rename_type rename_type; + struct rename *ren1; + struct rename *ren2; +}; + static inline void setup_rename_conflict_info(enum rename_type rename_type, - struct diff_filepair *pair1, - struct diff_filepair *pair2, - const char *branch1, - const char *branch2, - struct stage_data *dst_entry1, - struct stage_data *dst_entry2, - struct merge_options *o, - struct stage_data *src_entry1, - struct stage_data *src_entry2) -{ - int ostage1 = 0, ostage2; + struct merge_options *opt, + struct rename *ren1, + struct rename *ren2) +{ struct rename_conflict_info *ci; /* * When we have two renames involved, it's easiest to get the * correct things into stage 2 and 3, and to make sure that the * content merge puts HEAD before the other branch if we just - * ensure that branch1 == o->branch1. So, simply flip arguments + * ensure that branch1 == opt->branch1. So, simply flip arguments * around if we don't have that. */ - if (dst_entry2 && branch1 != o->branch1) { - setup_rename_conflict_info(rename_type, - pair2, pair1, - branch2, branch1, - dst_entry2, dst_entry1, - o, - src_entry2, src_entry1); + if (ren2 && ren1->branch != opt->branch1) { + setup_rename_conflict_info(rename_type, opt, ren2, ren1); return; } ci = xcalloc(1, sizeof(struct rename_conflict_info)); ci->rename_type = rename_type; - ci->pair1 = pair1; - ci->branch1 = branch1; - ci->branch2 = branch2; + ci->ren1 = ren1; + ci->ren2 = ren2; - ci->dst_entry1 = dst_entry1; - dst_entry1->rename_conflict_info = ci; - dst_entry1->processed = 0; - - assert(!pair2 == !dst_entry2); - if (dst_entry2) { - ci->dst_entry2 = dst_entry2; - ci->pair2 = pair2; - dst_entry2->rename_conflict_info = ci; - } - - /* - * For each rename, there could have been - * modifications on the side of history where that - * file was not renamed. - */ - if (rename_type == RENAME_ADD || - rename_type == RENAME_TWO_FILES_TO_ONE) { - ostage1 = o->branch1 == branch1 ? 3 : 2; - - ci->ren1_other.path = pair1->one->path; - oidcpy(&ci->ren1_other.oid, &src_entry1->stages[ostage1].oid); - ci->ren1_other.mode = src_entry1->stages[ostage1].mode; - } - - if (rename_type == RENAME_TWO_FILES_TO_ONE) { - ostage2 = ostage1 ^ 1; - - ci->ren2_other.path = pair2->one->path; - oidcpy(&ci->ren2_other.oid, &src_entry2->stages[ostage2].oid); - ci->ren2_other.mode = src_entry2->stages[ostage2].mode; + ci->ren1->dst_entry->processed = 0; + ci->ren1->dst_entry->rename_conflict_info = ci; + if (ren2) { + ci->ren2->dst_entry->rename_conflict_info = ci; } } -static int show(struct merge_options *o, int v) +static int show(struct merge_options *opt, int v) { - return (!o->call_depth && o->verbosity >= v) || o->verbosity >= 5; + return (!opt->call_depth && opt->verbosity >= v) || opt->verbosity >= 5; } __attribute__((format (printf, 3, 4))) -static void output(struct merge_options *o, int v, const char *fmt, ...) +static void output(struct merge_options *opt, int v, const char *fmt, ...) { va_list ap; - if (!show(o, v)) + if (!show(opt, v)) return; - strbuf_addchars(&o->obuf, ' ', o->call_depth * 2); + strbuf_addchars(&opt->obuf, ' ', opt->call_depth * 2); va_start(ap, fmt); - strbuf_vaddf(&o->obuf, fmt, ap); + strbuf_vaddf(&opt->obuf, fmt, ap); va_end(ap); - strbuf_addch(&o->obuf, '\n'); - if (!o->buffer_output) - flush_output(o); + strbuf_addch(&opt->obuf, '\n'); + if (!opt->buffer_output) + flush_output(opt); } -static void output_commit_title(struct merge_options *o, struct commit *commit) +static void output_commit_title(struct merge_options *opt, struct commit *commit) { struct merge_remote_desc *desc; - strbuf_addchars(&o->obuf, ' ', o->call_depth * 2); + strbuf_addchars(&opt->obuf, ' ', opt->call_depth * 2); desc = merge_remote_util(commit); if (desc) - strbuf_addf(&o->obuf, "virtual %s\n", desc->name); + strbuf_addf(&opt->obuf, "virtual %s\n", desc->name); else { - strbuf_add_unique_abbrev(&o->obuf, &commit->object.oid, + strbuf_add_unique_abbrev(&opt->obuf, &commit->object.oid, DEFAULT_ABBREV); - strbuf_addch(&o->obuf, ' '); + strbuf_addch(&opt->obuf, ' '); if (parse_commit(commit) != 0) - strbuf_addstr(&o->obuf, _("(bad commit)\n")); + strbuf_addstr(&opt->obuf, _("(bad commit)\n")); else { const char *title; const char *msg = get_commit_buffer(commit, NULL); int len = find_commit_subject(msg, &title); if (len) - strbuf_addf(&o->obuf, "%.*s\n", len, title); + strbuf_addf(&opt->obuf, "%.*s\n", len, title); unuse_commit_buffer(commit, msg); } } - flush_output(o); + flush_output(opt); } -static int add_cacheinfo(struct merge_options *o, - unsigned int mode, const struct object_id *oid, +static int add_cacheinfo(struct merge_options *opt, + const struct diff_filespec *blob, const char *path, int stage, int refresh, int options) { - struct index_state *istate = o->repo->index; + struct index_state *istate = opt->repo->index; struct cache_entry *ce; int ret; - ce = make_cache_entry(istate, mode, oid ? oid : &null_oid, path, stage, 0); + ce = make_cache_entry(istate, blob->mode, &blob->oid, path, stage, 0); if (!ce) - return err(o, _("add_cacheinfo failed for path '%s'; merge aborting."), path); + return err(opt, _("add_cacheinfo failed for path '%s'; merge aborting."), path); ret = add_index_entry(istate, ce, options); if (refresh) { @@@ -346,7 -366,7 +351,7 @@@ nce = refresh_cache_entry(istate, ce, CE_MATCH_REFRESH | CE_MATCH_IGNORE_MISSING); if (!nce) - return err(o, _("add_cacheinfo failed to refresh for path '%s'; merge aborting."), path); + return err(opt, _("add_cacheinfo failed to refresh for path '%s'; merge aborting."), path); if (nce != ce) ret = add_index_entry(istate, nce, options); } @@@ -359,7 -379,7 +364,7 @@@ static void init_tree_desc_from_tree(st init_tree_desc(desc, tree->buffer, tree->size); } -static int unpack_trees_start(struct merge_options *o, +static int unpack_trees_start(struct merge_options *opt, struct tree *common, struct tree *head, struct tree *merge) @@@ -368,49 -388,49 +373,49 @@@ struct tree_desc t[3]; struct index_state tmp_index = { NULL }; - memset(&o->unpack_opts, 0, sizeof(o->unpack_opts)); - if (o->call_depth) - o->unpack_opts.index_only = 1; + memset(&opt->unpack_opts, 0, sizeof(opt->unpack_opts)); + if (opt->call_depth) + opt->unpack_opts.index_only = 1; else - o->unpack_opts.update = 1; - o->unpack_opts.merge = 1; - o->unpack_opts.head_idx = 2; - o->unpack_opts.fn = threeway_merge; - o->unpack_opts.src_index = o->repo->index; - o->unpack_opts.dst_index = &tmp_index; - o->unpack_opts.aggressive = !merge_detect_rename(o); - setup_unpack_trees_porcelain(&o->unpack_opts, "merge"); + opt->unpack_opts.update = 1; + opt->unpack_opts.merge = 1; + opt->unpack_opts.head_idx = 2; + opt->unpack_opts.fn = threeway_merge; + opt->unpack_opts.src_index = opt->repo->index; + opt->unpack_opts.dst_index = &tmp_index; + opt->unpack_opts.aggressive = !merge_detect_rename(opt); + setup_unpack_trees_porcelain(&opt->unpack_opts, "merge"); init_tree_desc_from_tree(t+0, common); init_tree_desc_from_tree(t+1, head); init_tree_desc_from_tree(t+2, merge); - rc = unpack_trees(3, t, &o->unpack_opts); - cache_tree_free(&o->repo->index->cache_tree); + rc = unpack_trees(3, t, &opt->unpack_opts); + cache_tree_free(&opt->repo->index->cache_tree); /* - * Update o->repo->index to match the new results, AFTER saving a copy - * in o->orig_index. Update src_index to point to the saved copy. + * Update opt->repo->index to match the new results, AFTER saving a copy + * in opt->orig_index. Update src_index to point to the saved copy. * (verify_uptodate() checks src_index, and the original index is * the one that had the necessary modification timestamps.) */ - o->orig_index = *o->repo->index; - *o->repo->index = tmp_index; - o->unpack_opts.src_index = &o->orig_index; + opt->orig_index = *opt->repo->index; + *opt->repo->index = tmp_index; + opt->unpack_opts.src_index = &opt->orig_index; return rc; } -static void unpack_trees_finish(struct merge_options *o) +static void unpack_trees_finish(struct merge_options *opt) { - discard_index(&o->orig_index); - clear_unpack_trees_porcelain(&o->unpack_opts); + discard_index(&opt->orig_index); + clear_unpack_trees_porcelain(&opt->unpack_opts); } -struct tree *write_tree_from_memory(struct merge_options *o) +struct tree *write_tree_from_memory(struct merge_options *opt) { struct tree *result = NULL; - struct index_state *istate = o->repo->index; + struct index_state *istate = opt->repo->index; if (unmerged_index(istate)) { int i; @@@ -429,11 -449,11 +434,11 @@@ if (!cache_tree_fully_valid(istate->cache_tree) && cache_tree_update(istate, 0) < 0) { - err(o, _("error building trees")); + err(opt, _("error building trees")); return NULL; } - result = lookup_tree(o->repo, &istate->cache_tree->oid); + result = lookup_tree(opt->repo, &istate->cache_tree->oid); return result; } @@@ -444,36 -464,37 +449,36 @@@ static int save_files_dirs(const struc { struct path_hashmap_entry *entry; int baselen = base->len; - struct merge_options *o = context; + struct merge_options *opt = context; strbuf_addstr(base, path); FLEX_ALLOC_MEM(entry, path, base->buf, base->len); hashmap_entry_init(entry, path_hash(entry->path)); - hashmap_add(&o->current_file_dir_set, entry); + hashmap_add(&opt->current_file_dir_set, entry); strbuf_setlen(base, baselen); return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0); } -static void get_files_dirs(struct merge_options *o, struct tree *tree) +static void get_files_dirs(struct merge_options *opt, struct tree *tree) { struct pathspec match_all; memset(&match_all, 0, sizeof(match_all)); read_tree_recursive(the_repository, tree, "", 0, 0, - &match_all, save_files_dirs, o); + &match_all, save_files_dirs, opt); } static int get_tree_entry_if_blob(const struct object_id *tree, const char *path, - struct object_id *hashy, - unsigned int *mode_o) + struct diff_filespec *dfs) { int ret; - ret = get_tree_entry(tree, path, hashy, mode_o); - if (S_ISDIR(*mode_o)) { - oidcpy(hashy, &null_oid); - *mode_o = 0; + ret = get_tree_entry(tree, path, &dfs->oid, &dfs->mode); + if (S_ISDIR(dfs->mode)) { + oidcpy(&dfs->oid, &null_oid); + dfs->mode = 0; } return ret; } @@@ -488,9 -509,12 +493,9 @@@ static struct stage_data *insert_stage_ { struct string_list_item *item; struct stage_data *e = xcalloc(1, sizeof(struct stage_data)); - get_tree_entry_if_blob(&o->object.oid, path, - &e->stages[1].oid, &e->stages[1].mode); - get_tree_entry_if_blob(&a->object.oid, path, - &e->stages[2].oid, &e->stages[2].mode); - get_tree_entry_if_blob(&b->object.oid, path, - &e->stages[3].oid, &e->stages[3].mode); + get_tree_entry_if_blob(&o->object.oid, path, &e->stages[1]); + get_tree_entry_if_blob(&a->object.oid, path, &e->stages[2]); + get_tree_entry_if_blob(&b->object.oid, path, &e->stages[3]); item = string_list_insert(entries, path); item->util = e; return e; @@@ -554,7 -578,7 +559,7 @@@ static int string_list_df_name_compare( return onelen - twolen; } -static void record_df_conflict_files(struct merge_options *o, +static void record_df_conflict_files(struct merge_options *opt, struct string_list *entries) { /* If there is a D/F conflict and the file for such a conflict @@@ -579,7 -603,7 +584,7 @@@ * If we're merging merge-bases, we don't want to bother with * any working directory changes. */ - if (o->call_depth) + if (opt->call_depth) return; /* Ensure D/F conflicts are adjacent in the entries list. */ @@@ -591,7 -615,7 +596,7 @@@ df_sorted_entries.cmp = string_list_df_name_compare; string_list_sort(&df_sorted_entries); - string_list_clear(&o->df_conflict_file_set, 1); + string_list_clear(&opt->df_conflict_file_set, 1); for (i = 0; i < df_sorted_entries.nr; i++) { const char *path = df_sorted_entries.items[i].string; int len = strlen(path); @@@ -607,7 -631,7 +612,7 @@@ len > last_len && memcmp(path, last_file, last_len) == 0 && path[last_len] == '/') { - string_list_insert(&o->df_conflict_file_set, last_file); + string_list_insert(&opt->df_conflict_file_set, last_file); } /* @@@ -626,6 -650,33 +631,6 @@@ string_list_clear(&df_sorted_entries, 0); } -struct rename { - struct diff_filepair *pair; - /* - * Purpose of src_entry and dst_entry: - * - * If 'before' is renamed to 'after' then src_entry will contain - * the versions of 'before' from the merge_base, HEAD, and MERGE in - * stages 1, 2, and 3; dst_entry will contain the respective - * versions of 'after' in corresponding locations. Thus, we have a - * total of six modes and oids, though some will be null. (Stage 0 - * is ignored; we're interested in handling conflicts.) - * - * Since we don't turn on break-rewrites by default, neither - * src_entry nor dst_entry can have all three of their stages have - * non-null oids, meaning at most four of the six will be non-null. - * Also, since this is a rename, both src_entry and dst_entry will - * have at least one non-null oid, meaning at least two will be - * non-null. Of the six oids, a typical rename will have three be - * non-null. Only two implies a rename/delete, and four implies a - * rename/add. - */ - struct stage_data *src_entry; - struct stage_data *dst_entry; - unsigned add_turned_into_rename:1; - unsigned processed:1; -}; - static int update_stages(struct merge_options *opt, const char *path, const struct diff_filespec *o, const struct diff_filespec *a, @@@ -646,13 -697,13 +651,13 @@@ if (remove_file_from_index(opt->repo->index, path)) return -1; if (o) - if (add_cacheinfo(opt, o->mode, &o->oid, path, 1, 0, options)) + if (add_cacheinfo(opt, o, path, 1, 0, options)) return -1; if (a) - if (add_cacheinfo(opt, a->mode, &a->oid, path, 2, 0, options)) + if (add_cacheinfo(opt, a, path, 2, 0, options)) return -1; if (b) - if (add_cacheinfo(opt, b->mode, &b->oid, path, 3, 0, options)) + if (add_cacheinfo(opt, b, path, 3, 0, options)) return -1; return 0; } @@@ -671,20 -722,20 +676,20 @@@ static void update_entry(struct stage_d oidcpy(&entry->stages[3].oid, &b->oid); } -static int remove_file(struct merge_options *o, int clean, +static int remove_file(struct merge_options *opt, int clean, const char *path, int no_wd) { - int update_cache = o->call_depth || clean; - int update_working_directory = !o->call_depth && !no_wd; + int update_cache = opt->call_depth || clean; + int update_working_directory = !opt->call_depth && !no_wd; if (update_cache) { - if (remove_file_from_index(o->repo->index, path)) + if (remove_file_from_index(opt->repo->index, path)) return -1; } if (update_working_directory) { if (ignore_case) { struct cache_entry *ce; - ce = index_file_exists(o->repo->index, path, strlen(path), + ce = index_file_exists(opt->repo->index, path, strlen(path), ignore_case); if (ce && ce_stage(ce) == 0 && strcmp(path, ce->name)) return 0; @@@ -705,7 -756,7 +710,7 @@@ static void add_flattened_path(struct s out->buf[i] = '_'; } -static char *unique_path(struct merge_options *o, const char *path, const char *branch) +static char *unique_path(struct merge_options *opt, const char *path, const char *branch) { struct path_hashmap_entry *entry; struct strbuf newpath = STRBUF_INIT; @@@ -716,16 -767,16 +721,16 @@@ add_flattened_path(&newpath, branch); base_len = newpath.len; - while (hashmap_get_from_hash(&o->current_file_dir_set, + while (hashmap_get_from_hash(&opt->current_file_dir_set, path_hash(newpath.buf), newpath.buf) || - (!o->call_depth && file_exists(newpath.buf))) { + (!opt->call_depth && file_exists(newpath.buf))) { strbuf_setlen(&newpath, base_len); strbuf_addf(&newpath, "_%d", suffix++); } FLEX_ALLOC_MEM(entry, path, newpath.buf, newpath.len); hashmap_entry_init(entry, path_hash(entry->path)); - hashmap_add(&o->current_file_dir_set, entry); + hashmap_add(&opt->current_file_dir_set, entry); return strbuf_detach(&newpath, NULL); } @@@ -764,10 -815,10 +769,10 @@@ static int dir_in_way(struct index_stat * Returns whether path was tracked in the index before the merge started, * and its oid and mode match the specified values */ -static int was_tracked_and_matches(struct merge_options *o, const char *path, - const struct object_id *oid, unsigned mode) +static int was_tracked_and_matches(struct merge_options *opt, const char *path, + const struct diff_filespec *blob) { - int pos = index_name_pos(&o->orig_index, path, strlen(path)); + int pos = index_name_pos(&opt->orig_index, path, strlen(path)); struct cache_entry *ce; if (0 > pos) @@@ -775,16 -826,16 +780,16 @@@ return 0; /* See if the file we were tracking before matches */ - ce = o->orig_index.cache[pos]; - return (oid_eq(&ce->oid, oid) && ce->ce_mode == mode); + ce = opt->orig_index.cache[pos]; + return (oid_eq(&ce->oid, &blob->oid) && ce->ce_mode == blob->mode); } /* * Returns whether path was tracked in the index before the merge started */ -static int was_tracked(struct merge_options *o, const char *path) +static int was_tracked(struct merge_options *opt, const char *path) { - int pos = index_name_pos(&o->orig_index, path, strlen(path)); + int pos = index_name_pos(&opt->orig_index, path, strlen(path)); if (0 <= pos) /* we were tracking this path before the merge */ @@@ -793,13 -844,13 +798,13 @@@ return 0; } -static int would_lose_untracked(struct merge_options *o, const char *path) +static int would_lose_untracked(struct merge_options *opt, const char *path) { - struct index_state *istate = o->repo->index; + struct index_state *istate = opt->repo->index; /* * This may look like it can be simplified to: - * return !was_tracked(o, path) && file_exists(path) + * return !was_tracked(opt, path) && file_exists(path) * but it can't. This function needs to know whether path was in * the working tree due to EITHER having been tracked in the index * before the merge OR having been put into the working copy and @@@ -836,38 -887,38 +841,38 @@@ return file_exists(path); } -static int was_dirty(struct merge_options *o, const char *path) +static int was_dirty(struct merge_options *opt, const char *path) { struct cache_entry *ce; int dirty = 1; - if (o->call_depth || !was_tracked(o, path)) + if (opt->call_depth || !was_tracked(opt, path)) return !dirty; - ce = index_file_exists(o->unpack_opts.src_index, + ce = index_file_exists(opt->unpack_opts.src_index, path, strlen(path), ignore_case); - dirty = verify_uptodate(ce, &o->unpack_opts) != 0; + dirty = verify_uptodate(ce, &opt->unpack_opts) != 0; return dirty; } -static int make_room_for_path(struct merge_options *o, const char *path) +static int make_room_for_path(struct merge_options *opt, const char *path) { int status, i; const char *msg = _("failed to create path '%s'%s"); /* Unlink any D/F conflict files that are in the way */ - for (i = 0; i < o->df_conflict_file_set.nr; i++) { - const char *df_path = o->df_conflict_file_set.items[i].string; + for (i = 0; i < opt->df_conflict_file_set.nr; i++) { + const char *df_path = opt->df_conflict_file_set.items[i].string; size_t pathlen = strlen(path); size_t df_pathlen = strlen(df_path); if (df_pathlen < pathlen && path[df_pathlen] == '/' && strncmp(path, df_path, df_pathlen) == 0) { - output(o, 3, + output(opt, 3, _("Removing %s to make room for subdirectory\n"), df_path); unlink(df_path); - unsorted_string_list_delete_item(&o->df_conflict_file_set, + unsorted_string_list_delete_item(&opt->df_conflict_file_set, i, 0); break; } @@@ -878,16 -929,16 +883,16 @@@ if (status) { if (status == SCLD_EXISTS) /* something else exists */ - return err(o, msg, path, _(": perhaps a D/F conflict?")); - return err(o, msg, path, ""); + return err(opt, msg, path, _(": perhaps a D/F conflict?")); + return err(opt, msg, path, ""); } /* * Do not unlink a file in the work tree if we are not * tracking it. */ - if (would_lose_untracked(o, path)) - return err(o, _("refusing to lose untracked file at '%s'"), + if (would_lose_untracked(opt, path)) + return err(opt, _("refusing to lose untracked file at '%s'"), path); /* Successful unlink is good.. */ @@@ -897,18 -948,19 +902,18 @@@ if (errno == ENOENT) return 0; /* .. but not some other error (who really cares what?) */ - return err(o, msg, path, _(": perhaps a D/F conflict?")); + return err(opt, msg, path, _(": perhaps a D/F conflict?")); } -static int update_file_flags(struct merge_options *o, - const struct object_id *oid, - unsigned mode, +static int update_file_flags(struct merge_options *opt, + const struct diff_filespec *contents, const char *path, int update_cache, int update_wd) { int ret = 0; - if (o->call_depth) + if (opt->call_depth) update_wd = 0; if (update_wd) { @@@ -916,7 -968,7 +921,7 @@@ void *buf; unsigned long size; - if (S_ISGITLINK(mode)) { + if (S_ISGITLINK(contents->mode)) { /* * We may later decide to recursively descend into * the submodule directory and update its index @@@ -926,84 -978,84 +931,84 @@@ goto update_index; } - buf = read_object_file(oid, &type, &size); + buf = read_object_file(&contents->oid, &type, &size); if (!buf) - return err(o, _("cannot read object %s '%s'"), oid_to_hex(oid), path); + return err(opt, _("cannot read object %s '%s'"), + oid_to_hex(&contents->oid), path); if (type != OBJ_BLOB) { - ret = err(o, _("blob expected for %s '%s'"), oid_to_hex(oid), path); + ret = err(opt, _("blob expected for %s '%s'"), + oid_to_hex(&contents->oid), path); goto free_buf; } - if (S_ISREG(mode)) { + if (S_ISREG(contents->mode)) { struct strbuf strbuf = STRBUF_INIT; - if (convert_to_working_tree(o->repo->index, path, buf, size, &strbuf)) { + if (convert_to_working_tree(opt->repo->index, path, buf, size, &strbuf)) { free(buf); size = strbuf.len; buf = strbuf_detach(&strbuf, NULL); } } - if (make_room_for_path(o, path) < 0) { + if (make_room_for_path(opt, path) < 0) { update_wd = 0; goto free_buf; } - if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) { + if (S_ISREG(contents->mode) || + (!has_symlinks && S_ISLNK(contents->mode))) { int fd; - if (mode & 0100) - mode = 0777; - else - mode = 0666; + int mode = (contents->mode & 0100 ? 0777 : 0666); + fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); if (fd < 0) { - ret = err(o, _("failed to open '%s': %s"), + ret = err(opt, _("failed to open '%s': %s"), path, strerror(errno)); goto free_buf; } write_in_full(fd, buf, size); close(fd); - } else if (S_ISLNK(mode)) { + } else if (S_ISLNK(contents->mode)) { char *lnk = xmemdupz(buf, size); safe_create_leading_directories_const(path); unlink(path); if (symlink(lnk, path)) - ret = err(o, _("failed to symlink '%s': %s"), + ret = err(opt, _("failed to symlink '%s': %s"), path, strerror(errno)); free(lnk); } else - ret = err(o, + ret = err(opt, _("do not know what to do with %06o %s '%s'"), - mode, oid_to_hex(oid), path); + contents->mode, oid_to_hex(&contents->oid), path); free_buf: free(buf); } update_index: if (!ret && update_cache) - if (add_cacheinfo(o, mode, oid, path, 0, update_wd, + if (add_cacheinfo(opt, contents, path, 0, update_wd, ADD_CACHE_OK_TO_ADD)) return -1; return ret; } -static int update_file(struct merge_options *o, +static int update_file(struct merge_options *opt, int clean, - const struct object_id *oid, - unsigned mode, + const struct diff_filespec *contents, const char *path) { - return update_file_flags(o, oid, mode, path, o->call_depth || clean, !o->call_depth); + return update_file_flags(opt, contents, path, + opt->call_depth || clean, !opt->call_depth); } /* Low level file merging, update and removal */ struct merge_file_info { - struct object_id oid; - unsigned mode; + struct diff_filespec blob; /* mostly use oid & mode; sometimes path */ unsigned clean:1, merge:1; }; -static int merge_3way(struct merge_options *o, +static int merge_3way(struct merge_options *opt, mmbuffer_t *result_buf, - const struct diff_filespec *one, + const struct diff_filespec *o, const struct diff_filespec *a, const struct diff_filespec *b, const char *branch1, @@@ -1015,15 -1067,15 +1020,15 @@@ char *base_name, *name1, *name2; int merge_status; - ll_opts.renormalize = o->renormalize; + ll_opts.renormalize = opt->renormalize; ll_opts.extra_marker_size = extra_marker_size; - ll_opts.xdl_opts = o->xdl_opts; + ll_opts.xdl_opts = opt->xdl_opts; - if (o->call_depth) { + if (opt->call_depth) { ll_opts.virtual_ancestor = 1; ll_opts.variant = 0; } else { - switch (o->recursive_variant) { + switch (opt->recursive_variant) { case MERGE_RECURSIVE_OURS: ll_opts.variant = XDL_MERGE_FAVOR_OURS; break; @@@ -1036,27 -1088,26 +1041,27 @@@ } } + assert(a->path && b->path); if (strcmp(a->path, b->path) || - (o->ancestor != NULL && strcmp(a->path, one->path) != 0)) { - base_name = o->ancestor == NULL ? NULL : - mkpathdup("%s:%s", o->ancestor, one->path); + (opt->ancestor != NULL && strcmp(a->path, o->path) != 0)) { + base_name = opt->ancestor == NULL ? NULL : + mkpathdup("%s:%s", opt->ancestor, o->path); name1 = mkpathdup("%s:%s", branch1, a->path); name2 = mkpathdup("%s:%s", branch2, b->path); } else { - base_name = o->ancestor == NULL ? NULL : - mkpathdup("%s", o->ancestor); + base_name = opt->ancestor == NULL ? NULL : + mkpathdup("%s", opt->ancestor); name1 = mkpathdup("%s", branch1); name2 = mkpathdup("%s", branch2); } - read_mmblob(&orig, &one->oid); + read_mmblob(&orig, &o->oid); read_mmblob(&src1, &a->oid); read_mmblob(&src2, &b->oid); merge_status = ll_merge(result_buf, a->path, &orig, base_name, &src1, name1, &src2, name2, - o->repo->index, &ll_opts); + opt->repo->index, &ll_opts); free(base_name); free(name1); @@@ -1076,7 -1127,7 +1081,7 @@@ static int find_first_merges(struct rep struct commit *commit; int contains_another; - char merged_revision[42]; + char merged_revision[GIT_MAX_HEXSZ + 2]; const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path", "--all", merged_revision, NULL }; struct rev_info revs; @@@ -1138,12 -1189,7 +1143,12 @@@ static void print_commit(struct commit strbuf_release(&sb); } -static int merge_submodule(struct merge_options *o, +static int is_valid(const struct diff_filespec *dfs) +{ + return dfs->mode != 0 && !is_null_oid(&dfs->oid); +} + +static int merge_submodule(struct merge_options *opt, struct object_id *result, const char *path, const struct object_id *base, const struct object_id *a, const struct object_id *b) @@@ -1153,7 -1199,7 +1158,7 @@@ struct object_array merges; int i; - int search = !o->call_depth; + int search = !opt->call_depth; /* store a in result in case we fail */ oidcpy(result, a); @@@ -1167,32 -1213,32 +1172,32 @@@ return 0; if (add_submodule_odb(path)) { - output(o, 1, _("Failed to merge submodule %s (not checked out)"), path); + output(opt, 1, _("Failed to merge submodule %s (not checked out)"), path); return 0; } - if (!(commit_base = lookup_commit_reference(o->repo, base)) || - !(commit_a = lookup_commit_reference(o->repo, a)) || - !(commit_b = lookup_commit_reference(o->repo, b))) { - output(o, 1, _("Failed to merge submodule %s (commits not present)"), path); + if (!(commit_base = lookup_commit_reference(opt->repo, base)) || + !(commit_a = lookup_commit_reference(opt->repo, a)) || + !(commit_b = lookup_commit_reference(opt->repo, b))) { + output(opt, 1, _("Failed to merge submodule %s (commits not present)"), path); return 0; } /* check whether both changes are forward */ if (!in_merge_bases(commit_base, commit_a) || !in_merge_bases(commit_base, commit_b)) { - output(o, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path); + output(opt, 1, _("Failed to merge submodule %s (commits don't follow merge-base)"), path); return 0; } /* Case #1: a is contained in b or vice versa */ if (in_merge_bases(commit_a, commit_b)) { oidcpy(result, b); - if (show(o, 3)) { - output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path); - output_commit_title(o, commit_b); - } else if (show(o, 2)) - output(o, 2, _("Fast-forwarding submodule %s"), path); + if (show(opt, 3)) { + output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path); + output_commit_title(opt, commit_b); + } else if (show(opt, 2)) + output(opt, 2, _("Fast-forwarding submodule %s"), path); else ; /* no output */ @@@ -1200,11 -1246,11 +1205,11 @@@ } if (in_merge_bases(commit_b, commit_a)) { oidcpy(result, a); - if (show(o, 3)) { - output(o, 3, _("Fast-forwarding submodule %s to the following commit:"), path); - output_commit_title(o, commit_a); - } else if (show(o, 2)) - output(o, 2, _("Fast-forwarding submodule %s"), path); + if (show(opt, 3)) { + output(opt, 3, _("Fast-forwarding submodule %s to the following commit:"), path); + output_commit_title(opt, commit_a); + } else if (show(opt, 2)) + output(opt, 2, _("Fast-forwarding submodule %s"), path); else ; /* no output */ @@@ -1223,18 -1269,18 +1228,18 @@@ return 0; /* find commit which merges them */ - parent_count = find_first_merges(o->repo, &merges, path, + parent_count = find_first_merges(opt->repo, &merges, path, commit_a, commit_b); switch (parent_count) { case 0: - output(o, 1, _("Failed to merge submodule %s (merge following commits not found)"), path); + output(opt, 1, _("Failed to merge submodule %s (merge following commits not found)"), path); break; case 1: - output(o, 1, _("Failed to merge submodule %s (not fast-forward)"), path); - output(o, 2, _("Found a possible merge resolution for the submodule:\n")); + output(opt, 1, _("Failed to merge submodule %s (not fast-forward)"), path); + output(opt, 2, _("Found a possible merge resolution for the submodule:\n")); print_commit((struct commit *) merges.objects[0].item); - output(o, 2, _( + output(opt, 2, _( "If this is correct simply add it to the index " "for example\n" "by using:\n\n" @@@ -1244,7 -1290,7 +1249,7 @@@ break; default: - output(o, 1, _("Failed to merge submodule %s (multiple merges found)"), path); + output(opt, 1, _("Failed to merge submodule %s (multiple merges found)"), path); for (i = 0; i < merges.nr; i++) print_commit((struct commit *) merges.objects[i].item); } @@@ -1253,8 -1299,8 +1258,8 @@@ return 0; } -static int merge_mode_and_contents(struct merge_options *o, - const struct diff_filespec *one, +static int merge_mode_and_contents(struct merge_options *opt, + const struct diff_filespec *o, const struct diff_filespec *a, const struct diff_filespec *b, const char *filename, @@@ -1263,13 -1309,13 +1268,13 @@@ const int extra_marker_size, struct merge_file_info *result) { - if (o->branch1 != branch1) { + if (opt->branch1 != branch1) { /* * It's weird getting a reverse merge with HEAD on the bottom * side of the conflict markers and the other branch on the * top. Fix that. */ - return merge_mode_and_contents(o, one, b, a, + return merge_mode_and_contents(opt, o, b, a, filename, branch2, branch1, extra_marker_size, result); @@@ -1281,48 -1327,48 +1286,48 @@@ if ((S_IFMT & a->mode) != (S_IFMT & b->mode)) { result->clean = 0; if (S_ISREG(a->mode)) { - result->mode = a->mode; - oidcpy(&result->oid, &a->oid); + result->blob.mode = a->mode; + oidcpy(&result->blob.oid, &a->oid); } else { - result->mode = b->mode; - oidcpy(&result->oid, &b->oid); + result->blob.mode = b->mode; + oidcpy(&result->blob.oid, &b->oid); } } else { - if (!oid_eq(&a->oid, &one->oid) && !oid_eq(&b->oid, &one->oid)) + if (!oid_eq(&a->oid, &o->oid) && !oid_eq(&b->oid, &o->oid)) result->merge = 1; /* * Merge modes */ - if (a->mode == b->mode || a->mode == one->mode) - result->mode = b->mode; + if (a->mode == b->mode || a->mode == o->mode) + result->blob.mode = b->mode; else { - result->mode = a->mode; - if (b->mode != one->mode) { + result->blob.mode = a->mode; + if (b->mode != o->mode) { result->clean = 0; result->merge = 1; } } - if (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &one->oid)) - oidcpy(&result->oid, &b->oid); - else if (oid_eq(&b->oid, &one->oid)) - oidcpy(&result->oid, &a->oid); + if (oid_eq(&a->oid, &b->oid) || oid_eq(&a->oid, &o->oid)) + oidcpy(&result->blob.oid, &b->oid); + else if (oid_eq(&b->oid, &o->oid)) + oidcpy(&result->blob.oid, &a->oid); else if (S_ISREG(a->mode)) { mmbuffer_t result_buf; int ret = 0, merge_status; - merge_status = merge_3way(o, &result_buf, one, a, b, + merge_status = merge_3way(opt, &result_buf, o, a, b, branch1, branch2, extra_marker_size); if ((merge_status < 0) || !result_buf.ptr) - ret = err(o, _("Failed to execute internal merge")); + ret = err(opt, _("Failed to execute internal merge")); if (!ret && write_object_file(result_buf.ptr, result_buf.size, - blob_type, &result->oid)) - ret = err(o, _("Unable to add %s to database"), + blob_type, &result->blob.oid)) + ret = err(opt, _("Unable to add %s to database"), a->path); free(result_buf.ptr); @@@ -1330,23 -1376,23 +1335,23 @@@ return ret; result->clean = (merge_status == 0); } else if (S_ISGITLINK(a->mode)) { - result->clean = merge_submodule(o, &result->oid, - one->path, - &one->oid, + result->clean = merge_submodule(opt, &result->blob.oid, + o->path, + &o->oid, &a->oid, &b->oid); } else if (S_ISLNK(a->mode)) { - switch (o->recursive_variant) { + switch (opt->recursive_variant) { case MERGE_RECURSIVE_NORMAL: - oidcpy(&result->oid, &a->oid); + oidcpy(&result->blob.oid, &a->oid); if (!oid_eq(&a->oid, &b->oid)) result->clean = 0; break; case MERGE_RECURSIVE_OURS: - oidcpy(&result->oid, &a->oid); + oidcpy(&result->blob.oid, &a->oid); break; case MERGE_RECURSIVE_THEIRS: - oidcpy(&result->oid, &b->oid); + oidcpy(&result->blob.oid, &b->oid); break; } } else @@@ -1354,13 -1400,14 +1359,13 @@@ } if (result->merge) - output(o, 2, _("Auto-merging %s"), filename); + output(opt, 2, _("Auto-merging %s"), filename); return 0; } -static int handle_rename_via_dir(struct merge_options *o, - struct diff_filepair *pair, - const char *rename_branch) +static int handle_rename_via_dir(struct merge_options *opt, + struct rename_conflict_info *ci) { /* * Handle file adds that need to be renamed due to directory rename @@@ -1368,47 -1415,38 +1373,47 @@@ * there is no content merge to do; just move the file into the * desired final location. */ - const struct diff_filespec *dest = pair->two; + const struct rename *ren = ci->ren1; + const struct diff_filespec *dest = ren->pair->two; + char *file_path = dest->path; + int mark_conflicted = (opt->detect_directory_renames == 1); + assert(ren->dir_rename_original_dest); - if (!o->call_depth && would_lose_untracked(o, dest->path)) { - char *alt_path = unique_path(o, dest->path, rename_branch); + if (!opt->call_depth && would_lose_untracked(opt, dest->path)) { + mark_conflicted = 1; + file_path = unique_path(opt, dest->path, ren->branch); + output(opt, 1, _("Error: Refusing to lose untracked file at %s; " + "writing to %s instead."), + dest->path, file_path); + } - output(o, 1, _("Error: Refusing to lose untracked file at %s; " - "writing to %s instead."), - dest->path, alt_path); + if (mark_conflicted) { /* - * Write the file in worktree at alt_path, but not in the - * index. Instead, write to dest->path for the index but - * only at the higher appropriate stage. + * Write the file in worktree at file_path. In the index, + * only record the file at dest->path in the appropriate + * higher stage. */ - if (update_file(o, 0, &dest->oid, dest->mode, alt_path)) + if (update_file(opt, 0, dest, file_path)) return -1; - free(alt_path); - return update_stages(o, dest->path, NULL, - rename_branch == o->branch1 ? dest : NULL, - rename_branch == o->branch1 ? NULL : dest); + if (file_path != dest->path) + free(file_path); + if (update_stages(opt, dest->path, NULL, + ren->branch == opt->branch1 ? dest : NULL, + ren->branch == opt->branch1 ? NULL : dest)) + return -1; + return 0; /* not clean, but conflicted */ + } else { + /* Update dest->path both in index and in worktree */ + if (update_file(opt, 1, dest, dest->path)) + return -1; + return 1; /* clean */ } - - /* Update dest->path both in index and in worktree */ - if (update_file(o, 1, &dest->oid, dest->mode, dest->path)) - return -1; - return 0; } -static int handle_change_delete(struct merge_options *o, +static int handle_change_delete(struct merge_options *opt, const char *path, const char *old_path, - const struct object_id *o_oid, int o_mode, - const struct object_id *changed_oid, - int changed_mode, + const struct diff_filespec *o, + const struct diff_filespec *changed, const char *change_branch, const char *delete_branch, const char *change, const char *change_past) @@@ -1417,20 -1455,20 +1422,20 @@@ const char *update_path = path; int ret = 0; - if (dir_in_way(o->repo->index, path, !o->call_depth, 0) || - (!o->call_depth && would_lose_untracked(o, path))) { - update_path = alt_path = unique_path(o, path, change_branch); + if (dir_in_way(opt->repo->index, path, !opt->call_depth, 0) || + (!opt->call_depth && would_lose_untracked(opt, path))) { + update_path = alt_path = unique_path(opt, path, change_branch); } - if (o->call_depth) { + if (opt->call_depth) { /* * We cannot arbitrarily accept either a_sha or b_sha as * correct; since there is no true "middle point" between * them, simply reuse the base version for virtual merge base. */ - ret = remove_file_from_index(o->repo->index, path); + ret = remove_file_from_index(opt->repo->index, path); if (!ret) - ret = update_file(o, 0, o_oid, o_mode, update_path); + ret = update_file(opt, 0, o, update_path); } else { /* * Despite the four nearly duplicate messages and argument @@@ -1449,24 -1487,24 +1454,24 @@@ */ if (!alt_path) { if (!old_path) { - output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s " + output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s " "and %s in %s. Version %s of %s left in tree."), change, path, delete_branch, change_past, change_branch, change_branch, path); } else { - output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s " + output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s " "and %s to %s in %s. Version %s of %s left in tree."), change, old_path, delete_branch, change_past, path, change_branch, change_branch, path); } } else { if (!old_path) { - output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s " + output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s " "and %s in %s. Version %s of %s left in tree at %s."), change, path, delete_branch, change_past, change_branch, change_branch, path, alt_path); } else { - output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s " + output(opt, 1, _("CONFLICT (%s/delete): %s deleted in %s " "and %s to %s in %s. Version %s of %s left in tree at %s."), change, old_path, delete_branch, change_past, path, change_branch, change_branch, path, alt_path); @@@ -1474,127 -1512,142 +1479,127 @@@ } /* * No need to call update_file() on path when change_branch == - * o->branch1 && !alt_path, since that would needlessly touch + * opt->branch1 && !alt_path, since that would needlessly touch * path. We could call update_file_flags() with update_cache=0 * and update_wd=0, but that's a no-op. */ - if (change_branch != o->branch1 || alt_path) - ret = update_file(o, 0, changed_oid, changed_mode, update_path); + if (change_branch != opt->branch1 || alt_path) + ret = update_file(opt, 0, changed, update_path); } free(alt_path); return ret; } -static int handle_rename_delete(struct merge_options *o, - struct diff_filepair *pair, - const char *rename_branch, - const char *delete_branch) +static int handle_rename_delete(struct merge_options *opt, + struct rename_conflict_info *ci) { - const struct diff_filespec *orig = pair->one; - const struct diff_filespec *dest = pair->two; - - if (handle_change_delete(o, - o->call_depth ? orig->path : dest->path, - o->call_depth ? NULL : orig->path, - &orig->oid, orig->mode, - &dest->oid, dest->mode, + const struct rename *ren = ci->ren1; + const struct diff_filespec *orig = ren->pair->one; + const struct diff_filespec *dest = ren->pair->two; + const char *rename_branch = ren->branch; + const char *delete_branch = (opt->branch1 == ren->branch ? + opt->branch2 : opt->branch1); + + if (handle_change_delete(opt, + opt->call_depth ? orig->path : dest->path, + opt->call_depth ? NULL : orig->path, + orig, dest, rename_branch, delete_branch, _("rename"), _("renamed"))) return -1; - if (o->call_depth) - return remove_file_from_index(o->repo->index, dest->path); + if (opt->call_depth) + return remove_file_from_index(opt->repo->index, dest->path); else - return update_stages(o, dest->path, NULL, - rename_branch == o->branch1 ? dest : NULL, - rename_branch == o->branch1 ? NULL : dest); + return update_stages(opt, dest->path, NULL, + rename_branch == opt->branch1 ? dest : NULL, + rename_branch == opt->branch1 ? NULL : dest); } -static struct diff_filespec *filespec_from_entry(struct diff_filespec *target, - struct stage_data *entry, - int stage) -{ - struct object_id *oid = &entry->stages[stage].oid; - unsigned mode = entry->stages[stage].mode; - if (mode == 0 || is_null_oid(oid)) - return NULL; - oidcpy(&target->oid, oid); - target->mode = mode; - return target; -} - -static int handle_file_collision(struct merge_options *o, +static int handle_file_collision(struct merge_options *opt, const char *collide_path, const char *prev_path1, const char *prev_path2, const char *branch1, const char *branch2, - const struct object_id *a_oid, - unsigned int a_mode, - const struct object_id *b_oid, - unsigned int b_mode) + struct diff_filespec *a, + struct diff_filespec *b) { struct merge_file_info mfi; - struct diff_filespec null, a, b; + struct diff_filespec null; char *alt_path = NULL; const char *update_path = collide_path; /* * It's easiest to get the correct things into stage 2 and 3, and * to make sure that the content merge puts HEAD before the other - * branch if we just ensure that branch1 == o->branch1. So, simply + * branch if we just ensure that branch1 == opt->branch1. So, simply * flip arguments around if we don't have that. */ - if (branch1 != o->branch1) { - return handle_file_collision(o, collide_path, + if (branch1 != opt->branch1) { + return handle_file_collision(opt, collide_path, prev_path2, prev_path1, branch2, branch1, - b_oid, b_mode, - a_oid, a_mode); + b, a); } /* * In the recursive case, we just opt to undo renames */ - if (o->call_depth && (prev_path1 || prev_path2)) { - /* Put first file (a_oid, a_mode) in its original spot */ + if (opt->call_depth && (prev_path1 || prev_path2)) { + /* Put first file (a->oid, a->mode) in its original spot */ if (prev_path1) { - if (update_file(o, 1, a_oid, a_mode, prev_path1)) + if (update_file(opt, 1, a, prev_path1)) return -1; } else { - if (update_file(o, 1, a_oid, a_mode, collide_path)) + if (update_file(opt, 1, a, collide_path)) return -1; } - /* Put second file (b_oid, b_mode) in its original spot */ + /* Put second file (b->oid, b->mode) in its original spot */ if (prev_path2) { - if (update_file(o, 1, b_oid, b_mode, prev_path2)) + if (update_file(opt, 1, b, prev_path2)) return -1; } else { - if (update_file(o, 1, b_oid, b_mode, collide_path)) + if (update_file(opt, 1, b, collide_path)) return -1; } /* Don't leave something at collision path if unrenaming both */ if (prev_path1 && prev_path2) - remove_file(o, 1, collide_path, 0); + remove_file(opt, 1, collide_path, 0); return 0; } /* Remove rename sources if rename/add or rename/rename(2to1) */ if (prev_path1) - remove_file(o, 1, prev_path1, - o->call_depth || would_lose_untracked(o, prev_path1)); + remove_file(opt, 1, prev_path1, + opt->call_depth || would_lose_untracked(opt, prev_path1)); if (prev_path2) - remove_file(o, 1, prev_path2, - o->call_depth || would_lose_untracked(o, prev_path2)); + remove_file(opt, 1, prev_path2, + opt->call_depth || would_lose_untracked(opt, prev_path2)); /* * Remove the collision path, if it wouldn't cause dirty contents * or an untracked file to get lost. We'll either overwrite with * merged contents, or just write out to differently named files. */ - if (was_dirty(o, collide_path)) { - output(o, 1, _("Refusing to lose dirty file at %s"), + if (was_dirty(opt, collide_path)) { + output(opt, 1, _("Refusing to lose dirty file at %s"), collide_path); - update_path = alt_path = unique_path(o, collide_path, "merged"); - } else if (would_lose_untracked(o, collide_path)) { + update_path = alt_path = unique_path(opt, collide_path, "merged"); + } else if (would_lose_untracked(opt, collide_path)) { /* * Only way we get here is if both renames were from * a directory rename AND user had an untracked file * at the location where both files end up after the * two directory renames. See testcase 10d of t6043. */ - output(o, 1, _("Refusing to lose untracked file at " + output(opt, 1, _("Refusing to lose untracked file at " "%s, even though it's in the way."), collide_path); - update_path = alt_path = unique_path(o, collide_path, "merged"); + update_path = alt_path = unique_path(opt, collide_path, "merged"); } else { /* * FIXME: It's possible that the two files are identical @@@ -1606,22 -1659,30 +1611,22 @@@ * merge-recursive interoperate anyway, so punting for * now... */ - remove_file(o, 0, collide_path, 0); + remove_file(opt, 0, collide_path, 0); } /* Store things in diff_filespecs for functions that need it */ - memset(&a, 0, sizeof(struct diff_filespec)); - memset(&b, 0, sizeof(struct diff_filespec)); - null.path = a.path = b.path = (char *)collide_path; + null.path = (char *)collide_path; oidcpy(&null.oid, &null_oid); null.mode = 0; - oidcpy(&a.oid, a_oid); - a.mode = a_mode; - a.oid_valid = 1; - oidcpy(&b.oid, b_oid); - b.mode = b_mode; - b.oid_valid = 1; - - if (merge_mode_and_contents(o, &null, &a, &b, collide_path, - branch1, branch2, o->call_depth * 2, &mfi)) + + if (merge_mode_and_contents(opt, &null, a, b, collide_path, + branch1, branch2, opt->call_depth * 2, &mfi)) return -1; mfi.clean &= !alt_path; - if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, update_path)) + if (update_file(opt, mfi.clean, &mfi.blob, update_path)) return -1; - if (!mfi.clean && !o->call_depth && - update_stages(o, collide_path, NULL, &a, &b)) + if (!mfi.clean && !opt->call_depth && + update_stages(opt, collide_path, NULL, a, b)) return -1; free(alt_path); /* @@@ -1634,57 -1695,52 +1639,57 @@@ return mfi.clean; } -static int handle_rename_add(struct merge_options *o, +static int handle_rename_add(struct merge_options *opt, struct rename_conflict_info *ci) { /* a was renamed to c, and a separate c was added. */ - struct diff_filespec *a = ci->pair1->one; - struct diff_filespec *c = ci->pair1->two; + struct diff_filespec *a = ci->ren1->pair->one; + struct diff_filespec *c = ci->ren1->pair->two; char *path = c->path; char *prev_path_desc; struct merge_file_info mfi; - int other_stage = (ci->branch1 == o->branch1 ? 3 : 2); + const char *rename_branch = ci->ren1->branch; + const char *add_branch = (opt->branch1 == rename_branch ? + opt->branch2 : opt->branch1); + int other_stage = (ci->ren1->branch == opt->branch1 ? 3 : 2); - output(o, 1, _("CONFLICT (rename/add): " + output(opt, 1, _("CONFLICT (rename/add): " "Rename %s->%s in %s. Added %s in %s"), - a->path, c->path, ci->branch1, - c->path, ci->branch2); + a->path, c->path, rename_branch, + c->path, add_branch); prev_path_desc = xstrfmt("version of %s from %s", path, a->path); - if (merge_mode_and_contents(o, a, c, &ci->ren1_other, prev_path_desc, - o->branch1, o->branch2, - 1 + o->call_depth * 2, &mfi)) + if (merge_mode_and_contents(opt, a, c, + &ci->ren1->src_entry->stages[other_stage], + prev_path_desc, + opt->branch1, opt->branch2, + 1 + opt->call_depth * 2, &mfi)) return -1; free(prev_path_desc); - return handle_file_collision(o, + ci->ren1->dst_entry->stages[other_stage].path = mfi.blob.path = c->path; + return handle_file_collision(opt, c->path, a->path, NULL, - ci->branch1, ci->branch2, - &mfi.oid, mfi.mode, - &ci->dst_entry1->stages[other_stage].oid, - ci->dst_entry1->stages[other_stage].mode); + rename_branch, add_branch, + &mfi.blob, + &ci->ren1->dst_entry->stages[other_stage]); } -static char *find_path_for_conflict(struct merge_options *o, +static char *find_path_for_conflict(struct merge_options *opt, const char *path, const char *branch1, const char *branch2) { char *new_path = NULL; - if (dir_in_way(o->repo->index, path, !o->call_depth, 0)) { - new_path = unique_path(o, path, branch1); - output(o, 1, _("%s is a directory in %s adding " + if (dir_in_way(opt->repo->index, path, !opt->call_depth, 0)) { + new_path = unique_path(opt, path, branch1); + output(opt, 1, _("%s is a directory in %s adding " "as %s instead"), path, branch2, new_path); - } else if (would_lose_untracked(o, path)) { - new_path = unique_path(o, path, branch1); - output(o, 1, _("Refusing to lose untracked file" + } else if (would_lose_untracked(opt, path)) { + new_path = unique_path(opt, path, branch1); + output(opt, 1, _("Refusing to lose untracked file" " at %s; adding as %s instead"), path, new_path); } @@@ -1692,40 -1748,41 +1697,40 @@@ return new_path; } -static int handle_rename_rename_1to2(struct merge_options *o, +static int handle_rename_rename_1to2(struct merge_options *opt, struct rename_conflict_info *ci) { /* One file was renamed in both branches, but to different names. */ struct merge_file_info mfi; - struct diff_filespec other; struct diff_filespec *add; - struct diff_filespec *one = ci->pair1->one; - struct diff_filespec *a = ci->pair1->two; - struct diff_filespec *b = ci->pair2->two; + struct diff_filespec *o = ci->ren1->pair->one; + struct diff_filespec *a = ci->ren1->pair->two; + struct diff_filespec *b = ci->ren2->pair->two; char *path_desc; - output(o, 1, _("CONFLICT (rename/rename): " + output(opt, 1, _("CONFLICT (rename/rename): " "Rename \"%s\"->\"%s\" in branch \"%s\" " "rename \"%s\"->\"%s\" in \"%s\"%s"), - one->path, a->path, ci->branch1, - one->path, b->path, ci->branch2, - o->call_depth ? _(" (left unresolved)") : ""); + o->path, a->path, ci->ren1->branch, + o->path, b->path, ci->ren2->branch, + opt->call_depth ? _(" (left unresolved)") : ""); path_desc = xstrfmt("%s and %s, both renamed from %s", - a->path, b->path, one->path); - if (merge_mode_and_contents(o, one, a, b, path_desc, - ci->branch1, ci->branch2, - o->call_depth * 2, &mfi)) + a->path, b->path, o->path); + if (merge_mode_and_contents(opt, o, a, b, path_desc, + ci->ren1->branch, ci->ren2->branch, + opt->call_depth * 2, &mfi)) return -1; free(path_desc); - if (o->call_depth) { + if (opt->call_depth) { /* * FIXME: For rename/add-source conflicts (if we could detect * such), this is wrong. We should instead find a unique * pathname and then either rename the add-source file to that * unique path, or use that unique path instead of src here. */ - if (update_file(o, 0, &mfi.oid, mfi.mode, one->path)) + if (update_file(opt, 0, &mfi.blob, o->path)) return -1; /* @@@ -1736,65 -1793,61 +1741,65 @@@ * such cases, we should keep the added file around, * resolving the conflict at that path in its favor. */ - add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1); - if (add) { - if (update_file(o, 0, &add->oid, add->mode, a->path)) + add = &ci->ren1->dst_entry->stages[2 ^ 1]; + if (is_valid(add)) { + if (update_file(opt, 0, add, a->path)) return -1; } else - remove_file_from_index(o->repo->index, a->path); - add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1); - if (add) { - if (update_file(o, 0, &add->oid, add->mode, b->path)) + remove_file_from_index(opt->repo->index, a->path); + add = &ci->ren2->dst_entry->stages[3 ^ 1]; + if (is_valid(add)) { + if (update_file(opt, 0, add, b->path)) return -1; } else - remove_file_from_index(o->repo->index, b->path); + remove_file_from_index(opt->repo->index, b->path); } else { /* * For each destination path, we need to see if there is a * rename/add collision. If not, we can write the file out * to the specified location. */ - add = filespec_from_entry(&other, ci->dst_entry1, 2 ^ 1); - if (add) { - if (handle_file_collision(o, a->path, + add = &ci->ren1->dst_entry->stages[2 ^ 1]; + if (is_valid(add)) { + add->path = mfi.blob.path = a->path; + if (handle_file_collision(opt, a->path, NULL, NULL, - ci->branch1, ci->branch2, - &mfi.oid, mfi.mode, - &add->oid, add->mode) < 0) + ci->ren1->branch, + ci->ren2->branch, + &mfi.blob, add) < 0) return -1; } else { - char *new_path = find_path_for_conflict(o, a->path, - ci->branch1, - ci->branch2); - if (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : a->path)) + char *new_path = find_path_for_conflict(opt, a->path, + ci->ren1->branch, + ci->ren2->branch); + if (update_file(opt, 0, &mfi.blob, + new_path ? new_path : a->path)) return -1; free(new_path); - if (update_stages(o, a->path, NULL, a, NULL)) + if (update_stages(opt, a->path, NULL, a, NULL)) return -1; } - add = filespec_from_entry(&other, ci->dst_entry2, 3 ^ 1); - if (add) { - if (handle_file_collision(o, b->path, + add = &ci->ren2->dst_entry->stages[3 ^ 1]; + if (is_valid(add)) { + add->path = mfi.blob.path = b->path; + if (handle_file_collision(opt, b->path, NULL, NULL, - ci->branch1, ci->branch2, - &add->oid, add->mode, - &mfi.oid, mfi.mode) < 0) + ci->ren1->branch, + ci->ren2->branch, + add, &mfi.blob) < 0) return -1; } else { - char *new_path = find_path_for_conflict(o, b->path, - ci->branch2, - ci->branch1); - if (update_file(o, 0, &mfi.oid, mfi.mode, new_path ? new_path : b->path)) + char *new_path = find_path_for_conflict(opt, b->path, + ci->ren2->branch, + ci->ren1->branch); + if (update_file(opt, 0, &mfi.blob, + new_path ? new_path : b->path)) return -1; free(new_path); - if (update_stages(o, b->path, NULL, NULL, b)) + if (update_stages(opt, b->path, NULL, NULL, b)) return -1; } } @@@ -1802,68 -1855,58 +1807,68 @@@ return 0; } -static int handle_rename_rename_2to1(struct merge_options *o, +static int handle_rename_rename_2to1(struct merge_options *opt, struct rename_conflict_info *ci) { /* Two files, a & b, were renamed to the same thing, c. */ - struct diff_filespec *a = ci->pair1->one; - struct diff_filespec *b = ci->pair2->one; - struct diff_filespec *c1 = ci->pair1->two; - struct diff_filespec *c2 = ci->pair2->two; + struct diff_filespec *a = ci->ren1->pair->one; + struct diff_filespec *b = ci->ren2->pair->one; + struct diff_filespec *c1 = ci->ren1->pair->two; + struct diff_filespec *c2 = ci->ren2->pair->two; char *path = c1->path; /* == c2->path */ char *path_side_1_desc; char *path_side_2_desc; struct merge_file_info mfi_c1; struct merge_file_info mfi_c2; + int ostage1, ostage2; - output(o, 1, _("CONFLICT (rename/rename): " + output(opt, 1, _("CONFLICT (rename/rename): " "Rename %s->%s in %s. " "Rename %s->%s in %s"), - a->path, c1->path, ci->branch1, - b->path, c2->path, ci->branch2); + a->path, c1->path, ci->ren1->branch, + b->path, c2->path, ci->ren2->branch); path_side_1_desc = xstrfmt("version of %s from %s", path, a->path); path_side_2_desc = xstrfmt("version of %s from %s", path, b->path); - if (merge_mode_and_contents(o, a, c1, &ci->ren1_other, path_side_1_desc, - o->branch1, o->branch2, - 1 + o->call_depth * 2, &mfi_c1) || - merge_mode_and_contents(o, b, &ci->ren2_other, c2, path_side_2_desc, - o->branch1, o->branch2, - 1 + o->call_depth * 2, &mfi_c2)) + ostage1 = ci->ren1->branch == opt->branch1 ? 3 : 2; + ostage2 = ostage1 ^ 1; + ci->ren1->src_entry->stages[ostage1].path = a->path; + ci->ren2->src_entry->stages[ostage2].path = b->path; + if (merge_mode_and_contents(opt, a, c1, + &ci->ren1->src_entry->stages[ostage1], + path_side_1_desc, + opt->branch1, opt->branch2, + 1 + opt->call_depth * 2, &mfi_c1) || + merge_mode_and_contents(opt, b, + &ci->ren2->src_entry->stages[ostage2], + c2, path_side_2_desc, + opt->branch1, opt->branch2, + 1 + opt->call_depth * 2, &mfi_c2)) return -1; free(path_side_1_desc); free(path_side_2_desc); + mfi_c1.blob.path = path; + mfi_c2.blob.path = path; - return handle_file_collision(o, path, a->path, b->path, - ci->branch1, ci->branch2, - &mfi_c1.oid, mfi_c1.mode, - &mfi_c2.oid, mfi_c2.mode); + return handle_file_collision(opt, path, a->path, b->path, + ci->ren1->branch, ci->ren2->branch, + &mfi_c1.blob, &mfi_c2.blob); } /* * Get the diff_filepairs changed between o_tree and tree. */ -static struct diff_queue_struct *get_diffpairs(struct merge_options *o, +static struct diff_queue_struct *get_diffpairs(struct merge_options *opt, struct tree *o_tree, struct tree *tree) { struct diff_queue_struct *ret; struct diff_options opts; - repo_diff_setup(o->repo, &opts); + repo_diff_setup(opt->repo, &opts); opts.flags.recursive = 1; opts.flags.rename_empty = 0; - opts.detect_rename = merge_detect_rename(o); + opts.detect_rename = merge_detect_rename(opt); /* * We do not have logic to handle the detection of copies. In * fact, it may not even make sense to add such logic: would we @@@ -1872,17 -1915,17 +1877,17 @@@ */ if (opts.detect_rename > DIFF_DETECT_RENAME) opts.detect_rename = DIFF_DETECT_RENAME; - opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit : - o->diff_rename_limit >= 0 ? o->diff_rename_limit : + opts.rename_limit = opt->merge_rename_limit >= 0 ? opt->merge_rename_limit : + opt->diff_rename_limit >= 0 ? opt->diff_rename_limit : 1000; - opts.rename_score = o->rename_score; - opts.show_rename_progress = o->show_rename_progress; + opts.rename_score = opt->rename_score; + opts.show_rename_progress = opt->show_rename_progress; opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_setup_done(&opts); diff_tree_oid(&o_tree->object.oid, &tree->object.oid, "", &opts); diffcore_std(&opts); - if (opts.needed_rename_limit > o->needed_rename_limit) - o->needed_rename_limit = opts.needed_rename_limit; + if (opts.needed_rename_limit > opt->needed_rename_limit) + opt->needed_rename_limit = opts.needed_rename_limit; ret = xmalloc(sizeof(*ret)); *ret = diff_queued_diff; @@@ -1897,7 -1940,7 +1902,7 @@@ static int tree_has_path(struct tree *tree, const char *path) { struct object_id hashy; - unsigned int mode_o; + unsigned short mode_o; return !get_tree_entry(&tree->object.oid, path, &hashy, &mode_o); @@@ -2001,7 -2044,7 +2006,7 @@@ static void remove_hashmap_entries(stru * level conflicts for the renamed location. If there is a rename and * there are no conflicts, return the new name. Otherwise, return NULL. */ -static char *handle_path_level_conflicts(struct merge_options *o, +static char *handle_path_level_conflicts(struct merge_options *opt, const char *path, struct dir_rename_entry *entry, struct hashmap *collisions, @@@ -2022,7 -2065,7 +2027,7 @@@ /* This should only happen when entry->non_unique_new_dir set */ if (!entry->non_unique_new_dir) BUG("entry->non_unqiue_dir not set and !new_path"); - output(o, 1, _("CONFLICT (directory rename split): " + output(opt, 1, _("CONFLICT (directory rename split): " "Unclear where to place %s because directory " "%s was renamed to multiple other directories, " "with no destination getting a majority of the " @@@ -2054,7 -2097,7 +2059,7 @@@ collision_ent->reported_already = 1; strbuf_add_separated_string_list(&collision_paths, ", ", &collision_ent->source_files); - output(o, 1, _("CONFLICT (implicit dir rename): Existing " + output(opt, 1, _("CONFLICT (implicit dir rename): Existing " "file/dir at %s in the way of implicit " "directory rename(s) putting the following " "path(s) there: %s."), @@@ -2064,7 -2107,7 +2069,7 @@@ collision_ent->reported_already = 1; strbuf_add_separated_string_list(&collision_paths, ", ", &collision_ent->source_files); - output(o, 1, _("CONFLICT (implicit dir rename): Cannot map " + output(opt, 1, _("CONFLICT (implicit dir rename): Cannot map " "more than one path to %s; implicit directory " "renames tried to put these paths there: %s"), new_path, collision_paths.buf); @@@ -2101,7 -2144,7 +2106,7 @@@ * causes conflicts for files within those merged directories, then * that should be detected at the individual path level. */ -static void handle_directory_level_conflicts(struct merge_options *o, +static void handle_directory_level_conflicts(struct merge_options *opt, struct hashmap *dir_re_head, struct tree *head, struct hashmap *dir_re_merge, @@@ -2156,11 -2199,11 +2161,11 @@@ * know that head_ent->new_dir and merge_ent->new_dir * are different strings. */ - output(o, 1, _("CONFLICT (rename/rename): " + output(opt, 1, _("CONFLICT (rename/rename): " "Rename directory %s->%s in %s. " "Rename directory %s->%s in %s"), - head_ent->dir, head_ent->new_dir.buf, o->branch1, - head_ent->dir, merge_ent->new_dir.buf, o->branch2); + head_ent->dir, head_ent->new_dir.buf, opt->branch1, + head_ent->dir, merge_ent->new_dir.buf, opt->branch2); string_list_append(&remove_from_head, head_ent->dir)->util = head_ent; strbuf_release(&head_ent->new_dir); @@@ -2359,7 -2402,7 +2364,7 @@@ static void compute_collisions(struct h } } -static char *check_for_directory_rename(struct merge_options *o, +static char *check_for_directory_rename(struct merge_options *opt, const char *path, struct tree *tree, struct hashmap *dir_renames, @@@ -2400,11 -2443,11 +2405,11 @@@ */ oentry = dir_rename_find_entry(dir_rename_exclusions, entry->new_dir.buf); if (oentry) { - output(o, 1, _("WARNING: Avoiding applying %s -> %s rename " + output(opt, 1, _("WARNING: Avoiding applying %s -> %s rename " "to %s, because %s itself was renamed."), entry->dir, entry->new_dir.buf, path, entry->new_dir.buf); } else { - new_path = handle_path_level_conflicts(o, path, entry, + new_path = handle_path_level_conflicts(opt, path, entry, collisions, tree); *clean_merge &= (new_path != NULL); } @@@ -2412,7 -2455,7 +2417,7 @@@ return new_path; } -static void apply_directory_rename_modifications(struct merge_options *o, +static void apply_directory_rename_modifications(struct merge_options *opt, struct diff_filepair *pair, char *new_path, struct rename *re, @@@ -2435,11 -2478,11 +2440,11 @@@ * saying the file would have been overwritten), but it might * be dirty, though. */ - update_wd = !was_dirty(o, pair->two->path); + update_wd = !was_dirty(opt, pair->two->path); if (!update_wd) - output(o, 1, _("Refusing to lose dirty file at %s"), + output(opt, 1, _("Refusing to lose dirty file at %s"), pair->two->path); - remove_file(o, 1, pair->two->path, !update_wd); + remove_file(opt, 1, pair->two->path, !update_wd); /* Find or create a new re->dst_entry */ item = string_list_lookup(entries, new_path); @@@ -2499,18 -2542,16 +2504,18 @@@ &re->dst_entry->stages[stage].oid, &re->dst_entry->stages[stage].mode); - /* Update pair status */ - if (pair->status == 'A') { - /* - * Recording rename information for this add makes it look - * like a rename/delete conflict. Make sure we can - * correctly handle this as an add that was moved to a new - * directory instead of reporting a rename/delete conflict. - */ - re->add_turned_into_rename = 1; - } + /* + * Record the original change status (or 'type' of change). If it + * was originally an add ('A'), this lets us differentiate later + * between a RENAME_DELETE conflict and RENAME_VIA_DIR (they + * otherwise look the same). If it was originally a rename ('R'), + * this lets us remember and report accurately about the transitive + * renaming that occurred via the directory rename detection. Also, + * record the original destination name. + */ + re->dir_rename_original_type = pair->status; + re->dir_rename_original_dest = pair->two->path; + /* * We don't actually look at pair->status again, but it seems * pedagogically correct to adjust it. @@@ -2530,8 -2571,7 +2535,8 @@@ * to be able to associate the correct cache entries with the rename * information; tree is always equal to either a_tree or b_tree. */ -static struct string_list *get_renames(struct merge_options *o, +static struct string_list *get_renames(struct merge_options *opt, + const char *branch, struct diff_queue_struct *pairs, struct hashmap *dir_renames, struct hashmap *dir_rename_exclusions, @@@ -2561,7 -2601,7 +2566,7 @@@ diff_free_filepair(pair); continue; } - new_path = check_for_directory_rename(o, pair->two->path, tree, + new_path = check_for_directory_rename(opt, pair->two->path, tree, dir_renames, dir_rename_exclusions, &collisions, @@@ -2573,10 -2613,8 +2578,10 @@@ re = xmalloc(sizeof(*re)); re->processed = 0; - re->add_turned_into_rename = 0; re->pair = pair; + re->branch = branch; + re->dir_rename_original_type = '\0'; + re->dir_rename_original_dest = NULL; item = string_list_lookup(entries, re->pair->one->path); if (!item) re->src_entry = insert_stage_data(re->pair->one->path, @@@ -2593,7 -2631,7 +2598,7 @@@ item = string_list_insert(renames, pair->one->path); item->util = re; if (new_path) - apply_directory_rename_modifications(o, pair, new_path, + apply_directory_rename_modifications(opt, pair, new_path, re, tree, o_tree, a_tree, b_tree, entries); @@@ -2608,7 -2646,7 +2613,7 @@@ return renames; } -static int process_renames(struct merge_options *o, +static int process_renames(struct merge_options *opt, struct string_list *a_renames, struct string_list *b_renames) { @@@ -2631,6 -2669,7 +2636,6 @@@ for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) { struct string_list *renames1, *renames2Dst; struct rename *ren1 = NULL, *ren2 = NULL; - const char *branch1, *branch2; const char *ren1_src, *ren1_dst; struct string_list_item *lookup; @@@ -2651,9 -2690,13 +2656,9 @@@ if (ren1) { renames1 = a_renames; renames2Dst = &b_by_dst; - branch1 = o->branch1; - branch2 = o->branch2; } else { renames1 = b_renames; renames2Dst = &a_by_dst; - branch1 = o->branch2; - branch2 = o->branch1; SWAP(ren2, ren1); } @@@ -2687,13 -2730,22 +2692,13 @@@ * the base stage (think of rename + * add-source cases). */ - remove_file(o, 1, ren1_src, 1); + remove_file(opt, 1, ren1_src, 1); update_entry(ren1->dst_entry, ren1->pair->one, ren1->pair->two, ren2->pair->two); } - setup_rename_conflict_info(rename_type, - ren1->pair, - ren2->pair, - branch1, - branch2, - ren1->dst_entry, - ren2->dst_entry, - o, - NULL, - NULL); + setup_rename_conflict_info(rename_type, opt, ren1, ren2); } else if ((lookup = string_list_lookup(renames2Dst, ren1_dst))) { /* Two different files renamed to the same thing */ char *ren2_dst; @@@ -2712,7 -2764,16 +2717,7 @@@ ren2->src_entry->processed = 1; setup_rename_conflict_info(RENAME_TWO_FILES_TO_ONE, - ren1->pair, - ren2->pair, - branch1, - branch2, - ren1->dst_entry, - ren2->dst_entry, - o, - ren1->src_entry, - ren2->src_entry); - + opt, ren1, ren2); } else { /* Renamed in 1, maybe changed in 2 */ /* we only use sha1 and mode of these */ @@@ -2732,8 -2793,8 +2737,8 @@@ * stage and in other_stage (think of rename + * add-source case). */ - remove_file(o, 1, ren1_src, - renamed_stage == 2 || !was_tracked(o, ren1_src)); + remove_file(opt, 1, ren1_src, + renamed_stage == 2 || !was_tracked(opt, ren1_src)); oidcpy(&src_other.oid, &ren1->src_entry->stages[other_stage].oid); @@@ -2744,12 -2805,28 +2749,12 @@@ try_merge = 0; if (oid_eq(&src_other.oid, &null_oid) && - ren1->add_turned_into_rename) { + ren1->dir_rename_original_type == 'A') { setup_rename_conflict_info(RENAME_VIA_DIR, - ren1->pair, - NULL, - branch1, - branch2, - ren1->dst_entry, - NULL, - o, - NULL, - NULL); + opt, ren1, NULL); } else if (oid_eq(&src_other.oid, &null_oid)) { setup_rename_conflict_info(RENAME_DELETE, - ren1->pair, - NULL, - branch1, - branch2, - ren1->dst_entry, - NULL, - o, - NULL, - NULL); + opt, ren1, NULL); } else if ((dst_other.mode == ren1->pair->two->mode) && oid_eq(&dst_other.oid, &ren1->pair->two->oid)) { /* @@@ -2760,8 -2837,9 +2765,8 @@@ * update_file_flags() instead of * update_file(). */ - if (update_file_flags(o, - &ren1->pair->two->oid, - ren1->pair->two->mode, + if (update_file_flags(opt, + ren1->pair->two, ren1_dst, 1, /* update_cache */ 0 /* update_wd */)) @@@ -2775,17 -2853,25 +2780,17 @@@ * file, then the merge will be clean. */ setup_rename_conflict_info(RENAME_ADD, - ren1->pair, - NULL, - branch1, - branch2, - ren1->dst_entry, - NULL, - o, - ren1->src_entry, - NULL); + opt, ren1, NULL); } else try_merge = 1; if (clean_merge < 0) goto cleanup_and_return; if (try_merge) { - struct diff_filespec *one, *a, *b; + struct diff_filespec *o, *a, *b; src_other.path = (char *)ren1_src; - one = ren1->pair->one; + o = ren1->pair->one; if (a_renames == renames1) { a = ren1->pair->two; b = &src_other; @@@ -2793,9 -2879,17 +2798,9 @@@ b = ren1->pair->two; a = &src_other; } - update_entry(ren1->dst_entry, one, a, b); + update_entry(ren1->dst_entry, o, a, b); setup_rename_conflict_info(RENAME_NORMAL, - ren1->pair, - NULL, - branch1, - NULL, - ren1->dst_entry, - NULL, - o, - NULL, - NULL); + opt, ren1, NULL); } } } @@@ -2830,7 -2924,7 +2835,7 @@@ static void initial_cleanup_rename(stru free(pairs); } -static int detect_and_process_renames(struct merge_options *o, +static int detect_and_process_renames(struct merge_options *opt, struct tree *common, struct tree *head, struct tree *merge, @@@ -2844,17 -2938,17 +2849,17 @@@ ri->head_renames = NULL; ri->merge_renames = NULL; - if (!merge_detect_rename(o)) + if (!merge_detect_rename(opt)) return 1; - head_pairs = get_diffpairs(o, common, head); - merge_pairs = get_diffpairs(o, common, merge); + head_pairs = get_diffpairs(opt, common, head); + merge_pairs = get_diffpairs(opt, common, merge); - if (o->detect_directory_renames) { + if (opt->detect_directory_renames) { dir_re_head = get_directory_renames(head_pairs); dir_re_merge = get_directory_renames(merge_pairs); - handle_directory_level_conflicts(o, + handle_directory_level_conflicts(opt, dir_re_head, head, dir_re_merge, merge); } else { @@@ -2864,19 -2958,19 +2869,19 @@@ dir_rename_init(dir_re_merge); } - ri->head_renames = get_renames(o, head_pairs, + ri->head_renames = get_renames(opt, opt->branch1, head_pairs, dir_re_merge, dir_re_head, head, common, head, merge, entries, &clean); if (clean < 0) goto cleanup; - ri->merge_renames = get_renames(o, merge_pairs, + ri->merge_renames = get_renames(opt, opt->branch2, merge_pairs, dir_re_head, dir_re_merge, merge, common, head, merge, entries, &clean); if (clean < 0) goto cleanup; - clean &= process_renames(o, ri->head_renames, ri->merge_renames); + clean &= process_renames(opt, ri->head_renames, ri->merge_renames); cleanup: /* @@@ -2912,7 -3006,12 +2917,7 @@@ static void final_cleanup_renames(struc final_cleanup_rename(re_info->merge_renames); } -static struct object_id *stage_oid(const struct object_id *oid, unsigned mode) -{ - return (is_null_oid(oid) || mode == 0) ? NULL: (struct object_id *)oid; -} - -static int read_oid_strbuf(struct merge_options *o, +static int read_oid_strbuf(struct merge_options *opt, const struct object_id *oid, struct strbuf *dst) { @@@ -2921,99 -3020,130 +2926,99 @@@ unsigned long size; buf = read_object_file(oid, &type, &size); if (!buf) - return err(o, _("cannot read object %s"), oid_to_hex(oid)); + return err(opt, _("cannot read object %s"), oid_to_hex(oid)); if (type != OBJ_BLOB) { free(buf); - return err(o, _("object %s is not a blob"), oid_to_hex(oid)); + return err(opt, _("object %s is not a blob"), oid_to_hex(oid)); } strbuf_attach(dst, buf, size, size + 1); return 0; } static int blob_unchanged(struct merge_options *opt, - const struct object_id *o_oid, - unsigned o_mode, - const struct object_id *a_oid, - unsigned a_mode, + const struct diff_filespec *o, + const struct diff_filespec *a, int renormalize, const char *path) { - struct strbuf o = STRBUF_INIT; - struct strbuf a = STRBUF_INIT; + struct strbuf obuf = STRBUF_INIT; + struct strbuf abuf = STRBUF_INIT; int ret = 0; /* assume changed for safety */ + const struct index_state *idx = opt->repo->index; - if (a_mode != o_mode) + if (a->mode != o->mode) return 0; - if (oid_eq(o_oid, a_oid)) + if (oid_eq(&o->oid, &a->oid)) return 1; if (!renormalize) return 0; - assert(o_oid && a_oid); - if (read_oid_strbuf(opt, o_oid, &o) || read_oid_strbuf(opt, a_oid, &a)) + if (read_oid_strbuf(opt, &o->oid, &obuf) || + read_oid_strbuf(opt, &a->oid, &abuf)) goto error_return; /* * Note: binary | is used so that both renormalizations are * performed. Comparison can be skipped if both files are * unchanged since their sha1s have already been compared. */ - if (renormalize_buffer(opt->repo->index, path, o.buf, o.len, &o) | - renormalize_buffer(opt->repo->index, path, a.buf, a.len, &a)) - ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len)); + if (renormalize_buffer(idx, path, obuf.buf, obuf.len, &obuf) | + renormalize_buffer(idx, path, abuf.buf, abuf.len, &abuf)) + ret = (obuf.len == abuf.len && !memcmp(obuf.buf, abuf.buf, obuf.len)); error_return: - strbuf_release(&o); - strbuf_release(&a); + strbuf_release(&obuf); + strbuf_release(&abuf); return ret; } -static int handle_modify_delete(struct merge_options *o, +static int handle_modify_delete(struct merge_options *opt, const char *path, - struct object_id *o_oid, int o_mode, - struct object_id *a_oid, int a_mode, - struct object_id *b_oid, int b_mode) + const struct diff_filespec *o, + const struct diff_filespec *a, + const struct diff_filespec *b) { const char *modify_branch, *delete_branch; - struct object_id *changed_oid; - int changed_mode; - - if (a_oid) { - modify_branch = o->branch1; - delete_branch = o->branch2; - changed_oid = a_oid; - changed_mode = a_mode; + const struct diff_filespec *changed; + + if (is_valid(a)) { + modify_branch = opt->branch1; + delete_branch = opt->branch2; + changed = a; } else { - modify_branch = o->branch2; - delete_branch = o->branch1; - changed_oid = b_oid; - changed_mode = b_mode; + modify_branch = opt->branch2; + delete_branch = opt->branch1; + changed = b; } - return handle_change_delete(o, + return handle_change_delete(opt, path, NULL, - o_oid, o_mode, - changed_oid, changed_mode, + o, changed, modify_branch, delete_branch, _("modify"), _("modified")); } -static int handle_content_merge(struct merge_options *o, +static int handle_content_merge(struct merge_file_info *mfi, + struct merge_options *opt, const char *path, int is_dirty, - struct object_id *o_oid, int o_mode, - struct object_id *a_oid, int a_mode, - struct object_id *b_oid, int b_mode, - struct rename_conflict_info *rename_conflict_info) + const struct diff_filespec *o, + const struct diff_filespec *a, + const struct diff_filespec *b, + struct rename_conflict_info *ci) { const char *reason = _("content"); - const char *path1 = NULL, *path2 = NULL; - struct merge_file_info mfi; - struct diff_filespec one, a, b; unsigned df_conflict_remains = 0; - if (!o_oid) { + if (!is_valid(o)) reason = _("add/add"); - o_oid = (struct object_id *)&null_oid; - } - one.path = a.path = b.path = (char *)path; - oidcpy(&one.oid, o_oid); - one.mode = o_mode; - oidcpy(&a.oid, a_oid); - a.mode = a_mode; - oidcpy(&b.oid, b_oid); - b.mode = b_mode; - - if (rename_conflict_info) { - struct diff_filepair *pair1 = rename_conflict_info->pair1; - - path1 = (o->branch1 == rename_conflict_info->branch1) ? - pair1->two->path : pair1->one->path; - /* If rename_conflict_info->pair2 != NULL, we are in - * RENAME_ONE_FILE_TO_ONE case. Otherwise, we have a - * normal rename. - */ - path2 = (rename_conflict_info->pair2 || - o->branch2 == rename_conflict_info->branch1) ? - pair1->two->path : pair1->one->path; - one.path = pair1->one->path; - a.path = (char *)path1; - b.path = (char *)path2; - - if (dir_in_way(o->repo->index, path, !o->call_depth, - S_ISGITLINK(pair1->two->mode))) - df_conflict_remains = 1; - } - if (merge_mode_and_contents(o, &one, &a, &b, path, - o->branch1, o->branch2, - o->call_depth * 2, &mfi)) + + assert(o->path && a->path && b->path); + if (ci && dir_in_way(opt->repo->index, path, !opt->call_depth, + S_ISGITLINK(ci->ren1->pair->two->mode))) + df_conflict_remains = 1; + + if (merge_mode_and_contents(opt, o, a, b, path, + opt->branch1, opt->branch2, + opt->call_depth * 2, mfi)) return -1; /* @@@ -3022,14 -3152,15 +3027,14 @@@ * b) The merge matches what was in HEAD (content, mode, pathname) * c) The target path is usable (i.e. not involved in D/F conflict) */ - if (mfi.clean && - was_tracked_and_matches(o, path, &mfi.oid, mfi.mode) && + if (mfi->clean && was_tracked_and_matches(opt, path, &mfi->blob) && !df_conflict_remains) { int pos; struct cache_entry *ce; - output(o, 3, _("Skipped %s (merged same as existing)"), path); - if (add_cacheinfo(o, mfi.mode, &mfi.oid, path, - 0, (!o->call_depth && !is_dirty), 0)) + output(opt, 3, _("Skipped %s (merged same as existing)"), path); + if (add_cacheinfo(opt, &mfi->blob, path, + 0, (!opt->call_depth && !is_dirty), 0)) return -1; /* * However, add_cacheinfo() will delete the old cache entry @@@ -3037,196 -3168,109 +3042,196 @@@ * flag to avoid making the file appear as if it were * deleted by the user. */ - pos = index_name_pos(&o->orig_index, path, strlen(path)); - ce = o->orig_index.cache[pos]; + pos = index_name_pos(&opt->orig_index, path, strlen(path)); + ce = opt->orig_index.cache[pos]; if (ce_skip_worktree(ce)) { - pos = index_name_pos(o->repo->index, path, strlen(path)); - ce = o->repo->index->cache[pos]; + pos = index_name_pos(opt->repo->index, path, strlen(path)); + ce = opt->repo->index->cache[pos]; ce->ce_flags |= CE_SKIP_WORKTREE; } - return mfi.clean; + return mfi->clean; } - if (!mfi.clean) { - if (S_ISGITLINK(mfi.mode)) + if (!mfi->clean) { + if (S_ISGITLINK(mfi->blob.mode)) reason = _("submodule"); - output(o, 1, _("CONFLICT (%s): Merge conflict in %s"), + output(opt, 1, _("CONFLICT (%s): Merge conflict in %s"), reason, path); - if (rename_conflict_info && !df_conflict_remains) - if (update_stages(o, path, &one, &a, &b)) + if (ci && !df_conflict_remains) + if (update_stages(opt, path, o, a, b)) return -1; } if (df_conflict_remains || is_dirty) { char *new_path; - if (o->call_depth) { - remove_file_from_index(o->repo->index, path); + if (opt->call_depth) { + remove_file_from_index(opt->repo->index, path); } else { - if (!mfi.clean) { - if (update_stages(o, path, &one, &a, &b)) + if (!mfi->clean) { + if (update_stages(opt, path, o, a, b)) return -1; } else { - int file_from_stage2 = was_tracked(o, path); - struct diff_filespec merged; - oidcpy(&merged.oid, &mfi.oid); - merged.mode = mfi.mode; - - if (update_stages(o, path, NULL, - file_from_stage2 ? &merged : NULL, - file_from_stage2 ? NULL : &merged)) + int file_from_stage2 = was_tracked(opt, path); + + if (update_stages(opt, path, NULL, + file_from_stage2 ? &mfi->blob : NULL, + file_from_stage2 ? NULL : &mfi->blob)) return -1; } } - new_path = unique_path(o, path, rename_conflict_info->branch1); + new_path = unique_path(opt, path, ci->ren1->branch); if (is_dirty) { - output(o, 1, _("Refusing to lose dirty file at %s"), + output(opt, 1, _("Refusing to lose dirty file at %s"), path); } - output(o, 1, _("Adding as %s instead"), new_path); - if (update_file(o, 0, &mfi.oid, mfi.mode, new_path)) { + output(opt, 1, _("Adding as %s instead"), new_path); + if (update_file(opt, 0, &mfi->blob, new_path)) { free(new_path); return -1; } free(new_path); - mfi.clean = 0; - } else if (update_file(o, mfi.clean, &mfi.oid, mfi.mode, path)) + mfi->clean = 0; + } else if (update_file(opt, mfi->clean, &mfi->blob, path)) return -1; - return !is_dirty && mfi.clean; + return !is_dirty && mfi->clean; } -static int handle_rename_normal(struct merge_options *o, +static int handle_rename_normal(struct merge_options *opt, const char *path, - struct object_id *o_oid, unsigned int o_mode, - struct object_id *a_oid, unsigned int a_mode, - struct object_id *b_oid, unsigned int b_mode, + const struct diff_filespec *o, + const struct diff_filespec *a, + const struct diff_filespec *b, struct rename_conflict_info *ci) { + struct rename *ren = ci->ren1; + struct merge_file_info mfi; + int clean; + int side = (ren->branch == opt->branch1 ? 2 : 3); + /* Merge the content and write it out */ - return handle_content_merge(o, path, was_dirty(o, path), - o_oid, o_mode, a_oid, a_mode, b_oid, b_mode, - ci); + clean = handle_content_merge(&mfi, opt, path, was_dirty(opt, path), + o, a, b, ci); + + if (clean && opt->detect_directory_renames == 1 && + ren->dir_rename_original_dest) { + if (update_stages(opt, path, + NULL, + side == 2 ? &mfi.blob : NULL, + side == 2 ? NULL : &mfi.blob)) + return -1; + clean = 0; /* not clean, but conflicted */ + } + return clean; +} + +static void dir_rename_warning(const char *msg, + int is_add, + int clean, + struct merge_options *opt, + struct rename *ren) +{ + const char *other_branch; + other_branch = (ren->branch == opt->branch1 ? + opt->branch2 : opt->branch1); + if (is_add) { + output(opt, clean ? 2 : 1, msg, + ren->pair->one->path, ren->branch, + other_branch, ren->pair->two->path); + return; + } + output(opt, clean ? 2 : 1, msg, + ren->pair->one->path, ren->dir_rename_original_dest, ren->branch, + other_branch, ren->pair->two->path); +} +static int warn_about_dir_renamed_entries(struct merge_options *opt, + struct rename *ren) +{ + const char *msg; + int clean = 1, is_add; + + if (!ren) + return clean; + + /* Return early if ren was not affected/created by a directory rename */ + if (!ren->dir_rename_original_dest) + return clean; + + /* Sanity checks */ + assert(opt->detect_directory_renames > 0); + assert(ren->dir_rename_original_type == 'A' || + ren->dir_rename_original_type == 'R'); + + /* Check whether to treat directory renames as a conflict */ + clean = (opt->detect_directory_renames == 2); + + is_add = (ren->dir_rename_original_type == 'A'); + if (ren->dir_rename_original_type == 'A' && clean) { + msg = _("Path updated: %s added in %s inside a " + "directory that was renamed in %s; moving it to %s."); + } else if (ren->dir_rename_original_type == 'A' && !clean) { + msg = _("CONFLICT (file location): %s added in %s " + "inside a directory that was renamed in %s, " + "suggesting it should perhaps be moved to %s."); + } else if (ren->dir_rename_original_type == 'R' && clean) { + msg = _("Path updated: %s renamed to %s in %s, inside a " + "directory that was renamed in %s; moving it to %s."); + } else if (ren->dir_rename_original_type == 'R' && !clean) { + msg = _("CONFLICT (file location): %s renamed to %s in %s, " + "inside a directory that was renamed in %s, " + "suggesting it should perhaps be moved to %s."); + } else { + BUG("Impossible dir_rename_original_type/clean combination"); + } + dir_rename_warning(msg, is_add, clean, opt, ren); + + return clean; } /* Per entry merge function */ -static int process_entry(struct merge_options *o, +static int process_entry(struct merge_options *opt, const char *path, struct stage_data *entry) { int clean_merge = 1; - int normalize = o->renormalize; - unsigned o_mode = entry->stages[1].mode; - unsigned a_mode = entry->stages[2].mode; - unsigned b_mode = entry->stages[3].mode; - struct object_id *o_oid = stage_oid(&entry->stages[1].oid, o_mode); - struct object_id *a_oid = stage_oid(&entry->stages[2].oid, a_mode); - struct object_id *b_oid = stage_oid(&entry->stages[3].oid, b_mode); + int normalize = opt->renormalize; + + struct diff_filespec *o = &entry->stages[1]; + struct diff_filespec *a = &entry->stages[2]; + struct diff_filespec *b = &entry->stages[3]; + int o_valid = is_valid(o); + int a_valid = is_valid(a); + int b_valid = is_valid(b); + o->path = a->path = b->path = (char*)path; entry->processed = 1; if (entry->rename_conflict_info) { - struct rename_conflict_info *conflict_info = entry->rename_conflict_info; - switch (conflict_info->rename_type) { + struct rename_conflict_info *ci = entry->rename_conflict_info; + struct diff_filespec *temp; + int path_clean; + + path_clean = warn_about_dir_renamed_entries(opt, ci->ren1); + path_clean &= warn_about_dir_renamed_entries(opt, ci->ren2); + + /* + * For cases with a single rename, {o,a,b}->path have all been + * set to the rename target path; we need to set two of these + * back to the rename source. + * For rename/rename conflicts, we'll manually fix paths below. + */ + temp = (opt->branch1 == ci->ren1->branch) ? b : a; + o->path = temp->path = ci->ren1->pair->one->path; + if (ci->ren2) { + assert(opt->branch1 == ci->ren1->branch); + } + + switch (ci->rename_type) { case RENAME_NORMAL: case RENAME_ONE_FILE_TO_ONE: - clean_merge = handle_rename_normal(o, - path, - o_oid, o_mode, - a_oid, a_mode, - b_oid, b_mode, - conflict_info); + clean_merge = handle_rename_normal(opt, path, o, a, b, + ci); break; case RENAME_VIA_DIR: - clean_merge = 1; - if (handle_rename_via_dir(o, - conflict_info->pair1, - conflict_info->branch1)) - clean_merge = -1; + clean_merge = handle_rename_via_dir(opt, ci); break; case RENAME_ADD: /* @@@ -3235,172 -3279,165 +3240,172 @@@ * two-way merged cleanly with the added file, I * guess it's a clean merge? */ - clean_merge = handle_rename_add(o, conflict_info); + clean_merge = handle_rename_add(opt, ci); break; case RENAME_DELETE: clean_merge = 0; - if (handle_rename_delete(o, - conflict_info->pair1, - conflict_info->branch1, - conflict_info->branch2)) + if (handle_rename_delete(opt, ci)) clean_merge = -1; break; case RENAME_ONE_FILE_TO_TWO: + /* + * Manually fix up paths; note: + * ren[12]->pair->one->path are equal. + */ + o->path = ci->ren1->pair->one->path; + a->path = ci->ren1->pair->two->path; + b->path = ci->ren2->pair->two->path; + clean_merge = 0; - if (handle_rename_rename_1to2(o, conflict_info)) + if (handle_rename_rename_1to2(opt, ci)) clean_merge = -1; break; case RENAME_TWO_FILES_TO_ONE: + /* + * Manually fix up paths; note, + * ren[12]->pair->two->path are actually equal. + */ + o->path = NULL; + a->path = ci->ren1->pair->two->path; + b->path = ci->ren2->pair->two->path; + /* * Probably unclean merge, but if the two renamed * files merge cleanly and the two resulting files * can then be two-way merged cleanly, I guess it's * a clean merge? */ - clean_merge = handle_rename_rename_2to1(o, - conflict_info); + clean_merge = handle_rename_rename_2to1(opt, ci); break; default: entry->processed = 0; break; } - } else if (o_oid && (!a_oid || !b_oid)) { + if (path_clean < clean_merge) + clean_merge = path_clean; + } else if (o_valid && (!a_valid || !b_valid)) { /* Case A: Deleted in one */ - if ((!a_oid && !b_oid) || - (!b_oid && blob_unchanged(o, o_oid, o_mode, a_oid, a_mode, normalize, path)) || - (!a_oid && blob_unchanged(o, o_oid, o_mode, b_oid, b_mode, normalize, path))) { + if ((!a_valid && !b_valid) || + (!b_valid && blob_unchanged(opt, o, a, normalize, path)) || + (!a_valid && blob_unchanged(opt, o, b, normalize, path))) { /* Deleted in both or deleted in one and * unchanged in the other */ - if (a_oid) - output(o, 2, _("Removing %s"), path); + if (a_valid) + output(opt, 2, _("Removing %s"), path); /* do not touch working file if it did not exist */ - remove_file(o, 1, path, !a_oid); + remove_file(opt, 1, path, !a_valid); } else { /* Modify/delete; deleted side may have put a directory in the way */ clean_merge = 0; - if (handle_modify_delete(o, path, o_oid, o_mode, - a_oid, a_mode, b_oid, b_mode)) + if (handle_modify_delete(opt, path, o, a, b)) clean_merge = -1; } - } else if ((!o_oid && a_oid && !b_oid) || - (!o_oid && !a_oid && b_oid)) { + } else if ((!o_valid && a_valid && !b_valid) || + (!o_valid && !a_valid && b_valid)) { /* Case B: Added in one. */ /* [nothing|directory] -> ([nothing|directory], file) */ const char *add_branch; const char *other_branch; - unsigned mode; - const struct object_id *oid; const char *conf; + const struct diff_filespec *contents; - if (a_oid) { - add_branch = o->branch1; - other_branch = o->branch2; - mode = a_mode; - oid = a_oid; + if (a_valid) { + add_branch = opt->branch1; + other_branch = opt->branch2; + contents = a; conf = _("file/directory"); } else { - add_branch = o->branch2; - other_branch = o->branch1; - mode = b_mode; - oid = b_oid; + add_branch = opt->branch2; + other_branch = opt->branch1; + contents = b; conf = _("directory/file"); } - if (dir_in_way(o->repo->index, path, - !o->call_depth && !S_ISGITLINK(a_mode), + if (dir_in_way(opt->repo->index, path, + !opt->call_depth && !S_ISGITLINK(a->mode), 0)) { - char *new_path = unique_path(o, path, add_branch); + char *new_path = unique_path(opt, path, add_branch); clean_merge = 0; - output(o, 1, _("CONFLICT (%s): There is a directory with name %s in %s. " + output(opt, 1, _("CONFLICT (%s): There is a directory with name %s in %s. " "Adding %s as %s"), conf, path, other_branch, path, new_path); - if (update_file(o, 0, oid, mode, new_path)) + if (update_file(opt, 0, contents, new_path)) clean_merge = -1; - else if (o->call_depth) - remove_file_from_index(o->repo->index, path); + else if (opt->call_depth) + remove_file_from_index(opt->repo->index, path); free(new_path); } else { - output(o, 2, _("Adding %s"), path); + output(opt, 2, _("Adding %s"), path); /* do not overwrite file if already present */ - if (update_file_flags(o, oid, mode, path, 1, !a_oid)) + if (update_file_flags(opt, contents, path, 1, !a_valid)) clean_merge = -1; } - } else if (a_oid && b_oid) { - if (!o_oid) { + } else if (a_valid && b_valid) { + if (!o_valid) { /* Case C: Added in both (check for same permissions) */ - output(o, 1, + output(opt, 1, _("CONFLICT (add/add): Merge conflict in %s"), path); - clean_merge = handle_file_collision(o, + clean_merge = handle_file_collision(opt, path, NULL, NULL, - o->branch1, - o->branch2, - a_oid, a_mode, - b_oid, b_mode); + opt->branch1, + opt->branch2, + a, b); } else { /* case D: Modified in both, but differently. */ + struct merge_file_info mfi; int is_dirty = 0; /* unpack_trees would have bailed if dirty */ - clean_merge = handle_content_merge(o, path, + clean_merge = handle_content_merge(&mfi, opt, path, is_dirty, - o_oid, o_mode, - a_oid, a_mode, - b_oid, b_mode, - NULL); + o, a, b, NULL); } - } else if (!o_oid && !a_oid && !b_oid) { + } else if (!o_valid && !a_valid && !b_valid) { /* * this entry was deleted altogether. a_mode == 0 means * we had that path and want to actively remove it. */ - remove_file(o, 1, path, !a_mode); + remove_file(opt, 1, path, !a->mode); } else BUG("fatal merge failure, shouldn't happen."); return clean_merge; } -int merge_trees(struct merge_options *o, +int merge_trees(struct merge_options *opt, struct tree *head, struct tree *merge, struct tree *common, struct tree **result) { - struct index_state *istate = o->repo->index; + struct index_state *istate = opt->repo->index; int code, clean; struct strbuf sb = STRBUF_INIT; - if (!o->call_depth && repo_index_has_changes(o->repo, head, &sb)) { - err(o, _("Your local changes to the following files would be overwritten by merge:\n %s"), + if (!opt->call_depth && repo_index_has_changes(opt->repo, head, &sb)) { + err(opt, _("Your local changes to the following files would be overwritten by merge:\n %s"), sb.buf); return -1; } - if (o->subtree_shift) { - merge = shift_tree_object(o->repo, head, merge, o->subtree_shift); - common = shift_tree_object(o->repo, head, common, o->subtree_shift); + if (opt->subtree_shift) { + merge = shift_tree_object(opt->repo, head, merge, opt->subtree_shift); + common = shift_tree_object(opt->repo, head, common, opt->subtree_shift); } if (oid_eq(&common->object.oid, &merge->object.oid)) { - output(o, 0, _("Already up to date!")); + output(opt, 0, _("Already up to date!")); *result = head; return 1; } - code = unpack_trees_start(o, common, head, merge); + code = unpack_trees_start(opt, common, head, merge); if (code != 0) { - if (show(o, 4) || o->call_depth) - err(o, _("merging of trees %s and %s failed"), + if (show(opt, 4) || opt->call_depth) + err(opt, _("merging of trees %s and %s failed"), oid_to_hex(&head->object.oid), oid_to_hex(&merge->object.oid)); - unpack_trees_finish(o); + unpack_trees_finish(opt); return -1; } @@@ -3415,21 -3452,21 +3420,21 @@@ * opposed to decaring a local hashmap is for convenience * so that we don't have to pass it to around. */ - hashmap_init(&o->current_file_dir_set, path_hashmap_cmp, NULL, 512); - get_files_dirs(o, head); - get_files_dirs(o, merge); + hashmap_init(&opt->current_file_dir_set, path_hashmap_cmp, NULL, 512); + get_files_dirs(opt, head); + get_files_dirs(opt, merge); - entries = get_unmerged(o->repo->index); - clean = detect_and_process_renames(o, common, head, merge, + entries = get_unmerged(opt->repo->index); + clean = detect_and_process_renames(opt, common, head, merge, entries, &re_info); - record_df_conflict_files(o, entries); + record_df_conflict_files(opt, entries); if (clean < 0) goto cleanup; for (i = entries->nr-1; 0 <= i; i--) { const char *path = entries->items[i].string; struct stage_data *e = entries->items[i].util; if (!e->processed) { - int ret = process_entry(o, path, e); + int ret = process_entry(opt, path, e); if (!ret) clean = 0; else if (ret < 0) { @@@ -3451,19 -3488,19 +3456,19 @@@ string_list_clear(entries, 1); free(entries); - hashmap_free(&o->current_file_dir_set, 1); + hashmap_free(&opt->current_file_dir_set, 1); if (clean < 0) { - unpack_trees_finish(o); + unpack_trees_finish(opt); return clean; } } else clean = 1; - unpack_trees_finish(o); + unpack_trees_finish(opt); - if (o->call_depth && !(*result = write_tree_from_memory(o))) + if (opt->call_depth && !(*result = write_tree_from_memory(opt))) return -1; return clean; @@@ -3484,7 -3521,7 +3489,7 @@@ static struct commit_list *reverse_comm * Merge the commits h1 and h2, return the resulting virtual * commit object and a flag indicating the cleanness of the merge. */ -int merge_recursive(struct merge_options *o, +int merge_recursive(struct merge_options *opt, struct commit *h1, struct commit *h2, struct commit_list *ca, @@@ -3495,10 -3532,10 +3500,10 @@@ struct tree *mrtree; int clean; - if (show(o, 4)) { - output(o, 4, _("Merging:")); - output_commit_title(o, h1); - output_commit_title(o, h2); + if (show(opt, 4)) { + output(opt, 4, _("Merging:")); + output_commit_title(opt, h1); + output_commit_title(opt, h2); } if (!ca) { @@@ -3506,13 -3543,13 +3511,13 @@@ ca = reverse_commit_list(ca); } - if (show(o, 5)) { + if (show(opt, 5)) { unsigned cnt = commit_list_count(ca); - output(o, 5, Q_("found %u common ancestor:", + output(opt, 5, Q_("found %u common ancestor:", "found %u common ancestors:", cnt), cnt); for (iter = ca; iter; iter = iter->next) - output_commit_title(o, iter->item); + output_commit_title(opt, iter->item); } merged_common_ancestors = pop_commit(&ca); @@@ -3520,13 -3557,13 +3525,13 @@@ /* if there is no common ancestor, use an empty tree */ struct tree *tree; - tree = lookup_tree(o->repo, o->repo->hash_algo->empty_tree); - merged_common_ancestors = make_virtual_commit(o->repo, tree, "ancestor"); + tree = lookup_tree(opt->repo, opt->repo->hash_algo->empty_tree); + merged_common_ancestors = make_virtual_commit(opt->repo, tree, "ancestor"); } for (iter = ca; iter; iter = iter->next) { const char *saved_b1, *saved_b2; - o->call_depth++; + opt->call_depth++; /* * When the merge fails, the result contains files * with conflict markers. The cleanness flag is @@@ -3535,46 -3572,46 +3540,46 @@@ * overwritten it: the committed "conflicts" were * already resolved. */ - discard_index(o->repo->index); - saved_b1 = o->branch1; - saved_b2 = o->branch2; - o->branch1 = "Temporary merge branch 1"; - o->branch2 = "Temporary merge branch 2"; - if (merge_recursive(o, merged_common_ancestors, iter->item, + discard_index(opt->repo->index); + saved_b1 = opt->branch1; + saved_b2 = opt->branch2; + opt->branch1 = "Temporary merge branch 1"; + opt->branch2 = "Temporary merge branch 2"; + if (merge_recursive(opt, merged_common_ancestors, iter->item, NULL, &merged_common_ancestors) < 0) return -1; - o->branch1 = saved_b1; - o->branch2 = saved_b2; - o->call_depth--; + opt->branch1 = saved_b1; + opt->branch2 = saved_b2; + opt->call_depth--; if (!merged_common_ancestors) - return err(o, _("merge returned no commit")); + return err(opt, _("merge returned no commit")); } - discard_index(o->repo->index); - if (!o->call_depth) - repo_read_index(o->repo); + discard_index(opt->repo->index); + if (!opt->call_depth) + repo_read_index(opt->repo); - o->ancestor = "merged common ancestors"; - clean = merge_trees(o, get_commit_tree(h1), get_commit_tree(h2), + opt->ancestor = "merged common ancestors"; + clean = merge_trees(opt, get_commit_tree(h1), get_commit_tree(h2), get_commit_tree(merged_common_ancestors), &mrtree); if (clean < 0) { - flush_output(o); + flush_output(opt); return clean; } - if (o->call_depth) { - *result = make_virtual_commit(o->repo, mrtree, "merged tree"); + if (opt->call_depth) { + *result = make_virtual_commit(opt->repo, mrtree, "merged tree"); commit_list_insert(h1, &(*result)->parents); commit_list_insert(h2, &(*result)->parents->next); } - flush_output(o); - if (!o->call_depth && o->buffer_output < 2) - strbuf_release(&o->obuf); - if (show(o, 2)) + flush_output(opt); + if (!opt->call_depth && opt->buffer_output < 2) + strbuf_release(&opt->obuf); + if (show(opt, 2)) diff_warn_rename_limit("merge.renamelimit", - o->needed_rename_limit, 0); + opt->needed_rename_limit, 0); return clean; } @@@ -3596,7 -3633,7 +3601,7 @@@ static struct commit *get_ref(struct re return (struct commit *)object; } -int merge_recursive_generic(struct merge_options *o, +int merge_recursive_generic(struct merge_options *opt, const struct object_id *head, const struct object_id *merge, int num_base_list, @@@ -3605,136 -3642,127 +3610,136 @@@ { int clean; struct lock_file lock = LOCK_INIT; - struct commit *head_commit = get_ref(o->repo, head, o->branch1); - struct commit *next_commit = get_ref(o->repo, merge, o->branch2); + struct commit *head_commit = get_ref(opt->repo, head, opt->branch1); + struct commit *next_commit = get_ref(opt->repo, merge, opt->branch2); struct commit_list *ca = NULL; if (base_list) { int i; for (i = 0; i < num_base_list; ++i) { struct commit *base; - if (!(base = get_ref(o->repo, base_list[i], oid_to_hex(base_list[i])))) - return err(o, _("Could not parse object '%s'"), + if (!(base = get_ref(opt->repo, base_list[i], oid_to_hex(base_list[i])))) + return err(opt, _("Could not parse object '%s'"), oid_to_hex(base_list[i])); commit_list_insert(base, &ca); } } - repo_hold_locked_index(o->repo, &lock, LOCK_DIE_ON_ERROR); - clean = merge_recursive(o, head_commit, next_commit, ca, + repo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR); + clean = merge_recursive(opt, head_commit, next_commit, ca, result); if (clean < 0) { rollback_lock_file(&lock); return clean; } - if (write_locked_index(o->repo->index, &lock, + if (write_locked_index(opt->repo->index, &lock, COMMIT_LOCK | SKIP_IF_UNCHANGED)) - return err(o, _("Unable to write index.")); + return err(opt, _("Unable to write index.")); return clean ? 0 : 1; } -static void merge_recursive_config(struct merge_options *o) +static void merge_recursive_config(struct merge_options *opt) { char *value = NULL; - git_config_get_int("merge.verbosity", &o->verbosity); - git_config_get_int("diff.renamelimit", &o->diff_rename_limit); - git_config_get_int("merge.renamelimit", &o->merge_rename_limit); + git_config_get_int("merge.verbosity", &opt->verbosity); + git_config_get_int("diff.renamelimit", &opt->diff_rename_limit); + git_config_get_int("merge.renamelimit", &opt->merge_rename_limit); if (!git_config_get_string("diff.renames", &value)) { - o->diff_detect_rename = git_config_rename("diff.renames", value); + opt->diff_detect_rename = git_config_rename("diff.renames", value); free(value); } if (!git_config_get_string("merge.renames", &value)) { - o->merge_detect_rename = git_config_rename("merge.renames", value); + opt->merge_detect_rename = git_config_rename("merge.renames", value); + free(value); + } + if (!git_config_get_string("merge.directoryrenames", &value)) { + int boolval = git_parse_maybe_bool(value); + if (0 <= boolval) { + opt->detect_directory_renames = boolval ? 2 : 0; + } else if (!strcasecmp(value, "conflict")) { + opt->detect_directory_renames = 1; + } /* avoid erroring on values from future versions of git */ free(value); } git_config(git_xmerge_config, NULL); } -void init_merge_options(struct merge_options *o, +void init_merge_options(struct merge_options *opt, struct repository *repo) { const char *merge_verbosity; - memset(o, 0, sizeof(struct merge_options)); - o->repo = repo; - o->verbosity = 2; - o->buffer_output = 1; - o->diff_rename_limit = -1; - o->merge_rename_limit = -1; - o->renormalize = 0; - o->diff_detect_rename = -1; - o->merge_detect_rename = -1; - o->detect_directory_renames = 1; - merge_recursive_config(o); + memset(opt, 0, sizeof(struct merge_options)); + opt->repo = repo; + opt->verbosity = 2; + opt->buffer_output = 1; + opt->diff_rename_limit = -1; + opt->merge_rename_limit = -1; + opt->renormalize = 0; + opt->diff_detect_rename = -1; + opt->merge_detect_rename = -1; + opt->detect_directory_renames = 1; + merge_recursive_config(opt); merge_verbosity = getenv("GIT_MERGE_VERBOSITY"); if (merge_verbosity) - o->verbosity = strtol(merge_verbosity, NULL, 10); - if (o->verbosity >= 5) - o->buffer_output = 0; - strbuf_init(&o->obuf, 0); - string_list_init(&o->df_conflict_file_set, 1); + opt->verbosity = strtol(merge_verbosity, NULL, 10); + if (opt->verbosity >= 5) + opt->buffer_output = 0; + strbuf_init(&opt->obuf, 0); + string_list_init(&opt->df_conflict_file_set, 1); } -int parse_merge_opt(struct merge_options *o, const char *s) +int parse_merge_opt(struct merge_options *opt, const char *s) { const char *arg; if (!s || !*s) return -1; if (!strcmp(s, "ours")) - o->recursive_variant = MERGE_RECURSIVE_OURS; + opt->recursive_variant = MERGE_RECURSIVE_OURS; else if (!strcmp(s, "theirs")) - o->recursive_variant = MERGE_RECURSIVE_THEIRS; + opt->recursive_variant = MERGE_RECURSIVE_THEIRS; else if (!strcmp(s, "subtree")) - o->subtree_shift = ""; + opt->subtree_shift = ""; else if (skip_prefix(s, "subtree=", &arg)) - o->subtree_shift = arg; + opt->subtree_shift = arg; else if (!strcmp(s, "patience")) - o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF); + opt->xdl_opts = DIFF_WITH_ALG(opt, PATIENCE_DIFF); else if (!strcmp(s, "histogram")) - o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF); + opt->xdl_opts = DIFF_WITH_ALG(opt, HISTOGRAM_DIFF); else if (skip_prefix(s, "diff-algorithm=", &arg)) { long value = parse_algorithm_value(arg); if (value < 0) return -1; /* clear out previous settings */ - DIFF_XDL_CLR(o, NEED_MINIMAL); - o->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK; - o->xdl_opts |= value; + DIFF_XDL_CLR(opt, NEED_MINIMAL); + opt->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK; + opt->xdl_opts |= value; } else if (!strcmp(s, "ignore-space-change")) - DIFF_XDL_SET(o, IGNORE_WHITESPACE_CHANGE); + DIFF_XDL_SET(opt, IGNORE_WHITESPACE_CHANGE); else if (!strcmp(s, "ignore-all-space")) - DIFF_XDL_SET(o, IGNORE_WHITESPACE); + DIFF_XDL_SET(opt, IGNORE_WHITESPACE); else if (!strcmp(s, "ignore-space-at-eol")) - DIFF_XDL_SET(o, IGNORE_WHITESPACE_AT_EOL); + DIFF_XDL_SET(opt, IGNORE_WHITESPACE_AT_EOL); else if (!strcmp(s, "ignore-cr-at-eol")) - DIFF_XDL_SET(o, IGNORE_CR_AT_EOL); + DIFF_XDL_SET(opt, IGNORE_CR_AT_EOL); else if (!strcmp(s, "renormalize")) - o->renormalize = 1; + opt->renormalize = 1; else if (!strcmp(s, "no-renormalize")) - o->renormalize = 0; + opt->renormalize = 0; else if (!strcmp(s, "no-renames")) - o->merge_detect_rename = 0; + opt->merge_detect_rename = 0; else if (!strcmp(s, "find-renames")) { - o->merge_detect_rename = 1; - o->rename_score = 0; + opt->merge_detect_rename = 1; + opt->rename_score = 0; } else if (skip_prefix(s, "find-renames=", &arg) || skip_prefix(s, "rename-threshold=", &arg)) { - if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0) + if ((opt->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0) return -1; - o->merge_detect_rename = 1; + opt->merge_detect_rename = 1; } /* * Please update $__git_merge_strategy_options in diff --combined packfile.c index cdf6b6ec34,2ad4fab2f8..9f52af9281 --- a/packfile.c +++ b/packfile.c @@@ -235,7 -235,7 +235,7 @@@ struct packed_git *parse_pack_index(uns struct packed_git *p = alloc_packed_git(alloc); memcpy(p->pack_name, path, alloc); /* includes NUL */ - hashcpy(p->sha1, sha1); + hashcpy(p->hash, sha1); if (check_packed_git_idx(idx_path, p)) { free(p); return NULL; @@@ -309,7 -309,7 +309,7 @@@ void close_pack_windows(struct packed_g } } -static int close_pack_fd(struct packed_git *p) +int close_pack_fd(struct packed_git *p) { if (p->pack_fd < 0) return 0; @@@ -466,16 -466,6 +466,16 @@@ static unsigned int get_max_fd_limit(vo #endif } +const char *pack_basename(struct packed_git *p) +{ + const char *ret = strrchr(p->pack_name, '/'); + if (ret) + ret = ret + 1; /* skip past slash */ + else + ret = p->pack_name; /* we only have a base */ + return ret; +} + /* * Do not call this directly as this leaks p->pack_fd on error return; * call open_packed_git() instead. @@@ -492,7 -482,7 +492,7 @@@ static int open_packed_git_1(struct pac if (!p->index_data) { struct multi_pack_index *m; - const char *pack_name = strrchr(p->pack_name, '/'); + const char *pack_name = pack_basename(p); for (m = the_repository->objects->multi_pack_index; m; m = m->next) { @@@ -732,8 -722,8 +732,8 @@@ struct packed_git *add_packed_git(cons p->pack_local = local; p->mtime = st.st_mtime; if (path_len < the_hash_algo->hexsz || - get_sha1_hex(path + path_len - the_hash_algo->hexsz, p->sha1)) - hashclr(p->sha1); + get_sha1_hex(path + path_len - the_hash_algo->hexsz, p->hash)) + hashclr(p->hash); return p; } @@@ -903,25 -893,25 +903,25 @@@ static void prepare_packed_git(struct r * all unreachable objects about to be pruned, in which case they're not really * interesting as a measure of repo size in the first place. */ - unsigned long approximate_object_count(void) + unsigned long repo_approximate_object_count(struct repository *r) { - if (!the_repository->objects->approximate_object_count_valid) { + if (!r->objects->approximate_object_count_valid) { unsigned long count; struct multi_pack_index *m; struct packed_git *p; - prepare_packed_git(the_repository); + prepare_packed_git(r); count = 0; - for (m = get_multi_pack_index(the_repository); m; m = m->next) + for (m = get_multi_pack_index(r); m; m = m->next) count += m->num_objects; - for (p = the_repository->objects->packed_git; p; p = p->next) { + for (p = r->objects->packed_git; p; p = p->next) { if (open_pack_index(p)) continue; count += p->num_objects; } - the_repository->objects->approximate_object_count = count; + r->objects->approximate_object_count = count; } - return the_repository->objects->approximate_object_count; + return r->objects->approximate_object_count; } static void *get_next_packed_git(const void *p) @@@ -2033,10 -2023,8 +2033,10 @@@ int for_each_object_in_pack(struct pack uint32_t i; int r = 0; - if (flags & FOR_EACH_OBJECT_PACK_ORDER) - load_pack_revindex(p); + if (flags & FOR_EACH_OBJECT_PACK_ORDER) { + if (load_pack_revindex(p)) + return -1; + } for (i = 0; i < p->num_objects; i++) { uint32_t pos; diff --combined packfile.h index 12baa6118a,f04440f984..b678d35c0b --- a/packfile.h +++ b/packfile.h @@@ -15,29 -15,23 +15,29 @@@ struct object_info * * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx" */ -extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext); +char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext); /* * Return the name of the (local) packfile with the specified sha1 in * its name. The return value is a pointer to memory that is * overwritten each time this function is called. */ -extern char *sha1_pack_name(const unsigned char *sha1); +char *sha1_pack_name(const unsigned char *sha1); /* * Return the name of the (local) pack index file with the specified * sha1 in its name. The return value is a pointer to memory that is * overwritten each time this function is called. */ -extern char *sha1_pack_index_name(const unsigned char *sha1); +char *sha1_pack_index_name(const unsigned char *sha1); -extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path); +/* + * Return the basename of the packfile, omitting any containing directory + * (e.g., "pack-1234abcd[...].pack"). + */ +const char *pack_basename(struct packed_git *p); + +struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path); typedef void each_file_in_pack_dir_fn(const char *full_path, size_t full_path_len, const char *file_pach, void *data); @@@ -51,8 -45,8 +51,8 @@@ void for_each_file_in_pack_dir(const ch #define PACKDIR_FILE_GARBAGE 4 extern void (*report_garbage)(unsigned seen_bits, const char *path); -extern void reprepare_packed_git(struct repository *r); -extern void install_packed_git(struct repository *r, struct packed_git *pack); +void reprepare_packed_git(struct repository *r); +void install_packed_git(struct repository *r, struct packed_git *pack); struct packed_git *get_packed_git(struct repository *r); struct list_head *get_packed_git_mru(struct repository *r); @@@ -63,36 -57,35 +63,37 @@@ struct packed_git *get_all_packs(struc * Give a rough count of objects in the repository. This sacrifices accuracy * for speed. */ - unsigned long approximate_object_count(void); + unsigned long repo_approximate_object_count(struct repository *r); + #define approximate_object_count() repo_approximate_object_count(the_repository) -extern struct packed_git *find_sha1_pack(const unsigned char *sha1, - struct packed_git *packs); +struct packed_git *find_sha1_pack(const unsigned char *sha1, + struct packed_git *packs); -extern void pack_report(void); +void pack_report(void); /* * mmap the index file for the specified packfile (if it is not * already mmapped). Return 0 on success. */ -extern int open_pack_index(struct packed_git *); +int open_pack_index(struct packed_git *); /* * munmap the index file for the specified packfile (if it is * currently mmapped). */ -extern void close_pack_index(struct packed_git *); +void close_pack_index(struct packed_git *); + +int close_pack_fd(struct packed_git *p); -extern uint32_t get_pack_fanout(struct packed_git *p, uint32_t value); +uint32_t get_pack_fanout(struct packed_git *p, uint32_t value); -extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *); -extern void close_pack_windows(struct packed_git *); -extern void close_pack(struct packed_git *); -extern void close_all_packs(struct raw_object_store *o); -extern void unuse_pack(struct pack_window **); -extern void clear_delta_base_cache(void); -extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local); +unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *); +void close_pack_windows(struct packed_git *); +void close_pack(struct packed_git *); +void close_all_packs(struct raw_object_store *o); +void unuse_pack(struct pack_window **); +void clear_delta_base_cache(void); +struct packed_git *add_packed_git(const char *path, size_t path_len, int local); /* * Make sure that a pointer access into an mmap'd index file is within bounds, @@@ -102,7 -95,7 +103,7 @@@ * (like the 64-bit extended offset table), as we compare the size to the * fixed-length parts when we open the file. */ -extern void check_pack_index_ptr(const struct packed_git *p, const void *ptr); +void check_pack_index_ptr(const struct packed_git *p, const void *ptr); /* * Perform binary search on a pack-index for a given oid. Packfile is expected to @@@ -118,59 -111,59 +119,59 @@@ int bsearch_pack(const struct object_i * at the SHA-1 within the mmapped index. Return NULL if there is an * error. */ -extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n); +const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n); /* * Like nth_packed_object_sha1, but write the data into the object specified by * the the first argument. Returns the first argument on success, and NULL on * error. */ -extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n); +const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n); /* * Return the offset of the nth object within the specified packfile. * The index must already be opened. */ -extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t n); +off_t nth_packed_object_offset(const struct packed_git *, uint32_t n); /* * If the object named sha1 is present in the specified packfile, * return its offset within the packfile; otherwise, return 0. */ -extern off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *); +off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *); -extern int is_pack_valid(struct packed_git *); -extern void *unpack_entry(struct repository *r, struct packed_git *, off_t, enum object_type *, unsigned long *); -extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep); -extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t); -extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *); +int is_pack_valid(struct packed_git *); +void *unpack_entry(struct repository *r, struct packed_git *, off_t, enum object_type *, unsigned long *); +unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep); +unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t); +int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *); -extern void release_pack_memory(size_t); +void release_pack_memory(size_t); /* global flag to enable extra checks when accessing packed objects */ extern int do_check_packed_object_crc; -extern int packed_object_info(struct repository *r, - struct packed_git *pack, - off_t offset, struct object_info *); +int packed_object_info(struct repository *r, + struct packed_git *pack, + off_t offset, struct object_info *); -extern void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1); -extern const struct packed_git *has_packed_and_bad(struct repository *r, const unsigned char *sha1); +void mark_bad_packed_object(struct packed_git *p, const unsigned char *sha1); +const struct packed_git *has_packed_and_bad(struct repository *r, const unsigned char *sha1); /* * Iff a pack file in the given repository contains the object named by sha1, * return true and store its location to e. */ -extern int find_pack_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e); +int find_pack_entry(struct repository *r, const struct object_id *oid, struct pack_entry *e); -extern int has_object_pack(const struct object_id *oid); +int has_object_pack(const struct object_id *oid); -extern int has_pack_index(const unsigned char *sha1); +int has_pack_index(const unsigned char *sha1); /* * Return 1 if an object in a promisor packfile is or refers to the given * object, 0 otherwise. */ -extern int is_promisor_object(const struct object_id *oid); +int is_promisor_object(const struct object_id *oid); /* * Expose a function for fuzz testing. @@@ -182,7 -175,7 +183,7 @@@ * have a convenient entry-point for fuzz testing. For real uses, you should * probably use open_pack_index() or parse_pack_index() instead. */ -extern int load_idx(const char *path, const unsigned int hashsz, void *idx_map, - size_t idx_size, struct packed_git *p); +int load_idx(const char *path, const unsigned int hashsz, void *idx_map, + size_t idx_size, struct packed_git *p); #endif diff --combined sha1-name.c index 7754d3a3de,cf314ebb29..775a73d8ad --- a/sha1-name.c +++ b/sha1-name.c @@@ -12,18 -12,20 +12,20 @@@ #include "packfile.h" #include "object-store.h" #include "repository.h" + #include "submodule.h" #include "midx.h" #include "commit-reach.h" - static int get_oid_oneline(const char *, struct object_id *, struct commit_list *); + static int get_oid_oneline(struct repository *r, const char *, struct object_id *, struct commit_list *); - typedef int (*disambiguate_hint_fn)(const struct object_id *, void *); + typedef int (*disambiguate_hint_fn)(struct repository *, const struct object_id *, void *); struct disambiguate_state { int len; /* length of prefix in hex chars */ char hex_pfx[GIT_MAX_HEXSZ + 1]; struct object_id bin_pfx; + struct repository *repo; disambiguate_hint_fn fn; void *cb_data; struct object_id candidate; @@@ -38,7 -40,7 +40,7 @@@ static void update_candidates(struct disambiguate_state *ds, const struct object_id *current) { if (ds->always_call_fn) { - ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0; + ds->ambiguous = ds->fn(ds->repo, current, ds->cb_data) ? 1 : 0; return; } if (!ds->candidate_exists) { @@@ -58,7 -60,7 +60,7 @@@ } if (!ds->candidate_checked) { - ds->candidate_ok = ds->fn(&ds->candidate, ds->cb_data); + ds->candidate_ok = ds->fn(ds->repo, &ds->candidate, ds->cb_data); ds->disambiguate_fn_used = 1; ds->candidate_checked = 1; } @@@ -71,7 -73,7 +73,7 @@@ } /* if we reach this point, we know ds->candidate satisfies fn */ - if (ds->fn(current, ds->cb_data)) { + if (ds->fn(ds->repo, current, ds->cb_data)) { /* * if both current and candidate satisfy fn, we cannot * disambiguate. @@@ -89,9 -91,7 +91,7 @@@ static void find_short_object_filename( { struct object_directory *odb; - for (odb = the_repository->objects->odb; - odb && !ds->ambiguous; - odb = odb->next) { + for (odb = ds->repo->objects->odb; odb && !ds->ambiguous; odb = odb->next) { int pos; struct oid_array *loose_objects; @@@ -182,10 -182,10 +182,10 @@@ static void find_short_packed_object(st struct multi_pack_index *m; struct packed_git *p; - for (m = get_multi_pack_index(the_repository); m && !ds->ambiguous; + for (m = get_multi_pack_index(ds->repo); m && !ds->ambiguous; m = m->next) unique_in_midx(m, ds); - for (p = get_packed_git(the_repository); p && !ds->ambiguous; + for (p = get_packed_git(ds->repo); p && !ds->ambiguous; p = p->next) unique_in_pack(p, ds); } @@@ -215,7 -215,7 +215,7 @@@ static int finish_object_disambiguation * same repository! */ ds->candidate_ok = (!ds->disambiguate_fn_used || - ds->fn(&ds->candidate, ds->cb_data)); + ds->fn(ds->repo, &ds->candidate, ds->cb_data)); if (!ds->candidate_ok) return SHORT_NAME_AMBIGUOUS; @@@ -224,59 -224,67 +224,67 @@@ return 0; } - static int disambiguate_commit_only(const struct object_id *oid, void *cb_data_unused) + static int disambiguate_commit_only(struct repository *r, + const struct object_id *oid, + void *cb_data_unused) { - int kind = oid_object_info(the_repository, oid, NULL); + int kind = oid_object_info(r, oid, NULL); return kind == OBJ_COMMIT; } - static int disambiguate_committish_only(const struct object_id *oid, void *cb_data_unused) + static int disambiguate_committish_only(struct repository *r, + const struct object_id *oid, + void *cb_data_unused) { struct object *obj; int kind; - kind = oid_object_info(the_repository, oid, NULL); + kind = oid_object_info(r, oid, NULL); if (kind == OBJ_COMMIT) return 1; if (kind != OBJ_TAG) return 0; /* We need to do this the hard way... */ - obj = deref_tag(the_repository, parse_object(the_repository, oid), - NULL, 0); + obj = deref_tag(r, parse_object(r, oid), NULL, 0); if (obj && obj->type == OBJ_COMMIT) return 1; return 0; } - static int disambiguate_tree_only(const struct object_id *oid, void *cb_data_unused) + static int disambiguate_tree_only(struct repository *r, + const struct object_id *oid, + void *cb_data_unused) { - int kind = oid_object_info(the_repository, oid, NULL); + int kind = oid_object_info(r, oid, NULL); return kind == OBJ_TREE; } - static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_unused) + static int disambiguate_treeish_only(struct repository *r, + const struct object_id *oid, + void *cb_data_unused) { struct object *obj; int kind; - kind = oid_object_info(the_repository, oid, NULL); + kind = oid_object_info(r, oid, NULL); if (kind == OBJ_TREE || kind == OBJ_COMMIT) return 1; if (kind != OBJ_TAG) return 0; /* We need to do this the hard way... */ - obj = deref_tag(the_repository, parse_object(the_repository, oid), - NULL, 0); + obj = deref_tag(r, parse_object(r, oid), NULL, 0); if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT)) return 1; return 0; } - static int disambiguate_blob_only(const struct object_id *oid, void *cb_data_unused) + static int disambiguate_blob_only(struct repository *r, + const struct object_id *oid, + void *cb_data_unused) { - int kind = oid_object_info(the_repository, oid, NULL); + int kind = oid_object_info(r, oid, NULL); return kind == OBJ_BLOB; } @@@ -310,7 -318,8 +318,8 @@@ int set_disambiguate_hint_config(const return error("unknown hint type for '%s': %s", var, value); } - static int init_object_disambiguation(const char *name, int len, + static int init_object_disambiguation(struct repository *r, + const char *name, int len, struct disambiguate_state *ds) { int i; @@@ -341,7 -350,8 +350,8 @@@ ds->len = len; ds->hex_pfx[len] = '\0'; - prepare_alt_odb(the_repository); + ds->repo = r; + prepare_alt_odb(r); return 0; } @@@ -351,25 -361,25 +361,25 @@@ static int show_ambiguous_object(const struct strbuf desc = STRBUF_INIT; int type; - if (ds->fn && !ds->fn(oid, ds->cb_data)) + if (ds->fn && !ds->fn(ds->repo, oid, ds->cb_data)) return 0; - type = oid_object_info(the_repository, oid, NULL); + type = oid_object_info(ds->repo, oid, NULL); if (type == OBJ_COMMIT) { - struct commit *commit = lookup_commit(the_repository, oid); + struct commit *commit = lookup_commit(ds->repo, oid); if (commit) { struct pretty_print_context pp = {0}; pp.date_mode.type = DATE_SHORT; format_commit_message(commit, " %ad - %s", &desc, &pp); } } else if (type == OBJ_TAG) { - struct tag *tag = lookup_tag(the_repository, oid); + struct tag *tag = lookup_tag(ds->repo, oid); if (!parse_tag(tag) && tag->tag) strbuf_addf(&desc, " %s", tag->tag); } advise(" %s %s%s", - find_unique_abbrev(oid, DEFAULT_ABBREV), + repo_find_unique_abbrev(ds->repo, oid, DEFAULT_ABBREV), type_name(type) ? type_name(type) : "unknown type", desc.buf); @@@ -383,10 -393,18 +393,18 @@@ static int collect_ambiguous(const stru return 0; } + static int repo_collect_ambiguous(struct repository *r, + const struct object_id *oid, + void *data) + { + return collect_ambiguous(oid, data); + } + + static struct repository *sort_ambiguous_repo; static int sort_ambiguous(const void *a, const void *b) { - int a_type = oid_object_info(the_repository, a, NULL); - int b_type = oid_object_info(the_repository, b, NULL); + int a_type = oid_object_info(sort_ambiguous_repo, a, NULL); + int b_type = oid_object_info(sort_ambiguous_repo, b, NULL); int a_type_sort; int b_type_sort; @@@ -411,7 -429,16 +429,16 @@@ return a_type_sort > b_type_sort ? 1 : -1; } - static enum get_oid_result get_short_oid(const char *name, int len, + static void sort_ambiguous_oid_array(struct repository *r, struct oid_array *a) + { + /* mutex will be needed if this code is to be made thread safe */ + sort_ambiguous_repo = r; + QSORT(a->oid, a->nr, sort_ambiguous); + sort_ambiguous_repo = NULL; + } + + static enum get_oid_result get_short_oid(struct repository *r, + const char *name, int len, struct object_id *oid, unsigned flags) { @@@ -419,7 -446,7 +446,7 @@@ struct disambiguate_state ds; int quietly = !!(flags & GET_OID_QUIETLY); - if (init_object_disambiguation(name, len, &ds) < 0) + if (init_object_disambiguation(r, name, len, &ds) < 0) return -1; if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS)) @@@ -442,18 -469,6 +469,18 @@@ find_short_packed_object(&ds); status = finish_object_disambiguation(&ds, oid); + /* + * If we didn't find it, do the usual reprepare() slow-path, + * since the object may have recently been added to the repository + * or migrated from loose to packed. + */ + if (status == MISSING_OBJECT) { + reprepare_packed_git(the_repository); + find_short_object_filename(&ds); + find_short_packed_object(&ds); + status = finish_object_disambiguation(&ds, oid); + } + if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) { struct oid_array collect = OID_ARRAY_INIT; @@@ -469,8 -484,8 +496,8 @@@ ds.fn = NULL; advise(_("The candidates are:")); - for_each_abbrev(ds.hex_pfx, collect_ambiguous, &collect); - QSORT(collect.oid, collect.nr, sort_ambiguous); + repo_for_each_abbrev(r, ds.hex_pfx, collect_ambiguous, &collect); + sort_ambiguous_oid_array(r, &collect); if (oid_array_for_each(&collect, show_ambiguous_object, &ds)) BUG("show_ambiguous_object shouldn't return non-zero"); @@@ -480,17 -495,18 +507,18 @@@ return status; } - int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data) + int repo_for_each_abbrev(struct repository *r, const char *prefix, + each_abbrev_fn fn, void *cb_data) { struct oid_array collect = OID_ARRAY_INIT; struct disambiguate_state ds; int ret; - if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0) + if (init_object_disambiguation(r, prefix, strlen(prefix), &ds) < 0) return -1; ds.always_call_fn = 1; - ds.fn = collect_ambiguous; + ds.fn = repo_collect_ambiguous; ds.cb_data = &collect; find_short_object_filename(&ds); find_short_packed_object(&ds); @@@ -517,6 -533,7 +545,7 @@@ struct min_abbrev_data unsigned int init_len; unsigned int cur_len; char *hex; + struct repository *repo; const struct object_id *oid; }; @@@ -545,6 -562,13 +574,13 @@@ static int extend_abbrev_len(const stru return 0; } + static int repo_extend_abbrev_len(struct repository *r, + const struct object_id *oid, + void *cb_data) + { + return extend_abbrev_len(oid, cb_data); + } + static void find_abbrev_len_for_midx(struct multi_pack_index *m, struct min_abbrev_data *mad) { @@@ -622,21 -646,22 +658,22 @@@ static void find_abbrev_len_packed(stru struct multi_pack_index *m; struct packed_git *p; - for (m = get_multi_pack_index(the_repository); m; m = m->next) + for (m = get_multi_pack_index(mad->repo); m; m = m->next) find_abbrev_len_for_midx(m, mad); - for (p = get_packed_git(the_repository); p; p = p->next) + for (p = get_packed_git(mad->repo); p; p = p->next) find_abbrev_len_for_pack(p, mad); } - int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len) + int repo_find_unique_abbrev_r(struct repository *r, char *hex, + const struct object_id *oid, int len) { struct disambiguate_state ds; struct min_abbrev_data mad; struct object_id oid_ret; - const unsigned hexsz = the_hash_algo->hexsz; + const unsigned hexsz = r->hash_algo->hexsz; if (len < 0) { - unsigned long count = approximate_object_count(); + unsigned long count = repo_approximate_object_count(r); /* * Add one because the MSB only tells us the highest bit set, * not including the value of all the _other_ bits (so "15" @@@ -661,6 -686,7 +698,7 @@@ if (len == hexsz || !len) return hexsz; + mad.repo = r; mad.init_len = len; mad.cur_len = len; mad.hex = hex; @@@ -668,10 -694,10 +706,10 @@@ find_abbrev_len_packed(&mad); - if (init_object_disambiguation(hex, mad.cur_len, &ds) < 0) + if (init_object_disambiguation(r, hex, mad.cur_len, &ds) < 0) return -1; - ds.fn = extend_abbrev_len; + ds.fn = repo_extend_abbrev_len; ds.always_call_fn = 1; ds.cb_data = (void *)&mad; @@@ -682,13 -708,15 +720,15 @@@ return mad.cur_len; } - const char *find_unique_abbrev(const struct object_id *oid, int len) + const char *repo_find_unique_abbrev(struct repository *r, + const struct object_id *oid, + int len) { static int bufno; static char hexbuffer[4][GIT_MAX_HEXSZ + 1]; char *hex = hexbuffer[bufno]; bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer); - find_unique_abbrev_r(hex, oid, len); + repo_find_unique_abbrev_r(r, hex, oid, len); return hex; } @@@ -743,11 -771,11 +783,11 @@@ static inline int push_mark(const char return at_mark(string, len, suffix, ARRAY_SIZE(suffix)); } - static enum get_oid_result get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags); - static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf); + static enum get_oid_result get_oid_1(struct repository *r, const char *name, int len, struct object_id *oid, unsigned lookup_flags); + static int interpret_nth_prior_checkout(struct repository *r, const char *name, int namelen, struct strbuf *buf); - static int get_oid_basic(const char *str, int len, struct object_id *oid, - unsigned int flags) + static int get_oid_basic(struct repository *r, const char *str, int len, + struct object_id *oid, unsigned int flags) { static const char *warn_msg = "refname '%.*s' is ambiguous."; static const char *object_name_msg = N_( @@@ -765,9 -793,9 +805,9 @@@ int refs_found = 0; int at, reflog_len, nth_prior = 0; - if (len == the_hash_algo->hexsz && !get_oid_hex(str, oid)) { + if (len == r->hash_algo->hexsz && !get_oid_hex(str, oid)) { if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) { - refs_found = dwim_ref(str, len, &tmp_oid, &real_ref); + refs_found = repo_dwim_ref(r, str, len, &tmp_oid, &real_ref); if (refs_found > 0) { warning(warn_msg, len, str); if (advice_object_name_warning) @@@ -808,8 -836,8 +848,8 @@@ struct strbuf buf = STRBUF_INIT; int detached; - if (interpret_nth_prior_checkout(str, len, &buf) > 0) { - detached = (buf.len == the_hash_algo->hexsz && !get_oid_hex(buf.buf, oid)); + if (interpret_nth_prior_checkout(r, str, len, &buf) > 0) { + detached = (buf.len == r->hash_algo->hexsz && !get_oid_hex(buf.buf, oid)); strbuf_release(&buf); if (detached) return 0; @@@ -818,18 -846,18 +858,18 @@@ if (!len && reflog_len) /* allow "@{...}" to mean the current branch reflog */ - refs_found = dwim_ref("HEAD", 4, oid, &real_ref); + refs_found = repo_dwim_ref(r, "HEAD", 4, oid, &real_ref); else if (reflog_len) - refs_found = dwim_log(str, len, oid, &real_ref); + refs_found = repo_dwim_log(r, str, len, oid, &real_ref); else - refs_found = dwim_ref(str, len, oid, &real_ref); + refs_found = repo_dwim_ref(r, str, len, oid, &real_ref); if (!refs_found) return -1; if (warn_ambiguous_refs && !(flags & GET_OID_QUIETLY) && (refs_found > 1 || - !get_short_oid(str, len, &tmp_oid, GET_OID_QUIETLY))) + !get_short_oid(r, str, len, &tmp_oid, GET_OID_QUIETLY))) warning(warn_msg, len, str); if (reflog_len) { @@@ -861,7 -889,8 +901,8 @@@ return -1; } } - if (read_ref_at(real_ref, flags, at_time, nth, oid, NULL, + if (read_ref_at(get_main_ref_store(r), + real_ref, flags, at_time, nth, oid, NULL, &co_time, &co_tz, &co_cnt)) { if (!len) { if (starts_with(real_ref, "refs/heads/")) { @@@ -893,18 -922,19 +934,19 @@@ return 0; } - static enum get_oid_result get_parent(const char *name, int len, + static enum get_oid_result get_parent(struct repository *r, + const char *name, int len, struct object_id *result, int idx) { struct object_id oid; - enum get_oid_result ret = get_oid_1(name, len, &oid, + enum get_oid_result ret = get_oid_1(r, name, len, &oid, GET_OID_COMMITTISH); struct commit *commit; struct commit_list *p; if (ret) return ret; - commit = lookup_commit_reference(the_repository, &oid); + commit = lookup_commit_reference(r, &oid); if (parse_commit(commit)) return MISSING_OBJECT; if (!idx) { @@@ -922,7 -952,8 +964,8 @@@ return MISSING_OBJECT; } - static enum get_oid_result get_nth_ancestor(const char *name, int len, + static enum get_oid_result get_nth_ancestor(struct repository *r, + const char *name, int len, struct object_id *result, int generation) { @@@ -930,10 -961,10 +973,10 @@@ struct commit *commit; int ret; - ret = get_oid_1(name, len, &oid, GET_OID_COMMITTISH); + ret = get_oid_1(r, name, len, &oid, GET_OID_COMMITTISH); if (ret) return ret; - commit = lookup_commit_reference(the_repository, &oid); + commit = lookup_commit_reference(r, &oid); if (!commit) return MISSING_OBJECT; @@@ -946,20 -977,20 +989,20 @@@ return FOUND; } - struct object *peel_to_type(const char *name, int namelen, - struct object *o, enum object_type expected_type) + struct object *repo_peel_to_type(struct repository *r, const char *name, int namelen, + struct object *o, enum object_type expected_type) { if (name && !namelen) namelen = strlen(name); while (1) { - if (!o || (!o->parsed && !parse_object(the_repository, &o->oid))) + if (!o || (!o->parsed && !parse_object(r, &o->oid))) return NULL; if (expected_type == OBJ_ANY || o->type == expected_type) return o; if (o->type == OBJ_TAG) o = ((struct tag*) o)->tagged; else if (o->type == OBJ_COMMIT) - o = &(get_commit_tree(((struct commit *)o))->object); + o = &(repo_get_commit_tree(r, ((struct commit *)o))->object); else { if (name) error("%.*s: expected %s type, but the object " @@@ -971,8 -1002,8 +1014,8 @@@ } } - static int peel_onion(const char *name, int len, struct object_id *oid, - unsigned lookup_flags) + static int peel_onion(struct repository *r, const char *name, int len, + struct object_id *oid, unsigned lookup_flags) { struct object_id outer; const char *sp; @@@ -1022,15 -1053,15 +1065,15 @@@ else if (expected_type == OBJ_TREE) lookup_flags |= GET_OID_TREEISH; - if (get_oid_1(name, sp - name - 2, &outer, lookup_flags)) + if (get_oid_1(r, name, sp - name - 2, &outer, lookup_flags)) return -1; - o = parse_object(the_repository, &outer); + o = parse_object(r, &outer); if (!o) return -1; if (!expected_type) { - o = deref_tag(the_repository, o, name, sp - name - 2); - if (!o || (!o->parsed && !parse_object(the_repository, &o->oid))) + o = deref_tag(r, o, name, sp - name - 2); + if (!o || (!o->parsed && !parse_object(r, &o->oid))) return -1; oidcpy(oid, &o->oid); return 0; @@@ -1041,7 -1072,7 +1084,7 @@@ * if we do not get the needed object, we should * barf. */ - o = peel_to_type(name, len, o, expected_type); + o = repo_peel_to_type(r, name, len, o, expected_type); if (!o) return -1; @@@ -1061,14 -1092,16 +1104,16 @@@ prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1)); commit_list_insert((struct commit *)o, &list); - ret = get_oid_oneline(prefix, oid, list); + ret = get_oid_oneline(r, prefix, oid, list); free(prefix); return ret; } return 0; } - static int get_describe_name(const char *name, int len, struct object_id *oid) + static int get_describe_name(struct repository *r, + const char *name, int len, + struct object_id *oid) { const char *cp; unsigned flags = GET_OID_QUIETLY | GET_OID_COMMIT; @@@ -1082,14 -1115,16 +1127,16 @@@ if (ch == 'g' && cp[-1] == '-') { cp++; len -= cp - name; - return get_short_oid(cp, len, oid, flags); + return get_short_oid(r, + cp, len, oid, flags); } } } return -1; } - static enum get_oid_result get_oid_1(const char *name, int len, + static enum get_oid_result get_oid_1(struct repository *r, + const char *name, int len, struct object_id *oid, unsigned lookup_flags) { @@@ -1118,25 -1153,25 +1165,25 @@@ if (!num && len1 == len - 1) num = 1; if (has_suffix == '^') - return get_parent(name, len1, oid, num); + return get_parent(r, name, len1, oid, num); /* else if (has_suffix == '~') -- goes without saying */ - return get_nth_ancestor(name, len1, oid, num); + return get_nth_ancestor(r, name, len1, oid, num); } - ret = peel_onion(name, len, oid, lookup_flags); + ret = peel_onion(r, name, len, oid, lookup_flags); if (!ret) return FOUND; - ret = get_oid_basic(name, len, oid, lookup_flags); + ret = get_oid_basic(r, name, len, oid, lookup_flags); if (!ret) return FOUND; /* It could be describe output that is "SOMETHING-gXXXX" */ - ret = get_describe_name(name, len, oid); + ret = get_describe_name(r, name, len, oid); if (!ret) return FOUND; - return get_short_oid(name, len, oid, lookup_flags); + return get_short_oid(r, name, len, oid, lookup_flags); } /* @@@ -1155,15 -1190,21 +1202,21 @@@ /* Remember to update object flag allocation in object.h */ #define ONELINE_SEEN (1u<<20) + struct handle_one_ref_cb { + struct repository *repo; + struct commit_list **list; + }; + static int handle_one_ref(const char *path, const struct object_id *oid, int flag, void *cb_data) { - struct commit_list **list = cb_data; - struct object *object = parse_object(the_repository, oid); + struct handle_one_ref_cb *cb = cb_data; + struct commit_list **list = cb->list; + struct object *object = parse_object(cb->repo, oid); if (!object) return 0; if (object->type == OBJ_TAG) { - object = deref_tag(the_repository, object, path, + object = deref_tag(cb->repo, object, path, strlen(path)); if (!object) return 0; @@@ -1174,8 -1215,9 +1227,9 @@@ return 0; } - static int get_oid_oneline(const char *prefix, struct object_id *oid, - struct commit_list *list) + static int get_oid_oneline(struct repository *r, + const char *prefix, struct object_id *oid, + struct commit_list *list) { struct commit_list *backup = NULL, *l; int found = 0; @@@ -1206,7 -1248,7 +1260,7 @@@ int matches; commit = pop_most_recent_commit(&list, ONELINE_SEEN); - if (!parse_object(the_repository, &commit->object.oid)) + if (!parse_object(r, &commit->object.oid)) continue; buf = get_commit_buffer(commit, NULL); p = strstr(buf, "\n\n"); @@@ -1258,7 -1300,8 +1312,8 @@@ static int grab_nth_branch_switch(struc * Parse @{-N} syntax, return the number of characters parsed * if successful; otherwise signal an error with negative value. */ - static int interpret_nth_prior_checkout(const char *name, int namelen, + static int interpret_nth_prior_checkout(struct repository *r, + const char *name, int namelen, struct strbuf *buf) { long nth; @@@ -1282,18 -1325,22 +1337,22 @@@ cb.remaining = nth; strbuf_init(&cb.buf, 20); - retval = 0; - if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) { + retval = refs_for_each_reflog_ent_reverse(get_main_ref_store(r), + "HEAD", grab_nth_branch_switch, &cb); + if (0 < retval) { strbuf_reset(buf); strbuf_addbuf(buf, &cb.buf); retval = brace - name + 1; - } + } else + retval = 0; strbuf_release(&cb.buf); return retval; } - int get_oid_mb(const char *name, struct object_id *oid) + int repo_get_oid_mb(struct repository *r, + const char *name, + struct object_id *oid) { struct commit *one, *two; struct commit_list *mbs; @@@ -1303,27 -1350,29 +1362,29 @@@ dots = strstr(name, "..."); if (!dots) - return get_oid(name, oid); + return repo_get_oid(r, name, oid); if (dots == name) - st = get_oid("HEAD", &oid_tmp); + st = repo_get_oid(r, "HEAD", &oid_tmp); else { struct strbuf sb; strbuf_init(&sb, dots - name); strbuf_add(&sb, name, dots - name); - st = get_oid_committish(sb.buf, &oid_tmp); + st = repo_get_oid_committish(r, sb.buf, &oid_tmp); strbuf_release(&sb); } if (st) return st; - one = lookup_commit_reference_gently(the_repository, &oid_tmp, 0); + one = lookup_commit_reference_gently(r, &oid_tmp, 0); if (!one) return -1; - if (get_oid_committish(dots[3] ? (dots + 3) : "HEAD", &oid_tmp)) + if (repo_get_oid_committish(r, dots[3] ? (dots + 3) : "HEAD", &oid_tmp)) return -1; - two = lookup_commit_reference_gently(the_repository, &oid_tmp, 0); + two = lookup_commit_reference_gently(r, &oid_tmp, 0); if (!two) return -1; + if (r != the_repository) + BUG("sorry get_merge_bases() can't take struct repository yet"); mbs = get_merge_bases(one, two); if (!mbs || mbs->next) st = -1; @@@ -1357,7 -1406,8 +1418,8 @@@ static int interpret_empty_at(const cha return 1; } - static int reinterpret(const char *name, int namelen, int len, + static int reinterpret(struct repository *r, + const char *name, int namelen, int len, struct strbuf *buf, unsigned allowed) { /* we have extra data, which might need further processing */ @@@ -1366,7 -1416,7 +1428,7 @@@ int ret; strbuf_add(buf, name + len, namelen - len); - ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed); + ret = repo_interpret_branch_name(r, buf->buf, buf->len, &tmp, allowed); /* that data was not interpreted, remove our cruft */ if (ret < 0) { strbuf_setlen(buf, used); @@@ -1379,9 -1429,9 +1441,9 @@@ return ret - used + len; } - static void set_shortened_ref(struct strbuf *buf, const char *ref) + static void set_shortened_ref(struct repository *r, struct strbuf *buf, const char *ref) { - char *s = shorten_unambiguous_ref(ref, 0); + char *s = refs_shorten_unambiguous_ref(get_main_ref_store(r), ref, 0); strbuf_reset(buf); strbuf_addstr(buf, s); free(s); @@@ -1402,7 -1452,8 +1464,8 @@@ static int branch_interpret_allowed(con return 0; } - static int interpret_branch_mark(const char *name, int namelen, + static int interpret_branch_mark(struct repository *r, + const char *name, int namelen, int at, struct strbuf *buf, int (*get_mark)(const char *, int), const char *(*get_data)(struct branch *, @@@ -1435,12 -1486,14 +1498,14 @@@ if (!branch_interpret_allowed(value, allowed)) return -1; - set_shortened_ref(buf, value); + set_shortened_ref(r, buf, value); return len + at; } - int interpret_branch_name(const char *name, int namelen, struct strbuf *buf, - unsigned allowed) + int repo_interpret_branch_name(struct repository *r, + const char *name, int namelen, + struct strbuf *buf, + unsigned allowed) { char *at; const char *start; @@@ -1450,14 -1503,14 +1515,14 @@@ namelen = strlen(name); if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) { - len = interpret_nth_prior_checkout(name, namelen, buf); + len = interpret_nth_prior_checkout(r, name, namelen, buf); if (!len) { return len; /* syntax Ok, not enough switches */ } else if (len > 0) { if (len == namelen) return len; /* consumed all */ else - return reinterpret(name, namelen, len, buf, allowed); + return reinterpret(r, name, namelen, len, buf, allowed); } } @@@ -1468,17 -1521,17 +1533,17 @@@ if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) { len = interpret_empty_at(name, namelen, at - name, buf); if (len > 0) - return reinterpret(name, namelen, len, buf, + return reinterpret(r, name, namelen, len, buf, allowed); } - len = interpret_branch_mark(name, namelen, at - name, buf, + len = interpret_branch_mark(r, name, namelen, at - name, buf, upstream_mark, branch_get_upstream, allowed); if (len > 0) return len; - len = interpret_branch_mark(name, namelen, at - name, buf, + len = interpret_branch_mark(r, name, namelen, at - name, buf, push_mark, branch_get_push, allowed); if (len > 0) @@@ -1524,31 -1577,12 +1589,31 @@@ int strbuf_check_branch_ref(struct strb * This is like "get_oid_basic()", except it allows "object ID expressions", * notably "xyz^" for "parent of xyz" */ - int get_oid(const char *name, struct object_id *oid) + int repo_get_oid(struct repository *r, const char *name, struct object_id *oid) { struct object_context unused; - return get_oid_with_context(the_repository, name, 0, oid, &unused); + return get_oid_with_context(r, name, 0, oid, &unused); } +/* + * This returns a non-zero value if the string (built using printf + * format and the given arguments) is not a valid object. + */ +int get_oidf(struct object_id *oid, const char *fmt, ...) +{ + va_list ap; + int ret; + struct strbuf sb = STRBUF_INIT; + + va_start(ap, fmt); + strbuf_vaddf(&sb, fmt, ap); + va_end(ap); + + ret = get_oid(sb.buf, oid); + strbuf_release(&sb); + + return ret; +} /* * Many callers know that the user meant to name a commit-ish by @@@ -1560,43 -1594,48 +1625,48 @@@ * commit-ish. It is merely to give a hint to the disambiguation * machinery. */ - int get_oid_committish(const char *name, struct object_id *oid) + int repo_get_oid_committish(struct repository *r, + const char *name, + struct object_id *oid) { struct object_context unused; - return get_oid_with_context(the_repository, - name, GET_OID_COMMITTISH, + return get_oid_with_context(r, name, GET_OID_COMMITTISH, oid, &unused); } - int get_oid_treeish(const char *name, struct object_id *oid) + int repo_get_oid_treeish(struct repository *r, + const char *name, + struct object_id *oid) { struct object_context unused; - return get_oid_with_context(the_repository, - name, GET_OID_TREEISH, + return get_oid_with_context(r, name, GET_OID_TREEISH, oid, &unused); } - int get_oid_commit(const char *name, struct object_id *oid) + int repo_get_oid_commit(struct repository *r, + const char *name, + struct object_id *oid) { struct object_context unused; - return get_oid_with_context(the_repository, - name, GET_OID_COMMIT, + return get_oid_with_context(r, name, GET_OID_COMMIT, oid, &unused); } - int get_oid_tree(const char *name, struct object_id *oid) + int repo_get_oid_tree(struct repository *r, + const char *name, + struct object_id *oid) { struct object_context unused; - return get_oid_with_context(the_repository, - name, GET_OID_TREE, + return get_oid_with_context(r, name, GET_OID_TREE, oid, &unused); } - int get_oid_blob(const char *name, struct object_id *oid) + int repo_get_oid_blob(struct repository *r, + const char *name, + struct object_id *oid) { struct object_context unused; - return get_oid_with_context(the_repository, - name, GET_OID_BLOB, + return get_oid_with_context(r, name, GET_OID_BLOB, oid, &unused); } @@@ -1608,7 -1647,7 +1678,7 @@@ static void diagnose_invalid_oid_path(c int object_name_len) { struct object_id oid; - unsigned mode; + unsigned short mode; if (!prefix) prefix = ""; @@@ -1635,11 -1674,12 +1705,12 @@@ } /* Must be called only when :stage:filename doesn't exist. */ - static void diagnose_invalid_index_path(struct index_state *istate, + static void diagnose_invalid_index_path(struct repository *r, int stage, const char *prefix, const char *filename) { + struct index_state *istate = r->index; const struct cache_entry *ce; int pos; unsigned namelen = strlen(filename); @@@ -1679,7 -1719,7 +1750,7 @@@ ce_stage(ce), filename); } - if (file_exists(filename)) + if (repo_file_exists(r, filename)) die("Path '%s' exists on disk, but not in the index.", filename); if (is_missing_file_error(errno)) die("Path '%s' does not exist (neither on disk nor in the index).", @@@ -1689,12 -1729,12 +1760,12 @@@ } - static char *resolve_relative_path(const char *rel) + static char *resolve_relative_path(struct repository *r, const char *rel) { if (!starts_with(rel, "./") && !starts_with(rel, "../")) return NULL; - if (!is_inside_work_tree()) + if (r != the_repository || !is_inside_work_tree()) die("relative path syntax can't be used outside working tree."); /* die() inside prefix_path() if resolved path is outside worktree */ @@@ -1721,7 -1761,7 +1792,7 @@@ static enum get_oid_result get_oid_with memset(oc, 0, sizeof(*oc)); oc->mode = S_IFINVALID; strbuf_init(&oc->symlink_path, 0); - ret = get_oid_1(name, namelen, oid, flags); + ret = get_oid_1(repo, name, namelen, oid, flags); if (!ret) return ret; /* @@@ -1737,12 -1777,15 +1808,15 @@@ char *new_path = NULL; int pos; if (!only_to_die && namelen > 2 && name[1] == '/') { + struct handle_one_ref_cb cb; struct commit_list *list = NULL; - for_each_ref(handle_one_ref, &list); - head_ref(handle_one_ref, &list); + cb.repo = repo; + cb.list = &list; + refs_for_each_ref(repo->refs, handle_one_ref, &cb); + refs_head_ref(repo->refs, handle_one_ref, &cb); commit_list_sort_by_date(&list); - return get_oid_oneline(name + 2, oid, list); + return get_oid_oneline(repo, name + 2, oid, list); } if (namelen < 3 || name[2] != ':' || @@@ -1752,7 -1795,7 +1826,7 @@@ stage = name[1] - '0'; cp = name + 3; } - new_path = resolve_relative_path(cp); + new_path = resolve_relative_path(repo, cp); if (!new_path) { namelen = namelen - (cp - name); } else { @@@ -1764,7 -1807,7 +1838,7 @@@ oc->path = xstrdup(cp); if (!repo->index->cache) - repo_read_index(the_repository); + repo_read_index(repo); pos = index_name_pos(repo->index, cp, namelen); if (pos < 0) pos = -pos - 1; @@@ -1782,7 -1825,7 +1856,7 @@@ pos++; } if (only_to_die && name[1] && name[1] != '/') - diagnose_invalid_index_path(repo->index, stage, prefix, cp); + diagnose_invalid_index_path(repo, stage, prefix, cp); free(new_path); return -1; } @@@ -1802,13 -1845,21 +1876,21 @@@ sub_flags &= ~GET_OID_DISAMBIGUATORS; sub_flags |= GET_OID_TREEISH; - if (!get_oid_1(name, len, &tree_oid, sub_flags)) { + if (!get_oid_1(repo, name, len, &tree_oid, sub_flags)) { const char *filename = cp+1; char *new_filename = NULL; - new_filename = resolve_relative_path(filename); + new_filename = resolve_relative_path(repo, filename); if (new_filename) filename = new_filename; + /* + * NEEDSWORK: Eventually get_tree_entry*() should + * learn to take struct repository directly and we + * would not need to inject submodule odb to the + * in-core odb. + */ + if (repo != the_repository) + add_to_alternates_memory(repo->objects->odb->path); if (flags & GET_OID_FOLLOW_SYMLINKS) { ret = get_tree_entry_follow_symlinks(&tree_oid, filename, oid, &oc->symlink_path, @@@ -1843,11 -1894,13 +1925,13 @@@ * exist in 'HEAD'" when given "HEAD:doc", or it may return in which case * you have a chance to diagnose the error further. */ - void maybe_die_on_misspelt_object_name(const char *name, const char *prefix) + void maybe_die_on_misspelt_object_name(struct repository *r, + const char *name, + const char *prefix) { struct object_context oc; struct object_id oid; - get_oid_with_context_1(the_repository, name, GET_OID_ONLY_TO_DIE, + get_oid_with_context_1(r, name, GET_OID_ONLY_TO_DIE, prefix, &oid, &oc); } diff --combined upload-pack.c index cb603a6d8a,56505d60c3..d2ea5eb20d --- a/upload-pack.c +++ b/upload-pack.c @@@ -592,8 -592,7 +592,8 @@@ error return 1; } -static void check_non_tip(struct object_array *want_obj) +static void check_non_tip(struct object_array *want_obj, + struct packet_writer *writer) { int i; @@@ -612,13 -611,9 +612,13 @@@ error /* Pick one of them (we know there at least is one) */ for (i = 0; i < want_obj->nr; i++) { struct object *o = want_obj->objects[i].item; - if (!is_our_ref(o)) + if (!is_our_ref(o)) { + packet_writer_error(writer, + "upload-pack: not our ref %s", + oid_to_hex(&o->oid)); die("git upload-pack: not our ref %s", oid_to_hex(&o->oid)); + } } } @@@ -839,7 -834,7 +839,7 @@@ static int process_deepen_not(const cha if (skip_prefix(line, "deepen-not ", &arg)) { char *ref = NULL; struct object_id oid; - if (expand_ref(arg, strlen(arg), &oid, &ref) != 1) + if (expand_ref(the_repository, arg, strlen(arg), &oid, &ref) != 1) die("git upload-pack: ambiguous deepen-not: %s", line); string_list_append(deepen_not, ref); free(ref); @@@ -941,7 -936,7 +941,7 @@@ static void receive_needs(struct packet * by another process that handled the initial request. */ if (has_non_tip) - check_non_tip(want_obj); + check_non_tip(want_obj, &writer); if (!use_sideband && daemon_mode) no_progress = 1;