Commit 6b92c99bcbf32de131754a4f750278f84bf5b766

Edward Thomson 2014-01-19T01:20:25

Don't try to merge binary files

diff --git a/src/checkout.c b/src/checkout.c
index cc49800..f64aa9a 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -70,7 +70,8 @@ typedef struct {
 
 	int name_collision:1,
 		directoryfile:1,
-		one_to_two:1;
+		one_to_two:1,
+		binary:1;
 } checkout_conflictdata;
 
 static int checkout_notify(
@@ -681,6 +682,40 @@ GIT_INLINE(bool) conflict_pathspec_match(
 	return false;
 }
 
+GIT_INLINE(int) checkout_conflict_detect_binary(git_repository *repo, checkout_conflictdata *conflict)
+{
+	git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL;
+	int error = 0;
+
+	if (conflict->ancestor) {
+		if ((error = git_blob_lookup(&ancestor_blob, repo, &conflict->ancestor->oid)) < 0)
+			goto done;
+
+		conflict->binary = git_blob_is_binary(ancestor_blob);
+	}
+
+	if (!conflict->binary && conflict->ours) {
+		if ((error = git_blob_lookup(&our_blob, repo, &conflict->ours->oid)) < 0)
+			goto done;
+
+		conflict->binary = git_blob_is_binary(our_blob);
+	}
+
+	if (!conflict->binary && conflict->theirs) {
+		if ((error = git_blob_lookup(&their_blob, repo, &conflict->theirs->oid)) < 0)
+			goto done;
+
+		conflict->binary = git_blob_is_binary(their_blob);
+	}
+
+done:
+	git_blob_free(ancestor_blob);
+	git_blob_free(our_blob);
+	git_blob_free(their_blob);
+
+	return error;
+}
+
 static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec)
 {
 	git_index_conflict_iterator *iterator = NULL;
@@ -705,6 +740,9 @@ static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, g
 		conflict->ours = ours;
 		conflict->theirs = theirs;
 
+		if ((error = checkout_conflict_detect_binary(data->repo, conflict)) < 0)
+			goto done;
+
 		git_vector_insert(&data->conflicts, conflict);
 	}
 
@@ -1706,6 +1744,7 @@ static int checkout_create_conflicts(checkout_data *data)
 	int error = 0;
 
 	git_vector_foreach(&data->conflicts, i, conflict) {
+
 		/* Both deleted: nothing to do */
 		if (conflict->ours == NULL && conflict->theirs == NULL)
 			error = 0;
@@ -1749,7 +1788,11 @@ static int checkout_create_conflicts(checkout_data *data)
 		else if (S_ISLNK(conflict->theirs->mode))
 			error = checkout_write_entry(data, conflict, conflict->ours);
 
-		else
+		/* If any side is binary, write the ours side */
+		else if (conflict->binary)
+			error = checkout_write_entry(data, conflict, conflict->ours);
+
+		else if (!error)
 			error = checkout_write_merge(data, conflict);
 
 		if (error)
diff --git a/src/merge.c b/src/merge.c
index 3ac9167..124befc 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -551,6 +551,10 @@ static int merge_conflict_resolve_automerge(
 		strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0)
 		return 0;
 
+	/* Reject binary conflicts */
+	if (conflict->binary)
+		return 0;
+
 	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 ||
@@ -1150,6 +1154,44 @@ GIT_INLINE(int) merge_diff_detect_type(
 	return 0;
 }
 
+GIT_INLINE(int) merge_diff_detect_binary(
+	git_repository *repo,
+	git_merge_diff *conflict)
+{
+	git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL;
+	int error = 0;
+
+	if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry)) {
+		if ((error = git_blob_lookup(&ancestor_blob, repo, &conflict->ancestor_entry.oid)) < 0)
+			goto done;
+
+		conflict->binary = git_blob_is_binary(ancestor_blob);
+	}
+
+	if (!conflict->binary &&
+		GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry)) {
+		if ((error = git_blob_lookup(&our_blob, repo, &conflict->our_entry.oid)) < 0)
+			goto done;
+
+		conflict->binary = git_blob_is_binary(our_blob);
+	}
+
+	if (!conflict->binary &&
+		GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)) {
+		if ((error = git_blob_lookup(&their_blob, repo, &conflict->their_entry.oid)) < 0)
+			goto done;
+
+		conflict->binary = git_blob_is_binary(their_blob);
+	}
+
+done:
+	git_blob_free(ancestor_blob);
+	git_blob_free(our_blob);
+	git_blob_free(their_blob);
+
+	return error;
+}
+
 GIT_INLINE(int) index_entry_dup(
 	git_index_entry *out,
 	git_pool *pool,
@@ -1221,6 +1263,7 @@ static int merge_diff_list_insert_conflict(
 	if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL ||
 		merge_diff_detect_type(conflict) < 0 ||
 		merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 ||
+		merge_diff_detect_binary(diff_list->repo, conflict) < 0 ||
 		git_vector_insert(&diff_list->conflicts, conflict) < 0)
 		return -1;
 
diff --git a/src/merge.h b/src/merge.h
index b240f6c..dda0235 100644
--- a/src/merge.h
+++ b/src/merge.h
@@ -106,6 +106,8 @@ typedef struct {
 
 	git_index_entry their_entry;
 	git_delta_t their_status;
+
+	int binary:1;
 } git_merge_diff;
 
 /** Internal structure for merge inputs */
diff --git a/tests/merge/workdir/simple.c b/tests/merge/workdir/simple.c
index 2142fc4..d4f387a 100644
--- a/tests/merge/workdir/simple.c
+++ b/tests/merge/workdir/simple.c
@@ -489,3 +489,40 @@ void test_merge_workdir_simple__unrelated_with_conflicts(void)
 	git_merge_result_free(result);
 }
 
+void test_merge_workdir_simple__binary(void)
+{
+	git_oid our_oid, their_oid, our_file_oid;
+	git_commit *our_commit;
+	git_merge_head *their_head;
+	git_merge_result *result;
+	const git_index_entry *binary_entry;
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+	struct merge_index_entry merge_index_entries[] = {
+		{ 0100644, "1c51d885170f57a0c4e8c69ff6363d91a5b51f85", 1, "binary" },
+		{ 0100644, "23ed141a6ae1e798b2f721afedbe947c119111ba", 2, "binary" },
+		{ 0100644, "836b8b82b26cab22eaaed8820877c76d6c8bca19", 3, "binary" },
+	};
+
+	cl_git_pass(git_oid_fromstr(&our_oid, "cc338e4710c9b257106b8d16d82f86458d5beaf1"));
+	cl_git_pass(git_oid_fromstr(&their_oid, "ad01aebfdf2ac13145efafe3f9fcf798882f1730"));
+
+	cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid));
+	cl_git_pass(git_reset(repo, (git_object *)our_commit, GIT_RESET_HARD));
+
+	cl_git_pass(git_merge_head_from_oid(&their_head, repo, &their_oid));
+
+	cl_git_pass(git_merge(&result, repo, (const git_merge_head **)&their_head, 1, &opts));
+
+	cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
+
+	cl_git_pass(git_index_add_bypath(repo_index, "binary"));
+	cl_assert((binary_entry = git_index_get_bypath(repo_index, "binary", 0)) != NULL);
+
+	cl_git_pass(git_oid_fromstr(&our_file_oid, "23ed141a6ae1e798b2f721afedbe947c119111ba"));
+	cl_assert(git_oid_cmp(&binary_entry->oid, &our_file_oid) == 0);
+
+	git_merge_head_free(their_head);
+	git_merge_result_free(result);
+	git_commit_free(our_commit);
+}
diff --git a/tests/resources/merge-resolve/.gitted/objects/11/aeee27ac45a8402c2fd5b875d66dd844e5df00 b/tests/resources/merge-resolve/.gitted/objects/11/aeee27ac45a8402c2fd5b875d66dd844e5df00
new file mode 100644
index 0000000..90e729f
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/11/aeee27ac45a8402c2fd5b875d66dd844e5df00 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/1c/51d885170f57a0c4e8c69ff6363d91a5b51f85 b/tests/resources/merge-resolve/.gitted/objects/1c/51d885170f57a0c4e8c69ff6363d91a5b51f85
new file mode 100644
index 0000000..9a21e26
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/1c/51d885170f57a0c4e8c69ff6363d91a5b51f85 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/23/ed141a6ae1e798b2f721afedbe947c119111ba b/tests/resources/merge-resolve/.gitted/objects/23/ed141a6ae1e798b2f721afedbe947c119111ba
new file mode 100644
index 0000000..06dee3b
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/23/ed141a6ae1e798b2f721afedbe947c119111ba differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/34/8dcd41e2b467991578e92bedd16971b877ef1e b/tests/resources/merge-resolve/.gitted/objects/34/8dcd41e2b467991578e92bedd16971b877ef1e
new file mode 100644
index 0000000..fd61b6c
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/34/8dcd41e2b467991578e92bedd16971b877ef1e differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6e/3b9eb35214d4e31ed5789afc7d520ac798ce55 b/tests/resources/merge-resolve/.gitted/objects/6e/3b9eb35214d4e31ed5789afc7d520ac798ce55
new file mode 100644
index 0000000..c6100cb
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/6e/3b9eb35214d4e31ed5789afc7d520ac798ce55 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/83/6b8b82b26cab22eaaed8820877c76d6c8bca19 b/tests/resources/merge-resolve/.gitted/objects/83/6b8b82b26cab22eaaed8820877c76d6c8bca19
new file mode 100644
index 0000000..99f8286
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/83/6b8b82b26cab22eaaed8820877c76d6c8bca19 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ad/01aebfdf2ac13145efafe3f9fcf798882f1730 b/tests/resources/merge-resolve/.gitted/objects/ad/01aebfdf2ac13145efafe3f9fcf798882f1730
new file mode 100644
index 0000000..ae3ef8c
Binary files /dev/null and b/tests/resources/merge-resolve/.gitted/objects/ad/01aebfdf2ac13145efafe3f9fcf798882f1730 differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/cc/338e4710c9b257106b8d16d82f86458d5beaf1 b/tests/resources/merge-resolve/.gitted/objects/cc/338e4710c9b257106b8d16d82f86458d5beaf1
new file mode 100644
index 0000000..85b3b81
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cc/338e4710c9b257106b8d16d82f86458d5beaf1
@@ -0,0 +1,2 @@
+x��K!]s�����3`b�o�����1��gpW�E�G��m��x]�������6d
+���ea�Ή碵z�.���Dv	�[�h����D�[�Jﱶw����X[��.2�nu���VƉZ�ڳF�!x8�8���G���P��P_�?KN
\ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/d7/308cc367b2cc23f710834ec1fd8ffbacf1b460 b/tests/resources/merge-resolve/.gitted/objects/d7/308cc367b2cc23f710834ec1fd8ffbacf1b460
new file mode 100644
index 0000000..b02cda4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d7/308cc367b2cc23f710834ec1fd8ffbacf1b460
@@ -0,0 +1 @@
+x��K� @]s����)Ё�7���]�I(�x{��^��,�nлSo�`�/�X�)ٙB@G�Ô��a�D� ���4x�wl���Cv��?��-79d�,�h�F4����Z
;Ɲ�H����}���=�
\ No newline at end of file