summaryrefslogtreecommitdiff
path: root/merge-ort.c
diff options
context:
space:
mode:
Diffstat (limited to 'merge-ort.c')
-rw-r--r--merge-ort.c348
1 files changed, 246 insertions, 102 deletions
diff --git a/merge-ort.c b/merge-ort.c
index e5e22e3583..11029c10be 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -14,12 +14,14 @@
* "cale", "peedy", or "ins" instead of "ort"?)
*/
+#define USE_THE_REPOSITORY_VARIABLE
+
#include "git-compat-util.h"
#include "merge-ort.h"
#include "alloc.h"
+#include "advice.h"
#include "attr.h"
-#include "blob.h"
#include "cache-tree.h"
#include "commit.h"
#include "commit-reach.h"
@@ -39,11 +41,10 @@
#include "path.h"
#include "promisor-remote.h"
#include "read-cache-ll.h"
+#include "refs.h"
#include "revision.h"
#include "sparse-index.h"
#include "strmap.h"
-#include "submodule-config.h"
-#include "submodule.h"
#include "trace2.h"
#include "tree.h"
#include "unpack-trees.h"
@@ -545,15 +546,34 @@ enum conflict_and_info_types {
CONFLICT_SUBMODULE_MAY_HAVE_REWINDS,
CONFLICT_SUBMODULE_NULL_MERGE_BASE,
+ /* INSERT NEW ENTRIES HERE */
+
+ /*
+ * Keep this entry after all regular conflict and info types; only
+ * errors (failures causing immediate abort of the merge) should
+ * come after this.
+ */
+ NB_REGULAR_CONFLICT_TYPES,
+
+ /*
+ * Something is seriously wrong; cannot even perform merge;
+ * Keep this group _last_ other than NB_TOTAL_TYPES
+ */
+ ERROR_SUBMODULE_CORRUPT,
+ ERROR_THREEWAY_CONTENT_MERGE_FAILED,
+ ERROR_OBJECT_WRITE_FAILED,
+ ERROR_OBJECT_READ_FAILED,
+ ERROR_OBJECT_NOT_A_BLOB,
+
/* Keep this entry _last_ in the list */
- NB_CONFLICT_TYPES,
+ NB_TOTAL_TYPES,
};
/*
* Short description of conflict type, relied upon by external tools.
*
* We can add more entries, but DO NOT change any of these strings. Also,
- * Order MUST match conflict_info_and_types.
+ * please ensure the order matches what is used in conflict_info_and_types.
*/
static const char *type_short_descriptions[] = {
/*** "Simple" conflicts and informational messages ***/
@@ -596,7 +616,19 @@ static const char *type_short_descriptions[] = {
[CONFLICT_SUBMODULE_MAY_HAVE_REWINDS] =
"CONFLICT (submodule may have rewinds)",
[CONFLICT_SUBMODULE_NULL_MERGE_BASE] =
- "CONFLICT (submodule lacks merge base)"
+ "CONFLICT (submodule lacks merge base)",
+
+ /* Something is seriously wrong; cannot even perform merge */
+ [ERROR_SUBMODULE_CORRUPT] =
+ "ERROR (submodule corrupt)",
+ [ERROR_THREEWAY_CONTENT_MERGE_FAILED] =
+ "ERROR (three-way content merge failed)",
+ [ERROR_OBJECT_WRITE_FAILED] =
+ "ERROR (object write failed)",
+ [ERROR_OBJECT_READ_FAILED] =
+ "ERROR (object read failed)",
+ [ERROR_OBJECT_NOT_A_BLOB] =
+ "ERROR (object is not a blob)",
};
struct logical_conflict_info {
@@ -657,8 +689,7 @@ static void clear_or_reinit_internal_opts(struct merge_options_internal *opti,
*/
strmap_clear_func(&opti->conflicted, 0);
- if (opti->attr_index.cache_nr) /* true iff opt->renormalize */
- discard_index(&opti->attr_index);
+ discard_index(&opti->attr_index);
/* Free memory used by various renames maps */
for (i = MERGE_SIDE1; i <= MERGE_SIDE2; ++i) {
@@ -721,23 +752,6 @@ static void clear_or_reinit_internal_opts(struct merge_options_internal *opti,
renames->callback_data_nr = renames->callback_data_alloc = 0;
}
-__attribute__((format (printf, 2, 3)))
-static int err(struct merge_options *opt, const char *err, ...)
-{
- va_list params;
- struct strbuf sb = STRBUF_INIT;
-
- strbuf_addstr(&sb, "error: ");
- va_start(params, err);
- strbuf_vaddf(&sb, err, params);
- va_end(params);
-
- error("%s", sb.buf);
- strbuf_release(&sb);
-
- return -1;
-}
-
static void format_commit(struct strbuf *sb,
int indent,
struct repository *repo,
@@ -777,7 +791,8 @@ static void path_msg(struct merge_options *opt,
/* Sanity checks */
assert(omittable_hint ==
- !starts_with(type_short_descriptions[type], "CONFLICT") ||
+ (!starts_with(type_short_descriptions[type], "CONFLICT") &&
+ !starts_with(type_short_descriptions[type], "ERROR")) ||
type == CONFLICT_DIR_RENAME_SUGGESTED);
if (opt->record_conflict_msgs_as_headers && omittable_hint)
return; /* Do not record mere hints in headers */
@@ -1132,7 +1147,7 @@ static void collect_rename_info(struct merge_options *opt,
* Update dir_rename_mask (determines ignore-rename-source validity)
*
* dir_rename_mask helps us keep track of when directory rename
- * detection may be relevant. Basically, whenver a directory is
+ * detection may be relevant. Basically, whenever a directory is
* removed on one side of history, and a file is added to that
* directory on the other side of history, directory rename
* detection is relevant (meaning we have to detect renames for all
@@ -1676,12 +1691,14 @@ static int collect_merge_info(struct merge_options *opt,
info.data = opt;
info.show_all_errors = 1;
- parse_tree(merge_base);
- parse_tree(side1);
- parse_tree(side2);
- init_tree_desc(t + 0, merge_base->buffer, merge_base->size);
- init_tree_desc(t + 1, side1->buffer, side1->size);
- init_tree_desc(t + 2, side2->buffer, side2->size);
+ if (parse_tree(merge_base) < 0 ||
+ parse_tree(side1) < 0 ||
+ parse_tree(side2) < 0)
+ return -1;
+ init_tree_desc(t + 0, &merge_base->object.oid,
+ merge_base->buffer, merge_base->size);
+ init_tree_desc(t + 1, &side1->object.oid, side1->buffer, side1->size);
+ init_tree_desc(t + 2, &side2->object.oid, side2->buffer, side2->size);
trace2_region_enter("merge", "traverse_trees", opt->repo);
ret = traverse_trees(NULL, 3, t, &info);
@@ -1727,7 +1744,14 @@ static int find_first_merges(struct repository *repo,
die("revision walk setup failed");
while ((commit = get_revision(&revs)) != NULL) {
struct object *o = &(commit->object);
- if (repo_in_merge_bases(repo, b, commit))
+ int ret = repo_in_merge_bases(repo, b, commit);
+
+ if (ret < 0) {
+ object_array_clear(&merges);
+ release_revisions(&revs);
+ return ret;
+ }
+ if (ret > 0)
add_object_array(o, NULL, &merges);
}
reset_revision_walk();
@@ -1742,9 +1766,17 @@ static int find_first_merges(struct repository *repo,
contains_another = 0;
for (j = 0; j < merges.nr; j++) {
struct commit *m2 = (struct commit *) merges.objects[j].item;
- if (i != j && repo_in_merge_bases(repo, m2, m1)) {
- contains_another = 1;
- break;
+ if (i != j) {
+ int ret = repo_in_merge_bases(repo, m2, m1);
+ if (ret < 0) {
+ object_array_clear(&merges);
+ release_revisions(&revs);
+ return ret;
+ }
+ if (ret > 0) {
+ contains_another = 1;
+ break;
+ }
}
}
@@ -1766,7 +1798,7 @@ static int merge_submodule(struct merge_options *opt,
{
struct repository subrepo;
struct strbuf sb = STRBUF_INIT;
- int ret = 0;
+ int ret = 0, ret2;
struct commit *commit_o, *commit_a, *commit_b;
int parent_count;
struct object_array merges;
@@ -1813,8 +1845,28 @@ static int merge_submodule(struct merge_options *opt,
}
/* check whether both changes are forward */
- if (!repo_in_merge_bases(&subrepo, commit_o, commit_a) ||
- !repo_in_merge_bases(&subrepo, commit_o, commit_b)) {
+ ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_a);
+ if (ret2 < 0) {
+ path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
+ path, NULL, NULL, NULL,
+ _("error: failed to merge submodule %s "
+ "(repository corrupt)"),
+ path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (ret2 > 0)
+ ret2 = repo_in_merge_bases(&subrepo, commit_o, commit_b);
+ if (ret2 < 0) {
+ path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
+ path, NULL, NULL, NULL,
+ _("error: failed to merge submodule %s "
+ "(repository corrupt)"),
+ path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (!ret2) {
path_msg(opt, CONFLICT_SUBMODULE_MAY_HAVE_REWINDS, 0,
path, NULL, NULL, NULL,
_("Failed to merge submodule %s "
@@ -1824,7 +1876,17 @@ static int merge_submodule(struct merge_options *opt,
}
/* Case #1: a is contained in b or vice versa */
- if (repo_in_merge_bases(&subrepo, commit_a, commit_b)) {
+ ret2 = repo_in_merge_bases(&subrepo, commit_a, commit_b);
+ if (ret2 < 0) {
+ path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
+ path, NULL, NULL, NULL,
+ _("error: failed to merge submodule %s "
+ "(repository corrupt)"),
+ path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (ret2 > 0) {
oidcpy(result, b);
path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
path, NULL, NULL, NULL,
@@ -1833,7 +1895,17 @@ static int merge_submodule(struct merge_options *opt,
ret = 1;
goto cleanup;
}
- if (repo_in_merge_bases(&subrepo, commit_b, commit_a)) {
+ ret2 = repo_in_merge_bases(&subrepo, commit_b, commit_a);
+ if (ret2 < 0) {
+ path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
+ path, NULL, NULL, NULL,
+ _("error: failed to merge submodule %s "
+ "(repository corrupt)"),
+ path);
+ ret = -1;
+ goto cleanup;
+ }
+ if (ret2 > 0) {
oidcpy(result, a);
path_msg(opt, INFO_SUBMODULE_FAST_FORWARDING, 1,
path, NULL, NULL, NULL,
@@ -1858,6 +1930,14 @@ static int merge_submodule(struct merge_options *opt,
parent_count = find_first_merges(&subrepo, path, commit_a, commit_b,
&merges);
switch (parent_count) {
+ case -1:
+ path_msg(opt, ERROR_SUBMODULE_CORRUPT, 0,
+ path, NULL, NULL, NULL,
+ _("error: failed to merge submodule %s "
+ "(repository corrupt)"),
+ path);
+ ret = -1;
+ break;
case 0:
path_msg(opt, CONFLICT_SUBMODULE_FAILED_TO_MERGE, 0,
path, NULL, NULL, NULL,
@@ -1919,6 +1999,7 @@ static void initialize_attr_index(struct merge_options *opt)
struct index_state *attr_index = &opt->priv->attr_index;
struct cache_entry *ce;
+ attr_index->repo = opt->repo;
attr_index->initialized = 1;
if (!opt->renormalize)
@@ -1974,7 +2055,7 @@ static int merge_3way(struct merge_options *opt,
mmbuffer_t *result_buf)
{
mmfile_t orig, src1, src2;
- struct ll_merge_options ll_opts = {0};
+ struct ll_merge_options ll_opts = LL_MERGE_OPTIONS_INIT;
char *base, *name1, *name2;
enum ll_merge_result merge_status;
@@ -1984,6 +2065,7 @@ static int merge_3way(struct merge_options *opt,
ll_opts.renormalize = opt->renormalize;
ll_opts.extra_marker_size = extra_marker_size;
ll_opts.xdl_opts = opt->xdl_opts;
+ ll_opts.conflict_style = opt->conflict_style;
if (opt->priv->call_depth) {
ll_opts.virtual_ancestor = 1;
@@ -2057,7 +2139,7 @@ static int handle_content_merge(struct merge_options *opt,
* merges, which happens for example with rename/rename(2to1) and
* rename/add conflicts.
*/
- unsigned clean = 1;
+ int clean = 1;
/*
* handle_content_merge() needs both files to be of the same type, i.e.
@@ -2121,19 +2203,28 @@ static int handle_content_merge(struct merge_options *opt,
pathnames, extra_marker_size,
&result_buf);
- if ((merge_status < 0) || !result_buf.ptr)
- ret = err(opt, _("Failed to execute internal merge"));
+ if ((merge_status < 0) || !result_buf.ptr) {
+ path_msg(opt, ERROR_THREEWAY_CONTENT_MERGE_FAILED, 0,
+ pathnames[0], pathnames[1], pathnames[2], NULL,
+ _("error: failed to execute internal merge for %s"),
+ path);
+ ret = -1;
+ }
if (!ret &&
write_object_file(result_buf.ptr, result_buf.size,
- OBJ_BLOB, &result->oid))
- ret = err(opt, _("Unable to add %s to database"),
- path);
-
+ OBJ_BLOB, &result->oid)) {
+ path_msg(opt, ERROR_OBJECT_WRITE_FAILED, 0,
+ pathnames[0], pathnames[1], pathnames[2], NULL,
+ _("error: unable to add %s to database"), path);
+ ret = -1;
+ }
free(result_buf.ptr);
+
if (ret)
return -1;
- clean &= (merge_status == 0);
+ if (merge_status > 0)
+ clean = 0;
path_msg(opt, INFO_AUTO_MERGING, 1, path, NULL, NULL, NULL,
_("Auto-merging %s"), path);
} else if (S_ISGITLINK(a->mode)) {
@@ -2141,6 +2232,8 @@ static int handle_content_merge(struct merge_options *opt,
clean = merge_submodule(opt, pathnames[0],
two_way ? null_oid() : &o->oid,
&a->oid, &b->oid, &result->oid);
+ if (clean < 0)
+ return -1;
if (opt->priv->call_depth && two_way && !clean) {
result->mode = o->mode;
oidcpy(&result->oid, &o->oid);
@@ -2617,7 +2710,7 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
struct conflict_info *dir_ci;
char *cur_dir = dirs_to_insert.items[i].string;
- CALLOC_ARRAY(dir_ci, 1);
+ dir_ci = mem_pool_calloc(&opt->priv->pool, 1, sizeof(*dir_ci));
dir_ci->merged.directory_name = parent_name;
len = strlen(parent_name);
@@ -2661,7 +2754,7 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
oidcpy(&ci->stages[i].oid, null_oid());
}
- // Now we want to focus on new_ci, so reassign ci to it
+ /* Now we want to focus on new_ci, so reassign ci to it. */
ci = new_ci;
}
@@ -2745,6 +2838,8 @@ static void apply_directory_rename_modifications(struct merge_options *opt,
* Finally, record the new location.
*/
pair->two->path = new_path;
+
+ string_list_clear(&dirs_to_insert, 0);
}
/*** Function Grouping: functions related to regular rename detection ***/
@@ -3342,10 +3437,7 @@ static int collect_renames(struct merge_options *opt,
return clean;
}
-static int detect_and_process_renames(struct merge_options *opt,
- struct tree *merge_base,
- struct tree *side1,
- struct tree *side2)
+static int detect_and_process_renames(struct merge_options *opt)
{
struct diff_queue_struct combined = { 0 };
struct rename_info *renames = &opt->priv->renames;
@@ -3444,7 +3536,7 @@ simple_cleanup:
/* Free memory for renames->pairs[] and combined */
for (s = MERGE_SIDE1; s <= MERGE_SIDE2; s++) {
free(renames->pairs[s].queue);
- DIFF_QUEUE_CLEAR(&renames->pairs[s]);
+ diff_queue_init(&renames->pairs[s]);
}
for (i = 0; i < combined.nr; i++)
pool_diff_free_filepair(&opt->priv->pool, combined.queue[i]);
@@ -3511,17 +3603,25 @@ static int sort_dirs_next_to_their_children(const char *one, const char *two)
static int read_oid_strbuf(struct merge_options *opt,
const struct object_id *oid,
- struct strbuf *dst)
+ struct strbuf *dst,
+ const char *path)
{
void *buf;
enum object_type type;
unsigned long size;
buf = repo_read_object_file(the_repository, oid, &type, &size);
- if (!buf)
- return err(opt, _("cannot read object %s"), oid_to_hex(oid));
+ if (!buf) {
+ path_msg(opt, ERROR_OBJECT_READ_FAILED, 0,
+ path, NULL, NULL, NULL,
+ _("error: cannot read object %s"), oid_to_hex(oid));
+ return -1;
+ }
if (type != OBJ_BLOB) {
free(buf);
- return err(opt, _("object %s is not a blob"), oid_to_hex(oid));
+ path_msg(opt, ERROR_OBJECT_NOT_A_BLOB, 0,
+ path, NULL, NULL, NULL,
+ _("error: object %s is not a blob"), oid_to_hex(oid));
+ return -1;
}
strbuf_attach(dst, buf, size, size + 1);
return 0;
@@ -3545,8 +3645,8 @@ static int blob_unchanged(struct merge_options *opt,
if (oideq(&base->oid, &side->oid))
return 1;
- if (read_oid_strbuf(opt, &base->oid, &basebuf) ||
- read_oid_strbuf(opt, &side->oid, &sidebuf))
+ if (read_oid_strbuf(opt, &base->oid, &basebuf, path) ||
+ read_oid_strbuf(opt, &side->oid, &sidebuf, path))
goto error_return;
/*
* Note: binary | is used so that both renormalizations are
@@ -3737,7 +3837,7 @@ static int write_completed_directory(struct merge_options *opt,
* src/moduleB 2
*
* which is used to know that xtract.c & token.txt are from the
- * toplevel dirctory, while umm.c & stuff.h & baz.c are from the
+ * toplevel directory, while umm.c & stuff.h & baz.c are from the
* src/moduleB directory. Again, following the example above,
* once we need to process src/moduleB, then info->offsets is
* updated to
@@ -4399,10 +4499,12 @@ static int checkout(struct merge_options *opt,
unpack_opts.verbose_update = (opt->verbosity > 2);
unpack_opts.fn = twoway_merge;
unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */
- parse_tree(prev);
- init_tree_desc(&trees[0], prev->buffer, prev->size);
- parse_tree(next);
- init_tree_desc(&trees[1], next->buffer, next->size);
+ if (parse_tree(prev) < 0)
+ return -1;
+ init_tree_desc(&trees[0], &prev->object.oid, prev->buffer, prev->size);
+ if (parse_tree(next) < 0)
+ return -1;
+ init_tree_desc(&trees[1], &next->object.oid, next->buffer, next->size);
ret = unpack_trees(2, trees, &unpack_opts);
clear_unpack_trees_porcelain(&unpack_opts);
@@ -4579,7 +4681,7 @@ static void print_submodule_conflict_suggestion(struct string_list *csub) {
" - commit the resulting index in the superproject\n"),
tmp.buf, subs.buf);
- printf("%s", msg.buf);
+ advise_if_enabled(ADVICE_SUBMODULE_MERGE_CONFLICT, "%s", msg.buf);
strbuf_release(&subs);
strbuf_release(&tmp);
@@ -4594,6 +4696,7 @@ void merge_display_update_messages(struct merge_options *opt,
struct hashmap_iter iter;
struct strmap_entry *e;
struct string_list olist = STRING_LIST_INIT_NODUP;
+ FILE *o = stdout;
if (opt->record_conflict_msgs_as_headers)
BUG("Either display conflict messages or record them as headers, not both");
@@ -4610,6 +4713,10 @@ void merge_display_update_messages(struct merge_options *opt,
}
string_list_sort(&olist);
+ /* Print to stderr if we hit errors rather than just conflicts */
+ if (result->clean < 0)
+ o = stderr;
+
/* Iterate over the items, printing them */
for (int path_nr = 0; path_nr < olist.nr; ++path_nr) {
struct string_list *conflicts = olist.items[path_nr].util;
@@ -4617,25 +4724,31 @@ void merge_display_update_messages(struct merge_options *opt,
struct logical_conflict_info *info =
conflicts->items[i].util;
+ /* On failure, ignore regular conflict types */
+ if (result->clean < 0 &&
+ info->type < NB_REGULAR_CONFLICT_TYPES)
+ continue;
+
if (detailed) {
- printf("%lu", (unsigned long)info->paths.nr);
- putchar('\0');
+ fprintf(o, "%lu", (unsigned long)info->paths.nr);
+ fputc('\0', o);
for (int n = 0; n < info->paths.nr; n++) {
- fputs(info->paths.v[n], stdout);
- putchar('\0');
+ fputs(info->paths.v[n], o);
+ fputc('\0', o);
}
- fputs(type_short_descriptions[info->type],
- stdout);
- putchar('\0');
+ fputs(type_short_descriptions[info->type], o);
+ fputc('\0', o);
}
- puts(conflicts->items[i].string);
+ fputs(conflicts->items[i].string, o);
+ fputc('\n', o);
if (detailed)
- putchar('\0');
+ fputc('\0', o);
}
}
string_list_clear(&olist, 0);
- print_submodule_conflict_suggestion(&opti->conflicted_submodules);
+ if (result->clean >= 0)
+ print_submodule_conflict_suggestion(&opti->conflicted_submodules);
/* Also include needed rename limit adjustment now */
diff_warn_rename_limit("merge.renamelimit",
@@ -4683,9 +4796,6 @@ void merge_switch_to_result(struct merge_options *opt,
{
assert(opt->priv == NULL);
if (result->clean >= 0 && update_worktree_and_index) {
- const char *filename;
- FILE *fp;
-
trace2_region_enter("merge", "checkout", opt->repo);
if (checkout(opt, head, result->tree)) {
/* failure to function */
@@ -4711,10 +4821,17 @@ void merge_switch_to_result(struct merge_options *opt,
trace2_region_leave("merge", "record_conflicted", opt->repo);
trace2_region_enter("merge", "write_auto_merge", opt->repo);
- filename = git_path_auto_merge(opt->repo);
- fp = xfopen(filename, "w");
- fprintf(fp, "%s\n", oid_to_hex(&result->tree->object.oid));
- fclose(fp);
+ if (refs_update_ref(get_main_ref_store(opt->repo), "", "AUTO_MERGE",
+ &result->tree->object.oid, NULL, REF_NO_DEREF,
+ UPDATE_REFS_MSG_ON_ERR)) {
+ /* failure to function */
+ opt->priv = NULL;
+ result->clean = -1;
+ merge_finalize(opt, result);
+ trace2_region_leave("merge", "write_auto_merge",
+ opt->repo);
+ return;
+ }
trace2_region_leave("merge", "write_auto_merge", opt->repo);
}
if (display_update_msgs)
@@ -4902,8 +5019,7 @@ static void merge_start(struct merge_options *opt, struct merge_result *result)
trace2_region_leave("merge", "allocate/init", opt->repo);
}
-static void merge_check_renames_reusable(struct merge_options *opt,
- struct merge_result *result,
+static void merge_check_renames_reusable(struct merge_result *result,
struct tree *merge_base,
struct tree *side1,
struct tree *side2)
@@ -4948,6 +5064,26 @@ static void merge_check_renames_reusable(struct merge_options *opt,
/*** Function Grouping: merge_incore_*() and their internal variants ***/
+static void move_opt_priv_to_result_priv(struct merge_options *opt,
+ struct merge_result *result)
+{
+ /*
+ * opt->priv and result->priv are a bit weird. opt->priv contains
+ * information that we can re-use in subsequent merge operations to
+ * enable our cached renames optimization. The best way to provide
+ * that to subsequent merges is putting it in result->priv.
+ * However, putting it directly there would mean retrofitting lots
+ * of functions in this file to also take a merge_result pointer,
+ * which is ugly and annoying. So, we just make sure at the end of
+ * the merge (the outer merge if there are internal recursive ones)
+ * to move it.
+ */
+ assert(opt->priv && !result->priv);
+ result->priv = opt->priv;
+ result->_properly_initialized = RESULT_INITIALIZED;
+ opt->priv = NULL;
+}
+
/*
* Originally from merge_trees_internal(); heavily adapted, though.
*/
@@ -4973,18 +5109,18 @@ redo:
* TRANSLATORS: The %s arguments are: 1) tree hash of a merge
* base, and 2-3) the trees for the two trees we're merging.
*/
- err(opt, _("collecting merge info failed for trees %s, %s, %s"),
+ error(_("collecting merge info failed for trees %s, %s, %s"),
oid_to_hex(&merge_base->object.oid),
oid_to_hex(&side1->object.oid),
oid_to_hex(&side2->object.oid));
result->clean = -1;
+ move_opt_priv_to_result_priv(opt, result);
return;
}
trace2_region_leave("merge", "collect_merge_info", opt->repo);
trace2_region_enter("merge", "renames", opt->repo);
- result->clean = detect_and_process_renames(opt, merge_base,
- side1, side2);
+ result->clean = detect_and_process_renames(opt);
trace2_region_leave("merge", "renames", opt->repo);
if (opt->priv->renames.redo_after_renames == 2) {
trace2_region_enter("merge", "reset_maps", opt->repo);
@@ -5003,32 +5139,37 @@ redo:
if (result->clean >= 0) {
result->tree = parse_tree_indirect(&working_tree_oid);
+ if (!result->tree)
+ die(_("unable to read tree (%s)"),
+ oid_to_hex(&working_tree_oid));
/* existence of conflicted entries implies unclean */
result->clean &= strmap_empty(&opt->priv->conflicted);
}
- if (!opt->priv->call_depth) {
- result->priv = opt->priv;
- result->_properly_initialized = RESULT_INITIALIZED;
- opt->priv = NULL;
- }
+ if (!opt->priv->call_depth || result->clean < 0)
+ move_opt_priv_to_result_priv(opt, result);
}
/*
* Originally from merge_recursive_internal(); somewhat adapted, though.
*/
static void merge_ort_internal(struct merge_options *opt,
- struct commit_list *merge_bases,
+ const struct commit_list *_merge_bases,
struct commit *h1,
struct commit *h2,
struct merge_result *result)
{
+ struct commit_list *merge_bases = copy_commit_list(_merge_bases);
struct commit *next;
struct commit *merged_merge_bases;
const char *ancestor_name;
struct strbuf merge_base_abbrev = STRBUF_INIT;
if (!merge_bases) {
- merge_bases = repo_get_merge_bases(the_repository, h1, h2);
+ if (repo_get_merge_bases(the_repository, h1, h2,
+ &merge_bases) < 0) {
+ result->clean = -1;
+ goto out;
+ }
/* See merge-ort.h:merge_incore_recursive() declaration NOTE */
merge_bases = reverse_commit_list(merge_bases);
}
@@ -5071,7 +5212,7 @@ static void merge_ort_internal(struct merge_options *opt,
opt->branch2 = "Temporary merge branch 2";
merge_ort_internal(opt, NULL, prev, next, result);
if (result->clean < 0)
- return;
+ goto out;
opt->branch1 = saved_b1;
opt->branch2 = saved_b2;
opt->priv->call_depth--;
@@ -5094,6 +5235,9 @@ static void merge_ort_internal(struct merge_options *opt,
result);
strbuf_release(&merge_base_abbrev);
opt->ancestor = NULL; /* avoid accidental re-use of opt->ancestor */
+
+out:
+ free_commit_list(merge_bases);
}
void merge_incore_nonrecursive(struct merge_options *opt,
@@ -5106,7 +5250,7 @@ void merge_incore_nonrecursive(struct merge_options *opt,
trace2_region_enter("merge", "merge_start", opt->repo);
assert(opt->ancestor != NULL);
- merge_check_renames_reusable(opt, result, merge_base, side1, side2);
+ merge_check_renames_reusable(result, merge_base, side1, side2);
merge_start(opt, result);
/*
* Record the trees used in this merge, so if there's a next merge in
@@ -5123,7 +5267,7 @@ void merge_incore_nonrecursive(struct merge_options *opt,
}
void merge_incore_recursive(struct merge_options *opt,
- struct commit_list *merge_bases,
+ const struct commit_list *merge_bases,
struct commit *side1,
struct commit *side2,
struct merge_result *result)