merge: add GIT_MERGE_TREE_FAIL_ON_CONFLICT Provide a new merge option, GIT_MERGE_TREE_FAIL_ON_CONFLICT, which will stop on the first conflict and fail the merge operation with GIT_EMERGECONFLICT.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 4698366..1b528cf 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -49,6 +49,7 @@ typedef enum {
GIT_EINVALID = -21, /**< Invalid operation or input */
GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */
GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */
+ GIT_EMERGECONFLICT = -24, /**< A merge conflict exists and cannot continue */
GIT_PASSTHROUGH = -30, /**< Internal only */
GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
diff --git a/include/git2/merge.h b/include/git2/merge.h
index 5fef452..ced9e51 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -72,6 +72,13 @@ typedef enum {
* the ability to merge between a modified and renamed file.
*/
GIT_MERGE_TREE_FIND_RENAMES = (1 << 0),
+
+ /**
+ * If a conflict occurs, exit immediately instead of attempting to
+ * continue resolving conflicts. The merge operation will fail with
+ * GIT_EMERGECONFLICT and no index will be returned.
+ */
+ GIT_MERGE_TREE_FAIL_ON_CONFLICT = (1 << 1),
} git_merge_tree_flag_t;
/**
diff --git a/src/merge.c b/src/merge.c
index 930457b..3bed0fd 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -1701,8 +1701,15 @@ int git_merge__iterators(
if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.file_favor, opts.file_flags)) < 0)
goto done;
- if (!resolved)
+ if (!resolved) {
+ if ((opts.tree_flags & GIT_MERGE_TREE_FAIL_ON_CONFLICT)) {
+ giterr_set(GITERR_MERGE, "merge conflicts exist");
+ error = GIT_EMERGECONFLICT;
+ goto done;
+ }
+
git_vector_insert(&diff_list->conflicts, conflict);
+ }
}
if (!given_opts || !given_opts->metric)
diff --git a/tests/merge/merge_helpers.c b/tests/merge/merge_helpers.c
index f814714..986a365 100644
--- a/tests/merge/merge_helpers.c
+++ b/tests/merge/merge_helpers.c
@@ -40,7 +40,7 @@ int merge_trees_from_branches(
cl_git_pass(git_commit_tree(&our_tree, our_commit));
cl_git_pass(git_commit_tree(&their_tree, their_commit));
- cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts));
+ error = git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts);
git_buf_free(&branch_buf);
git_tree_free(our_tree);
@@ -50,7 +50,7 @@ int merge_trees_from_branches(
git_commit_free(their_commit);
git_commit_free(ancestor_commit);
- return 0;
+ return error;
}
int merge_commits_from_branches(
@@ -61,6 +61,7 @@ int merge_commits_from_branches(
git_commit *our_commit, *their_commit;
git_oid our_oid, their_oid;
git_buf branch_buf = GIT_BUF_INIT;
+ int error;
git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name);
cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr));
@@ -71,13 +72,13 @@ int merge_commits_from_branches(
cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr));
cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid));
- cl_git_pass(git_merge_commits(index, repo, our_commit, their_commit, opts));
+ error = git_merge_commits(index, repo, our_commit, their_commit, opts);
git_buf_free(&branch_buf);
git_commit_free(our_commit);
git_commit_free(their_commit);
- return 0;
+ return error;
}
int merge_branches(git_repository *repo,
diff --git a/tests/merge/trees/commits.c b/tests/merge/trees/commits.c
index c4e4709..2e3c457 100644
--- a/tests/merge/trees/commits.c
+++ b/tests/merge/trees/commits.c
@@ -94,7 +94,6 @@ void test_merge_trees_commits__no_ancestor(void)
git_index_free(index);
}
-
void test_merge_trees_commits__df_conflict(void)
{
git_index *index;
@@ -129,3 +128,20 @@ void test_merge_trees_commits__df_conflict(void)
git_index_free(index);
}
+
+void test_merge_trees_commits__fail_on_conflict(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ opts.tree_flags |= GIT_MERGE_TREE_FAIL_ON_CONFLICT;
+
+ cl_git_fail_with(GIT_EMERGECONFLICT,
+ merge_trees_from_branches(&index, repo, "df_side1", "df_side2", &opts));
+
+ cl_git_fail_with(GIT_EMERGECONFLICT,
+ merge_commits_from_branches(&index, repo, "master", "unrelated", &opts));
+ cl_git_fail_with(GIT_EMERGECONFLICT,
+ merge_commits_from_branches(&index, repo, "master", "branch", &opts));
+}
+