blob: 558de940e1d2268565e5621bf14c99b42e6c0577 [file] [log] [blame]
/*
* Regression tests for the conflict resolver in the libsvn_client library.
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#define SVN_DEPRECATED
#include "svn_client.h"
#include "svn_dirent_uri.h"
#include "svn_hash.h"
#include "svn_props.h"
#include "../svn_test.h"
#include "../svn_test_fs.h"
#include "../libsvn_wc/utils.h"
struct status_baton
{
svn_client_status_t *status;
apr_pool_t *result_pool;
};
/* Implements svn_client_status_func_t */
static svn_error_t *
status_func(void *baton, const char *path,
const svn_client_status_t *status,
apr_pool_t *scratch_pool)
{
struct status_baton *sb = baton;
sb->status = svn_client_status_dup(status, sb->result_pool);
return SVN_NO_ERROR;
}
struct info_baton
{
svn_client_info2_t *info;
apr_pool_t *result_pool;
};
/* Implements svn_client_info_receiver2_t */
static svn_error_t *
info_func(void *baton, const char *abspath_or_url,
const svn_client_info2_t *info,
apr_pool_t *scratch_pool)
{
struct info_baton *ib = baton;
ib->info = svn_client_info2_dup(info, ib->result_pool);
return SVN_NO_ERROR;
}
/* A helper function which checks offered conflict resolution options. */
static svn_error_t *
assert_conflict_options(const apr_array_header_t *actual,
const svn_client_conflict_option_id_t *expected,
apr_pool_t *pool)
{
svn_stringbuf_t *actual_str = svn_stringbuf_create_empty(pool);
svn_stringbuf_t *expected_str = svn_stringbuf_create_empty(pool);
int i;
for (i = 0; i < actual->nelts; i++)
{
svn_client_conflict_option_t *opt;
svn_client_conflict_option_id_t id;
opt = APR_ARRAY_IDX(actual, i, svn_client_conflict_option_t *);
if (i > 0)
svn_stringbuf_appendcstr(actual_str, ", ");
id = svn_client_conflict_option_get_id(opt);
svn_stringbuf_appendcstr(actual_str, apr_itoa(pool, id));
}
for (i = 0; expected[i] >= 0; i++)
{
if (i > 0)
svn_stringbuf_appendcstr(expected_str, ", ");
svn_stringbuf_appendcstr(expected_str, apr_itoa(pool, expected[i]));
}
SVN_TEST_STRING_ASSERT(actual_str->data, expected_str->data);
return SVN_NO_ERROR;
}
static svn_error_t *
assert_tree_conflict_options(svn_client_conflict_t *conflict,
svn_client_ctx_t *ctx,
const svn_client_conflict_option_id_t *expected,
apr_pool_t *pool)
{
apr_array_header_t *actual;
SVN_ERR(svn_client_conflict_tree_get_resolution_options(&actual, conflict,
ctx, pool, pool));
SVN_ERR(assert_conflict_options(actual, expected, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
assert_prop_conflict_options(svn_client_conflict_t *conflict,
svn_client_ctx_t *ctx,
const svn_client_conflict_option_id_t *expected,
apr_pool_t *pool)
{
apr_array_header_t *actual;
SVN_ERR(svn_client_conflict_prop_get_resolution_options(&actual, conflict,
ctx, pool, pool));
SVN_ERR(assert_conflict_options(actual, expected, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
assert_text_conflict_options(svn_client_conflict_t *conflict,
svn_client_ctx_t *ctx,
const svn_client_conflict_option_id_t *expected,
apr_pool_t *pool)
{
apr_array_header_t *actual;
SVN_ERR(svn_client_conflict_text_get_resolution_options(&actual, conflict,
ctx, pool, pool));
SVN_ERR(assert_conflict_options(actual, expected, pool));
return SVN_NO_ERROR;
}
/*
* The following tests verify resolution of "incoming file add vs.
* local file obstruction upon merge" tree conflicts.
*/
/* 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";
static const char *deleted_dir_child = "lambda";
static const char *new_dir_name = "newdir";
static const char *unversioned_file_name = "unversioned.txt";
/* File property content. */
static const char *propval_trunk = "This is a property on the trunk.";
static const char *propval_branch = "This is a property on the branch.";
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 =
"This is a modified file on the branch\n";
static const char *added_file_on_branch_content =
"This is a file added on the branch\n";
static const char *modified_file_in_working_copy_content =
"This is a modified file in the working copy\n";
static const char *unversioned_file_content =
"This is an unversioned file\n";
/* A helper function which prepares a working copy for the tests below. */
static svn_error_t *
create_wc_with_file_add_vs_file_add_merge_conflict(svn_test__sandbox_t *b,
svn_boolean_t do_switch)
{
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;
svn_client_conflict_t *conflict;
svn_boolean_t tree_conflicted;
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, ""));
/* Add new files on trunk and the branch which occupy the same path
* but have different content and properties. */
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 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, ""));
new_file_path = svn_relpath_join(branch_path, new_file_name, b->pool);
SVN_ERR(sbox_file_write(b, new_file_path,
/* NB: Ensure that the file content's length
* differs between the two branches! Tests are
* 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(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);
if (do_switch)
{
svn_revnum_t result_rev;
/* This should raise an "incoming add vs local add" conflict. */
SVN_ERR(svn_client_switch3(&result_rev, sbox_wc_path(b, branch_path),
trunk_url, &opt_rev, &opt_rev,
svn_depth_infinity, TRUE, TRUE, FALSE, FALSE,
ctx, b->pool));
opt_rev.kind = svn_opt_revision_head;
}
else
{
SVN_ERR(sbox_wc_commit(b, ""));
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
/* Run a merge from the trunk to the branch.
* This should raise an "incoming add vs local obstruction" conflict. */
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));
opt_rev.kind = svn_opt_revision_working;
}
/* Ensure that the file has the expected status. */
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);
if (do_switch)
{
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_modified);
}
else
{
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_normal);
}
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_file_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_added_file_text_merge,
svn_client_conflict_option_incoming_added_file_replace_and_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_added_file_text_merge,
svn_client_conflict_option_incoming_added_file_replace_and_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);
if (do_switch)
SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) ==
svn_wc_conflict_reason_added);
else
SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) ==
svn_wc_conflict_reason_obstructed);
SVN_TEST_ASSERT(svn_client_conflict_get_incoming_change(conflict) ==
svn_wc_conflict_action_add);
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_incoming_added_file_text_merge(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_client_ctx_t *ctx;
svn_client_conflict_t *conflict;
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, "merge_incoming_added_file_text_merge",
opts, pool));
SVN_ERR(create_wc_with_file_add_vs_file_add_merge_conflict(b, FALSE));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
new_file_path = svn_relpath_join(branch_path, new_file_name, 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_tree_resolve_by_id(
conflict,
svn_client_conflict_option_incoming_added_file_text_merge,
ctx, b->pool));
/* Ensure that the 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_conflicted);
SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted);
/* ### Shouldn't there be a property conflict? The trunk wins. */
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_file_path),
ctx, b->pool, b->pool));
/* We should have a text conflict instead of a tree 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);
/* Verify the 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;
}
static svn_error_t *
test_merge_incoming_added_file_replace_and_merge(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_client_ctx_t *ctx;
svn_client_conflict_t *conflict;
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, "merge_incoming_added_file_replace_and_merge", opts, pool));
SVN_ERR(create_wc_with_file_add_vs_file_add_merge_conflict(b, FALSE));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
new_file_path = svn_relpath_join(branch_path, new_file_name, 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_tree_resolve_by_id(
conflict,
svn_client_conflict_option_incoming_added_file_replace_and_merge,
ctx, b->pool));
/* Ensure that the 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_conflicted);
SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted);
/* ### Shouldn't there be a property conflict? The trunk wins. */
SVN_TEST_ASSERT(status->prop_status == svn_wc_status_normal);
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_file_path),
ctx, b->pool, b->pool));
/* We should have a text conflict instead of a tree 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);
/* Verify the 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;
}
/*
* The following tests verify resolution of "incoming dir add vs.
* local dir obstruction upon merge" tree conflicts.
*/
/* 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,
svn_boolean_t file_change_on_trunk,
svn_boolean_t with_move,
svn_boolean_t file_change_on_branch)
{
static const char *new_dir_path;
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;
svn_client_conflict_t *conflict;
svn_boolean_t tree_conflicted;
const char *move_src_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, ""));
/* Add new directories on trunk and the branch which occupy the same path
* but have different content and properties. */
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(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_on_trunk)
{
SVN_ERR(sbox_file_write(b, new_file_path,
"This is a change to the new file"
"on the trunk\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));
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);
SVN_ERR(sbox_wc_mkdir(b, new_dir_path));
new_file_path = svn_relpath_join(branch_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 between the two branches! Tests are
* 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, ""));
if (file_change_on_branch)
{
SVN_ERR(sbox_file_write(b, new_file_path,
"This is a change to the new file "
"on the branch\n"));
SVN_ERR(sbox_wc_commit(b, ""));
}
/* Run a merge from the trunk to the branch. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, SVN_VA_NULL);
opt_rev.kind = svn_opt_revision_head;
opt_rev.value.number = SVN_INVALID_REVNUM;
/* This should raise an "incoming add vs local obstruction" tree conflict. */
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));
/* 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->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));
{
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,
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,
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,
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_obstructed);
SVN_TEST_ASSERT(svn_client_conflict_get_incoming_change(conflict) ==
svn_wc_conflict_action_add);
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_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;
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;
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_added_dir_ignore",
opts, pool));
SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, 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_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_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);
return SVN_NO_ERROR;
}
static svn_error_t *
test_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, "merge_incoming_added_dir_merge",
opts, pool));
SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, 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, ctx, 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_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);
new_file_path = svn_relpath_join(branch_path,
svn_relpath_join(new_dir_name,
new_file_name, b->pool),
b->pool);
/* Ensure that the file has the expected status. */
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_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);
/* The file should now have a text conflict. */
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. */
static svn_error_t *
test_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, "merge_incoming_added_dir_merge2",
opts, pool));
SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, TRUE, 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, ctx, 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_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);
new_file_path = svn_relpath_join(branch_path,
svn_relpath_join(new_dir_name,
new_file_name, b->pool),
b->pool);
/* Ensure that the file has the expected status. */
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_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);
/* The file should now have a text conflict. */
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 trunk 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_trunk);
return SVN_NO_ERROR;
}
/* Same test as above, but with an additional move operation on the trunk. */
static svn_error_t *
test_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, "merge_incoming_added_dir_merge3",
opts, pool));
SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, TRUE, 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, ctx, 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_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);
/* There should now be an 'add vs add' conflict on the new file. */
new_file_path = svn_relpath_join(branch_path,
svn_relpath_join(new_dir_name,
new_file_name, b->pool),
b->pool);
/* Ensure that the file has the expected status. */
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_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);
/* We should now 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 trunk 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_trunk);
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_incoming_added_dir_replace(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;
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;
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_added_dir_replace",
opts, pool));
SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, 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, ctx, b->pool));
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_incoming_added_dir_replace,
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_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_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);
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);
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_merge_incoming_added_dir_replace_and_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;
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
SVN_ERR(svn_test__sandbox_create(b,
"merge_incoming_added_dir_replace_and_merge",
opts, pool));
SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, 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, ctx, b->pool));
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_incoming_added_dir_replace_and_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_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_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);
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);
return SVN_NO_ERROR;
}
/* Same test as above, but with an additional file change on the branch
* which makes resolution work as expected. */
static svn_error_t *
test_merge_incoming_added_dir_replace_and_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;
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
SVN_ERR(svn_test__sandbox_create(
b, "merge_incoming_added_dir_replace_and_merge2", opts, pool));
SVN_ERR(create_wc_with_dir_add_vs_dir_add_merge_conflict(b, FALSE, FALSE,
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, ctx, b->pool));
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_incoming_added_dir_replace_and_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_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_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);
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);
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_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;
svn_opt_revision_t opt_rev;
const char *deleted_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, ""));
if (move)
{
const char *move_target_path;
/* Move a file on the trunk. */
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
{
/* Delete a file on the trunk. */
deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool);
SVN_ERR(sbox_wc_delete(b, deleted_path));
SVN_ERR(sbox_wc_commit(b, ""));
}
/* Modify a file on the branch. */
deleted_path = svn_relpath_join(branch_path, deleted_file_name, b->pool);
SVN_ERR(sbox_file_write(b, deleted_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);
if (do_switch)
{
/* Switch the branch working copy to trunk. */
svn_revnum_t result_rev;
/* This should raise an "incoming delete vs local edit" tree conflict. */
SVN_ERR(svn_client_switch3(&result_rev, sbox_wc_path(b, branch_path),
trunk_url, &opt_rev, &opt_rev,
svn_depth_infinity,
TRUE, FALSE, FALSE, FALSE, ctx, b->pool));
}
else
{
/* Commit modification and run a merge from the trunk to the branch. */
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(trunk_url, NULL, &opt_rev,
sbox_wc_path(b, branch_path),
svn_depth_infinity,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
NULL, ctx, b->pool));
}
return SVN_NO_ERROR;
}
/* Test 'incoming delete ignore' option. */
static svn_error_t *
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;
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, "merge_incoming_delete_file_ignore",
opts, pool));
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));
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_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_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, 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_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;
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, "merge_incoming_delete_file_accept",
opts, pool));
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));
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_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 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);
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 move file text merge' option for merge. */
static svn_error_t *
test_merge_incoming_move_file_text_merge(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 *new_file_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_stringbuf_t *buf;
svn_node_kind_t kind;
SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_move_file_text_merge",
opts, pool));
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));
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_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);
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_file_update_conflict(svn_test__sandbox_t *b,
svn_boolean_t move)
{
const char *deleted_path;
SVN_ERR(sbox_add_and_commit_greek_tree(b));
if (move)
{
const char *move_target_path;
/* Move a file on the trunk. */
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));
SVN_ERR(sbox_wc_commit(b, ""));
}
else
{
/* Delete a file on the trunk. */
deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool);
SVN_ERR(sbox_wc_delete(b, deleted_path));
SVN_ERR(sbox_wc_commit(b, ""));
}
/* Update into the past. */
SVN_ERR(sbox_wc_update(b, "", 1));
/* Modify a file in the working copy. */
deleted_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool);
SVN_ERR(sbox_file_write(b, deleted_path, modified_file_on_branch_content));
/* Update to HEAD.
* This should raise an "incoming delete vs local edit" tree conflict. */
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
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,
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 *new_file_path;
svn_client_conflict_t *conflict;
struct status_baton sb;
struct svn_client_status_t *status;
svn_opt_revision_t opt_rev;
svn_node_kind_t node_kind;
svn_stringbuf_t *buf;
SVN_ERR(svn_test__sandbox_create(b, "update_incoming_move_file_text_merge",
opts, pool));
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));
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_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_resolve_by_id(
conflict, svn_client_conflict_option_incoming_move_file_text_merge,
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);
/* Ensure that the moved 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, new_file_name, 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_modified);
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);
/* 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);
return SVN_NO_ERROR;
}
/* Test 'incoming move file text merge' option for switch. */
static svn_error_t *
test_switch_incoming_move_file_text_merge(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 *new_file_path;
svn_client_conflict_t *conflict;
struct status_baton sb;
struct svn_client_status_t *status;
svn_opt_revision_t opt_rev;
svn_node_kind_t node_kind;
svn_stringbuf_t *buf;
SVN_ERR(svn_test__sandbox_create(b, "switch_incoming_move_file_text_merge",
opts, pool));
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));
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_resolve_by_id(
conflict, svn_client_conflict_option_incoming_move_file_text_merge,
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);
/* Ensure that the moved file has the expected status. */
opt_rev.kind = svn_opt_revision_working;
sb.result_pool = b->pool;
new_file_path = svn_relpath_join(branch_path, new_file_name, 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_modified);
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);
/* 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);
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(svn_test__sandbox_t *b,
svn_boolean_t move,
svn_boolean_t do_switch,
svn_boolean_t local_edit,
svn_boolean_t local_add)
{
svn_client_ctx_t *ctx;
static const char *trunk_url;
svn_opt_revision_t opt_rev;
const char *deleted_path;
const char *deleted_child_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, ""));
/* On the trunk, add a file inside the dir about to be moved/deleted. */
new_file_path = svn_relpath_join(trunk_path,
svn_relpath_join(deleted_dir_name,
new_file_name, b->pool),
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_commit(b, ""));
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
if (move)
{
const char *move_target_path;
/* 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, ""));
}
else
{
/* Delete a directory on the trunk. */
deleted_path = svn_relpath_join(trunk_path, deleted_dir_name, b->pool);
SVN_ERR(sbox_wc_delete(b, deleted_path));
SVN_ERR(sbox_wc_commit(b, ""));
}
if (local_add)
{
const char *new_child_path;
new_child_path = svn_relpath_join(branch_path,
svn_relpath_join(deleted_dir_name,
new_file_name_branch,
b->pool),
b->pool);
/* Add new file on the branch. */
SVN_ERR(sbox_file_write(b, new_child_path, added_file_on_branch_content));
SVN_ERR(sbox_wc_add(b, new_child_path));
}
else
{
/* Modify a file on the branch. */
deleted_child_path = svn_relpath_join(branch_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);
if (do_switch)
{
/* Switch the branch working copy to trunk. */
svn_revnum_t result_rev;
/* This should raise an "incoming delete vs local edit" tree conflict. */
SVN_ERR(svn_client_switch3(&result_rev, sbox_wc_path(b, branch_path),
trunk_url, &opt_rev, &opt_rev,
svn_depth_infinity,
TRUE, FALSE, FALSE, FALSE, ctx, b->pool));
}
else
{
/* Commit modification and run a merge from the trunk to the branch. */
SVN_ERR(sbox_wc_commit(b, ""));
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
if (local_edit)
{
/* Modify the file in the working copy. */
SVN_ERR(sbox_file_write(b, deleted_child_path,
modified_file_in_working_copy_content));
}
/* This should raise an "incoming delete vs local edit" tree conflict. */
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));
}
return SVN_NO_ERROR;
}
/* Test 'incoming move dir merge' resolution option. */
static svn_error_t *
test_merge_incoming_move_dir(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", opts, pool));
SVN_ERR(create_wc_with_incoming_delete_dir_conflict(b, TRUE, FALSE, FALSE,
FALSE));
deleted_path = svn_relpath_join(branch_path, deleted_dir_name, b->pool);
moved_to_path = svn_relpath_join(branch_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));
/* 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_branch/newdir'
* (2): 'A/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(trunk_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;
}
/* Test 'incoming move dir merge' resolution option with local mods. */
static svn_error_t *
test_merge_incoming_move_dir2(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;
SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_move_dir2", opts, pool));
SVN_ERR(create_wc_with_incoming_delete_dir_conflict(b, TRUE, FALSE, TRUE,
FALSE));
deleted_path = svn_relpath_join(branch_path, deleted_dir_name, b->pool);
moved_to_path = svn_relpath_join(branch_path, new_dir_name, b->pool);
/* Resolve the tree conflict. */
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(svn_client_conflict_tree_resolve_by_id(
conflict, svn_client_conflict_option_incoming_move_dir_merge,
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_in_working_copy_content);
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_incoming_move_dir3(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;
const char *child_url;
svn_client_conflict_t *conflict;
struct status_baton sb;
struct info_baton ib;
struct svn_client_status_t *status;
svn_stringbuf_t *buf;
svn_opt_revision_t opt_rev;
SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_move_dir3", opts, pool));
SVN_ERR(create_wc_with_incoming_delete_dir_conflict(b, TRUE, FALSE, FALSE,
TRUE));
deleted_path = svn_relpath_join(branch_path, deleted_dir_name, b->pool);
moved_to_path = svn_relpath_join(branch_path, new_dir_name, b->pool);
/* Resolve the tree conflict. */
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(svn_client_conflict_tree_resolve_by_id(
conflict, svn_client_conflict_option_incoming_move_dir_merge,
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 file added on the branch has the expected content. */
child_path = svn_relpath_join(branch_path,
svn_relpath_join(new_dir_name,
new_file_name_branch,
b->pool),
b->pool);
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, child_path),
b->pool));
SVN_TEST_STRING_ASSERT(buf->data, added_file_on_branch_content);
/* Ensure that the file added on the branch 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, child_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_file);
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);
/* Ensure that the file added on the trunk has the expected content. */
child_path = svn_relpath_join(trunk_path,
svn_relpath_join(new_dir_name,
new_file_name,
b->pool),
b->pool);
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, child_path),
b->pool));
SVN_TEST_STRING_ASSERT(buf->data, "This is a new file on the trunk\n");
/* Ensure that the file added on the trunk 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, child_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_file);
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);
/* Commit and make sure both files are present in the resulting revision. */
SVN_ERR(sbox_wc_commit(b, ""));
ib.result_pool = b->pool;
opt_rev.kind = svn_opt_revision_head;
/* The file added on the branch should be present. */
child_url = apr_pstrcat(b->pool, b->repos_url, "/", branch_path, "/",
new_dir_name, "/", new_file_name_branch, SVN_VA_NULL);
SVN_ERR(svn_client_info4(child_url, &opt_rev, &opt_rev, svn_depth_empty,
TRUE, TRUE, TRUE, NULL,
info_func, &ib, ctx, b->pool));
/* The file added on the trunk should be present. */
child_url = apr_pstrcat(b->pool, b->repos_url, "/", branch_path, "/",
new_dir_name, "/", new_file_name, SVN_VA_NULL);
SVN_ERR(svn_client_info4(child_url, &opt_rev, &opt_rev, svn_depth_empty,
TRUE, TRUE, TRUE, NULL,
info_func, &ib, ctx, b->pool));
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_vs_local_delete(svn_test__sandbox_t *b)
{
svn_client_ctx_t *ctx;
static const char *trunk_url;
svn_opt_revision_t opt_rev;
const char *copy_src_path;
const char *copy_dst_name;
const char *copy_dst_path;
const char *deleted_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, ""));
/* On the trunk, copy "mu" to "mu-copied". */
copy_src_path = svn_relpath_join(trunk_path, deleted_file_name, b->pool);
copy_dst_name = apr_pstrcat(b->pool, deleted_file_name, "-copied",
SVN_VA_NULL);
copy_dst_path = svn_relpath_join(trunk_path, copy_dst_name, b->pool);
SVN_ERR(sbox_wc_copy(b, copy_src_path, copy_dst_path));
SVN_ERR(sbox_wc_commit(b, ""));
/* Merge the file copy to the branch. */
trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, SVN_VA_NULL);
opt_rev.kind = svn_opt_revision_head;
opt_rev.value.number = SVN_INVALID_REVNUM;
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
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));
SVN_ERR(sbox_wc_commit(b, ""));
/* Now delete the copied file on the trunk. */
deleted_file_path = svn_relpath_join(trunk_path, copy_dst_name, b->pool);
SVN_ERR(sbox_wc_delete(b, deleted_file_path));
SVN_ERR(sbox_wc_commit(b, ""));
/* Delete the corresponding file on the branch. */
deleted_file_path = svn_relpath_join(branch_path, copy_dst_name,
b->pool);
SVN_ERR(sbox_wc_delete(b, deleted_file_path));
SVN_ERR(sbox_wc_commit(b, ""));
/* Run a merge from the trunk to the branch.
* This should raise an "incoming delete vs local delete" tree conflict. */
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));
return SVN_NO_ERROR;
}
/* Test for the 'incoming delete vs local delete' bug fixed by r1751893. */
static svn_error_t *
test_merge_incoming_delete_vs_local_delete(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 *copy_dst_name;
const char *copy_dst_path;
svn_client_conflict_t *conflict;
svn_node_kind_t node_kind;
SVN_ERR(svn_test__sandbox_create(b, "merge_incoming_delete_vs_local_delete",
opts, pool));
SVN_ERR(create_wc_with_incoming_delete_vs_local_delete(b));
copy_dst_name = apr_pstrcat(b->pool, deleted_file_name, "-copied",
SVN_VA_NULL);
copy_dst_path = svn_relpath_join(branch_path, copy_dst_name, b->pool);
/* Resolve the tree conflict. Before r1751893 there was an unintended error.*/
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, copy_dst_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));
/* The file should be gone. */
SVN_ERR(svn_io_check_path(sbox_wc_path(b, copy_dst_path), &node_kind,
b->pool));
SVN_TEST_ASSERT(node_kind == svn_node_none);
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_file_prop(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;
apr_array_header_t *resolution_options;
svn_client_conflict_option_t *option;
const svn_string_t *propval;
SVN_ERR(svn_test__sandbox_create(b, "merge_file_prop", opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b));
/* Create a copy of node "A". */
SVN_ERR(sbox_wc_copy(b, "A", "A1"));
SVN_ERR(sbox_wc_commit(b, ""));
/* Commit conflicting file properties. */
SVN_ERR(sbox_wc_propset(b, "prop", "val1", "A/mu"));
SVN_ERR(sbox_wc_propset(b, "prop", "val2", "A1/mu"));
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));
/* The file "mu" should have a property conflict. */
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, 1);
SVN_TEST_STRING_ASSERT(APR_ARRAY_IDX(props_conflicted, 0, const char *),
"prop");
SVN_TEST_ASSERT(!tree_conflicted);
{
svn_client_conflict_option_id_t expected_opts[] = {
svn_client_conflict_option_postpone,
svn_client_conflict_option_base_text,
svn_client_conflict_option_incoming_text,
svn_client_conflict_option_working_text,
svn_client_conflict_option_incoming_text_where_conflicted,
svn_client_conflict_option_working_text_where_conflicted,
svn_client_conflict_option_merged_text,
-1 /* end of list */
};
SVN_ERR(assert_prop_conflict_options(conflict, ctx, expected_opts, pool));
}
SVN_ERR(svn_client_conflict_prop_get_resolution_options(&resolution_options,
conflict, ctx,
pool, pool));
option = svn_client_conflict_option_find_by_id(
resolution_options,
svn_client_conflict_option_merged_text);
svn_client_conflict_option_set_merged_propval(
option, svn_string_create("merged-val", pool));
/* Resolve the conflict with a merged property value. */
SVN_ERR(svn_client_conflict_prop_resolve(conflict, "prop", option,
ctx, pool));
/* The file should not be in conflict. */
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);
/* And it should have the expected property value. */
SVN_ERR(svn_wc_prop_get2(&propval, ctx->wc_ctx, sbox_wc_path(b, "A1/mu"),
"prop", pool, pool));
SVN_TEST_STRING_ASSERT(propval->data, "merged-val");
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_incoming_move_file_text_merge_conflict(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;
const char *base_abspath;
const char *working_abspath;
const char *incoming_old_abspath;
const char *incoming_new_abspath;
svn_stringbuf_t *buf;
SVN_ERR(svn_test__sandbox_create(
b, "merge_incoming_move_file_text_merge_conflict", opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b));
/* Write initial file content. */
SVN_ERR(sbox_file_write(b, "A/mu", "Initial 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 and edit it. */
SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved"));
SVN_ERR(sbox_file_write(b, "A/mu-moved", "New trunk content.\n"));
SVN_ERR(sbox_wc_commit(b, ""));
/* On "branch", edit the file. */
SVN_ERR(sbox_file_write(b, "A1/mu", "New branch 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));
/* We should now have a text conflict in the file "mu-moved". */
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);
/* Check available text conflict resolution options. */
{
svn_client_conflict_option_id_t expected_opts[] = {
svn_client_conflict_option_postpone,
svn_client_conflict_option_base_text,
svn_client_conflict_option_incoming_text,
svn_client_conflict_option_working_text,
svn_client_conflict_option_incoming_text_where_conflicted,
svn_client_conflict_option_working_text_where_conflicted,
svn_client_conflict_option_merged_text,
-1 /* end of list */
};
SVN_ERR(assert_text_conflict_options(conflict, ctx, expected_opts, pool));
}
/* Check versions of the text-conflicted file. */
SVN_ERR(svn_client_conflict_text_get_contents(&base_abspath,
&working_abspath,
&incoming_old_abspath,
&incoming_new_abspath,
conflict, pool, pool));
SVN_TEST_ASSERT(base_abspath == NULL);
SVN_ERR(svn_stringbuf_from_file2(&buf, incoming_old_abspath, pool));
SVN_TEST_STRING_ASSERT(buf->data, "Initial content.\n");
SVN_ERR(svn_stringbuf_from_file2(&buf, working_abspath, pool));
SVN_TEST_STRING_ASSERT(buf->data, "New branch content.\n");
SVN_ERR(svn_stringbuf_from_file2(&buf, incoming_new_abspath, pool));
SVN_TEST_STRING_ASSERT(buf->data, "New trunk content.\n");
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A1/mu-moved"),
pool));
SVN_TEST_STRING_ASSERT(buf->data,
"<<<<<<< .working\n"
"New branch content.\n"
"||||||| .old\n"
"Initial content.\n"
"=======\n"
"New trunk content.\n"
">>>>>>> .new\n");
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_incoming_edit_file_moved_away(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;
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));
SVN_ERR(sbox_add_and_commit_greek_tree(b));
/* Create a copy of node "A". */
SVN_ERR(sbox_wc_copy(b, "A", "A1"));
SVN_ERR(sbox_wc_commit(b, ""));
/* On "trunk", edit the file. */
SVN_ERR(sbox_file_write(b, "A/mu", "New trunk content.\n"));
SVN_ERR(sbox_wc_commit(b, ""));
/* On "branch", move the file. */
SVN_ERR(sbox_wc_move(b, "A1/mu", "A1/mu-moved"));
SVN_ERR(sbox_wc_commit(b, ""));
SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
/* Merge "trunk" to "branch". */
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_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,
-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_local_move_file_text_merge,
-1 /* end of list */
};
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(
conflict, svn_client_conflict_option_local_move_file_text_merge,
ctx, pool));
/* The file should not be in conflict. */
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 the expected content. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A1/mu-moved"),
pool));
SVN_TEST_STRING_ASSERT(buf->data, "New trunk content.\n");
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_incoming_chained_move_local_edit(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_chained_move_local_edit", opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b));
/* Create a copy of node "A". */
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 "trunk", move the file again. */
SVN_ERR(sbox_wc_move(b, "A/mu-moved", "A/mu-moved-again"));
SVN_ERR(sbox_wc_commit(b, ""));
/* On "branch", edit the file. */
SVN_ERR(sbox_file_write(b, "A1/mu", "New branch content.\n"));
SVN_ERR(sbox_wc_commit(b, ""));
SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
/* Merge "trunk" to "branch". */
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_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));
/* This used to fail around r1764234. The conflict resolver was
* unable to detect the move, and didn't offer the
* svn_client_conflict_option_incoming_move_file_text_merge option. */
{
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-again". */
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict, svn_client_conflict_option_incoming_move_file_text_merge,
ctx, pool));
/* The file should not be in conflict. */
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);
/* The move destination should have the expected content. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A1/mu-moved-again"),
pool));
SVN_TEST_STRING_ASSERT(buf->data, "New branch content.\n");
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_incoming_move_dir_with_moved_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_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;
struct status_baton sb;
struct svn_client_status_t *status;
SVN_ERR(svn_test__sandbox_create(
b, "merge_incoming_move_dir_with_moved_file", opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(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 a file and then move the dir containing the file. */
SVN_ERR(sbox_wc_move(b, "A/B/lambda", "A/B/lambda-moved"));
SVN_ERR(sbox_wc_move(b, "A/B", "A/B-moved"));
SVN_ERR(sbox_wc_commit(b, ""));
/* On "branch", edit the file. */
SVN_ERR(sbox_file_write(b, "A1/B/lambda", "New branch content.\n"));
SVN_ERR(sbox_wc_commit(b, ""));
SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
/* Merge "trunk" to "branch". */
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_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 on the dir. */
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A1/B"), 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_dir_merge,
-1 /* end of list */
};
SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
}
/* Resolve the tree conflict by moving the local directory and merging. */
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict, svn_client_conflict_option_incoming_move_dir_merge,
ctx, pool));
/* The dir should not be in conflict. */
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A1/B"),
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);
/* Ensure that the move source dir has the expected status. */
opt_rev.kind = svn_opt_revision_working;
sb.result_pool = pool;
SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A1/B"),
&opt_rev, svn_depth_empty, TRUE, TRUE,
TRUE, TRUE, FALSE, TRUE, NULL,
status_func, &sb, pool));
status = sb.status;
SVN_TEST_INT_ASSERT(status->kind, svn_node_dir);
SVN_TEST_ASSERT(status->versioned);
SVN_TEST_ASSERT(!status->conflicted);
SVN_TEST_INT_ASSERT(status->node_status, svn_wc_status_deleted);
SVN_TEST_INT_ASSERT(status->text_status, svn_wc_status_normal);
SVN_TEST_INT_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, NULL);
SVN_TEST_STRING_ASSERT(status->moved_to_abspath,
sbox_wc_path(b, "A1/B-moved"));
/* Ensure that the move destination dir has the expected status. */
opt_rev.kind = svn_opt_revision_working;
sb.result_pool = pool;
SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A1/B-moved"),
&opt_rev, svn_depth_empty, TRUE, TRUE,
TRUE, TRUE, FALSE, TRUE, NULL,
status_func, &sb, pool));
status = sb.status;
SVN_TEST_INT_ASSERT(status->kind, svn_node_dir);
SVN_TEST_ASSERT(status->versioned);
SVN_TEST_ASSERT(!status->conflicted);
SVN_TEST_INT_ASSERT(status->node_status, svn_wc_status_added);
SVN_TEST_INT_ASSERT(status->text_status, svn_wc_status_normal);
SVN_TEST_INT_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, "A1/B"));
SVN_TEST_STRING_ASSERT(status->moved_to_abspath, NULL);
/* We should have another tree conflict on the moved-away file. */
SVN_ERR(svn_client_conflict_get(&conflict,
sbox_wc_path(b, "A1/B-moved/lambda"),
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));
}
/* ### Need to test resolving the conflict on "A1/B-moved/lambda". */
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_incoming_file_move_new_line_of_history(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_ERR(svn_test__sandbox_create(
b, "merge_incoming_file_move_new_line_of_history", opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b));
/* Create a copy of node "A". */
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 "trunk", change the line of history of the moved file by
* replacing it. */
SVN_ERR(sbox_wc_delete(b, "A/mu-moved"));
SVN_ERR(sbox_file_write(b, "A/mu-moved", "x"));
SVN_ERR(sbox_wc_add(b, "A/mu-moved"));
SVN_ERR(sbox_wc_commit(b, ""));
/* On "trunk", move the replaced file. */
SVN_ERR(sbox_wc_move(b, "A/mu-moved", "A/mu-moved-again"));
SVN_ERR(sbox_wc_commit(b, ""));
/* On "branch", edit the file. */
SVN_ERR(sbox_file_write(b, "A1/mu", "New branch content.\n"));
SVN_ERR(sbox_wc_commit(b, ""));
SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
/* Merge "trunk" to "branch". */
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_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));
/* The svn_client_conflict_option_incoming_move_file_text_merge option
* should not be available, as the "mu" file was actually deleted at
* some point (and the remaining move is a part of the new line of
* history). */
{
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, pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
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;
const char *moved_dir;
const char *deleted_file;
const char *moved_file;
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_stringbuf_t *buf;
svn_node_kind_t kind;
svn_opt_revision_t opt_rev;
svn_client_status_t *status;
struct status_baton sb;
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. */
deleted_dir = svn_relpath_join(trunk_path, "B", b->pool);
moved_dir = svn_relpath_join(trunk_path, "C/B", b->pool);
SVN_ERR(sbox_wc_move(b, deleted_dir, moved_dir));
/* Rename a file inside the moved directory. */
deleted_file = svn_relpath_join(moved_dir, "lambda" , b->pool);
moved_file = svn_relpath_join(moved_dir, "lambda-moved", b->pool);
SVN_ERR(sbox_wc_move(b, deleted_file, moved_file));
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));
/* Modify a file in the working copy. */
deleted_file = svn_relpath_join(trunk_path, "B/lambda", b->pool);
SVN_ERR(sbox_file_write(b, deleted_file, modified_file_content));
/* Update to HEAD.
* This should raise an "incoming move vs local edit" tree conflict. */
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
/* We should have a tree conflict in the directory "A/B". */
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A/B"), 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_dir_merge,
-1 /* end of list */
};
SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
}
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict, svn_client_conflict_option_incoming_move_dir_merge,
ctx, pool));
/* There should now be a tree conflict inside the moved directory,
* signaling a missing file. */
deleted_file = svn_relpath_join(moved_dir, "lambda" , b->pool);
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, deleted_file),
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);
SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) ==
svn_wc_conflict_reason_edited);
SVN_TEST_ASSERT(svn_client_conflict_get_incoming_change(conflict) ==
svn_wc_conflict_action_delete);
/* Make sure the file has the expected content. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, deleted_file), pool));
SVN_TEST_STRING_ASSERT(buf->data, modified_file_content);
/* 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));
}
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict, svn_client_conflict_option_incoming_move_file_text_merge,
ctx, pool));
/* Ensure that the deleted file is gone. */
SVN_ERR(svn_io_check_path(sbox_wc_path(b, deleted_file), &kind, b->pool));
SVN_TEST_ASSERT(kind == svn_node_none);
/* Ensure that the moved-target 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, moved_file),
&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_modified);
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 not be in conflict. */
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, moved_file),
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);
/* Make sure the file has the expected content. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, moved_file), pool));
SVN_TEST_STRING_ASSERT(buf->data, modified_file_content);
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;
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 a new file and 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 new file on the trunk\n"));
SVN_ERR(sbox_wc_add(b, new_file_path));
SVN_ERR(sbox_wc_propset(b, "prop", "propval", new_file_path));
SVN_ERR(sbox_wc_commit(b, ""));
/* Update into the past. */
SVN_ERR(sbox_wc_update(b, "", 1));
/* 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"));
SVN_ERR(sbox_wc_add(b, new_file_path));
SVN_ERR(sbox_wc_propset(b, "prop", propval_different, new_file_path));
/* Update to HEAD.
* 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));
opt_rev.kind = svn_opt_revision_head;
opt_rev.value.number = SVN_INVALID_REVNUM;
/* Ensure that the file has the expected status. */
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_replaced);
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);
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path),
ctx, b->pool, 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_file_text_merge(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_client_ctx_t *ctx;
svn_client_conflict_t *conflict;
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_file_text_merge",
opts, pool));
SVN_ERR(create_wc_with_file_add_vs_file_add_update_conflict(b));
/* Resolve the tree conflict. */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
new_file_path = svn_relpath_join(trunk_path, new_file_name, b->pool);
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path),
ctx, b->pool, b->pool));
/* 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_added_file_text_merge,
-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));
/* Check available tree conflict resolution options.
* The list of options remains unchanged after get_details(). */
{
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_added_file_text_merge,
-1 /* end of list */
};
SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
}
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_incoming_added_file_text_merge,
ctx, b->pool));
/* Ensure that the 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_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);
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path),
ctx, b->pool, b->pool));
/* We should have a text conflict instead of a tree 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);
/* Verify the merged property value. ### Should we have a prop conflict? */
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_merge_incoming_move_file_prop_merge_conflict(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;
const svn_string_t *base_propval;
const svn_string_t *working_propval;
const svn_string_t *incoming_old_propval;
const svn_string_t *incoming_new_propval;
SVN_ERR(svn_test__sandbox_create(
b, "merge_incoming_move_file_prop_merge_conflict", opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b));
/* Add a file property. */
SVN_ERR(sbox_wc_propset(b, "prop", "val-initial", "A/mu"));;
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 and edit the property. */
SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved"));
SVN_ERR(sbox_wc_propset(b, "prop", "val-trunk", "A/mu-moved"));;
SVN_ERR(sbox_wc_commit(b, ""));
/* On "branch", edit the same property. */
SVN_ERR(sbox_wc_propset(b, "prop", "val-branch", "A1/mu"));;
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 "trunk" to "branch". */
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));
/* We should now have a property conflict in the file "mu-moved". */
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, 1);
SVN_TEST_STRING_ASSERT(APR_ARRAY_IDX(props_conflicted, 0, const char *),
"prop");
SVN_TEST_ASSERT(!tree_conflicted);
/* Check available property conflict resolution options. */
{
svn_client_conflict_option_id_t expected_opts[] = {
svn_client_conflict_option_postpone,
svn_client_conflict_option_base_text,
svn_client_conflict_option_incoming_text,
svn_client_conflict_option_working_text,
svn_client_conflict_option_incoming_text_where_conflicted,
svn_client_conflict_option_working_text_where_conflicted,
svn_client_conflict_option_merged_text,
-1 /* end of list */
};
SVN_ERR(assert_prop_conflict_options(conflict, ctx, expected_opts, pool));
}
/* Check conflicted property values. */
SVN_ERR(svn_client_conflict_prop_get_propvals(&base_propval,
&working_propval,
&incoming_old_propval,
&incoming_new_propval,
conflict, "prop", pool));
/* ### Is this the proper expectation for base_propval? */
SVN_TEST_STRING_ASSERT(base_propval->data, "val-branch");
SVN_TEST_STRING_ASSERT(working_propval->data, "val-branch");
SVN_TEST_STRING_ASSERT(incoming_old_propval->data, "val-initial");
SVN_TEST_STRING_ASSERT(incoming_new_propval->data, "val-trunk");
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_incoming_move_file_text_merge_keywords(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_keywords", opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b));
/* Set svn:keywords on a file. */
SVN_ERR(sbox_wc_propset(b, SVN_PROP_KEYWORDS, "Revision", "A/mu"));;
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", begin using keywords in the file and move it. */
SVN_ERR(sbox_file_write(b, "A/mu", "$Revision$\n"));
SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved"));
SVN_ERR(sbox_wc_commit(b, ""));
/* On "branch", edit the file and make it equal to what's in trunk. */
SVN_ERR(sbox_file_write(b, "A1/mu", "$Revision$\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 (with expanded keywords). */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A1/mu-moved"),
pool));
SVN_TEST_STRING_ASSERT(buf->data, "$Revision: 5 $\n");
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 across 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);
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_ASSERT(status->moved_from_abspath == NULL);
SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
return SVN_NO_ERROR;
}
static svn_error_t *
test_cherry_pick_post_move_edit_dir(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_dir",
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 directory B. */
SVN_ERR(sbox_wc_move(b, "A/B", "A/B-moved"));
SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
/* On "trunk", edit B-moved/lambda. This will be r4. */
SVN_ERR(sbox_file_write(b, "A/B-moved/lambda", "Modified content."
APR_EOL_STR));
SVN_ERR(sbox_wc_commit(b, "")); /* r4 */
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
/* Perform a cherry-pick merge of r4 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 = 4;
merge_range.start.kind = svn_opt_revision_number;
merge_range.start.value.number = 3;
merge_range.end.kind = svn_opt_revision_number;
merge_range.end.value.number = 4;
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 missing 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/B-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_dir_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_dir_merge,
ctx, b->pool));
/* The node "B-moved" should no longer exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict,
sbox_wc_path(b, "A1/B-moved"),
ctx, pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* And "A1/B/lambda" should have expected contents. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A1/B/lambda"), pool));
SVN_TEST_STRING_ASSERT(buf->data, "Modified content." APR_EOL_STR);
return SVN_NO_ERROR;
}
static svn_error_t *
test_local_missing_abiguous_moves(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
svn_opt_revision_t opt_rev;
svn_client_ctx_t *ctx;
svn_client_conflict_t *conflict;
apr_array_header_t *options;
svn_client_conflict_option_t *option;
apr_array_header_t *possible_moved_to_repos_relpaths;
apr_array_header_t *possible_moved_to_abspaths;
struct status_baton sb;
struct svn_client_status_t *status;
svn_stringbuf_t *buf;
SVN_ERR(svn_test__sandbox_create(b, "local_missing_ambiguous_moves", opts,
pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
/* Create a copy of node "A" (the "trunk") to "A1" (the "branch"). */
SVN_ERR(sbox_wc_copy(b, "A", "A1"));
SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
/* Copy a file across branch boundaries (gives ambiguous WC targets later). */
SVN_ERR(sbox_wc_copy(b, "A/mu", "A1/mu-copied-from-A"));
/* Create an ambiguous move with the "trunk". */
SVN_ERR(sbox_wc_copy(b, "A/mu", "A/mu-copied"));
SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved"));
SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
/* Modify the moved file on the "branch". */
SVN_ERR(sbox_file_write(b, "A1/mu", "Modified content." APR_EOL_STR));
SVN_ERR(sbox_wc_commit(b, "")); /* r4 */
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
/* Merge "A1" ("branch") into "A" ("trunk"). */
opt_rev.kind = svn_opt_revision_head;
opt_rev.value.number = SVN_INVALID_REVNUM;
SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
SVN_ERR(svn_client_merge_peg5(svn_path_url_add_component2(b->repos_url, "A1",
pool),
NULL, &opt_rev, sbox_wc_path(b, "A"),
svn_depth_infinity,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
NULL, ctx, pool));
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A/mu"),
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,
-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_local_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_local_move_file_text_merge);
SVN_TEST_ASSERT(option != NULL);
/*
* Possible repository destinations for moved-away 'A/mu' are:
* (1): '^/A/mu-copied'
* (2): '^/A/mu-moved'
* (3): '^/A1/mu-copied-from-A'
*/
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, 3);
SVN_TEST_STRING_ASSERT(
APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 0, const char *),
"A/mu-copied");
SVN_TEST_STRING_ASSERT(
APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 1, const char *),
"A/mu-moved");
SVN_TEST_STRING_ASSERT(
APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 2, const char *),
"A1/mu-copied-from-A");
/* Move target for "A/mu-copied" (selected by default) is not ambiguous. */
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, "A/mu-copied"));
/* Move target for "A/mu-moved" is not ambiguous. */
SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(option, 1,
ctx, b->pool));
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, "A/mu-moved"));
/* Select move target "A1/mu-copied-from-A". */
SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(option, 2,
ctx, b->pool));
/*
* Possible working copy destinations for moved-away 'A/mu' are:
* (1): 'A/mu-copied-from-A'
* (2): 'A1/mu-copied-from-A'
*/
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, 2);
SVN_TEST_STRING_ASSERT(
APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *),
sbox_wc_path(b, "A/mu-copied-from-A"));
SVN_TEST_STRING_ASSERT(
APR_ARRAY_IDX(possible_moved_to_abspaths, 1, const char *),
sbox_wc_path(b, "A1/mu-copied-from-A"));
/* Select move target "A/mu-moved". */
SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(option, 1,
ctx, b->pool));
/* Resolve the tree conflict. */
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_local_move_file_text_merge, ctx,
b->pool));
/* The node "A/mu" should no longer exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict,
sbox_wc_path(b, "A/mu"),
ctx, pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* Ensure that the merged 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, "A/mu-moved"),
&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_modified);
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);
/* And it should have expected contents. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/mu-moved"),
pool));
SVN_TEST_STRING_ASSERT(buf->data, "Modified content." APR_EOL_STR);
return SVN_NO_ERROR;
}
static svn_error_t *
test_local_missing_abiguous_moves_dir(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
svn_opt_revision_t opt_rev;
svn_client_ctx_t *ctx;
svn_client_conflict_t *conflict;
apr_array_header_t *options;
svn_client_conflict_option_t *option;
apr_array_header_t *possible_moved_to_repos_relpaths;
apr_array_header_t *possible_moved_to_abspaths;
struct status_baton sb;
struct svn_client_status_t *status;
svn_stringbuf_t *buf;
SVN_ERR(svn_test__sandbox_create(b, "local_missing_ambiguous_moves_dir",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
/* Create a copy of node "A" (the "trunk") to "A1" (the "branch"). */
SVN_ERR(sbox_wc_copy(b, "A", "A1"));
SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
/* Copy a dir across branch boundaries (gives ambiguous WC targets later). */
SVN_ERR(sbox_wc_copy(b, "A/B", "A1/B-copied-from-A"));
/* Create an ambiguous move with the "trunk". */
SVN_ERR(sbox_wc_copy(b, "A/B", "A/B-copied"));
SVN_ERR(sbox_wc_move(b, "A/B", "A/B-moved"));
SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
/* Modify a file in the moved directory on the "branch". */
SVN_ERR(sbox_file_write(b, "A1/B/lambda", "Modified content." APR_EOL_STR));
SVN_ERR(sbox_wc_commit(b, "")); /* r4 */
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
/* Merge "A1" ("branch") into "A" ("trunk"). */
opt_rev.kind = svn_opt_revision_head;
opt_rev.value.number = SVN_INVALID_REVNUM;
SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
SVN_ERR(svn_client_merge_peg5(svn_path_url_add_component2(b->repos_url, "A1",
pool),
NULL, &opt_rev, sbox_wc_path(b, "A"),
svn_depth_infinity,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
NULL, ctx, pool));
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, "A/B"),
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,
-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_local_move_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_resolution_options(&options, conflict,
ctx, b->pool,
b->pool));
option = svn_client_conflict_option_find_by_id(
options, svn_client_conflict_option_local_move_dir_merge);
SVN_TEST_ASSERT(option != NULL);
/*
* Possible repository destinations for moved-away 'A/B' are:
* (1): '^/A/B-copied'
* (2): '^/A/B-moved'
* (3): '^/A1/B-copied-from-A'
*/
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, 3);
SVN_TEST_STRING_ASSERT(
APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 0, const char *),
"A/B-copied");
SVN_TEST_STRING_ASSERT(
APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 1, const char *),
"A/B-moved");
SVN_TEST_STRING_ASSERT(
APR_ARRAY_IDX(possible_moved_to_repos_relpaths, 2, const char *),
"A1/B-copied-from-A");
/* Move target for "A/B-copied" (selected by default) is not ambiguous. */
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, "A/B-copied"));
/* Move target for "A/B-moved" is not ambiguous. */
SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(option, 1,
ctx, b->pool));
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, "A/B-moved"));
/* Select move target "A1/mu-copied-from-A". */
SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(option, 2,
ctx, b->pool));
/*
* Possible working copy destinations for moved-away 'A/B-copied-from-A' are:
* (1): 'A/B-copied-from-A'
* (2): 'A1/B-copied-from-A'
*/
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, 2);
SVN_TEST_STRING_ASSERT(
APR_ARRAY_IDX(possible_moved_to_abspaths, 0, const char *),
sbox_wc_path(b, "A/B-copied-from-A"));
SVN_TEST_STRING_ASSERT(
APR_ARRAY_IDX(possible_moved_to_abspaths, 1, const char *),
sbox_wc_path(b, "A1/B-copied-from-A"));
/* Select move target "A/B-moved". */
SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(option, 1,
ctx, b->pool));
/* Resolve the tree conflict. */
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_local_move_dir_merge, ctx,
b->pool));
/* The node "A/B" should no longer exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict,
sbox_wc_path(b, "A/B"),
ctx, pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* Ensure that the merged 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, "A/B-moved/lambda"),
&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_modified);
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);
/* And it should have expected contents. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/B-moved/lambda"),
pool));
SVN_TEST_STRING_ASSERT(buf->data, "Modified content." APR_EOL_STR);
return SVN_NO_ERROR;
}
static svn_error_t *
test_file_vs_dir_move_merge_assertion_failure(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
svn_opt_revision_t opt_rev, peg_rev;
svn_client_ctx_t *ctx;
svn_client_conflict_t *conflict;
apr_array_header_t *options;
svn_client_conflict_option_t *option;
const char *wc_path;
SVN_ERR(svn_test__sandbox_create(b,
"file_vs_dir_move_merge_assertion_failure",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
/* Create a copy of node "A" (the "trunk") to "A1" (the "branch"). */
SVN_ERR(sbox_wc_copy(b, "A", "A1"));
SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
/* Move and modify file on the "branch" */
SVN_ERR(sbox_wc_move(b, "A1/B/lambda", "A1/B/lambda-moved"));
SVN_ERR(sbox_file_write(b, "A1/B/lambda-moved",
"Modified content." APR_EOL_STR));
SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
/* Move a directory and modify a file inside of it on the "trunk". */
SVN_ERR(sbox_wc_move(b, "A/B", "A/B-moved"));
SVN_ERR(sbox_file_write(b, "A/B-moved/lambda",
"Modified content." APR_EOL_STR));
SVN_ERR(sbox_wc_commit(b, "")); /* r4 */
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
/* Create a fresh working copy for "A1" ("branch"). */
wc_path = svn_test_data_path("file_vs_dir_move_merge_assertion_failure2",
pool);
SVN_ERR(svn_io_remove_dir2(wc_path, TRUE, NULL, NULL, pool));
SVN_ERR(svn_io_make_dir_recursively(wc_path, pool));
svn_test_add_dir_cleanup(wc_path);
/* Merge "A" ("trunk") into a fresh working copy of "A1" ("branch"). */
opt_rev.kind = svn_opt_revision_head;
opt_rev.value.number = SVN_INVALID_REVNUM;
peg_rev.kind = svn_opt_revision_unspecified;
SVN_ERR(svn_test__create_client_ctx(&ctx, b, pool));
SVN_ERR(svn_client_checkout3(NULL, svn_path_url_add_component2(b->repos_url,
"A1", pool),
wc_path, &peg_rev, &opt_rev, svn_depth_infinity,
TRUE, FALSE, ctx, pool));
SVN_ERR(svn_client_merge_peg5(svn_path_url_add_component2(b->repos_url, "A",
pool),
NULL, &opt_rev, wc_path, svn_depth_infinity,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
NULL, ctx, pool));
SVN_ERR(svn_client_conflict_get(&conflict,
svn_dirent_join(wc_path, "B", b->pool),
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_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_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);
/* Resolve this conflict. Another one will be raised. */
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_incoming_move_dir_merge, ctx,
b->pool));
SVN_ERR(svn_client_conflict_get(&conflict,
svn_dirent_join_many(b->pool,
wc_path, "B-moved", "lambda", NULL),
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,
-1 /* end of list */
};
SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
b->pool));
}
/* This used to trigger an assertion failure:
* svn_tests: E235000: In file 'subversion/libsvn_client/conflicts.c' \
* line 2242: assertion failed (start_rev > end_rev) */
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,
-1 /* end of list */
};
SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts,
b->pool));
}
/* Subversion is not yet smart enough to resolve this tree conflict. */
return SVN_NO_ERROR;
}
static svn_error_t *
test_update_file_add_vs_unversiond_file(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
svn_client_conflict_t *conflict;
svn_client_ctx_t *ctx;
struct status_baton sb;
struct svn_client_status_t *status;
svn_opt_revision_t opt_rev;
svn_stringbuf_t *buf;
SVN_ERR(svn_test__sandbox_create(b, "update_file_add_vs_unversioned_file",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
/* Add a new file. */
SVN_ERR(sbox_file_write(b, new_file_name, new_file_content));
SVN_ERR(sbox_wc_add(b, new_file_name));
SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
SVN_ERR(sbox_wc_update(b, "", 1)); /* back to r1 */
/* Create an identical unversioned file. */
SVN_ERR(sbox_file_write(b, new_file_name, new_file_content));
SVN_ERR(sbox_wc_update(b, "", 2)); /* back to r2 */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_name),
ctx, b->pool, b->pool));
SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) ==
svn_wc_conflict_reason_unversioned);
SVN_TEST_ASSERT(svn_client_conflict_get_incoming_change(conflict) ==
svn_wc_conflict_action_add);
{
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_added_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_resolve_by_id(
conflict,
svn_client_conflict_option_incoming_added_file_text_merge,
ctx, b->pool));
/* Ensure that the 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_name),
&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_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);
/* Ensure that the file has the expected content. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, new_file_name),
b->pool));
SVN_TEST_STRING_ASSERT(buf->data, new_file_content);
return SVN_NO_ERROR;
}
static svn_error_t *
test_switch_file_add_vs_unversiond_file(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
svn_client_conflict_t *conflict;
svn_client_ctx_t *ctx;
struct status_baton sb;
struct svn_client_status_t *status;
svn_opt_revision_t opt_rev;
svn_stringbuf_t *buf;
svn_revnum_t result_rev;
const char *trunk_url;
const char *new_file_path;
SVN_ERR(svn_test__sandbox_create(b, "switch_file_add_vs_unversioned_file",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
/* Create a branch of node "A". */
SVN_ERR(sbox_wc_copy(b, trunk_path, branch_path));
SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
/* Add a new file on trunk. */
new_file_path = svn_relpath_join(trunk_path, new_file_name, b->pool);
SVN_ERR(sbox_file_write(b, new_file_path, new_file_content));
SVN_ERR(sbox_wc_add(b, new_file_path));
SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
SVN_ERR(sbox_wc_update(b, "", 2)); /* back to r2 */
/* Create an identical unversioned file on the branch. */
new_file_path = svn_relpath_join(branch_path, new_file_name, b->pool);
SVN_ERR(sbox_file_write(b, new_file_path, new_file_content));
/* Switch branch to trunk. */
trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, SVN_VA_NULL);
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;
SVN_ERR(svn_client_switch3(&result_rev, sbox_wc_path(b, branch_path),
trunk_url, &opt_rev, &opt_rev,
svn_depth_infinity,
TRUE, FALSE, FALSE, FALSE, ctx, b->pool));
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_file_path),
ctx, b->pool, b->pool));
SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) ==
svn_wc_conflict_reason_unversioned);
SVN_TEST_ASSERT(svn_client_conflict_get_incoming_change(conflict) ==
svn_wc_conflict_action_add);
{
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_added_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_resolve_by_id(
conflict,
svn_client_conflict_option_incoming_added_file_text_merge,
ctx, b->pool));
/* Ensure that the 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_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);
/* Ensure that the 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, new_file_content);
return SVN_NO_ERROR;
}
static svn_error_t *
create_unversioned_dir(const char **new_file_path,
const char **unversioned_file_path,
const char *new_dir_path,
svn_test__sandbox_t *b, apr_pool_t *pool)
{
apr_file_t *file;
apr_size_t content_len;
/* Create an unversioned directory. */
SVN_ERR(svn_io_dir_make(sbox_wc_path(b, new_dir_path), APR_OS_DEFAULT,
b->pool));
/* Create an unversioned file which will collide with a versioned file. */
*new_file_path = svn_relpath_join(new_dir_path, new_file_name, b->pool);
SVN_ERR(svn_io_file_open(&file, sbox_wc_path(b, *new_file_path),
(APR_READ | APR_WRITE | APR_CREATE | APR_TRUNCATE), APR_OS_DEFAULT,
b->pool));
content_len = strlen(unversioned_file_content);
SVN_ERR(svn_io_file_write(file, unversioned_file_content, &content_len,
b->pool));
SVN_ERR(svn_io_file_close(file, b->pool));
/* Create another unversioned file at a different path. */
*unversioned_file_path = svn_relpath_join(new_dir_path, unversioned_file_name,
b->pool);
SVN_ERR(svn_io_file_open(&file, sbox_wc_path(b, *unversioned_file_path),
(APR_READ | APR_WRITE | APR_CREATE | APR_TRUNCATE), APR_OS_DEFAULT,
b->pool));
content_len = strlen(unversioned_file_content);
SVN_ERR(svn_io_file_write(file, unversioned_file_content, &content_len,
b->pool));
SVN_ERR(svn_io_file_close(file, b->pool));
return SVN_NO_ERROR;
}
static svn_error_t *
resolve_added_dir_vs_unversioned_dir(const char *new_dir_path,
const char *new_file_path,
const char *unversioned_file_path,
svn_test__sandbox_t *b, apr_pool_t *pool)
{
svn_client_ctx_t *ctx;
svn_client_conflict_t *conflict;
svn_opt_revision_t opt_rev;
struct status_baton sb;
struct svn_client_status_t *status;
svn_stringbuf_t *buf;
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
SVN_ERR(svn_client_conflict_get(&conflict, sbox_wc_path(b, new_dir_path),
ctx, b->pool, b->pool));
SVN_TEST_ASSERT(svn_client_conflict_get_local_change(conflict) ==
svn_wc_conflict_reason_unversioned);
SVN_TEST_ASSERT(svn_client_conflict_get_incoming_change(conflict) ==
svn_wc_conflict_action_add);
{
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_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_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_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);
/* Ensure that the "collision" 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_modified);
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);
/* Ensure that the 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, unversioned_file_content);
/* Ensure that the unversioned file has the expected status. */
opt_rev.kind = svn_opt_revision_working;
sb.result_pool = b->pool;
SVN_TEST_ASSERT_ERROR(
svn_client_status6(NULL, ctx, sbox_wc_path(b, unversioned_file_path),
&opt_rev, svn_depth_unknown, TRUE, TRUE,
TRUE, TRUE, FALSE, TRUE, NULL,
status_func, &sb, b->pool),
SVN_ERR_ENTRY_NOT_FOUND);
/* Ensure that the file has the expected content. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, unversioned_file_path),
b->pool));
SVN_TEST_STRING_ASSERT(buf->data, unversioned_file_content);
return SVN_NO_ERROR;
}
static svn_error_t *
test_update_dir_add_vs_unversioned_dir(const svn_test_opts_t *opts,
apr_pool_t *pool)
{
svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
const char *new_dir_path;
const char *new_file_path;
const char *unversioned_file_path;
SVN_ERR(svn_test__sandbox_create(b, "update_dir_add_vs_unversioned_dir",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
/* Add a new directory */
new_dir_path = svn_relpath_join(trunk_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, new_file_content));
SVN_ERR(sbox_wc_add(b, new_file_path));
SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
SVN_ERR(sbox_wc_update(b, "", 1)); /* back to r1 */
new_dir_path = svn_relpath_join(trunk_path, new_dir_name, b->pool);
SVN_ERR(create_unversioned_dir(&new_file_path, &unversioned_file_path,
new_dir_path, b, b->pool));
SVN_ERR(sbox_wc_update(b, "", 2)); /* back to r2 */
SVN_ERR(resolve_added_dir_vs_unversioned_dir(new_dir_path, new_file_path,
unversioned_file_path,
b, b->pool));
return SVN_NO_ERROR;
}
static svn_error_t *
test_switch_dir_add_vs_unversioned_dir(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_revnum_t result_rev;
const char *trunk_url;
const char *new_dir_path;
const char *new_file_path;
const char *unversioned_file_path;
SVN_ERR(svn_test__sandbox_create(b, "switch_dir_add_vs_unversioned_dir",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
/* Create a branch of node "A". */
SVN_ERR(sbox_wc_copy(b, trunk_path, branch_path));
SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
/* Add a new directory on trunk. */
new_dir_path = svn_relpath_join(trunk_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, new_file_content));
SVN_ERR(sbox_wc_add(b, new_file_path));
SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
SVN_ERR(sbox_wc_update(b, "", 2)); /* back to r2 */
new_dir_path = svn_relpath_join(branch_path, new_dir_name, b->pool);
SVN_ERR(create_unversioned_dir(&new_file_path, &unversioned_file_path,
new_dir_path, b, b->pool));
/* Switch branch to trunk. */
trunk_url = apr_pstrcat(b->pool, b->repos_url, "/", trunk_path, SVN_VA_NULL);
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;
SVN_ERR(svn_client_switch3(&result_rev, sbox_wc_path(b, branch_path),
trunk_url, &opt_rev, &opt_rev,
svn_depth_infinity,
TRUE, FALSE, FALSE, FALSE, ctx, b->pool));
SVN_ERR(resolve_added_dir_vs_unversioned_dir(new_dir_path, new_file_path,
unversioned_file_path,
b, b->pool));
return SVN_NO_ERROR;
}
static svn_error_t *
create_file_move_vs_file_move_merge_conflict(svn_client_conflict_t **conflict,
svn_boolean_t edit_file,
svn_test__sandbox_t *b,
svn_client_ctx_t *ctx)
{
svn_opt_revision_t opt_rev;
const char *branch_url;
/* Create a branch of node "A". */
SVN_ERR(sbox_wc_copy(b, "A", "A2"));
SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
/* Move a file on trunk. */
SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved"));
SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
/* Move the same file to a different location on the branch. */
SVN_ERR(sbox_wc_move(b, "A2/mu", "A2/mu-also-moved"));
SVN_ERR(sbox_wc_commit(b, ""));
if (edit_file)
{
/* Edit moved a file on the branch. */
SVN_ERR(sbox_file_write(b, "A2/mu-also-moved", modified_file_content));
SVN_ERR(sbox_wc_commit(b, ""));
}
/* Merge branch to trunk. */
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
branch_url = apr_pstrcat(b->pool, b->repos_url, "/A2", SVN_VA_NULL);
opt_rev.kind = svn_opt_revision_head;
opt_rev.value.number = SVN_INVALID_REVNUM;
SVN_ERR(svn_client_merge_peg5(branch_url, NULL, &opt_rev,
sbox_wc_path(b, "A"),
svn_depth_infinity,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
NULL, ctx, b->pool));
SVN_ERR(svn_client_conflict_get(conflict, sbox_wc_path(b, "A/mu"),
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_both_moved_file_merge,
svn_client_conflict_option_both_moved_file_move_merge,
-1 /* end of list */
};
SVN_ERR(assert_tree_conflict_options(*conflict, ctx, expected_opts,
b->pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_file_move_vs_file_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;
svn_client_conflict_t *conflict;
svn_opt_revision_t opt_rev;
struct status_baton sb;
struct svn_client_status_t *status;
SVN_ERR(svn_test__sandbox_create(b, "merge_file_move_vs_file_move",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
SVN_ERR(create_file_move_vs_file_move_merge_conflict(&conflict,
FALSE, b, ctx));
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_both_moved_file_merge,
ctx, b->pool));
/* The node "A/mu" should no longer exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(
&conflict, sbox_wc_path(b, "A/mu"), ctx, pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* The node "A/mu-also-moved" should not exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(
&conflict, sbox_wc_path(b, "A/mu-also-moved"), ctx,
pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* Ensure that the merged 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, "A/mu-moved"),
&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_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);
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_file_move_vs_file_move_accept_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;
svn_client_conflict_t *conflict;
svn_opt_revision_t opt_rev;
struct status_baton sb;
struct svn_client_status_t *status;
SVN_ERR(svn_test__sandbox_create(b,
"merge_file_move_vs_file_move_accept_move",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
SVN_ERR(create_file_move_vs_file_move_merge_conflict(&conflict,
FALSE, b, ctx));
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_both_moved_file_move_merge,
ctx, b->pool));
/* The node "A/mu" should no longer exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict,
sbox_wc_path(b, "A/mu"),
ctx, pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* The node "A/mu-moved" should be moved to "A/mu-also-moved". */
opt_rev.kind = svn_opt_revision_working;
sb.result_pool = b->pool;
SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-moved"),
&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);
SVN_TEST_STRING_ASSERT(status->moved_to_abspath,
sbox_wc_path(b, "A/mu-also-moved"));
/* Ensure that the merged 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, "A/mu-also-moved"),
&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, "A/mu-moved"));
SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_file_edit_move_vs_file_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;
svn_client_conflict_t *conflict;
svn_opt_revision_t opt_rev;
struct status_baton sb;
struct svn_client_status_t *status;
svn_stringbuf_t *buf;
SVN_ERR(svn_test__sandbox_create(b, "merge_file_edit_move_vs_file_move",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
SVN_ERR(create_file_move_vs_file_move_merge_conflict(&conflict,
TRUE, b, ctx));
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_both_moved_file_merge,
ctx, b->pool));
/* The node "A/mu" should no longer exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(
&conflict, sbox_wc_path(b, "A/mu"), ctx, pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* The node "A/mu-also-moved" should not exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(
&conflict, sbox_wc_path(b, "A/mu-also-moved"), ctx,
pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* Ensure that the merged 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, "A/mu-moved"),
&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_modified);
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);
/* Make sure the file has the expected content. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/mu-moved"), pool));
SVN_TEST_STRING_ASSERT(buf->data, modified_file_content);
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_file_edit_move_vs_file_move_accept_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;
svn_client_conflict_t *conflict;
svn_opt_revision_t opt_rev;
struct status_baton sb;
struct svn_client_status_t *status;
svn_stringbuf_t *buf;
SVN_ERR(svn_test__sandbox_create(
b, "merge_file_edit_move_vs_file_move_accept_move", opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
SVN_ERR(create_file_move_vs_file_move_merge_conflict(&conflict,
TRUE, b, ctx));
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_both_moved_file_move_merge,
ctx, b->pool));
/* The node "A/mu" should no longer exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict,
sbox_wc_path(b, "A/mu"),
ctx, pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* The node "A/mu-moved" should be moved to "A/mu-also-moved". */
opt_rev.kind = svn_opt_revision_working;
sb.result_pool = b->pool;
SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-moved"),
&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);
SVN_TEST_STRING_ASSERT(status->moved_to_abspath,
sbox_wc_path(b, "A/mu-also-moved"));
/* Ensure that the merged 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, "A/mu-also-moved"),
&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_STRING_ASSERT(status->moved_from_abspath,
sbox_wc_path(b, "A/mu-moved"));
SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
/* Make sure the file has the expected content. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/mu-also-moved"),
pool));
SVN_TEST_STRING_ASSERT(buf->data, modified_file_content);
return SVN_NO_ERROR;
}
static svn_error_t *
create_dir_move_vs_dir_move_merge_conflict(svn_client_conflict_t **conflict,
svn_test__sandbox_t *b,
svn_client_ctx_t *ctx)
{
svn_opt_revision_t opt_rev;
const char *branch_url;
apr_array_header_t *options;
svn_client_conflict_option_t *option;
apr_array_header_t *possible_moved_to_abspaths;
/* Create a branch of node "A". */
SVN_ERR(sbox_wc_copy(b, "A", "A2"));
SVN_ERR(sbox_wc_commit(b, "")); /* r2 */
/* Move a directory on trunk. */
SVN_ERR(sbox_wc_move(b, "A/B", "A/B-moved"));
SVN_ERR(sbox_wc_commit(b, "")); /* r3 */
/* Edit a file in the moved directory on trunk. */
SVN_ERR(sbox_file_write(b, "A/B-moved/E/alpha",
modified_file_content));
SVN_ERR(sbox_wc_commit(b, ""));
/* Move the same directory to a different location on the branch. */
SVN_ERR(sbox_wc_move(b, "A2/B", "A2/B-also-moved"));
SVN_ERR(sbox_wc_commit(b, ""));
/* Edit a file in the moved directory on the branch. */
SVN_ERR(sbox_file_write(b, "A2/B-also-moved/lambda",
modified_file_content));
SVN_ERR(sbox_wc_commit(b, ""));
/* Merge branch to trunk. */
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
branch_url = apr_pstrcat(b->pool, b->repos_url, "/A2", SVN_VA_NULL);
opt_rev.kind = svn_opt_revision_head;
opt_rev.value.number = SVN_INVALID_REVNUM;
SVN_ERR(svn_client_merge_peg5(branch_url, NULL, &opt_rev,
sbox_wc_path(b, "A"),
svn_depth_infinity,
FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,
NULL, ctx, b->pool));
SVN_ERR(svn_client_conflict_get(conflict, sbox_wc_path(b, "A/B"),
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_both_moved_dir_merge,
svn_client_conflict_option_both_moved_dir_move_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_both_moved_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 move destinations because both
* branches are checked out into the same working copy.
*
* Possible working copy destinations for moved-away 'A/B' are:
* (1): 'A/B-also-moved
* (2): 'A2/B-also-moved
* 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, "A/B-also-moved"));
SVN_TEST_STRING_ASSERT(
APR_ARRAY_IDX(possible_moved_to_abspaths, 1, const char *),
sbox_wc_path(b, "A2/B-also-moved"));
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_dir_move_vs_dir_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;
svn_client_conflict_t *conflict;
svn_opt_revision_t opt_rev;
struct status_baton sb;
struct svn_client_status_t *status;
svn_stringbuf_t *buf;
SVN_ERR(svn_test__sandbox_create(b, "merge_dir_move_vs_dir_move",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
SVN_ERR(create_dir_move_vs_dir_move_merge_conflict(&conflict, b, ctx));
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_both_moved_dir_merge,
ctx, b->pool));
/* The node "A/B" should not exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict,
sbox_wc_path(b, "A/B"),
ctx, pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* The node "A/B-also-moved" should not exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(
&conflict,
sbox_wc_path(b, "A/B-also-moved"), ctx, pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* Ensure that the merged 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, "A/B-moved"),
&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);
/* Make sure the edited files have the expected content. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/B-moved/lambda"),
pool));
SVN_TEST_STRING_ASSERT(buf->data, modified_file_content);
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/B-moved/E/alpha"),
pool));
SVN_TEST_STRING_ASSERT(buf->data, modified_file_content);
return SVN_NO_ERROR;
}
static svn_error_t *
test_merge_dir_move_vs_dir_move_accept_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;
svn_client_conflict_t *conflict;
svn_opt_revision_t opt_rev;
struct status_baton sb;
struct svn_client_status_t *status;
svn_stringbuf_t *buf;
SVN_ERR(svn_test__sandbox_create(b, "merge_dir_move_vs_dir_move_accept_move",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
SVN_ERR(create_dir_move_vs_dir_move_merge_conflict(&conflict, b, ctx));
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_both_moved_dir_move_merge,
ctx, b->pool));
/* The node "A/B" should not exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(&conflict,
sbox_wc_path(b, "A/B"),
ctx, pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* The node "A/B-moved" should be moved to A/B-also-moved. */
opt_rev.kind = svn_opt_revision_working;
sb.result_pool = b->pool;
SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/B-moved"),
&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, "A/B-also-moved"));
/* Ensure that the merged directory has the expected status. */
SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/B-also-moved"),
&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, "A/B-moved"));
SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
/* Make sure the edited files have the expected content. */
SVN_ERR(svn_stringbuf_from_file2(&buf,
sbox_wc_path(b, "A/B-also-moved/lambda"),
pool));
SVN_TEST_STRING_ASSERT(buf->data, modified_file_content);
SVN_ERR(svn_stringbuf_from_file2(&buf,
sbox_wc_path(b, "A/B-also-moved/E/alpha"),
pool));
SVN_TEST_STRING_ASSERT(buf->data, modified_file_content);
return SVN_NO_ERROR;
}
static svn_error_t *
create_file_move_vs_file_move_update_conflict(svn_client_conflict_t **conflict,
svn_test__sandbox_t *b,
svn_client_ctx_t *ctx)
{
apr_array_header_t *options;
svn_client_conflict_option_t *option;
apr_array_header_t *possible_moved_to_abspaths;
/* Move a file. */
SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved"));
/* Edit moved file. */
SVN_ERR(sbox_file_write(b, "A/mu-moved", modified_file_content));
SVN_ERR(sbox_wc_commit(b, ""));
/* Update back to r1, */
SVN_ERR(sbox_wc_update(b, "", 1)); /* r2 */
/* Copy the file to test handling of ambiguous moves. */
SVN_ERR(sbox_wc_copy(b, "A/mu", "A/mu-copied"));
/* Move the same file to a different location. */
SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-also-moved"));
/* Edit moved file. */
SVN_ERR(sbox_file_write(b, "A/mu-also-moved",
modified_file_in_working_copy_content));
/* Update to r2. */
/* This should raise an "incoming delete vs local delete" tree conflict. */
SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
SVN_ERR(svn_client_conflict_get(conflict, sbox_wc_path(b, "A/mu"),
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_both_moved_file_merge,
svn_client_conflict_option_both_moved_file_move_merge,
-1 /* end of list */
};
SVN_ERR(assert_tree_conflict_options(*conflict, ctx, expected_opts,
b->pool));
}
/* Check possible move destinations for the file. */
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_both_moved_file_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 file:
*
* Possible working copy destinations for moved-away 'A/mu' are:
* (1): 'A/mu-also-moved'
* (2): 'A/mu-copied'
* 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, "A/mu-also-moved"));
SVN_TEST_STRING_ASSERT(
APR_ARRAY_IDX(possible_moved_to_abspaths, 1, const char *),
sbox_wc_path(b, "A/mu-copied"));
return SVN_NO_ERROR;
}
static svn_error_t *
test_update_file_move_vs_file_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;
svn_client_conflict_t *conflict;
svn_opt_revision_t opt_rev;
struct status_baton sb;
struct svn_client_status_t *status;
svn_stringbuf_t *buf;
svn_node_kind_t kind;
char *conflicted_content;
SVN_ERR(svn_test__sandbox_create(b, "update_file_move_vs_file_move",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
SVN_ERR(create_file_move_vs_file_move_update_conflict(&conflict, b, ctx));
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_both_moved_file_merge,
ctx, b->pool));
/* The node "A/mu" should no longer exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(
&conflict, sbox_wc_path(b, "A/mu"), ctx, pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* The node "A/mu-moved" should now have moved to "A/mu-also-moved. */
SVN_ERR(svn_io_check_path(sbox_wc_path(b, "A/mu"), &kind, b->pool));
SVN_TEST_ASSERT(kind == svn_node_none);
opt_rev.kind = svn_opt_revision_working;
sb.result_pool = b->pool;
SVN_ERR(svn_client_status6(NULL, ctx, sbox_wc_path(b, "A/mu-moved"),
&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);
SVN_TEST_STRING_ASSERT(status->moved_to_abspath,
sbox_wc_path(b, "A/mu-also-moved"));
/* Ensure that the merged 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, "A/mu-also-moved"),
&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_conflicted);
SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted);
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, "A/mu-moved"));
SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
/* Ensure that the moved+merged file has the expected content. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/mu-also-moved"),
b->pool));
conflicted_content = apr_psprintf(b->pool,
"<<<<<<< .working\n"
"%s"
"||||||| .old\n"
"This is the file 'mu'.\n"
"=======\n"
"%s"
">>>>>>> .new\n",
modified_file_in_working_copy_content,
modified_file_content);
SVN_TEST_STRING_ASSERT(buf->data, conflicted_content);
return SVN_NO_ERROR;
}
/* Same test case as above, but accept the incoming move. */
static svn_error_t *
test_update_file_move_vs_file_move_accept_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;
svn_client_conflict_t *conflict;
svn_opt_revision_t opt_rev;
struct status_baton sb;
struct svn_client_status_t *status;
svn_stringbuf_t *buf;
char *conflicted_content;
SVN_ERR(svn_test__sandbox_create(b,
"update_file_move_vs_file_move_accept_move",
opts, pool));
SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
SVN_ERR(create_file_move_vs_file_move_update_conflict(&conflict, b, ctx));
SVN_ERR(svn_client_conflict_tree_resolve_by_id(
conflict,
svn_client_conflict_option_both_moved_file_move_merge,
ctx, b->pool));
/* The node "A/mu" should no longer exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(
&conflict, sbox_wc_path(b, "A/mu"), ctx, pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* The node "A/mu-also-moved" should not exist. */
SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(
&conflict, sbox_wc_path(b, "A/mu-also-moved"), ctx,
pool, pool),
SVN_ERR_WC_PATH_NOT_FOUND);
/* Ensure that the merged 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, "A/mu-moved"),
&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_conflicted);
SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted);
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 moved+merged file has the expected content. */
SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/mu-moved"),
b->pool));
conflicted_content = apr_psprintf(b->pool,
"<<<<<<< .working\n" /* ### labels need fixing */
"%s"
"||||||| .old\n"
"This is the file 'mu'.\n"
"=======\n"
"%s"
">>>>>>> .new\n",
modified_file_content,
modified_file_in_working_copy_content);
SVN_TEST_STRING_ASSERT(buf->data, conflicted_content);
return SVN_NO_ERROR;
}
/* ========================================================================== */
static int max_threads = 1;
static struct svn_test_descriptor_t test_funcs[] =
{
SVN_TEST_NULL,
SVN_TEST_OPTS_PASS(test_merge_incoming_added_file_text_merge,
"merge incoming add file text merge"),
SVN_TEST_OPTS_PASS(test_merge_incoming_added_file_replace_and_merge,
"merge incoming add file replace and merge"),
SVN_TEST_OPTS_PASS(test_merge_incoming_added_dir_ignore,
"merge incoming add dir ignore"),
SVN_TEST_OPTS_PASS(test_merge_incoming_added_dir_merge,
"merge incoming add dir merge"),
SVN_TEST_OPTS_PASS(test_merge_incoming_added_dir_merge2,
"merge incoming add dir merge with file change"),
SVN_TEST_OPTS_PASS(test_merge_incoming_added_dir_merge3,
"merge incoming add dir merge with move history"),
SVN_TEST_OPTS_PASS(test_merge_incoming_added_dir_replace,
"merge incoming add dir replace"),
SVN_TEST_OPTS_PASS(test_merge_incoming_added_dir_replace_and_merge,
"merge incoming add dir replace and merge"),
SVN_TEST_OPTS_PASS(test_merge_incoming_added_dir_replace_and_merge2,
"merge incoming add dir replace with file change"),
SVN_TEST_OPTS_PASS(test_merge_incoming_delete_file_ignore,
"merge incoming delete file ignore"),
SVN_TEST_OPTS_PASS(test_merge_incoming_delete_file_accept,
"merge incoming delete file accept"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_file_text_merge,
"merge incoming move file text merge"),
SVN_TEST_OPTS_PASS(test_update_incoming_delete_file_ignore,
"update incoming delete file ignore"),
SVN_TEST_OPTS_PASS(test_update_incoming_delete_file_accept,
"update incoming delete file accept"),
SVN_TEST_OPTS_PASS(test_update_incoming_move_file_text_merge,
"update incoming move file text merge"),
SVN_TEST_OPTS_PASS(test_switch_incoming_move_file_text_merge,
"switch incoming move file text merge"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_dir,
"merge incoming move dir"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_dir2,
"merge incoming move dir with local edit"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_dir3,
"merge incoming move dir with local add"),
SVN_TEST_OPTS_PASS(test_merge_incoming_delete_vs_local_delete,
"merge incoming delete vs local delete"),
SVN_TEST_OPTS_PASS(test_merge_file_prop,
"merge file property"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_file_text_merge_conflict,
"merge incoming move file merge with text conflict"),
SVN_TEST_OPTS_PASS(test_merge_incoming_edit_file_moved_away,
"merge incoming edit for a moved-away working file"),
SVN_TEST_OPTS_PASS(test_merge_incoming_chained_move_local_edit,
"merge incoming chained move vs local edit"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_dir_with_moved_file,
"merge incoming moved dir with moved file"),
SVN_TEST_OPTS_PASS(test_merge_incoming_file_move_new_line_of_history,
"merge incoming file move with new line of history"),
SVN_TEST_OPTS_PASS(test_update_incoming_dir_move_with_nested_file_move,
"update incoming dir move with nested file move"),
SVN_TEST_OPTS_PASS(test_update_incoming_dir_move_with_parent_move,
"update incoming dir move with parent move"),
SVN_TEST_OPTS_PASS(test_update_incoming_dir_move_with_parent_moved_back,
"update incoming dir move with parent moved back"),
SVN_TEST_OPTS_PASS(test_update_incoming_dir_move_with_parent_moved_twice,
"update incoming dir move with parent moved twice"),
SVN_TEST_OPTS_PASS(test_update_incoming_added_file_text_merge,
"update incoming add file text merge"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_file_prop_merge_conflict,
"merge incoming move file merge with prop conflict"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_file_text_merge_keywords,
"merge incoming move file merge with keywords"),
SVN_TEST_OPTS_PASS(test_update_incoming_added_dir_ignore,
"update incoming add dir ignore"),
SVN_TEST_OPTS_PASS(test_update_incoming_added_dir_merge,
"update incoming add dir merge"),
SVN_TEST_OPTS_PASS(test_update_incoming_added_dir_merge2,
"update incoming add dir merge with obstructions"),
SVN_TEST_OPTS_PASS(test_cherry_pick_moved_file_with_propdel,
"cherry-pick with moved file and propdel"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_file_text_merge_crlf,
"merge incoming move file merge with CRLF eols"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_file_text_merge_native_eol,
"merge incoming move file merge with native eols"),
SVN_TEST_OPTS_PASS(test_cherry_pick_post_move_edit,
"cherry-pick edit from moved file"),
SVN_TEST_OPTS_PASS(test_merge_incoming_move_dir_across_branches,
"merge incoming dir move across branches"),
SVN_TEST_OPTS_PASS(test_update_incoming_delete_locally_deleted_file,
"update incoming delete to deleted file (#4739)"),
SVN_TEST_OPTS_PASS(test_merge_two_added_dirs_assertion_failure,
"merge two added dirs assertion failure (#4744)"),
SVN_TEST_OPTS_PASS(test_merge_incoming_delete_file_unrelated_move,
"do not suggest unrelated move targets (#4766)"),
SVN_TEST_OPTS_PASS(test_cherry_pick_post_move_edit_dir,
"cherry-pick edit from moved directory"),
SVN_TEST_OPTS_PASS(test_local_missing_abiguous_moves,
"local missing conflict with ambiguous moves"),
SVN_TEST_OPTS_PASS(test_local_missing_abiguous_moves_dir,
"local missing conflict with ambiguous dir moves"),
SVN_TEST_OPTS_PASS(test_file_vs_dir_move_merge_assertion_failure,
"file v dir move merge assertion failure"),
SVN_TEST_OPTS_PASS(test_update_file_add_vs_unversiond_file,
"file add vs unversioned file during update"),
SVN_TEST_OPTS_PASS(test_switch_file_add_vs_unversiond_file,
"file add vs unversioned file during switch"),
SVN_TEST_OPTS_PASS(test_update_dir_add_vs_unversioned_dir,
"dir add vs unversioned dir during update"),
SVN_TEST_OPTS_PASS(test_switch_dir_add_vs_unversioned_dir,
"dir add vs unversioned dir during switch"),
SVN_TEST_OPTS_PASS(test_merge_file_move_vs_file_move,
"file move vs file move during merge"),
SVN_TEST_OPTS_PASS(test_merge_file_move_vs_file_move_accept_move,
"file move vs file move during merge accept move"),
SVN_TEST_OPTS_PASS(test_merge_file_edit_move_vs_file_move,
"file move vs file edit-move during merge"),
SVN_TEST_OPTS_PASS(test_merge_file_edit_move_vs_file_move_accept_move,
"file edit-move vs file move merge accept move"),
SVN_TEST_OPTS_PASS(test_merge_dir_move_vs_dir_move,
"dir move vs dir move during merge"),
SVN_TEST_OPTS_PASS(test_merge_dir_move_vs_dir_move_accept_move,
"dir move vs dir move during merge accept move"),
SVN_TEST_OPTS_PASS(test_update_file_move_vs_file_move,
"file move vs file move during update"),
SVN_TEST_OPTS_PASS(test_update_file_move_vs_file_move_accept_move,
"file move vs file move during update accept move"),
SVN_TEST_NULL
};
SVN_TEST_MAIN