subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From s...@apache.org
Subject svn commit: r1841732 - in /subversion/branches/1.11.x: ./ STATUS subversion/include/svn_client.h subversion/libsvn_client/conflicts.c subversion/svn/conflict-callbacks.c subversion/tests/libsvn_client/conflicts-test.c
Date Sun, 23 Sep 2018 09:43:41 GMT
Author: stsp
Date: Sun Sep 23 09:43:41 2018
New Revision: 1841732

URL: http://svn.apache.org/viewvc?rev=1841732&view=rev
Log:
Merge r1840990, r1840991, r1840995, and r1840997 from trunk.

 * r1840990, r1840991, r1840995, r1840997
   Various conflict resolver improvements.
   Notes:
     I am proposing these in a batch because merging them out of order would
     result in conflicts, and because I was planning to make these changes
     before 1.11 was branched in the first place but didn't get around to it.
     - r1840990 is a cosmetic output change
     - r1840991 prevents an out-of-bounds array access
     - r1840995 implements support for ambiguous moves in 'local missing' cases
     - r1840997 improves resolver API semantics
     Ideally, these should be merged before 1.11.0 is released.
   Votes:
     +0: julianfoad (only r1840990,r1840991)
     +1: stsp, brane, jcorvel

Modified:
    subversion/branches/1.11.x/   (props changed)
    subversion/branches/1.11.x/STATUS
    subversion/branches/1.11.x/subversion/include/svn_client.h
    subversion/branches/1.11.x/subversion/libsvn_client/conflicts.c
    subversion/branches/1.11.x/subversion/svn/conflict-callbacks.c
    subversion/branches/1.11.x/subversion/tests/libsvn_client/conflicts-test.c

Propchange: subversion/branches/1.11.x/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Sun Sep 23 09:43:41 2018
@@ -99,4 +99,4 @@
 /subversion/branches/verify-at-commit:1462039-1462408
 /subversion/branches/verify-keep-going:1439280-1546110
 /subversion/branches/wc-collate-path:1402685-1480384
-/subversion/trunk:1841091,1841098,1841136,1841272
+/subversion/trunk:1840990-1840991,1840995,1840997,1841091,1841098,1841136,1841272

Modified: subversion/branches/1.11.x/STATUS
URL: http://svn.apache.org/viewvc/subversion/branches/1.11.x/STATUS?rev=1841732&r1=1841731&r2=1841732&view=diff
==============================================================================
--- subversion/branches/1.11.x/STATUS (original)
+++ subversion/branches/1.11.x/STATUS Sun Sep 23 09:43:41 2018
@@ -61,19 +61,3 @@ Approved changes:
      +1: danielsh (confirmed the buildsystem parts won't affect anything other
                    than the java bindings; this counts as three +1s for the
                    build system changes)
-
- * r1840990, r1840991, r1840995, r1840997
-   Various conflict resolver improvements.
-   Notes:
-     I am proposing these in a batch because merging them out of order would
-     result in conflicts, and because I was planning to make these changes
-     before 1.11 was branched in the first place but didn't get around to it.
-     - r1840990 is a cosmetic output change
-     - r1840991 prevents an out-of-bounds array access
-     - r1840995 implements support for ambiguous moves in 'local missing' cases
-     - r1840997 improves resolver API semantics
-     Ideally, these should be merged before 1.11.0 is released.
-   Votes:
-     +0: julianfoad (only r1840990,r1840991)
-     +1: stsp, brane, jcorvel
-

Modified: subversion/branches/1.11.x/subversion/include/svn_client.h
URL: http://svn.apache.org/viewvc/subversion/branches/1.11.x/subversion/include/svn_client.h?rev=1841732&r1=1841731&r2=1841732&view=diff
==============================================================================
--- subversion/branches/1.11.x/subversion/include/svn_client.h (original)
+++ subversion/branches/1.11.x/subversion/include/svn_client.h Sun Sep 23 09:43:41 2018
@@ -4585,27 +4585,45 @@ svn_client_conflict_option_set_merged_pr
   const svn_string_t *merged_propval);
 
 /**
- * Get a list of possible repository paths which can be applied to the
- * svn_client_conflict_option_incoming_move_file_text_merge, or
- * svn_client_conflict_option_local_move_file_text_merge, or
- * svn_client_conflict_option_incoming_move_dir_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
+ * Get a list of possible repository paths which can be applied to @a option.
+ *
+ * In some situations, there can be multiple possible destinations for a 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.
  *
+ * @a *possible_moved_to_repos_relpaths is set to NULL if the @a option does
+ * not support multiple move targets. API users may assume that only one option
+ * among those which can be applied to a conflict supports move targets.
+ *
  * The array is allocated in @a result_pool and will have "const char *"
  * elements pointing to strings also allocated in @a result_pool.
  * All paths are relpaths, and relative to the repository root.
  *
- * @see svn_client_conflict_option_set_moved_to_repos_relpath()
+ * @see svn_client_conflict_option_set_moved_to_repos_relpath2()
+ * @since New in 1.11.
+ */
+svn_error_t *
+svn_client_conflict_option_get_moved_to_repos_relpath_candidates2(
+  apr_array_header_t **possible_moved_to_repos_relpaths,
+  svn_client_conflict_option_t *option,
+  apr_pool_t *result_pool,
+  apr_pool_t *scratch_pool);
+
+/**
+ * Get a list of possible repository paths which can be applied to the
+ * svn_client_conflict_option_incoming_move_file_text_merge, or the
+ * svn_client_conflict_option_incoming_move_dir_merge resolution @a option.
+ *
+ * In SVN 1.10, if a different option is passed in, this function will
+ * raise an assertion failure. Otherwise this function behaves just like
+ * svn_client_conflict_option_get_moved_to_repos_relpath_candidates2().
+ *
  * @since New in 1.10.
+ * @deprecated use svn_client_conflict_option_get_moved_to_repos_relpath_candidates2()
  */
 svn_error_t *
 svn_client_conflict_option_get_moved_to_repos_relpath_candidates(
@@ -4615,10 +4633,9 @@ svn_client_conflict_option_get_moved_to_
   apr_pool_t *scratch_pool);
 
 /**
- * Set the preferred moved target repository path for the
- * svn_client_conflict_option_incoming_move_file_text_merge or
- * svn_client_conflict_option_incoming_move_dir_merge resolution option.
- * 
+ * Set the preferred moved target repository path. If @a option is not
+ * applicable to a moved target repository path, do nothing.
+ *
  * @a preferred_move_target_idx must be a valid index into the list returned
  * by svn_client_conflict_option_get_moved_to_repos_relpath_candidates().
  * 
@@ -4627,7 +4644,23 @@ svn_client_conflict_option_get_moved_to_
  * svn_client_conflict_option_get_description(). Call these functions again
  * to get updated descriptions containing the newly selected move target.
  *
+ * @since New in 1.11.
+ */
+svn_error_t *
+svn_client_conflict_option_set_moved_to_repos_relpath2(
+  svn_client_conflict_option_t *option,
+  int preferred_move_target_idx,
+  svn_client_ctx_t *ctx,
+  apr_pool_t *scratch_pool);
+
+/**
+ * Like svn_client_conflict_option_set_moved_to_repos_relpath2(), except
+ * that in SVN 1.10 it raises an assertion failure if an option other
+ * than svn_client_conflict_option_incoming_move_file_text_merge or
+ * svn_client_conflict_option_incoming_move_dir_merge is passed.
+ *
  * @since New in 1.10.
+ * @deprecated use svn_client_conflict_option_set_moved_to_repos_relpath2()
  */
 svn_error_t *
 svn_client_conflict_option_set_moved_to_repos_relpath(
@@ -4638,25 +4671,45 @@ svn_client_conflict_option_set_moved_to_
 
 /**
  * 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,
- * svn_client_conflict_option_local_move_file_text_merge,
- * or svn_client_conflict_option_incoming_move_dir_merge resolution @a option.
- * (If a different option is passed in, this function will raise an assertion
- * failure.)
- *
- * All paths in the returned list correspond to the repository path which
- * is assumed to be the destination of the incoming move operation.
- * To support cases where this destination path is ambiguous, the client may
- * call svn_client_conflict_option_get_moved_to_repos_relpath_candidates()
- * before calling this function to let the user select a repository path first.
+ * applied to @a option.
+ *
+ * All working copy paths in the returned list correspond to one repository
+ * path which is be one of the possible destinations of a move operation.
+ * More than one repository-side move target candidate may exist; call
+ * svn_client_conflict_option_get_moved_to_repos_relpath_candidates() before
+ * calling this function to let the user select a repository path first.
+ * Otherwise, one of the repository-side paths will be selected internally.
  * 
+ * @a *possible_moved_to_abspaths is set to NULL if the @a option does not
+ * support multiple move targets. API users may assume that only one option
+ * among those which can be applied to a conflict supports move targets.
+ *
  * 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()
+ * @see svn_client_conflict_option_set_moved_to_abspath2()
+ * @since New in 1.11.
+ */
+svn_error_t *
+svn_client_conflict_option_get_moved_to_abspath_candidates2(
+  apr_array_header_t **possible_moved_to_abspaths,
+  svn_client_conflict_option_t *option,
+  apr_pool_t *result_pool,
+  apr_pool_t *scratch_pool);
+
+/**
+ * Get a list of possible moved-to abspaths in the working copy which can be
+ * svn_client_conflict_option_incoming_move_file_text_merge, or the
+ * svn_client_conflict_option_incoming_move_dir_merge resolution @a option.
+ *
+ * In SVN 1.10, if a different option is passed in, this function will
+ * raise an assertion failure. Otherwise this function behaves just like
+ * svn_client_conflict_option_get_moved_to_abspath_candidates2().
+ *
  * @since New in 1.10.
+ * @deprecated use svn_client_conflict_option_get_moved_to_abspath_candidates2()
  */
 svn_error_t *
 svn_client_conflict_option_get_moved_to_abspath_candidates(
@@ -4666,15 +4719,34 @@ svn_client_conflict_option_get_moved_to_
   apr_pool_t *scratch_pool);
 
 /**
- * Set the preferred moved target abspath for the
- * svn_client_conflict_option_incoming_move_file_text_merge or
- * svn_client_conflict_option_local_move_file_text_merge or
- * svn_client_conflict_option_incoming_move_dir_merge resolution option.
+ * Set the preferred moved target working copy path. If @a option is not
+ * applicable to a moved target working copy path, do nothing.
  * 
  * @a preferred_move_target_idx must be a valid index into the list
- * returned by svn_client_conflict_option_get_moved_to_abspath_candidates().
+ * returned by svn_client_conflict_option_get_moved_to_abspath_candidates2().
  * 
+ * This function can be called multiple times.
+ * It affects the output of svn_client_conflict_tree_get_description() and
+ * svn_client_conflict_option_get_description(). Call these functions again
+ * to get updated descriptions containing the newly selected move target.
+ *
+ * @since New in 1.11.
+ */
+svn_error_t *
+svn_client_conflict_option_set_moved_to_abspath2(
+  svn_client_conflict_option_t *option,
+  int preferred_move_target_idx,
+  svn_client_ctx_t *ctx,
+  apr_pool_t *scratch_pool);
+
+/**
+ * Like svn_client_conflict_option_set_moved_to_abspath2(), except that
+ * in SVN 1.10 this function raises an assertion failure if an option
+ * other than svn_client_conflict_option_incoming_move_file_text_merge or
+ * svn_client_conflict_option_incoming_move_dir_merge is passed.
+ *
  * @since New in 1.10.
+ * @deprecated use svn_client_conflict_option_set_moved_to_abspath2()
  */
 svn_error_t *
 svn_client_conflict_option_set_moved_to_abspath(

Modified: subversion/branches/1.11.x/subversion/libsvn_client/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.11.x/subversion/libsvn_client/conflicts.c?rev=1841732&r1=1841731&r2=1841732&view=diff
==============================================================================
--- subversion/branches/1.11.x/subversion/libsvn_client/conflicts.c (original)
+++ subversion/branches/1.11.x/subversion/libsvn_client/conflicts.c Sun Sep 23 09:43:41 2018
@@ -2350,6 +2350,8 @@ struct conflict_tree_local_missing_detai
   /* If not SVN_INVALID_REVNUM, the node was deleted in DELETED_REV. */
   svn_revnum_t deleted_rev;
 
+  /* ### Add 'added_rev', like in conflict_tree_incoming_delete_details? */
+
   /* Author who committed DELETED_REV. */
   const char *deleted_rev_author;
 
@@ -2357,17 +2359,38 @@ struct conflict_tree_local_missing_detai
   const char *deleted_repos_relpath;
 
   /* Move information about the conflict victim. If not NULL, this is an
-   * array of repos_move_info elements. Each element is the head of a
-   * move chain which starts in DELETED_REV. */
+   * array of 'struct repos_move_info *' elements. Each element is the
+   * head of a move chain which starts in DELETED_REV. */
   apr_array_header_t *moves;
 
-  /* If not NULL, this is the move target abspath. */
-  const char *moved_to_abspath;
+  /* If moves is not NULL, a map of repos_relpaths and working copy nodes.
+  *
+   * Each key is a "const char *" repository relpath corresponding to a
+   * possible repository-side move destination node in the revision which
+   * is the merge-right revision in case of a merge.
+   *
+   * Each value is an apr_array_header_t *.
+   * Each array consists of "const char *" absolute paths to working copy
+   * nodes which correspond to the repository node selected by the map key.
+   * Each such working copy node is a potential local move target which can
+   * be chosen to find a suitable merge target when resolving a tree conflict.
+   *
+   * This may be an empty hash map in case if there is no move target path
+   * in the working copy. */
+  apr_hash_t *wc_move_targets;
+
+  /* If not NULL, the preferred move target repository relpath. This is our key
+   * into the WC_MOVE_TARGETS map above (can be overridden by the user). */
+  const char *move_target_repos_relpath;
+
+  /* The current index into the list of working copy nodes corresponding to
+   * MOVE_TARGET_REPOS_REPLATH (can be overridden by the user). */
+  int wc_move_target_idx;
 
   /* Move information about siblings. Siblings are nodes which share
    * a youngest common ancestor with the conflict victim. E.g. in case
    * of a merge operation they are part of the merge source branch.
-   * If not NULL, this is an array of repos_move_info elements.
+   * If not NULL, this is an array of 'struct repos_move_info *' elements.
    * Each element is the head of a move chain, which starts at some
    * point in history after siblings and conflict victim forked off
    * their common ancestor. */
@@ -2635,6 +2658,133 @@ collect_sibling_move_candidates(apr_arra
   return SVN_NO_ERROR;
 }
 
+/* Follow each move chain starting a MOVE all the way to the end to find
+ * the possible working copy locations for VICTIM_ABSPATH which corresponds
+ * to VICTIM_REPOS_REPLATH@VICTIM_REVISION.
+ * Add each such location to the WC_MOVE_TARGETS hash table, keyed on the
+ * repos_relpath which is the corresponding move destination in the repository.
+ * This function is recursive. */
+static svn_error_t *
+follow_move_chains(apr_hash_t *wc_move_targets,
+                   struct repos_move_info *move,
+                   svn_client_ctx_t *ctx,
+                   const char *victim_abspath,
+                   svn_node_kind_t victim_node_kind,
+                   const char *victim_repos_relpath,
+                   svn_revnum_t victim_revision,
+                   apr_pool_t *result_pool,
+                   apr_pool_t *scratch_pool)
+{
+  /* If this is the end of a move chain, look for matching paths in
+   * the working copy and add them to our collection if found. */
+  if (move->next == NULL)
+    {
+      apr_array_header_t *candidate_abspaths;
+
+      /* Gather candidate nodes which represent this moved_to_repos_relpath. */
+      SVN_ERR(svn_wc__guess_incoming_move_target_nodes(
+                &candidate_abspaths, ctx->wc_ctx,
+                victim_abspath, victim_node_kind,
+                move->moved_to_repos_relpath,
+                scratch_pool, scratch_pool));
+      if (candidate_abspaths->nelts > 0)
+        {
+          apr_array_header_t *moved_to_abspaths;
+          int i;
+          apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+          moved_to_abspaths = apr_array_make(result_pool, 1,
+                                             sizeof (const char *));
+
+          for (i = 0; i < candidate_abspaths->nelts; i++)
+            {
+              const char *candidate_abspath;
+              const char *repos_root_url;
+              const char *repos_uuid;
+              const char *candidate_repos_relpath;
+              svn_revnum_t candidate_revision;
+
+              svn_pool_clear(iterpool);
+
+              candidate_abspath = APR_ARRAY_IDX(candidate_abspaths, i,
+                                                const char *);
+              SVN_ERR(svn_wc__node_get_origin(NULL, &candidate_revision,
+                                              &candidate_repos_relpath,
+                                              &repos_root_url,
+                                              &repos_uuid,
+                                              NULL, NULL,
+                                              ctx->wc_ctx,
+                                              candidate_abspath,
+                                              FALSE,
+                                              iterpool, iterpool));
+
+              if (candidate_revision == SVN_INVALID_REVNUM)
+                continue;
+
+              /* If the conflict victim and the move target candidate
+               * are not from the same revision we must ensure that
+               * they are related. */
+               if (candidate_revision != victim_revision)
+                {
+                  svn_client__pathrev_t *yca_loc;
+                  svn_error_t *err;
+
+                  err = find_yca(&yca_loc, victim_repos_relpath,
+                                 victim_revision,
+                                 candidate_repos_relpath,
+                                 candidate_revision,
+                                 repos_root_url, repos_uuid,
+                                 NULL, ctx, iterpool, iterpool);
+                  if (err)
+                    {
+                      if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
+                        {
+                          svn_error_clear(err);
+                          yca_loc = NULL;
+                        }
+                      else
+                        return svn_error_trace(err);
+                    }
+
+                  if (yca_loc == NULL)
+                    continue;
+                }
+
+              APR_ARRAY_PUSH(moved_to_abspaths, const char *) =
+                apr_pstrdup(result_pool, candidate_abspath);
+            }
+          svn_pool_destroy(iterpool);
+
+          svn_hash_sets(wc_move_targets, move->moved_to_repos_relpath,
+                        moved_to_abspaths);
+        }
+    }
+  else
+    {
+      int i;
+      apr_pool_t *iterpool;
+
+      /* Recurse into each of the possible move chains. */
+      iterpool = svn_pool_create(scratch_pool);
+      for (i = 0; i < move->next->nelts; i++)
+        {
+          struct repos_move_info *next_move;
+
+          svn_pool_clear(iterpool);
+
+          next_move = APR_ARRAY_IDX(move->next, i, struct repos_move_info *);
+          SVN_ERR(follow_move_chains(wc_move_targets, next_move,
+                                     ctx, victim_abspath, victim_node_kind,
+                                     victim_repos_relpath, victim_revision,
+                                     result_pool, iterpool));
+                                        
+        }
+      svn_pool_destroy(iterpool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Implements tree_conflict_get_details_func_t. */
 static svn_error_t *
 conflict_tree_get_details_local_missing(svn_client_conflict_t *conflict,
@@ -2862,6 +3012,51 @@ conflict_tree_get_details_local_missing(
                                                       deleted_basename,
                                                       conflict->pool); 
   details->moves = moves;
+  if (details->moves != NULL)
+    {
+      apr_pool_t *iterpool;
+      int i;
+
+      details->wc_move_targets = apr_hash_make(conflict->pool);
+      iterpool = svn_pool_create(scratch_pool);
+      for (i = 0; i < details->moves->nelts; i++)
+        {
+          struct repos_move_info *move;
+
+          svn_pool_clear(iterpool);
+          move = APR_ARRAY_IDX(details->moves, i, struct repos_move_info *);
+          SVN_ERR(follow_move_chains(details->wc_move_targets, move, ctx,
+                                     conflict->local_abspath,
+                                     svn_node_file,
+                                     new_repos_relpath,
+                                     new_rev,
+                                     scratch_pool, iterpool));
+        }
+      svn_pool_destroy(iterpool);
+
+      if (apr_hash_count(details->wc_move_targets) > 0)
+        {
+          apr_array_header_t *move_target_repos_relpaths;
+          const svn_sort__item_t *item;
+
+          /* Initialize to the first possible move target. Hopefully,
+           * in most cases there will only be one candidate anyway. */
+          move_target_repos_relpaths = svn_sort__hash(
+                                         details->wc_move_targets,
+                                         svn_sort_compare_items_as_paths,
+                                         scratch_pool);
+          item = &APR_ARRAY_IDX(move_target_repos_relpaths,
+                                0, svn_sort__item_t);
+          details->move_target_repos_relpath = item->key;
+          details->wc_move_target_idx = 0;
+        }
+      else
+        {
+          details->move_target_repos_relpath = NULL;
+          details->wc_move_target_idx = 0;
+        }
+    }
+                                         
   details->sibling_moves = sibling_moves;
   details->wc_siblings = wc_siblings;
   if (details->wc_siblings && details->wc_siblings->nelts == 1)
@@ -2875,7 +3070,7 @@ conflict_tree_get_details_local_missing(
           conflict->recommended_option_id =
             svn_client_conflict_option_sibling_move_dir_merge;
     }
-                                         
+
   conflict->tree_conflict_local_details = details;
 
   return SVN_NO_ERROR;
@@ -4784,176 +4979,49 @@ get_incoming_delete_details_for_reverse_
   return SVN_NO_ERROR;
 }
 
-/* Follow each move chain starting a MOVE all the way to the end to find
- * the possible working copy locations for VICTIM_ABSPATH which corresponds
- * to VICTIM_REPOS_REPLATH@VICTIM_REVISION.
- * Add each such location to the WC_MOVE_TARGETS hash table, keyed on the
- * repos_relpath which is the corresponding move destination in the repository.
- * This function is recursive. */
 static svn_error_t *
-follow_move_chains(apr_hash_t *wc_move_targets,
-                   struct repos_move_info *move,
-                   svn_client_ctx_t *ctx,
-                   const char *victim_abspath,
-                   svn_node_kind_t victim_node_kind,
-                   const char *victim_repos_relpath,
-                   svn_revnum_t victim_revision,
-                   apr_pool_t *result_pool,
-                   apr_pool_t *scratch_pool)
+init_wc_move_targets(struct conflict_tree_incoming_delete_details *details,
+                     svn_client_conflict_t *conflict,
+                     svn_client_ctx_t *ctx,
+                     apr_pool_t *scratch_pool)
 {
-  /* If this is the end of a move chain, look for matching paths in
-   * the working copy and add them to our collection if found. */
-  if (move->next == NULL)
-    {
-      apr_array_header_t *candidate_abspaths;
+  int i;
+  const char *victim_abspath;
+  svn_node_kind_t victim_node_kind;
+  const char *incoming_new_repos_relpath;
+  svn_revnum_t incoming_new_pegrev;
 
-      /* Gather candidate nodes which represent this moved_to_repos_relpath. */
-      SVN_ERR(svn_wc__guess_incoming_move_target_nodes(
-                &candidate_abspaths, ctx->wc_ctx,
-                victim_abspath, victim_node_kind,
-                move->moved_to_repos_relpath,
-                scratch_pool, scratch_pool));
-      if (candidate_abspaths->nelts > 0)
-        {
-          apr_array_header_t *moved_to_abspaths;
-          int i;
-          apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+  victim_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
+  /* ### Should we get the old location in case of reverse-merges? */
+  SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+            &incoming_new_repos_relpath, &incoming_new_pegrev,
+            NULL, conflict,
+            scratch_pool, scratch_pool));
+  details->wc_move_targets = apr_hash_make(conflict->pool);
+  for (i = 0; i < details->moves->nelts; i++)
+    {
+      struct repos_move_info *move;
 
-          moved_to_abspaths = apr_array_make(result_pool, 1,
-                                             sizeof (const char *));
+      move = APR_ARRAY_IDX(details->moves, i, struct repos_move_info *);
+      SVN_ERR(follow_move_chains(details->wc_move_targets, move,
+                                 ctx, victim_abspath,
+                                 victim_node_kind,
+                                 incoming_new_repos_relpath,
+                                 incoming_new_pegrev,
+                                 conflict->pool, scratch_pool));
+    }
 
-          for (i = 0; i < candidate_abspaths->nelts; i++)
-            {
-              const char *candidate_abspath;
-              const char *repos_root_url;
-              const char *repos_uuid;
-              const char *candidate_repos_relpath;
-              svn_revnum_t candidate_revision;
+  /* Initialize to the first possible move target. Hopefully,
+   * in most cases there will only be one candidate anyway. */
+  details->move_target_repos_relpath =
+    get_moved_to_repos_relpath(details, scratch_pool);
+  details->wc_move_target_idx = 0;
 
-              svn_pool_clear(iterpool);
-
-              candidate_abspath = APR_ARRAY_IDX(candidate_abspaths, i,
-                                                const char *);
-              SVN_ERR(svn_wc__node_get_origin(NULL, &candidate_revision,
-                                              &candidate_repos_relpath,
-                                              &repos_root_url,
-                                              &repos_uuid,
-                                              NULL, NULL,
-                                              ctx->wc_ctx,
-                                              candidate_abspath,
-                                              FALSE,
-                                              iterpool, iterpool));
-
-              if (candidate_revision == SVN_INVALID_REVNUM)
-                continue;
-
-              /* If the conflict victim and the move target candidate
-               * are not from the same revision we must ensure that
-               * they are related. */
-               if (candidate_revision != victim_revision)
-                {
-                  svn_client__pathrev_t *yca_loc;
-                  svn_error_t *err;
-
-                  err = find_yca(&yca_loc, victim_repos_relpath,
-                                 victim_revision,
-                                 candidate_repos_relpath,
-                                 candidate_revision,
-                                 repos_root_url, repos_uuid,
-                                 NULL, ctx, iterpool, iterpool);
-                  if (err)
-                    {
-                      if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
-                        {
-                          svn_error_clear(err);
-                          yca_loc = NULL;
-                        }
-                      else
-                        return svn_error_trace(err);
-                    }
-
-                  if (yca_loc == NULL)
-                    continue;
-                }
-
-              APR_ARRAY_PUSH(moved_to_abspaths, const char *) =
-                apr_pstrdup(result_pool, candidate_abspath);
-            }
-          svn_pool_destroy(iterpool);
-
-          svn_hash_sets(wc_move_targets, move->moved_to_repos_relpath,
-                        moved_to_abspaths);
-        }
-    }
-  else
-    {
-      int i;
-      apr_pool_t *iterpool;
-
-      /* Recurse into each of the possible move chains. */
-      iterpool = svn_pool_create(scratch_pool);
-      for (i = 0; i < move->next->nelts; i++)
-        {
-          struct repos_move_info *next_move;
-
-          svn_pool_clear(iterpool);
-
-          next_move = APR_ARRAY_IDX(move->next, i, struct repos_move_info *);
-          SVN_ERR(follow_move_chains(wc_move_targets, next_move,
-                                     ctx, victim_abspath, victim_node_kind,
-                                     victim_repos_relpath, victim_revision,
-                                     result_pool, iterpool));
-                                        
-        }
-      svn_pool_destroy(iterpool);
-    }
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-init_wc_move_targets(struct conflict_tree_incoming_delete_details *details,
-                     svn_client_conflict_t *conflict,
-                     svn_client_ctx_t *ctx,
-                     apr_pool_t *scratch_pool)
-{
-  int i;
-  const char *victim_abspath;
-  svn_node_kind_t victim_node_kind;
-  const char *incoming_new_repos_relpath;
-  svn_revnum_t incoming_new_pegrev;
-
-  victim_abspath = svn_client_conflict_get_local_abspath(conflict);
-  victim_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
-  /* ### Should we get the old location in case of reverse-merges? */
-  SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
-            &incoming_new_repos_relpath, &incoming_new_pegrev,
-            NULL, conflict,
-            scratch_pool, scratch_pool));
-  details->wc_move_targets = apr_hash_make(conflict->pool);
-  for (i = 0; i < details->moves->nelts; i++)
-    {
-      struct repos_move_info *move;
-
-      move = APR_ARRAY_IDX(details->moves, i, struct repos_move_info *);
-      SVN_ERR(follow_move_chains(details->wc_move_targets, move,
-                                 ctx, victim_abspath,
-                                 victim_node_kind,
-                                 incoming_new_repos_relpath,
-                                 incoming_new_pegrev,
-                                 conflict->pool, scratch_pool));
-    }
-
-  /* Initialize to the first possible move target. Hopefully,
-   * in most cases there will only be one candidate anyway. */
-  details->move_target_repos_relpath =
-    get_moved_to_repos_relpath(details, scratch_pool);
-  details->wc_move_target_idx = 0;
-
-  /* If only one move target exists recommend a resolution option. */
-  if (apr_hash_count(details->wc_move_targets) == 1)
-    {
-      apr_array_header_t *wc_abspaths;
+  /* If only one move target exists recommend a resolution option. */
+  if (apr_hash_count(details->wc_move_targets) == 1)
+    {
+      apr_array_header_t *wc_abspaths;
 
       wc_abspaths = svn_hash_gets(details->wc_move_targets,
                                   details->move_target_repos_relpath);
@@ -9004,6 +9072,11 @@ resolve_local_move_file_merge(svn_client
   apr_array_header_t *propdiffs;
   struct conflict_tree_local_missing_details *details;
   const char *merge_target_abspath;
+  const char *wcroot_abspath;
+
+  SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx,
+                             conflict->local_abspath, scratch_pool,
+                             scratch_pool));
 
   details = conflict->tree_conflict_local_details;
 
@@ -9019,12 +9092,28 @@ resolve_local_move_file_merge(svn_client
             NULL, conflict, scratch_pool,
             scratch_pool));
 
-  if (details->wc_siblings) /* local missing */
-    merge_target_abspath = APR_ARRAY_IDX(details->wc_siblings,
-                                         details->preferred_sibling_idx,
-                                         const char *);
-  else /* local move */
-    merge_target_abspath = details->moved_to_abspath;
+  if (details->wc_siblings)
+    {
+      merge_target_abspath = APR_ARRAY_IDX(details->wc_siblings,
+                                           details->preferred_sibling_idx,
+                                           const char *);
+    }
+  else if (details->wc_move_targets && details->move_target_repos_relpath)
+    {
+      apr_array_header_t *moves;
+      moves = svn_hash_gets(details->wc_move_targets,
+                            details->move_target_repos_relpath);
+      merge_target_abspath = APR_ARRAY_IDX(moves, details->wc_move_target_idx,
+                                           const char *);
+    }
+  else
+    return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                             _("Corresponding working copy node not found "
+                               "for '%s'"),
+                             svn_dirent_local_style(
+                               svn_dirent_skip_ancestor(
+                                 wcroot_abspath, conflict->local_abspath),
+                             scratch_pool));
 
   SVN_ERR(svn_wc__get_tmpdir(&wc_tmpdir, ctx->wc_ctx,
                              merge_target_abspath,
@@ -9381,13 +9470,13 @@ svn_client_conflict_text_get_resolution_
       add_resolution_option(*options, conflict,
         svn_client_conflict_option_incoming_text_where_conflicted,
         _("Accept incoming for conflicts"),
-        _("accept changes only where they conflict"),
+        _("accept incoming changes only where they conflict"),
         resolve_text_conflict);
 
       add_resolution_option(*options, conflict,
         svn_client_conflict_option_working_text_where_conflicted,
         _("Reject conflicts"),
-        _("reject changes which conflict and accept the rest"),
+        _("reject incoming changes which conflict and accept the rest"),
         resolve_text_conflict);
 
       add_resolution_option(*options, conflict,
@@ -10291,64 +10380,30 @@ configure_option_local_move_file_merge(s
                                  scratch_pool, scratch_pool));
 
       details = conflict->tree_conflict_local_details;
-      if (details != NULL && details->moves != NULL)
+      if (details != NULL && details->moves != NULL &&
+          details->move_target_repos_relpath != NULL)
         {
-          apr_hash_t *wc_move_targets = apr_hash_make(scratch_pool);
-          apr_pool_t *iterpool;
-          int i;
-
-          iterpool = svn_pool_create(scratch_pool);
-          for (i = 0; i < details->moves->nelts; i++)
-            {
-              struct repos_move_info *move;
-
-              svn_pool_clear(iterpool);
-              move = APR_ARRAY_IDX(details->moves, i, struct repos_move_info *);
-              SVN_ERR(follow_move_chains(wc_move_targets, move, ctx,
-                                         conflict->local_abspath,
-                                         svn_node_file,
-                                         incoming_new_repos_relpath,
-                                         incoming_new_pegrev,
-                                         scratch_pool, iterpool));
-            }
-          svn_pool_destroy(iterpool);
-
-          if (apr_hash_count(wc_move_targets) > 0)
-            {
-              apr_array_header_t *move_target_repos_relpaths;
-              const svn_sort__item_t *item;
-              apr_array_header_t *moved_to_abspaths;
-              const char *description;
+          apr_array_header_t *moves;
+          const char *moved_to_abspath;
+          const char *description;
 
-              /* Initialize to the first possible move target. Hopefully,
-               * in most cases there will only be one candidate anyway. */
-              move_target_repos_relpaths = svn_sort__hash(
-                                             wc_move_targets,
-                                             svn_sort_compare_items_as_paths,
-                                             scratch_pool);
-              item = &APR_ARRAY_IDX(move_target_repos_relpaths,
-                                    0, svn_sort__item_t);
-              moved_to_abspaths = item->value;
-              details->moved_to_abspath =
-                apr_pstrdup(conflict->pool,
-                            APR_ARRAY_IDX(moved_to_abspaths, 0, const char *));
+          moves = svn_hash_gets(details->wc_move_targets,
+                                details->move_target_repos_relpath);
+          moved_to_abspath =
+            APR_ARRAY_IDX(moves, details->wc_move_target_idx, const char *);
 
-              description =
-                apr_psprintf(
-                  scratch_pool, _("apply changes to move destination '%s'"),
-                  svn_dirent_local_style(
-                    svn_dirent_skip_ancestor(wcroot_abspath,
-                                             details->moved_to_abspath),
-                    scratch_pool));
+          description =
+            apr_psprintf(
+              scratch_pool, _("apply changes to move destination '%s'"),
+              svn_dirent_local_style(
+                svn_dirent_skip_ancestor(wcroot_abspath, moved_to_abspath),
+                scratch_pool));
 
-              add_resolution_option(
-                options, conflict,
-                svn_client_conflict_option_local_move_file_text_merge,
-                _("Apply to move destination"),
-                description, resolve_local_move_file_merge);
-            }
-          else
-            details->moved_to_abspath = NULL;
+          add_resolution_option(
+            options, conflict,
+            svn_client_conflict_option_local_move_file_text_merge,
+            _("Apply to move destination"),
+            description, resolve_local_move_file_merge);
         }
     }
 
@@ -10439,43 +10494,24 @@ configure_option_sibling_move_merge(svn_
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_client_conflict_option_get_moved_to_repos_relpath_candidates(
+/* Return a copy of the repos replath candidate list. */
+static svn_error_t *
+get_repos_relpath_candidates(
   apr_array_header_t **possible_moved_to_repos_relpaths,
-  svn_client_conflict_option_t *option,
+  apr_hash_t *wc_move_targets,
   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 *sorted_repos_relpaths;
   int i;
 
-  SVN_ERR_ASSERT(svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_incoming_move_file_text_merge ||
-                 svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_incoming_move_dir_merge);
-
-  victim_abspath = svn_client_conflict_get_local_abspath(conflict);
-  details = conflict->tree_conflict_incoming_details;
-  if (details == NULL || details->wc_move_targets == NULL)
-    return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
-                             _("Getting a list of possible move 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 repos replath candidate list. */
-  sorted_repos_relpaths = svn_sort__hash(details->wc_move_targets,
+  sorted_repos_relpaths = svn_sort__hash(wc_move_targets,
                                          svn_sort_compare_items_as_paths,
                                          scratch_pool);
 
-  *possible_moved_to_repos_relpaths = apr_array_make(
-                                        result_pool,
-                                        sorted_repos_relpaths->nelts,
-                                        sizeof (const char *));
+  *possible_moved_to_repos_relpaths =
+    apr_array_make(result_pool, sorted_repos_relpaths->nelts,
+                   sizeof (const char *));
   for (i = 0; i < sorted_repos_relpaths->nelts; i++)
     {
       svn_sort__item_t item;
@@ -10491,37 +10527,107 @@ svn_client_conflict_option_get_moved_to_
 }
 
 svn_error_t *
-svn_client_conflict_option_set_moved_to_repos_relpath(
+svn_client_conflict_option_get_moved_to_repos_relpath_candidates2(
+  apr_array_header_t **possible_moved_to_repos_relpaths,
   svn_client_conflict_option_t *option,
-  int preferred_move_target_idx,
-  svn_client_ctx_t *ctx,
+  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;
+  svn_wc_operation_t operation;
+  svn_wc_conflict_action_t incoming_change;
+  svn_wc_conflict_reason_t local_change;
+  svn_client_conflict_option_id_t id;
+
+  id = svn_client_conflict_option_get_id(option);
+  if (id != svn_client_conflict_option_incoming_move_file_text_merge &&
+      id != svn_client_conflict_option_incoming_move_dir_merge &&
+      id != svn_client_conflict_option_local_move_file_text_merge &&
+      id != svn_client_conflict_option_local_move_dir_merge &&
+      id != svn_client_conflict_option_sibling_move_file_text_merge &&
+      id != svn_client_conflict_option_sibling_move_dir_merge)
+    {
+      /* We cannot operate on this option. */
+      *possible_moved_to_repos_relpaths = NULL;
+      return SVN_NO_ERROR;
+    }
+
+  victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+  operation = svn_client_conflict_get_operation(conflict);
+  incoming_change = svn_client_conflict_get_incoming_change(conflict);
+  local_change = svn_client_conflict_get_local_change(conflict);
+
+  if (operation == svn_wc_operation_merge &&
+      incoming_change == svn_wc_conflict_action_edit &&
+      local_change == svn_wc_conflict_reason_missing)
+    {
+      struct conflict_tree_local_missing_details *details;
+
+      details = conflict->tree_conflict_local_details;
+      if (details == NULL || details->wc_move_targets == NULL)
+        return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                                 _("Getting a list of possible move targets "
+                                   "requires details for tree conflict at '%s' "
+                                   "to be fetched from the repository first"),
+                                svn_dirent_local_style(victim_abspath,
+                                                       scratch_pool));
+
+      SVN_ERR(get_repos_relpath_candidates(possible_moved_to_repos_relpaths,
+                                           details->wc_move_targets,
+                                           result_pool, scratch_pool));
+    }
+  else
+    {
+      struct conflict_tree_incoming_delete_details *details;
+
+      details = conflict->tree_conflict_incoming_details;
+      if (details == NULL || details->wc_move_targets == NULL)
+        return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                                 _("Getting a list of possible move targets "
+                                   "requires details for tree conflict at '%s' "
+                                   "to be fetched from the repository first"),
+                                svn_dirent_local_style(victim_abspath,
+                                                       scratch_pool));
+
+      SVN_ERR(get_repos_relpath_candidates(possible_moved_to_repos_relpaths,
+                                           details->wc_move_targets,
+                                           result_pool, scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_conflict_option_get_moved_to_repos_relpath_candidates(
+  apr_array_header_t **possible_moved_to_repos_relpaths,
+  svn_client_conflict_option_t *option,
+  apr_pool_t *result_pool,
+  apr_pool_t *scratch_pool)
+{
+  /* The only difference to API version 2 is an assertion failure if
+   * an unexpected option is passed.
+   * We do not emulate this old behaviour since clients written against
+   * the previous API will just keep working. */
+  return svn_error_trace(
+    svn_client_conflict_option_get_moved_to_repos_relpath_candidates2(
+      possible_moved_to_repos_relpaths, option, result_pool, scratch_pool));
+}
+
+static svn_error_t *
+set_wc_move_target(const char **new_hash_key,
+                   apr_hash_t *wc_move_targets,
+                   int preferred_move_target_idx,
+                   const char *victim_abspath,
+                   apr_pool_t *scratch_pool)
+{
   apr_array_header_t *move_target_repos_relpaths;
   svn_sort__item_t item;
   const char *move_target_repos_relpath;
   apr_hash_index_t *hi;
 
-  SVN_ERR_ASSERT(svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_incoming_move_file_text_merge ||
-                 svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_incoming_move_dir_merge);
-
-  victim_abspath = svn_client_conflict_get_local_abspath(conflict);
-  details = conflict->tree_conflict_incoming_details;
-  if (details == NULL || details->wc_move_targets == NULL)
-    return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
-                             _("Setting a move target requires details "
-                               "for tree conflict at '%s' to be fetched "
-                               "from the repository first"),
-                            svn_dirent_local_style(victim_abspath,
-                                                   scratch_pool));
-
   if (preferred_move_target_idx < 0 ||
-      preferred_move_target_idx >= apr_hash_count(details->wc_move_targets))
+      preferred_move_target_idx >= apr_hash_count(wc_move_targets))
     return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
                              _("Index '%d' is out of bounds of the possible "
                                "move target list for '%s'"),
@@ -10530,15 +10636,14 @@ svn_client_conflict_option_set_moved_to_
                                                    scratch_pool));
 
   /* Translate the index back into a hash table key. */
-  move_target_repos_relpaths =
-    svn_sort__hash(details->wc_move_targets,
-                   svn_sort_compare_items_as_paths,
-                   scratch_pool);
+  move_target_repos_relpaths = svn_sort__hash(wc_move_targets,
+                                              svn_sort_compare_items_as_paths,
+                                              scratch_pool);
   item = APR_ARRAY_IDX(move_target_repos_relpaths, preferred_move_target_idx,
                        svn_sort__item_t);
   move_target_repos_relpath = item.key;
   /* Find our copy of the hash key and remember the user's preference. */
-  for (hi = apr_hash_first(scratch_pool, details->wc_move_targets);
+  for (hi = apr_hash_first(scratch_pool, wc_move_targets);
        hi != NULL;
        hi = apr_hash_next(hi))
     {
@@ -10546,15 +10651,7 @@ svn_client_conflict_option_set_moved_to_
 
       if (strcmp(move_target_repos_relpath, repos_relpath) == 0)
         {
-          details->move_target_repos_relpath = repos_relpath;
-          /* Update option description. */
-          SVN_ERR(describe_incoming_move_merge_conflict_option(
-                    &option->description,
-                    conflict, ctx,
-                    details,
-                    conflict->pool,
-                    scratch_pool));
-
+          *new_hash_key = repos_relpath;
           return SVN_NO_ERROR;
         }
     }
@@ -10568,7 +10665,108 @@ svn_client_conflict_option_set_moved_to_
 }
 
 svn_error_t *
-svn_client_conflict_option_get_moved_to_abspath_candidates(
+svn_client_conflict_option_set_moved_to_repos_relpath2(
+  svn_client_conflict_option_t *option,
+  int preferred_move_target_idx,
+  svn_client_ctx_t *ctx,
+  apr_pool_t *scratch_pool)
+{
+  svn_client_conflict_t *conflict = option->conflict;
+  const char *victim_abspath;
+  svn_wc_operation_t operation;
+  svn_wc_conflict_action_t incoming_change;
+  svn_wc_conflict_reason_t local_change;
+  svn_client_conflict_option_id_t id;
+
+  id = svn_client_conflict_option_get_id(option);
+  if (id != svn_client_conflict_option_incoming_move_file_text_merge &&
+      id != svn_client_conflict_option_incoming_move_dir_merge &&
+      id != svn_client_conflict_option_local_move_file_text_merge &&
+      id != svn_client_conflict_option_local_move_dir_merge &&
+      id != svn_client_conflict_option_sibling_move_file_text_merge &&
+      id != svn_client_conflict_option_sibling_move_dir_merge)
+    return SVN_NO_ERROR; /* We cannot operate on this option. Nothing to do. */
+
+  victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+  operation = svn_client_conflict_get_operation(conflict);
+  incoming_change = svn_client_conflict_get_incoming_change(conflict);
+  local_change = svn_client_conflict_get_local_change(conflict);
+  
+  if (operation == svn_wc_operation_merge &&
+      incoming_change == svn_wc_conflict_action_edit &&
+      local_change == svn_wc_conflict_reason_missing)
+    {
+      struct conflict_tree_local_missing_details *details;
+
+      details = conflict->tree_conflict_local_details;
+      if (details == NULL || details->wc_move_targets == NULL)
+        return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                                 _("Setting a move target requires details "
+                                   "for tree conflict at '%s' to be fetched "
+                                   "from the repository first"),
+                                svn_dirent_local_style(victim_abspath,
+                                                       scratch_pool));
+
+      SVN_ERR(set_wc_move_target(&details->move_target_repos_relpath,
+                                 details->wc_move_targets,
+                                 preferred_move_target_idx,
+                                 victim_abspath, scratch_pool));
+      details->wc_move_target_idx = 0;
+
+      /* Update option description. */
+      SVN_ERR(conflict_tree_get_description_local_missing(
+                &option->description, conflict, ctx,
+                conflict->pool, scratch_pool));
+    }
+  else
+    {
+      struct conflict_tree_incoming_delete_details *details;
+
+      details = conflict->tree_conflict_incoming_details;
+      if (details == NULL || details->wc_move_targets == NULL)
+        return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                                 _("Setting a move target requires details "
+                                   "for tree conflict at '%s' to be fetched "
+                                   "from the repository first"),
+                                svn_dirent_local_style(victim_abspath,
+                                                       scratch_pool));
+
+      SVN_ERR(set_wc_move_target(&details->move_target_repos_relpath,
+                                 details->wc_move_targets,
+                                 preferred_move_target_idx,
+                                 victim_abspath, scratch_pool));
+      details->wc_move_target_idx = 0;
+
+      /* Update option description. */
+      SVN_ERR(describe_incoming_move_merge_conflict_option(
+                &option->description,
+                conflict, ctx,
+                details,
+                conflict->pool,
+                scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_conflict_option_set_moved_to_repos_relpath(
+  svn_client_conflict_option_t *option,
+  int preferred_move_target_idx,
+  svn_client_ctx_t *ctx,
+  apr_pool_t *scratch_pool)
+{
+  /* The only difference to API version 2 is an assertion failure if
+   * an unexpected option is passed.
+   * We do not emulate this old behaviour since clients written against
+   * the previous API will just keep working. */
+  return svn_error_trace(
+    svn_client_conflict_option_set_moved_to_repos_relpath2(option,
+      preferred_move_target_idx, ctx, scratch_pool));
+}
+
+svn_error_t *
+svn_client_conflict_option_get_moved_to_abspath_candidates2(
   apr_array_header_t **possible_moved_to_abspaths,
   svn_client_conflict_option_t *option,
   apr_pool_t *result_pool,
@@ -10580,17 +10778,20 @@ svn_client_conflict_option_get_moved_to_
   svn_wc_conflict_action_t incoming_change;
   svn_wc_conflict_reason_t local_change;
   int i;
+  svn_client_conflict_option_id_t id;
 
-  SVN_ERR_ASSERT(svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_incoming_move_file_text_merge ||
-                 svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_local_move_file_text_merge ||
-                 svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_incoming_move_dir_merge ||
-                 svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_sibling_move_file_text_merge ||
-                 svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_sibling_move_dir_merge);
+  id = svn_client_conflict_option_get_id(option);
+  if (id != svn_client_conflict_option_incoming_move_file_text_merge &&
+      id != svn_client_conflict_option_incoming_move_dir_merge &&
+      id != svn_client_conflict_option_local_move_file_text_merge &&
+      id != svn_client_conflict_option_local_move_dir_merge &&
+      id != svn_client_conflict_option_sibling_move_file_text_merge &&
+      id != svn_client_conflict_option_sibling_move_dir_merge)
+    {
+      /* We cannot operate on this option. */
+      *possible_moved_to_abspaths = NULL;
+      return NULL;
+    }
 
   victim_abspath = svn_client_conflict_get_local_abspath(conflict);
   operation = svn_client_conflict_get_operation(conflict);
@@ -10605,7 +10806,7 @@ svn_client_conflict_option_get_moved_to_
 
       details = conflict->tree_conflict_local_details;
       if (details == NULL ||
-          (details->moved_to_abspath == NULL && details->wc_siblings == NULL))
+          (details->wc_move_targets == NULL && details->wc_siblings == NULL))
        return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
                                 _("Getting a list of possible move siblings "
                                   "requires details for tree conflict at '%s' "
@@ -10615,9 +10816,22 @@ svn_client_conflict_option_get_moved_to_
 
        *possible_moved_to_abspaths = apr_array_make(result_pool, 1,
                                                     sizeof (const char *));
-      if (details->moved_to_abspath)
-         APR_ARRAY_PUSH(*possible_moved_to_abspaths, const char *) =
-           apr_pstrdup(result_pool, details->moved_to_abspath);
+      if (details->wc_move_targets)
+        {
+          apr_array_header_t *move_target_wc_abspaths;
+          move_target_wc_abspaths =
+            svn_hash_gets(details->wc_move_targets,
+                          details->move_target_repos_relpath);
+          for (i = 0; i < move_target_wc_abspaths->nelts; i++)
+            {
+              const char *moved_to_abspath;
+
+              moved_to_abspath = APR_ARRAY_IDX(move_target_wc_abspaths, i,
+                                               const char *);
+              APR_ARRAY_PUSH(*possible_moved_to_abspaths, const char *) =
+                apr_pstrdup(result_pool, moved_to_abspath);
+            }
+        }
 
       /* ### Siblings are actually 'corresponding nodes', not 'move targets'.
          ### But we provide them here to avoid another API function. */
@@ -10671,7 +10885,23 @@ svn_client_conflict_option_get_moved_to_
 }
 
 svn_error_t *
-svn_client_conflict_option_set_moved_to_abspath(
+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)
+{
+  /* The only difference to API version 2 is an assertion failure if
+   * an unexpected option is passed.
+   * We do not emulate this old behaviour since clients written against
+   * the previous API will just keep working. */
+  return svn_error_trace(
+    svn_client_conflict_option_get_moved_to_abspath_candidates2(
+      possible_moved_to_abspaths, option, result_pool, scratch_pool));
+}
+
+svn_error_t *
+svn_client_conflict_option_set_moved_to_abspath2(
   svn_client_conflict_option_t *option,
   int preferred_move_target_idx,
   svn_client_ctx_t *ctx,
@@ -10682,17 +10912,16 @@ svn_client_conflict_option_set_moved_to_
   svn_wc_operation_t operation;
   svn_wc_conflict_action_t incoming_change;
   svn_wc_conflict_reason_t local_change;
+  svn_client_conflict_option_id_t id;
 
-  SVN_ERR_ASSERT(svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_incoming_move_file_text_merge ||
-                 svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_local_move_file_text_merge ||
-                 svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_incoming_move_dir_merge ||
-                 svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_sibling_move_file_text_merge ||
-                 svn_client_conflict_option_get_id(option) ==
-                 svn_client_conflict_option_sibling_move_dir_merge);
+  id = svn_client_conflict_option_get_id(option);
+  if (id != svn_client_conflict_option_incoming_move_file_text_merge &&
+      id != svn_client_conflict_option_incoming_move_dir_merge &&
+      id != svn_client_conflict_option_local_move_file_text_merge &&
+      id != svn_client_conflict_option_local_move_dir_merge &&
+      id != svn_client_conflict_option_sibling_move_file_text_merge &&
+      id != svn_client_conflict_option_sibling_move_dir_merge)
+    return NULL; /* We cannot operate on this option. Nothing to do. */
 
   victim_abspath = svn_client_conflict_get_local_abspath(conflict);
   operation = svn_client_conflict_get_operation(conflict);
@@ -10714,35 +10943,63 @@ svn_client_conflict_option_set_moved_to_
                                  scratch_pool));
 
       details = conflict->tree_conflict_local_details;
-      if (details == NULL || details->wc_siblings == NULL)
+      if (details == NULL || (details->wc_siblings == NULL &&
+          details->wc_move_targets == NULL))
        return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
-                                _("Setting a move sibling requires details "
+                                _("Setting a move target requires details "
                                   "for tree conflict at '%s' to be fetched "
                                   "from the repository first"),
                                 svn_dirent_local_style(victim_abspath,
                                                        scratch_pool));
 
-      if (preferred_move_target_idx < 0 ||
-          preferred_move_target_idx > details->wc_siblings->nelts)
-        return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
-                                 _("Index '%d' is out of bounds of the possible "
-                                   "move sibling list for '%s'"),
-                                preferred_move_target_idx,
-                                svn_dirent_local_style(victim_abspath,
-                                                       scratch_pool));
-      /* Record the user's preference. */
-      details->preferred_sibling_idx = preferred_move_target_idx;
+      if (details->wc_siblings)
+        {
+          if (preferred_move_target_idx < 0 ||
+              preferred_move_target_idx > details->wc_siblings->nelts)
+            return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+                                     _("Index '%d' is out of bounds of the "
+                                       "possible move sibling list for '%s'"),
+                                    preferred_move_target_idx,
+                                    svn_dirent_local_style(victim_abspath,
+                                                           scratch_pool));
+          /* Record the user's preference. */
+          details->preferred_sibling_idx = preferred_move_target_idx;
 
-      /* Update option description. */
-      preferred_sibling = APR_ARRAY_IDX(details->wc_siblings,
-                                        details->preferred_sibling_idx,
-                                        const char *);
-      option->description =
-        apr_psprintf(
-          conflict->pool, _("apply changes to '%s'"),
-          svn_dirent_local_style(
-            svn_dirent_skip_ancestor(wcroot_abspath, preferred_sibling),
-            scratch_pool));
+          /* Update option description. */
+          preferred_sibling = APR_ARRAY_IDX(details->wc_siblings,
+                                            details->preferred_sibling_idx,
+                                            const char *);
+          option->description =
+            apr_psprintf(
+              conflict->pool, _("apply changes to '%s'"),
+              svn_dirent_local_style(
+                svn_dirent_skip_ancestor(wcroot_abspath, preferred_sibling),
+                scratch_pool));
+        }
+      else if (details->wc_move_targets)
+       {
+          apr_array_header_t *move_target_wc_abspaths;
+          move_target_wc_abspaths =
+            svn_hash_gets(details->wc_move_targets,
+                          details->move_target_repos_relpath);
+
+          if (preferred_move_target_idx < 0 ||
+              preferred_move_target_idx > move_target_wc_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. */
+          details->wc_move_target_idx = preferred_move_target_idx;
+
+          /* Update option description. */
+          SVN_ERR(conflict_tree_get_description_local_missing(
+                    &option->description, conflict, ctx,
+                    conflict->pool, scratch_pool));
+       }
     }
   else
     {
@@ -10785,6 +11042,22 @@ svn_client_conflict_option_set_moved_to_
 }
 
 svn_error_t *
+svn_client_conflict_option_set_moved_to_abspath(
+  svn_client_conflict_option_t *option,
+  int preferred_move_target_idx,
+  svn_client_ctx_t *ctx,
+  apr_pool_t *scratch_pool)
+{
+  /* The only difference to API version 2 is an assertion failure if
+   * an unexpected option is passed.
+   * We do not emulate this old behaviour since clients written against
+   * the previous API will just keep working. */
+  return svn_error_trace(
+    svn_client_conflict_option_set_moved_to_abspath2(option,
+      preferred_move_target_idx, ctx, scratch_pool));
+}
+
+svn_error_t *
 svn_client_conflict_tree_get_resolution_options(apr_array_header_t **options,
                                                 svn_client_conflict_t *conflict,
                                                 svn_client_ctx_t *ctx,

Modified: subversion/branches/1.11.x/subversion/svn/conflict-callbacks.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.11.x/subversion/svn/conflict-callbacks.c?rev=1841732&r1=1841731&r2=1841732&view=diff
==============================================================================
--- subversion/branches/1.11.x/subversion/svn/conflict-callbacks.c (original)
+++ subversion/branches/1.11.x/subversion/svn/conflict-callbacks.c Sun Sep 23 09:43:41 2018
@@ -1534,22 +1534,14 @@ build_tree_conflict_options(
           id != svn_client_conflict_option_accept_current_wc_state)
         *all_options_are_dumb = FALSE;
 
-      if (id == svn_client_conflict_option_incoming_move_file_text_merge ||
-          id == svn_client_conflict_option_incoming_move_dir_merge)
-        {
+        if (*possible_moved_to_repos_relpaths == NULL)
           SVN_ERR(
-            svn_client_conflict_option_get_moved_to_repos_relpath_candidates(
+            svn_client_conflict_option_get_moved_to_repos_relpath_candidates2(
               possible_moved_to_repos_relpaths, builtin_option,
               result_pool, iterpool));
-          SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
-                    possible_moved_to_abspaths, builtin_option,
-                    result_pool, iterpool));
-        }
-      else if (id == svn_client_conflict_option_local_move_file_text_merge ||
-          id == svn_client_conflict_option_local_move_dir_merge ||
-          id == svn_client_conflict_option_sibling_move_file_text_merge ||
-          id == svn_client_conflict_option_sibling_move_dir_merge)
-          SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+
+        if (*possible_moved_to_abspaths == NULL)
+          SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates2(
                     possible_moved_to_abspaths, builtin_option,
                     result_pool, iterpool));
     }
@@ -1682,6 +1674,69 @@ prompt_move_target_path(int *preferred_m
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+find_conflict_option_with_repos_move_targets(
+  svn_client_conflict_option_t **option_with_move_targets,
+  apr_array_header_t *options,
+  apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  int i;
+  apr_array_header_t *possible_moved_to_repos_relpaths = NULL;
+  
+  *option_with_move_targets = NULL;
+
+  for (i = 0; i < options->nelts; i++)
+    {
+      svn_client_conflict_option_t *option;
+
+      svn_pool_clear(iterpool);
+      option = APR_ARRAY_IDX(options, i, svn_client_conflict_option_t *);
+      SVN_ERR(svn_client_conflict_option_get_moved_to_repos_relpath_candidates2(
+        &possible_moved_to_repos_relpaths, option, iterpool, iterpool));
+      if (possible_moved_to_repos_relpaths)
+        {
+          *option_with_move_targets = option;
+          break;
+        }
+    }
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+find_conflict_option_with_working_copy_move_targets(
+  svn_client_conflict_option_t **option_with_move_targets,
+  apr_array_header_t *options,
+  apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  int i;
+  apr_array_header_t *possible_moved_to_abspaths = NULL;
+  
+  *option_with_move_targets = NULL;
+
+  for (i = 0; i < options->nelts; i++)
+    {
+      svn_client_conflict_option_t *option;
+
+      svn_pool_clear(iterpool);
+      option = APR_ARRAY_IDX(options, i, svn_client_conflict_option_t *);
+      SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates2(
+              &possible_moved_to_abspaths, option, scratch_pool,
+              iterpool));
+      if (possible_moved_to_abspaths)
+        {
+          *option_with_move_targets = option;
+          break;
+        }
+    }
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
 /* Ask the user what to do about the tree conflict described by CONFLICT
  * and either resolve the conflict accordingly or postpone resolution.
  * SCRATCH_POOL is used for temporary allocations. */
@@ -1809,7 +1864,7 @@ handle_tree_conflict(svn_boolean_t *reso
         {
           int preferred_move_target_idx;
           apr_array_header_t *options;
-          svn_client_conflict_option_t *conflict_option;
+          svn_client_conflict_option_t *option;
 
           SVN_ERR(prompt_move_target_path(&preferred_move_target_idx,
                                           possible_moved_to_repos_relpaths,
@@ -1822,22 +1877,12 @@ handle_tree_conflict(svn_boolean_t *reso
                                                                   ctx,
                                                                   iterpool,
                                                                   iterpool));
-          conflict_option =
-            svn_client_conflict_option_find_by_id( 
-              options,
-              svn_client_conflict_option_incoming_move_file_text_merge);
-          if (conflict_option == NULL)
+          SVN_ERR(find_conflict_option_with_repos_move_targets(
+            &option, options, iterpool));
+          if (option)
             {
-              conflict_option =
-                svn_client_conflict_option_find_by_id( 
-                  options, svn_client_conflict_option_incoming_move_dir_merge);
-            }
-
-          if (conflict_option)
-            {
-              SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(
-                        conflict_option, preferred_move_target_idx,
-                        ctx, iterpool));
+              SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath2(
+                        option, preferred_move_target_idx, ctx, iterpool));
               repos_move_target_chosen = TRUE;
               wc_move_target_chosen = FALSE;
 
@@ -1863,7 +1908,7 @@ handle_tree_conflict(svn_boolean_t *reso
         {
           int preferred_move_target_idx;
           apr_array_header_t *options;
-          svn_client_conflict_option_t *conflict_option;
+          svn_client_conflict_option_t *option;
 
           SVN_ERR(prompt_move_target_path(&preferred_move_target_idx,
                                            possible_moved_to_abspaths, TRUE,
@@ -1875,50 +1920,12 @@ handle_tree_conflict(svn_boolean_t *reso
                                                                   ctx,
                                                                   iterpool,
                                                                   iterpool));
-          conflict_option =
-            svn_client_conflict_option_find_by_id( 
-              options,
-              svn_client_conflict_option_incoming_move_file_text_merge);
-          if (conflict_option == NULL)
-            {
-              conflict_option =
-                svn_client_conflict_option_find_by_id( 
-                  options,
-                  svn_client_conflict_option_local_move_file_text_merge);
-            }
-          if (conflict_option == NULL)
-            {
-              conflict_option =
-                svn_client_conflict_option_find_by_id( 
-                  options,
-                  svn_client_conflict_option_local_move_dir_merge);
-            }
-          if (conflict_option == NULL)
-            {
-              conflict_option =
-                svn_client_conflict_option_find_by_id( 
-                  options,
-                  svn_client_conflict_option_sibling_move_file_text_merge);
-            }
-          if (conflict_option == NULL)
-            {
-              conflict_option =
-                svn_client_conflict_option_find_by_id( 
-                  options,
-                  svn_client_conflict_option_sibling_move_dir_merge);
-            }
-          if (conflict_option == NULL)
-            {
-              conflict_option =
-                svn_client_conflict_option_find_by_id( 
-                  options, svn_client_conflict_option_incoming_move_dir_merge);
-            }
-
-          if (conflict_option)
+          SVN_ERR(find_conflict_option_with_working_copy_move_targets(
+            &option, options, iterpool));
+          if (option)
             {
-              SVN_ERR(svn_client_conflict_option_set_moved_to_abspath(
-                        conflict_option, preferred_move_target_idx, ctx,
-                        iterpool));
+              SVN_ERR(svn_client_conflict_option_set_moved_to_abspath2(
+                        option, preferred_move_target_idx, ctx, iterpool));
               wc_move_target_chosen = TRUE;
 
               /* Update option description. */

Modified: subversion/branches/1.11.x/subversion/tests/libsvn_client/conflicts-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/1.11.x/subversion/tests/libsvn_client/conflicts-test.c?rev=1841732&r1=1841731&r2=1841732&view=diff
==============================================================================
--- subversion/branches/1.11.x/subversion/tests/libsvn_client/conflicts-test.c (original)
+++ subversion/branches/1.11.x/subversion/tests/libsvn_client/conflicts-test.c Sun Sep 23 09:43:41 2018
@@ -5700,6 +5700,185 @@ test_cherry_pick_post_move_edit_dir(cons
   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;
+}
+
 /* ========================================================================== */
 
 
@@ -5798,6 +5977,8 @@ static struct svn_test_descriptor_t test
                        "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_NULL
   };
 



Mime
View raw message