You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2018/11/07 12:30:11 UTC
svn commit: r1846002 [39/44] - in /subversion/branches/ra-git: ./ build/
build/ac-macros/ build/generator/ build/generator/swig/
build/generator/templates/ build/generator/util/ build/win32/
contrib/client-side/ contrib/client-side/svn_load_dirs/ contr...
Modified: subversion/branches/ra-git/subversion/tests/libsvn_client/conflicts-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/tests/libsvn_client/conflicts-test.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/tests/libsvn_client/conflicts-test.c (original)
+++ subversion/branches/ra-git/subversion/tests/libsvn_client/conflicts-test.c Wed Nov 7 12:30:06 2018
@@ -162,7 +162,9 @@ assert_text_conflict_options(svn_client_
/* Some paths we'll care about. */
static const char *trunk_path = "A";
static const char *branch_path = "A_branch";
+static const char *branch2_path = "A_branch2";
static const char *new_file_name = "newfile.txt";
+static const char *new_file2_name = "newfile2.txt";
static const char *new_file_name_branch = "newfile-on-branch.txt";
static const char *deleted_file_name = "mu";
static const char *deleted_dir_name = "B";
@@ -175,6 +177,8 @@ static const char *propval_branch = "Thi
static const char *propval_different = "This is a different property value.";
/* File content. */
+static const char *new_file_content =
+ "This is a new file\n";
static const char *modified_file_content =
"This is a modified file\n";
static const char *modified_file_on_branch_content =
@@ -539,7 +543,7 @@ create_wc_with_dir_add_vs_dir_add_merge_
/* 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_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);
@@ -607,7 +611,6 @@ create_wc_with_dir_add_vs_dir_add_merge_
svn_client_conflict_option_postpone,
svn_client_conflict_option_accept_current_wc_state,
svn_client_conflict_option_incoming_add_ignore,
- svn_client_conflict_option_incoming_added_dir_merge,
svn_client_conflict_option_incoming_added_dir_replace,
svn_client_conflict_option_incoming_added_dir_replace_and_merge,
-1 /* end of list */
@@ -1289,9 +1292,10 @@ test_merge_incoming_added_dir_replace_an
/* A helper function which prepares a working copy for the tests below. */
static svn_error_t *
-create_wc_with_incoming_delete_merge_conflict(svn_test__sandbox_t *b,
- svn_boolean_t move,
- svn_boolean_t do_switch)
+create_wc_with_incoming_delete_file_merge_conflict(svn_test__sandbox_t *b,
+ svn_boolean_t move,
+ svn_boolean_t move_unrelated,
+ svn_boolean_t do_switch)
{
svn_client_ctx_t *ctx;
static const char *trunk_url;
@@ -1312,6 +1316,17 @@ create_wc_with_incoming_delete_merge_con
deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool);
move_target_path = svn_relpath_join(trunk_path, new_file_name, b->pool);
SVN_ERR(sbox_wc_move(b, deleted_path, move_target_path));
+ if (move_unrelated)
+ {
+ /* Move an unrelated file on trunk as part of the same commit. */
+ deleted_path = svn_relpath_join(trunk_path,
+ svn_relpath_join(deleted_dir_name,
+ deleted_dir_child,
+ b->pool),
+ b->pool);
+ move_target_path = svn_relpath_join(trunk_path, new_file2_name, b->pool);
+ SVN_ERR(sbox_wc_move(b, deleted_path, move_target_path));
+ }
SVN_ERR(sbox_wc_commit(b, ""));
}
else
@@ -1360,7 +1375,8 @@ create_wc_with_incoming_delete_merge_con
/* Test 'incoming delete ignore' option. */
static svn_error_t *
-test_merge_incoming_delete_ignore(const svn_test_opts_t *opts, apr_pool_t *pool)
+test_merge_incoming_delete_file_ignore(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
{
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
svn_client_ctx_t *ctx;
@@ -1373,10 +1389,11 @@ test_merge_incoming_delete_ignore(const
struct svn_client_status_t *status;
svn_opt_revision_t opt_rev;
- SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_delete_ignore",
+ SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_delete_file_ignore",
opts, pool));
- SVN_ERR(create_wc_with_incoming_delete_merge_conflict(b, FALSE, FALSE));
+ SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, FALSE, FALSE,
+ FALSE));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -1451,7 +1468,8 @@ test_merge_incoming_delete_ignore(const
/* Test 'incoming delete accept' option. */
static svn_error_t *
-test_merge_incoming_delete_accept(const svn_test_opts_t *opts, apr_pool_t *pool)
+test_merge_incoming_delete_file_accept(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
{
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
svn_client_ctx_t *ctx;
@@ -1464,10 +1482,11 @@ test_merge_incoming_delete_accept(const
struct svn_client_status_t *status;
svn_opt_revision_t opt_rev;
- SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_delete_accept",
+ SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_delete_file_accept",
opts, pool));
- SVN_ERR(create_wc_with_incoming_delete_merge_conflict(b, FALSE, FALSE));
+ SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, FALSE, FALSE,
+ FALSE));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -1562,7 +1581,8 @@ test_merge_incoming_move_file_text_merge
SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_move_file_text_merge",
opts, pool));
- SVN_ERR(create_wc_with_incoming_delete_merge_conflict(b, TRUE, FALSE));
+ SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, TRUE, FALSE,
+ FALSE));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -1668,8 +1688,8 @@ test_merge_incoming_move_file_text_merge
/* A helper function which prepares a working copy for the tests below. */
static svn_error_t *
-create_wc_with_incoming_delete_update_conflict(svn_test__sandbox_t *b,
- svn_boolean_t move)
+create_wc_with_incoming_delete_file_update_conflict(svn_test__sandbox_t *b,
+ svn_boolean_t move)
{
const char *deleted_path;
@@ -1707,6 +1727,158 @@ create_wc_with_incoming_delete_update_co
return SVN_NO_ERROR;
}
+/* Test 'incoming delete ignore' option. */
+static svn_error_t *
+test_update_incoming_delete_file_ignore(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_client_ctx_t *ctx;
+ const char *deleted_path;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t tree_conflicted;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ struct status_baton sb;
+ struct svn_client_status_t *status;
+ svn_opt_revision_t opt_rev;
+
+ SVN_ERR(svn_test__sandbox_create(b, "update_incoming_delete_file_ignore",
+ opts, pool));
+
+ SVN_ERR(create_wc_with_incoming_delete_file_update_conflict(b, FALSE));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool);
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path),
+ ctx, b->pool, b->pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_ignore,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_ignore,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict, svn_client_conflict_option_incoming_delete_ignore,
+ ctx, b->pool));
+
+ /* Ensure that the deleted file 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, deleted_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_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_added);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_modified);
+ 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, deleted_path),
+ ctx, b->pool, b->pool));
+
+ /* The file 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);
+
+ return SVN_NO_ERROR;
+}
+
+/* Test 'incoming delete accept' option. */
+static svn_error_t *
+test_update_incoming_delete_file_accept(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_client_ctx_t *ctx;
+ const char *deleted_path;
+ svn_client_conflict_t *conflict;
+ svn_node_kind_t node_kind;
+
+ SVN_ERR(svn_test__sandbox_create(b, "update_incoming_delete_file_accept",
+ opts, pool));
+
+ SVN_ERR(create_wc_with_incoming_delete_file_update_conflict(b, FALSE));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool);
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path),
+ ctx, b->pool, b->pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_ignore,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_ignore,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict, svn_client_conflict_option_incoming_delete_accept,
+ ctx, b->pool));
+
+ /* Ensure that the deleted file is gone. */
+ SVN_ERR(svn_io_check_path(sbox_wc_path(b, deleted_path), &node_kind,
+ b->pool));
+ SVN_TEST_ASSERT(node_kind == svn_node_none);
+
+ return SVN_NO_ERROR;
+}
+
/* Test 'incoming move file text merge' option for update. */
static svn_error_t *
test_update_incoming_move_file_text_merge(const svn_test_opts_t *opts,
@@ -1726,7 +1898,7 @@ test_update_incoming_move_file_text_merg
SVN_ERR(svn_test__sandbox_create(b, "update_incoming_move_file_text_merge",
opts, pool));
- SVN_ERR(create_wc_with_incoming_delete_update_conflict(b, TRUE));
+ SVN_ERR(create_wc_with_incoming_delete_file_update_conflict(b, TRUE));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -1816,7 +1988,8 @@ test_switch_incoming_move_file_text_merg
SVN_ERR(svn_test__sandbox_create(b, "switch_incoming_move_file_text_merge",
opts, pool));
- SVN_ERR(create_wc_with_incoming_delete_merge_conflict(b, TRUE, TRUE));
+ SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, TRUE, FALSE,
+ TRUE));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
@@ -2733,10 +2906,13 @@ test_merge_incoming_edit_file_moved_away
svn_client_ctx_t *ctx;
svn_opt_revision_t opt_rev;
svn_client_conflict_t *conflict;
+ apr_array_header_t *options;
+ svn_client_conflict_option_t *option;
svn_boolean_t text_conflicted;
apr_array_header_t *props_conflicted;
svn_boolean_t tree_conflicted;
svn_stringbuf_t *buf;
+ apr_array_header_t *possible_moved_to_abspaths;
SVN_ERR(svn_test__sandbox_create(
b, "merge_incoming_edit_file_moved_away", opts, pool));
@@ -2798,6 +2974,19 @@ test_merge_incoming_edit_file_moved_away
SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
}
+ SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options, conflict,
+ ctx, b->pool,
+ b->pool));
+ option = svn_client_conflict_option_find_by_id(
+ options, svn_client_conflict_option_local_move_file_text_merge);
+ SVN_TEST_ASSERT(option != NULL);
+ SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+ &possible_moved_to_abspaths, option, b->pool, b->pool));
+ SVN_TEST_INT_ASSERT(possible_moved_to_abspaths->nelts, 1);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *),
+ sbox_wc_path(b, "A1/mu-moved"));
+
/* Resolve the tree conflict by applying the incoming edit to the local
* move destination "mu-moved". */
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
@@ -3195,8 +3384,13 @@ test_merge_incoming_file_move_new_line_o
}
static svn_error_t *
-test_update_incoming_dir_move_with_nested_file_move(const svn_test_opts_t *opts,
- apr_pool_t *pool)
+run_test_update_incoming_dir_move_with_nested_file_move(
+ const svn_test_opts_t *opts,
+ svn_boolean_t move_parent,
+ svn_boolean_t move_back,
+ svn_boolean_t move_parent_twice,
+ const char *sandbox_name,
+ apr_pool_t *pool)
{
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
const char *deleted_dir;
@@ -3214,8 +3408,7 @@ test_update_incoming_dir_move_with_neste
svn_client_status_t *status;
struct status_baton sb;
- SVN_ERR(svn_test__sandbox_create(
- b, "update_incoming_dir_move_with_moved_file", opts, pool));
+ SVN_ERR(svn_test__sandbox_create(b, sandbox_name, opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b));
/* Move a directory on the trunk into another directory. */
@@ -3230,6 +3423,38 @@ test_update_incoming_dir_move_with_neste
SVN_ERR(sbox_wc_commit(b, ""));
+ if (move_parent)
+ {
+ /* Move the directory again. */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ deleted_dir = svn_relpath_join(trunk_path, "C/B", b->pool);
+ moved_dir = svn_relpath_join(trunk_path, "D/H/B", b->pool);
+ SVN_ERR(sbox_wc_move(b, deleted_dir, moved_dir));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ if (move_back)
+ {
+ /* And back again. */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ deleted_dir = svn_relpath_join(trunk_path, "D/H/B", b->pool);
+ moved_dir = svn_relpath_join(trunk_path, "C/B", b->pool);
+ SVN_ERR(sbox_wc_move(b, deleted_dir, moved_dir));
+ SVN_ERR(sbox_wc_commit(b, ""));
+ }
+ else if (move_parent_twice)
+ {
+ /* Move the directory again. */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ deleted_dir = svn_relpath_join(trunk_path, "D/H", b->pool);
+ moved_dir = svn_relpath_join(trunk_path, "D/G/H", b->pool);
+ SVN_ERR(sbox_wc_move(b, deleted_dir, moved_dir));
+ SVN_ERR(sbox_wc_commit(b, ""));
+ moved_dir = svn_relpath_join(trunk_path, "D/G/H/B", b->pool);
+ }
+
+ moved_file = svn_relpath_join(moved_dir, "lambda-moved", b->pool);
+ }
+
/* Update into the past. */
SVN_ERR(sbox_wc_update(b, "", 1));
@@ -3374,13 +3599,54 @@ test_update_incoming_dir_move_with_neste
return SVN_NO_ERROR;
}
+static svn_error_t *
+test_update_incoming_dir_move_with_nested_file_move(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ return run_test_update_incoming_dir_move_with_nested_file_move(
+ opts, FALSE, FALSE, FALSE,
+ "update_incoming_dir_move_with_nested_file_move", pool);
+}
+
+/* Same test as above, but with a moved parent directory. */
+static svn_error_t *
+test_update_incoming_dir_move_with_parent_move(
+ const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ return run_test_update_incoming_dir_move_with_nested_file_move(
+ opts, TRUE, FALSE, FALSE,
+ "update_incoming_dir_move_with_parent_move", pool);
+}
+
+/* Same test as above, but with the parent directory moved back. */
+static svn_error_t *
+test_update_incoming_dir_move_with_parent_moved_back(
+ const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ return run_test_update_incoming_dir_move_with_nested_file_move(
+ opts, TRUE, TRUE, FALSE,
+ "update_incoming_dir_move_with_parent_moved_back", pool);
+}
+
+/* Same test as above, but with the parent directory moved twice. */
+static svn_error_t *
+test_update_incoming_dir_move_with_parent_moved_twice(
+ const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ return run_test_update_incoming_dir_move_with_nested_file_move(
+ opts, TRUE, FALSE, TRUE,
+ "update_incoming_dir_move_with_parent_moved_twice", pool);
+}
+
/* A helper function which prepares a working copy for the tests below. */
static svn_error_t *
create_wc_with_file_add_vs_file_add_update_conflict(svn_test__sandbox_t *b)
{
static const char *new_file_path;
svn_client_ctx_t *ctx;
- static const char *trunk_url;
svn_opt_revision_t opt_rev;
svn_client_status_t *status;
struct status_baton sb;
@@ -3400,7 +3666,7 @@ create_wc_with_file_add_vs_file_add_upda
/* Update into the past. */
SVN_ERR(sbox_wc_update(b, "", 1));
- /* Add a different file and commit. */
+ /* Add a different file scheduled for commit. */
new_file_path = svn_relpath_join(trunk_path, new_file_name, b->pool);
SVN_ERR(sbox_file_write(b, new_file_path,
"This is a different new file on the trunk\n"));
@@ -3415,7 +3681,6 @@ create_wc_with_file_add_vs_file_add_upda
opt_rev.kind = svn_opt_revision_head;
opt_rev.value.number = SVN_INVALID_REVNUM;
- trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, SVN_VA_NULL);
/* Ensure that the file has the expected status. */
sb.result_pool = b->pool;
@@ -3789,6 +4054,2316 @@ test_merge_incoming_move_file_text_merge
return SVN_NO_ERROR;
}
+/* A helper function which prepares a working copy for the tests below. */
+static svn_error_t *
+create_wc_with_dir_add_vs_dir_add_update_conflict(
+ svn_test__sandbox_t *b,
+ svn_boolean_t unversioned_obstructions)
+{
+ static const char *new_dir_path;
+ static const char *new_dir_child_path;
+ static const char *new_file_path;
+ static const char *new_file_child_path;
+ svn_client_ctx_t *ctx;
+ svn_opt_revision_t opt_rev;
+ svn_client_status_t *status;
+ struct status_baton sb;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t tree_conflicted;
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b));
+
+ /* Add new directories on trunk and in the working copy which occupy
+ * the same path but have different content and properties. */
+ new_dir_path = svn_relpath_join(trunk_path, new_dir_name, b->pool);
+ SVN_ERR(sbox_wc_mkdir(b, new_dir_path));
+ SVN_ERR(sbox_wc_propset(b, "prop", propval_trunk, new_dir_path));
+ 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));
+ /* Create a directory and a file which will be obstructed during update. */
+ new_dir_child_path = svn_relpath_join(new_dir_path, "dir_child", b->pool);
+ SVN_ERR(sbox_wc_mkdir(b, new_dir_child_path));
+ new_file_child_path = svn_relpath_join(new_dir_path, "file_child", b->pool);
+ SVN_ERR(sbox_file_write(b, new_file_child_path,
+ "This is a child file on the trunk\n"));
+ SVN_ERR(sbox_wc_add(b, new_file_child_path));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ /* Update back into the past. */
+ SVN_ERR(sbox_wc_update(b, "", 1));
+
+ new_dir_path = svn_relpath_join(trunk_path, new_dir_name, b->pool);
+ SVN_ERR(sbox_wc_mkdir(b, new_dir_path));
+ SVN_ERR(sbox_wc_propset(b, "prop", propval_different, new_dir_path));
+ new_file_path = svn_relpath_join(trunk_path,
+ svn_relpath_join(new_dir_name,
+ new_file_name, b->pool),
+ b->pool);
+ SVN_ERR(sbox_file_write(b, new_file_path,
+ /* NB: Ensure that the file content's length
+ * differs! Tests are run with sleep for
+ * timestamps disabled. */
+ "This is a different new file\n"));
+ SVN_ERR(sbox_wc_add(b, new_file_path));
+ SVN_ERR(sbox_wc_propset(b, "prop", propval_different, new_file_path));
+
+ /* Add a file and a directory which obstruct incoming children. */
+ SVN_ERR(sbox_file_write(b, new_dir_child_path,
+ "This is a new file on the trunk\n"));
+ if (!unversioned_obstructions)
+ {
+ SVN_ERR(sbox_wc_mkdir(b, new_file_child_path));
+ SVN_ERR(sbox_wc_add(b, new_dir_child_path));
+ }
+ else
+ SVN_ERR(svn_io_dir_make(sbox_wc_path(b, new_file_child_path),
+ APR_OS_DEFAULT, b->pool));
+
+ /* Update to the HEAD revision.
+ * This should raise an "incoming add vs local add" tree conflict. */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, 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_empty, TRUE, FALSE,
+ 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_replaced);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified);
+ 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));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_add_ignore,
+ svn_client_conflict_option_incoming_added_dir_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_add_ignore,
+ svn_client_conflict_option_incoming_added_dir_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ /* Ensure that the expected tree conflict is present. */
+ SVN_ERR(svn_client_conflict_get_conflicted(NULL, NULL, &tree_conflicted,
+ conflict, b->pool, b->pool));
+ SVN_TEST_ASSERT(tree_conflicted);
+ SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) ==
+ svn_wc_conflict_reason_added);
+ SVN_TEST_ASSERT(svn_client_conflict_get_incoming_change(conflict) ==
+ svn_wc_conflict_action_add);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_update_incoming_added_dir_ignore(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, "update_incoming_added_dir_ignore",
+ opts, pool));
+
+ SVN_ERR(create_wc_with_dir_add_vs_dir_add_update_conflict(b, FALSE));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ new_dir_path = svn_relpath_join(trunk_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_resolve_by_id(
+ conflict, svn_client_conflict_option_incoming_add_ignore, ctx,
+ 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_empty, TRUE, FALSE,
+ 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_replaced);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified);
+ 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));
+
+ /* Verify the added dir's property value. */
+ /* ### Shouldn't there be a property conflict? The local change wins. */
+ SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx,
+ sbox_wc_path(b, new_dir_path),
+ "prop", b->pool, b->pool));
+ SVN_TEST_STRING_ASSERT(propval->data, propval_different);
+
+ /* 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);
+
+ /* Ensure that the newly added file has the expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ new_file_path = svn_relpath_join(trunk_path,
+ svn_relpath_join(new_dir_name,
+ new_file_name, b->pool),
+ b->pool);
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_file_path),
+ &opt_rev, svn_depth_empty, TRUE, FALSE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_added);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_modified);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified);
+ 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);
+
+ /* Verify the added file's property value. */
+ /* ### Shouldn't there be a property conflict? The local change 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_different);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_update_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_dir_child_path;
+ const char *new_file_path;
+ const char *new_file_child_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, "update_incoming_added_dir_merge",
+ opts, pool));
+
+ SVN_ERR(create_wc_with_dir_add_vs_dir_add_update_conflict(b, FALSE));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ new_dir_path = svn_relpath_join(trunk_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_resolve_by_id(
+ conflict, svn_client_conflict_option_incoming_added_dir_merge, ctx,
+ 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_empty, TRUE, FALSE,
+ 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_modified);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified);
+ 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);
+
+ /* Verify the added dir's property value. */
+ /* ### Shouldn't there be a property conflict? The local change wins. */
+ SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx,
+ sbox_wc_path(b, new_dir_path),
+ "prop", b->pool, b->pool));
+ SVN_TEST_STRING_ASSERT(propval->data, propval_different);
+ 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);
+
+ /* Ensure that the newly added file has the expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ new_file_path = svn_relpath_join(trunk_path,
+ svn_relpath_join(new_dir_name,
+ new_file_name, b->pool),
+ b->pool);
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_file_path),
+ &opt_rev, svn_depth_empty, TRUE, FALSE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_conflicted);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified);
+ 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);
+
+ /* Verify the added file's property value. */
+ /* ### Shouldn't there be a property conflict? The local change 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_different);
+
+ /* Ensure that the obstructing added file child of newdir has the
+ * expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ new_dir_child_path = svn_relpath_join(new_dir_path, "dir_child", b->pool);
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_dir_child_path),
+ &opt_rev, svn_depth_empty, TRUE, FALSE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_replaced);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_modified);
+ 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);
+
+ /* The file should be a tree conflict victim. */
+ SVN_ERR(svn_client_conflict_get(&conflict,
+ sbox_wc_path(b, new_dir_child_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);
+
+ /* Ensure that the obstructing added dir child of newdir has the
+ * expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ new_file_child_path = svn_relpath_join(new_dir_path, "file_child", b->pool);
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_file_child_path),
+ &opt_rev, svn_depth_empty, TRUE, FALSE,
+ 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_replaced);
+ 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);
+
+ /* The directory should be a tree conflict victim. */
+ SVN_ERR(svn_client_conflict_get(&conflict,
+ sbox_wc_path(b, new_file_child_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);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_update_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_dir_child_path;
+ const char *new_file_path;
+ const char *new_file_child_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, "update_incoming_added_dir_merge2",
+ opts, pool));
+
+ SVN_ERR(create_wc_with_dir_add_vs_dir_add_update_conflict(b, TRUE));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ new_dir_path = svn_relpath_join(trunk_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_resolve_by_id(
+ conflict, svn_client_conflict_option_incoming_added_dir_merge, ctx,
+ 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_empty, TRUE, FALSE,
+ 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_modified);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified);
+ 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);
+
+ /* Verify the added dir's property value. */
+ /* ### Shouldn't there be a property conflict? The local change wins. */
+ SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx,
+ sbox_wc_path(b, new_dir_path),
+ "prop", b->pool, b->pool));
+ SVN_TEST_STRING_ASSERT(propval->data, propval_different);
+ 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);
+
+ /* Ensure that the newly added file has the expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ new_file_path = svn_relpath_join(trunk_path,
+ svn_relpath_join(new_dir_name,
+ new_file_name, b->pool),
+ b->pool);
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_file_path),
+ &opt_rev, svn_depth_empty, TRUE, FALSE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_conflicted);
+ SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted);
+ SVN_TEST_ASSERT(status->prop_status == svn_wc_status_modified);
+ 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);
+
+ /* Verify the added file's property value. */
+ /* ### Shouldn't there be a property conflict? The local change 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_different);
+
+ /* Ensure that the obstructing added file child of newdir has the
+ * expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ new_dir_child_path = svn_relpath_join(new_dir_path, "dir_child", b->pool);
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_dir_child_path),
+ &opt_rev, svn_depth_empty, TRUE, FALSE,
+ 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_obstructed);
+ 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);
+
+ /* Ensure that the obstructing added dir child of newdir has the
+ * expected status. */
+ opt_rev.kind = svn_opt_revision_working;
+ sb.result_pool = b->pool;
+ new_file_child_path = svn_relpath_join(new_dir_path, "file_child", b->pool);
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, new_file_child_path),
+ &opt_rev, svn_depth_empty, TRUE, FALSE,
+ TRUE, TRUE, FALSE, TRUE, NULL,
+ status_func, &sb, b->pool));
+ status = sb.status;
+ SVN_TEST_ASSERT(status->kind == svn_node_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_obstructed);
+ 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);
+
+ return SVN_NO_ERROR;
+}
+
+/* Regression test for chrash fixed in r1780259. */
+static svn_error_t *
+test_cherry_pick_moved_file_with_propdel(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ const char *vendor_url;
+ svn_opt_revision_t peg_rev;
+ apr_array_header_t *ranges_to_merge;
+ svn_opt_revision_range_t merge_range;
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t tree_conflicted;
+
+ SVN_ERR(svn_test__sandbox_create(b,
+ "test_cherry_pick_moved_file_with_propdel",
+ opts, pool));
+
+ SVN_ERR(sbox_wc_mkdir(b, "A"));
+ SVN_ERR(sbox_wc_mkdir(b, "A2"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r1 */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ /* Let A/B/E act as a vendor branch of A2/E; A/B/E/lambda has a property. */
+ SVN_ERR(sbox_wc_mkdir(b, "A/B"));
+ SVN_ERR(sbox_wc_mkdir(b, "A/B/E"));
+ SVN_ERR(sbox_file_write(b, "A/B/E/lambda", "This is the file lambda.\n"));
+ SVN_ERR(sbox_wc_add(b, "A/B/E/lambda"));
+ SVN_ERR(sbox_wc_propset(b, "propname", "propval", "A/B/E/lambda"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ SVN_ERR(sbox_wc_copy(b, "A/B/E", "A2/E"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ /* Move vendor's E/lambda a level up and delete the property. */
+ SVN_ERR(sbox_wc_move(b, "A/B/E/lambda", "A/B/lambda"));
+ SVN_ERR(sbox_wc_propset(b, "propname", NULL /* propdel */, "A/B/lambda"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r4 */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ /* Move vendor's lambda to a new subdirectory. */
+ SVN_ERR(sbox_wc_mkdir(b, "A/B/newdir"));
+ SVN_ERR(sbox_wc_move(b, "A/B/lambda", "A/B/newdir/lambda"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r5 */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ /* Force a cherry-pick merge of A/B@5 to A2/E. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ vendor_url = apr_pstrcat(b->pool, b->repos_url, "/A/B", SVN_VA_NULL);
+ peg_rev.kind = svn_opt_revision_number;
+ peg_rev.value.number = 5;
+ merge_range.start.kind = svn_opt_revision_number;
+ merge_range.start.value.number = 4;
+ merge_range.end.kind = svn_opt_revision_number;
+ merge_range.end.value.number = 5;
+ ranges_to_merge = apr_array_make(b->pool, 1,
+ sizeof(svn_opt_revision_range_t *));
+ APR_ARRAY_PUSH(ranges_to_merge, svn_opt_revision_range_t *) = &merge_range;
+ /* This should raise a "local edit vs incoming delete or move" conflict. */
+ SVN_ERR(svn_client_merge_peg5(vendor_url, ranges_to_merge, &peg_rev,
+ sbox_wc_path(b, "A2/E"), svn_depth_infinity,
+ TRUE, TRUE, FALSE, FALSE, FALSE, FALSE,
+ NULL, ctx, b->pool));
+
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A2/E/lambda"),
+ ctx, b->pool, b->pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(NULL, NULL, &tree_conflicted,
+ conflict, b->pool, b->pool));
+ SVN_TEST_ASSERT(tree_conflicted);
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_ignore,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_move_file_text_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ /* Try to resolve the conflict. This crashed before r1780259 due to the
+ * fact that a non-existent ancestor property was not accounted for. */
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict,
+ svn_client_conflict_option_incoming_move_file_text_merge,
+ ctx, b->pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_merge_incoming_move_file_text_merge_crlf(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_client_ctx_t *ctx;
+ svn_opt_revision_t opt_rev;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ svn_stringbuf_t *buf;
+
+ SVN_ERR(svn_test__sandbox_create(
+ b, "merge_incoming_move_file_text_merge_crlf", opts, pool));
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b));
+ /* Edit the file to have CRLF line endings. */
+ SVN_ERR(sbox_file_write(b, "A/mu", "Original content.\r\n"));
+ SVN_ERR(sbox_wc_commit(b, ""));
+ /* Create a copy of node "A". */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ SVN_ERR(sbox_wc_copy(b, "A", "A1"));
+ SVN_ERR(sbox_wc_commit(b, ""));
+ /* On "trunk", move the file. */
+ SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved"));
+ SVN_ERR(sbox_wc_commit(b, ""));
+ /* On "branch", edit the file. */
+ SVN_ERR(sbox_file_write(b, "A1/mu", "Modified content.\r\n"));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ opt_rev.kind = svn_opt_revision_head;
+ opt_rev.value.number = SVN_INVALID_REVNUM;
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
+
+ /* Merge "A" to "A1". */
+ SVN_ERR(svn_client_merge_peg5(svn_path_url_add_component2(b->repos_url, "A",
+ pool),
+ NULL, &opt_rev, sbox_wc_path(b, "A1"),
+ svn_depth_infinity,
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+ NULL, ctx, pool));
+
+ /* We should have a tree conflict in the file "mu". */
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A1/mu"), ctx,
+ pool, pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, pool, pool));
+ SVN_TEST_ASSERT(!text_conflicted);
+ SVN_TEST_INT_ASSERT(props_conflicted->nelts, 0);
+ SVN_TEST_ASSERT(tree_conflicted);
+
+ /* Check available tree conflict resolution options. */
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_ignore,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_move_file_text_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
+ }
+
+ /* Resolve the tree conflict by moving "mu" to "mu-moved". */
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict, svn_client_conflict_option_incoming_move_file_text_merge,
+ ctx, pool));
+
+ /* The file should no longer be in conflict, and should not have a
+ * text conflict, because the contents are identical in "trunk" and
+ * in the "branch". */
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A1/mu-moved"),
+ ctx, pool, pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, pool, pool));
+ SVN_TEST_ASSERT(!text_conflicted);
+ SVN_TEST_INT_ASSERT(props_conflicted->nelts, 0);
+ SVN_TEST_ASSERT(!tree_conflicted);
+
+ /* And it should have expected contents. */
+ SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A1/mu-moved"),
+ pool));
+ SVN_TEST_STRING_ASSERT(buf->data, "Modified content.\r\n");
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_merge_incoming_move_file_text_merge_native_eol(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_client_ctx_t *ctx;
+ svn_opt_revision_t opt_rev;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ svn_stringbuf_t *buf;
+
+ SVN_ERR(svn_test__sandbox_create(
+ b, "merge_incoming_move_file_text_merge_native_eol", opts, pool));
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b));
+ /* Set svn:eol-style on a file and edit it. */
+ SVN_ERR(sbox_wc_propset(b, SVN_PROP_EOL_STYLE, "native", "A/mu"));;
+ SVN_ERR(sbox_file_write(b, "A/mu", "Original content.\n"));
+ SVN_ERR(sbox_wc_commit(b, ""));
+ /* Create a copy of node "A". */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ SVN_ERR(sbox_wc_copy(b, "A", "A1"));
+ SVN_ERR(sbox_wc_commit(b, ""));
+ /* On "trunk", move the file. */
+ SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved"));
+ SVN_ERR(sbox_wc_commit(b, ""));
+ /* On "branch", edit the file. */
+ SVN_ERR(sbox_file_write(b, "A1/mu", "Modified content.\n"));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ opt_rev.kind = svn_opt_revision_head;
+ opt_rev.value.number = SVN_INVALID_REVNUM;
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
+
+ /* Merge "A" to "A1". */
+ SVN_ERR(svn_client_merge_peg5(svn_path_url_add_component2(b->repos_url, "A",
+ pool),
+ NULL, &opt_rev, sbox_wc_path(b, "A1"),
+ svn_depth_infinity,
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+ NULL, ctx, pool));
+
+ /* We should have a tree conflict in the file "mu". */
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A1/mu"), ctx,
+ pool, pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, pool, pool));
+ SVN_TEST_ASSERT(!text_conflicted);
+ SVN_TEST_INT_ASSERT(props_conflicted->nelts, 0);
+ SVN_TEST_ASSERT(tree_conflicted);
+
+ /* Check available tree conflict resolution options. */
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_ignore,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_move_file_text_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
+ }
+
+ /* Resolve the tree conflict by moving "mu" to "mu-moved". */
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict, svn_client_conflict_option_incoming_move_file_text_merge,
+ ctx, pool));
+
+ /* The file should no longer be in conflict, and should not have a
+ * text conflict, because the contents are identical in "trunk" and
+ * in the "branch". */
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A1/mu-moved"),
+ ctx, pool, pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, pool, pool));
+ SVN_TEST_ASSERT(!text_conflicted);
+ SVN_TEST_INT_ASSERT(props_conflicted->nelts, 0);
+ SVN_TEST_ASSERT(!tree_conflicted);
+
+ /* And it should have expected contents. */
+ SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A1/mu-moved"),
+ pool));
+ SVN_TEST_STRING_ASSERT(buf->data, "Modified content." APR_EOL_STR);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_cherry_pick_post_move_edit(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ const char *trunk_url;
+ svn_opt_revision_t peg_rev;
+ apr_array_header_t *ranges_to_merge;
+ svn_opt_revision_range_t merge_range;
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t tree_conflicted;
+ svn_stringbuf_t *buf;
+
+ SVN_ERR(svn_test__sandbox_create(b,
+ "test_cherry_pick_post_move_edit",
+ opts, pool));
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
+ /* Create a copy of node "A". */
+ SVN_ERR(sbox_wc_copy(b, "A", "A1"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
+ /* On "trunk", move the file mu. */
+ SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved"));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
+ /* On "trunk", edit mu-moved. This will be r4. */
+ SVN_ERR(sbox_file_write(b, "A/mu-moved", "Modified content." APR_EOL_STR));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r4 */
+ /* On "trunk", edit mu-moved. This will be r5, which we'll cherry-pick. */
+ SVN_ERR(sbox_file_write(b, "A/mu-moved",
+ "More modified content." APR_EOL_STR));
+ SVN_ERR(sbox_wc_commit(b, "")); /* r5 */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ /* Perform a cherry-pick merge of r5 from A to A1. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ trunk_url = apr_pstrcat(b->pool, b->repos_url, "/A", SVN_VA_NULL);
+ peg_rev.kind = svn_opt_revision_number;
+ peg_rev.value.number = 5;
+ merge_range.start.kind = svn_opt_revision_number;
+ merge_range.start.value.number = 4;
+ merge_range.end.kind = svn_opt_revision_number;
+ merge_range.end.value.number = 5;
+ ranges_to_merge = apr_array_make(b->pool, 1,
+ sizeof(svn_opt_revision_range_t *));
+ APR_ARRAY_PUSH(ranges_to_merge, svn_opt_revision_range_t *) = &merge_range;
+ /* This should raise a "local delete or move vs incoming edit" conflict. */
+ SVN_ERR(svn_client_merge_peg5(trunk_url, ranges_to_merge, &peg_rev,
+ sbox_wc_path(b, "A1"), svn_depth_infinity,
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+ NULL, ctx, b->pool));
+
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A1/mu-moved"),
+ ctx, b->pool, b->pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(NULL, NULL, &tree_conflicted,
+ conflict, b->pool, b->pool));
+ SVN_TEST_ASSERT(tree_conflicted);
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_sibling_move_file_text_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ /* Try to resolve the conflict. */
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict,
+ svn_client_conflict_option_sibling_move_file_text_merge,
+ ctx, b->pool));
+
+ /* The node "A1/mu-moved" should no longer exist. */
+ SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict,
+ sbox_wc_path(b, "A1/mu-moved"),
+ ctx, pool, pool),
+ SVN_ERR_WC_PATH_NOT_FOUND);
+
+ /* And "A1/mu" should have expected contents. */
+ SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A1/mu"), pool));
+ SVN_TEST_STRING_ASSERT(buf->data,
+ "<<<<<<< .working" "\n"
+ "This is the file 'mu'." "\n"
+ "||||||| .old" "\n"
+ "Modified content." APR_EOL_STR
+ "=======" "\n"
+ "More modified content." APR_EOL_STR
+ ">>>>>>> .new" "\n");
+
+ return SVN_NO_ERROR;
+}
+
+/* A helper function which prepares a working copy for the tests below. */
+static svn_error_t *
+create_wc_with_incoming_delete_dir_conflict_across_branches(
+ svn_test__sandbox_t *b)
+{
+ svn_client_ctx_t *ctx;
+ const char *trunk_url;
+ const char *branch_url;
+ svn_opt_revision_t opt_rev;
+ const char *deleted_path;
+ const char *deleted_child_path;
+ const char *move_target_path;
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b));
+
+ /* Create a branch of node "A". */
+ SVN_ERR(sbox_wc_copy(b, trunk_path, branch_path));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ /* Create a second branch ("branch2") of the first branch. */
+ SVN_ERR(sbox_wc_copy(b, branch_path, branch2_path));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ /* Move a directory on the trunk. */
+ deleted_path = svn_relpath_join(trunk_path, deleted_dir_name, b->pool);
+ move_target_path = svn_relpath_join(trunk_path, new_dir_name, b->pool);
+ SVN_ERR(sbox_wc_move(b, deleted_path, move_target_path));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ /* Modify a file in that directory on branch2. */
+ deleted_child_path = svn_relpath_join(branch2_path,
+ svn_relpath_join(deleted_dir_name,
+ deleted_dir_child,
+ b->pool),
+ b->pool);
+ SVN_ERR(sbox_file_write(b, deleted_child_path,
+ modified_file_on_branch_content));
+
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ opt_rev.kind = svn_opt_revision_head;
+ opt_rev.value.number = SVN_INVALID_REVNUM;
+ trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path,
+ SVN_VA_NULL);
+ branch_url = apr_pstrcat(b->pool, b->repos_url, "/", branch_path,
+ SVN_VA_NULL);
+
+ /* Commit modification and run a merge from the trunk to the branch.
+ * This merge should not raise a conflict. */
+ SVN_ERR(sbox_wc_commit(b, ""));
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ SVN_ERR(svn_client_merge_peg5(trunk_url, NULL, &opt_rev,
+ sbox_wc_path(b, branch_path),
+ svn_depth_infinity,
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+ NULL, ctx, b->pool));
+
+ /* Commit merge result end run a merge from branch to branch2. */
+ SVN_ERR(sbox_wc_commit(b, ""));
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ /* This should raise an "incoming delete vs local edit" tree conflict. */
+ SVN_ERR(svn_client_merge_peg5(branch_url, NULL, &opt_rev,
+ sbox_wc_path(b, branch2_path),
+ svn_depth_infinity,
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+ NULL, ctx, b->pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_merge_incoming_move_dir_across_branches(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_client_ctx_t *ctx;
+ const char *deleted_path;
+ const char *moved_to_path;
+ const char *child_path;
+ svn_client_conflict_t *conflict;
+ struct status_baton sb;
+ struct svn_client_status_t *status;
+ svn_stringbuf_t *buf;
+ svn_opt_revision_t opt_rev;
+ apr_array_header_t *options;
+ svn_client_conflict_option_t *option;
+ apr_array_header_t *possible_moved_to_abspaths;
+
+ SVN_ERR(svn_test__sandbox_create(b,
+ "merge_incoming_move_dir accross branches",
+ opts, pool));
+
+ SVN_ERR(create_wc_with_incoming_delete_dir_conflict_across_branches(b));
+
+ deleted_path = svn_relpath_join(branch2_path, deleted_dir_name, b->pool);
+ moved_to_path = svn_relpath_join(branch2_path, new_dir_name, b->pool);
+
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path),
+ ctx, b->pool, b->pool));
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+
+ SVN_ERR_ASSERT(svn_client_conflict_get_local_change(conflict) ==
+ svn_wc_conflict_reason_edited);
+
+ /* Check possible move destinations for the directory. */
+ SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options, conflict,
+ ctx, b->pool,
+ b->pool));
+ option = svn_client_conflict_option_find_by_id(
+ options, svn_client_conflict_option_incoming_move_dir_merge);
+ SVN_TEST_ASSERT(option != NULL);
+
+ SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+ &possible_moved_to_abspaths, option, b->pool, b->pool));
+
+ /* The resolver finds two possible destinations for the moved folder:
+ *
+ * Possible working copy destinations for moved-away 'A_branch/B' are:
+ * (1): 'A_branch2/newdir'
+ * (2): 'A_branch/newdir'
+ * Only one destination can be a move; the others are copies.
+ */
+ SVN_TEST_INT_ASSERT(possible_moved_to_abspaths->nelts, 2);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *),
+ sbox_wc_path(b, moved_to_path));
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_abspaths, 1, const char *),
+ sbox_wc_path(b, svn_relpath_join(branch_path, new_dir_name, b->pool)));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_client_conflict_option_set_moved_to_abspath(option, 0,
+ ctx, b->pool));
+ SVN_ERR(svn_client_conflict_tree_resolve(conflict, option, ctx, b->pool));
+
+ /* Ensure that the moved-away directory has the expected status. */
+ sb.result_pool = b->pool;
+ opt_rev.kind = svn_opt_revision_working;
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, deleted_path),
+ &opt_rev, svn_depth_empty, 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_deleted);
+ 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_STRING_ASSERT(status->moved_to_abspath,
+ sbox_wc_path(b, moved_to_path));
+
+ /* Ensure that the moved-here directory has the expected status. */
+ sb.result_pool = b->pool;
+ opt_rev.kind = svn_opt_revision_working;
+ SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, moved_to_path),
+ &opt_rev, svn_depth_empty, 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_added);
+ 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_STRING_ASSERT(status->moved_from_abspath,
+ sbox_wc_path(b, deleted_path));
+ SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
+
+ /* Ensure that the edited file has the expected content. */
+ child_path = svn_relpath_join(moved_to_path, deleted_dir_child,
+ b->pool);
+ SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, child_path),
+ b->pool));
+ SVN_TEST_STRING_ASSERT(buf->data, modified_file_on_branch_content);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_update_incoming_delete_locally_deleted_file(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ svn_wc_status3_t *wc_status;
+
+ SVN_ERR(svn_test__sandbox_create(
+ b, "update_incoming_delete_locally_deleted_file", opts, pool));
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b));
+ /* Delete the file. */
+ SVN_ERR(sbox_wc_delete(b, "A/mu"));
+ SVN_ERR(sbox_wc_commit(b, ""));
+ /* Update to revision before delete. */
+ SVN_ERR(sbox_wc_update(b, "", 1));
+ /* Delete the file locally. */
+ SVN_ERR(sbox_wc_delete(b, "A/mu"));
+ /* Attempt an update to HEAD. */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+ /* We should have a tree conflict in the file "mu". */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A/mu"),
+ ctx, pool, pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, pool, pool));
+ SVN_TEST_ASSERT(!text_conflicted);
+ SVN_TEST_INT_ASSERT(props_conflicted->nelts, 0);
+ SVN_TEST_ASSERT(tree_conflicted);
+
+ /* Check available tree conflict resolution options. */
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
+ }
+
+ /* Resolve the tree conflict accepting the incoming deletion. */
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict, svn_client_conflict_option_incoming_delete_accept,
+ ctx, pool));
+
+ /* Check the status. */
+ SVN_ERR(svn_wc_status3(&wc_status, ctx->wc_ctx, sbox_wc_path(b, "A/mu"),
+ pool, pool));
+ SVN_TEST_INT_ASSERT(wc_status->kind, svn_node_unknown);
+ SVN_TEST_ASSERT(!wc_status->versioned);
+ SVN_TEST_ASSERT(!wc_status->conflicted);
+ SVN_TEST_INT_ASSERT(wc_status->node_status, svn_wc_status_none);
+ SVN_TEST_INT_ASSERT(wc_status->text_status, svn_wc_status_none);
+ SVN_TEST_INT_ASSERT(wc_status->prop_status, svn_wc_status_none);
+ SVN_TEST_INT_ASSERT(wc_status->actual_kind, svn_node_none);
+
+ return SVN_NO_ERROR;
+}
+
+/* A helper function which prepares a working copy for the test below. */
+static svn_error_t *
+create_wc_with_added_dir_conflict_across_branches(svn_test__sandbox_t *b,
+ svn_client_ctx_t *ctx)
+{
+ const char *trunk_url;
+ const char *branch_url;
+ svn_opt_revision_t opt_rev;
+ const char *new_dir_path;
+ const char *new_file_path;
+
+ SVN_ERR(sbox_add_and_commit_greek_tree(b));
+
+ /* Create a branch of node "A". */
+ SVN_ERR(sbox_wc_copy(b, trunk_path, branch_path));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ /* Create a second branch ("branch2") of node "A". */
+ SVN_ERR(sbox_wc_copy(b, trunk_path, branch2_path));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ /* Add directories with differing content to both branches. */
+ 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(new_dir_path, new_file_name, b->pool);
+ SVN_ERR(sbox_file_write(b, new_file_path,
+ "This is a new file on branch 1\n"));
+ SVN_ERR(sbox_wc_add(b, new_file_path));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ new_dir_path = svn_relpath_join(branch2_path, new_dir_name, b->pool);
+ SVN_ERR(sbox_wc_mkdir(b, new_dir_path));
+ 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 branch 2\n"));
+ SVN_ERR(sbox_wc_add(b, new_file_path));
+ SVN_ERR(sbox_wc_commit(b, ""));
+
+ /* Merge the differences between trunk and branch into branch2.
+ * This merge should raise an add vs. add conflict on the new directory. */
+ SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+ opt_rev.kind = svn_opt_revision_head;
+ opt_rev.value.number = SVN_INVALID_REVNUM;
+ trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path,
+ SVN_VA_NULL);
+ branch_url = apr_pstrcat(b->pool, b->repos_url, "/", branch2_path,
+ SVN_VA_NULL);
+ opt_rev.kind = svn_opt_revision_head;
+ opt_rev.value.number = SVN_INVALID_REVNUM;
+ SVN_ERR(svn_client_merge5(trunk_url, &opt_rev, branch_url, &opt_rev,
+ sbox_wc_path(b, branch2_path),
+ svn_depth_infinity,
+ FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
+ NULL, ctx, b->pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_merge_two_added_dirs_assertion_failure(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_client_ctx_t *ctx;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ svn_wc_status3_t *wc_status;
+ const char *new_dir_path;
+
+ SVN_ERR(svn_test__sandbox_create(
+ b, "test_merge_two_added_dirs_assertion_failure", opts, pool));
+
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
+ SVN_ERR(create_wc_with_added_dir_conflict_across_branches(b, ctx));
+
+ /* We should have a tree conflict in the directory "A_branch2/newdir". */
+ new_dir_path = svn_relpath_join(branch2_path, new_dir_name, b->pool);
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path),
+ ctx, pool, pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, pool, pool));
+ SVN_TEST_ASSERT(!text_conflicted);
+ SVN_TEST_INT_ASSERT(props_conflicted->nelts, 0);
+ SVN_TEST_ASSERT(tree_conflicted);
+
+ /* Check available tree conflict resolution options. */
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_add_ignore,
+ svn_client_conflict_option_incoming_added_dir_replace,
+ svn_client_conflict_option_incoming_added_dir_replace_and_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
+ }
+
+ /* This call used to run into an assertion failure (start_rev > end_rev). */
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_add_ignore,
+ svn_client_conflict_option_incoming_added_dir_replace,
+ svn_client_conflict_option_incoming_added_dir_replace_and_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
+ }
+
+ /* Resolve the tree conflict by replace + merge. */
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict,
+ svn_client_conflict_option_incoming_added_dir_replace_and_merge,
+ ctx, pool));
+
+ /* Check the status. */
+ SVN_ERR(svn_wc_status3(&wc_status, ctx->wc_ctx, sbox_wc_path(b, new_dir_path),
+ pool, pool));
+ SVN_TEST_INT_ASSERT(wc_status->kind, svn_node_dir);
+ SVN_TEST_ASSERT(wc_status->versioned);
+ SVN_TEST_ASSERT(!wc_status->conflicted);
+ SVN_TEST_INT_ASSERT(wc_status->node_status, svn_wc_status_replaced);
+ SVN_TEST_INT_ASSERT(wc_status->text_status, svn_wc_status_normal);
+ SVN_TEST_INT_ASSERT(wc_status->prop_status, svn_wc_status_none);
+ SVN_TEST_INT_ASSERT(wc_status->actual_kind, svn_node_dir);
+
+ return SVN_NO_ERROR;
+}
+
+/* Test for issue #4766: resolver adds unrelated moves to move target list */
+static svn_error_t *
+test_merge_incoming_delete_file_unrelated_move(const svn_test_opts_t *opts,
+ apr_pool_t *pool)
+{
+ svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+ svn_client_ctx_t *ctx;
+ const char *deleted_path;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t tree_conflicted;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ struct status_baton sb;
+ struct svn_client_status_t *status;
+ svn_opt_revision_t opt_rev;
+ apr_array_header_t *possible_moved_to_repos_relpaths;
+ apr_array_header_t *options;
+ svn_client_conflict_option_t *option;
+ const char *new_file_path;
+ const char *moved_to_repos_path;
+ svn_node_kind_t kind;
+ svn_stringbuf_t *buf;
+
+ SVN_ERR(svn_test__sandbox_create(b,
+ "merge_incoming_delete_file_unrelated_move",
+ opts, pool));
+
+ SVN_ERR(create_wc_with_incoming_delete_file_merge_conflict(b, TRUE, TRUE,
+ FALSE));
+
+ /* Resolve the tree conflict. */
+ SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+ deleted_path = svn_relpath_join(branch_path, deleted_file_name, b->pool);
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path),
+ ctx, b->pool, b->pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_delete_ignore,
+ svn_client_conflict_option_incoming_delete_accept,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, b->pool));
+
+ {
+ svn_client_conflict_option_id_t expected_opts[] = {
+ svn_client_conflict_option_postpone,
+ svn_client_conflict_option_accept_current_wc_state,
+ svn_client_conflict_option_incoming_move_file_text_merge,
+ -1 /* end of list */
+ };
+ SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
+ b->pool));
+ }
+
+ SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options, conflict,
+ ctx, b->pool,
+ b->pool));
+ option = svn_client_conflict_option_find_by_id(
+ options, svn_client_conflict_option_incoming_move_file_text_merge);
+ SVN_TEST_ASSERT(option != NULL);
+
+ /* Assert that only one move target candidate has been found in repository. */
+ SVN_ERR(svn_client_conflict_option_get_moved_to_repos_relpath_candidates(
+ &possible_moved_to_repos_relpaths, option, b->pool, b->pool));
+
+ SVN_TEST_INT_ASSERT(possible_moved_to_repos_relpaths->nelts, 1);
+ moved_to_repos_path = svn_relpath_join(trunk_path, new_file_name, b->pool);
+ SVN_TEST_STRING_ASSERT(
+ APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 0, const char *), moved_to_repos_path);
+
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+ conflict, svn_client_conflict_option_incoming_move_file_text_merge,
+ ctx, b->pool));
+
+ /* Ensure that the deleted file 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, deleted_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_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_deleted);
+ 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);
+ new_file_path = svn_relpath_join(branch_path, new_file_name, b->pool);
+ SVN_TEST_STRING_ASSERT(status->moved_to_abspath,
+ sbox_wc_path(b, new_file_path));
+
+ SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_path),
+ ctx, b->pool, b->pool));
+
+ /* The file 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);
+
+ /* Ensure that the moved file 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_file_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_file);
+ SVN_TEST_ASSERT(status->versioned);
+ SVN_TEST_ASSERT(!status->conflicted);
+ SVN_TEST_ASSERT(status->node_status == svn_wc_status_added);
+ 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_STRING_ASSERT(status->moved_from_abspath,
+ sbox_wc_path(b, deleted_path));
+ SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
+
+ /* Ensure that the original file was removed. */
+ SVN_ERR(svn_io_check_path(sbox_wc_path(b, deleted_path), &kind, b->pool));
+ SVN_TEST_ASSERT(kind == svn_node_none);
+
+ /* Ensure that the moved file has the expected content. */
+ SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, new_file_path),
+ b->pool));
+ SVN_TEST_STRING_ASSERT(buf->data, modified_file_on_branch_content);
+
+ /* Ensure that the unrelated moved file 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, svn_relpath_join(branch_path,
+ new_file2_name, b->pool)),
+ &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_file);
[... 850 lines stripped ...]