Author: stsp
Date: Thu Apr 21 14:04:08 2016
New Revision: 1740320
URL: http://svn.apache.org/viewvc?rev=1740320&view=rev
Log:
Add a conflict resolution option for dir/dir "incoming add vs local
obstruction upon merge". This option merges the two directories.
Again, the implementation is not atomic, yet.
And it doesn't always work as expected.
Add two XFAIL regression tests which illustrate known problems.
* subversion/include/svn_client.h
(svn_client_conflict_option_merge_incoming_added_dir_merge): New option ID.
* subversion/libsvn_client/conflicts.c
(resolve_merge_incoming_added_dir_merge,
configure_option_merge_incoming_added_dir_merge): New resolution option.
(svn_client_conflict_tree_get_resolution_options): Configure the new option.
* subversion/svn/conflict-callbacks.c
(builtin_resolver_options): Map a menu key to the new option.
* subversion/tests/libsvn_client/conflicts-test.c
(create_wc_with_dir_add_vs_dir_add_merge_conflict): Expand to cover more
test scenarios.
(test_option_merge_incoming_added_dir_merge,
test_option_merge_incoming_added_dir_merge2,
test_option_merge_incoming_added_dir_merge3, test_list): New tests.
Modified:
subversion/trunk/subversion/include/svn_client.h
subversion/trunk/subversion/libsvn_client/conflicts.c
subversion/trunk/subversion/svn/conflict-callbacks.c
subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c
Modified: subversion/trunk/subversion/include/svn_client.h
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1740320&r1=1740319&r2=1740320&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Thu Apr 21 14:04:08 2016
@@ -4414,6 +4414,9 @@ typedef enum svn_client_conflict_option_
svn_client_conflict_option_merge_incoming_added_file_replace,
svn_client_conflict_option_merge_incoming_added_file_replace_and_merge,
+ /* Options for incoming dir add vs local dir 'obstruction' on merge. */
+ svn_client_conflict_option_merge_incoming_added_dir_merge,
+
} svn_client_conflict_option_id_t;
/**
Modified: subversion/trunk/subversion/libsvn_client/conflicts.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/conflicts.c?rev=1740320&r1=1740319&r2=1740320&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/conflicts.c (original)
+++ subversion/trunk/subversion/libsvn_client/conflicts.c Thu Apr 21 14:04:08
2016
@@ -4013,6 +4013,112 @@ resolve_merge_incoming_added_file_replac
scratch_pool));
}
+/* Implements conflict_option_resolve_func_t. */
+static svn_error_t *
+resolve_merge_incoming_added_dir_merge(svn_client_conflict_option_t *option,
+ svn_client_conflict_t *conflict,
+ apr_pool_t *scratch_pool)
+{
+ const char *repos_root_url;
+ const char *repos_uuid;
+ const char *incoming_new_repos_relpath;
+ svn_revnum_t incoming_new_pegrev;
+ const char *local_abspath;
+ const char *lock_abspath;
+ svn_client_ctx_t *ctx = conflict->ctx;
+ struct conflict_tree_incoming_add_details *details;
+ svn_client__conflict_report_t *conflict_report;
+ const char *source1;
+ svn_opt_revision_t revision1;
+ const char *source2;
+ svn_opt_revision_t revision2;
+ svn_error_t *err;
+
+ local_abspath = svn_client_conflict_get_local_abspath(conflict);
+
+ details = conflict->tree_conflict_incoming_details;
+ if (details == NULL)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Conflict resolution option '%d' requires "
+ "details for tree conflict at '%s' to be "
+ "fetched from the repository."),
+ option->id,
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
+
+ /* Set up merge sources to merge the entire incoming added directory tree. */
+ SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, &repos_uuid,
+ conflict, scratch_pool,
+ scratch_pool));
+ source1 = svn_path_url_add_component2(repos_root_url,
+ details->repos_relpath,
+ scratch_pool);
+ revision1.kind = svn_opt_revision_number;
+ revision1.value.number = details->added_rev;
+ SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+ &incoming_new_repos_relpath, &incoming_new_pegrev,
+ NULL, conflict, scratch_pool, scratch_pool));
+ source2 = svn_path_url_add_component2(repos_root_url,
+ incoming_new_repos_relpath,
+ scratch_pool);
+ revision2.kind = svn_opt_revision_number;
+ revision2.value.number = incoming_new_pegrev;
+
+ /* ### The following WC modifications should be atomic. */
+ SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
+ local_abspath,
+ scratch_pool, scratch_pool));
+
+ /* Resolve to current working copy state. The merge requires this. */
+ err = svn_wc__del_tree_conflict(ctx->wc_ctx, local_abspath, scratch_pool);
+ if (err)
+ return svn_error_compose_create(err,
+ svn_wc__release_write_lock(ctx->wc_ctx,
+ lock_abspath,
+ scratch_pool));
+
+ /* ### Should we do anything about mergeinfo? We need to run a no-ancestry
+ * ### merge to get a useful result because mergeinfo-aware merges may split
+ * ### this merge into several ranges and then abort early as soon as a
+ * ### conflict occurs (which will happen invariably when merging unrelated
+ * ### trees). The original merge which raised the tree conflict in the
+ * ### first place created mergeinfo which also describes this merge,
+ * ### unless 1) the working copy's mergeinfo was changed since, or
+ * ### 2) the newly added directory's history has location segments with
+ * ### paths outside the original merge source's natural history's path
+ * ### (see the test_option_merge_incoming_added_dir_merge3() test). */
+ err = svn_client__merge_locked(&conflict_report,
+ source1, &revision1,
+ source2, &revision2,
+ local_abspath, svn_depth_infinity,
+ TRUE, TRUE, /* do a no-ancestry merge */
+ FALSE, FALSE, FALSE,
+ TRUE, /* Allow mixed-rev just in case, since
+ * conflict victims can't be updated to
+ * straighten out mixed-rev trees. */
+ NULL, ctx, scratch_pool, scratch_pool);
+
+ err = svn_error_compose_create(err,
+ svn_client__make_merge_conflict_error(
+ conflict_report, scratch_pool));
+ err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
+ lock_abspath,
+
scratch_pool));
+ svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
+ SVN_ERR(err);
+
+ if (ctx->notify_func2)
+ ctx->notify_func2(ctx->notify_baton2,
+ svn_wc_create_notify(local_abspath,
+ svn_wc_notify_resolved_tree,
+ scratch_pool),
+ scratch_pool);
+
+ conflict->resolution_tree = svn_client_conflict_option_get_id(option);
+
+ return SVN_NO_ERROR;
+}
+
/* Resolver options for a text conflict */
static const svn_client_conflict_option_t text_conflict_options[] =
{
@@ -4591,6 +4697,59 @@ configure_option_merge_incoming_added_fi
return SVN_NO_ERROR;
}
+/* Configure 'incoming added dir merge' resolution option for a tree
+ * conflict. */
+static svn_error_t *
+configure_option_merge_incoming_added_dir_merge(svn_client_conflict_t
*conflict,
+ apr_array_header_t *options,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc_operation_t operation;
+ svn_wc_conflict_action_t incoming_change;
+ svn_wc_conflict_reason_t local_change;
+ svn_node_kind_t victim_node_kind;
+ const char *incoming_new_repos_relpath;
+ svn_revnum_t incoming_new_pegrev;
+ svn_node_kind_t incoming_new_kind;
+
+ operation = svn_client_conflict_get_operation(conflict);
+ incoming_change = svn_client_conflict_get_incoming_change(conflict);
+ local_change = svn_client_conflict_get_local_change(conflict);
+ victim_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
+ SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+ &incoming_new_repos_relpath, &incoming_new_pegrev,
+ &incoming_new_kind, conflict, scratch_pool,
+ scratch_pool));
+
+ if (operation == svn_wc_operation_merge &&
+ victim_node_kind == svn_node_dir &&
+ incoming_new_kind == svn_node_dir &&
+ incoming_change == svn_wc_conflict_action_add &&
+ local_change == svn_wc_conflict_reason_obstructed)
+ {
+ svn_client_conflict_option_t *option;
+ const char *wcroot_abspath;
+
+ option = apr_pcalloc(options->pool, sizeof(*option));
+ option->id = svn_client_conflict_option_merge_incoming_added_dir_merge;
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, conflict->ctx->wc_ctx,
+ conflict->local_abspath, scratch_pool,
+ scratch_pool));
+ option->description =
+ apr_psprintf(options->pool, _("merge '^/%s@%ld' into '%s'"),
+ incoming_new_repos_relpath, incoming_new_pegrev,
+ svn_dirent_local_style(
+ svn_dirent_skip_ancestor(wcroot_abspath,
+ conflict->local_abspath),
+ scratch_pool));
+ option->conflict = conflict;
+ option->do_resolve_func = resolve_merge_incoming_added_dir_merge;
+ APR_ARRAY_PUSH(options, const svn_client_conflict_option_t *) = option;
+ }
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_client_conflict_tree_get_resolution_options(apr_array_header_t **options,
svn_client_conflict_t
*conflict,
@@ -4628,6 +4787,8 @@ svn_client_conflict_tree_get_resolution_
conflict, *options, scratch_pool));
SVN_ERR(configure_option_merge_incoming_added_file_replace_and_merge(
conflict, *options, scratch_pool));
+ SVN_ERR(configure_option_merge_incoming_added_dir_merge(conflict, *options,
+ scratch_pool));
return SVN_NO_ERROR;
}
Modified: subversion/trunk/subversion/svn/conflict-callbacks.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/conflict-callbacks.c?rev=1740320&r1=1740319&r2=1740320&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/conflict-callbacks.c (original)
+++ subversion/trunk/subversion/svn/conflict-callbacks.c Thu Apr 21 14:04:08
2016
@@ -431,6 +431,10 @@ static const resolver_option_t builtin_r
{ "M", N_("replace my file with incoming file and merge the files"), NULL,
svn_client_conflict_option_merge_incoming_added_file_replace_and_merge },
+ /* Options for incoming dir add vs local dir add upon merge. */
+ { "m", N_("merge the directories"), NULL,
+ svn_client_conflict_option_merge_incoming_added_dir_merge },
+
{ NULL }
};
Modified: subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c?rev=1740320&r1=1740319&r2=1740320&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c Thu Apr 21
14:04:08 2016
@@ -457,7 +457,9 @@ static const char *new_dir_name = "newdi
/* A helper function which prepares a working copy for the tests below. */
static svn_error_t *
-create_wc_with_dir_add_vs_dir_add_merge_conflict(svn_test__sandbox_t *b)
+create_wc_with_dir_add_vs_dir_add_merge_conflict(svn_test__sandbox_t *b,
+ svn_boolean_t file_change,
+ svn_boolean_t with_move)
{
static const char *new_dir_path;
static const char *new_file_path;
@@ -468,6 +470,7 @@ create_wc_with_dir_add_vs_dir_add_merge_
struct status_baton sb;
svn_client_conflict_t *conflict;
svn_boolean_t tree_conflicted;
+ const char *move_src_path;
SVN_ERR(sbox_add_and_commit_greek_tree(b));
@@ -477,16 +480,40 @@ create_wc_with_dir_add_vs_dir_add_merge_
/* Add new directories on trunk and the branch which occupy the same path
* but have different content and properties. */
- new_dir_path = svn_relpath_join(trunk_path, new_dir_name, b->pool);
+ if (with_move)
+ {
+ /* History starts at ^/newdir.orig, outside of ^/A (the "trunk").
+ * Then a move to ^/A/newdir causes a collision. */
+ move_src_path = apr_pstrcat(b->pool, new_dir_name, ".orig", SVN_VA_NULL);
+ new_dir_path = move_src_path;
+ }
+ else
+ {
+ new_dir_path = svn_relpath_join(trunk_path, new_dir_name, b->pool);
+ move_src_path = NULL;
+ }
+
SVN_ERR(sbox_wc_mkdir(b, new_dir_path));
- new_file_path = svn_relpath_join(trunk_path,
- svn_relpath_join(new_dir_name,
- new_file_name, b->pool),
- b->pool);
+ new_file_path = svn_relpath_join(new_dir_path, new_file_name, b->pool);
SVN_ERR(sbox_file_write(b, new_file_path,
"This is a new file on the trunk\n"));
SVN_ERR(sbox_wc_add(b, new_file_path));
+ SVN_ERR(sbox_wc_propset(b, "prop", propval_trunk, new_file_path));
SVN_ERR(sbox_wc_commit(b, ""));
+ if (file_change)
+ {
+ SVN_ERR(sbox_file_write(b, new_file_path,
+ "This is a change to the new file\n"));
+ SVN_ERR(sbox_wc_commit(b, ""));
+ }
+ if (with_move)
+ {
+ /* Now move the new directory to the colliding path. */
+ new_dir_path = svn_relpath_join(trunk_path, new_dir_name, b->pool);
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ sbox_wc_move(b, move_src_path, new_dir_path);
+ SVN_ERR(sbox_wc_commit(b, ""));
+ }
new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool);
SVN_ERR(sbox_wc_mkdir(b, new_dir_path));
new_file_path = svn_relpath_join(branch_path,
@@ -499,6 +526,7 @@ create_wc_with_dir_add_vs_dir_add_merge_
* run with sleep for timestamps disabled. */
"This is a new file on the branch\n"));
SVN_ERR(sbox_wc_add(b, new_file_path));
+ SVN_ERR(sbox_wc_propset(b, "prop", propval_branch, new_file_path));
SVN_ERR(sbox_wc_commit(b, ""));
/* Run a merge from the trunk to the branch. */
@@ -568,7 +596,7 @@ test_option_merge_incoming_added_dir_ign
SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_ignore",
opts, pool));
- SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b));
+ SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -615,6 +643,304 @@ test_option_merge_incoming_added_dir_ign
return SVN_NO_ERROR;
}
+/* This test currently fails to meet expectations. Our merge code doesn't
+ * support a merge of files which were added in the same revision as their
+ * parent directory and were not modified since. */
+static svn_error_t *
+test_option_merge_incoming_added_dir_merge(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ const char *new_dir_path;
+ const char *new_file_path;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ struct status_baton sb;
+ struct svn_client_status_t *status;
+ svn_opt_revision_t opt_rev;
+ const svn_string_t *propval;
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+
+ SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_merge",
+ opts, pool));
+
+ SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool);
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path),
+ ctx, b->pool, b->pool));
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, b->pool));
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict,
+ svn_client_conflict_option_merge_incoming_added_dir_merge,
+ b->pool));
+
+ /* Ensure that the directory has the expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_dir_path),
+ &opt_rev, svn_depth_unknown, TRUE, TRUE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_dir);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_normal);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none);
+ SVN_TEST_ASSERT(!status->copied);
+ SVN_TEST_ASSERT(!status->switched);
+ SVN_TEST_ASSERT(!status->file_external);
+ SVN_TEST_ASSERT(status->moved_from_abspath == NULL);
+ SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
+
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path),
+ ctx, b->pool, b->pool));
+
+ /* The directory should not be in conflict. */
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, b->pool, b->pool));
+ SVN_TEST_ASSERT(!text_conflicted &&
+ props_conflicted->nelts == 0 &&
+ !tree_conflicted);
+
+ /* XFAIL: Currently, no text conflict is raised since the file is not merged.
+ * We should have a text conflict in the file. */
+ new_file_path = svn_relpath_join(branch_path,
+ svn_relpath_join(new_dir_name,
+ new_file_name, b->pool),
+ b->pool);
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path),
+ ctx, b->pool, b->pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, b->pool, b->pool));
+ SVN_TEST_ASSERT(text_conflicted &&
+ props_conflicted->nelts == 0 &&
+ !tree_conflicted);
+
+ /* Verify the file's merged property value. */
+ SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx,
+ sbox_wc_path(b, new_file_path),
+ "prop", b->pool, b->pool));
+ SVN_TEST_STRING_ASSERT(propval->data, propval_trunk);
+
+ return SVN_NO_ERROR;
+}
+
+/* Same test as above, but with an additional file change on the trunk
+ * which makes resolution work as expected. */
+static svn_error_t *
+test_option_merge_incoming_added_dir_merge2(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ const char *new_dir_path;
+ const char *new_file_path;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ struct status_baton sb;
+ struct svn_client_status_t *status;
+ svn_opt_revision_t opt_rev;
+ const svn_string_t *propval;
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+
+ SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_merge2",
+ opts, pool));
+
+ SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, TRUE, FALSE));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool);
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path),
+ ctx, b->pool, b->pool));
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, b->pool));
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict,
+ svn_client_conflict_option_merge_incoming_added_dir_merge,
+ b->pool));
+
+ /* Ensure that the directory has the expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_dir_path),
+ &opt_rev, svn_depth_unknown, TRUE, TRUE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_dir);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_normal);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none);
+ SVN_TEST_ASSERT(!status->copied);
+ SVN_TEST_ASSERT(!status->switched);
+ SVN_TEST_ASSERT(!status->file_external);
+ SVN_TEST_ASSERT(status->moved_from_abspath == NULL);
+ SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
+
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path),
+ ctx, b->pool, b->pool));
+
+ /* The directory should not be in conflict. */
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, b->pool, b->pool));
+ SVN_TEST_ASSERT(!text_conflicted &&
+ props_conflicted->nelts == 0 &&
+ !tree_conflicted);
+
+ /* We should have a text conflict in the file. */
+ new_file_path = svn_relpath_join(branch_path,
+ svn_relpath_join(new_dir_name,
+ new_file_name, b->pool),
+ b->pool);
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path),
+ ctx, b->pool, b->pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, b->pool, b->pool));
+ SVN_TEST_ASSERT(text_conflicted &&
+ props_conflicted->nelts == 0 &&
+ !tree_conflicted);
+
+ /* Verify the file's merged property value. */
+ /* ### Shouldn't there be a property conflict? The branch wins. */
+ SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx,
+ sbox_wc_path(b, new_file_path),
+ "prop", b->pool, b->pool));
+ SVN_TEST_STRING_ASSERT(propval->data, propval_branch);
+
+ return SVN_NO_ERROR;
+}
+
+/* Same test as above, but with an additional move operation on the trunk. */
+static svn_error_t *
+test_option_merge_incoming_added_dir_merge3(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ const char *new_dir_path;
+ const char *new_file_path;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ struct status_baton sb;
+ struct svn_client_status_t *status;
+ svn_opt_revision_t opt_rev;
+ const svn_string_t *propval;
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+
+ SVN_ERR(svn_test__sandbox_create(b, "incoming_added_dir_merge3",
+ opts, pool));
+
+ SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, TRUE, TRUE));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool);
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path),
+ ctx, b->pool, b->pool));
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, b->pool));
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict,
+ svn_client_conflict_option_merge_incoming_added_dir_merge,
+ b->pool));
+
+ /* Ensure that the directory has the expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_dir_path),
+ &opt_rev, svn_depth_unknown, TRUE, TRUE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_dir);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_normal);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_none);
+ SVN_TEST_ASSERT(!status->copied);
+ SVN_TEST_ASSERT(!status->switched);
+ SVN_TEST_ASSERT(!status->file_external);
+ SVN_TEST_ASSERT(status->moved_from_abspath == NULL);
+ SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
+
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path),
+ ctx, b->pool, b->pool));
+
+ /* The directory should not be in conflict. */
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, b->pool, b->pool));
+ SVN_TEST_ASSERT(!text_conflicted &&
+ props_conflicted->nelts == 0 &&
+ !tree_conflicted);
+
+ /* We should have a text conflict in the file. */
+ new_file_path = svn_relpath_join(branch_path,
+ svn_relpath_join(new_dir_name,
+ new_file_name, b->pool),
+ b->pool);
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path),
+ ctx, b->pool, b->pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, b->pool, b->pool));
+ SVN_TEST_ASSERT(text_conflicted &&
+ props_conflicted->nelts == 0 &&
+ !tree_conflicted);
+
+ /* Verify the file's merged property value. */
+ /* ### Shouldn't there be a property conflict? The branch wins. */
+ SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx,
+ sbox_wc_path(b, new_file_path),
+ "prop", b->pool, b->pool));
+ SVN_TEST_STRING_ASSERT(propval->data, propval_branch);
+
+ /* XFAIL: Currently, no subtree mergeinfo is created.
+ *
+ * Verify the directory's subtree mergeinfo. It should mention both
+ * location segments of ^/A/newdir's history, shouldn't it? Like this:
+ *
+ * /A/newdir:2-6
+ * /newdir.orig:4
+ *
+ * ### /newdir.orig was created in r3 and moved to /A/newdir in r5.
+ * ### Should the second line say "/newdir.orig:3-4" instead? */
+ SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx,
+ sbox_wc_path(b, new_dir_path),
+ "svn:mergeinfo", b->pool, b->pool));
+ SVN_TEST_ASSERT(propval != NULL);
+ SVN_TEST_STRING_ASSERT(propval->data,
+ apr_psprintf(b->pool, "/%s:2-6\n/%s:4",
+ svn_relpath_join(trunk_path,
+ new_dir_name,
+ b->pool),
+ apr_pstrcat(b->pool,
+ new_dir_name, ".orig",
+ SVN_VA_NULL)));
+ return SVN_NO_ERROR;
+}
+
/* ==========================================================================
*/
@@ -633,6 +959,12 @@ static struct svn_test_descriptor_t test
"test incoming add file replace and merge"),
SVN_TEST_OPTS_PASS(test_option_merge_incoming_added_dir_ignore,
"test incoming add dir ignore"),
+ SVN_TEST_OPTS_XFAIL(test_option_merge_incoming_added_dir_merge,
+ "test incoming add dir merge"),
+ SVN_TEST_OPTS_PASS(test_option_merge_incoming_added_dir_merge2,
+ "test incoming add dir merge with file change"),
+ SVN_TEST_OPTS_XFAIL(test_option_merge_incoming_added_dir_merge3,
+ "test incoming add dir merge with move history"),
SVN_TEST_NULL
};