[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: svn commit: r1740320 - in /subversion/trunk/subversion: include/svn_client.h libsvn_client/conflicts.c svn/conflict-callbacks.c tests/libsvn_client/conflicts-test.c

From: Stefan Sperling <stsp_at_apache.org>
Date: Thu, 21 Apr 2016 16:34:59 +0200

On Thu, Apr 21, 2016 at 02:04:08PM -0000, stsp_at_apache.org wrote:
> 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.

I'll need some help here to decide how to proceed.

Briefly, the problems I'm running into are:

 - It is not possible to merge an "only-adds" delta from rN-1 to rN for
   a path created in rN. Essentially, this is the same issue as we had
   for 'svn diff -cN' for a node created in rN, which was fixed at some point.
   So this is a case where svn diff -cN works, but svn merge -cN doesn't.
   I now need svn merge -cN to work in this case (see the 1st of 3 tests
   added in this commit).

 - I'm not sure to what extent the resolver should be responsible for
   mergeinfo. Should it assume that existing mergeinfo in a working copy
   remains valid when further merges are performed to resolve a tree
   conflict? I guess not. In the case I'm looking at, the merge only
   produces a delta if run with --no-ancestry (why?) which disables
   mergeinfo recording. It is possible to construct cases where the lack
   of additional mergeinfo recording seems wrong (see the 3rd of 3 tests
   added in this commit).

Does anyone have suggestions about these problems?

Should the resolver be using the standard merge code at all in this case?
Perhaps that's the wrong approach?

Implementation aside, I do think the option to merge the two directories
makes sense, even if they are ancestrally unrelated. I can imagine situations
where this would be the right thing to do when resolving an add/add conflict.
Note that the default behaviour of 'svn update' matches this resolution option.
'svn update' doesn't even flag a tree conflict in this case (but both
directories have a common ancestor by definition and belong to the same
branch, so merging them is not as crazy as it is in the case of 'svn merge').

The full diff is below for context.
Please see the comments in there for more information.

> * 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
> };
>
>
>
Received on 2016-04-21 16:35:08 CEST

This is an archived mail posted to the Subversion Dev mailing list.