You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2016/11/19 14:14:21 UTC
svn commit: r1770496 - in /subversion/trunk/subversion:
libsvn_client/conflicts.c tests/libsvn_client/conflicts-test.c
Author: stsp
Date: Sat Nov 19 14:14:21 2016
New Revision: 1770496
URL: http://svn.apache.org/viewvc?rev=1770496&view=rev
Log:
Implement conflict option 'merge files' for 'add file vs add file' upon update.
* subversion/libsvn_client/conflicts.c
(resolve_merge_incoming_added_file_text_update): New helper function.
This function keeps the repository's line of history by undoing the
replacement scheduled while the tree conflict was raised.
(configure_option_incoming_added_file_text_merge): Configure new option.
* subversion/tests/libsvn_client/conflicts-test.c
(test_update_incoming_added_file_text_merge): Update test expectations.
In particular, stop expecting the replace_and_merge option for updates
since such an option makes no sense as there is only one line of history
to choose from. A valid choice would be replace the file entirely to create
a new line of history, but that should be handled by another option.
(test_funcs): Mark test_update_incoming_added_file_text_merge as PASS.
Modified:
subversion/trunk/subversion/libsvn_client/conflicts.c
subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c
Modified: subversion/trunk/subversion/libsvn_client/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/conflicts.c?rev=1770496&r1=1770495&r2=1770496&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/conflicts.c (original)
+++ subversion/trunk/subversion/libsvn_client/conflicts.c Sat Nov 19 14:14:21 2016
@@ -5235,6 +5235,144 @@ unlock_wc:
return SVN_NO_ERROR;
}
+
+/* Implements conflict_option_resolve_func_t. */
+static svn_error_t *
+resolve_merge_incoming_added_file_text_update(
+ svn_client_conflict_option_t *option,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ const char *wc_tmpdir;
+ const char *local_abspath;
+ const char *lock_abspath;
+ svn_wc_merge_outcome_t merge_content_outcome;
+ svn_wc_notify_state_t merge_props_outcome;
+ const char *empty_file_abspath;
+ const char *working_file_tmp_abspath;
+ svn_stream_t *working_file_stream;
+ svn_stream_t *working_file_tmp_stream;
+ apr_hash_t *working_props;
+ apr_hash_index_t *hi;
+ apr_array_header_t *propdiffs;
+ svn_error_t *err;
+
+ local_abspath = svn_client_conflict_get_local_abspath(conflict);
+
+ /* Set up tempory storage for the working version of file. */
+ SVN_ERR(svn_wc__get_tmpdir(&wc_tmpdir, ctx->wc_ctx, local_abspath,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_stream_open_unique(&working_file_tmp_stream,
+ &working_file_tmp_abspath, wc_tmpdir,
+ /* Don't delete automatically! */
+ svn_io_file_del_none,
+ scratch_pool, scratch_pool));
+
+ /* Copy the working file to temporary storage. */
+ SVN_ERR(svn_stream_open_readonly(&working_file_stream, local_abspath,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_stream_copy3(working_file_stream, working_file_tmp_stream,
+ ctx->cancel_func, ctx->cancel_baton,
+ scratch_pool));
+
+ /* Get a copy of the working file's properties. */
+ SVN_ERR(svn_wc_prop_list2(&working_props, ctx->wc_ctx, local_abspath,
+ scratch_pool, scratch_pool));
+
+ /* Delete entry and wc props from the returned set of properties.. */
+ for (hi = apr_hash_first(scratch_pool, working_props);
+ hi != NULL;
+ hi = apr_hash_next(hi))
+ {
+ const char *propname = apr_hash_this_key(hi);
+
+ if (!svn_wc_is_normal_prop(propname))
+ svn_hash_sets(working_props, propname, NULL);
+ }
+
+ /* Create an empty file as fake "merge-base" for the two added files.
+ * The files are not ancestrally related so this is the best we can do. */
+ SVN_ERR(svn_io_open_unique_file3(NULL, &empty_file_abspath, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ scratch_pool, scratch_pool));
+
+ /* Create a property diff which shows all props as added. */
+ SVN_ERR(svn_prop_diffs(&propdiffs, working_props,
+ apr_hash_make(scratch_pool), scratch_pool));
+
+ /* ### The following WC modifications should be atomic. */
+ SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
+ local_abspath,
+ scratch_pool, scratch_pool));
+
+ /* Revert the path in order to restore the repository's line of
+ * history, which is part of the BASE tree. This revert operation
+ * is why are being careful about not losing the temporary copy. */
+ err = svn_wc_revert5(ctx->wc_ctx, local_abspath, svn_depth_empty,
+ FALSE, NULL, TRUE, FALSE,
+ NULL, NULL, /* no cancellation */
+ ctx->notify_func2, ctx->notify_baton2,
+ scratch_pool);
+ if (err)
+ goto unlock_wc;
+
+ /* Perform the file merge. ### Merge into tempfile and then rename on top? */
+ err = svn_wc_merge5(&merge_content_outcome, &merge_props_outcome,
+ ctx->wc_ctx, empty_file_abspath,
+ working_file_tmp_abspath, local_abspath,
+ NULL, NULL, NULL, /* labels */
+ NULL, NULL, /* conflict versions */
+ FALSE, /* dry run */
+ NULL, NULL, /* diff3_cmd, merge_options */
+ NULL, propdiffs,
+ NULL, NULL, /* conflict func/baton */
+ NULL, NULL, /* don't allow user to cancel here */
+ scratch_pool);
+
+unlock_wc:
+ if (err)
+ err = svn_error_quick_wrapf(
+ err, _("If needed, a backup copy of '%s' can be found at '%s'"),
+ svn_dirent_local_style(local_abspath, scratch_pool),
+ svn_dirent_local_style(working_file_tmp_abspath, scratch_pool));
+ err = svn_error_compose_create(err,
+ svn_wc__release_write_lock(ctx->wc_ctx,
+ lock_abspath,
+ scratch_pool));
+ svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
+ SVN_ERR(err);
+
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+
+ /* Tell the world about the file merge that just happened. */
+ notify = svn_wc_create_notify(local_abspath,
+ svn_wc_notify_update_update,
+ scratch_pool);
+ if (merge_content_outcome == svn_wc_merge_conflict)
+ notify->content_state = svn_wc_notify_state_conflicted;
+ else
+ notify->content_state = svn_wc_notify_state_merged;
+ notify->prop_state = merge_props_outcome;
+ notify->kind = svn_node_file;
+ ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+
+ /* And also about the successfully resolved tree conflict. */
+ notify = svn_wc_create_notify(local_abspath, svn_wc_notify_resolved_tree,
+ scratch_pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+ }
+
+ conflict->resolution_tree = svn_client_conflict_option_get_id(option);
+
+ /* All is good -- remove temporary copy of the working file. */
+ SVN_ERR(svn_io_remove_file2(working_file_tmp_abspath, TRUE, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
/* Implements conflict_option_resolve_func_t. */
static svn_error_t *
resolve_merge_incoming_added_file_text_merge(
@@ -7773,11 +7911,11 @@ configure_option_incoming_added_file_tex
&incoming_new_kind, conflict, scratch_pool,
scratch_pool));
- if (operation == svn_wc_operation_merge &&
- victim_node_kind == svn_node_file &&
+ if (victim_node_kind == svn_node_file &&
incoming_new_kind == svn_node_file &&
incoming_change == svn_wc_conflict_action_add &&
- local_change == svn_wc_conflict_reason_obstructed)
+ (local_change == svn_wc_conflict_reason_obstructed ||
+ local_change == svn_wc_conflict_reason_added))
{
const char *description;
const char *wcroot_abspath;
@@ -7785,19 +7923,31 @@ configure_option_incoming_added_file_tex
SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx,
conflict->local_abspath, scratch_pool,
scratch_pool));
- description =
- apr_psprintf(scratch_pool, _("merge '^/%s@%ld' into '%s'"),
- incoming_new_repos_relpath, incoming_new_pegrev,
- svn_dirent_local_style(
- svn_dirent_skip_ancestor(wcroot_abspath,
- conflict->local_abspath),
- scratch_pool));
+
+ if (operation == svn_wc_operation_merge)
+ description =
+ apr_psprintf(scratch_pool, _("merge '^/%s@%ld' into '%s'"),
+ incoming_new_repos_relpath, incoming_new_pegrev,
+ svn_dirent_local_style(
+ svn_dirent_skip_ancestor(wcroot_abspath,
+ conflict->local_abspath),
+ scratch_pool));
+ else
+ description =
+ apr_psprintf(scratch_pool, _("merge local '%s' and '^/%s@%ld'"),
+ svn_dirent_local_style(
+ svn_dirent_skip_ancestor(wcroot_abspath,
+ conflict->local_abspath),
+ scratch_pool),
+ incoming_new_repos_relpath, incoming_new_pegrev);
add_resolution_option(
options, conflict,
svn_client_conflict_option_incoming_added_file_text_merge,
_("Merge the files"), description,
- resolve_merge_incoming_added_file_text_merge);
+ operation == svn_wc_operation_merge
+ ? resolve_merge_incoming_added_file_text_merge
+ : resolve_merge_incoming_added_file_text_update);
}
return SVN_NO_ERROR;
Modified: subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c?rev=1770496&r1=1770495&r2=1770496&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_client/conflicts-test.c Sat Nov 19 14:14:21 2016
@@ -3394,6 +3394,7 @@ test_update_incoming_added_file_text_mer
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));
@@ -3401,14 +3402,13 @@ test_update_incoming_added_file_text_mer
SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, pool));
- /* Check available tree conflict resolution options. */
- /* XFAIL: The list of options remains unchanged after get_details(). */
+ /* 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,
- svn_client_conflict_option_incoming_added_file_replace_and_merge,
-1 /* end of list */
};
SVN_ERR(assert_tree_conflict_options(conflict, ctx, expected_opts, pool));
@@ -3430,7 +3430,7 @@ test_update_incoming_added_file_text_mer
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->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);
@@ -3439,6 +3439,7 @@ test_update_incoming_added_file_text_mer
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));
@@ -3761,7 +3762,7 @@ static struct svn_test_descriptor_t test
"merge incoming file move with new line of history"),
SVN_TEST_OPTS_XFAIL(test_update_incoming_dir_move_with_nested_file_move,
"update incoming dir move with nested file move"),
- SVN_TEST_OPTS_XFAIL(test_update_incoming_added_file_text_merge,
+ SVN_TEST_OPTS_PASS(test_update_incoming_added_file_text_merge,
"update incoming add file text merge"),
SVN_TEST_OPTS_XFAIL(test_merge_incoming_move_file_prop_merge_conflict,
"merge incoming move file merge with prop conflict"),