summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVicent Marti <vicent@github.com>2014-03-24 18:09:13 +0100
committerVicent Marti <vicent@github.com>2014-03-24 18:09:13 +0100
commit85a41fc4bf148dc954fa8124b61355ee648c40ba (patch)
tree477ab7f38edf350dff922e9b21612f4d71ae3c76 /src
parentf57cc63811db2717e9ba42e720a75f71557f9451 (diff)
parent58c2b1c4218a2f90ee2745687f2e17670bde0b1b (diff)
downloadlibgit2-85a41fc4bf148dc954fa8124b61355ee648c40ba.tar.gz
Merge pull request #2183 from ethomson/merge_refactor
Refactor the `git_merge` API
Diffstat (limited to 'src')
-rw-r--r--src/checkout.c43
-rw-r--r--src/index.c8
-rw-r--r--src/index.h2
-rw-r--r--src/merge.c400
-rw-r--r--src/merge.h17
-rw-r--r--src/merge_file.c263
-rw-r--r--src/merge_file.h78
-rw-r--r--src/revert.c6
8 files changed, 403 insertions, 414 deletions
diff --git a/src/checkout.c b/src/checkout.c
index 5dd4ec71c..f882f3593 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -1681,29 +1681,20 @@ static int checkout_write_merge(
{
git_buf our_label = GIT_BUF_INIT, their_label = GIT_BUF_INIT,
path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT;
- git_merge_file_options merge_file_opts = GIT_MERGE_FILE_OPTIONS_INIT;
- git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
- ours = GIT_MERGE_FILE_INPUT_INIT,
- theirs = GIT_MERGE_FILE_INPUT_INIT;
- git_merge_file_result result = GIT_MERGE_FILE_RESULT_INIT;
+ git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
git_filebuf output = GIT_FILEBUF_INIT;
int error = 0;
if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)
- merge_file_opts.style = GIT_MERGE_FILE_STYLE_DIFF3;
-
- if ((conflict->ancestor &&
- (error = git_merge_file_input_from_index_entry(
- &ancestor, data->repo, conflict->ancestor)) < 0) ||
- (error = git_merge_file_input_from_index_entry(
- &ours, data->repo, conflict->ours)) < 0 ||
- (error = git_merge_file_input_from_index_entry(
- &theirs, data->repo, conflict->theirs)) < 0)
- goto done;
+ opts.flags |= GIT_MERGE_FILE_STYLE_DIFF3;
- ancestor.label = data->opts.ancestor_label ? data->opts.ancestor_label : "ancestor";
- ours.label = data->opts.our_label ? data->opts.our_label : "ours";
- theirs.label = data->opts.their_label ? data->opts.their_label : "theirs";
+ opts.ancestor_label = data->opts.ancestor_label ?
+ data->opts.ancestor_label : "ancestor";
+ opts.our_label = data->opts.our_label ?
+ data->opts.our_label : "ours";
+ opts.their_label = data->opts.their_label ?
+ data->opts.their_label : "theirs";
/* If all the paths are identical, decorate the diff3 file with the branch
* names. Otherwise, append branch_name:path.
@@ -1712,16 +1703,17 @@ static int checkout_write_merge(
strcmp(conflict->ours->path, conflict->theirs->path) != 0) {
if ((error = conflict_entry_name(
- &our_label, ours.label, conflict->ours->path)) < 0 ||
+ &our_label, opts.our_label, conflict->ours->path)) < 0 ||
(error = conflict_entry_name(
- &their_label, theirs.label, conflict->theirs->path)) < 0)
+ &their_label, opts.their_label, conflict->theirs->path)) < 0)
goto done;
- ours.label = git_buf_cstr(&our_label);
- theirs.label = git_buf_cstr(&their_label);
+ opts.our_label = git_buf_cstr(&our_label);
+ opts.their_label = git_buf_cstr(&their_label);
}
- if ((error = git_merge_files(&result, &ancestor, &ours, &theirs, &merge_file_opts)) < 0)
+ if ((error = git_merge_file_from_index(&result, data->repo,
+ conflict->ancestor, conflict->ours, conflict->theirs, &opts)) < 0)
goto done;
if (result.path == NULL || result.mode == 0) {
@@ -1739,7 +1731,7 @@ static int checkout_write_merge(
if ((error = git_futils_mkpath2file(path_workdir.ptr, 0755)) < 0 ||
(error = git_filebuf_open(&output, path_workdir.ptr, GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 ||
- (error = git_filebuf_write(&output, result.data, result.len)) < 0 ||
+ (error = git_filebuf_write(&output, result.ptr, result.len)) < 0 ||
(error = git_filebuf_commit(&output)) < 0)
goto done;
@@ -1747,9 +1739,6 @@ done:
git_buf_free(&our_label);
git_buf_free(&their_label);
- git_merge_file_input_free(&ancestor);
- git_merge_file_input_free(&ours);
- git_merge_file_input_free(&theirs);
git_merge_file_result_free(&result);
git_buf_free(&path_workdir);
git_buf_free(&path_suffixed);
diff --git a/src/index.c b/src/index.c
index 0d7d50668..ea0815e4c 100644
--- a/src/index.c
+++ b/src/index.c
@@ -300,7 +300,7 @@ static void index_entry_free(git_index_entry *entry)
git__free(entry);
}
-static unsigned int index_create_mode(unsigned int mode)
+unsigned int git_index__create_mode(unsigned int mode)
{
if (S_ISLNK(mode))
return S_IFLNK;
@@ -320,9 +320,9 @@ static unsigned int index_merge_mode(
if (index->distrust_filemode && S_ISREG(mode))
return (existing && S_ISREG(existing->mode)) ?
- existing->mode : index_create_mode(0666);
+ existing->mode : git_index__create_mode(0666);
- return index_create_mode(mode);
+ return git_index__create_mode(mode);
}
void git_index__set_ignore_case(git_index *index, bool ignore_case)
@@ -619,7 +619,7 @@ void git_index_entry__init_from_stat(
entry->dev = st->st_rdev;
entry->ino = st->st_ino;
entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ?
- index_create_mode(0666) : index_create_mode(st->st_mode);
+ git_index__create_mode(0666) : git_index__create_mode(st->st_mode);
entry->uid = st->st_uid;
entry->gid = st->st_gid;
entry->file_size = st->st_size;
diff --git a/src/index.h b/src/index.h
index f88d110f7..259a3149f 100644
--- a/src/index.h
+++ b/src/index.h
@@ -60,4 +60,6 @@ extern int git_index__find(
extern void git_index__set_ignore_case(git_index *index, bool ignore_case);
+extern unsigned int git_index__create_mode(unsigned int mode);
+
#endif
diff --git a/src/merge.c b/src/merge.c
index 66952c074..42fbd79c9 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -545,11 +545,9 @@ static int merge_conflict_resolve_automerge(
const git_merge_diff *conflict,
unsigned int merge_file_favor)
{
- git_merge_file_options merge_file_opts = GIT_MERGE_FILE_OPTIONS_INIT;
- git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
- ours = GIT_MERGE_FILE_INPUT_INIT,
- theirs = GIT_MERGE_FILE_INPUT_INIT;
- git_merge_file_result result = GIT_MERGE_FILE_RESULT_INIT;
+ const git_index_entry *ancestor = NULL, *ours = NULL, *theirs = NULL;
+ git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
git_index_entry *index_entry;
git_odb *odb = NULL;
git_oid automerge_oid;
@@ -559,7 +557,9 @@ static int merge_conflict_resolve_automerge(
*resolved = 0;
- merge_file_opts.favor = merge_file_favor;
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ||
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
+ return 0;
/* Reject D/F conflicts */
if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE)
@@ -590,13 +590,19 @@ static int merge_conflict_resolve_automerge(
if (conflict->binary)
return 0;
+ ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
+ &conflict->ancestor_entry : NULL;
+ ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
+ &conflict->our_entry : NULL;
+ theirs = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
+ &conflict->their_entry : NULL;
+
+ opts.favor = merge_file_favor;
+
if ((error = git_repository_odb(&odb, diff_list->repo)) < 0 ||
- (error = git_merge_file_input_from_index_entry(&ancestor, diff_list->repo, &conflict->ancestor_entry)) < 0 ||
- (error = git_merge_file_input_from_index_entry(&ours, diff_list->repo, &conflict->our_entry)) < 0 ||
- (error = git_merge_file_input_from_index_entry(&theirs, diff_list->repo, &conflict->their_entry)) < 0 ||
- (error = git_merge_files(&result, &ancestor, &ours, &theirs, &merge_file_opts)) < 0 ||
+ (error = git_merge_file_from_index(&result, diff_list->repo, ancestor, ours, theirs, &opts)) < 0 ||
!result.automergeable ||
- (error = git_odb_write(&automerge_oid, odb, result.data, result.len, GIT_OBJ_BLOB)) < 0)
+ (error = git_odb_write(&automerge_oid, odb, result.ptr, result.len, GIT_OBJ_BLOB)) < 0)
goto done;
if ((index_entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL)
@@ -615,9 +621,6 @@ static int merge_conflict_resolve_automerge(
*resolved = 1;
done:
- git_merge_file_input_free(&ancestor);
- git_merge_file_input_free(&ours);
- git_merge_file_input_free(&theirs);
git_merge_file_result_free(&result);
git_odb_free(odb);
@@ -667,7 +670,7 @@ static int index_entry_similarity_exact(
git_index_entry *b,
size_t b_idx,
void **cache,
- const git_merge_tree_opts *opts)
+ const git_merge_options *opts)
{
GIT_UNUSED(repo);
GIT_UNUSED(a_idx);
@@ -685,7 +688,7 @@ static int index_entry_similarity_calc(
void **out,
git_repository *repo,
git_index_entry *entry,
- const git_merge_tree_opts *opts)
+ const git_merge_options *opts)
{
git_blob *blob;
git_diff_file diff_file = {{{0}}};
@@ -725,7 +728,7 @@ static int index_entry_similarity_inexact(
git_index_entry *b,
size_t b_idx,
void **cache,
- const git_merge_tree_opts *opts)
+ const git_merge_options *opts)
{
int score = 0;
int error = 0;
@@ -762,9 +765,9 @@ static int merge_diff_mark_similarity(
git_merge_diff_list *diff_list,
struct merge_diff_similarity *similarity_ours,
struct merge_diff_similarity *similarity_theirs,
- int (*similarity_fn)(git_repository *, git_index_entry *, size_t, git_index_entry *, size_t, void **, const git_merge_tree_opts *),
+ int (*similarity_fn)(git_repository *, git_index_entry *, size_t, git_index_entry *, size_t, void **, const git_merge_options *),
void **cache,
- const git_merge_tree_opts *opts)
+ const git_merge_options *opts)
{
size_t i, j;
git_merge_diff *conflict_src, *conflict_tgt;
@@ -865,7 +868,7 @@ static void merge_diff_mark_rename_conflict(
bool theirs_renamed,
size_t theirs_source_idx,
git_merge_diff *target,
- const git_merge_tree_opts *opts)
+ const git_merge_options *opts)
{
git_merge_diff *ours_source = NULL, *theirs_source = NULL;
@@ -935,7 +938,7 @@ static void merge_diff_list_coalesce_renames(
git_merge_diff_list *diff_list,
struct merge_diff_similarity *similarity_ours,
struct merge_diff_similarity *similarity_theirs,
- const git_merge_tree_opts *opts)
+ const git_merge_options *opts)
{
size_t i;
bool ours_renamed = 0, theirs_renamed = 0;
@@ -1025,7 +1028,7 @@ static void merge_diff_list_count_candidates(
int git_merge_diff_list__find_renames(
git_repository *repo,
git_merge_diff_list *diff_list,
- const git_merge_tree_opts *opts)
+ const git_merge_options *opts)
{
struct merge_diff_similarity *similarity_ours, *similarity_theirs;
void **cache = NULL;
@@ -1453,10 +1456,10 @@ void git_merge_diff_list__free(git_merge_diff_list *diff_list)
git__free(diff_list);
}
-static int merge_tree_normalize_opts(
+static int merge_normalize_opts(
git_repository *repo,
- git_merge_tree_opts *opts,
- const git_merge_tree_opts *given)
+ git_merge_options *opts,
+ const git_merge_options *given)
{
git_config *cfg = NULL;
int error = 0;
@@ -1467,9 +1470,9 @@ static int merge_tree_normalize_opts(
return error;
if (given != NULL)
- memcpy(opts, given, sizeof(git_merge_tree_opts));
+ memcpy(opts, given, sizeof(git_merge_options));
else {
- git_merge_tree_opts init = GIT_MERGE_TREE_OPTS_INIT;
+ git_merge_options init = GIT_MERGE_OPTIONS_INIT;
memcpy(opts, &init, sizeof(init));
opts->flags = GIT_MERGE_TREE_FIND_RENAMES;
@@ -1637,10 +1640,10 @@ int git_merge_trees(
const git_tree *ancestor_tree,
const git_tree *our_tree,
const git_tree *their_tree,
- const git_merge_tree_opts *given_opts)
+ const git_merge_options *given_opts)
{
git_merge_diff_list *diff_list;
- git_merge_tree_opts opts;
+ git_merge_options opts;
git_merge_diff *conflict;
git_vector changes;
size_t i;
@@ -1650,9 +1653,9 @@ int git_merge_trees(
*out = NULL;
- GITERR_CHECK_VERSION(given_opts, GIT_MERGE_TREE_OPTS_VERSION, "git_merge_tree_opts");
+ GITERR_CHECK_VERSION(given_opts, GIT_MERGE_OPTIONS_VERSION, "git_merge_options");
- if ((error = merge_tree_normalize_opts(repo, &opts, given_opts)) < 0)
+ if ((error = merge_normalize_opts(repo, &opts, given_opts)) < 0)
return error;
diff_list = git_merge_diff_list__alloc(repo);
@@ -1691,7 +1694,7 @@ int git_merge_commits(
git_repository *repo,
const git_commit *our_commit,
const git_commit *their_commit,
- const git_merge_tree_opts *opts)
+ const git_merge_options *opts)
{
git_oid ancestor_oid;
git_commit *ancestor_commit = NULL;
@@ -1777,31 +1780,20 @@ cleanup:
return error;
}
-static int write_merge_mode(git_repository *repo, unsigned int flags)
+static int write_merge_mode(git_repository *repo)
{
git_filebuf file = GIT_FILEBUF_INIT;
git_buf file_path = GIT_BUF_INIT;
int error = 0;
- /* For future expansion */
- GIT_UNUSED(flags);
-
assert(repo);
if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MODE_FILE)) < 0 ||
(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0)
goto cleanup;
- /*
- * no-ff is the only thing allowed here at present. One would
- * presume they would be space-delimited when there are more, but
- * this needs to be revisited.
- */
-
- if (flags & GIT_MERGE_NO_FASTFORWARD) {
- if ((error = git_filebuf_write(&file, "no-ff", 5)) < 0)
- goto cleanup;
- }
+ if ((error = git_filebuf_write(&file, "no-ff", 5)) < 0)
+ goto cleanup;
error = git_filebuf_commit(&file);
@@ -2117,6 +2109,25 @@ cleanup:
return error;
}
+int git_merge__setup(
+ git_repository *repo,
+ const git_merge_head *our_head,
+ const git_merge_head *heads[],
+ size_t heads_len)
+{
+ int error = 0;
+
+ assert (repo && our_head && heads);
+
+ if ((error = write_orig_head(repo, our_head)) == 0 &&
+ (error = write_merge_head(repo, heads, heads_len)) == 0 &&
+ (error = write_merge_mode(repo)) == 0) {
+ error = write_merge_msg(repo, heads, heads_len);
+ }
+
+ return error;
+}
+
/* Merge branches */
static int merge_ancestor_head(
@@ -2150,37 +2161,6 @@ on_error:
return error;
}
-GIT_INLINE(bool) merge_check_uptodate(
- git_merge_result *result,
- const git_merge_head *ancestor_head,
- const git_merge_head *their_head)
-{
- if (git_oid_cmp(&ancestor_head->oid, &their_head->oid) == 0) {
- result->is_uptodate = 1;
- return true;
- }
-
- return false;
-}
-
-GIT_INLINE(bool) merge_check_fastforward(
- git_merge_result *result,
- const git_merge_head *ancestor_head,
- const git_merge_head *our_head,
- const git_merge_head *their_head,
- unsigned int flags)
-{
- if ((flags & GIT_MERGE_NO_FASTFORWARD) == 0 &&
- git_oid_cmp(&ancestor_head->oid, &our_head->oid) == 0) {
- result->is_fastforward = 1;
- git_oid_cpy(&result->fastforward_oid, &their_head->oid);
-
- return true;
- }
-
- return false;
-}
-
const char *merge_their_label(const char *branchname)
{
const char *slash;
@@ -2194,10 +2174,10 @@ const char *merge_their_label(const char *branchname)
return slash+1;
}
-static int merge_normalize_opts(
+static int merge_normalize_checkout_opts(
git_repository *repo,
- git_merge_opts *opts,
- const git_merge_opts *given,
+ git_checkout_options *checkout_opts,
+ const git_checkout_options *given_checkout_opts,
const git_merge_head *ancestor_head,
const git_merge_head *our_head,
size_t their_heads_len,
@@ -2209,38 +2189,38 @@ static int merge_normalize_opts(
GIT_UNUSED(repo);
- if (given != NULL)
- memcpy(opts, given, sizeof(git_merge_opts));
+ if (given_checkout_opts != NULL)
+ memcpy(checkout_opts, given_checkout_opts, sizeof(git_checkout_options));
else {
- git_merge_opts default_opts = GIT_MERGE_OPTS_INIT;
- memcpy(opts, &default_opts, sizeof(git_merge_opts));
+ git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ memcpy(checkout_opts, &default_checkout_opts, sizeof(git_checkout_options));
}
- if (!opts->checkout_opts.checkout_strategy)
- opts->checkout_opts.checkout_strategy = default_checkout_strategy;
+ if (!checkout_opts->checkout_strategy)
+ checkout_opts->checkout_strategy = default_checkout_strategy;
/* TODO: for multiple ancestors in merge-recursive, this is "merged common ancestors" */
- if (!opts->checkout_opts.ancestor_label) {
+ if (!checkout_opts->ancestor_label) {
if (ancestor_head && ancestor_head->commit)
- opts->checkout_opts.ancestor_label = git_commit_summary(ancestor_head->commit);
+ checkout_opts->ancestor_label = git_commit_summary(ancestor_head->commit);
else
- opts->checkout_opts.ancestor_label = "ancestor";
+ checkout_opts->ancestor_label = "ancestor";
}
- if (!opts->checkout_opts.our_label) {
+ if (!checkout_opts->our_label) {
if (our_head && our_head->ref_name)
- opts->checkout_opts.our_label = our_head->ref_name;
+ checkout_opts->our_label = our_head->ref_name;
else
- opts->checkout_opts.our_label = "ours";
+ checkout_opts->our_label = "ours";
}
- if (!opts->checkout_opts.their_label) {
+ if (!checkout_opts->their_label) {
if (their_heads_len == 1 && their_heads[0]->ref_name)
- opts->checkout_opts.their_label = merge_their_label(their_heads[0]->ref_name);
+ checkout_opts->their_label = merge_their_label(their_heads[0]->ref_name);
else if (their_heads_len == 1)
- opts->checkout_opts.their_label = their_heads[0]->oid_str;
+ checkout_opts->their_label = their_heads[0]->oid_str;
else
- opts->checkout_opts.their_label = "theirs";
+ checkout_opts->their_label = "theirs";
}
return error;
@@ -2500,71 +2480,128 @@ static int merge_state_cleanup(git_repository *repo)
return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
}
-int git_merge(
- git_merge_result **out,
+static int merge_heads(
+ git_merge_head **ancestor_head_out,
+ git_merge_head **our_head_out,
git_repository *repo,
const git_merge_head **their_heads,
- size_t their_heads_len,
- const git_merge_opts *given_opts)
+ size_t their_heads_len)
{
- git_merge_result *result;
- git_merge_opts opts;
+ git_merge_head *ancestor_head = NULL, *our_head = NULL;
git_reference *our_ref = NULL;
+ int error = 0;
+
+ *ancestor_head_out = NULL;
+ *our_head_out = NULL;
+
+ if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
+ goto done;
+
+ if ((error = git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE)) < 0 ||
+ (error = git_merge_head_from_ref(&our_head, repo, our_ref)) < 0)
+ goto done;
+
+ if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto done;
+
+ giterr_clear();
+ error = 0;
+ }
+
+ *ancestor_head_out = ancestor_head;
+ *our_head_out = our_head;
+
+done:
+ if (error < 0) {
+ git_merge_head_free(ancestor_head);
+ git_merge_head_free(our_head);
+ }
+
+ git_reference_free(our_ref);
+
+ return error;
+}
+
+int git_merge_analysis(
+ git_merge_analysis_t *out,
+ git_repository *repo,
+ const git_merge_head **their_heads,
+ size_t their_heads_len)
+{
git_merge_head *ancestor_head = NULL, *our_head = NULL;
- git_tree *ancestor_tree = NULL, *our_tree = NULL, **their_trees = NULL;
- git_index *index_new = NULL, *index_repo = NULL;
- size_t i;
int error = 0;
assert(out && repo && their_heads);
- *out = NULL;
+ *out = GIT_MERGE_ANALYSIS_NONE;
- GITERR_CHECK_VERSION(given_opts, GIT_MERGE_OPTS_VERSION, "git_merge_opts");
+ if (git_repository_head_unborn(repo)) {
+ *out = GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN;
+ goto done;
+ }
if (their_heads_len != 1) {
giterr_set(GITERR_MERGE, "Can only merge a single branch");
- return -1;
+ error = -1;
+ goto done;
}
- result = git__calloc(1, sizeof(git_merge_result));
- GITERR_CHECK_ALLOC(result);
+ if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0)
+ goto done;
- their_trees = git__calloc(their_heads_len, sizeof(git_tree *));
- GITERR_CHECK_ALLOC(their_trees);
+ /* We're up-to-date if we're trying to merge our own common ancestor. */
+ if (ancestor_head && git_oid_equal(&ancestor_head->oid, &their_heads[0]->oid))
+ *out = GIT_MERGE_ANALYSIS_UP_TO_DATE;
- if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
- goto on_error;
+ /* We're fastforwardable if we're our own common ancestor. */
+ else if (ancestor_head && git_oid_equal(&ancestor_head->oid, &our_head->oid))
+ *out = GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL;
- if ((error = git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE)) < 0 ||
- (error = git_merge_head_from_ref(&our_head, repo, our_ref)) < 0)
- goto on_error;
+ /* Otherwise, just a normal merge is possible. */
+ else
+ *out = GIT_MERGE_ANALYSIS_NORMAL;
- if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0 &&
- error != GIT_ENOTFOUND)
- goto on_error;
+done:
+ git_merge_head_free(ancestor_head);
+ git_merge_head_free(our_head);
+ return error;
+}
- if ((error = merge_normalize_opts(repo, &opts, given_opts, ancestor_head, our_head, their_heads_len, their_heads)) < 0)
- goto on_error;
+int git_merge(
+ git_repository *repo,
+ const git_merge_head **their_heads,
+ size_t their_heads_len,
+ const git_merge_options *merge_opts,
+ const git_checkout_options *given_checkout_opts)
+{
+ git_reference *our_ref = NULL;
+ git_checkout_options checkout_opts;
+ git_merge_head *ancestor_head = NULL, *our_head = NULL;
+ git_tree *ancestor_tree = NULL, *our_tree = NULL, **their_trees = NULL;
+ git_index *index_new = NULL, *index_repo = NULL;
+ size_t i;
+ int error = 0;
- if (their_heads_len == 1 &&
- ancestor_head != NULL &&
- (merge_check_uptodate(result, ancestor_head, their_heads[0]) ||
- merge_check_fastforward(result, ancestor_head, our_head, their_heads[0], opts.merge_flags))) {
- *out = result;
- goto done;
+ assert(repo && their_heads);
+
+ if (their_heads_len != 1) {
+ giterr_set(GITERR_MERGE, "Can only merge a single branch");
+ return -1;
}
- /* If FASTFORWARD_ONLY is specified, fail. */
- if ((opts.merge_flags & GIT_MERGE_FASTFORWARD_ONLY) ==
- GIT_MERGE_FASTFORWARD_ONLY) {
- giterr_set(GITERR_MERGE, "Not a fast-forward.");
- error = GIT_ENONFASTFORWARD;
+ their_trees = git__calloc(their_heads_len, sizeof(git_tree *));
+ GITERR_CHECK_ALLOC(their_trees);
+
+ if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0)
+ goto on_error;
+
+ if ((error = merge_normalize_checkout_opts(repo, &checkout_opts, given_checkout_opts,
+ ancestor_head, our_head, their_heads_len, their_heads)) < 0)
goto on_error;
- }
/* Write the merge files to the repository. */
- if ((error = git_merge__setup(repo, our_head, their_heads, their_heads_len, opts.merge_flags)) < 0)
+ if ((error = git_merge__setup(repo, our_head, their_heads, their_heads_len)) < 0)
goto on_error;
if (ancestor_head != NULL &&
@@ -2581,22 +2618,18 @@ int git_merge(
/* TODO: recursive, octopus, etc... */
- if ((error = git_merge_trees(&index_new, repo, ancestor_tree, our_tree, their_trees[0], &opts.merge_tree_opts)) < 0 ||
+ if ((error = git_merge_trees(&index_new, repo, ancestor_tree, our_tree, their_trees[0], merge_opts)) < 0 ||
(error = git_merge__indexes(repo, index_new)) < 0 ||
(error = git_repository_index(&index_repo, repo)) < 0 ||
- (error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0)
+ (error = git_checkout_index(repo, index_repo, &checkout_opts)) < 0)
goto on_error;
- result->index = index_new;
-
- *out = result;
goto done;
on_error:
merge_state_cleanup(repo);
git_index_free(index_new);
- git__free(result);
done:
git_index_free(index_repo);
@@ -2617,61 +2650,6 @@ done:
return error;
}
-int git_merge__setup(
- git_repository *repo,
- const git_merge_head *our_head,
- const git_merge_head *heads[],
- size_t heads_len,
- unsigned int flags)
-{
- int error = 0;
-
- assert (repo && our_head && heads);
-
- if ((error = write_orig_head(repo, our_head)) == 0 &&
- (error = write_merge_head(repo, heads, heads_len)) == 0 &&
- (error = write_merge_mode(repo, flags)) == 0) {
- error = write_merge_msg(repo, heads, heads_len);
- }
-
- return error;
-}
-
-/* Merge result data */
-
-int git_merge_result_is_uptodate(git_merge_result *merge_result)
-{
- assert(merge_result);
-
- return merge_result->is_uptodate;
-}
-
-int git_merge_result_is_fastforward(git_merge_result *merge_result)
-{
- assert(merge_result);
-
- return merge_result->is_fastforward;
-}
-
-int git_merge_result_fastforward_id(git_oid *out, git_merge_result *merge_result)
-{
- assert(out && merge_result);
-
- git_oid_cpy(out, &merge_result->fastforward_oid);
- return 0;
-}
-
-void git_merge_result_free(git_merge_result *merge_result)
-{
- if (merge_result == NULL)
- return;
-
- git_index_free(merge_result->index);
- merge_result->index = NULL;
-
- git__free(merge_result);
-}
-
/* Merge heads are the input to merge */
static int merge_head_init(
@@ -2776,25 +2754,37 @@ void git_merge_head_free(git_merge_head *head)
git__free(head);
}
-int git_merge_init_opts(git_merge_opts* opts, int version)
+int git_merge_init_options(git_merge_options *opts, int version)
{
- if (version != GIT_MERGE_OPTS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_opts", version);
+ if (version != GIT_MERGE_OPTIONS_VERSION) {
+ giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_options", version);
return -1;
} else {
- git_merge_opts o = GIT_MERGE_OPTS_INIT;
- memcpy(opts, &o, sizeof(o));
+ git_merge_options default_opts = GIT_MERGE_OPTIONS_INIT;
+ memcpy(opts, &default_opts, sizeof(git_merge_options));
+ return 0;
+ }
+}
+
+int git_merge_file_init_input(git_merge_file_input *input, int version)
+{
+ if (version != GIT_MERGE_FILE_INPUT_VERSION) {
+ giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_file_input", version);
+ return -1;
+ } else {
+ git_merge_file_input i = GIT_MERGE_FILE_INPUT_INIT;
+ memcpy(input, &i, sizeof(i));
return 0;
}
}
-int git_merge_tree_init_opts(git_merge_tree_opts* opts, int version)
+int git_merge_file_init_options(git_merge_file_options *opts, int version)
{
- if (version != GIT_MERGE_TREE_OPTS_VERSION) {
- giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_tree_opts", version);
+ if (version != GIT_MERGE_FILE_OPTIONS_VERSION) {
+ giterr_set(GITERR_INVALID, "Invalid version %d for git_merge_file_options", version);
return -1;
} else {
- git_merge_tree_opts o = GIT_MERGE_TREE_OPTS_INIT;
+ git_merge_file_options o = GIT_MERGE_FILE_OPTIONS_INIT;
memcpy(opts, &o, sizeof(o));
return 0;
}
diff --git a/src/merge.h b/src/merge.h
index dda023528..2362da04d 100644
--- a/src/merge.h
+++ b/src/merge.h
@@ -120,16 +120,6 @@ struct git_merge_head {
git_commit *commit;
};
-/** Internal structure for merge results */
-struct git_merge_result {
- bool is_uptodate;
-
- bool is_fastforward;
- git_oid fastforward_oid;
-
- git_index *index;
-};
-
int git_merge__bases_many(
git_commit_list **out,
git_revwalk *walk,
@@ -147,7 +137,7 @@ int git_merge_diff_list__find_differences(git_merge_diff_list *merge_diff_list,
const git_tree *ours_tree,
const git_tree *theirs_tree);
-int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list *merge_diff_list, const git_merge_tree_opts *opts);
+int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list *merge_diff_list, const git_merge_options *opts);
void git_merge_diff_list__free(git_merge_diff_list *diff_list);
@@ -156,9 +146,8 @@ void git_merge_diff_list__free(git_merge_diff_list *diff_list);
int git_merge__setup(
git_repository *repo,
const git_merge_head *our_head,
- const git_merge_head *their_heads[],
- size_t their_heads_len,
- unsigned int flags);
+ const git_merge_head *heads[],
+ size_t heads_len);
int git_merge__indexes(git_repository *repo, git_index *index_new);
diff --git a/src/merge_file.c b/src/merge_file.c
index 986fbf9fe..fc45cbfbf 100644
--- a/src/merge_file.c
+++ b/src/merge_file.c
@@ -8,6 +8,9 @@
#include "common.h"
#include "repository.h"
#include "merge_file.h"
+#include "posix.h"
+#include "fileops.h"
+#include "index.h"
#include "git2/repository.h"
#include "git2/object.h"
@@ -22,17 +25,17 @@ GIT_INLINE(const char *) merge_file_best_path(
const git_merge_file_input *ours,
const git_merge_file_input *theirs)
{
- if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
- if (strcmp(ours->path, theirs->path) == 0)
+ if (!ancestor) {
+ if (ours && theirs && strcmp(ours->path, theirs->path) == 0)
return ours->path;
return NULL;
}
- if (strcmp(ancestor->path, ours->path) == 0)
- return theirs->path;
- else if(strcmp(ancestor->path, theirs->path) == 0)
- return ours->path;
+ if (ours && strcmp(ancestor->path, ours->path) == 0)
+ return theirs ? theirs->path : NULL;
+ else if(theirs && strcmp(ancestor->path, theirs->path) == 0)
+ return ours ? ours->path : NULL;
return NULL;
}
@@ -47,136 +50,230 @@ GIT_INLINE(int) merge_file_best_mode(
* assume executable. Otherwise, if any mode changed from the ancestor,
* use that one.
*/
- if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
- if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE ||
- theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE)
+ if (!ancestor) {
+ if ((ours && ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE) ||
+ (theirs && theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE))
return GIT_FILEMODE_BLOB_EXECUTABLE;
return GIT_FILEMODE_BLOB;
- }
+ } else if (ours && theirs) {
+ if (ancestor->mode == ours->mode)
+ return theirs->mode;
- if (ancestor->mode == ours->mode)
- return theirs->mode;
- else if(ancestor->mode == theirs->mode)
return ours->mode;
+ }
return 0;
}
-int git_merge_file_input_from_index_entry(
- git_merge_file_input *input,
- git_repository *repo,
+int git_merge_file__input_from_index(
+ git_merge_file_input *input_out,
+ git_odb_object **odb_object_out,
+ git_odb *odb,
const git_index_entry *entry)
{
- git_odb *odb = NULL;
int error = 0;
- assert(input && repo && entry);
-
- if (entry->mode == 0)
- return 0;
+ assert(input_out && odb_object_out && odb && entry);
- if ((error = git_repository_odb(&odb, repo)) < 0 ||
- (error = git_odb_read(&input->odb_object, odb, &entry->id)) < 0)
+ if ((error = git_odb_read(odb_object_out, odb, &entry->id)) < 0)
goto done;
- input->mode = entry->mode;
- input->path = git__strdup(entry->path);
- input->mmfile.size = git_odb_object_size(input->odb_object);
- input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object);
-
- if (input->label == NULL)
- input->label = entry->path;
+ input_out->path = entry->path;
+ input_out->mode = entry->mode;
+ input_out->ptr = (char *)git_odb_object_data(*odb_object_out);
+ input_out->size = git_odb_object_size(*odb_object_out);
done:
- git_odb_free(odb);
-
return error;
}
-int git_merge_file_input_from_diff_file(
- git_merge_file_input *input,
- git_repository *repo,
- const git_diff_file *file)
+static void merge_file_normalize_opts(
+ git_merge_file_options *out,
+ const git_merge_file_options *given_opts)
{
- git_odb *odb = NULL;
- int error = 0;
-
- assert(input && repo && file);
-
- if (file->mode == 0)
- return 0;
-
- if ((error = git_repository_odb(&odb, repo)) < 0 ||
- (error = git_odb_read(&input->odb_object, odb, &file->id)) < 0)
- goto done;
-
- input->mode = file->mode;
- input->path = git__strdup(file->path);
- input->mmfile.size = git_odb_object_size(input->odb_object);
- input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object);
-
- if (input->label == NULL)
- input->label = file->path;
-
-done:
- git_odb_free(odb);
-
- return error;
+ if (given_opts)
+ memcpy(out, given_opts, sizeof(git_merge_file_options));
+ else {
+ git_merge_file_options default_opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ memcpy(out, &default_opts, sizeof(git_merge_file_options));
+ }
}
-int git_merge_files(
+static int git_merge_file__from_inputs(
git_merge_file_result *out,
- git_merge_file_input *ancestor,
- git_merge_file_input *ours,
- git_merge_file_input *theirs,
- git_merge_file_options *opts)
+ const git_merge_file_input *ancestor,
+ const git_merge_file_input *ours,
+ const git_merge_file_input *theirs,
+ const git_merge_file_options *given_opts)
{
xmparam_t xmparam;
+ mmfile_t ancestor_mmfile = {0}, our_mmfile = {0}, their_mmfile = {0};
mmbuffer_t mmbuffer;
+ git_merge_file_options options = GIT_MERGE_FILE_OPTIONS_INIT;
+ const char *path;
int xdl_result;
int error = 0;
- assert(out && ancestor && ours && theirs);
-
memset(out, 0x0, sizeof(git_merge_file_result));
- if (!GIT_MERGE_FILE_SIDE_EXISTS(ours) || !GIT_MERGE_FILE_SIDE_EXISTS(theirs))
- return 0;
+ merge_file_normalize_opts(&options, given_opts);
memset(&xmparam, 0x0, sizeof(xmparam_t));
- xmparam.ancestor = ancestor->label;
- xmparam.file1 = ours->label;
- xmparam.file2 = theirs->label;
- out->path = merge_file_best_path(ancestor, ours, theirs);
- out->mode = merge_file_best_mode(ancestor, ours, theirs);
+ if (ancestor) {
+ xmparam.ancestor = (options.ancestor_label) ?
+ options.ancestor_label : ancestor->path;
+ ancestor_mmfile.ptr = (char *)ancestor->ptr;
+ ancestor_mmfile.size = ancestor->size;
+ }
- if (opts && opts->favor == GIT_MERGE_FILE_FAVOR_OURS)
+ xmparam.file1 = (options.our_label) ?
+ options.our_label : ours->path;
+ our_mmfile.ptr = (char *)ours->ptr;
+ our_mmfile.size = ours->size;
+
+ xmparam.file2 = (options.their_label) ?
+ options.their_label : theirs->path;
+ their_mmfile.ptr = (char *)theirs->ptr;
+ their_mmfile.size = theirs->size;
+
+ if (options.favor == GIT_MERGE_FILE_FAVOR_OURS)
xmparam.favor = XDL_MERGE_FAVOR_OURS;
- else if (opts && opts->favor == GIT_MERGE_FILE_FAVOR_THEIRS)
+ else if (options.favor == GIT_MERGE_FILE_FAVOR_THEIRS)
xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
- else if (opts && opts->favor == GIT_MERGE_FILE_FAVOR_UNION)
+ else if (options.favor == GIT_MERGE_FILE_FAVOR_UNION)
xmparam.favor = XDL_MERGE_FAVOR_UNION;
- xmparam.level =
- (opts && (opts->flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM)) ?
+ xmparam.level = (options.flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM) ?
XDL_MERGE_ZEALOUS_ALNUM : XDL_MERGE_ZEALOUS;
- if (opts && opts->style == GIT_MERGE_FILE_STYLE_DIFF3)
+ if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3)
xmparam.style = XDL_MERGE_DIFF3;
- if ((xdl_result = xdl_merge(&ancestor->mmfile, &ours->mmfile,
- &theirs->mmfile, &xmparam, &mmbuffer)) < 0) {
+ if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile,
+ &their_mmfile, &xmparam, &mmbuffer)) < 0) {
giterr_set(GITERR_MERGE, "Failed to merge files.");
error = -1;
goto done;
}
+ if ((path = merge_file_best_path(ancestor, ours, theirs)) != NULL &&
+ (out->path = strdup(path)) == NULL) {
+ error = -1;
+ goto done;
+ }
+
out->automergeable = (xdl_result == 0);
- out->data = (unsigned char *)mmbuffer.ptr;
+ out->ptr = (unsigned char *)mmbuffer.ptr;
out->len = mmbuffer.size;
+ out->mode = merge_file_best_mode(ancestor, ours, theirs);
done:
+ if (error < 0)
+ git_merge_file_result_free(out);
+
return error;
}
+
+static git_merge_file_input *git_merge_file__normalize_inputs(
+ git_merge_file_input *out,
+ const git_merge_file_input *given)
+{
+ memcpy(out, given, sizeof(git_merge_file_input));
+
+ if (!out->path)
+ out->path = "file.txt";
+
+ if (!out->mode)
+ out->mode = 0100644;
+
+ return out;
+}
+
+int git_merge_file(
+ git_merge_file_result *out,
+ const git_merge_file_input *ancestor,
+ const git_merge_file_input *ours,
+ const git_merge_file_input *theirs,
+ const git_merge_file_options *options)
+{
+ git_merge_file_input inputs[3] = { {0} };
+
+ assert(out && ours && theirs);
+
+ memset(out, 0x0, sizeof(git_merge_file_result));
+
+ if (ancestor)
+ ancestor = git_merge_file__normalize_inputs(&inputs[0], ancestor);
+
+ ours = git_merge_file__normalize_inputs(&inputs[1], ours);
+ theirs = git_merge_file__normalize_inputs(&inputs[2], theirs);
+
+ return git_merge_file__from_inputs(out, ancestor, ours, theirs, options);
+}
+
+int git_merge_file_from_index(
+ git_merge_file_result *out,
+ git_repository *repo,
+ const git_index_entry *ancestor,
+ const git_index_entry *ours,
+ const git_index_entry *theirs,
+ const git_merge_file_options *options)
+{
+ git_merge_file_input inputs[3] = { {0} },
+ *ancestor_input = NULL, *our_input = NULL, *their_input = NULL;
+ git_odb *odb = NULL;
+ git_odb_object *odb_object[3] = { 0 };
+ int error = 0;
+
+ assert(out && repo && ours && theirs);
+
+ memset(out, 0x0, sizeof(git_merge_file_result));
+
+ if ((error = git_repository_odb(&odb, repo)) < 0)
+ goto done;
+
+ if (ancestor) {
+ if ((error = git_merge_file__input_from_index(
+ &inputs[0], &odb_object[0], odb, ancestor)) < 0)
+ goto done;
+
+ ancestor_input = &inputs[0];
+ }
+
+ if ((error = git_merge_file__input_from_index(
+ &inputs[1], &odb_object[1], odb, ours)) < 0)
+ goto done;
+
+ our_input = &inputs[1];
+
+ if ((error = git_merge_file__input_from_index(
+ &inputs[2], &odb_object[2], odb, theirs)) < 0)
+ goto done;
+
+ their_input = &inputs[2];
+
+ if ((error = git_merge_file__from_inputs(out,
+ ancestor_input, our_input, their_input, options)) < 0)
+ goto done;
+
+done:
+ git_odb_object_free(odb_object[0]);
+ git_odb_object_free(odb_object[1]);
+ git_odb_object_free(odb_object[2]);
+ git_odb_free(odb);
+
+ return error;
+}
+
+void git_merge_file_result_free(git_merge_file_result *result)
+{
+ if (result == NULL)
+ return;
+
+ git__free(result->path);
+
+ /* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */
+ free(result->ptr);
+}
diff --git a/src/merge_file.h b/src/merge_file.h
index 332be490b..263391ee3 100644
--- a/src/merge_file.h
+++ b/src/merge_file.h
@@ -11,82 +11,4 @@
#include "git2/merge.h"
-typedef struct {
- const char *label;
- char *path;
- unsigned int mode;
- mmfile_t mmfile;
-
- git_odb_object *odb_object;
-} git_merge_file_input;
-
-#define GIT_MERGE_FILE_INPUT_INIT {0}
-
-typedef struct {
- bool automergeable;
-
- const char *path;
- int mode;
-
- unsigned char *data;
- size_t len;
-} git_merge_file_result;
-
-#define GIT_MERGE_FILE_RESULT_INIT {0}
-
-typedef enum {
- /* Condense non-alphanumeric regions for simplified diff file */
- GIT_MERGE_FILE_SIMPLIFY_ALNUM = (1 << 0),
-} git_merge_file_flags_t;
-
-typedef enum {
- /* Create standard conflicted merge files */
- GIT_MERGE_FILE_STYLE_MERGE = 0,
-
- /* Create diff3-style files */
- GIT_MERGE_FILE_STYLE_DIFF3 = 1,
-} git_merge_file_style_t;
-
-typedef struct {
- git_merge_file_favor_t favor;
- git_merge_file_flags_t flags;
- git_merge_file_style_t style;
-} git_merge_file_options;
-
-#define GIT_MERGE_FILE_OPTIONS_INIT {0}
-
-int git_merge_file_input_from_index_entry(
- git_merge_file_input *input,
- git_repository *repo,
- const git_index_entry *entry);
-
-int git_merge_file_input_from_diff_file(
- git_merge_file_input *input,
- git_repository *repo,
- const git_diff_file *file);
-
-int git_merge_files(
- git_merge_file_result *out,
- git_merge_file_input *ancestor,
- git_merge_file_input *ours,
- git_merge_file_input *theirs,
- git_merge_file_options *opts);
-
-GIT_INLINE(void) git_merge_file_input_free(git_merge_file_input *input)
-{
- assert(input);
- git__free(input->path);
- git_odb_object_free(input->odb_object);
-}
-
-GIT_INLINE(void) git_merge_file_result_free(git_merge_file_result *filediff)
-{
- if (filediff == NULL)
- return;
-
- /* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */
- if (filediff->data != NULL)
- free(filediff->data);
-}
-
#endif
diff --git a/src/revert.c b/src/revert.c
index 8e84463db..4039ec34c 100644
--- a/src/revert.c
+++ b/src/revert.c
@@ -121,7 +121,7 @@ int git_revert_commit(
git_commit *revert_commit,
git_commit *our_commit,
unsigned int mainline,
- const git_merge_tree_opts *merge_tree_opts)
+ const git_merge_options *merge_opts)
{
git_commit *parent_commit = NULL;
git_tree *parent_tree = NULL, *our_tree = NULL, *revert_tree = NULL;
@@ -152,7 +152,7 @@ int git_revert_commit(
(error = git_commit_tree(&our_tree, our_commit)) < 0)
goto done;
- error = git_merge_trees(out, repo, revert_tree, our_tree, parent_tree, merge_tree_opts);
+ error = git_merge_trees(out, repo, revert_tree, our_tree, parent_tree, merge_opts);
done:
git_tree_free(parent_tree);
@@ -198,7 +198,7 @@ int git_revert(
(error = write_merge_msg(repo, commit_oidstr, commit_msg)) < 0 ||
(error = git_repository_head(&our_ref, repo)) < 0 ||
(error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJ_COMMIT)) < 0 ||
- (error = git_revert_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_tree_opts)) < 0 ||
+ (error = git_revert_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
(error = git_merge__indexes(repo, index_new)) < 0 ||
(error = git_repository_index(&index_repo, repo)) < 0 ||
(error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0)