You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2022/01/14 14:01:51 UTC

svn commit: r1897034 [16/37] - in /subversion/branches/multi-wc-format: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/ contrib/client-side/svn_load_dirs/ contrib/hook-scripts/ contrib/s...

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/copy.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/copy.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/copy.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/copy.c Fri Jan 14 14:01:45 2022
@@ -571,14 +571,14 @@ pin_externals_prop(svn_string_t **pinned
  * mentioned in EXTERNALS_TO_PIN.
  * The pinning operation takes place as part of the copy operation for
  * the source/destination pair PAIR. Use RA_SESSION and REPOS_ROOT_URL
- * to contact the repository containing the externals definition, if neccesary.
+ * to contact the repository containing the externals definition, if necessary.
  * Use CX to fopen additional RA sessions to external repositories, if
- * neccessary. Allocate *NEW_EXTERNALS in RESULT_POOL.
+ * necessary. Allocate *NEW_EXTERNALS in RESULT_POOL.
  * Use SCRATCH_POOL for temporary allocations. */
 static svn_error_t *
 resolve_pinned_externals(apr_hash_t **pinned_externals,
                          const apr_hash_t *externals_to_pin,
-                         svn_client__copy_pair_t *pair,
+                         const svn_client__copy_pair_t *pair,
                          svn_ra_session_t *ra_session,
                          const char *repos_root_url,
                          svn_client_ctx_t *ctx,
@@ -1099,14 +1099,13 @@ verify_wc_dsts(const apr_array_header_t
   return SVN_NO_ERROR;
 }
 
+/* Verify that the WC sources in COPY_PAIRS exist, and set pair->src_kind
+   for each.
+ */
 static svn_error_t *
-verify_wc_srcs_and_dsts(const apr_array_header_t *copy_pairs,
-                        svn_boolean_t make_parents,
-                        svn_boolean_t is_move,
-                        svn_boolean_t metadata_only,
-                        svn_client_ctx_t *ctx,
-                        apr_pool_t *result_pool,
-                        apr_pool_t *scratch_pool)
+verify_wc_srcs(const apr_array_header_t *copy_pairs,
+               svn_client_ctx_t *ctx,
+               apr_pool_t *scratch_pool)
 {
   int i;
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
@@ -1133,10 +1132,6 @@ verify_wc_srcs_and_dsts(const apr_array_
                                         pair->src_abspath_or_url,
                                         scratch_pool));
     }
-
-  SVN_ERR(verify_wc_dsts(copy_pairs, make_parents, is_move, metadata_only, ctx,
-                         result_pool, iterpool));
-
   svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
@@ -1163,10 +1158,6 @@ typedef struct path_driver_info_t
    or move operation. */
 struct path_driver_cb_baton
 {
-  /* The editor (and its state) used to perform the operation. */
-  const svn_delta_editor_t *editor;
-  void *edit_baton;
-
   /* A hash of path -> path_driver_info_t *'s. */
   apr_hash_t *action_hash;
 
@@ -1176,6 +1167,8 @@ struct path_driver_cb_baton
 
 static svn_error_t *
 path_driver_cb_func(void **dir_baton,
+                    const svn_delta_editor_t *editor,
+                    void *edit_baton,
                     void *parent_baton,
                     void *callback_baton,
                     const char *path,
@@ -1196,9 +1189,9 @@ path_driver_cb_func(void **dir_baton,
   /* Check to see if we need to add the path as a parent directory. */
   if (path_info->dir_add)
     {
-      return cb_baton->editor->add_directory(path, parent_baton, NULL,
-                                             SVN_INVALID_REVNUM, pool,
-                                             dir_baton);
+      return editor->add_directory(path, parent_baton, NULL,
+                                   SVN_INVALID_REVNUM, pool,
+                                   dir_baton);
     }
 
   /* If this is a resurrection, we know the source and dest paths are
@@ -1230,8 +1223,8 @@ path_driver_cb_func(void **dir_baton,
 
   if (do_delete)
     {
-      SVN_ERR(cb_baton->editor->delete_entry(path, SVN_INVALID_REVNUM,
-                                             parent_baton, pool));
+      SVN_ERR(editor->delete_entry(path, SVN_INVALID_REVNUM,
+                                   parent_baton, pool));
     }
   if (do_add)
     {
@@ -1240,40 +1233,40 @@ path_driver_cb_func(void **dir_baton,
       if (path_info->src_kind == svn_node_file)
         {
           void *file_baton;
-          SVN_ERR(cb_baton->editor->add_file(path, parent_baton,
-                                             path_info->src_url,
-                                             path_info->src_revnum,
-                                             pool, &file_baton));
+          SVN_ERR(editor->add_file(path, parent_baton,
+                                   path_info->src_url,
+                                   path_info->src_revnum,
+                                   pool, &file_baton));
           if (path_info->mergeinfo)
-            SVN_ERR(cb_baton->editor->change_file_prop(file_baton,
-                                                       SVN_PROP_MERGEINFO,
-                                                       path_info->mergeinfo,
-                                                       pool));
-          SVN_ERR(cb_baton->editor->close_file(file_baton, NULL, pool));
+            SVN_ERR(editor->change_file_prop(file_baton,
+                                             SVN_PROP_MERGEINFO,
+                                             path_info->mergeinfo,
+                                             pool));
+          SVN_ERR(editor->close_file(file_baton, NULL, pool));
         }
       else
         {
-          SVN_ERR(cb_baton->editor->add_directory(path, parent_baton,
-                                                  path_info->src_url,
-                                                  path_info->src_revnum,
-                                                  pool, dir_baton));
+          SVN_ERR(editor->add_directory(path, parent_baton,
+                                        path_info->src_url,
+                                        path_info->src_revnum,
+                                        pool, dir_baton));
           if (path_info->mergeinfo)
-            SVN_ERR(cb_baton->editor->change_dir_prop(*dir_baton,
-                                                      SVN_PROP_MERGEINFO,
-                                                      path_info->mergeinfo,
-                                                      pool));
+            SVN_ERR(editor->change_dir_prop(*dir_baton,
+                                            SVN_PROP_MERGEINFO,
+                                            path_info->mergeinfo,
+                                            pool));
         }
     }
 
   if (path_info->externals)
     {
       if (*dir_baton == NULL)
-        SVN_ERR(cb_baton->editor->open_directory(path, parent_baton,
-                                                 SVN_INVALID_REVNUM,
-                                                 pool, dir_baton));
+        SVN_ERR(editor->open_directory(path, parent_baton,
+                                       SVN_INVALID_REVNUM,
+                                       pool, dir_baton));
 
-      SVN_ERR(cb_baton->editor->change_dir_prop(*dir_baton, SVN_PROP_EXTERNALS,
-                                                path_info->externals, pool));
+      SVN_ERR(editor->change_dir_prop(*dir_baton, SVN_PROP_EXTERNALS,
+                                      path_info->externals, pool));
     }
 
   return SVN_NO_ERROR;
@@ -1857,13 +1850,11 @@ repos_to_repos_copy(const apr_array_head
                                     pool));
 
   /* Setup the callback baton. */
-  cb_baton.editor = editor;
-  cb_baton.edit_baton = edit_baton;
   cb_baton.action_hash = action_hash;
   cb_baton.is_move = is_move;
 
   /* Call the path-based editor driver. */
-  err = svn_delta_path_driver2(editor, edit_baton, paths, TRUE,
+  err = svn_delta_path_driver3(editor, edit_baton, paths, TRUE,
                                path_driver_cb_func, &cb_baton, pool);
   if (err)
     {
@@ -2318,9 +2309,15 @@ struct notification_adjust_baton
 };
 
 /* A svn_wc_notify_func2_t function that wraps BATON->inner_func (whose
- * baton is BATON->inner_baton) and adjusts the notification paths that
- * start with BATON->checkout_abspath to start instead with
- * BATON->final_abspath. */
+ * baton is BATON->inner_baton) to turn the result of a 'checkout' into
+ * what we want to see for a 'copy to WC' operation.
+ *
+ *  - Adjust the notification paths that start with BATON->checkout_abspath
+ *    to start instead with BATON->final_abspath.
+ *  - Change start-of-update notification into a plain WC 'add' for the root.
+ *  - Change checkout 'add' notifications into a plain WC 'add'.
+ *  - Discard 'update_completed' notifications.
+ */
 static void
 notification_adjust_func(void *baton,
                          const svn_wc_notify_t *notify,
@@ -2333,18 +2330,373 @@ notification_adjust_func(void *baton,
   relpath = svn_dirent_skip_ancestor(nb->checkout_abspath, notify->path);
   inner_notify->path = svn_dirent_join(nb->final_abspath, relpath, pool);
 
+  /* Convert 'update' notifications to plain 'add' notifications; discard
+     notifications about checkout/update starting/finishing. */
+  if (notify->action == svn_wc_notify_update_started  /* root */
+      || notify->action == svn_wc_notify_update_add)  /* non-root */
+    {
+      inner_notify->action = svn_wc_notify_add;
+    }
+  else if (notify->action == svn_wc_notify_update_update
+           || notify->action == svn_wc_notify_update_completed)
+    {
+      /* update_update happens only for a prop mod on root; the root was
+         already notified so discard this */
+      return;
+    }
+
   if (nb->inner_func)
     nb->inner_func(nb->inner_baton, inner_notify, pool);
 }
 
-/* Peform each individual copy operation for a repos -> wc copy.  A
+/** Copy a directory tree from a remote repository.
+ *
+ * Copy from RA_SESSION:LOCATION to WC_CTX:DST_ABSPATH.
+ *
+ * Create the directory DST_ABSPATH, if not present. Its parent should be
+ * already under version control in the WC and in a suitable state for
+ * scheduling the addition of a child.
+ *
+ * Ignore any incoming non-regular properties (entry-props, DAV/WC-props).
+ * Remove any incoming 'svn:mergeinfo' properties.
+ */
+static svn_error_t *
+copy_foreign_dir(svn_ra_session_t *ra_session,
+                 const svn_client__pathrev_t *location,
+                 const char *dst_abspath,
+                 svn_wc_notify_func2_t notify_func,
+                 void *notify_baton,
+                 svn_cancel_func_t cancel_func,
+                 void *cancel_baton,
+                 svn_client_ctx_t *ctx,
+                 apr_pool_t *scratch_pool)
+{
+  const svn_delta_editor_t *editor;
+  void *eb;
+  const svn_delta_editor_t *wrapped_editor;
+  void *wrapped_baton;
+  const svn_ra_reporter3_t *reporter;
+  void *reporter_baton;
+
+  /* Get a WC editor. It does not need an RA session because we will not
+     be sending it any 'copy from' requests, only 'add' requests. */
+  SVN_ERR(svn_client__wc_editor_internal(&editor, &eb,
+                                         dst_abspath,
+                                         TRUE /*root_dir_add*/,
+                                         TRUE /*ignore_mergeinfo_changes*/,
+                                         FALSE /*manage_wc_write_lock*/,
+                                         notify_func, notify_baton,
+                                         NULL /*ra_session*/,
+                                         ctx, scratch_pool));
+
+  SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
+                                            editor, eb,
+                                            &wrapped_editor, &wrapped_baton,
+                                            scratch_pool));
+
+  SVN_ERR(svn_ra_do_update3(ra_session, &reporter, &reporter_baton,
+                            location->rev, "", svn_depth_infinity,
+                            FALSE, FALSE, wrapped_editor, wrapped_baton,
+                            scratch_pool, scratch_pool));
+
+  SVN_ERR(reporter->set_path(reporter_baton, "", location->rev,
+                             svn_depth_infinity /* irrelevant */,
+                             TRUE /*start_empty*/,
+                             NULL, scratch_pool));
+
+  SVN_ERR(reporter->finish_report(reporter_baton, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Implementation of svn_client__repos_to_wc_copy() for a dir.
+ */
+static svn_error_t *
+svn_client__repos_to_wc_copy_dir(svn_boolean_t *timestamp_sleep,
+                                 const char *src_url,
+                                 svn_revnum_t src_revnum,
+                                 const char *dst_abspath,
+                                 svn_boolean_t same_repositories,
+                                 svn_ra_session_t *ra_session,
+                                 svn_client_ctx_t *ctx,
+                                 apr_pool_t *scratch_pool)
+{
+  const char *tmpdir_abspath, *tmp_abspath;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
+
+  if (!same_repositories)
+    {
+      svn_client__pathrev_t *location;
+
+      *timestamp_sleep = TRUE;
+
+      /* ### Reparenting "ra_session" can't be right, can it? As this is
+             a foreign repo, surely we need a new RA session? */
+      SVN_ERR(svn_client__pathrev_create_with_session(&location, ra_session,
+                                                      src_revnum, src_url,
+                                                      scratch_pool));
+      SVN_ERR(svn_ra_reparent(ra_session, src_url, scratch_pool));
+      SVN_ERR(copy_foreign_dir(ra_session, location,
+                               dst_abspath,
+                               ctx->notify_func2, ctx->notify_baton2,
+                               ctx->cancel_func, ctx->cancel_baton,
+                               ctx, scratch_pool));
+
+      return SVN_NO_ERROR;
+    }
+
+  /* Find a temporary location in which to check out the copy source. */
+  SVN_ERR(svn_wc__get_tmpdir(&tmpdir_abspath, ctx->wc_ctx, dst_abspath,
+                             scratch_pool, scratch_pool));
+
+  /* Get a temporary path. The crude way we do this is to create a
+     temporary file, remember its name, and let it be deleted immediately. */
+  SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_abspath, tmpdir_abspath,
+                                   svn_io_file_del_on_close,
+                                   scratch_pool, scratch_pool));
+
+  /* Make a new checkout of the requested source. While doing so,
+   * resolve copy_src_revnum to an actual revision number in case it
+   * was until now 'invalid' meaning 'head'.  Ask this function not to
+   * sleep for timestamps, by passing a sleep_needed output param.
+   * Send notifications for all nodes except the root node, and adjust
+   * them to refer to the destination rather than this temporary path. */
+  {
+    svn_wc_notify_func2_t old_notify_func2 = ctx->notify_func2;
+    void *old_notify_baton2 = ctx->notify_baton2;
+    struct notification_adjust_baton nb;
+    svn_error_t *err;
+    svn_opt_revision_t copy_src_revision;
+
+    copy_src_revision.kind = svn_opt_revision_number;
+    copy_src_revision.value.number = src_revnum;
+
+    nb.inner_func = ctx->notify_func2;
+    nb.inner_baton = ctx->notify_baton2;
+    nb.checkout_abspath = tmp_abspath;
+    nb.final_abspath = dst_abspath;
+    ctx->notify_func2 = notification_adjust_func;
+    ctx->notify_baton2 = &nb;
+
+    err = svn_client__checkout_internal(NULL /*result_rev*/, timestamp_sleep,
+                                        src_url,
+                                        tmp_abspath,
+                                        &copy_src_revision,
+                                        &copy_src_revision,
+                                        svn_depth_infinity,
+                                        TRUE /*ignore_externals*/,
+                                        FALSE, /* we don't allow obstructions */
+                                        NULL, /* default WC format */
+                                        ra_session, ctx, scratch_pool);
+
+    ctx->notify_func2 = old_notify_func2;
+    ctx->notify_baton2 = old_notify_baton2;
+
+    SVN_ERR(err);
+  }
+
+  /* Schedule dst_path for addition in parent, with copy history.
+     Don't send any notification here.
+     Then remove the temporary checkout's .svn dir in preparation for
+     moving the rest of it into the final destination. */
+  SVN_ERR(svn_wc_copy3(ctx->wc_ctx, tmp_abspath, dst_abspath,
+                       TRUE /* metadata_only */,
+                       NULL, NULL, /* don't allow user to cancel here */
+                       NULL, NULL, scratch_pool));
+  SVN_ERR(svn_wc__acquire_write_lock(NULL, ctx->wc_ctx, tmp_abspath,
+                                     FALSE, scratch_pool, scratch_pool));
+  SVN_ERR(svn_wc_remove_from_revision_control2(ctx->wc_ctx,
+                                               tmp_abspath,
+                                               FALSE, FALSE,
+                                               NULL, NULL, /* don't cancel */
+                                               scratch_pool));
+
+  /* Move the temporary disk tree into place. */
+  SVN_ERR(svn_io_file_rename2(tmp_abspath, dst_abspath, FALSE, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Implementation of svn_client__repos_to_wc_copy() for a file.
+ *
+ * This has no 'ignore_externals' parameter because we don't support the
+ * 'svn:externals' property being set on a file.
+ */
+static svn_error_t *
+svn_client__repos_to_wc_copy_file(svn_boolean_t *timestamp_sleep,
+                                  const char *src_url,
+                                  svn_revnum_t src_rev,
+                                  const char *dst_abspath,
+                                  svn_boolean_t same_repositories,
+                                  svn_ra_session_t *ra_session,
+                                  svn_client_ctx_t *ctx,
+                                  apr_pool_t *scratch_pool)
+{
+  const char *src_rel;
+  apr_hash_t *new_props;
+  svn_stream_t *new_base_contents = svn_stream_buffered(scratch_pool);
+
+  SVN_ERR(svn_ra_get_path_relative_to_session(ra_session, &src_rel, src_url,
+                                              scratch_pool));
+  /* Fetch the file content. */
+  SVN_ERR(svn_ra_get_file(ra_session, src_rel, src_rev,
+                          new_base_contents, NULL, &new_props,
+                          scratch_pool));
+  if (!same_repositories)
+    svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
+
+  *timestamp_sleep = TRUE;
+  SVN_ERR(svn_wc_add_repos_file4(
+            ctx->wc_ctx, dst_abspath,
+            new_base_contents, NULL, new_props, NULL,
+            same_repositories ? src_url : NULL,
+            same_repositories ? src_rev : SVN_INVALID_REVNUM,
+            ctx->cancel_func, ctx->cancel_baton,
+            scratch_pool));
+  /* Do our own notification for the root node, even if we could possibly
+     have delegated it.  See also issue #2198. */
+  if (ctx->notify_func2)
+    {
+      svn_wc_notify_t *notify
+        = svn_wc_create_notify(dst_abspath, svn_wc_notify_add, scratch_pool);
+
+      notify->kind = svn_node_file;
+      ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+    }
+  return SVN_NO_ERROR;
+}
+
+/* Are RA_SESSION and the versioned *parent* dir of WC_TARGET_ABSPATH in
+ * the same repository?
+ */
+static svn_error_t *
+is_same_repository(svn_boolean_t *same_repository,
+                   svn_ra_session_t *ra_session,
+                   const char *wc_target_abspath,
+                   svn_client_ctx_t *ctx,
+                   apr_pool_t *scratch_pool)
+{
+  const char *src_uuid, *dst_uuid;
+
+  /* Get the repository UUIDs of copy source URL and WC parent path */
+  SVN_ERR(svn_ra_get_uuid2(ra_session, &src_uuid, scratch_pool));
+  SVN_ERR(svn_client_get_repos_root(NULL /*root_url*/, &dst_uuid,
+                                    svn_dirent_dirname(wc_target_abspath,
+                                                       scratch_pool),
+                                    ctx, scratch_pool, scratch_pool));
+  *same_repository = (strcmp(src_uuid, dst_uuid) == 0);
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__repos_to_wc_copy_internal(svn_boolean_t *timestamp_sleep,
+                             svn_node_kind_t kind,
+                             const char *src_url,
+                             svn_revnum_t src_rev,
+                             const char *dst_abspath,
+                             svn_ra_session_t *ra_session,
+                             svn_client_ctx_t *ctx,
+                             apr_pool_t *scratch_pool)
+{
+  const char *old_session_url;
+  svn_boolean_t timestamp_sleep_ignored;
+  svn_boolean_t same_repositories;
+
+  SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
+                                            src_url, scratch_pool));
+
+  SVN_ERR(is_same_repository(&same_repositories,
+                             ra_session, dst_abspath, ctx, scratch_pool));
+
+  if (!timestamp_sleep)
+    timestamp_sleep = &timestamp_sleep_ignored;
+
+  if (kind == svn_node_dir)
+    {
+      SVN_ERR(svn_client__repos_to_wc_copy_dir(timestamp_sleep,
+                                               src_url, src_rev,
+                                               dst_abspath,
+                                               same_repositories,
+                                               ra_session,
+                                               ctx, scratch_pool));
+    }
+  else if (kind == svn_node_file)
+    {
+      SVN_ERR(svn_client__repos_to_wc_copy_file(timestamp_sleep,
+                                                src_url, src_rev,
+                                                dst_abspath,
+                                                same_repositories,
+                                                ra_session,
+                                                ctx, scratch_pool));
+    }
+
+  /* Reparent the session back to the original URL. */
+  SVN_ERR(svn_ra_reparent(ra_session, old_session_url, scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__repos_to_wc_copy_by_editor(svn_boolean_t *timestamp_sleep,
+                svn_node_kind_t kind,
+                const char *src_url,
+                svn_revnum_t src_rev,
+                const char *dst_abspath,
+                svn_ra_session_t *ra_session,
+                svn_client_ctx_t *ctx,
+                apr_pool_t *scratch_pool)
+{
+  const svn_delta_editor_t *editor;
+  void *eb;
+  const char *src_anchor = svn_uri_dirname(src_url, scratch_pool);
+  const char *dst_target = svn_dirent_basename(dst_abspath, scratch_pool);
+  void *rb, *db;
+
+  SVN_ERR(svn_ra_reparent(ra_session, src_anchor, scratch_pool));
+
+  SVN_ERR(svn_client__wc_editor_internal(
+            &editor, &eb,
+            svn_dirent_dirname(dst_abspath, scratch_pool),
+            FALSE /*root_dir_add*/,
+            FALSE /*ignore_mergeinfo_changes*/,
+            FALSE /*manage_wc_write_lock*/,
+            ctx->notify_func2, ctx->notify_baton2,
+            ra_session,
+            ctx, scratch_pool));
+
+  SVN_ERR(editor->open_root(eb, SVN_INVALID_REVNUM, scratch_pool, &rb));
+  if (kind == svn_node_dir)
+    {
+      SVN_ERR(editor->add_directory(dst_target, rb,
+                                    src_url, src_rev,
+                                    scratch_pool,
+                                    &db));
+      SVN_ERR(editor->close_directory(db, scratch_pool));
+    }
+  else
+    {
+      SVN_ERR(editor->add_file(dst_target, rb,
+                               src_url, src_rev,
+                               scratch_pool,
+                               &db));
+      SVN_ERR(editor->close_file(db, NULL, scratch_pool));
+    }
+  SVN_ERR(editor->close_edit(eb, scratch_pool));
+
+  if (timestamp_sleep)
+    *timestamp_sleep = TRUE;
+  return SVN_NO_ERROR;
+}
+
+/* Perform each individual copy operation for a repos -> wc copy.  A
    helper for repos_to_wc_copy().
 
-   Resolve PAIR->src_revnum to a real revision number if it isn't already. */
+   PAIR->src_revnum PAIR->src_abspath_or_url should already have been
+   resolved to the operative revision number and operative URL.
+ */
 static svn_error_t *
 repos_to_wc_copy_single(svn_boolean_t *timestamp_sleep,
-                        svn_client__copy_pair_t *pair,
-                        svn_boolean_t same_repositories,
+                        const svn_client__copy_pair_t *pair,
                         svn_boolean_t ignore_externals,
                         svn_boolean_t pin_externals,
                         const apr_hash_t *externals_to_pin,
@@ -2354,9 +2706,14 @@ repos_to_wc_copy_single(svn_boolean_t *t
 {
   apr_hash_t *src_mergeinfo;
   const char *dst_abspath = pair->dst_abspath_or_url;
+  svn_boolean_t same_repositories;
 
+  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(pair->src_revnum));
+  SVN_ERR_ASSERT(svn_path_is_url(pair->src_abspath_or_url));
   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
 
+  SVN_ERR(is_same_repository(&same_repositories,
+                             ra_session, dst_abspath, ctx, pool));
   if (!same_repositories && ctx->notify_func2)
     {
       svn_wc_notify_t *notify;
@@ -2372,136 +2729,59 @@ repos_to_wc_copy_single(svn_boolean_t *t
         SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
     }
 
-  if (pair->src_kind == svn_node_dir)
+  SVN_ERR(svn_client__repos_to_wc_copy_by_editor(
+            timestamp_sleep,
+            pair->src_kind,
+            pair->src_abspath_or_url,
+            pair->src_revnum,
+            dst_abspath,
+            ra_session, ctx, pool));
+
+  /* Fetch externals, pinning them if requested */
+  if (!ignore_externals && pair->src_kind == svn_node_dir)
     {
       if (same_repositories)
         {
-          const char *tmpdir_abspath, *tmp_abspath;
-
-          /* Find a temporary location in which to check out the copy source. */
-          SVN_ERR(svn_wc__get_tmpdir(&tmpdir_abspath, ctx->wc_ctx, dst_abspath,
-                                     pool, pool));
-
-          SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_abspath, tmpdir_abspath,
-                                           svn_io_file_del_on_close, pool, pool));
-
-          /* Make a new checkout of the requested source. While doing so,
-           * resolve pair->src_revnum to an actual revision number in case it
-           * was until now 'invalid' meaning 'head'.  Ask this function not to
-           * sleep for timestamps, by passing a sleep_needed output param.
-           * Send notifications for all nodes except the root node, and adjust
-           * them to refer to the destination rather than this temporary path. */
-          {
-            svn_wc_notify_func2_t old_notify_func2 = ctx->notify_func2;
-            void *old_notify_baton2 = ctx->notify_baton2;
-            struct notification_adjust_baton nb;
-            svn_error_t *err;
-
-            nb.inner_func = ctx->notify_func2;
-            nb.inner_baton = ctx->notify_baton2;
-            nb.checkout_abspath = tmp_abspath;
-            nb.final_abspath = dst_abspath;
-            ctx->notify_func2 = notification_adjust_func;
-            ctx->notify_baton2 = &nb;
-
-            /* Avoid a chicken-and-egg problem:
-             * If pinning externals we'll need to adjust externals
-             * properties before checking out any externals.
-             * But copy needs to happen before pinning because else there
-             * are no svn:externals properties to pin. */
-            if (pin_externals)
-              ignore_externals = TRUE;
-
-            err = svn_client__checkout_internal(&pair->src_revnum, timestamp_sleep,
-                                                pair->src_original,
-                                                tmp_abspath,
-                                                &pair->src_peg_revision,
-                                                &pair->src_op_revision,
-                                                svn_depth_infinity,
-                                                ignore_externals, FALSE,
-                                                NULL, /* default WC format */
-                                                ra_session, ctx, pool);
-
-            ctx->notify_func2 = old_notify_func2;
-            ctx->notify_baton2 = old_notify_baton2;
-
-            SVN_ERR(err);
-          }
-
-          *timestamp_sleep = TRUE;
-
-          /* Schedule dst_path for addition in parent, with copy history.
-             Don't send any notification here.
-             Then remove the temporary checkout's .svn dir in preparation for
-             moving the rest of it into the final destination. */
-          SVN_ERR(svn_wc_copy3(ctx->wc_ctx, tmp_abspath, dst_abspath,
-                               TRUE /* metadata_only */,
-                               ctx->cancel_func, ctx->cancel_baton,
-                               NULL, NULL, pool));
-          SVN_ERR(svn_wc__acquire_write_lock(NULL, ctx->wc_ctx, tmp_abspath,
-                                             FALSE, pool, pool));
-          SVN_ERR(svn_wc_remove_from_revision_control2(ctx->wc_ctx,
-                                                       tmp_abspath,
-                                                       FALSE, FALSE,
-                                                       ctx->cancel_func,
-                                                       ctx->cancel_baton,
-                                                       pool));
-
-          /* Move the temporary disk tree into place. */
-          SVN_ERR(svn_io_file_rename2(tmp_abspath, dst_abspath, FALSE, pool));
-        }
-      else
-        {
-          *timestamp_sleep = TRUE;
-
-          SVN_ERR(svn_client__copy_foreign(pair->src_abspath_or_url,
-                                           dst_abspath,
-                                           &pair->src_peg_revision,
-                                           &pair->src_op_revision,
-                                           svn_depth_infinity,
-                                           FALSE /* make_parents */,
-                                           TRUE /* already_locked */,
-                                           ctx, pool));
-
-          return SVN_NO_ERROR;
-        }
-
-      if (pin_externals)
-        {
-          apr_hash_t *pinned_externals;
-          apr_hash_index_t *hi;
-          apr_pool_t *iterpool;
           const char *repos_root_url;
           apr_hash_t *new_externals;
           apr_hash_t *new_depths;
 
           SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
-          SVN_ERR(resolve_pinned_externals(&pinned_externals,
-                                           externals_to_pin, pair,
-                                           ra_session, repos_root_url,
-                                           ctx, pool, pool));
 
-          iterpool = svn_pool_create(pool);
-          for (hi = apr_hash_first(pool, pinned_externals);
-               hi;
-               hi = apr_hash_next(hi))
+          if (pin_externals)
             {
-              const char *dst_relpath = apr_hash_this_key(hi);
-              svn_string_t *externals_propval = apr_hash_this_val(hi);
-              const char *local_abspath;
+              apr_hash_t *pinned_externals;
+              apr_hash_index_t *hi;
+              apr_pool_t *iterpool;
+
+              SVN_ERR(resolve_pinned_externals(&pinned_externals,
+                                               externals_to_pin, pair,
+                                               ra_session, repos_root_url,
+                                               ctx, pool, pool));
+
+              iterpool = svn_pool_create(pool);
+              for (hi = apr_hash_first(pool, pinned_externals);
+                   hi;
+                   hi = apr_hash_next(hi))
+                {
+                  const char *dst_relpath = apr_hash_this_key(hi);
+                  svn_string_t *externals_propval = apr_hash_this_val(hi);
+                  const char *local_abspath;
 
-              svn_pool_clear(iterpool);
+                  svn_pool_clear(iterpool);
 
-              local_abspath = svn_dirent_join(pair->dst_abspath_or_url,
-                                              dst_relpath, iterpool);
-              /* ### use a work queue? */
-              SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath,
-                                       SVN_PROP_EXTERNALS, externals_propval,
-                                       svn_depth_empty, TRUE /* skip_checks */,
-                                       NULL  /* changelist_filter */,
-                                       ctx->cancel_func, ctx->cancel_baton,
-                                       NULL, NULL, /* no extra notification */
-                                       iterpool));
+                  local_abspath = svn_dirent_join(pair->dst_abspath_or_url,
+                                                  dst_relpath, iterpool);
+                  /* ### use a work queue? */
+                  SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath,
+                                           SVN_PROP_EXTERNALS, externals_propval,
+                                           svn_depth_empty, TRUE /* skip_checks */,
+                                           NULL  /* changelist_filter */,
+                                           ctx->cancel_func, ctx->cancel_baton,
+                                           NULL, NULL, /* no extra notification */
+                                           iterpool));
+                }
+              svn_pool_destroy(iterpool);
             }
 
           /* Now update all externals in the newly created copy. */
@@ -2510,65 +2790,30 @@ repos_to_wc_copy_single(svn_boolean_t *t
                                                        ctx->wc_ctx,
                                                        dst_abspath,
                                                        svn_depth_infinity,
-                                                       iterpool, iterpool));
+                                                       pool, pool));
           SVN_ERR(svn_client__handle_externals(new_externals,
                                                new_depths,
                                                repos_root_url, dst_abspath,
                                                svn_depth_infinity,
                                                timestamp_sleep,
                                                ra_session,
-                                               ctx, iterpool));
-          svn_pool_destroy(iterpool);
+                                               ctx, pool));
         }
-    } /* end directory case */
+    }
 
-  else if (pair->src_kind == svn_node_file)
+  if (same_repositories)
     {
-      apr_hash_t *new_props;
-      const char *src_rel;
-      svn_stream_t *new_base_contents = svn_stream_buffered(pool);
-
-      SVN_ERR(svn_ra_get_path_relative_to_session(ra_session, &src_rel,
-                                                  pair->src_abspath_or_url,
-                                                  pool));
-      /* Fetch the file content. While doing so, resolve pair->src_revnum
-       * to an actual revision number if it's 'invalid' meaning 'head'. */
-      SVN_ERR(svn_ra_get_file(ra_session, src_rel, pair->src_revnum,
-                              new_base_contents,
-                              &pair->src_revnum, &new_props, pool));
-
-      if (new_props && ! same_repositories)
-        svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
+      /* Record the implied mergeinfo. */
+      SVN_ERR(svn_client__get_repos_mergeinfo(&src_mergeinfo, ra_session,
+                                              pair->src_abspath_or_url,
+                                              pair->src_revnum,
+                                              svn_mergeinfo_inherited,
+                                              TRUE /*squelch_incapable*/,
+                                              pool));
+      SVN_ERR(extend_wc_mergeinfo(dst_abspath, src_mergeinfo, ctx, pool));
 
-      *timestamp_sleep = TRUE;
-
-      SVN_ERR(svn_wc_add_repos_file4(
-         ctx->wc_ctx, dst_abspath,
-         new_base_contents, NULL, new_props, NULL,
-         same_repositories ? pair->src_abspath_or_url : NULL,
-         same_repositories ? pair->src_revnum : SVN_INVALID_REVNUM,
-         ctx->cancel_func, ctx->cancel_baton,
-         pool));
-    }
-
-  /* Record the implied mergeinfo (before the notification callback
-     is invoked for the root node). */
-  SVN_ERR(svn_client__get_repos_mergeinfo(
-            &src_mergeinfo, ra_session,
-            pair->src_abspath_or_url, pair->src_revnum,
-            svn_mergeinfo_inherited, TRUE /*squelch_incapable*/, pool));
-  SVN_ERR(extend_wc_mergeinfo(dst_abspath, src_mergeinfo, ctx, pool));
-
-  /* Do our own notification for the root node, even if we could possibly
-     have delegated it.  See also issue #1552.
-
-     ### Maybe this notification should mention the mergeinfo change. */
-  if (ctx->notify_func2)
-    {
-      svn_wc_notify_t *notify = svn_wc_create_notify(
-                                  dst_abspath, svn_wc_notify_add, pool);
-      notify->kind = pair->src_kind;
-      ctx->notify_func2(ctx->notify_baton2, notify, pool);
+      /* ### Maybe the notification should mention this mergeinfo change. */
+      /* ### Maybe we should do this during rather than after the copy. */
     }
 
   return SVN_NO_ERROR;
@@ -2586,38 +2831,8 @@ repos_to_wc_copy_locked(svn_boolean_t *t
                         apr_pool_t *scratch_pool)
 {
   int i;
-  svn_boolean_t same_repositories;
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
-  /* We've already checked for physical obstruction by a working file.
-     But there could also be logical obstruction by an entry whose
-     working file happens to be missing.*/
-  SVN_ERR(verify_wc_dsts(copy_pairs, FALSE, FALSE, FALSE /* metadata_only */,
-                         ctx, scratch_pool, iterpool));
-
-  /* Decide whether the two repositories are the same or not. */
-  {
-    const char *parent_abspath;
-    const char *src_uuid, *dst_uuid;
-
-    /* Get the repository uuid of SRC_URL */
-    SVN_ERR(svn_ra_get_uuid2(ra_session, &src_uuid, iterpool));
-
-    /* Get repository uuid of dst's parent directory, since dst may
-       not exist.  ### TODO:  we should probably walk up the wc here,
-       in case the parent dir has an imaginary URL.  */
-    if (copy_pairs->nelts == 1)
-      parent_abspath = svn_dirent_dirname(top_dst_abspath, scratch_pool);
-    else
-      parent_abspath = top_dst_abspath;
-
-    SVN_ERR(svn_client_get_repos_root(NULL /* root_url */, &dst_uuid,
-                                      parent_abspath, ctx,
-                                      iterpool, iterpool));
-    /* ### Also check repos_root_url? */
-    same_repositories = (strcmp(src_uuid, dst_uuid) == 0);
-  }
-
   /* Perform the move for each of the copy_pairs. */
   for (i = 0; i < copy_pairs->nelts; i++)
     {
@@ -2630,7 +2845,6 @@ repos_to_wc_copy_locked(svn_boolean_t *t
       SVN_ERR(repos_to_wc_copy_single(timestamp_sleep,
                                       APR_ARRAY_IDX(copy_pairs, i,
                                                     svn_client__copy_pair_t *),
-                                      same_repositories,
                                       ignore_externals,
                                       pin_externals, externals_to_pin,
                                       ra_session, ctx, iterpool));
@@ -2643,7 +2857,6 @@ repos_to_wc_copy_locked(svn_boolean_t *t
 static svn_error_t *
 repos_to_wc_copy(svn_boolean_t *timestamp_sleep,
                  const apr_array_header_t *copy_pairs,
-                 svn_boolean_t make_parents,
                  svn_boolean_t ignore_externals,
                  svn_boolean_t pin_externals,
                  const apr_hash_t *externals_to_pin,
@@ -2697,8 +2910,6 @@ repos_to_wc_copy(svn_boolean_t *timestam
     {
       svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
                                                     svn_client__copy_pair_t *);
-      svn_node_kind_t dst_parent_kind, dst_kind;
-      const char *dst_parent;
       const char *src_rel;
 
       svn_pool_clear(iterpool);
@@ -2722,43 +2933,6 @@ repos_to_wc_copy(svn_boolean_t *timestam
                _("Path '%s' not found in head revision"),
                pair->src_abspath_or_url);
         }
-
-      /* Figure out about dst. */
-      SVN_ERR(svn_io_check_path(pair->dst_abspath_or_url, &dst_kind,
-                                iterpool));
-      if (dst_kind != svn_node_none)
-        {
-          return svn_error_createf(
-            SVN_ERR_ENTRY_EXISTS, NULL,
-            _("Path '%s' already exists"),
-            svn_dirent_local_style(pair->dst_abspath_or_url, pool));
-        }
-
-      /* Make sure the destination parent is a directory and produce a clear
-         error message if it is not. */
-      dst_parent = svn_dirent_dirname(pair->dst_abspath_or_url, iterpool);
-      SVN_ERR(svn_io_check_path(dst_parent, &dst_parent_kind, iterpool));
-      if (make_parents && dst_parent_kind == svn_node_none)
-        {
-          SVN_ERR(svn_client__make_local_parents(dst_parent, TRUE, ctx,
-                                                 iterpool));
-        }
-      else if (make_parents && dst_parent_kind == svn_node_dir)
-        {
-          SVN_ERR(svn_wc_read_kind2(&dst_parent_kind, ctx->wc_ctx, dst_parent,
-                                    FALSE, TRUE, iterpool));
-          if (dst_parent_kind == svn_node_none)
-            {
-              SVN_ERR(svn_client__make_local_parents(dst_parent, TRUE, ctx,
-                                                     iterpool));
-            }
-        }
-      else if (dst_parent_kind != svn_node_dir)
-        {
-          return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
-                                   _("Path '%s' is not a directory"),
-                                   svn_dirent_local_style(dst_parent, pool));
-        }
     }
   svn_pool_destroy(iterpool);
 
@@ -3081,8 +3255,9 @@ try_copy(svn_boolean_t *timestamp_sleep,
   /* Now, call the right handler for the operation. */
   if ((! srcs_are_urls) && (! dst_is_url))
     {
-      SVN_ERR(verify_wc_srcs_and_dsts(copy_pairs, make_parents, is_move,
-                                      metadata_only, ctx, pool, pool));
+      SVN_ERR(verify_wc_srcs(copy_pairs, ctx, pool));
+      SVN_ERR(verify_wc_dsts(copy_pairs, make_parents, is_move, metadata_only,
+                             ctx, pool, pool));
 
       /* Copy or move all targets. */
       if (is_move)
@@ -3112,9 +3287,13 @@ try_copy(svn_boolean_t *timestamp_sleep,
     }
   else if ((srcs_are_urls) && (! dst_is_url))
     {
+      SVN_ERR(verify_wc_dsts(copy_pairs, make_parents,
+                             FALSE, FALSE /* metadata_only */,
+                             ctx, pool, pool));
+
       return svn_error_trace(
         repos_to_wc_copy(timestamp_sleep,
-                         copy_pairs, make_parents, ignore_externals,
+                         copy_pairs, ignore_externals,
                          pin_externals, externals_to_pin, ctx, pool));
     }
   else

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/delete.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/delete.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/delete.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/delete.c Fri Jan 14 14:01:45 2022
@@ -181,12 +181,13 @@ can_delete_node(svn_boolean_t *target_mi
 
 static svn_error_t *
 path_driver_cb_func(void **dir_baton,
+                    const svn_delta_editor_t *editor,
+                    void *edit_baton,
                     void *parent_baton,
                     void *callback_baton,
                     const char *path,
                     apr_pool_t *pool)
 {
-  const svn_delta_editor_t *editor = callback_baton;
   *dir_baton = NULL;
   return editor->delete_entry(path, SVN_INVALID_REVNUM, parent_baton, pool);
 }
@@ -248,8 +249,8 @@ single_repos_delete(svn_ra_session_t *ra
                                     pool));
 
   /* Call the path-based editor driver. */
-  err = svn_delta_path_driver2(editor, edit_baton, relpaths, TRUE,
-                               path_driver_cb_func, (void *)editor, pool);
+  err = svn_delta_path_driver3(editor, edit_baton, relpaths, TRUE,
+                               path_driver_cb_func, NULL, pool);
 
   if (err)
     {

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/deprecated.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/deprecated.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/deprecated.c Fri Jan 14 14:01:45 2022
@@ -166,6 +166,61 @@ svn_client_mkdir(svn_client_commit_info_
 }
 
 /*** From blame.c ***/
+struct blame_receiver_wrapper_baton3 {
+  void *baton;
+  svn_client_blame_receiver3_t receiver;
+  svn_revnum_t start_revnum;
+  svn_revnum_t end_revnum;
+};
+
+static svn_error_t *
+blame_wrapper_receiver3(void *baton,
+   apr_int64_t line_no,
+   svn_revnum_t revision,
+   apr_hash_t *rev_props,
+   svn_revnum_t merged_revision,
+   apr_hash_t *merged_rev_props,
+   const char *merged_path,
+   const svn_string_t *line,
+   svn_boolean_t local_change,
+   apr_pool_t *pool)
+{
+  struct blame_receiver_wrapper_baton3 *brwb = baton;
+
+  if (brwb->receiver)
+    return brwb->receiver(brwb->baton, brwb->start_revnum, brwb->end_revnum,
+                          line_no,
+                          revision, rev_props, merged_revision,
+                          merged_rev_props, merged_path, line->data,
+                          local_change, pool);
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_blame5(const char *target,
+                  const svn_opt_revision_t *peg_revision,
+                  const svn_opt_revision_t *start,
+                  const svn_opt_revision_t *end,
+                  const svn_diff_file_options_t *diff_options,
+                  svn_boolean_t ignore_mime_type,
+                  svn_boolean_t include_merged_revisions,
+                  svn_client_blame_receiver3_t receiver,
+                  void *receiver_baton,
+                  svn_client_ctx_t *ctx,
+                  apr_pool_t *pool)
+{
+  struct blame_receiver_wrapper_baton3 baton;
+
+  baton.receiver = receiver;
+  baton.baton = receiver_baton;
+
+  return svn_client_blame6(&baton.start_revnum, &baton.end_revnum,
+                           target, peg_revision, start, end,
+                           diff_options,
+                           ignore_mime_type, include_merged_revisions,
+                           blame_wrapper_receiver3, &baton, ctx, pool);
+}
 
 struct blame_receiver_wrapper_baton2 {
   void *baton;

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/diff.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/diff.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/diff.c Fri Jan 14 14:01:45 2022
@@ -51,6 +51,7 @@
 #include "svn_subst.h"
 #include "client.h"
 
+#include "private/svn_client_shelf.h"
 #include "private/svn_wc_private.h"
 #include "private/svn_diff_private.h"
 #include "private/svn_subr_private.h"
@@ -587,7 +588,7 @@ print_diff_index_header(svn_stream_t *ou
 
    ### FIXME needs proper docstring
 
-   If USE_GIT_DIFF_FORMAT is TRUE, pring git diff headers, which always
+   If USE_GIT_DIFF_FORMAT is TRUE, print git diff headers, which always
    show paths relative to the repository root. DDI->session_relpath and
    DDI->wc_ctx are needed to normalize paths relative the repository root,
    and are ignored if USE_GIT_DIFF_FORMAT is FALSE.
@@ -1439,7 +1440,7 @@ diff_dir_deleted(const char *relpath,
    With only one distinct revision the working copy provides the
    other.  When path is a URL there is no working copy. Thus
 
-     1: compare repository versions for URL coresponding to working copy
+     1: compare repository versions for URL corresponding to working copy
      2: compare working copy against repository version
      3: compare repository versions for URL
      4: nothing to do.
@@ -1661,7 +1662,7 @@ diff_prepare_repos_repos(const char **ur
     {
       /* It would be nice if we could just return an error when resolving a
          location fails... But in many such cases we prefer diffing against
-         an not existing location to show adds od removes (see issue #4153) */
+         a non-existent location to show adds or removes (see issue #4153) */
 
       if (resolved2
           && (peg_kind != svn_opt_revision_unspecified
@@ -1687,7 +1688,7 @@ diff_prepare_repos_repos(const char **ur
     {
       /* It would be nice if we could just return an error when resolving a
          location fails... But in many such cases we prefer diffing against
-         an not existing location to show adds od removes (see issue #4153) */
+         a non-existent location to show adds or removes (see issue #4153) */
 
       if (resolved1
           && (peg_kind != svn_opt_revision_unspecified
@@ -1893,7 +1894,7 @@ diff_wc_wc(const char *path1,
    for the underlying diff implementation if the target on either side
    is a file, else at the actual requested targets.
 
-   (The choice of WC anchor implementated here for DDI->anchor appears to
+   (The choice of WC anchor implemented here for DDI->anchor appears to
    be: choose PATH_OR_URL2 (if it's a WC path) or else PATH_OR_URL1 (if
    it's a WC path); then take its parent dir unless both resolved URLs
    refer to directories.)

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/export.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/export.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/export.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/export.c Fri Jan 14 14:01:45 2022
@@ -182,16 +182,18 @@ export_node(void *baton,
 {
   struct export_info_baton *eib = baton;
   svn_wc_context_t *wc_ctx = eib->wc_ctx;
-  apr_hash_t *kw = NULL;
+  apr_hash_t *kw;
   svn_subst_eol_style_t style;
   apr_hash_t *props;
   svn_string_t *eol_style, *keywords, *executable, *special;
-  const char *eol = NULL;
+  const char *eol_style_val;
+  const char *eol;
   svn_boolean_t local_mod = FALSE;
   apr_time_t tm;
   svn_stream_t *source;
   svn_stream_t *dst_stream;
-  const char *dst_tmp;
+  const char *tmp_abspath;
+  svn_wc__working_file_writer_t *file_writer;
   svn_error_t *err;
 
   const char *to_abspath = svn_dirent_join(
@@ -268,7 +270,7 @@ export_node(void *baton,
     }
 
   /* Skip file externals if they are a descendant of the export,
-     BUT NOT if we are explictly exporting the file external. */
+     BUT NOT if we are explicitly exporting the file external. */
   if (status->file_external && strcmp(eib->origin_abspath, local_abspath) != 0)
     return SVN_NO_ERROR;
 
@@ -340,26 +342,17 @@ export_node(void *baton,
         local_mod = TRUE;
     }
 
-  /* We can early-exit if we're creating a special file. */
   special = svn_hash_gets(props, SVN_PROP_SPECIAL);
-  if (special != NULL)
-    {
-      /* Create the destination as a special file, and copy the source
-         details into the destination stream. */
-      /* ### And forget the notification */
-      SVN_ERR(svn_subst_create_specialfile(&dst_stream, to_abspath,
-                                           scratch_pool, scratch_pool));
-      return svn_error_trace(
-        svn_stream_copy3(source, dst_stream, NULL, NULL, scratch_pool));
-    }
-
-
   eol_style = svn_hash_gets(props, SVN_PROP_EOL_STYLE);
   keywords = svn_hash_gets(props, SVN_PROP_KEYWORDS);
   executable = svn_hash_gets(props, SVN_PROP_EXECUTABLE);
 
   if (eol_style)
-    SVN_ERR(get_eol_style(&style, &eol, eol_style->data, eib->native_eol));
+    eol_style_val = eol_style->data;
+  else
+    eol_style_val = NULL;
+
+  SVN_ERR(get_eol_style(&style, &eol, eol_style_val, eib->native_eol));
 
   if (local_mod)
     {
@@ -372,7 +365,7 @@ export_node(void *baton,
       tm = status->changed_date;
     }
 
-  if (keywords)
+  if (keywords && !eib->ignore_keywords)
     {
       svn_revnum_t changed_rev = status->changed_rev;
       const char *suffix;
@@ -400,39 +393,33 @@ export_node(void *baton,
                                         url, status->repos_root_url, tm,
                                         author, scratch_pool));
     }
+  else
+    {
+      kw = NULL;
+    }
 
-  /* For atomicity, we translate to a tmp file and then rename the tmp file
-     over the real destination. */
-  SVN_ERR(svn_stream_open_unique(&dst_stream, &dst_tmp,
-                                 svn_dirent_dirname(to_abspath, scratch_pool),
-                                 svn_io_file_del_none, scratch_pool,
-                                 scratch_pool));
-
-  /* If some translation is needed, then wrap the output stream (this is
-     more efficient than wrapping the input). */
-  if (eol || (kw && (apr_hash_count(kw) > 0)))
-    dst_stream = svn_subst_stream_translated(dst_stream,
-                                             eol,
-                                             FALSE /* repair */,
-                                             kw,
-                                             ! eib->ignore_keywords /* expand */,
-                                             scratch_pool);
+  tmp_abspath = svn_dirent_dirname(to_abspath, scratch_pool);
+  SVN_ERR(svn_wc__working_file_writer_open(&file_writer,
+                                           tmp_abspath,
+                                           tm,
+                                           style,
+                                           eol,
+                                           FALSE /* repair_eol */,
+                                           kw,
+                                           special != NULL,
+                                           executable != NULL,
+                                           FALSE /* is_readonly */,
+                                           scratch_pool,
+                                           scratch_pool));
 
   /* ###: use cancel func/baton in place of NULL/NULL below. */
-  err = svn_stream_copy3(source, dst_stream, NULL, NULL, scratch_pool);
-
-  if (!err && executable)
-    err = svn_io_set_file_executable(dst_tmp, TRUE, FALSE, scratch_pool);
+  dst_stream = svn_wc__working_file_writer_get_stream(file_writer);
+  SVN_ERR(svn_stream_copy3(source, dst_stream, NULL, NULL, scratch_pool));
 
-  if (!err)
-    err = svn_io_set_file_affected_time(tm, dst_tmp, scratch_pool);
-
-  if (err)
-    return svn_error_compose_create(err, svn_io_remove_file2(dst_tmp, FALSE,
-                                                             scratch_pool));
-
-  /* Now that dst_tmp contains the translated data, do the atomic rename. */
-  SVN_ERR(svn_io_file_rename2(dst_tmp, to_abspath, FALSE, scratch_pool));
+  SVN_ERR(svn_wc__working_file_writer_finalize(NULL, NULL, file_writer,
+                                               scratch_pool));
+  SVN_ERR(svn_wc__working_file_writer_install(file_writer, to_abspath,
+                                              scratch_pool));
 
   if (eib->notify_func)
     {
@@ -526,11 +513,8 @@ struct file_baton
   struct edit_baton *edit_baton;
 
   const char *path;
-  const char *tmppath;
-
-  /* We need to keep this around so we can explicitly close it in close_file,
-     thus flushing its output to disk so we can copy and translate it. */
-  svn_stream_t *tmp_stream;
+  /* The writer for the file being exported. */
+  svn_wc__working_file_writer_t *file_writer;
 
   /* The MD5 digest of the file's fulltext.  This is all zeros until
      the last textdelta window handler call returns. */
@@ -558,8 +542,6 @@ struct handler_baton
 {
   svn_txdelta_window_handler_t apply_handler;
   void *apply_baton;
-  apr_pool_t *pool;
-  const char *tmppath;
 };
 
 
@@ -679,23 +661,74 @@ static svn_error_t *
 window_handler(svn_txdelta_window_t *window, void *baton)
 {
   struct handler_baton *hb = baton;
-  svn_error_t *err;
 
-  err = hb->apply_handler(window, hb->apply_baton);
-  if (err)
+  SVN_ERR(hb->apply_handler(window, hb->apply_baton));
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Create the writer for the file being exported based on the
+   state in the file baton FB. */
+static svn_error_t *
+open_working_file_writer(svn_wc__working_file_writer_t **writer_p,
+                         struct file_baton *fb,
+                         apr_pool_t *result_pool,
+                         apr_pool_t *scratch_pool)
+{
+  const char *eol_style_val;
+  svn_subst_eol_style_t eol_style;
+  const char *eol;
+  apr_hash_t *keywords;
+  apr_time_t final_mtime;
+  const char *tmp_path;
+  const char *tmp_abspath;
+
+  if (fb->eol_style_val)
+    eol_style_val = fb->eol_style_val->data;
+  else
+    eol_style_val = NULL;
+
+  SVN_ERR(get_eol_style(&eol_style, &eol, eol_style_val,
+                        fb->edit_baton->native_eol));
+
+  if (fb->keywords_val)
     {
-      /* We failed to apply the patch; clean up the temporary file.  */
-      err = svn_error_compose_create(
-                    err,
-                    svn_io_remove_file2(hb->tmppath, TRUE, hb->pool));
+      SVN_ERR(svn_subst_build_keywords3(&keywords, fb->keywords_val->data,
+                                        fb->revision, fb->url,
+                                        fb->repos_root_url, fb->date,
+                                        fb->author, scratch_pool));
+    }
+  else
+    {
+      keywords = NULL;
     }
 
-  return svn_error_trace(err);
-}
+  if (fb->date)
+    final_mtime = fb->date;
+  else
+    final_mtime = -1;
 
+  /* Create a temporary file in the same directory as the file. */
+  tmp_path = svn_dirent_dirname(fb->path, scratch_pool);
+  SVN_ERR(svn_dirent_get_absolute(&tmp_abspath, tmp_path, scratch_pool));
+  SVN_ERR(svn_wc__working_file_writer_open(writer_p,
+                                           tmp_abspath,
+                                           final_mtime,
+                                           eol_style,
+                                           eol,
+                                           TRUE /* repair_eol */,
+                                           keywords,
+                                           fb->special,
+                                           fb->executable_val != NULL,
+                                           FALSE /* is_readonly */,
+                                           result_pool,
+                                           scratch_pool));
 
+  return SVN_NO_ERROR;
+}
 
-/* Write incoming data into the tmpfile stream */
+/* Write incoming data into the file writer */
 static svn_error_t *
 apply_textdelta(void *file_baton,
                 const char *base_checksum,
@@ -706,21 +739,10 @@ apply_textdelta(void *file_baton,
   struct file_baton *fb = file_baton;
   struct handler_baton *hb = apr_palloc(pool, sizeof(*hb));
 
-  /* Create a temporary file in the same directory as the file. We're going
-     to rename the thing into place when we're done. */
-  SVN_ERR(svn_stream_open_unique(&fb->tmp_stream, &fb->tmppath,
-                                 svn_dirent_dirname(fb->path, pool),
-                                 svn_io_file_del_none, fb->pool, fb->pool));
-
-  hb->pool = pool;
-  hb->tmppath = fb->tmppath;
-
-  /* svn_txdelta_apply() closes the stream, but we want to close it in the
-     close_file() function, so disown it here. */
-  /* ### contrast to when we call svn_ra_get_file() which does NOT close the
-     ### tmp_stream. we *should* be much more consistent! */
+  SVN_ERR(open_working_file_writer(&fb->file_writer, fb, fb->pool, pool));
+
   svn_txdelta_apply(svn_stream_empty(pool),
-                    svn_stream_disown(fb->tmp_stream, pool),
+                    svn_wc__working_file_writer_get_stream(fb->file_writer),
                     fb->text_digest, NULL, pool,
                     &hb->apply_handler, &hb->apply_baton);
 
@@ -785,23 +807,21 @@ change_dir_prop(void *dir_baton,
 }
 
 
-/* Move the tmpfile to file, and send feedback. */
+/* Install the file, and send feedback. */
 static svn_error_t *
 close_file(void *file_baton,
            const char *text_digest,
            apr_pool_t *pool)
 {
   struct file_baton *fb = file_baton;
-  struct edit_baton *eb = fb->edit_baton;
   svn_checksum_t *text_checksum;
   svn_checksum_t *actual_checksum;
+  const char *target_abspath;
 
   /* Was a txdelta even sent? */
-  if (! fb->tmppath)
+  if (! fb->file_writer)
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_stream_close(fb->tmp_stream));
-
   SVN_ERR(svn_checksum_parse_hex(&text_checksum, svn_checksum_md5, text_digest,
                                  pool));
   actual_checksum = svn_checksum__from_digest_md5(fb->text_digest, pool);
@@ -814,45 +834,12 @@ close_file(void *file_baton,
                                      _("Checksum mismatch for '%s'"),
                                      svn_dirent_local_style(fb->path, pool));
 
-  if ((! fb->eol_style_val) && (! fb->keywords_val) && (! fb->special))
-    {
-      SVN_ERR(svn_io_file_rename2(fb->tmppath, fb->path, FALSE, pool));
-    }
-  else
-    {
-      svn_subst_eol_style_t style;
-      const char *eol = NULL;
-      svn_boolean_t repair = FALSE;
-      apr_hash_t *final_kw = NULL;
+  SVN_ERR(svn_dirent_get_absolute(&target_abspath, fb->path, pool));
 
-      if (fb->eol_style_val)
-        {
-          SVN_ERR(get_eol_style(&style, &eol, fb->eol_style_val->data,
-                                eb->native_eol));
-          repair = TRUE;
-        }
-
-      if (fb->keywords_val)
-        SVN_ERR(svn_subst_build_keywords3(&final_kw, fb->keywords_val->data,
-                                          fb->revision, fb->url,
-                                          fb->repos_root_url, fb->date,
-                                          fb->author, pool));
-
-      SVN_ERR(svn_subst_copy_and_translate4(fb->tmppath, fb->path,
-                                            eol, repair, final_kw,
-                                            TRUE, /* expand */
-                                            fb->special,
-                                            eb->cancel_func, eb->cancel_baton,
-                                            pool));
-
-      SVN_ERR(svn_io_remove_file2(fb->tmppath, FALSE, pool));
-    }
-
-  if (fb->executable_val)
-    SVN_ERR(svn_io_set_file_executable(fb->path, TRUE, FALSE, pool));
-
-  if (fb->date && (! fb->special))
-    SVN_ERR(svn_io_set_file_affected_time(fb->date, fb->path, pool));
+  SVN_ERR(svn_wc__working_file_writer_finalize(NULL, NULL, fb->file_writer,
+                                               pool));
+  SVN_ERR(svn_wc__working_file_writer_install(fb->file_writer, target_abspath,
+                                              pool));
 
   if (fb->edit_baton->notify_func)
     {
@@ -1212,6 +1199,8 @@ export_file(const char *from_url,
   apr_hash_index_t *hi;
   struct file_baton *fb = apr_pcalloc(scratch_pool, sizeof(*fb));
   svn_node_kind_t to_kind;
+  svn_revnum_t target_rev;
+  svn_stream_t *stream;
 
   SVN_ERR_ASSERT(svn_path_is_url(from_url));
 
@@ -1251,18 +1240,20 @@ export_file(const char *from_url,
   fb->pool = scratch_pool;
   fb->repos_root_url = eb->repos_root_url;
 
-  /* Copied from apply_textdelta(). */
-  SVN_ERR(svn_stream_open_unique(&fb->tmp_stream, &fb->tmppath,
-                                 svn_dirent_dirname(fb->path, scratch_pool),
-                                 svn_io_file_del_none,
-                                 fb->pool, fb->pool));
-
-  /* Step outside the editor-likeness for a moment, to actually talk
-   * to the repository. */
-  /* ### note: the stream will not be closed */
-  SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev,
-                          fb->tmp_stream,
-                          NULL, &props, scratch_pool));
+  /* Grab some properties we need to know in order to figure out if anything
+     special needs to be done with this file. */
+  target_rev = loc->rev;
+  if (SVN_IS_VALID_REVNUM(target_rev))
+    {
+      SVN_ERR(svn_ra_get_file(ra_session, "", target_rev, NULL, NULL,
+                              &props, scratch_pool));
+    }
+  else
+    {
+      /* For HEAD, fetch the actual revision and use in subsequent calls. */
+      SVN_ERR(svn_ra_get_file(ra_session, "", SVN_INVALID_REVNUM, NULL,
+                              &target_rev, &props, scratch_pool));
+    }
 
   /* Push the props into change_file_prop(), to update the file_baton
    * with information. */
@@ -1274,8 +1265,16 @@ export_file(const char *from_url,
       SVN_ERR(change_file_prop(fb, propname, propval, scratch_pool));
     }
 
-  /* And now just use close_file() to do all the keyword and EOL
-   * work, and put the file into place. */
+  /* Step outside the editor-likeness for a moment, to open the file writer
+   * and to actually talk to the repository. */
+  SVN_ERR(open_working_file_writer(&fb->file_writer, fb, fb->pool,
+                                   scratch_pool));
+  stream = svn_wc__working_file_writer_get_stream(fb->file_writer);
+  SVN_ERR(svn_ra_get_file(ra_session, "", target_rev, stream, NULL, NULL,
+                          scratch_pool));
+  SVN_ERR(svn_stream_close(stream));
+
+  /* And now just use close_file() to put the file into place. */
   SVN_ERR(close_file(fb, NULL, scratch_pool));
 
   return SVN_NO_ERROR;

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/import.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/import.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/import.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/import.c Fri Jan 14 14:01:45 2022
@@ -609,7 +609,7 @@ import_dir(const svn_delta_editor_t *edi
  * EDIT_BATON.  LOCAL_ABSPATH can be a file or directory.
  *
  * Sets *UPDATED_REPOSITORY to TRUE when the repository was modified by
- * a successfull commit, otherwise to FALSE.
+ * a successful commit, otherwise to FALSE.
  *
  * DEPTH is the depth at which to import LOCAL_ABSPATH; it behaves as for
  * svn_client_import5().

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/info.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/info.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/info.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/info.c Fri Jan 14 14:01:45 2022
@@ -167,7 +167,8 @@ build_info_from_dirent(svn_client_info2_
 #define DIRENT_FIELDS (SVN_DIRENT_KIND        | \
                        SVN_DIRENT_CREATED_REV | \
                        SVN_DIRENT_TIME        | \
-                       SVN_DIRENT_LAST_AUTHOR)
+                       SVN_DIRENT_LAST_AUTHOR | \
+                       SVN_DIRENT_SIZE)
 
 
 /* Helper func for recursively fetching svn_dirent_t's from a remote
@@ -267,6 +268,7 @@ same_resource_in_head(svn_boolean_t *sam
                                     ctx, pool);
   if (err &&
       ((err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES) ||
+       (err->apr_err == SVN_ERR_FS_NOT_DIRECTORY) ||
        (err->apr_err == SVN_ERR_FS_NOT_FOUND)))
     {
       svn_error_clear(err);