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/10 15:04:06 UTC

svn commit: r1747727 - in /subversion/trunk/subversion: include/private/svn_wc_private.h include/svn_client.h libsvn_client/conflicts.c libsvn_wc/conflicts.c libsvn_wc/wc-queries.sql libsvn_wc/wc_db.c libsvn_wc/wc_db.h svn/conflict-callbacks.c

Author: stsp
Date: Fri Jun 10 15:04:06 2016
New Revision: 1747727

URL: http://svn.apache.org/viewvc?rev=1747727&view=rev
Log:
Add a conflict resolver option which follows an incoming file move.
This new option works for update, switch, and merge operations.

There is no unit test for this option yet.
I have not tested reverse-updates or reverse-merges yet.
We need many many additional tests anyway.

* subversion/include/private/svn_wc_private.h
  (svn_wc__guess_incoming_move_target_node): Declare.

* subversion/include/svn_client.h
  (svn_client_conflict_option_incoming_move_file_text_merge): New option ID.

* subversion/libsvn_client/conflicts.c
  (conflict_tree_incoming_delete_details): Add 'moved_to_abspath'.
  (resolve_merge_incoming_added_file_text_merge): Fix typo in comment.
  (resolve_incoming_move_file_text_merge): New resolver function.
  (configure_option_incoming_move_file_merge): New option config function.
  (svn_client_conflict_tree_get_resolution_options): Configure new option.

* subversion/libsvn_wc/conflicts.c
  (svn_wc__guess_incoming_move_target_node): Implement. This function
   attempts to map a deleted tree conflict victim to a working copy node
   which corresponds to the incoming move target.

* subversion/libsvn_wc/wc-queries.sql
  (STMT_FIND_REPOS_PATH_IN_WC): New query. Returns all local relpaths at
   the 'working' layer (i.e. from nodes_current) which map to a particular
   repository path at a particular revision.

* subversion/libsvn_wc/wc_db.c
  (svn_wc__find_repos_node_in_wc): New. Runs the above query.

* subversion/libsvn_wc/wc_db.h
  (svn_wc__find_repos_node_in_wc): Declare.

* subversion/svn/conflict-callbacks.c
  (builtin_resolver_options): Wire up the new option in the conflict menu.

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
    subversion/trunk/subversion/libsvn_wc/wc-queries.sql
    subversion/trunk/subversion/libsvn_wc/wc_db.c
    subversion/trunk/subversion/libsvn_wc/wc_db.h
    subversion/trunk/subversion/svn/conflict-callbacks.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=1747727&r1=1747726&r2=1747727&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_wc_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_wc_private.h Fri Jun 10 15:04:06 2016
@@ -1890,6 +1890,30 @@ 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
+ * MOVED_TO_REPOS_RELPATH@REV of the tree conflict victim at VICTIM_ABSPATH.
+ * If no such node can be found, set *MOVED_TO_ABSPATH to NULL.
+ *
+ * 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
+ * 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.
+ */
+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,
+                                        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
  * for addition to the repository, remembering the history. Mark @a src_abspath

Modified: subversion/trunk/subversion/include/svn_client.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_client.h?rev=1747727&r1=1747726&r2=1747727&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_client.h (original)
+++ subversion/trunk/subversion/include/svn_client.h Fri Jun 10 15:04:06 2016
@@ -4423,6 +4423,9 @@ typedef enum svn_client_conflict_option_
   svn_client_conflict_option_incoming_delete_ignore,
   svn_client_conflict_option_incoming_delete_accept,
 
+  /* Options for incoming move vs local edit */
+  svn_client_conflict_option_incoming_move_file_text_merge,
+
 } svn_client_conflict_option_id_t;
 
 /**

Modified: subversion/trunk/subversion/libsvn_client/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/conflicts.c?rev=1747727&r1=1747726&r2=1747727&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/conflicts.c (original)
+++ subversion/trunk/subversion/libsvn_client/conflicts.c Fri Jun 10 15:04:06 2016
@@ -1773,6 +1773,10 @@ 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 were we believe the moved-here node corresponding to the
+   * deleted node exists in the working copy. */
+  const char *moved_to_abspath;
 };
 
 static const char *
@@ -4541,7 +4545,7 @@ resolve_merge_incoming_added_file_text_m
 
   local_abspath = svn_client_conflict_get_local_abspath(conflict);
 
-  /* Set up tempory storage for the repository version of file. */
+  /* Set up temporary storage for the repository version of file. */
   SVN_ERR(svn_wc__get_tmpdir(&wc_tmpdir, ctx->wc_ctx, local_abspath,
                              scratch_pool, scratch_pool));
   SVN_ERR(svn_io_open_unique_file3(&incoming_new_file,
@@ -5579,6 +5583,174 @@ unlock_wc:
   return SVN_NO_ERROR;
 }
 
+/* Implements conflict_option_resolve_func_t. */
+static svn_error_t *
+resolve_incoming_move_file_text_merge(svn_client_conflict_option_t *option,
+                                      svn_client_conflict_t *conflict,
+                                      apr_pool_t *scratch_pool)
+{
+  svn_client_conflict_option_id_t option_id;
+  const char *local_abspath;
+  const char *lock_abspath;
+  svn_client_ctx_t *ctx = conflict->ctx;
+  svn_error_t *err;
+  const char *repos_root_url;
+  const char *repos_uuid;
+  const char *incoming_old_repos_relpath;
+  svn_revnum_t incoming_old_pegrev;
+  const char *incoming_new_repos_relpath;
+  svn_revnum_t incoming_new_pegrev;
+  const char *wc_tmpdir;
+  apr_file_t *ancestor_file;
+  const char *ancestor_abspath;
+  svn_stream_t *ancestor_stream;
+  apr_hash_t *victim_props;
+  apr_hash_t *move_target_props;
+  const char *ancestor_url;
+  const char *corrected_url;
+  svn_ra_session_t *ra_session;
+  svn_wc_merge_outcome_t merge_content_outcome;
+  svn_wc_notify_state_t merge_props_outcome;
+  apr_array_header_t *propdiffs;
+  struct conflict_tree_incoming_delete_details *details;
+
+  details = conflict->tree_conflict_incoming_details;
+  if (details == 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,
+                            svn_dirent_local_style(local_abspath,
+                                                   scratch_pool));
+
+  option_id = svn_client_conflict_option_get_id(option);
+  local_abspath = svn_client_conflict_get_local_abspath(conflict);
+  SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, &repos_uuid,
+                                             conflict, scratch_pool,
+                                             scratch_pool));
+  SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+            &incoming_old_repos_relpath, &incoming_old_pegrev,
+            NULL, conflict, scratch_pool,
+            scratch_pool));
+  SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+            &incoming_new_repos_relpath, &incoming_new_pegrev,
+            NULL, conflict, scratch_pool,
+            scratch_pool));
+
+  /* Set up temporary storage for the common ancestor version of the file. */
+  SVN_ERR(svn_wc__get_tmpdir(&wc_tmpdir, ctx->wc_ctx, local_abspath,
+                             scratch_pool, scratch_pool));
+  SVN_ERR(svn_io_open_unique_file3(&ancestor_file,
+                                   &ancestor_abspath, wc_tmpdir,
+                                   svn_io_file_del_on_pool_cleanup,
+                                   scratch_pool, scratch_pool));
+  ancestor_stream = svn_stream_from_aprfile2(ancestor_file, TRUE, scratch_pool);
+
+  /* Fetch the ancestor file's content. */
+  ancestor_url = svn_path_url_add_component2(repos_root_url,
+                                             incoming_old_repos_relpath,
+                                             scratch_pool);
+  SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
+                                               ancestor_url, NULL, NULL,
+                                               FALSE, FALSE, conflict->ctx,
+                                               scratch_pool, scratch_pool));
+  SVN_ERR(svn_ra_get_file(ra_session, "", incoming_old_pegrev,
+                          ancestor_stream, NULL, /* fetched_rev */
+                          NULL /* we don't need these props */, scratch_pool));
+
+  /* Flush ancestor file to disk. */
+  SVN_ERR(svn_stream_close(ancestor_stream));
+  SVN_ERR(svn_io_file_flush(ancestor_file, scratch_pool));
+
+  /* ### The following WC modifications should be atomic. */
+  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(&lock_abspath, ctx->wc_ctx,
+                                                 local_abspath,
+                                                 scratch_pool, scratch_pool));
+
+  err = verify_local_state_for_incoming_delete(conflict, option, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+   /* Get a copy of the conflict victim's properties. */
+  err = svn_wc_prop_list2(&victim_props, ctx->wc_ctx, local_abspath,
+                          scratch_pool, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  /* Get a copy of the move target's properties. */
+  err = svn_wc_prop_list2(&move_target_props, ctx->wc_ctx,
+                          details->moved_to_abspath,
+                          scratch_pool, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  /* Create a property diff for the files. */
+  err = svn_prop_diffs(&propdiffs, victim_props, move_target_props,
+                       scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  /* 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,
+                      NULL, NULL, NULL, /* labels */
+                      NULL, NULL, /* conflict versions */
+                      FALSE, /* dry run */
+                      NULL, NULL, /* diff3_cmd, merge_options */
+                      NULL, propdiffs,
+                      NULL, NULL, /* conflict func/baton */
+                      ctx->cancel_func, ctx->cancel_baton,
+                      scratch_pool);
+  svn_io_sleep_for_timestamps(details->moved_to_abspath, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  if (ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify;
+
+      /* Tell the world about the file merge that just happened. */
+      notify = svn_wc_create_notify(details->moved_to_abspath,
+                                    svn_wc_notify_update_update,
+                                    scratch_pool);
+      if (merge_content_outcome == svn_wc_merge_conflict)
+        notify->content_state = svn_wc_notify_state_conflicted;
+      else
+        notify->content_state = svn_wc_notify_state_merged;
+      notify->prop_state = merge_props_outcome;
+      notify->kind = svn_node_file;
+      ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+    }
+
+  /* The merge is done. Local edits are now at the moved-to location.
+   * Delete the tree conflict victim (clears the tree conflict marker). */
+  err = svn_wc_delete4(ctx->wc_ctx, local_abspath, FALSE, FALSE,
+                       ctx->cancel_func, ctx->cancel_baton,
+                       ctx->notify_func2, ctx->notify_baton2,
+                       scratch_pool);
+
+  if (ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify;
+
+      notify = svn_wc_create_notify(local_abspath, svn_wc_notify_resolved_tree,
+                                    scratch_pool);
+      ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+    }
+
+  conflict->resolution_tree = option_id;
+
+unlock_wc:
+  err = svn_error_compose_create(err, svn_wc__release_write_lock(ctx->wc_ctx,
+                                                                 lock_abspath,
+                                                                 scratch_pool));
+  SVN_ERR(err);
+
+  return SVN_NO_ERROR;
+}
+
 /* Resolver options for a text conflict */
 static const svn_client_conflict_option_t text_conflict_options[] =
 {
@@ -6404,6 +6576,92 @@ configure_option_incoming_delete_accept(
   return SVN_NO_ERROR;
 }
 
+/* Configure 'incoming move file merge' resolution option for
+ * a tree conflict. */
+static svn_error_t *
+configure_option_incoming_move_file_merge(svn_client_conflict_t *conflict,
+                                          apr_array_header_t *options,
+                                          apr_pool_t *scratch_pool)
+{
+  svn_node_kind_t victim_node_kind;
+  svn_wc_conflict_action_t incoming_change;
+  svn_wc_conflict_reason_t local_change;
+  const char *incoming_old_repos_relpath;
+  svn_revnum_t incoming_old_pegrev;
+  svn_node_kind_t incoming_old_kind;
+  const char *incoming_new_repos_relpath;
+  svn_revnum_t incoming_new_pegrev;
+  svn_node_kind_t incoming_new_kind;
+  struct conflict_tree_incoming_delete_details *details;
+
+  details = conflict->tree_conflict_incoming_details;
+  if (details == NULL || details->move == NULL)
+    return SVN_NO_ERROR;
+
+  incoming_change = svn_client_conflict_get_incoming_change(conflict);
+  local_change = svn_client_conflict_get_local_change(conflict);
+  victim_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
+  SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+            &incoming_old_repos_relpath, &incoming_old_pegrev,
+            &incoming_old_kind, conflict, scratch_pool,
+            scratch_pool));
+  SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+            &incoming_new_repos_relpath, &incoming_new_pegrev,
+            &incoming_new_kind, conflict, scratch_pool,
+            scratch_pool));
+
+  if (victim_node_kind == svn_node_file &&
+      incoming_old_kind == svn_node_file &&
+      incoming_new_kind == svn_node_none &&
+      incoming_change == svn_wc_conflict_action_delete)
+    {
+      svn_client_conflict_option_t *option;
+      const char *wcroot_abspath;
+      const char *local_abspath;
+      struct repos_move_info *move;
+      const char *moved_to_abspath;
+
+      option = apr_pcalloc(options->pool, sizeof(*option));
+      option->id = svn_client_conflict_option_incoming_move_file_text_merge;
+      SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, conflict->ctx->wc_ctx,
+                                 conflict->local_abspath, scratch_pool,
+                                 scratch_pool));
+      local_abspath = svn_client_conflict_get_local_abspath(conflict);
+
+      /* Find the last move in the move chain. This should correspond to
+       * the node's location in incoming_new_pegrev. */
+      /* ### What about reverse-merges and reverse-updates? */
+      move = details->move;
+      while (move->next)
+        move = move->next;
+
+      SVN_ERR(svn_wc__guess_incoming_move_target_node(
+                &moved_to_abspath, conflict->ctx->wc_ctx, local_abspath,
+                move->moved_to_repos_relpath, incoming_new_pegrev,
+                scratch_pool, scratch_pool));
+      if (moved_to_abspath == NULL)
+        return SVN_NO_ERROR;
+
+      details->moved_to_abspath = apr_pstrdup(conflict->pool,
+                                              moved_to_abspath);
+      option->description =
+        apr_psprintf(
+          options->pool,
+          _("follow move-away of '%s' and merge with '%s'"),
+          svn_dirent_local_style(svn_dirent_skip_ancestor(wcroot_abspath,
+                                                          local_abspath),
+                                 scratch_pool),
+          svn_dirent_local_style(svn_dirent_skip_ancestor(wcroot_abspath,
+                                                          moved_to_abspath),
+                                 scratch_pool));
+      option->conflict = conflict;
+      option->do_resolve_func = resolve_incoming_move_file_text_merge;
+      APR_ARRAY_PUSH(options, const svn_client_conflict_option_t *) = option;
+    }
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_client_conflict_tree_get_resolution_options(apr_array_header_t **options,
                                                 svn_client_conflict_t *conflict,
@@ -6451,6 +6709,8 @@ svn_client_conflict_tree_get_resolution_
                                                   scratch_pool));
   SVN_ERR(configure_option_incoming_delete_accept(conflict, *options,
                                                   scratch_pool));
+  SVN_ERR(configure_option_incoming_move_file_merge(conflict, *options,
+                                                    scratch_pool));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/trunk/subversion/libsvn_wc/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/conflicts.c?rev=1747727&r1=1747726&r2=1747727&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/conflicts.c (original)
+++ subversion/trunk/subversion/libsvn_wc/conflicts.c Fri Jun 10 15:04:06 2016
@@ -3684,3 +3684,74 @@ svn_wc__conflict_tree_update_moved_away_
 
   return SVN_NO_ERROR;
 }
+
+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,
+                                        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;
+
+  *moved_to_abspath = NULL;
+  SVN_ERR(svn_wc__find_repos_node_in_wc(&candidates, wc_ctx->db, victim_abspath,
+                                        moved_to_repos_relpath, rev,
+                                        scratch_pool, scratch_pool));
+
+  /* Find a "useful move target" node in our set of candidates.
+   * Since there is no way to be certain, filter out nodes which seem
+   * unlikely candidates, and return the first node which is "good enough".
+   * Nodes which are tree conflict victims don't count, and nodes which
+   * cannot be modified (e.g. replaced or deleted nodes) don't count.
+   * Ignore switched nodes as well, since that is an unlikely case during
+   * update/swtich/merge conflict resolution. And externals shouldn't even
+   * be on our candidate list in the first place. */
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; i < candidates->nelts; i++)
+    {
+      const char *local_abspath;
+      svn_boolean_t tree_conflicted;
+      svn_wc__db_status_t status;
+      svn_boolean_t is_wcroot;
+      svn_boolean_t is_switched;
+
+      svn_pool_clear(iterpool);
+
+      local_abspath = APR_ARRAY_IDX(candidates, i, const char *);
+
+      SVN_ERR(svn_wc__internal_conflicted_p(NULL, NULL, &tree_conflicted,
+                                            wc_ctx->db, local_abspath,
+                                            iterpool));
+      if (tree_conflicted)
+        continue;
+
+      SVN_ERR(svn_wc__db_read_info(&status,
+                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                   NULL, NULL, NULL, NULL, NULL,
+                                   wc_ctx->db, local_abspath, iterpool,
+                                   iterpool));
+      if (status != svn_wc__db_status_normal &&
+          status != svn_wc__db_status_added)
+        continue;
+
+      SVN_ERR(svn_wc__db_is_switched(&is_wcroot, &is_switched, NULL,
+                                     wc_ctx->db, local_abspath, iterpool));
+      if (is_wcroot || is_switched)
+        continue;
+
+      /* This might be a move target. Fingers crossed ;-) */
+      *moved_to_abspath = apr_pstrdup(result_pool, local_abspath);
+      break;
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}

Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=1747727&r1=1747726&r2=1747727&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Fri Jun 10 15:04:06 2016
@@ -1291,6 +1291,10 @@ PRAGMA locking_mode = exclusive;
    exclusive-locking is mostly used on remote file systems. */
 PRAGMA journal_mode = DELETE
 
+-- STMT_FIND_REPOS_PATH_IN_WC
+SELECT local_relpath FROM nodes_current
+  WHERE wc_id = ?1 AND repos_path = ?2 AND revision = ?3
+
 /* ------------------------------------------------------------------------- */
 
 /* these are used in entries.c  */

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1747727&r1=1747726&r2=1747727&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.c Fri Jun 10 15:04:06 2016
@@ -16577,3 +16577,48 @@ svn_wc__db_process_commit_queue(svn_wc__
 
   return SVN_NO_ERROR;
 }
+
+svn_error_t *
+svn_wc__find_repos_node_in_wc(apr_array_header_t **local_abspath_list,
+                              svn_wc__db_t *db,
+                              const char *wri_abspath,
+                              const char *repos_relpath,
+                              svn_revnum_t rev,
+                              apr_pool_t *result_pool,
+                              apr_pool_t *scratch_pool)
+{
+  svn_wc__db_wcroot_t *wcroot;
+  const char *wri_relpath;
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
+
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &wri_relpath, db,
+                                                 wri_abspath, scratch_pool,
+                                                 scratch_pool));
+  VERIFY_USABLE_WCROOT(wcroot);
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_FIND_REPOS_PATH_IN_WC));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, repos_relpath, rev));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  *local_abspath_list = apr_array_make(result_pool, have_row ? 1 : 0,
+                                       sizeof(const char*));
+  while (have_row)
+    {
+      const char *local_relpath;
+      const char *local_abspath;
+
+      local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+      local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
+                                      result_pool);
+      APR_ARRAY_PUSH(*local_abspath_list, const char *) = local_abspath;
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+    
+  return svn_error_trace(svn_sqlite__reset(stmt));
+}
+

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.h?rev=1747727&r1=1747726&r2=1747727&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.h Fri Jun 10 15:04:06 2016
@@ -3459,6 +3459,24 @@ svn_wc__required_lock_for_resolve(const
                                   const char *local_abspath,
                                   apr_pool_t *result_pool,
                                   apr_pool_t *scratch_pool);
+
+/* Return an array of const char * elements, which represent local absolute
+ * paths for nodes, within the working copy indicated by WRI_ABSPATH, which
+ * correspond to REPOS_RELPATH@REV.
+ * If no such nodes exist, return an empty array.
+ *
+ * Note that this function returns each and every such node that is known
+ * in the WC, including, for example, nodes that were children of a directory
+ * which has been replaced.
+ */
+svn_error_t *
+svn_wc__find_repos_node_in_wc(apr_array_header_t **local_abspath_list,
+                              svn_wc__db_t *db,
+                              const char *wri_abspath,
+                              const char *repos_relpath,
+                              svn_revnum_t rev,
+                              apr_pool_t *result_pool,
+                              apr_pool_t *scratch_pool);
 /* @} */
 
 typedef svn_error_t * (*svn_wc__db_verify_cb_t)(void *baton,

Modified: subversion/trunk/subversion/svn/conflict-callbacks.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/conflict-callbacks.c?rev=1747727&r1=1747726&r2=1747727&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/conflict-callbacks.c (original)
+++ subversion/trunk/subversion/svn/conflict-callbacks.c Fri Jun 10 15:04:06 2016
@@ -445,6 +445,10 @@ static const resolver_option_t builtin_r
   { "a", N_("accept incoming deletion"), NULL,
     svn_client_conflict_option_incoming_delete_accept },
 
+  /* Options for incoming move vs local edit. */
+  { "m", N_("follow incoming move and merge"), NULL,
+    svn_client_conflict_option_incoming_move_file_text_merge },
+
   { NULL }
 };