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/06/15 10:59:56 UTC
svn commit: r1748549 - in /subversion/trunk/subversion:
include/private/svn_wc_private.h include/svn_client.h
libsvn_client/conflicts.c libsvn_wc/conflicts.c
Author: stsp
Date: Wed Jun 15 10:59:56 2016
New Revision: 1748549
URL: http://svn.apache.org/viewvc?rev=1748549&view=rev
Log:
Add APIs to the conflict resolver which allow clients to resolve ambiguous
move target paths.
* subversion/include/private/svn_wc_private.h
(svn_wc__guess_incoming_move_target_node): Rename to ...
(svn_wc__guess_incoming_move_target_nodes): ... this, and return the full
list of canidates, instead of just returning the "best" candidate.
* subversion/include/svn_client.h
(svn_client_conflict_option_get_moved_to_abspath_candidates,
svn_client_conflict_option_set_moved_to_abspath): Declare.
* subversion/libsvn_client/conflicts.c
(svn_client_conflict_option_t): Add type-specific data for tree conflicts
involving incoming moves.
(conflict_tree_incoming_delete_details): Remove moved_to_abspath. It has
been superseded by type-specific option data.
(resolve_incoming_move_file_text_merge): Tweak sanity check for availability
of conflict details. Get the move target path from option data instead of
details data.
(configure_option_incoming_move_file_merge): Store move target canidates
returned by libsvn_wc as type-specific option data.
(svn_client_conflict_option_get_moved_to_abspath_candidates): New. Return
a copy of the list of move target candidates stored in option data.
(svn_client_conflict_option_set_moved_to_abspath): New. Allows the client
to pick a move target from the list of candidates.
* subversion/libsvn_wc/conflicts.c
(svn_wc__guess_incoming_move_target_node): Renamed to ...
(svn_wc__guess_incoming_move_target_nodes): .. this. Return an array of
candidates. Ensure that our preferred candidate appears first in the list.
Modified:
subversion/trunk/subversion/include/private/svn_wc_private.h
subversion/trunk/subversion/include/svn_client.h
subversion/trunk/subversion/libsvn_client/conflicts.c
subversion/trunk/subversion/libsvn_wc/conflicts.c
Modified: subversion/trunk/subversion/include/private/svn_wc_private.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_wc_private.h?rev=1748549&r1=1748548&r2=1748549&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_wc_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_wc_private.h Wed Jun 15 10:59:56 2016
@@ -1890,31 +1890,31 @@ svn_wc__conflict_tree_update_moved_away_
void *notify_baton,
apr_pool_t *scratch_pool);
-/* Find a node in the working copy which corresponds to the new location
+/* Find nodes in the working copy which corresponds to the new location
* MOVED_TO_REPOS_RELPATH@REV of the tree conflict victim at VICTIM_ABSPATH.
- * The node must be of the same node kind as VICTIM_NODE_KIND.
- * If no such node can be found, set *MOVED_TO_ABSPATH to NULL.
+ * The nodes must be of the same node kind as VICTIM_NODE_KIND.
+ * If no such node can be found, set *POSSIBLE_TARGETS to an empty array.
*
- * The node should be useful for conflict resolution, e.g. it should be
- * possible to merge changes into this node to resolve an incoming-move
- * tree conflict. But the exact criteria for selecting the node are left
+ * The nodes should be useful for conflict resolution, e.g. it should be
+ * possible to merge changes into these nodes to resolve an incoming-move
+ * tree conflict. But the exact criteria for selecting a node are left
* to the implementation of this function.
* Note that this function may not necessarily return a node which was
* actually moved. The only hard guarantee is that the node corresponds to
* the repository node MOVED_TO_REPOS_RELPATH@REV specified by the caller.
* In many cases, this will be a moved node if the caller's parameters are
- * correct. But users should be able to override the selection made by
- * this function.
+ * correct. Users should be able to perform a sanity check on the results
+ * returned from this function.
*/
svn_error_t *
-svn_wc__guess_incoming_move_target_node(const char **moved_to_abspath,
- svn_wc_context_t *wc_ctx,
- const char *victim_abspath,
- svn_node_kind_t victim_node_kind,
- const char *moved_to_repos_relpath,
- svn_revnum_t rev,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool);
+svn_wc__guess_incoming_move_target_nodes(apr_array_header_t **possible_targets,
+ svn_wc_context_t *wc_ctx,
+ const char *victim_abspath,
+ svn_node_kind_t victim_node_kind,
+ const char *moved_to_repos_relpath,
+ svn_revnum_t rev,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
/**
* Move @a src_abspath to @a dst_abspath, by scheduling @a dst_abspath
Modified: subversion/trunk/subversion/include/svn_client.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1748549&r1=1748548&r2=1748549&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Wed Jun 15 10:59:56 2016
@@ -4446,6 +4446,53 @@ svn_client_conflict_option_set_merged_pr
const svn_string_t *merged_propval);
/**
+ * Get a list of possible moved-to abspaths in the working copy which can be
+ * applied to the svn_client_conflict_option_incoming_move_file_text_merge
+ * resolution @a option. (If a different option is passed in, this function
+ * will raise an assertion failure.)
+ *
+ * In some situations, there can be multiple possible destinations for an
+ * incoming move. One such situation is where a file was copied and moved
+ * in the same revision: svn cp a b; svn mv a c; svn commit
+ * When this move is merged elsewhere, both b and c will appear as valid move
+ * destinations to the conflict resolver. To resolve such ambiguity, the client
+ * may call this function to obtain a list of possible destinations the user
+ * may choose from.
+ *
+ * The array is allocated in @a result_pool and will have "const char *"
+ * elements pointing to strings also allocated in @a result_pool.
+ *
+ * If no possible moved-to paths can be found, return an empty array.
+ * This doesn't mean that no move happened in the repository. It is possible
+ * that the move destination is outside the scope of the current working copy,
+ * for example, in which case the conflict must be resolved in some other way.
+ *
+ * @see svn_client_conflict_option_set_moved_to_abspath()
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_option_get_moved_to_abspath_candidates(
+ apr_array_header_t **possible_moved_to_abspaths,
+ svn_client_conflict_option_t *option,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/**
+ * Set the preferred moved target abspath for the
+ * svn_client_conflict_option_incoming_move_file_text_merge resolution option.
+ *
+ * @a preferred_move_target_idx must be a valid index into the list
+ * returned by svn_client_conflict_option_get_moved_to_abspath_candidates().
+ *
+ * @since New in 1.10.
+ */
+svn_error_t *
+svn_client_conflict_option_set_moved_to_abspath(
+ svn_client_conflict_option_t *option,
+ int preferred_move_target_idx,
+ apr_pool_t *scratch_pool);
+
+/**
* Given an @a option_id, try to find the corresponding option in @a options,
* which is an array of svn_client_conflict_option_t * elements.
*
Modified: subversion/trunk/subversion/libsvn_client/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/conflicts.c?rev=1748549&r1=1748548&r2=1748549&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/conflicts.c (original)
+++ subversion/trunk/subversion/libsvn_client/conflicts.c Wed Jun 15 10:59:56 2016
@@ -132,6 +132,13 @@ struct svn_client_conflict_option_t
/* A merged property value, if supplied by the API user, else NULL. */
const svn_string_t *merged_propval;
} prop;
+
+ struct {
+ /* A list of possible working copy nodes for an incoming move target. */
+ apr_array_header_t *possible_moved_to_abspaths;
+ /* The current preferred move target (can be overridden by the user). */
+ int preferred_move_target_idx;
+ } tree;
} type_data;
};
@@ -1814,10 +1821,6 @@ struct conflict_tree_incoming_delete_det
* or in ADDED_REV (in which case moves should be interpreted in reverse).
* Follow MOVE->NEXT for subsequent moves in later revisions. */
struct repos_move_info *move;
-
- /* The path where we believe the moved-here node corresponding to the
- * deleted node exists in the working copy. */
- const char *moved_to_abspath;
};
static const char *
@@ -5818,20 +5821,23 @@ resolve_incoming_move_file_text_merge(sv
svn_wc_notify_state_t merge_props_outcome;
apr_array_header_t *propdiffs;
struct conflict_tree_incoming_delete_details *details;
+ const char *moved_to_abspath;
local_abspath = svn_client_conflict_get_local_abspath(conflict);
operation = svn_client_conflict_get_operation(conflict);
details = conflict->tree_conflict_incoming_details;
- if (details == NULL)
+ if (details == NULL || details->move == NULL)
return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
- _("Conflict resolution option '%d' requires "
- "details for tree conflict at '%s' to be "
- "fetched from the repository."),
- option->id,
+ _("The specified conflict resolution option "
+ "requires details for tree conflict at '%s' "
+ "to be fetched from the repository first."),
svn_dirent_local_style(local_abspath,
scratch_pool));
option_id = svn_client_conflict_option_get_id(option);
+ SVN_ERR_ASSERT(option_id ==
+ svn_client_conflict_option_incoming_move_file_text_merge);
+
SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, &repos_uuid,
conflict, scratch_pool,
scratch_pool));
@@ -5869,11 +5875,16 @@ resolve_incoming_move_file_text_merge(sv
SVN_ERR(svn_stream_close(ancestor_stream));
SVN_ERR(svn_io_file_flush(ancestor_file, scratch_pool));
+ moved_to_abspath = APR_ARRAY_IDX(
+ option->type_data.tree.possible_moved_to_abspaths,
+ option->type_data.tree.preferred_move_target_idx,
+ const char *);
+
/* ### The following WC modifications should be atomic. */
SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
&lock_abspath, ctx->wc_ctx,
svn_dirent_get_longest_ancestor(local_abspath,
- details->moved_to_abspath,
+ moved_to_abspath,
scratch_pool),
scratch_pool, scratch_pool));
@@ -5889,7 +5900,7 @@ resolve_incoming_move_file_text_merge(sv
/* Get a copy of the move target's properties. */
err = svn_wc_prop_list2(&move_target_props, ctx->wc_ctx,
- details->moved_to_abspath,
+ moved_to_abspath,
scratch_pool, scratch_pool);
if (err)
goto unlock_wc;
@@ -5903,7 +5914,7 @@ resolve_incoming_move_file_text_merge(sv
/* Perform the file merge. */
err = svn_wc_merge5(&merge_content_outcome, &merge_props_outcome,
ctx->wc_ctx, ancestor_abspath,
- local_abspath, details->moved_to_abspath,
+ local_abspath, moved_to_abspath,
NULL, NULL, NULL, /* labels */
NULL, NULL, /* conflict versions */
FALSE, /* dry run */
@@ -5912,7 +5923,7 @@ resolve_incoming_move_file_text_merge(sv
NULL, NULL, /* conflict func/baton */
NULL, NULL, /* don't allow user to cancel here */
scratch_pool);
- svn_io_sleep_for_timestamps(details->moved_to_abspath, scratch_pool);
+ svn_io_sleep_for_timestamps(moved_to_abspath, scratch_pool);
if (err)
goto unlock_wc;
@@ -5921,7 +5932,7 @@ resolve_incoming_move_file_text_merge(sv
svn_wc_notify_t *notify;
/* Tell the world about the file merge that just happened. */
- notify = svn_wc_create_notify(details->moved_to_abspath,
+ notify = svn_wc_create_notify(moved_to_abspath,
svn_wc_notify_update_update,
scratch_pool);
if (merge_content_outcome == svn_wc_merge_conflict)
@@ -5950,7 +5961,7 @@ resolve_incoming_move_file_text_merge(sv
{
/* The move operation is not part of natural history. We must replicate
* this move in our history. Record a move in the working copy. */
- err = svn_wc__move2(ctx->wc_ctx, local_abspath, details->moved_to_abspath,
+ err = svn_wc__move2(ctx->wc_ctx, local_abspath, moved_to_abspath,
TRUE, /* this is a meta-data only move */
FALSE, /* mixed-revisions don't apply to files */
NULL, NULL, /* don't allow user to cancel here */
@@ -6848,6 +6859,7 @@ configure_option_incoming_move_file_merg
const char *victim_abspath;
struct repos_move_info *move;
const char *moved_to_abspath;
+ apr_array_header_t *possible_moved_to_abspaths;
option = apr_pcalloc(options->pool, sizeof(*option));
option->id = svn_client_conflict_option_incoming_move_file_text_merge;
@@ -6863,16 +6875,26 @@ configure_option_incoming_move_file_merg
while (move->next)
move = move->next;
- SVN_ERR(svn_wc__guess_incoming_move_target_node(
- &moved_to_abspath, conflict->ctx->wc_ctx,
- victim_abspath, victim_node_kind,
+ SVN_ERR(svn_wc__guess_incoming_move_target_nodes(
+ &possible_moved_to_abspaths,
+ conflict->ctx->wc_ctx, victim_abspath, victim_node_kind,
move->moved_to_repos_relpath, incoming_new_pegrev,
- scratch_pool, scratch_pool));
- if (moved_to_abspath == NULL)
+ conflict->pool, scratch_pool));
+
+ /* Save the move target list for later use. */
+ option->type_data.tree.possible_moved_to_abspaths =
+ possible_moved_to_abspaths;
+ option->type_data.tree.preferred_move_target_idx = 0;
+
+ if (possible_moved_to_abspaths->nelts == 0)
return SVN_NO_ERROR;
- details->moved_to_abspath = apr_pstrdup(conflict->pool,
- moved_to_abspath);
+ /* Pick the first possible move target from the list.
+ * In most cases there will only be one candidate anyway. */
+ moved_to_abspath = APR_ARRAY_IDX(
+ possible_moved_to_abspaths,
+ option->type_data.tree.preferred_move_target_idx,
+ const char *);
option->description =
apr_psprintf(
options->pool,
@@ -6891,6 +6913,109 @@ configure_option_incoming_move_file_merg
return SVN_NO_ERROR;
}
+svn_error_t *
+svn_client_conflict_option_get_moved_to_abspath_candidates(
+ apr_array_header_t **possible_moved_to_abspaths,
+ svn_client_conflict_option_t *option,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_conflict_t *conflict = option->conflict;
+ struct conflict_tree_incoming_delete_details *details;
+ const char *victim_abspath;
+ apr_array_header_t *option_move_target_list;
+ int i;
+
+ SVN_ERR_ASSERT(svn_client_conflict_option_get_id(option) ==
+ svn_client_conflict_option_incoming_move_file_text_merge);
+
+ victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+ details = conflict->tree_conflict_incoming_details;
+ if (details == NULL || details->move == NULL)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Getting a list of possible moved targets "
+ "requires details for tree conflict at '%s' "
+ "to be fetched from the repository first"),
+ svn_dirent_local_style(victim_abspath,
+ scratch_pool));
+
+ /* Return a copy of the option's move target candidate list. */
+ option_move_target_list = option->type_data.tree.possible_moved_to_abspaths;
+ *possible_moved_to_abspaths = apr_array_make(result_pool,
+ option_move_target_list->nelts,
+ sizeof (const char *));
+ for (i = 0; i < option_move_target_list->nelts; i++)
+ {
+ const char *moved_to_abspath;
+
+ moved_to_abspath = APR_ARRAY_IDX(option_move_target_list, i,
+ const char *);
+ APR_ARRAY_PUSH(*possible_moved_to_abspaths, const char *) =
+ apr_pstrdup(result_pool, moved_to_abspath);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_conflict_option_set_moved_to_abspath(
+ svn_client_conflict_option_t *option,
+ int preferred_move_target_idx,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_conflict_t *conflict = option->conflict;
+ struct conflict_tree_incoming_delete_details *details;
+ const char *victim_abspath;
+ apr_array_header_t *possible_moved_to_abspaths;
+ const char *moved_to_abspath;
+ const char *wcroot_abspath;
+
+ SVN_ERR_ASSERT(svn_client_conflict_option_get_id(option) ==
+ svn_client_conflict_option_incoming_move_file_text_merge);
+
+ victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+ details = conflict->tree_conflict_incoming_details;
+ if (details == NULL || details->move == NULL)
+ return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Setting a possible moved target requires "
+ "details for tree conflict at '%s' "
+ "to be fetched from the repository first"),
+ svn_dirent_local_style(victim_abspath,
+ scratch_pool));
+
+ possible_moved_to_abspaths =
+ option->type_data.tree.possible_moved_to_abspaths;
+ if (preferred_move_target_idx < 0 ||
+ preferred_move_target_idx > possible_moved_to_abspaths->nelts)
+ return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+ _("Index '%d' is out of bounds of the possible "
+ "move target list for '%s'"),
+ preferred_move_target_idx,
+ svn_dirent_local_style(victim_abspath,
+ scratch_pool));
+
+ /* Record the user's preference and update the option description. */
+ option->type_data.tree.preferred_move_target_idx = preferred_move_target_idx;
+
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, conflict->ctx->wc_ctx,
+ victim_abspath, scratch_pool,
+ scratch_pool));
+ moved_to_abspath = APR_ARRAY_IDX(possible_moved_to_abspaths,
+ preferred_move_target_idx,
+ const char *);
+ option->description =
+ apr_psprintf(
+ conflict->pool, /* ### need options->pool here! */
+ _("follow move-away of '%s' and merge with '%s'"),
+ svn_dirent_local_style(svn_dirent_skip_ancestor(wcroot_abspath,
+ victim_abspath),
+ scratch_pool),
+ svn_dirent_local_style(svn_dirent_skip_ancestor(wcroot_abspath,
+ moved_to_abspath),
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_client_conflict_tree_get_resolution_options(apr_array_header_t **options,
svn_client_conflict_t *conflict,
Modified: subversion/trunk/subversion/libsvn_wc/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/conflicts.c?rev=1748549&r1=1748548&r2=1748549&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/conflicts.c (original)
+++ subversion/trunk/subversion/libsvn_wc/conflicts.c Wed Jun 15 10:59:56 2016
@@ -50,6 +50,7 @@
#include "private/svn_wc_private.h"
#include "private/svn_skel.h"
+#include "private/svn_sorts_private.h"
#include "private/svn_string_private.h"
#include "svn_private_config.h"
@@ -3695,21 +3696,21 @@ svn_wc__conflict_tree_update_moved_away_
}
svn_error_t *
-svn_wc__guess_incoming_move_target_node(const char **moved_to_abspath,
- svn_wc_context_t *wc_ctx,
- const char *victim_abspath,
- svn_node_kind_t victim_node_kind,
- const char *moved_to_repos_relpath,
- svn_revnum_t rev,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+svn_wc__guess_incoming_move_target_nodes(apr_array_header_t **possible_targets,
+ svn_wc_context_t *wc_ctx,
+ const char *victim_abspath,
+ svn_node_kind_t victim_node_kind,
+ const char *moved_to_repos_relpath,
+ svn_revnum_t rev,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
apr_array_header_t *candidates;
apr_pool_t *iterpool;
int i;
apr_size_t longest_ancestor_len = 0;
- *moved_to_abspath = NULL;
+ *possible_targets = apr_array_make(result_pool, 1, sizeof(const char *));
SVN_ERR(svn_wc__find_repos_node_in_wc(&candidates, wc_ctx->db, victim_abspath,
moved_to_repos_relpath, rev,
scratch_pool, scratch_pool));
@@ -3736,6 +3737,8 @@ svn_wc__guess_incoming_move_target_node(
svn_boolean_t is_wcroot;
svn_boolean_t is_switched;
svn_node_kind_t node_kind;
+ const char *moved_to_abspath;
+ int insert_index;
svn_pool_clear(iterpool);
@@ -3766,6 +3769,12 @@ svn_wc__guess_incoming_move_target_node(
if (is_wcroot || is_switched)
continue;
+ /* This might be a move target. Fingers crossed ;-) */
+ moved_to_abspath = apr_pstrdup(result_pool, local_abspath);
+
+ /* Insert the move target into the list. Targets which are closer
+ * (path-wise) to the conflict victim are more likely to be a good
+ * match, so put them at the front of the list. */
ancestor_abspath = svn_dirent_get_longest_ancestor(local_abspath,
victim_abspath,
iterpool);
@@ -3773,10 +3782,14 @@ svn_wc__guess_incoming_move_target_node(
if (ancestor_len >= longest_ancestor_len)
{
longest_ancestor_len = ancestor_len;
-
- /* This might be a move target. Fingers crossed ;-) */
- *moved_to_abspath = apr_pstrdup(result_pool, local_abspath);
+ insert_index = 0; /* prepend */
+ }
+ else
+ {
+ insert_index = (*possible_targets)->nelts; /* append */
}
+ svn_sort__array_insert(*possible_targets, &moved_to_abspath,
+ insert_index);
}
svn_pool_destroy(iterpool);