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);