Commit c629d2a1 by Edward Thomson

merge: support zdiff3 conflict styles

parent 1458fb56
...@@ -183,6 +183,9 @@ typedef enum { ...@@ -183,6 +183,9 @@ typedef enum {
*/ */
GIT_CHECKOUT_DRY_RUN = (1u << 24), GIT_CHECKOUT_DRY_RUN = (1u << 24),
/** Include common ancestor data in zdiff3 format for conflicts */
GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3 = (1u << 25),
/** /**
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
*/ */
......
...@@ -159,7 +159,10 @@ typedef enum { ...@@ -159,7 +159,10 @@ typedef enum {
GIT_MERGE_FILE_DIFF_PATIENCE = (1 << 6), GIT_MERGE_FILE_DIFF_PATIENCE = (1 << 6),
/** Take extra time to find minimal diff */ /** Take extra time to find minimal diff */
GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7) GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7),
/** Create zdiff3 ("zealous diff3")-style files */
GIT_MERGE_FILE_STYLE_ZDIFF3 = (1 << 8)
} git_merge_file_flag_t; } git_merge_file_flag_t;
#define GIT_MERGE_CONFLICT_MARKER_SIZE 7 #define GIT_MERGE_CONFLICT_MARKER_SIZE 7
......
...@@ -2070,6 +2070,9 @@ static int checkout_write_merge( ...@@ -2070,6 +2070,9 @@ static int checkout_write_merge(
if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3) if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)
opts.flags |= GIT_MERGE_FILE_STYLE_DIFF3; opts.flags |= GIT_MERGE_FILE_STYLE_DIFF3;
if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3)
opts.flags |= GIT_MERGE_FILE_STYLE_ZDIFF3;
opts.ancestor_label = data->opts.ancestor_label ? opts.ancestor_label = data->opts.ancestor_label ?
data->opts.ancestor_label : "ancestor"; data->opts.ancestor_label : "ancestor";
opts.our_label = data->opts.our_label ? opts.our_label = data->opts.our_label ?
...@@ -2493,6 +2496,8 @@ static int checkout_data_init( ...@@ -2493,6 +2496,8 @@ static int checkout_data_init(
data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_MERGE; data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_MERGE;
else if (strcmp(conflict_style->value, "diff3") == 0) else if (strcmp(conflict_style->value, "diff3") == 0)
data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3; data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3;
else if (strcmp(conflict_style->value, "zdiff3") == 0)
data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3;
else { else {
git_error_set(GIT_ERROR_CHECKOUT, "unknown style '%s' given for 'merge.conflictstyle'", git_error_set(GIT_ERROR_CHECKOUT, "unknown style '%s' given for 'merge.conflictstyle'",
conflict_style->value); conflict_style->value);
......
...@@ -123,6 +123,8 @@ static int merge_file__xdiff( ...@@ -123,6 +123,8 @@ static int merge_file__xdiff(
if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3) if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3)
xmparam.style = XDL_MERGE_DIFF3; xmparam.style = XDL_MERGE_DIFF3;
if (options.flags & GIT_MERGE_FILE_STYLE_ZDIFF3)
xmparam.style = XDL_MERGE_ZEALOUS_DIFF3;
if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE) if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE)
xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE; xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE;
......
...@@ -36,6 +36,15 @@ ...@@ -36,6 +36,15 @@
"this file is changed in branch and master\n" \ "this file is changed in branch and master\n" \
">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n" ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n"
#define CONFLICTING_ZDIFF3_FILE \
"<<<<<<< HEAD\n" \
"this file is changed in master and branch\n" \
"||||||| initial\n" \
"this file is a conflict\n" \
"=======\n" \
"this file is changed in branch and master\n" \
">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n"
#define CONFLICTING_UNION_FILE \ #define CONFLICTING_UNION_FILE \
"this file is changed in master and branch\n" \ "this file is changed in master and branch\n" \
"this file is changed in branch and master\n" "this file is changed in branch and master\n"
......
...@@ -424,3 +424,42 @@ void test_merge_files__crlf_conflict_markers_for_crlf_files(void) ...@@ -424,3 +424,42 @@ void test_merge_files__crlf_conflict_markers_for_crlf_files(void)
cl_assert(memcmp(expected_diff3, result.ptr, expected_len) == 0); cl_assert(memcmp(expected_diff3, result.ptr, expected_len) == 0);
git_merge_file_result_free(&result); git_merge_file_result_free(&result);
} }
void test_merge_files__conflicts_in_zdiff3(void)
{
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_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
git_merge_file_result result = {0};
const char *expected_zdiff3 =
"1,\nfoo,\nbar,\n" \
"<<<<<<< file.txt\n" \
"||||||| file.txt\n# add more here\n" \
"=======\nquux,\nwoot,\n" \
">>>>>>> file.txt\nbaz,\n3,\n";
size_t expected_zdiff3_len = strlen(expected_zdiff3);
ancestor.ptr = "1,\n# add more here\n3,\n";
ancestor.size = strlen(ancestor.ptr);
ancestor.path = "file.txt";
ancestor.mode = 0100644;
ours.ptr = "1,\nfoo,\nbar,\nbaz,\n3,\n";
ours.size = strlen(ours.ptr);
ours.path = "file.txt";
ours.mode = 0100644;
theirs.ptr = "1,\nfoo,\nbar,\nquux,\nwoot,\nbaz,\n3,\n";
theirs.size = strlen(theirs.ptr);
theirs.path = "file.txt";
theirs.mode = 0100644;
opts.flags |= GIT_MERGE_FILE_STYLE_ZDIFF3;
cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
cl_assert_equal_i(0, result.automergeable);
cl_assert_equal_i(expected_zdiff3_len, result.len);
cl_assert(memcmp(expected_zdiff3, result.ptr, expected_zdiff3_len) == 0);
git_merge_file_result_free(&result);
}
...@@ -323,6 +323,42 @@ void test_merge_workdir_simple__diff3(void) ...@@ -323,6 +323,42 @@ void test_merge_workdir_simple__diff3(void)
cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3)); cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
} }
void test_merge_workdir_simple__zdiff3(void)
{
git_str conflicting_buf = GIT_STR_INIT;
struct merge_index_entry merge_index_entries[] = {
ADDED_IN_MASTER_INDEX_ENTRY,
AUTOMERGEABLE_INDEX_ENTRY,
CHANGED_IN_BRANCH_INDEX_ENTRY,
CHANGED_IN_MASTER_INDEX_ENTRY,
{ 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
{ 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
UNCHANGED_INDEX_ENTRY,
};
struct merge_reuc_entry merge_reuc_entries[] = {
AUTOMERGEABLE_REUC_ENTRY,
REMOVED_IN_BRANCH_REUC_ENTRY,
REMOVED_IN_MASTER_REUC_ENTRY
};
set_core_autocrlf_to(repo, false);
merge_simple_branch(0, GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3);
cl_git_pass(git_futils_readbuffer(&conflicting_buf,
TEST_REPO_PATH "/conflicting.txt"));
cl_assert_equal_s(CONFLICTING_ZDIFF3_FILE, conflicting_buf.ptr);
git_str_dispose(&conflicting_buf);
cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
}
void test_merge_workdir_simple__union(void) void test_merge_workdir_simple__union(void)
{ {
git_str conflicting_buf = GIT_STR_INIT; git_str conflicting_buf = GIT_STR_INIT;
...@@ -436,6 +472,48 @@ void test_merge_workdir_simple__diff3_from_config(void) ...@@ -436,6 +472,48 @@ void test_merge_workdir_simple__diff3_from_config(void)
git_config_free(config); git_config_free(config);
} }
void test_merge_workdir_simple__zdiff3_from_config(void)
{
git_config *config;
git_str conflicting_buf = GIT_STR_INIT;
struct merge_index_entry merge_index_entries[] = {
ADDED_IN_MASTER_INDEX_ENTRY,
AUTOMERGEABLE_INDEX_ENTRY,
CHANGED_IN_BRANCH_INDEX_ENTRY,
CHANGED_IN_MASTER_INDEX_ENTRY,
{ 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
{ 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
UNCHANGED_INDEX_ENTRY,
};
struct merge_reuc_entry merge_reuc_entries[] = {
AUTOMERGEABLE_REUC_ENTRY,
REMOVED_IN_BRANCH_REUC_ENTRY,
REMOVED_IN_MASTER_REUC_ENTRY
};
cl_git_pass(git_repository_config(&config, repo));
cl_git_pass(git_config_set_string(config, "merge.conflictstyle", "zdiff3"));
set_core_autocrlf_to(repo, false);
merge_simple_branch(0, 0);
cl_git_pass(git_futils_readbuffer(&conflicting_buf,
TEST_REPO_PATH "/conflicting.txt"));
cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_ZDIFF3_FILE) == 0);
git_str_dispose(&conflicting_buf);
cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
git_config_free(config);
}
void test_merge_workdir_simple__merge_overrides_config(void) void test_merge_workdir_simple__merge_overrides_config(void)
{ {
git_config *config; git_config *config;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment