subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From julianf...@apache.org
Subject svn commit: r1853900 - in /subversion/branches/shelving-v3: ./ subversion/include/private/ subversion/libsvn_client/ subversion/libsvn_repos/ subversion/libsvn_subr/ subversion/libsvn_wc/ subversion/tests/cmdline/ subversion/tests/cmdline/svntest/ subv...
Date Tue, 19 Feb 2019 17:10:12 GMT
Author: julianfoad
Date: Tue Feb 19 17:10:11 2019
New Revision: 1853900

URL: http://svn.apache.org/viewvc?rev=1853900&view=rev
Log:
On the 'shelving-v3' branch: Catch up with trunk@1853898.

Modified:
    subversion/branches/shelving-v3/   (props changed)
    subversion/branches/shelving-v3/subversion/include/private/svn_client_mtcc.h
    subversion/branches/shelving-v3/subversion/include/private/svn_wc_private.h
    subversion/branches/shelving-v3/subversion/libsvn_client/conflicts.c
    subversion/branches/shelving-v3/subversion/libsvn_client/shelf.c
    subversion/branches/shelving-v3/subversion/libsvn_repos/dump.c
    subversion/branches/shelving-v3/subversion/libsvn_subr/error.c
    subversion/branches/shelving-v3/subversion/libsvn_wc/node.c
    subversion/branches/shelving-v3/subversion/libsvn_wc/wc-queries.sql
    subversion/branches/shelving-v3/subversion/libsvn_wc/wc_db.c
    subversion/branches/shelving-v3/subversion/libsvn_wc/wc_db.h
    subversion/branches/shelving-v3/subversion/libsvn_wc/wcroot_anchor.c
    subversion/branches/shelving-v3/subversion/tests/cmdline/svnadmin_tests.py
    subversion/branches/shelving-v3/subversion/tests/cmdline/svntest/sandbox.py
    subversion/branches/shelving-v3/subversion/tests/libsvn_client/conflicts-test.c
    subversion/branches/shelving-v3/subversion/tests/libsvn_wc/wc-queries-test.c

Propchange: subversion/branches/shelving-v3/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Tue Feb 19 17:10:11 2019
@@ -101,3 +101,4 @@
 /subversion/branches/verify-at-commit:1462039-1462408
 /subversion/branches/verify-keep-going:1439280-1546110
 /subversion/branches/wc-collate-path:1402685-1480384
+/subversion/trunk:1853394-1853898

Modified: subversion/branches/shelving-v3/subversion/include/private/svn_client_mtcc.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/include/private/svn_client_mtcc.h?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/include/private/svn_client_mtcc.h (original)
+++ subversion/branches/shelving-v3/subversion/include/private/svn_client_mtcc.h Tue Feb 19 17:10:11 2019
@@ -207,6 +207,17 @@ svn_client__mtcc_check_path(svn_node_kin
 /** Commits all operations stored in @a mtcc as a new revision and destroys
  * @a mtcc.
  *
+ * A log message is obtained from the log message callback in the client
+ * context in @a mtcc.
+ *
+ * @a revprop_table (if non-NULL) supplies additional revision properties;
+ * it may not supply any "svn:*" revision properties.
+ *
+ * As with svn_ra_get_commit_editor3(), after the commit has succeeded,
+ * it will invoke @a commit_callback (if non-NULL) with filled-in
+ * #svn_commit_info_t *, @a commit_baton, and @a scratch_pool or some subpool
+ * thereof as arguments.
+ *
  * @since New in 1.9.
  */
 svn_error_t *

Modified: subversion/branches/shelving-v3/subversion/include/private/svn_wc_private.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/include/private/svn_wc_private.h?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/include/private/svn_wc_private.h (original)
+++ subversion/branches/shelving-v3/subversion/include/private/svn_wc_private.h Tue Feb 19 17:10:11 2019
@@ -348,9 +348,9 @@ svn_wc__get_wcroot(const char **wcroot_a
                    apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool);
 
-/** Set @a *dir to the abspath of the directory in which shelved patches
- * are stored, which is inside the WC's administrative directory, and ensure
- * the directory exists.
+/** Set @a *dir to the abspath of the directory in which administrative
+ * data for experimental features may be stored. This directory is inside
+ * the WC's administrative directory. Ensure the directory exists.
  *
  * @a local_abspath is any path in the WC, and is used to find the WC root.
  *
@@ -358,11 +358,11 @@ svn_wc__get_wcroot(const char **wcroot_a
  */
 SVN_EXPERIMENTAL
 svn_error_t *
-svn_wc__get_shelves_dir(char **dir,
-                        svn_wc_context_t *wc_ctx,
-                        const char *local_abspath,
-                        apr_pool_t *result_pool,
-                        apr_pool_t *scratch_pool);
+svn_wc__get_experimental_dir(char **dir,
+                             svn_wc_context_t *wc_ctx,
+                             const char *local_abspath,
+                             apr_pool_t *result_pool,
+                             apr_pool_t *scratch_pool);
 
 /**
  * The following are temporary APIs to aid in the transition from wc-1 to
@@ -635,6 +635,24 @@ svn_wc__find_working_nodes_with_basename
                                          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
+ * are copies of REPOS_RELPATH and have node kind KIND.
+ * If no such nodes exist, return an empty array.
+ *
+ * This function returns only paths to nodes which are present in the highest
+ * layer of the WC. In other words, paths to deleted and/or excluded nodes are
+ * never returned.
+ */
+svn_error_t *
+svn_wc__find_copies_of_repos_path(apr_array_header_t **abspaths,
+                                  const char *wri_abspath,
+                                  const char *repos_relpath,
+                                  svn_node_kind_t kind,
+                                  svn_wc_context_t *wc_ctx,
+                                  apr_pool_t *result_pool,
+                                  apr_pool_t *scratch_pool);
+
 /* Get the working revision of @a local_abspath using @a wc_ctx. If @a
  * local_abspath is not in the working copy, return @c
  * SVN_ERR_WC_PATH_NOT_FOUND.

Modified: subversion/branches/shelving-v3/subversion/libsvn_client/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/libsvn_client/conflicts.c?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/libsvn_client/conflicts.c (original)
+++ subversion/branches/shelving-v3/subversion/libsvn_client/conflicts.c Tue Feb 19 17:10:11 2019
@@ -10773,6 +10773,7 @@ configure_option_incoming_move_file_merg
 {
   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;
@@ -10781,6 +10782,7 @@ configure_option_incoming_move_file_merg
   svn_node_kind_t incoming_new_kind;
 
   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,
@@ -10794,7 +10796,8 @@ configure_option_incoming_move_file_merg
   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)
+      incoming_change == svn_wc_conflict_action_delete &&
+      local_change == svn_wc_conflict_reason_edited)
     {
       struct conflict_tree_incoming_delete_details *details;
       const char *description;
@@ -11068,6 +11071,648 @@ configure_option_sibling_move_merge(svn_
   return SVN_NO_ERROR;
 }
 
+struct conflict_tree_update_local_moved_away_details {
+  /*
+   * This array consists of "const char *" absolute paths to working copy
+   * nodes which are uncomitted copies and correspond to the repository path
+   * of the conflict victim.
+   * 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 array in case if there is no move target path in
+   * the working copy. */
+  apr_array_header_t *wc_move_targets;
+
+  /* Current index into the list of working copy paths in WC_MOVE_TARGETS. */
+  int preferred_move_target_idx;
+};
+
+/* Implements conflict_option_resolve_func_t.
+ * Resolve an incoming move vs local move conflict by merging from the
+ * incoming move's target location to the local move's target location,
+ * overriding the incoming move. The original local move was broken during
+ * update/switch, so overriding the incoming move involves recording a new
+ * move from the incoming move's target location to the local move's target
+ * location. */
+static svn_error_t *
+resolve_both_moved_file_update_keep_local_move(
+  svn_client_conflict_option_t *option,
+  svn_client_conflict_t *conflict,
+  svn_client_ctx_t *ctx,
+  apr_pool_t *scratch_pool)
+{
+  svn_client_conflict_option_id_t option_id;
+  const char *victim_abspath;
+  const char *local_moved_to_abspath;
+  svn_wc_operation_t operation;
+  const char *lock_abspath;
+  svn_error_t *err;
+  const char *repos_root_url;
+  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;
+  const char *ancestor_abspath;
+  svn_stream_t *ancestor_stream;
+  apr_hash_t *ancestor_props;
+  apr_hash_t *incoming_props;
+  apr_hash_t *local_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 *incoming_details;
+  apr_array_header_t *possible_moved_to_abspaths;
+  const char *incoming_moved_to_abspath;
+  struct conflict_tree_update_local_moved_away_details *local_details;
+
+  victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+  operation = svn_client_conflict_get_operation(conflict);
+  incoming_details = conflict->tree_conflict_incoming_details;
+  if (incoming_details == NULL || incoming_details->moves == NULL)
+    return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                             _("The specified conflict resolution option "
+                               "requires details for tree conflict at '%s' "
+                               "to be fetched from the repository first."),
+                            svn_dirent_local_style(victim_abspath,
+                                                   scratch_pool));
+  if (operation == svn_wc_operation_none)
+    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+                             _("Invalid operation code '%d' recorded for "
+                               "conflict at '%s'"), operation,
+                             svn_dirent_local_style(victim_abspath,
+                                                    scratch_pool));
+
+  option_id = svn_client_conflict_option_get_id(option);
+  SVN_ERR_ASSERT(option_id == svn_client_conflict_option_both_moved_file_merge);
+                  
+  SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, NULL,
+                                             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, victim_abspath,
+                             scratch_pool, scratch_pool));
+  SVN_ERR(svn_stream_open_unique(&ancestor_stream,
+                                 &ancestor_abspath, wc_tmpdir,
+                                 svn_io_file_del_on_pool_cleanup,
+                                 scratch_pool, 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, ctx,
+                                               scratch_pool, scratch_pool));
+  SVN_ERR(svn_ra_get_file(ra_session, "", incoming_old_pegrev,
+                          ancestor_stream, NULL, /* fetched_rev */
+                          &ancestor_props, scratch_pool));
+  filter_props(ancestor_props, scratch_pool);
+
+  /* Close stream to flush ancestor file to disk. */
+  SVN_ERR(svn_stream_close(ancestor_stream));
+
+  possible_moved_to_abspaths =
+    svn_hash_gets(incoming_details->wc_move_targets,
+                  get_moved_to_repos_relpath(incoming_details, scratch_pool));
+  incoming_moved_to_abspath =
+    APR_ARRAY_IDX(possible_moved_to_abspaths,
+                  incoming_details->wc_move_target_idx, const char *);
+
+  local_details = conflict->tree_conflict_local_details;
+  local_moved_to_abspath =
+    APR_ARRAY_IDX(local_details->wc_move_targets,
+                  local_details->preferred_move_target_idx, const char *);
+
+  /* ### The following WC modifications should be atomic. */
+  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
+            &lock_abspath, ctx->wc_ctx,
+            svn_dirent_get_longest_ancestor(victim_abspath,
+                                            local_moved_to_abspath,
+                                            scratch_pool),
+            scratch_pool, scratch_pool));
+
+   /* Get a copy of the incoming moved item's properties. */
+  err = svn_wc_prop_list2(&incoming_props, ctx->wc_ctx,
+                          incoming_moved_to_abspath,
+                          scratch_pool, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  /* Get a copy of the local move target's properties. */
+  err = svn_wc_prop_list2(&local_props, ctx->wc_ctx,
+                          local_moved_to_abspath,
+                          scratch_pool, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  /* Create a property diff for the files. */
+  err = svn_prop_diffs(&propdiffs, incoming_props, local_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,
+                      incoming_moved_to_abspath, local_moved_to_abspath,
+                      NULL, NULL, NULL, /* labels */
+                      NULL, NULL, /* conflict versions */
+                      FALSE, /* dry run */
+                      NULL, NULL, /* diff3_cmd, merge_options */
+                      apr_hash_count(ancestor_props) ? ancestor_props : NULL,
+                      propdiffs,
+                      NULL, NULL, /* conflict func/baton */
+                      NULL, NULL, /* don't allow user to cancel here */
+                      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(local_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);
+    }
+
+  /* Record a new move which overrides the incoming move. */
+  err = svn_wc__move2(ctx->wc_ctx, incoming_moved_to_abspath,
+                      local_moved_to_abspath,
+                      TRUE, /* meta-data only move */
+                      FALSE, /* mixed-revisions don't apply to files */
+                      NULL, NULL, /* don't allow user to cancel here */
+                      NULL, NULL, /* no extra notification */
+                      scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  /* Remove moved-away file from disk. */
+  err = svn_io_remove_file2(incoming_moved_to_abspath, TRUE, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  err = svn_wc__del_tree_conflict(ctx->wc_ctx, victim_abspath, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  if (ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify;
+
+      notify = svn_wc_create_notify(victim_abspath, svn_wc_notify_resolved_tree,
+                                    scratch_pool);
+      ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+    }
+
+  svn_io_sleep_for_timestamps(local_moved_to_abspath, 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;
+}
+
+/* Implements conflict_option_resolve_func_t.
+ * Resolve an incoming move vs local move conflict by merging from the
+ * local move's target location to the incoming move's target location,
+ * and reverting the local move. */
+static svn_error_t *
+resolve_both_moved_file_update_keep_incoming_move(
+  svn_client_conflict_option_t *option,
+  svn_client_conflict_t *conflict,
+  svn_client_ctx_t *ctx,
+  apr_pool_t *scratch_pool)
+{
+  svn_client_conflict_option_id_t option_id;
+  const char *victim_abspath;
+  const char *local_moved_to_abspath;
+  svn_wc_operation_t operation;
+  const char *lock_abspath;
+  svn_error_t *err;
+  const char *repos_root_url;
+  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;
+  const char *ancestor_abspath;
+  svn_stream_t *ancestor_stream;
+  apr_hash_t *ancestor_props;
+  apr_hash_t *incoming_props;
+  apr_hash_t *local_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 *incoming_details;
+  apr_array_header_t *possible_moved_to_abspaths;
+  const char *incoming_moved_to_abspath;
+  struct conflict_tree_update_local_moved_away_details *local_details;
+
+  victim_abspath = svn_client_conflict_get_local_abspath(conflict);
+  operation = svn_client_conflict_get_operation(conflict);
+  incoming_details = conflict->tree_conflict_incoming_details;
+  if (incoming_details == NULL || incoming_details->moves == NULL)
+    return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                             _("The specified conflict resolution option "
+                               "requires details for tree conflict at '%s' "
+                               "to be fetched from the repository first."),
+                            svn_dirent_local_style(victim_abspath,
+                                                   scratch_pool));
+  if (operation == svn_wc_operation_none)
+    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+                             _("Invalid operation code '%d' recorded for "
+                               "conflict at '%s'"), operation,
+                             svn_dirent_local_style(victim_abspath,
+                                                    scratch_pool));
+
+  option_id = svn_client_conflict_option_get_id(option);
+  SVN_ERR_ASSERT(option_id ==
+                 svn_client_conflict_option_both_moved_file_move_merge);
+                  
+  SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, NULL,
+                                             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, victim_abspath,
+                             scratch_pool, scratch_pool));
+  SVN_ERR(svn_stream_open_unique(&ancestor_stream,
+                                 &ancestor_abspath, wc_tmpdir,
+                                 svn_io_file_del_on_pool_cleanup,
+                                 scratch_pool, 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, ctx,
+                                               scratch_pool, scratch_pool));
+  SVN_ERR(svn_ra_get_file(ra_session, "", incoming_old_pegrev,
+                          ancestor_stream, NULL, /* fetched_rev */
+                          &ancestor_props, scratch_pool));
+  filter_props(ancestor_props, scratch_pool);
+
+  /* Close stream to flush ancestor file to disk. */
+  SVN_ERR(svn_stream_close(ancestor_stream));
+
+  possible_moved_to_abspaths =
+    svn_hash_gets(incoming_details->wc_move_targets,
+                  get_moved_to_repos_relpath(incoming_details, scratch_pool));
+  incoming_moved_to_abspath =
+    APR_ARRAY_IDX(possible_moved_to_abspaths,
+                  incoming_details->wc_move_target_idx, const char *);
+
+  local_details = conflict->tree_conflict_local_details;
+  local_moved_to_abspath =
+    APR_ARRAY_IDX(local_details->wc_move_targets,
+                  local_details->preferred_move_target_idx, const char *);
+
+  /* ### The following WC modifications should be atomic. */
+  SVN_ERR(svn_wc__acquire_write_lock_for_resolve(
+            &lock_abspath, ctx->wc_ctx,
+            svn_dirent_get_longest_ancestor(victim_abspath,
+                                            local_moved_to_abspath,
+                                            scratch_pool),
+            scratch_pool, scratch_pool));
+
+   /* Get a copy of the incoming moved item's properties. */
+  err = svn_wc_prop_list2(&incoming_props, ctx->wc_ctx,
+                          incoming_moved_to_abspath,
+                          scratch_pool, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  /* Get a copy of the local move target's properties. */
+  err = svn_wc_prop_list2(&local_props, ctx->wc_ctx,
+                          local_moved_to_abspath,
+                          scratch_pool, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  /* Create a property diff for the files. */
+  err = svn_prop_diffs(&propdiffs, incoming_props, local_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_moved_to_abspath, incoming_moved_to_abspath,
+                      NULL, NULL, NULL, /* labels */
+                      NULL, NULL, /* conflict versions */
+                      FALSE, /* dry run */
+                      NULL, NULL, /* diff3_cmd, merge_options */
+                      apr_hash_count(ancestor_props) ? ancestor_props : NULL,
+                      propdiffs,
+                      NULL, NULL, /* conflict func/baton */
+                      NULL, NULL, /* don't allow user to cancel here */
+                      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(local_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);
+    }
+
+  /* Revert the copy-half of the local move. The delete-half of this move
+   * has already been deleted during the update/switch operation. */
+  err = svn_wc_revert6(ctx->wc_ctx, local_moved_to_abspath, svn_depth_empty,
+                       FALSE, NULL, TRUE, FALSE,
+                       TRUE /*added_keep_local*/,
+                       NULL, NULL, /* no cancellation */
+                       ctx->notify_func2, ctx->notify_baton2,
+                       scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  err = svn_wc__del_tree_conflict(ctx->wc_ctx, victim_abspath, scratch_pool);
+  if (err)
+    goto unlock_wc;
+
+  if (ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify;
+
+      notify = svn_wc_create_notify(victim_abspath, svn_wc_notify_resolved_tree,
+                                    scratch_pool);
+      ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+    }
+
+  svn_io_sleep_for_timestamps(local_moved_to_abspath, 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;
+}
+
+/* Implements tree_conflict_get_details_func_t. */
+static svn_error_t *
+conflict_tree_get_details_update_local_moved_away(
+  svn_client_conflict_t *conflict,
+  svn_client_ctx_t *ctx,
+  apr_pool_t *scratch_pool)
+{
+  struct conflict_tree_update_local_moved_away_details *details;
+  const char *incoming_old_repos_relpath;
+  svn_node_kind_t incoming_old_kind;
+
+  SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+            &incoming_old_repos_relpath, NULL, &incoming_old_kind,
+            conflict, scratch_pool, scratch_pool));
+
+  details = apr_pcalloc(conflict->pool, sizeof(*details));
+
+  details->wc_move_targets = apr_array_make(conflict->pool, 1,
+                                            sizeof(const char *));
+  
+  /* Search the WC for copies of the conflict victim. */
+  SVN_ERR(svn_wc__find_copies_of_repos_path(&details->wc_move_targets,
+                                            conflict->local_abspath,
+                                            incoming_old_repos_relpath,
+                                            incoming_old_kind,
+                                            ctx->wc_ctx,
+                                            conflict->pool,
+                                            scratch_pool));
+
+  conflict->tree_conflict_local_details = details;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+get_both_moved_file_paths(const char **incoming_moved_to_abspath,
+                          const char **local_moved_to_abspath,
+                          svn_client_conflict_t *conflict,
+                          apr_pool_t *scratch_pool)
+{
+  struct conflict_tree_incoming_delete_details *incoming_details;
+  apr_array_header_t *incoming_move_target_wc_abspaths;
+  svn_wc_operation_t operation;
+
+  operation = svn_client_conflict_get_operation(conflict);
+
+  *incoming_moved_to_abspath = NULL;
+  *local_moved_to_abspath = NULL;
+
+  incoming_details = conflict->tree_conflict_incoming_details;
+  if (incoming_details == NULL || incoming_details->moves == NULL ||
+      apr_hash_count(incoming_details->wc_move_targets) == 0)
+          return SVN_NO_ERROR;
+
+  incoming_move_target_wc_abspaths =
+    svn_hash_gets(incoming_details->wc_move_targets,
+                  get_moved_to_repos_relpath(incoming_details,
+                                             scratch_pool));
+  *incoming_moved_to_abspath =
+    APR_ARRAY_IDX(incoming_move_target_wc_abspaths,
+                  incoming_details->wc_move_target_idx, const char *);
+
+  if (operation == svn_wc_operation_merge)
+    {
+      struct conflict_tree_local_missing_details *local_details;
+      apr_array_header_t *local_moves;
+
+      local_details = conflict->tree_conflict_local_details;
+      if (local_details == NULL ||
+          apr_hash_count(local_details->wc_move_targets) == 0)
+          return SVN_NO_ERROR;
+
+      local_moves = svn_hash_gets(local_details->wc_move_targets,
+                                  local_details->move_target_repos_relpath);
+      *local_moved_to_abspath =
+        APR_ARRAY_IDX(local_moves, local_details->wc_move_target_idx,
+                      const char *);
+    }
+  else
+    {
+      struct conflict_tree_update_local_moved_away_details *local_details;
+
+      local_details = conflict->tree_conflict_local_details;
+      if (local_details == NULL ||
+          local_details->wc_move_targets->nelts == 0)
+          return SVN_NO_ERROR;
+
+      *local_moved_to_abspath =
+        APR_ARRAY_IDX(local_details->wc_move_targets,
+                      local_details->preferred_move_target_idx,
+                      const char *);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+conflict_tree_get_description_update_both_moved_file_merge(
+  const char **description,
+  svn_client_conflict_t *conflict,
+  svn_client_ctx_t *ctx,
+  apr_pool_t *result_pool,
+  apr_pool_t *scratch_pool)
+{
+  const char *incoming_moved_to_abspath;
+  const char *local_moved_to_abspath;
+  svn_wc_operation_t operation;
+  const char *wcroot_abspath;
+
+  *description = NULL;
+
+  SVN_ERR(get_both_moved_file_paths(&incoming_moved_to_abspath,
+                                    &local_moved_to_abspath,
+                                    conflict, scratch_pool));
+  if (incoming_moved_to_abspath == NULL || local_moved_to_abspath == NULL)
+    return SVN_NO_ERROR;
+
+  SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx,
+                             conflict->local_abspath, scratch_pool,
+                             scratch_pool));
+
+  operation = svn_client_conflict_get_operation(conflict);
+
+  if (operation == svn_wc_operation_merge)
+    {
+      /* In case of a merge, the incoming move has A+ (copied) status... */
+      *description =
+        apr_psprintf(
+          scratch_pool,
+            _("apply changes to '%s' and revert addition of '%s'"),
+          svn_dirent_local_style(
+            svn_dirent_skip_ancestor(wcroot_abspath, local_moved_to_abspath),
+            scratch_pool),
+          svn_dirent_local_style(
+            svn_dirent_skip_ancestor(wcroot_abspath, incoming_moved_to_abspath),
+            scratch_pool));
+    }
+  else
+    {
+      /* ...but in case of update/switch the local move has "A+" status. */
+      *description =
+        apr_psprintf(
+          scratch_pool,
+          _("override incoming move and merge incoming changes from '%s' "
+            "to '%s'"),
+          svn_dirent_local_style(
+            svn_dirent_skip_ancestor(wcroot_abspath, incoming_moved_to_abspath),
+            scratch_pool),
+          svn_dirent_local_style(
+            svn_dirent_skip_ancestor(wcroot_abspath, local_moved_to_abspath),
+            scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+conflict_tree_get_description_update_both_moved_file_move_merge(
+  const char **description,
+  svn_client_conflict_t *conflict,
+  svn_client_ctx_t *ctx,
+  apr_pool_t *result_pool,
+  apr_pool_t *scratch_pool)
+{
+  const char *incoming_moved_to_abspath;
+  const char *local_moved_to_abspath;
+  svn_wc_operation_t operation;
+  const char *wcroot_abspath;
+
+  *description = NULL;
+
+  SVN_ERR(get_both_moved_file_paths(&incoming_moved_to_abspath,
+                                    &local_moved_to_abspath,
+                                    conflict, scratch_pool));
+  if (incoming_moved_to_abspath == NULL || local_moved_to_abspath == NULL)
+    return SVN_NO_ERROR;
+
+  SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx,
+                             conflict->local_abspath, scratch_pool,
+                             scratch_pool));
+
+  operation = svn_client_conflict_get_operation(conflict);
+
+  if (operation == svn_wc_operation_merge)
+    {
+      SVN_ERR(describe_incoming_move_merge_conflict_option(
+                description, conflict, ctx, local_moved_to_abspath,
+                scratch_pool, scratch_pool));
+    }
+  else
+    {
+      *description =
+        apr_psprintf(
+          scratch_pool,
+          _("accept incoming move and merge local changes from "
+            "'%s' to '%s'"),
+          svn_dirent_local_style(
+            svn_dirent_skip_ancestor(wcroot_abspath, local_moved_to_abspath),
+            scratch_pool),
+          svn_dirent_local_style(
+            svn_dirent_skip_ancestor(wcroot_abspath, incoming_moved_to_abspath),
+            scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Configure 'both moved file merge' resolution options for a tree conflict. */
 static svn_error_t *
 configure_option_both_moved_file_merge(svn_client_conflict_t *conflict,
@@ -11104,66 +11749,44 @@ configure_option_both_moved_file_merge(s
             &incoming_new_kind, conflict, scratch_pool,
             scratch_pool));
 
-  if (operation == svn_wc_operation_merge &&
-      victim_node_kind == svn_node_none &&
+  /* ### what about the switch operation? */
+  if (((operation == svn_wc_operation_merge &&
+        victim_node_kind == svn_node_none) ||
+       (operation == svn_wc_operation_update &&
+         victim_node_kind == svn_node_file)) &&
       incoming_old_kind == svn_node_file &&
       incoming_new_kind == svn_node_none &&
-      local_change == svn_wc_conflict_reason_missing &&
+      ((operation == svn_wc_operation_merge &&
+       local_change == svn_wc_conflict_reason_missing) ||
+       (operation == svn_wc_operation_update &&
+       local_change == svn_wc_conflict_reason_moved_away)) &&
       incoming_change == svn_wc_conflict_action_delete)
     {
-      struct conflict_tree_incoming_delete_details *incoming_details;
-      struct conflict_tree_local_missing_details *local_details;
       const char *description;
-      apr_array_header_t *local_moves;
-      const char *local_moved_to_abspath;
-      const char *incoming_moved_to_abspath;
-      apr_array_header_t *incoming_move_target_wc_abspaths;
 
-      incoming_details = conflict->tree_conflict_incoming_details;
-      if (incoming_details == NULL || incoming_details->moves == NULL ||
-          apr_hash_count(incoming_details->wc_move_targets) == 0)
-              return SVN_NO_ERROR;
+      SVN_ERR(conflict_tree_get_description_update_both_moved_file_merge(
+                &description, conflict, ctx, conflict->pool, scratch_pool));
 
-      local_details = conflict->tree_conflict_local_details;
-      if (local_details == NULL ||
-          apr_hash_count(local_details->wc_move_targets) == 0)
-          return SVN_NO_ERROR;
-
-      local_moves = svn_hash_gets(local_details->wc_move_targets,
-                                  local_details->move_target_repos_relpath);
-      local_moved_to_abspath =
-        APR_ARRAY_IDX(local_moves, local_details->wc_move_target_idx,
-                      const char *);
-
-      incoming_move_target_wc_abspaths =
-        svn_hash_gets(incoming_details->wc_move_targets,
-                      get_moved_to_repos_relpath(incoming_details,
-                                                 scratch_pool));
-      incoming_moved_to_abspath =
-        APR_ARRAY_IDX(incoming_move_target_wc_abspaths,
-                      incoming_details->wc_move_target_idx, const char *);
+      if (description == NULL) /* details not fetched yet */
+        return SVN_NO_ERROR;
 
-      description =
-        apr_psprintf(
-          scratch_pool, _("apply changes to '%s' and revert addition of '%s'"),
-          svn_dirent_local_style(
-            svn_dirent_skip_ancestor(wcroot_abspath, local_moved_to_abspath),
-            scratch_pool),
-          svn_dirent_local_style(
-            svn_dirent_skip_ancestor(wcroot_abspath, incoming_moved_to_abspath),
-            scratch_pool));
       add_resolution_option(
         options, conflict, svn_client_conflict_option_both_moved_file_merge,
         _("Merge to corresponding local location"),
-        description, resolve_both_moved_file_text_merge);
+        description,
+        operation == svn_wc_operation_merge ?
+          resolve_both_moved_file_text_merge :
+          resolve_both_moved_file_update_keep_local_move);
+
+      SVN_ERR(conflict_tree_get_description_update_both_moved_file_move_merge(
+                &description, conflict, ctx, conflict->pool, scratch_pool));
 
-      SVN_ERR(describe_incoming_move_merge_conflict_option(
-                &description, conflict, ctx, local_moved_to_abspath,
-                scratch_pool, scratch_pool));
       add_resolution_option(options, conflict,
          svn_client_conflict_option_both_moved_file_move_merge,
         _("Move and merge"), description,
-        resolve_incoming_move_file_text_merge);
+        operation == svn_wc_operation_merge ?
+          resolve_incoming_move_file_text_merge :
+          resolve_both_moved_file_update_keep_incoming_move);
     }
 
   return SVN_NO_ERROR;
@@ -11648,6 +12271,36 @@ svn_client_conflict_option_get_moved_to_
             }
         }
     }
+  else if ((operation == svn_wc_operation_update ||
+            operation == svn_wc_operation_switch) &&
+           incoming_change == svn_wc_conflict_action_delete &&
+           local_change == svn_wc_conflict_reason_moved_away)
+    {
+      struct conflict_tree_update_local_moved_away_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));
+
+      /* Return a copy of the option's move target candidate list. */
+      *possible_moved_to_abspaths =
+         apr_array_make(result_pool, details->wc_move_targets->nelts,
+                        sizeof (const char *));
+      for (i = 0; i < details->wc_move_targets->nelts; i++)
+        {
+          const char *moved_to_abspath;
+
+          moved_to_abspath = APR_ARRAY_IDX(details->wc_move_targets, i,
+                                           const char *);
+          APR_ARRAY_PUSH(*possible_moved_to_abspaths, const char *) =
+             apr_pstrdup(result_pool, moved_to_abspath);
+        }
+    }
   else
     {
       struct conflict_tree_incoming_delete_details *details;
@@ -11805,6 +12458,54 @@ svn_client_conflict_option_set_moved_to_
                     conflict->pool, scratch_pool));
        }
     }
+  else if ((operation == svn_wc_operation_update ||
+            operation == svn_wc_operation_switch) &&
+           incoming_change == svn_wc_conflict_action_delete &&
+           local_change == svn_wc_conflict_reason_moved_away)
+    {
+      struct conflict_tree_update_local_moved_away_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));
+
+      if (preferred_move_target_idx < 0 ||
+          preferred_move_target_idx > details->wc_move_targets->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->preferred_move_target_idx = preferred_move_target_idx;
+
+      /* Update option description. */
+      if (id == svn_client_conflict_option_both_moved_file_merge)
+        SVN_ERR(conflict_tree_get_description_update_both_moved_file_merge(
+                  &option->description, conflict, ctx, conflict->pool,
+                  scratch_pool));
+      else if (id == svn_client_conflict_option_both_moved_file_move_merge)
+        SVN_ERR(conflict_tree_get_description_update_both_moved_file_move_merge(
+           &option->description, conflict, ctx, conflict->pool, scratch_pool));
+#if 0  /* ### TODO: Also handle options for directories! */
+      else if (id == svn_client_conflict_option_both_moved_dir_merge)
+        {
+        }
+      else if (id == svn_client_conflict_option_both_moved_dir_move_merge)
+        {
+        }
+#endif
+      else
+        return svn_error_createf(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+                                 _("Unexpected option id '%d'"), id);
+    }
   else
     {
       struct conflict_tree_incoming_delete_details *details;
@@ -12471,6 +13172,7 @@ conflict_type_specific_setup(svn_client_
                              apr_pool_t *scratch_pool)
 {
   svn_boolean_t tree_conflicted;
+  svn_wc_operation_t operation;
   svn_wc_conflict_action_t incoming_change;
   svn_wc_conflict_reason_t local_change;
 
@@ -12487,6 +13189,7 @@ conflict_type_specific_setup(svn_client_
   conflict->tree_conflict_get_local_description_func =
     conflict_tree_get_local_description_generic;
 
+  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);
 
@@ -12521,6 +13224,12 @@ conflict_type_specific_setup(svn_client_
       conflict->tree_conflict_get_local_details_func =
         conflict_tree_get_details_local_missing;
     }
+  else if (local_change == svn_wc_conflict_reason_moved_away &&
+           operation == svn_wc_operation_update /* ### what about switch? */)
+    {
+      conflict->tree_conflict_get_local_details_func =
+        conflict_tree_get_details_update_local_moved_away;
+    }
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/shelving-v3/subversion/libsvn_client/shelf.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/libsvn_client/shelf.c?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/libsvn_client/shelf.c (original)
+++ subversion/branches/shelving-v3/subversion/libsvn_client/shelf.c Tue Feb 19 17:10:11 2019
@@ -114,6 +114,28 @@ shelf_name_from_filename(char **name,
   return SVN_NO_ERROR;
 }
 
+/* Set *DIR to the shelf storage directory inside the WC's administrative
+ * area. Ensure the directory exists. */
+static svn_error_t *
+get_shelves_dir(char **dir,
+                svn_wc_context_t *wc_ctx,
+                const char *local_abspath,
+                apr_pool_t *result_pool,
+                apr_pool_t *scratch_pool)
+{
+  char *experimental_abspath;
+
+  SVN_ERR(svn_wc__get_experimental_dir(&experimental_abspath,
+                                       wc_ctx, local_abspath,
+                                       scratch_pool, scratch_pool));
+  *dir = svn_dirent_join(experimental_abspath, "shelves/v3", result_pool);
+
+  /* Ensure the directory exists. (Other versions of svn don't create it.) */
+  SVN_ERR(svn_io_make_dir_recursively(*dir, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* Set *ABSPATH to the abspath of the file storage dir for SHELF
  * version VERSION, no matter whether it exists.
  */
@@ -522,33 +544,6 @@ svn_client__shelf_version_status_walk(sv
 /*-------------------------------------------------------------------------*/
 /* Shelf Storage */
 
-/* Get the WC root and corresponding shelves dir.
- *
- * ### Presently, this returns a shelves dir OUTSIDE the WC; this is a
- * (temporary) convenience for shelf_copy_base() which would need to be
- * smarter if it were inside '.svn'.
- */
-static svn_error_t *
-get_shelves_dir(const char **wc_root_abspath_p,
-                char **shelves_dir_abspath_p,
-                const char *local_abspath,
-                svn_client_ctx_t *ctx,
-                apr_pool_t *result_pool)
-{
-  SVN_ERR(svn_client_get_wc_root(wc_root_abspath_p,
-                                 local_abspath, ctx,
-                                 result_pool, result_pool));
-  /*SVN_ERR(svn_wc__get_shelves_dir(shelves_dir_abspath_p,
-                                  ctx->wc_ctx, local_abspath,
-                                  result_pool, result_pool));*/
-  *shelves_dir_abspath_p = apr_pstrcat(result_pool,
-                                       *wc_root_abspath_p, ".shelves",
-                                       SVN_VA_NULL);
-  /* Ensure the directory exists. */
-  SVN_ERR(svn_io_make_dir_recursively(*shelves_dir_abspath_p, result_pool));
-  return SVN_NO_ERROR;
-}
-
 /* Construct a shelf object representing an empty shelf: no versions,
  * no revprops, no looking to see if such a shelf exists on disk.
  */
@@ -562,8 +557,11 @@ shelf_construct(svn_client__shelf_t **sh
   svn_client__shelf_t *shelf = apr_palloc(result_pool, sizeof(*shelf));
   char *shelves_dir;
 
-  SVN_ERR(get_shelves_dir(&shelf->wc_root_abspath, &shelves_dir,
-                          local_abspath, ctx, result_pool));
+  SVN_ERR(svn_client_get_wc_root(&shelf->wc_root_abspath,
+                                 local_abspath, ctx,
+                                 result_pool, result_pool));
+  SVN_ERR(get_shelves_dir(&shelves_dir, ctx->wc_ctx, local_abspath,
+                          result_pool, result_pool));
   shelf->shelves_dir = shelves_dir;
   shelf->ctx = ctx;
   shelf->pool = result_pool;
@@ -1184,8 +1182,10 @@ svn_client__shelf_list(apr_hash_t **shel
   apr_hash_t *dirents;
   apr_hash_index_t *hi;
 
-  SVN_ERR(get_shelves_dir(&wc_root_abspath, &shelves_dir,
-                          local_abspath, ctx, scratch_pool));
+  SVN_ERR(svn_wc__get_wcroot(&wc_root_abspath, ctx->wc_ctx, local_abspath,
+                             scratch_pool, scratch_pool));
+  SVN_ERR(get_shelves_dir(&shelves_dir, ctx->wc_ctx, local_abspath,
+                          scratch_pool, scratch_pool));
   SVN_ERR(svn_io_get_dirents3(&dirents, shelves_dir, FALSE /*only_check_type*/,
                               result_pool, scratch_pool));
 

Modified: subversion/branches/shelving-v3/subversion/libsvn_repos/dump.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/libsvn_repos/dump.c?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/libsvn_repos/dump.c (original)
+++ subversion/branches/shelving-v3/subversion/libsvn_repos/dump.c Tue Feb 19 17:10:11 2019
@@ -44,6 +44,7 @@
 #include "private/svn_sorts_private.h"
 #include "private/svn_utf_private.h"
 #include "private/svn_cache.h"
+#include "private/svn_fspath.h"
 
 #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
 
@@ -1996,6 +1997,11 @@ dump_filter_authz_func(svn_boolean_t *al
 {
   dump_filter_baton_t *b = baton;
 
+  /* For some nodes (e.g. files under copied directory) PATH may be
+   * non-canonical (missing leading '/').  Canonicalize PATH before
+   * passing it to FILTER_FUNC. */
+  path = svn_fspath__canonicalize(path, pool);
+
   return svn_error_trace(b->filter_func(allowed, root, path, b->filter_baton,
                                         pool));
 }

Modified: subversion/branches/shelving-v3/subversion/libsvn_subr/error.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/libsvn_subr/error.c?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/libsvn_subr/error.c (original)
+++ subversion/branches/shelving-v3/subversion/libsvn_subr/error.c Tue Feb 19 17:10:11 2019
@@ -146,6 +146,7 @@ svn_error__locate(const char *file, long
 
 /* Cleanup function for errors.  svn_error_clear () removes this so
    errors that are properly handled *don't* hit this code. */
+#ifdef SVN_DEBUG
 static apr_status_t err_abort(void *data)
 {
   svn_error_t *err = data;  /* For easy viewing in a debugger */
@@ -155,6 +156,7 @@ static apr_status_t err_abort(void *data
     abort();
   return APR_SUCCESS;
 }
+#endif
 
 
 static svn_error_t *

Modified: subversion/branches/shelving-v3/subversion/libsvn_wc/node.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/libsvn_wc/node.c?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/libsvn_wc/node.c (original)
+++ subversion/branches/shelving-v3/subversion/libsvn_wc/node.c Tue Feb 19 17:10:11 2019
@@ -1140,3 +1140,17 @@ svn_wc__find_working_nodes_with_basename
                            abspaths, wc_ctx->db, wri_abspath, basename, kind,
                            result_pool, scratch_pool));
 }
+
+svn_error_t *
+svn_wc__find_copies_of_repos_path(apr_array_header_t **abspaths,
+                                  const char *wri_abspath,
+                                  const char *repos_relpath,
+                                  svn_node_kind_t kind,
+                                  svn_wc_context_t *wc_ctx,
+                                  apr_pool_t *result_pool,
+                                  apr_pool_t *scratch_pool)
+{
+  return svn_error_trace(svn_wc__db_find_copies_of_repos_path(
+                           abspaths, wc_ctx->db, wri_abspath, repos_relpath,
+                           kind, result_pool, scratch_pool));
+}

Modified: subversion/branches/shelving-v3/subversion/libsvn_wc/wc-queries.sql
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/libsvn_wc/wc-queries.sql?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/branches/shelving-v3/subversion/libsvn_wc/wc-queries.sql Tue Feb 19 17:10:11 2019
@@ -1796,6 +1796,17 @@ WHERE wc_id = ?1
 SELECT 1 FROM sqlite_master WHERE name='sqlite_stat1' AND type='table'
 LIMIT 1
 
+-- STMT_SELECT_COPIES_OF_REPOS_RELPATH
+SELECT local_relpath
+FROM nodes n
+WHERE wc_id = ?1 AND repos_path = ?2 AND kind = ?3
+  AND presence = MAP_NORMAL
+  AND op_depth = (SELECT MAX(op_depth)
+                  FROM NODES w
+                  WHERE w.wc_id = ?1
+                    AND w.local_relpath = n.local_relpath)
+ORDER BY local_relpath ASC
+
 /* ------------------------------------------------------------------------- */
 
 /* Grab all the statements related to the schema.  */

Modified: subversion/branches/shelving-v3/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/libsvn_wc/wc_db.c?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/shelving-v3/subversion/libsvn_wc/wc_db.c Tue Feb 19 17:10:11 2019
@@ -16663,3 +16663,47 @@ svn_wc__db_find_working_nodes_with_basen
 
   return svn_error_trace(svn_sqlite__reset(stmt));
 }
+
+svn_error_t *
+svn_wc__db_find_copies_of_repos_path(apr_array_header_t **local_abspaths,
+                                     svn_wc__db_t *db,
+                                     const char *wri_abspath,
+                                     const char *repos_relpath,
+                                     svn_node_kind_t kind,
+                                     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_SELECT_COPIES_OF_REPOS_RELPATH));
+  SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, repos_relpath,
+                            kind_map, kind));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  *local_abspaths = apr_array_make(result_pool, 1, 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_abspaths, const char *) = local_abspath;
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+
+  return svn_error_trace(svn_sqlite__reset(stmt));
+}

Modified: subversion/branches/shelving-v3/subversion/libsvn_wc/wc_db.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/libsvn_wc/wc_db.h?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/branches/shelving-v3/subversion/libsvn_wc/wc_db.h Tue Feb 19 17:10:11 2019
@@ -3520,6 +3520,24 @@ svn_wc__db_find_working_nodes_with_basen
                                             svn_node_kind_t kind,
                                             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
+ * are copies of REPOS_RELPATH and have node kind KIND.
+ * If no such nodes exist, return an empty array.
+ *
+ * This function returns only paths to nodes which are present in the highest
+ * layer of the WC. In other words, paths to deleted and/or excluded nodes are
+ * never returned.
+ */
+svn_error_t *
+svn_wc__db_find_copies_of_repos_path(apr_array_header_t **local_abspaths,
+                                     svn_wc__db_t *db,
+                                     const char *wri_abspath,
+                                     const char *repos_relpath,
+                                     svn_node_kind_t kind,
+                                     apr_pool_t *result_pool,
+                                     apr_pool_t *scratch_pool);
 /* @} */
 
 typedef svn_error_t * (*svn_wc__db_verify_cb_t)(void *baton,

Modified: subversion/branches/shelving-v3/subversion/libsvn_wc/wcroot_anchor.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/libsvn_wc/wcroot_anchor.c?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/libsvn_wc/wcroot_anchor.c (original)
+++ subversion/branches/shelving-v3/subversion/libsvn_wc/wcroot_anchor.c Tue Feb 19 17:10:11 2019
@@ -183,21 +183,20 @@ svn_wc__get_wcroot(const char **wcroot_a
 
 
 svn_error_t *
-svn_wc__get_shelves_dir(char **dir,
-                        svn_wc_context_t *wc_ctx,
-                        const char *local_abspath,
-                        apr_pool_t *result_pool,
-                        apr_pool_t *scratch_pool)
+svn_wc__get_experimental_dir(char **dir,
+                             svn_wc_context_t *wc_ctx,
+                             const char *local_abspath,
+                             apr_pool_t *result_pool,
+                             apr_pool_t *scratch_pool)
 {
   const char *wcroot_abspath;
 
   SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, wc_ctx, local_abspath,
                              scratch_pool, scratch_pool));
   *dir = svn_dirent_join(wcroot_abspath,
-                         SVN_WC_ADM_DIR_NAME "/" SVN_WC__ADM_EXPERIMENTAL "/"
-                           "shelves/v2",
+                         SVN_WC_ADM_DIR_NAME "/" SVN_WC__ADM_EXPERIMENTAL,
                          result_pool);
-  
+
   /* Ensure the directory exists. (Other versions of svn don't create it.) */
   SVN_ERR(svn_io_make_dir_recursively(*dir, scratch_pool));
 

Modified: subversion/branches/shelving-v3/subversion/tests/cmdline/svnadmin_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/tests/cmdline/svnadmin_tests.py?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/tests/cmdline/svnadmin_tests.py (original)
+++ subversion/branches/shelving-v3/subversion/tests/cmdline/svnadmin_tests.py Tue Feb 19 17:10:11 2019
@@ -3920,6 +3920,51 @@ def recover_prunes_rep_cache_when_disabl
 
   check_recover_prunes_rep_cache(sbox, enable_rep_sharing=False)
 
+@Issue(4760)
+def dump_include_copied_directory(sbox):
+  "include copied directory with nested nodes"
+
+  sbox.build(create_wc=False)
+
+  svntest.actions.run_and_verify_svn(svntest.verify.AnyOutput, [], "copy",
+                                     sbox.repo_url + '/A/D',
+                                     sbox.repo_url + '/COPY',
+                                     "-m", "Create branch.")
+
+  # Dump repository with only /COPY path included.
+  _, dump, _ = svntest.actions.run_and_verify_svnadmin(None, [],
+                                                       'dump', '-q',
+                                                       '--include', '/COPY',
+                                                       sbox.repo_dir)
+
+  # Load repository from dump.
+  sbox2 = sbox.clone_dependent()
+  sbox2.build(create_wc=False, empty=True)
+  load_and_verify_dumpstream(sbox2, None, [], None, False, dump)
+
+  # Check log.
+  expected_output = svntest.verify.RegexListOutput([
+    '-+\\n',
+    'r2\ .*\n',
+    # Only '/COPY' is added
+    re.escape('Changed paths:\n'),
+    re.escape('   A /COPY'),
+    re.escape('   A /COPY/G'),
+    re.escape('   A /COPY/G/pi'),
+    re.escape('   A /COPY/G/rho'),
+    re.escape('   A /COPY/G/tau'),
+    re.escape('   A /COPY/H'),
+    re.escape('   A /COPY/H/chi'),
+    re.escape('   A /COPY/H/omega'),
+    re.escape('   A /COPY/H/psi'),
+    re.escape('   A /COPY/gamma'),
+    '-+\\n',
+    'r1\ .*\n',
+    '-+\\n'
+  ])
+  svntest.actions.run_and_verify_svn(expected_output, [],
+                                     'log', '-v', '-q', sbox2.repo_url)
+
 ########################################################################
 # Run the tests
 
@@ -3997,6 +4042,7 @@ test_list = [ None,
               dump_no_canonicalize_svndate,
               recover_prunes_rep_cache_when_enabled,
               recover_prunes_rep_cache_when_disabled,
+              dump_include_copied_directory,
              ]
 
 if __name__ == '__main__':

Modified: subversion/branches/shelving-v3/subversion/tests/cmdline/svntest/sandbox.py
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/tests/cmdline/svntest/sandbox.py?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/tests/cmdline/svntest/sandbox.py (original)
+++ subversion/branches/shelving-v3/subversion/tests/cmdline/svntest/sandbox.py Tue Feb 19 17:10:11 2019
@@ -162,8 +162,6 @@ class Sandbox:
     self._ensure_authz()
     svntest.actions.make_repo_and_wc(self, create_wc, read_only, empty,
                                      minor_version, tree)
-    if create_wc:
-      self.add_test_path(self.wc_dir + '.shelves')
     self._is_built = True
 
   def _ensure_authz(self):

Modified: subversion/branches/shelving-v3/subversion/tests/libsvn_client/conflicts-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/tests/libsvn_client/conflicts-test.c?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/tests/libsvn_client/conflicts-test.c (original)
+++ subversion/branches/shelving-v3/subversion/tests/libsvn_client/conflicts-test.c Tue Feb 19 17:10:11 2019
@@ -7197,6 +7197,268 @@ test_merge_dir_move_vs_dir_move_accept_m
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+create_file_move_vs_file_move_update_conflict(svn_client_conflict_t **conflict,
+                                              svn_test__sandbox_t *b,
+                                              svn_client_ctx_t *ctx)
+{
+  apr_array_header_t *options;
+  svn_client_conflict_option_t *option;
+  apr_array_header_t *possible_moved_to_abspaths;
+
+  /* Move a file. */
+  SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-moved"));
+
+  /* Edit moved file. */
+  SVN_ERR(sbox_file_write(b, "A/mu-moved", modified_file_content));
+  SVN_ERR(sbox_wc_commit(b, ""));
+
+  /* Update back to r1, */
+  SVN_ERR(sbox_wc_update(b, "", 1)); /* r2 */
+
+  /* Copy the file to test handling of ambiguous moves. */
+  SVN_ERR(sbox_wc_copy(b, "A/mu", "A/mu-copied"));
+
+  /* Move the same file to a different location. */
+  SVN_ERR(sbox_wc_move(b, "A/mu", "A/mu-also-moved"));
+
+  /* Edit moved file. */
+  SVN_ERR(sbox_file_write(b, "A/mu-also-moved",
+                          modified_file_in_working_copy_content));
+
+  /* Update to r2. */
+  /* This should raise an "incoming delete vs local delete" tree conflict. */
+  SVN_ERR(sbox_wc_update(b, "", SVN_INVALID_REVNUM));
+
+  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,
+      svn_client_conflict_option_incoming_delete_ignore,
+      svn_client_conflict_option_incoming_delete_accept,
+      -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_both_moved_file_merge,
+      svn_client_conflict_option_both_moved_file_move_merge,
+      -1 /* end of list */
+    };
+    SVN_ERR(assert_tree_conflict_options(*conflict, ctx, expected_opts,
+                                         b->pool));
+  }
+
+  /* Check possible move destinations for the file. */
+  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_both_moved_file_merge);
+  SVN_TEST_ASSERT(option != NULL);
+
+  SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+            &possible_moved_to_abspaths, option, b->pool, b->pool));
+
+  /* The resolver finds two possible destinations for the moved file:
+   *
+   *   Possible working copy destinations for moved-away 'A/mu' are:
+   *    (1): 'A/mu-also-moved'
+   *    (2): 'A/mu-copied'
+   *   Only one destination can be a move; the others are copies.
+   */
+  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-also-moved"));
+  SVN_TEST_STRING_ASSERT(
+    APR_ARRAY_IDX(possible_moved_to_abspaths, 1, const char *),
+    sbox_wc_path(b, "A/mu-copied"));
+
+  return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
+test_update_file_move_vs_file_move(const svn_test_opts_t *opts,
+                                   apr_pool_t *pool)
+{
+  svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+  svn_client_ctx_t *ctx;
+  svn_client_conflict_t *conflict;
+  svn_opt_revision_t opt_rev;
+  struct status_baton sb;
+  struct svn_client_status_t *status;
+  svn_stringbuf_t *buf;
+  svn_node_kind_t kind;
+  char *conflicted_content;
+
+  SVN_ERR(svn_test__sandbox_create(b, "update_file_move_vs_file_move",
+                                   opts, pool));
+
+  SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
+
+  SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+  SVN_ERR(create_file_move_vs_file_move_update_conflict(&conflict, b, ctx));
+
+  SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+            conflict,
+            svn_client_conflict_option_both_moved_file_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);
+
+  /* The node "A/mu-moved" should now have moved to "A/mu-also-moved. */
+  SVN_ERR(svn_io_check_path(sbox_wc_path(b, "A/mu"), &kind, b->pool));
+  SVN_TEST_ASSERT(kind == svn_node_none);
+  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_deleted);
+  SVN_TEST_ASSERT(status->text_status == svn_wc_status_normal);
+  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_STRING_ASSERT(status->moved_to_abspath,
+                         sbox_wc_path(b, "A/mu-also-moved"));
+
+  /* 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-also-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_conflicted);
+  SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted);
+  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_STRING_ASSERT(status->moved_from_abspath,
+                         sbox_wc_path(b, "A/mu-moved"));
+  SVN_TEST_ASSERT(status->moved_to_abspath == NULL);
+
+  /* Ensure that the moved+merged file has the expected content. */
+  SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/mu-also-moved"),
+                                   b->pool));
+  conflicted_content = apr_psprintf(b->pool,
+												 "<<<<<<< .working\n"
+												"%s"
+												"||||||| .old\n"
+												"This is the file 'mu'.\n"
+												"=======\n"
+												"%s"
+												">>>>>>> .new\n",
+									      modified_file_in_working_copy_content,
+												modified_file_content);
+  SVN_TEST_STRING_ASSERT(buf->data, conflicted_content);
+
+  return SVN_NO_ERROR;
+}
+
+/* Same test case as above, but accept the incoming move. */
+static svn_error_t *
+test_update_file_move_vs_file_move_accept_move(const svn_test_opts_t *opts,
+                                               apr_pool_t *pool)
+{
+  svn_test__sandbox_t *b = apr_palloc(pool, sizeof(*b));
+  svn_client_ctx_t *ctx;
+  svn_client_conflict_t *conflict;
+  svn_opt_revision_t opt_rev;
+  struct status_baton sb;
+  struct svn_client_status_t *status;
+  svn_stringbuf_t *buf;
+  char *conflicted_content;
+
+  SVN_ERR(svn_test__sandbox_create(b,
+                                   "update_file_move_vs_file_move_accept_move",
+                                   opts, pool));
+
+  SVN_ERR(sbox_add_and_commit_greek_tree(b)); /* r1 */
+
+  SVN_ERR(svn_test__create_client_ctx(&ctx, b, b->pool));
+  SVN_ERR(create_file_move_vs_file_move_update_conflict(&conflict, b, ctx));
+
+  SVN_ERR(svn_client_conflict_tree_resolve_by_id(
+            conflict,
+            svn_client_conflict_option_both_moved_file_move_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);
+
+  /* The node "A/mu-also-moved" should not exist. */
+  SVN_TEST_ASSERT_ERROR(svn_client_conflict_get(
+                          &conflict, sbox_wc_path(b, "A/mu-also-moved"), 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_conflicted);
+  SVN_TEST_ASSERT(status->text_status == svn_wc_status_conflicted);
+  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);
+
+  /* Ensure that the moved+merged file has the expected content. */
+  SVN_ERR(svn_stringbuf_from_file2(&buf, sbox_wc_path(b, "A/mu-moved"),
+                                   b->pool));
+  conflicted_content = apr_psprintf(b->pool,
+												 "<<<<<<< .working\n" /* ### labels need fixing */
+												"%s"
+												"||||||| .old\n"
+												"This is the file 'mu'.\n"
+												"=======\n"
+												"%s"
+												">>>>>>> .new\n",
+												modified_file_content,
+									      modified_file_in_working_copy_content);
+  SVN_TEST_STRING_ASSERT(buf->data, conflicted_content);
+
+  return SVN_NO_ERROR;
+}
+
+
 /* ========================================================================== */
 
 
@@ -7321,6 +7583,10 @@ static struct svn_test_descriptor_t test
                        "dir move vs dir move during merge"),
     SVN_TEST_OPTS_PASS(test_merge_dir_move_vs_dir_move_accept_move,
                        "dir move vs dir move during merge accept move"),
+    SVN_TEST_OPTS_PASS(test_update_file_move_vs_file_move,
+                       "file move vs file move during update"),
+    SVN_TEST_OPTS_PASS(test_update_file_move_vs_file_move_accept_move,
+                       "file move vs file move during update accept move"),
     SVN_TEST_NULL
   };
 

Modified: subversion/branches/shelving-v3/subversion/tests/libsvn_wc/wc-queries-test.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/tests/libsvn_wc/wc-queries-test.c?rev=1853900&r1=1853899&r2=1853900&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/tests/libsvn_wc/wc-queries-test.c (original)
+++ subversion/branches/shelving-v3/subversion/tests/libsvn_wc/wc-queries-test.c Tue Feb 19 17:10:11 2019
@@ -100,6 +100,7 @@ static const int slow_statements[] =
   STMT_SELECT_UPDATE_MOVE_LIST,
   STMT_FIND_REPOS_PATH_IN_WC,
   STMT_SELECT_PRESENT_HIGHEST_WORKING_NODES_BY_BASENAME_AND_KIND,
+  STMT_SELECT_COPIES_OF_REPOS_RELPATH,
 
   /* Designed as slow to avoid penalty on other queries */
   STMT_SELECT_UNREFERENCED_PRISTINES,



Mime
View raw message