Commit 8683d31f080eb12fe769ab2363165ec17562c840

Edward Thomson 2015-10-22T14:39:20

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.

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));
+}
+