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 [18/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/shelf.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/shelf.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/shelf.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/shelf.c Fri Jan 14 14:01:45 2022
@@ -38,6 +38,7 @@
 #include "svn_props.h"
 
 #include "client.h"
+#include "private/svn_client_shelf.h"
 #include "private/svn_client_private.h"
 #include "private/svn_wc_private.h"
 #include "private/svn_sorts_private.h"
@@ -100,7 +101,7 @@ shelf_name_from_filename(char **name,
 {
   size_t len = strlen(filename);
   static const char suffix[] = ".current";
-  int suffix_len = sizeof(suffix) - 1;
+  size_t suffix_len = sizeof(suffix) - 1;
 
   if (len > suffix_len && strcmp(filename + len - suffix_len, suffix) == 0)
     {
@@ -114,6 +115,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.
  */
@@ -128,7 +151,7 @@ shelf_version_files_dir_abspath(const ch
   char *filename;
 
   SVN_ERR(shelf_name_encode(&codename, shelf->name, result_pool));
-  filename = apr_psprintf(scratch_pool, "%s-%03d.d", codename, version);
+  filename = apr_psprintf(scratch_pool, "%s-%03d.wc", codename, version);
   *abspath = svn_dirent_join(shelf->shelves_dir, filename, result_pool);
   return SVN_NO_ERROR;
 }
@@ -154,86 +177,6 @@ shelf_version_create(svn_client__shelf_v
   return SVN_NO_ERROR;
 }
 
-/* Set *ABSPATH to the abspath of the metadata file for SHELF_VERSION
- * node at RELPATH, no matter whether it exists.
- */
-static svn_error_t *
-get_metadata_abspath(char **abspath,
-                     svn_client__shelf_version_t *shelf_version,
-                     const char *wc_relpath,
-                     apr_pool_t *result_pool,
-                     apr_pool_t *scratch_pool)
-{
-  wc_relpath = apr_psprintf(scratch_pool, "%s.meta", wc_relpath);
-  *abspath = svn_dirent_join(shelf_version->files_dir_abspath, wc_relpath,
-                             result_pool);
-  return SVN_NO_ERROR;
-}
-
-/* Set *ABSPATH to the abspath of the base text file for SHELF_VERSION
- * node at RELPATH, no matter whether it exists.
- */
-static svn_error_t *
-get_base_file_abspath(char **base_abspath,
-                      svn_client__shelf_version_t *shelf_version,
-                      const char *wc_relpath,
-                      apr_pool_t *result_pool,
-                      apr_pool_t *scratch_pool)
-{
-  wc_relpath = apr_psprintf(scratch_pool, "%s.base", wc_relpath);
-  *base_abspath = svn_dirent_join(shelf_version->files_dir_abspath, wc_relpath,
-                                  result_pool);
-  return SVN_NO_ERROR;
-}
-
-/* Set *ABSPATH to the abspath of the working text file for SHELF_VERSION
- * node at RELPATH, no matter whether it exists.
- */
-static svn_error_t *
-get_working_file_abspath(char **work_abspath,
-                         svn_client__shelf_version_t *shelf_version,
-                         const char *wc_relpath,
-                         apr_pool_t *result_pool,
-                         apr_pool_t *scratch_pool)
-{
-  wc_relpath = apr_psprintf(scratch_pool, "%s.work", wc_relpath);
-  *work_abspath = svn_dirent_join(shelf_version->files_dir_abspath, wc_relpath,
-                                  result_pool);
-  return SVN_NO_ERROR;
-}
-
-/* Set *ABSPATH to the abspath of the base props file for SHELF_VERSION
- * node at RELPATH, no matter whether it exists.
- */
-static svn_error_t *
-get_base_props_abspath(char **base_abspath,
-                       svn_client__shelf_version_t *shelf_version,
-                       const char *wc_relpath,
-                       apr_pool_t *result_pool,
-                       apr_pool_t *scratch_pool)
-{
-  wc_relpath = apr_psprintf(scratch_pool, "%s.base-props", wc_relpath);
-  *base_abspath = svn_dirent_join(shelf_version->files_dir_abspath, wc_relpath,
-                                  result_pool);
-  return SVN_NO_ERROR;
-}
-
-/* Set *ABSPATH to the abspath of the working props file for SHELF_VERSION
- * node at RELPATH, no matter whether it exists.
- */
-static svn_error_t *
-get_working_props_abspath(char **work_abspath,
-                          svn_client__shelf_version_t *shelf_version,
-                          const char *wc_relpath,
-                          apr_pool_t *result_pool,
-                          apr_pool_t *scratch_pool)
-{
-  wc_relpath = apr_psprintf(scratch_pool, "%s.work-props", wc_relpath);
-  *work_abspath = svn_dirent_join(shelf_version->files_dir_abspath, wc_relpath,
-                                  result_pool);
-  return SVN_NO_ERROR;
-}
-
 /* Delete the storage for SHELF:VERSION. */
 static svn_error_t *
 shelf_version_delete(svn_client__shelf_t *shelf,
@@ -416,150 +359,19 @@ shelf_write_current(svn_client__shelf_t
 /*-------------------------------------------------------------------------*/
 /* Status Reporting */
 
-/* Create a status struct with all fields initialized to valid values
- * representing 'uninteresting' or 'unknown' status.
- */
-static svn_wc_status3_t *
-status_create(apr_pool_t *result_pool)
-{
-  svn_wc_status3_t *s = apr_pcalloc(result_pool, sizeof(*s));
-
-  s->filesize = SVN_INVALID_FILESIZE;
-  s->versioned = TRUE;
-  s->node_status = svn_wc_status_none;
-  s->text_status = svn_wc_status_none;
-  s->prop_status = svn_wc_status_none;
-  s->revision = SVN_INVALID_REVNUM;
-  s->changed_rev = SVN_INVALID_REVNUM;
-  s->repos_node_status = svn_wc_status_none;
-  s->repos_text_status = svn_wc_status_none;
-  s->repos_prop_status = svn_wc_status_none;
-  s->ood_changed_rev = SVN_INVALID_REVNUM;
-  return s;
-}
-
-/* Convert from svn_node_kind_t to a single character representation. */
-static char
-kind_to_char(svn_node_kind_t kind)
-{
-  return (kind == svn_node_dir ? 'd'
-            : kind == svn_node_file ? 'f'
-                : kind == svn_node_symlink ? 'l'
-                    : '?');
-}
-
-/* Convert to svn_node_kind_t from a single character representation. */
-static svn_node_kind_t
-char_to_kind(char kind)
-{
-  return (kind == 'd' ? svn_node_dir
-            : kind == 'f' ? svn_node_file
-                : kind == 'l' ? svn_node_symlink
-                    : svn_node_unknown);
-}
-
-/* Return the single character representation of STATUS.
- * (Similar to subversion/svn/status.c:generate_status_code()
- * and subversion/tests/libsvn_client/client-test.c:status_to_char().) */
-static char
-status_to_char(enum svn_wc_status_kind status)
-{
-  switch (status)
-    {
-    case svn_wc_status_none:        return '.';
-    case svn_wc_status_unversioned: return '?';
-    case svn_wc_status_normal:      return ' ';
-    case svn_wc_status_added:       return 'A';
-    case svn_wc_status_missing:     return '!';
-    case svn_wc_status_deleted:     return 'D';
-    case svn_wc_status_replaced:    return 'R';
-    case svn_wc_status_modified:    return 'M';
-    case svn_wc_status_merged:      return 'G';
-    case svn_wc_status_conflicted:  return 'C';
-    case svn_wc_status_ignored:     return 'I';
-    case svn_wc_status_obstructed:  return '~';
-    case svn_wc_status_external:    return 'X';
-    case svn_wc_status_incomplete:  return ':';
-    default:                        return '*';
-    }
-}
-
-static enum svn_wc_status_kind
-char_to_status(char status)
-{
-  switch (status)
-    {
-    case '.': return svn_wc_status_none;
-    case '?': return svn_wc_status_unversioned;
-    case ' ': return svn_wc_status_normal;
-    case 'A': return svn_wc_status_added;
-    case '!': return svn_wc_status_missing;
-    case 'D': return svn_wc_status_deleted;
-    case 'R': return svn_wc_status_replaced;
-    case 'M': return svn_wc_status_modified;
-    case 'G': return svn_wc_status_merged;
-    case 'C': return svn_wc_status_conflicted;
-    case 'I': return svn_wc_status_ignored;
-    case '~': return svn_wc_status_obstructed;
-    case 'X': return svn_wc_status_external;
-    case ':': return svn_wc_status_incomplete;
-    default:  return (enum svn_wc_status_kind)0;
-    }
-}
-
-/* Write a serial representation of (some fields of) STATUS to STREAM.
+/* Adjust a status STATUS_IN obtained from the shelf storage WC, to add
+ * shelf-related metadata:
+ *  - changelist: 'svn:shelf:SHELFNAME'
  */
 static svn_error_t *
-wc_status_serialize(svn_stream_t *stream,
-                    const svn_wc_status3_t *status,
-                    apr_pool_t *scratch_pool)
-{
-  SVN_ERR(svn_stream_printf(stream, scratch_pool, "%c %c%c%c %ld",
-                            kind_to_char(status->kind),
-                            status_to_char(status->node_status),
-                            status_to_char(status->text_status),
-                            status_to_char(status->prop_status),
-                            status->revision));
-  return SVN_NO_ERROR;
-}
-
-/* Read a serial representation of (some fields of) STATUS from STREAM.
- */
-static svn_error_t *
-wc_status_unserialize(svn_wc_status3_t *status,
-                      svn_stream_t *stream,
-                      apr_pool_t *result_pool)
-{
-  svn_stringbuf_t *sb;
-  char *string;
-
-  SVN_ERR(svn_stringbuf_from_stream(&sb, stream, 100, result_pool));
-  string = sb->data;
-  status->kind = char_to_kind(string[0]);
-  status->node_status = char_to_status(string[2]);
-  status->text_status = char_to_status(string[3]);
-  status->prop_status = char_to_status(string[4]);
-  sscanf(string + 6, "%ld", &status->revision);
-  return SVN_NO_ERROR;
-}
-
-/* Write status to shelf storage.
- */
-static svn_error_t *
-status_write(svn_client__shelf_version_t *shelf_version,
-             const char *relpath,
-             const svn_wc_status3_t *status,
-             apr_pool_t *scratch_pool)
-{
-  char *file_abspath;
-  svn_stream_t *stream;
-
-  SVN_ERR(get_metadata_abspath(&file_abspath, shelf_version, relpath,
-                               scratch_pool, scratch_pool));
-  SVN_ERR(svn_stream_open_writable(&stream, file_abspath,
-                                   scratch_pool, scratch_pool));
-  SVN_ERR(wc_status_serialize(stream, status, scratch_pool));
-  SVN_ERR(svn_stream_close(stream));
+status_augment(svn_wc_status3_t **status_p,
+               const svn_wc_status3_t *status_in,
+               svn_client__shelf_version_t *shelf_version,
+               apr_pool_t *result_pool)
+{
+  *status_p = svn_wc_dup_status3(status_in, result_pool);
+  (*status_p)->changelist = apr_psprintf(result_pool, "svn:shelf:%s",
+                                         shelf_version->shelf->name);
   return SVN_NO_ERROR;
 }
 
@@ -572,68 +384,57 @@ status_read(svn_wc_status3_t **status,
             apr_pool_t *result_pool,
             apr_pool_t *scratch_pool)
 {
-  svn_wc_status3_t *s = status_create(result_pool);
-  char *file_abspath;
-  svn_stream_t *stream;
-
-  SVN_ERR(get_metadata_abspath(&file_abspath, shelf_version, relpath,
-                               scratch_pool, scratch_pool));
-  SVN_ERR(svn_stream_open_readonly(&stream, file_abspath,
-                                   scratch_pool, scratch_pool));
-  SVN_ERR(wc_status_unserialize(s, stream, result_pool));
-  SVN_ERR(svn_stream_close(stream));
-
-  s->changelist = apr_psprintf(result_pool, "svn:shelf:%s",
-                               shelf_version->shelf->name);
-  *status = s;
+  svn_client_ctx_t *ctx = shelf_version->shelf->ctx;
+  char *abspath
+    = svn_dirent_join(shelf_version->files_dir_abspath, relpath,
+                      scratch_pool);
+
+  SVN_ERR(svn_wc_status3(status, ctx->wc_ctx, abspath,
+                         result_pool, scratch_pool));
+  SVN_ERR(status_augment(status, *status, shelf_version, result_pool));
   return SVN_NO_ERROR;
 }
 
 /* A visitor function type for use with shelf_status_walk().
  * The same as svn_wc_status_func4_t except relpath instead of abspath.
- * Only some fields in STATUS are available.
  */
 typedef svn_error_t *(*shelf_status_visitor_t)(void *baton,
                                                const char *relpath,
-                                               svn_wc_status3_t *status,
+                                               const svn_wc_status3_t *status,
                                                apr_pool_t *scratch_pool);
 
 /* Baton for shelved_files_walk_visitor(). */
 struct shelf_status_baton_t
 {
   svn_client__shelf_version_t *shelf_version;
-  const char *top_relpath;
-  const char *walk_root_abspath;
   shelf_status_visitor_t walk_func;
   void *walk_baton;
 };
 
-/* Call BATON->walk_func(BATON->walk_baton, relpath, ...) for the shelved
- * 'binary' file stored at ABSPATH.
- * Implements svn_io_walk_func_t. */
+/* Convert a svn_wc_status_func4_t callback invocation to call a
+ * shelf_status_visitor_t callback.
+ *
+ * Call BATON->walk_func(BATON->walk_baton, relpath, ...) for the shelved
+ * storage path ABSPATH, converting ABSPATH to a WC-relative path, and
+ * augmenting the STATUS.
+ *
+ * The opposite of wc_status_visitor().
+ *
+ * Implements svn_wc_status_func4_t. */
 static svn_error_t *
 shelf_status_visitor(void *baton,
                      const char *abspath,
-                     const apr_finfo_t *finfo,
+                     const svn_wc_status3_t *status,
                      apr_pool_t *scratch_pool)
 {
   struct shelf_status_baton_t *b = baton;
   const char *relpath;
+  svn_wc_status3_t *new_status;
 
-  relpath = svn_dirent_skip_ancestor(b->walk_root_abspath, abspath);
-  if (finfo->filetype == APR_REG
-      && (strlen(relpath) >= 5 && strcmp(relpath+strlen(relpath)-5, ".meta") == 0))
-    {
-      svn_wc_status3_t *s;
-
-      relpath = apr_pstrndup(scratch_pool, relpath, strlen(relpath) - 5);
-      if (!svn_relpath_skip_ancestor(b->top_relpath, relpath))
-        return SVN_NO_ERROR;
-
-      SVN_ERR(status_read(&s, b->shelf_version, relpath,
-                          scratch_pool, scratch_pool));
-      SVN_ERR(b->walk_func(b->walk_baton, relpath, s, scratch_pool));
-    }
+  relpath = svn_dirent_skip_ancestor(b->shelf_version->files_dir_abspath,
+                                     abspath);
+  SVN_ERR(status_augment(&new_status, status, b->shelf_version, scratch_pool));
+  SVN_ERR(b->walk_func(b->walk_baton, relpath, new_status, scratch_pool));
   return SVN_NO_ERROR;
 }
 
@@ -647,24 +448,16 @@ shelf_status_visit_path(svn_client__shel
                         void *walk_baton,
                         apr_pool_t *scratch_pool)
 {
-  struct shelf_status_baton_t baton;
-  char *abspath;
-  apr_finfo_t finfo;
+  svn_wc_status3_t *status;
 
-  baton.shelf_version = shelf_version;
-  baton.top_relpath = wc_relpath;
-  baton.walk_root_abspath = shelf_version->files_dir_abspath;
-  baton.walk_func = walk_func;
-  baton.walk_baton = walk_baton;
-  SVN_ERR(get_metadata_abspath(&abspath, shelf_version, wc_relpath,
-                               scratch_pool, scratch_pool));
-  SVN_ERR(svn_io_stat(&finfo, abspath, APR_FINFO_TYPE, scratch_pool));
-  SVN_ERR(shelf_status_visitor(&baton, abspath, &finfo, scratch_pool));
+  SVN_ERR(status_read(&status, shelf_version, wc_relpath,
+                      scratch_pool, scratch_pool));
+  SVN_ERR(walk_func(walk_baton, wc_relpath, status, scratch_pool));
   return SVN_NO_ERROR;
 }
 
 /* Report the shelved status of all the shelved paths in SHELF_VERSION
- * via WALK_FUNC(WALK_BATON, ...).
+ * at and under WC_RELPATH, via WALK_FUNC(WALK_BATON, ...).
  */
 static svn_error_t *
 shelf_status_walk(svn_client__shelf_version_t *shelf_version,
@@ -673,17 +466,25 @@ shelf_status_walk(svn_client__shelf_vers
                   void *walk_baton,
                   apr_pool_t *scratch_pool)
 {
+  svn_client_ctx_t *ctx = shelf_version->shelf->ctx;
+  char *walk_root_abspath
+    = svn_dirent_join(shelf_version->files_dir_abspath, wc_relpath,
+                      scratch_pool);
   struct shelf_status_baton_t baton;
   svn_error_t *err;
 
   baton.shelf_version = shelf_version;
-  baton.top_relpath = wc_relpath;
-  baton.walk_root_abspath = shelf_version->files_dir_abspath;
   baton.walk_func = walk_func;
   baton.walk_baton = walk_baton;
-  err = svn_io_dir_walk2(baton.walk_root_abspath, 0 /*wanted*/,
-                         shelf_status_visitor, &baton,
-                         scratch_pool);
+  err = svn_wc_walk_status(ctx->wc_ctx, walk_root_abspath,
+                           svn_depth_infinity,
+                           FALSE /*get_all*/,
+                           TRUE /*no_ignore*/,
+                           FALSE /*ignore_text_mods*/,
+                           NULL /*ignore_patterns: use the defaults*/,
+                           shelf_status_visitor, &baton,
+                           NULL, NULL, /*cancellation*/
+                           scratch_pool);
   if (err && APR_STATUS_IS_ENOENT(err->apr_err))
     svn_error_clear(err);
   else
@@ -692,6 +493,7 @@ shelf_status_walk(svn_client__shelf_vers
   return SVN_NO_ERROR;
 }
 
+/* Baton for wc_status_visitor(). */
 typedef struct wc_status_baton_t
 {
   svn_client__shelf_version_t *shelf_version;
@@ -699,11 +501,20 @@ typedef struct wc_status_baton_t
   void *walk_baton;
 } wc_status_baton_t;
 
+/* Convert a shelf_status_visitor_t callback invocation to call a
+ * svn_wc_status_func4_t callback.
+ *
+ * Call BATON->walk_func(BATON->walk_baton, abspath, ...) for the WC-
+ * relative path RELPATH, converting RELPATH to an abspath in the user's WC.
+ *
+ * The opposite of shelf_status_visitor().
+ *
+ * Implements shelf_status_visitor_t. */
 static svn_error_t *
 wc_status_visitor(void *baton,
-                      const char *relpath,
-                      svn_wc_status3_t *status,
-                      apr_pool_t *scratch_pool)
+                  const char *relpath,
+                  const svn_wc_status3_t *status,
+                  apr_pool_t *scratch_pool)
 {
   struct wc_status_baton_t *b = baton;
   svn_client__shelf_t *shelf = b->shelf_version->shelf;
@@ -734,431 +545,6 @@ svn_client__shelf_version_status_walk(sv
 /*-------------------------------------------------------------------------*/
 /* Shelf Storage */
 
-/* A baton for use with write_changes_visitor(). */
-typedef struct write_changes_baton_t {
-  const char *wc_root_abspath;
-  svn_client__shelf_version_t *shelf_version;
-  svn_client_ctx_t *ctx;
-  svn_boolean_t any_shelved;  /* were any paths successfully shelved? */
-  svn_client_status_func_t was_shelved_func;
-  void *was_shelved_baton;
-  svn_client_status_func_t was_not_shelved_func;
-  void *was_not_shelved_baton;
-  apr_pool_t *pool;  /* pool for data in 'unshelvable', etc. */
-} write_changes_baton_t;
-
-/*  */
-static svn_error_t *
-notify_shelved(write_changes_baton_t *wb,
-               const char *wc_relpath,
-               const char *local_abspath,
-               const svn_wc_status3_t *wc_status,
-               apr_pool_t *scratch_pool)
-{
-  if (wb->was_shelved_func)
-    {
-      svn_client_status_t *cst;
-
-      SVN_ERR(svn_client__create_status(&cst, wb->ctx->wc_ctx, local_abspath,
-                                        wc_status,
-                                        scratch_pool, scratch_pool));
-      SVN_ERR(wb->was_shelved_func(wb->was_shelved_baton,
-                                   wc_relpath, cst, scratch_pool));
-    }
-
-  wb->any_shelved = TRUE;
-  return SVN_NO_ERROR;
-}
-
-/*  */
-static svn_error_t *
-notify_not_shelved(write_changes_baton_t *wb,
-                   const char *wc_relpath,
-                   const char *local_abspath,
-                   const svn_wc_status3_t *wc_status,
-                   apr_pool_t *scratch_pool)
-{
-  if (wb->was_not_shelved_func)
-    {
-      svn_client_status_t *cst;
-
-      SVN_ERR(svn_client__create_status(&cst, wb->ctx->wc_ctx, local_abspath,
-                                        wc_status,
-                                        scratch_pool, scratch_pool));
-      SVN_ERR(wb->was_not_shelved_func(wb->was_not_shelved_baton,
-                                       wc_relpath, cst, scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Read BASE_PROPS and WORK_PROPS from the WC, setting each to null if
- * the node has no base or working version (respectively).
- */
-static svn_error_t *
-read_props_from_wc(apr_hash_t **base_props,
-                   apr_hash_t **work_props,
-                   enum svn_wc_status_kind node_status,
-                   const char *from_wc_abspath,
-                   svn_client_ctx_t *ctx,
-                   apr_pool_t *result_pool,
-                   apr_pool_t *scratch_pool)
-{
-  if (node_status != svn_wc_status_added)
-    SVN_ERR(svn_wc_get_pristine_props(base_props, ctx->wc_ctx, from_wc_abspath,
-                                      result_pool, scratch_pool));
-  else
-    *base_props = NULL;
-  if (node_status != svn_wc_status_deleted)
-    SVN_ERR(svn_wc_prop_list2(work_props, ctx->wc_ctx, from_wc_abspath,
-                              result_pool, scratch_pool));
-  else
-    *work_props = NULL;
-  return SVN_NO_ERROR;
-}
-
-/* Write BASE_PROPS and WORK_PROPS to storage in SHELF_VERSION:WC_RELPATH.
- */
-static svn_error_t *
-write_props_to_shelf(svn_client__shelf_version_t *shelf_version,
-                     const char *wc_relpath,
-                     apr_hash_t *base_props,
-                     apr_hash_t *work_props,
-                     apr_pool_t *scratch_pool)
-{
-  char *stored_props_abspath;
-  svn_stream_t *stream;
-
-  if (base_props)
-    {
-      SVN_ERR(get_base_props_abspath(&stored_props_abspath,
-                                     shelf_version, wc_relpath,
-                                     scratch_pool, scratch_pool));
-      SVN_ERR(svn_stream_open_writable(&stream, stored_props_abspath,
-                                       scratch_pool, scratch_pool));
-      SVN_ERR(svn_hash_write2(base_props, stream, NULL, scratch_pool));
-      SVN_ERR(svn_stream_close(stream));
-    }
-
-  if (work_props)
-    {
-      SVN_ERR(get_working_props_abspath(&stored_props_abspath,
-                                        shelf_version, wc_relpath,
-                                        scratch_pool, scratch_pool));
-      SVN_ERR(svn_stream_open_writable(&stream, stored_props_abspath,
-                                       scratch_pool, scratch_pool));
-      SVN_ERR(svn_hash_write2(work_props, stream, NULL, scratch_pool));
-      SVN_ERR(svn_stream_close(stream));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Read BASE_PROPS and WORK_PROPS from storage in SHELF_VERSION:WC_RELPATH.
- */
-static svn_error_t *
-read_props_from_shelf(apr_hash_t **base_props,
-                      apr_hash_t **work_props,
-                      enum svn_wc_status_kind node_status,
-                      svn_client__shelf_version_t *shelf_version,
-                      const char *wc_relpath,
-                      apr_pool_t *result_pool,
-                      apr_pool_t *scratch_pool)
-{
-  char *stored_props_abspath;
-  svn_stream_t *stream;
-
-  if (node_status != svn_wc_status_added)
-    {
-      *base_props = apr_hash_make(result_pool);
-      SVN_ERR(get_base_props_abspath(&stored_props_abspath,
-                                     shelf_version, wc_relpath,
-                                     scratch_pool, scratch_pool));
-      SVN_ERR(svn_stream_open_readonly(&stream, stored_props_abspath,
-                                       scratch_pool, scratch_pool));
-      SVN_ERR(svn_hash_read2(*base_props, stream, NULL, scratch_pool));
-      SVN_ERR(svn_stream_close(stream));
-    }
-  else
-    *base_props = NULL;
-
-  if (node_status != svn_wc_status_deleted)
-    {
-      *work_props = apr_hash_make(result_pool);
-      SVN_ERR(get_working_props_abspath(&stored_props_abspath,
-                                        shelf_version, wc_relpath,
-                                        scratch_pool, scratch_pool));
-      SVN_ERR(svn_stream_open_readonly(&stream, stored_props_abspath,
-                                       scratch_pool, scratch_pool));
-      SVN_ERR(svn_hash_read2(*work_props, stream, NULL, scratch_pool));
-      SVN_ERR(svn_stream_close(stream));
-    }
-  else
-    *work_props = NULL;
-
-  return SVN_NO_ERROR;
-}
-
-/* Store metadata for any node, and base and working files if it's a file.
- *
- * Copy the WC base and working files at FROM_WC_ABSPATH to the storage
- * area in SHELF_VERSION.
- */
-static svn_error_t *
-store_file(const char *from_wc_abspath,
-           const char *wc_relpath,
-           svn_client__shelf_version_t *shelf_version,
-           const svn_wc_status3_t *status,
-           svn_client_ctx_t *ctx,
-           apr_pool_t *scratch_pool)
-{
-  char *stored_abspath;
-  apr_hash_t *base_props, *work_props;
-
-  SVN_ERR(get_working_file_abspath(&stored_abspath,
-                                   shelf_version, wc_relpath,
-                                   scratch_pool, scratch_pool));
-  SVN_ERR(svn_io_make_dir_recursively(svn_dirent_dirname(stored_abspath,
-                                                         scratch_pool),
-                                      scratch_pool));
-  SVN_ERR(status_write(shelf_version, wc_relpath,
-                       status, scratch_pool));
-
-  /* properties */
-  SVN_ERR(read_props_from_wc(&base_props, &work_props,
-                             status->node_status,
-                             from_wc_abspath, ctx,
-                             scratch_pool, scratch_pool));
-  SVN_ERR(write_props_to_shelf(shelf_version, wc_relpath,
-                               base_props, work_props,
-                               scratch_pool));
-
-  /* file text */
-  if (status->kind == svn_node_file)
-    {
-      svn_stream_t *wc_base_stream;
-      svn_node_kind_t work_kind;
-
-      /* Copy the base file (copy-from base, if copied/moved), if present */
-      SVN_ERR(svn_wc_get_pristine_contents2(&wc_base_stream,
-                                            ctx->wc_ctx, from_wc_abspath,
-                                            scratch_pool, scratch_pool));
-      if (wc_base_stream)
-        {
-          char *stored_base_abspath;
-          svn_stream_t *stored_base_stream;
-
-          SVN_ERR(get_base_file_abspath(&stored_base_abspath,
-                                        shelf_version, wc_relpath,
-                                        scratch_pool, scratch_pool));
-          SVN_ERR(svn_stream_open_writable(&stored_base_stream,
-                                           stored_base_abspath,
-                                           scratch_pool, scratch_pool));
-          SVN_ERR(svn_stream_copy3(wc_base_stream, stored_base_stream,
-                                   NULL, NULL, scratch_pool));
-        }
-
-      /* Copy the working file, if present */
-      SVN_ERR(svn_io_check_path(from_wc_abspath, &work_kind, scratch_pool));
-      if (work_kind == svn_node_file)
-        {
-          SVN_ERR(svn_io_copy_file(from_wc_abspath, stored_abspath,
-                                   TRUE /*copy_perms*/, scratch_pool));
-        }
-    }
-  return SVN_NO_ERROR;
-}
-
-/* An implementation of svn_wc_status_func4_t. */
-static svn_error_t *
-write_changes_visitor(void *baton,
-                      const char *local_abspath,
-                      const svn_wc_status3_t *status,
-                      apr_pool_t *scratch_pool)
-{
-  write_changes_baton_t *wb = baton;
-  const char *wc_relpath = svn_dirent_skip_ancestor(wb->wc_root_abspath,
-                                                    local_abspath);
-
-  /* Catch any conflict, even a tree conflict on a path that has
-     node-status 'unversioned'. */
-  if (status->conflicted)
-    {
-      SVN_ERR(notify_not_shelved(wb, wc_relpath, local_abspath,
-                                 status, scratch_pool));
-    }
-  else switch (status->node_status)
-    {
-      case svn_wc_status_deleted:
-      case svn_wc_status_added:
-      case svn_wc_status_replaced:
-        if (status->kind != svn_node_file
-            || status->copied)
-          {
-            SVN_ERR(notify_not_shelved(wb, wc_relpath, local_abspath,
-                                       status, scratch_pool));
-            break;
-          }
-        /* fall through */
-      case svn_wc_status_modified:
-      {
-        /* Store metadata, and base and working versions if it's a file */
-        SVN_ERR(store_file(local_abspath, wc_relpath, wb->shelf_version,
-                           status, wb->ctx, scratch_pool));
-        SVN_ERR(notify_shelved(wb, wc_relpath, local_abspath,
-                               status, scratch_pool));
-        break;
-      }
-
-      case svn_wc_status_incomplete:
-        if ((status->text_status != svn_wc_status_normal
-             && status->text_status != svn_wc_status_none)
-            || (status->prop_status != svn_wc_status_normal
-                && status->prop_status != svn_wc_status_none))
-          {
-            /* Incomplete, but local modifications */
-            SVN_ERR(notify_not_shelved(wb, wc_relpath, local_abspath,
-                                       status, scratch_pool));
-          }
-        break;
-
-      case svn_wc_status_conflicted:
-      case svn_wc_status_missing:
-      case svn_wc_status_obstructed:
-        SVN_ERR(notify_not_shelved(wb, wc_relpath, local_abspath,
-                                   status, scratch_pool));
-        break;
-
-      case svn_wc_status_normal:
-      case svn_wc_status_ignored:
-      case svn_wc_status_none:
-      case svn_wc_status_external:
-      case svn_wc_status_unversioned:
-      default:
-        break;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* A baton for use with changelist_filter_func(). */
-struct changelist_filter_baton_t {
-  apr_hash_t *changelist_hash;
-  svn_wc_status_func4_t status_func;
-  void *status_baton;
-};
-
-/* Filter out paths that are not in the requested changelist(s).
- * Implements svn_wc_status_func4_t. */
-static svn_error_t *
-changelist_filter_func(void *baton,
-                       const char *local_abspath,
-                       const svn_wc_status3_t *status,
-                       apr_pool_t *scratch_pool)
-{
-  struct changelist_filter_baton_t *b = baton;
-
-  if (b->changelist_hash
-      && (! status->changelist
-          || ! svn_hash_gets(b->changelist_hash, status->changelist)))
-    {
-      return SVN_NO_ERROR;
-    }
-
-  SVN_ERR(b->status_func(b->status_baton, local_abspath, status,
-                         scratch_pool));
-  return SVN_NO_ERROR;
-}
-
-/*
- * Walk the WC tree(s) rooted at PATHS, to depth DEPTH, omitting paths that
- * are not in one of the CHANGELISTS (if not null).
- *
- * Call STATUS_FUNC(STATUS_BATON, ...) for each visited path.
- *
- * PATHS are absolute, or relative to CWD.
- */
-static svn_error_t *
-wc_walk_status_multi(const apr_array_header_t *paths,
-                     svn_depth_t depth,
-                     const apr_array_header_t *changelists,
-                     svn_wc_status_func4_t status_func,
-                     void *status_baton,
-                     svn_client_ctx_t *ctx,
-                     apr_pool_t *scratch_pool)
-{
-  struct changelist_filter_baton_t cb = {0};
-  int i;
-
-  if (changelists && changelists->nelts)
-    SVN_ERR(svn_hash_from_cstring_keys(&cb.changelist_hash,
-                                       changelists, scratch_pool));
-  cb.status_func = status_func;
-  cb.status_baton = status_baton;
-
-  for (i = 0; i < paths->nelts; i++)
-    {
-      const char *path = APR_ARRAY_IDX(paths, i, const char *);
-
-      if (svn_path_is_url(path))
-        return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
-                                 _("'%s' is not a local path"), path);
-      SVN_ERR(svn_dirent_get_absolute(&path, path, scratch_pool));
-
-      SVN_ERR(svn_wc_walk_status(ctx->wc_ctx, path, depth,
-                                 FALSE /*get_all*/, FALSE /*no_ignore*/,
-                                 FALSE /*ignore_text_mods*/,
-                                 NULL /*ignore_patterns*/,
-                                 changelist_filter_func, &cb,
-                                 ctx->cancel_func, ctx->cancel_baton,
-                                 scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/** Write local changes to the shelf storage.
- *
- * @a paths, @a depth, @a changelists: The selection of local paths to diff.
- *
- * @a paths are relative to CWD (or absolute).
- */
-static svn_error_t *
-shelf_write_changes(svn_boolean_t *any_shelved,
-                    svn_client__shelf_version_t *shelf_version,
-                    const apr_array_header_t *paths,
-                    svn_depth_t depth,
-                    const apr_array_header_t *changelists,
-                    svn_client_status_func_t shelved_func,
-                    void *shelved_baton,
-                    svn_client_status_func_t not_shelved_func,
-                    void *not_shelved_baton,
-                    const char *wc_root_abspath,
-                    svn_client_ctx_t *ctx,
-                    apr_pool_t *result_pool,
-                    apr_pool_t *scratch_pool)
-{
-  write_changes_baton_t wb = { 0 };
-
-  wb.wc_root_abspath = wc_root_abspath;
-  wb.shelf_version = shelf_version;
-  wb.ctx = ctx;
-  wb.any_shelved = FALSE;
-  wb.was_shelved_func = shelved_func;
-  wb.was_shelved_baton = shelved_baton;
-  wb.was_not_shelved_func = not_shelved_func;
-  wb.was_not_shelved_baton = not_shelved_baton;
-  wb.pool = result_pool;
-
-  /* Walk the WC */
-  SVN_ERR(wc_walk_status_multi(paths, depth, changelists,
-                               write_changes_visitor, &wb,
-                               ctx, scratch_pool));
-
-  *any_shelved = wb.any_shelved;
-  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.
  */
@@ -1175,9 +561,8 @@ shelf_construct(svn_client__shelf_t **sh
   SVN_ERR(svn_client_get_wc_root(&shelf->wc_root_abspath,
                                  local_abspath, ctx,
                                  result_pool, result_pool));
-  SVN_ERR(svn_wc__get_shelves_dir(&shelves_dir,
-                                  ctx->wc_ctx, local_abspath,
-                                  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;
@@ -1273,7 +658,6 @@ svn_client__shelf_delete(const char *nam
 struct paths_changed_walk_baton_t
 {
   apr_hash_t *paths_hash;
-  svn_boolean_t as_abspath;
   const char *wc_root_abspath;
   apr_pool_t *pool;
 };
@@ -1283,14 +667,12 @@ struct paths_changed_walk_baton_t
 static svn_error_t *
 paths_changed_visitor(void *baton,
                       const char *relpath,
-                      svn_wc_status3_t *s,
+                      const svn_wc_status3_t *s,
                       apr_pool_t *scratch_pool)
 {
   struct paths_changed_walk_baton_t *b = baton;
 
-  relpath = (b->as_abspath
-             ? svn_dirent_join(b->wc_root_abspath, relpath, b->pool)
-             : apr_pstrdup(b->pool, relpath));
+  relpath = apr_pstrdup(b->pool, relpath);
   svn_hash_sets(b->paths_hash, relpath, relpath);
   return SVN_NO_ERROR;
 }
@@ -1302,7 +684,6 @@ static svn_error_t *
 shelf_paths_changed(apr_hash_t **paths_hash_p,
                     apr_array_header_t **paths_array_p,
                     svn_client__shelf_version_t *shelf_version,
-                    svn_boolean_t as_abspath,
                     apr_pool_t *result_pool,
                     apr_pool_t *scratch_pool)
 {
@@ -1311,7 +692,6 @@ shelf_paths_changed(apr_hash_t **paths_h
   struct paths_changed_walk_baton_t baton;
 
   baton.paths_hash = paths_hash;
-  baton.as_abspath = as_abspath;
   baton.wc_root_abspath = shelf->wc_root_abspath;
   baton.pool = result_pool;
   SVN_ERR(shelf_status_walk(shelf_version, "",
@@ -1333,546 +713,243 @@ svn_client__shelf_paths_changed(apr_hash
                                apr_pool_t *scratch_pool)
 {
   SVN_ERR(shelf_paths_changed(affected_paths, NULL, shelf_version,
-                              FALSE /*as_abspath*/,
                               result_pool, scratch_pool));
   return SVN_NO_ERROR;
 }
 
-/* Send a notification */
-static svn_error_t *
-send_notification(const char *local_abspath,
-                  svn_wc_notify_action_t action,
-                  svn_node_kind_t kind,
-                  svn_wc_notify_state_t content_state,
-                  svn_wc_notify_state_t prop_state,
-                  svn_wc_notify_func2_t notify_func,
-                  void *notify_baton,
-                  apr_pool_t *scratch_pool)
-{
-  if (notify_func)
-    {
-      svn_wc_notify_t *notify
-        = svn_wc_create_notify(local_abspath, action, scratch_pool);
-
-      notify->kind = kind;
-      notify->content_state = content_state;
-      notify->prop_state = prop_state;
-      notify_func(notify_baton, notify, scratch_pool);
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Merge a shelved change into WC_ABSPATH.
- */
-static svn_error_t *
-wc_file_merge(const char *wc_abspath,
-              const char *left_file,
-              const char *right_file,
-              /*const*/ apr_hash_t *left_props,
-              /*const*/ apr_hash_t *right_props,
-              svn_client_ctx_t *ctx,
-              apr_pool_t *scratch_pool)
-{
-  svn_wc_notify_state_t property_state;
-  svn_boolean_t has_local_mods;
-  enum svn_wc_merge_outcome_t content_outcome;
-  const char *target_label, *left_label, *right_label;
-  apr_array_header_t *prop_changes;
-
-  /* xgettext: the '.working', '.merge-left' and '.merge-right' strings
-     are used to tag onto a file name in case of a merge conflict */
-  target_label = apr_psprintf(scratch_pool, _(".working"));
-  left_label = apr_psprintf(scratch_pool, _(".merge-left"));
-  right_label = apr_psprintf(scratch_pool, _(".merge-right"));
-
-  SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props, scratch_pool));
-  SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, ctx->wc_ctx,
-                                  wc_abspath, FALSE, scratch_pool));
-
-  /* Do property merge and text merge in one step so that keyword expansion
-     takes into account the new property values. */
-  SVN_WC__CALL_WITH_WRITE_LOCK(
-    svn_wc_merge5(&content_outcome, &property_state, ctx->wc_ctx,
-                  left_file, right_file, wc_abspath,
-                  left_label, right_label, target_label,
-                  NULL, NULL, /*left, right conflict-versions*/
-                  FALSE /*dry_run*/, NULL /*diff3_cmd*/,
-                  NULL /*merge_options*/,
-                  left_props, prop_changes,
-                  NULL, NULL,
-                  ctx->cancel_func, ctx->cancel_baton,
-                  scratch_pool),
-    ctx->wc_ctx, wc_abspath,
-    FALSE /*lock_anchor*/, scratch_pool);
-
-  return SVN_NO_ERROR;
-}
-
-/* Merge a shelved change (of properties) into the dir at WC_ABSPATH.
- */
-static svn_error_t *
-wc_dir_props_merge(const char *wc_abspath,
-                   /*const*/ apr_hash_t *left_props,
-                   /*const*/ apr_hash_t *right_props,
-                   svn_client_ctx_t *ctx,
-                   apr_pool_t *result_pool,
-                   apr_pool_t *scratch_pool)
-{
-  apr_array_header_t *prop_changes;
-  svn_wc_notify_state_t property_state;
-
-  SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props, scratch_pool));
-  SVN_WC__CALL_WITH_WRITE_LOCK(
-    svn_wc_merge_props3(&property_state, ctx->wc_ctx,
-                        wc_abspath,
-                        NULL, NULL, /*left, right conflict-versions*/
-                        left_props, prop_changes,
-                        FALSE /*dry_run*/,
-                        NULL, NULL,
-                        ctx->cancel_func, ctx->cancel_baton,
-                        scratch_pool),
-    ctx->wc_ctx, wc_abspath,
-    FALSE /*lock_anchor*/, scratch_pool);
-
-  return SVN_NO_ERROR;
-}
-
-/* Apply a shelved "delete" to TO_WC_ABSPATH.
- */
-static svn_error_t *
-wc_node_delete(const char *to_wc_abspath,
-               svn_client_ctx_t *ctx,
-               apr_pool_t *scratch_pool)
-{
-  SVN_WC__CALL_WITH_WRITE_LOCK(
-    svn_wc_delete4(ctx->wc_ctx,
-                   to_wc_abspath,
-                   FALSE /*keep_local*/,
-                   TRUE /*delete_unversioned_target*/,
-                   NULL, NULL, NULL, NULL, /*cancel, notify*/
-                   scratch_pool),
-    ctx->wc_ctx, to_wc_abspath,
-    TRUE /*lock_anchor*/, scratch_pool);
-  return SVN_NO_ERROR;
-}
-
-/* Apply a shelved "add" to TO_WC_ABSPATH.
- * The node must already exist on disk, in a versioned parent dir.
- */
-static svn_error_t *
-wc_node_add(const char *to_wc_abspath,
-            apr_hash_t *work_props,
-            svn_client_ctx_t *ctx,
-            apr_pool_t *scratch_pool)
+svn_error_t *
+svn_client__shelf_replay(svn_client__shelf_version_t *shelf_version,
+                         const char *top_relpath,
+                         const svn_delta_editor_t *editor,
+                         void *edit_baton,
+                         svn_wc_notify_func2_t notify_func,
+                         void *notify_baton,
+                         apr_pool_t *scratch_pool)
 {
-  /* If it was not already versioned, schedule the node for addition.
-     (Do not apply autoprops, because this isn't a user-facing "add" but
-     restoring a previously saved state.) */
-  SVN_WC__CALL_WITH_WRITE_LOCK(
-    svn_wc_add_from_disk3(ctx->wc_ctx,
-                          to_wc_abspath, work_props,
-                          FALSE /* skip checks */,
-                          NULL, NULL, scratch_pool),
-    ctx->wc_ctx, to_wc_abspath,
-    TRUE /*lock_anchor*/, scratch_pool);
+  svn_client_ctx_t *ctx = shelf_version->shelf->ctx;
+  apr_array_header_t *src_targets = apr_array_make(scratch_pool, 1,
+                                                   sizeof(char *));
+  const char *src_wc_abspath
+    = svn_dirent_join(shelf_version->files_dir_abspath, top_relpath, scratch_pool);
+
+  APR_ARRAY_PUSH(src_targets, const char *) = src_wc_abspath;
+  SVN_ERR(svn_client__wc_replay(src_wc_abspath,
+                                src_targets, svn_depth_infinity, NULL,
+                                editor, edit_baton,
+                                notify_func, notify_baton,
+                                ctx, scratch_pool));
   return SVN_NO_ERROR;
 }
 
-/* Baton for apply_file_visitor(). */
-struct apply_files_baton_t
+/* Baton for test_apply_file_visitor(). */
+struct test_apply_files_baton_t
 {
   svn_client__shelf_version_t *shelf_version;
-  svn_boolean_t test_only;  /* only check whether it would conflict */
   svn_boolean_t conflict;  /* would it conflict? */
   svn_client_ctx_t *ctx;
 };
 
-/* Copy the file RELPATH from shelf binary file storage to the WC.
- *
- * If it is not already versioned, schedule the file for addition.
- *
- * Make any missing parent directories.
- *
- * In test mode (BATON->test_only): set BATON->conflict if we can't apply
- * the change to WC at RELPATH without conflict. But in fact, just check
+/* Ideally, set BATON->conflict if we can't apply a change to WC
+ * at RELPATH without conflict. But in fact, just check
  * if WC at RELPATH is locally modified.
  *
  * Implements shelved_files_walk_func_t. */
 static svn_error_t *
-apply_file_visitor(void *baton,
-                   const char *relpath,
-                   svn_wc_status3_t *s,
-                   apr_pool_t *scratch_pool)
+test_apply_file_visitor(void *baton,
+                        const char *relpath,
+                        const svn_wc_status3_t *s,
+                        apr_pool_t *scratch_pool)
 {
-  struct apply_files_baton_t *b = baton;
+  struct test_apply_files_baton_t *b = baton;
   const char *wc_root_abspath = b->shelf_version->shelf->wc_root_abspath;
-  char *stored_base_abspath, *stored_work_abspath;
-  apr_hash_t *base_props, *work_props;
   const char *to_wc_abspath = svn_dirent_join(wc_root_abspath, relpath,
                                               scratch_pool);
-  const char *to_dir_abspath = svn_dirent_dirname(to_wc_abspath, scratch_pool);
-
-  SVN_ERR(get_base_file_abspath(&stored_base_abspath,
-                                b->shelf_version, relpath,
-                                scratch_pool, scratch_pool));
-  SVN_ERR(get_working_file_abspath(&stored_work_abspath,
-                                   b->shelf_version, relpath,
-                                   scratch_pool, scratch_pool));
-  SVN_ERR(read_props_from_shelf(&base_props, &work_props,
-                                s->node_status,
-                                b->shelf_version, relpath,
-                                scratch_pool, scratch_pool));
-
-  if (b->test_only)
-    {
-      svn_wc_status3_t *status;
-
-      SVN_ERR(svn_wc_status3(&status, b->ctx->wc_ctx, to_wc_abspath,
-                             scratch_pool, scratch_pool));
-      switch (status->node_status)
-        {
-        case svn_wc_status_normal:
-        case svn_wc_status_none:
-          break;
-        default:
-          b->conflict = TRUE;
-        }
-
-      return SVN_NO_ERROR;
-    }
-
-  /* Handle 'delete' and the delete half of 'replace' */
-  if (s->node_status == svn_wc_status_deleted
-      || s->node_status == svn_wc_status_replaced)
-    {
-      SVN_ERR(wc_node_delete(to_wc_abspath, b->ctx, scratch_pool));
-      if (s->node_status != svn_wc_status_replaced)
-        {
-          SVN_ERR(send_notification(to_wc_abspath, svn_wc_notify_update_delete,
-                                    s->kind,
-                                    svn_wc_notify_state_inapplicable,
-                                    svn_wc_notify_state_inapplicable,
-                                    b->ctx->notify_func2, b->ctx->notify_baton2,
-                                    scratch_pool));
-        }
-    }
-
-  /* If we can merge a file, do so. */
-  if (s->node_status == svn_wc_status_modified)
-    {
-      if (s->kind == svn_node_dir)
-        {
-          SVN_ERR(wc_dir_props_merge(to_wc_abspath,
-                                     base_props, work_props,
-                                     b->ctx, scratch_pool, scratch_pool));
-        }
-      else if (s->kind == svn_node_file)
-        {
-          SVN_ERR(wc_file_merge(to_wc_abspath,
-                                stored_base_abspath, stored_work_abspath,
-                                base_props, work_props,
-                                b->ctx, scratch_pool));
-        }
-      SVN_ERR(send_notification(to_wc_abspath, svn_wc_notify_update_update,
-                                s->kind,
-                                (s->kind == svn_node_dir)
-                                  ? svn_wc_notify_state_inapplicable
-                                  : svn_wc_notify_state_merged,
-                                (s->kind == svn_node_dir)
-                                  ? svn_wc_notify_state_merged
-                                  : svn_wc_notify_state_unknown,
-                                b->ctx->notify_func2, b->ctx->notify_baton2,
-                                scratch_pool));
-    }
-
-  /* For an added file, copy it into the WC and ensure it's versioned. */
-  if (s->node_status == svn_wc_status_added
-      || s->node_status == svn_wc_status_replaced)
-    {
-      if (s->kind == svn_node_dir)
-        {
-          SVN_ERR(svn_io_make_dir_recursively(to_wc_abspath, scratch_pool));
-        }
-      else if (s->kind == svn_node_file)
-        {
-          SVN_ERR(svn_io_make_dir_recursively(to_dir_abspath, scratch_pool));
-          SVN_ERR(svn_io_copy_file(stored_work_abspath, to_wc_abspath,
-                                   TRUE /*copy_perms*/, scratch_pool));
-        }
-      SVN_ERR(wc_node_add(to_wc_abspath, work_props, b->ctx, scratch_pool));
-      SVN_ERR(send_notification(to_wc_abspath,
-                                (s->node_status == svn_wc_status_replaced)
-                                  ? svn_wc_notify_update_replace
-                                  : svn_wc_notify_update_add,
-                                s->kind,
-                                svn_wc_notify_state_inapplicable,
-                                svn_wc_notify_state_inapplicable,
-                                b->ctx->notify_func2, b->ctx->notify_baton2,
-                                scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*-------------------------------------------------------------------------*/
-/* Diff */
-
-/*  */
-static svn_error_t *
-file_changed(svn_client__shelf_version_t *shelf_version,
-             const char *relpath,
-             svn_wc_status3_t *s,
-             const svn_diff_tree_processor_t *diff_processor,
-             svn_diff_source_t *left_source,
-             svn_diff_source_t *right_source,
-             const char *left_stored_abspath,
-             const char *right_stored_abspath,
-             void *dir_baton,
-             apr_pool_t *scratch_pool)
-{
-  void *fb;
-  svn_boolean_t skip = FALSE;
-
-  SVN_ERR(diff_processor->file_opened(&fb, &skip, relpath,
-                                      left_source, right_source,
-                                      NULL /*copyfrom*/,
-                                      dir_baton, diff_processor,
-                                      scratch_pool, scratch_pool));
-  if (!skip)
-    {
-      apr_hash_t *left_props, *right_props;
-      apr_array_header_t *prop_changes;
+  svn_wc_status3_t *status;
 
-      SVN_ERR(read_props_from_shelf(&left_props, &right_props,
-                                    s->node_status, shelf_version, relpath,
-                                    scratch_pool, scratch_pool));
-      SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props,
-                             scratch_pool));
-      SVN_ERR(diff_processor->file_changed(
-                relpath,
-                left_source, right_source,
-                left_stored_abspath, right_stored_abspath,
-                left_props, right_props,
-                TRUE /*file_modified*/, prop_changes,
-                fb, diff_processor, scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/*  */
-static svn_error_t *
-file_deleted(svn_client__shelf_version_t *shelf_version,
-             const char *relpath,
-             svn_wc_status3_t *s,
-             const svn_diff_tree_processor_t *diff_processor,
-             svn_diff_source_t *left_source,
-             const char *left_stored_abspath,
-             void *dir_baton,
-             apr_pool_t *scratch_pool)
-{
-  void *fb;
-  svn_boolean_t skip = FALSE;
-
-  SVN_ERR(diff_processor->file_opened(&fb, &skip, relpath,
-                                      left_source, NULL, NULL /*copyfrom*/,
-                                      dir_baton, diff_processor,
-                                      scratch_pool, scratch_pool));
-  if (!skip)
-    {
-      apr_hash_t *left_props, *right_props;
-
-      SVN_ERR(read_props_from_shelf(&left_props, &right_props,
-                                    s->node_status, shelf_version, relpath,
-                                    scratch_pool, scratch_pool));
-      SVN_ERR(diff_processor->file_deleted(relpath,
-                                           left_source,
-                                           left_stored_abspath,
-                                           left_props,
-                                           fb, diff_processor,
-                                           scratch_pool));
+  SVN_ERR(svn_wc_status3(&status, b->ctx->wc_ctx, to_wc_abspath,
+                         scratch_pool, scratch_pool));
+  switch (status->node_status)
+    {
+    case svn_wc_status_normal:
+    case svn_wc_status_none:
+      break;
+    default:
+      b->conflict = TRUE;
     }
 
   return SVN_NO_ERROR;
 }
 
-/*  */
-static svn_error_t *
-file_added(svn_client__shelf_version_t *shelf_version,
-           const char *relpath,
-           svn_wc_status3_t *s,
-           const svn_diff_tree_processor_t *diff_processor,
-           svn_diff_source_t *right_source,
-           const char *right_stored_abspath,
-           void *dir_baton,
-           apr_pool_t *scratch_pool)
-{
-  void *fb;
-  svn_boolean_t skip = FALSE;
-
-  SVN_ERR(diff_processor->file_opened(&fb, &skip, relpath,
-                                      NULL, right_source, NULL /*copyfrom*/,
-                                      dir_baton, diff_processor,
-                                      scratch_pool, scratch_pool));
-  if (!skip)
-    {
-      apr_hash_t *left_props, *right_props;
-
-      SVN_ERR(read_props_from_shelf(&left_props, &right_props,
-                                    s->node_status, shelf_version, relpath,
-                                    scratch_pool, scratch_pool));
-      SVN_ERR(diff_processor->file_added(
-                relpath,
-                NULL /*copyfrom_source*/, right_source,
-                NULL /*copyfrom_abspath*/, right_stored_abspath,
-                NULL /*copyfrom_props*/, right_props,
-                fb, diff_processor, scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Baton for diff_visitor(). */
-struct diff_baton_t
-{
-  svn_client__shelf_version_t *shelf_version;
-  const char *top_relpath;  /* top of diff, relative to shelf */
-  const char *walk_root_abspath;
-  const svn_diff_tree_processor_t *diff_processor;
-};
-
-/* Drive BATON->diff_processor.
- * Implements svn_io_walk_func_t. */
-static svn_error_t *
-diff_visitor(void *baton,
-             const char *abspath,
-             const apr_finfo_t *finfo,
-             apr_pool_t *scratch_pool)
-{
-  struct diff_baton_t *b = baton;
-  const char *relpath;
-
-  relpath = svn_dirent_skip_ancestor(b->walk_root_abspath, abspath);
-  if (finfo->filetype == APR_REG
-           && (strlen(relpath) >= 5 && strcmp(relpath+strlen(relpath)-5, ".meta") == 0))
-    {
-      svn_wc_status3_t *s;
-      void *db = NULL;
-      svn_diff_source_t *left_source;
-      svn_diff_source_t *right_source;
-      char *left_stored_abspath, *right_stored_abspath;
-
-      relpath = apr_pstrndup(scratch_pool, relpath, strlen(relpath) - 5);
-      if (!svn_relpath_skip_ancestor(b->top_relpath, relpath))
-        return SVN_NO_ERROR;
-
-      SVN_ERR(status_read(&s, b->shelf_version, relpath,
-                          scratch_pool, scratch_pool));
-
-      left_source = svn_diff__source_create(s->revision, scratch_pool);
-      right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
-      SVN_ERR(get_base_file_abspath(&left_stored_abspath,
-                                    b->shelf_version, relpath,
-                                    scratch_pool, scratch_pool));
-      SVN_ERR(get_working_file_abspath(&right_stored_abspath,
-                                       b->shelf_version, relpath,
-                                       scratch_pool, scratch_pool));
-
-      switch (s->node_status)
-        {
-        case svn_wc_status_modified:
-          SVN_ERR(file_changed(b->shelf_version, relpath, s,
-                               b->diff_processor,
-                               left_source, right_source,
-                               left_stored_abspath, right_stored_abspath,
-                               db, scratch_pool));
-          break;
-        case svn_wc_status_added:
-          SVN_ERR(file_added(b->shelf_version, relpath, s,
-                             b->diff_processor,
-                             right_source, right_stored_abspath,
-                             db, scratch_pool));
-          break;
-        case svn_wc_status_deleted:
-          SVN_ERR(file_deleted(b->shelf_version, relpath, s,
-                               b->diff_processor,
-                               left_source, left_stored_abspath,
-                               db, scratch_pool));
-          break;
-        case svn_wc_status_replaced:
-          SVN_ERR(file_deleted(b->shelf_version, relpath, s,
-                               b->diff_processor,
-                               left_source, left_stored_abspath,
-                               db, scratch_pool));
-          SVN_ERR(file_added(b->shelf_version, relpath, s,
-                             b->diff_processor,
-                             right_source, right_stored_abspath,
-                             db, scratch_pool));
-        default:
-          break;
-        }
-    }
-  return SVN_NO_ERROR;
-}
-
 svn_error_t *
 svn_client__shelf_test_apply_file(svn_boolean_t *conflict_p,
                                  svn_client__shelf_version_t *shelf_version,
                                  const char *file_relpath,
                                  apr_pool_t *scratch_pool)
 {
-  struct apply_files_baton_t baton = {0};
+  struct test_apply_files_baton_t baton = {0};
 
   baton.shelf_version = shelf_version;
-  baton.test_only = TRUE;
   baton.conflict = FALSE;
   baton.ctx = shelf_version->shelf->ctx;
   SVN_ERR(shelf_status_visit_path(shelf_version, file_relpath,
-                           apply_file_visitor, &baton,
+                           test_apply_file_visitor, &baton,
                            scratch_pool));
   *conflict_p = baton.conflict;
 
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+wc_mods_editor(const svn_delta_editor_t **editor_p,
+               void **edit_baton_p,
+               const char *dst_wc_abspath,
+               svn_wc_notify_func2_t notify_func,
+               void *notify_baton,
+               svn_client_ctx_t *ctx,
+               apr_pool_t *result_pool,
+               apr_pool_t *scratch_pool)
+{
+  svn_client__pathrev_t *base;
+  const char *dst_wc_url;
+  svn_ra_session_t *ra_session;
+
+  /* We'll need an RA session to obtain the base of any copies */
+  SVN_ERR(svn_client__wc_node_get_base(&base,
+                                       dst_wc_abspath, ctx->wc_ctx,
+                                       scratch_pool, scratch_pool));
+  dst_wc_url = base->url;
+  SVN_ERR(svn_client_open_ra_session2(&ra_session,
+                                      dst_wc_url, dst_wc_abspath,
+                                      ctx, result_pool, scratch_pool));
+  SVN_ERR(svn_client__wc_editor(editor_p, edit_baton_p,
+                                dst_wc_abspath,
+                                notify_func, notify_baton,
+                                ra_session, ctx, result_pool));
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__shelf_mods_editor(const svn_delta_editor_t **editor_p,
+                              void **edit_baton_p,
+                              svn_client__shelf_version_t *shelf_version,
+                              svn_wc_notify_func2_t notify_func,
+                              void *notify_baton,
+                              svn_client_ctx_t *ctx,
+                              apr_pool_t *result_pool)
+{
+  SVN_ERR(wc_mods_editor(editor_p, edit_baton_p,
+                         shelf_version->files_dir_abspath,
+                         notify_func, notify_baton,
+                         ctx, result_pool, result_pool));
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_client__shelf_apply(svn_client__shelf_version_t *shelf_version,
                        svn_boolean_t dry_run,
                        apr_pool_t *scratch_pool)
 {
-  struct apply_files_baton_t baton = {0};
+  svn_client__shelf_t *shelf = shelf_version->shelf;
+  const svn_delta_editor_t *editor;
+  void *edit_baton;
 
-  baton.shelf_version = shelf_version;
-  baton.ctx = shelf_version->shelf->ctx;
-  SVN_ERR(shelf_status_walk(shelf_version, "",
-                            apply_file_visitor, &baton,
-                            scratch_pool));
+  SVN_ERR(wc_mods_editor(&editor, &edit_baton,
+                         shelf->wc_root_abspath,
+                         NULL, NULL, /*notification*/
+                         shelf->ctx, scratch_pool, scratch_pool));
+
+  SVN_ERR(svn_client__shelf_replay(shelf_version, "",
+                                   editor, edit_baton,
+                                   shelf->ctx->notify_func2, shelf->ctx->notify_baton2,
+                                   scratch_pool));
 
-  svn_io_sleep_for_timestamps(shelf_version->shelf->wc_root_abspath,
+  svn_io_sleep_for_timestamps(shelf->wc_root_abspath,
                               scratch_pool);
   return SVN_NO_ERROR;
 }
 
+/* Baton for paths_changed_visitor(). */
+struct unapply_walk_baton_t
+{
+  const char *wc_root_abspath;
+  svn_boolean_t dry_run;
+  svn_boolean_t use_commit_times;
+  svn_client_ctx_t *ctx;
+  apr_pool_t *pool;
+};
+
+/* Revert the change at RELPATH in the user's WC.
+ * Implements shelved_files_walk_func_t. */
+static svn_error_t *
+unapply_visitor(void *baton,
+                const char *relpath,
+                const svn_wc_status3_t *s,
+                apr_pool_t *scratch_pool)
+{
+  struct unapply_walk_baton_t *b = baton;
+  const char *abspath = svn_dirent_join(b->wc_root_abspath, relpath,
+                                        scratch_pool);
+
+  if (!b->dry_run)
+    {
+      apr_array_header_t *targets
+        = apr_array_make(scratch_pool, 1, sizeof(char *));
+      svn_depth_t depth;
+
+      APR_ARRAY_PUSH(targets, const char *) = abspath;
+
+      /* If the local modification is a "delete" then revert it all
+         (recursively). Otherwise we'd have to walk paths in
+         top-down order to revert a delete, whereas we need bottom-up
+         order to revert children of an added directory. */
+      if (s->node_status == svn_wc_status_deleted
+          || s->node_status == svn_wc_status_replaced
+          || s->node_status == svn_wc_status_added)
+        depth = svn_depth_infinity;
+      else
+        depth = svn_depth_empty;
+      SVN_ERR(svn_wc_revert6(b->ctx->wc_ctx,
+                             abspath,
+                             depth,
+                             b->use_commit_times,
+                             NULL /*changelists*/,
+                             FALSE /*clear_changelists*/,
+                             FALSE /*metadata_only*/,
+                             FALSE /*added_keep_local*/,
+                             b->ctx->cancel_func, b->ctx->cancel_baton,
+                             NULL, NULL, /*notification*/
+                             scratch_pool));
+    }
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_client__shelf_unapply(svn_client__shelf_version_t *shelf_version,
                          svn_boolean_t dry_run,
                          apr_pool_t *scratch_pool)
 {
-  apr_array_header_t *targets;
+  svn_client_ctx_t *ctx = shelf_version->shelf->ctx;
+  svn_client__shelf_t *shelf = shelf_version->shelf;
+  struct unapply_walk_baton_t baton;
+  svn_config_t *cfg;
 
-  SVN_ERR(shelf_paths_changed(NULL, &targets, shelf_version,
-                              TRUE /*as_abspath*/,
-                              scratch_pool, scratch_pool));
-  if (!dry_run)
-    {
-      SVN_ERR(svn_client_revert4(targets, svn_depth_empty,
-                                 NULL /*changelists*/,
-                                 FALSE /*clear_changelists*/,
-                                 FALSE /*metadata_only*/,
-                                 FALSE /*added_keep_local*/,
-                                 shelf_version->shelf->ctx, scratch_pool));
-    }
+  baton.wc_root_abspath = shelf->wc_root_abspath;
+  baton.dry_run = dry_run;
+  baton.ctx = ctx;
+  baton.pool = scratch_pool;
+
+  cfg = ctx->config ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG)
+                    : NULL;
+  SVN_ERR(svn_config_get_bool(cfg, &baton.use_commit_times,
+                              SVN_CONFIG_SECTION_MISCELLANY,
+                              SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE));
+
+  SVN_WC__CALL_WITH_WRITE_LOCK(
+    shelf_status_walk(shelf_version, "",
+                      unapply_visitor, &baton,
+                      scratch_pool),
+    ctx->wc_ctx, shelf_version->shelf->wc_root_abspath,
+    FALSE /*lock_anchor*/, scratch_pool);
   return SVN_NO_ERROR;
 }
 
@@ -1903,22 +980,106 @@ svn_client__shelf_diff(svn_client__shelf
                        const svn_diff_tree_processor_t *diff_processor,
                        apr_pool_t *scratch_pool)
 {
-  struct diff_baton_t baton;
+  svn_client_ctx_t *ctx = shelf_version->shelf->ctx;
+  char *local_abspath
+    = svn_dirent_join(shelf_version->files_dir_abspath, shelf_relpath,
+                      scratch_pool);
 
   if (shelf_version->version_number == 0)
     return SVN_NO_ERROR;
 
-  baton.shelf_version = shelf_version;
-  baton.top_relpath = shelf_relpath;
-  baton.walk_root_abspath = shelf_version->files_dir_abspath;
-  baton.diff_processor = diff_processor;
-  SVN_ERR(svn_io_dir_walk2(baton.walk_root_abspath, 0 /*wanted*/,
-                           diff_visitor, &baton,
-                           scratch_pool));
+  SVN_ERR(svn_wc__diff7(FALSE /*anchor_at_given_paths*/,
+                        ctx->wc_ctx, local_abspath,
+                        depth,
+                        ignore_ancestry,
+                        NULL /*changelists*/,
+                        diff_processor,
+                        NULL, NULL, /*cancellation*/
+                        scratch_pool, scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+/* Populate the storage a new shelf-version object NEW_SHELF_VERSION,
+ * by creating a shelf storage WC with its base state copied from the
+ * 'real' WC.
+ */
+static svn_error_t *
+shelf_copy_base(svn_client__shelf_version_t *new_shelf_version,
+                apr_pool_t *scratch_pool)
+{
+  svn_client_ctx_t *ctx = new_shelf_version->shelf->ctx;
+  const char *users_wc_abspath = new_shelf_version->shelf->wc_root_abspath;
+  svn_client__pathrev_t *users_wc_root_base;
+  svn_opt_revision_t users_wc_root_rev;
+  svn_ra_session_t *ra_session = NULL;
+  svn_boolean_t sleep_here = FALSE;
+
+  SVN_ERR(svn_client__wc_node_get_base(&users_wc_root_base,
+                                       users_wc_abspath, ctx->wc_ctx,
+                                       scratch_pool, scratch_pool));
 
+  /* ### We need to read and recreate the mixed-rev, switched-URL,
+     mixed-depth WC state; but for a rough start we'll just use
+     HEAD, unswitched, depth-infinity. */
+  users_wc_root_rev.kind = svn_opt_revision_head;
+
+  /* ### TODO: Create an RA session that reads from the user's WC.
+     For a rough start, we'll just let 'checkout' read from the repo. */
+
+  SVN_ERR(svn_client__checkout_internal(NULL /*result_rev*/, &sleep_here,
+                                        users_wc_root_base->url,
+                                        new_shelf_version->files_dir_abspath,
+                                        &users_wc_root_rev, &users_wc_root_rev,
+                                        svn_depth_infinity,
+                                        TRUE /*ignore_externals*/,
+                                        FALSE /*allow_unver_obstructions*/,
+                                        NULL, /* default WC format */
+                                        ra_session,
+                                        ctx, scratch_pool));
+  /* ### hopefully we won't eventually need to sleep_here... */
+  if (sleep_here)
+    svn_io_sleep_for_timestamps(new_shelf_version->files_dir_abspath,
+                                scratch_pool);
   return SVN_NO_ERROR;
 }
 
+/*  */
+struct shelf_save_notifer_baton_t
+{
+  svn_client__shelf_version_t *shelf_version;
+  svn_wc_notify_func2_t notify_func;
+  void *notify_baton;
+  svn_client_status_func_t shelved_func;
+  void *shelved_baton;
+  svn_boolean_t any_shelved;
+};
+
+/*  */
+static void
+shelf_save_notifier(void *baton,
+                    const svn_wc_notify_t *notify,
+                    apr_pool_t *pool)
+{
+  struct shelf_save_notifer_baton_t *nb = baton;
+  const char *wc_relpath
+    = svn_dirent_skip_ancestor(nb->shelf_version->shelf->wc_root_abspath,
+                               notify->path);
+  svn_client_status_t *cst = NULL;
+#if 0
+  svn_wc_status3_t *wc_status;
+
+  svn_error_clear(status_read(&wc_status, nb->shelf_version, wc_relpath,
+                              pool, pool));
+  svn_error_clear(svn_client__create_status(
+                    &cst, nb->shelf_version->shelf->ctx->wc_ctx,
+                    notify->path, wc_status, pool, pool));
+#endif
+  svn_error_clear(nb->shelved_func(nb->shelved_baton, wc_relpath, cst, pool));
+  nb->any_shelved = TRUE;
+
+  nb->notify_func(nb->notify_baton, notify, pool);
+}
+
 svn_error_t *
 svn_client__shelf_save_new_version3(svn_client__shelf_version_t **new_version_p,
                                    svn_client__shelf_t *shelf,
@@ -1931,28 +1092,41 @@ svn_client__shelf_save_new_version3(svn_
                                    void *not_shelved_baton,
                                    apr_pool_t *scratch_pool)
 {
+  svn_client_ctx_t *ctx = shelf->ctx;
   int next_version = shelf->max_version + 1;
   svn_client__shelf_version_t *new_shelf_version;
-  svn_boolean_t any_shelved;
+  struct shelf_save_notifer_baton_t nb;
+  const svn_delta_editor_t *editor;
+  void *edit_baton;
 
   SVN_ERR(shelf_version_create(&new_shelf_version,
                                shelf, next_version, scratch_pool));
-  SVN_ERR(shelf_write_changes(&any_shelved,
-                              new_shelf_version,
-                              paths, depth, changelists,
-                              shelved_func, shelved_baton,
-                              not_shelved_func, not_shelved_baton,
-                              shelf->wc_root_abspath,
-                              shelf->ctx, scratch_pool, scratch_pool));
+  SVN_ERR(shelf_copy_base(new_shelf_version, scratch_pool));
+
+  nb.shelf_version = new_shelf_version;
+  nb.notify_func = ctx->notify_func2;
+  nb.notify_baton = ctx->notify_baton2;
+  nb.shelved_func = shelved_func;
+  nb.shelved_baton = shelved_baton;
+  nb.any_shelved = FALSE;
+  SVN_ERR(svn_client__shelf_mods_editor(&editor, &edit_baton,
+                                        new_shelf_version,
+                                        NULL, NULL, /*notification*/
+                                        ctx, scratch_pool));
+  SVN_ERR(svn_client__wc_replay(shelf->wc_root_abspath,
+                                paths, depth, changelists,
+                                editor, edit_baton,
+                                shelf_save_notifier, &nb,
+                                ctx, scratch_pool));
 
-  if (any_shelved)
+  if (nb.any_shelved)
     {
       shelf->max_version = next_version;
       SVN_ERR(shelf_write_current(shelf, scratch_pool));
 
       if (new_version_p)
         SVN_ERR(svn_client__shelf_version_open(new_version_p, shelf, next_version,
-                                              scratch_pool, scratch_pool));
+                                               scratch_pool, scratch_pool));
     }
   else
     {
@@ -2003,8 +1177,8 @@ svn_client__shelf_list(apr_hash_t **shel
 
   SVN_ERR(svn_wc__get_wcroot(&wc_root_abspath, ctx->wc_ctx, local_abspath,
                              scratch_pool, scratch_pool));
-  SVN_ERR(svn_wc__get_shelves_dir(&shelves_dir, 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/multi-wc-format/subversion/libsvn_client/status.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/status.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/status.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/status.c Fri Jan 14 14:01:45 2022
@@ -44,6 +44,7 @@
 #include "svn_error.h"
 #include "svn_hash.h"
 
+#include "private/svn_client_shelf.h"
 #include "private/svn_client_private.h"
 #include "private/svn_sorts_private.h"
 #include "private/svn_wc_private.h"

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/switch.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/switch.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/switch.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/switch.c Fri Jan 14 14:01:45 2022
@@ -216,7 +216,7 @@ switch_internal(svn_revnum_t *result_rev
                              anchor_url, switch_loc->repos_root_url);
 
   /* If we're not ignoring ancestry, then error out if the switch
-     source and target don't have a common ancestory.
+     source and target don't have a common ancestry.
 
      ### We're acting on the anchor here, not the target.  Is that
      ### okay? */
@@ -279,7 +279,7 @@ switch_internal(svn_revnum_t *result_rev
 
           /* If LOCAL_ABSPATH will be unswitched relative to its parent, then
              it doesn't need an iprop cache.  Note: It doesn't matter if
-             LOCAL_ABSPATH is withing a switched subtree, only if it's the
+             LOCAL_ABSPATH is within a switched subtree, only if it's the
              *root* of a switched subtree.*/
           if (strcmp(unswitched_url, switch_loc->url) == 0)
             needs_iprop_cache = FALSE;

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/update.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/update.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/update.c Fri Jan 14 14:01:45 2022
@@ -96,7 +96,7 @@ svn_client__dirent_fetcher(void *baton,
    folder. ANCHOR_ABSPATH is the w/c root and LOCAL_ABSPATH will still
    be considered empty, if it is equal to ANCHOR_ABSPATH and only
    contains the admin sub-folder.
-   If the w/c folder already exists but cannot be openend, we return
+   If the w/c folder already exists but cannot be opened, we return
    "unclean" - just in case. Most likely, the caller will have to bail
    out later due to the same error we got here.
  */

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/upgrade.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/upgrade.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/upgrade.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/upgrade.c Fri Jan 14 14:01:45 2022
@@ -337,7 +337,7 @@ upgrade_externals_from_properties(svn_cl
 {
   apr_hash_index_t *hi;
   apr_pool_t *iterpool;
-  apr_pool_t *iterpool2;
+  apr_pool_t *inner_iterpool;
   apr_hash_t *externals;
   svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}};
 
@@ -351,7 +351,7 @@ upgrade_externals_from_properties(svn_cl
                               scratch_pool, scratch_pool));
 
   iterpool = svn_pool_create(scratch_pool);
-  iterpool2 = svn_pool_create(scratch_pool);
+  inner_iterpool = svn_pool_create(scratch_pool);
 
   for (hi = apr_hash_first(scratch_pool, externals); hi;
        hi = apr_hash_next(hi))
@@ -385,14 +385,12 @@ upgrade_externals_from_properties(svn_cl
                                         iterpool, iterpool);
 
       if (!err)
-        externals_parent_url = svn_path_url_add_component2(
-                                    externals_parent_repos_root_url,
-                                    externals_parent_repos_relpath,
-                                    iterpool);
-      if (!err)
-        err = svn_wc_parse_externals_description3(
-                  &externals_p, svn_dirent_dirname(local_abspath, iterpool),
-                  external_desc->data, FALSE, iterpool);
+        {
+          err = svn_wc_parse_externals_description3(
+              &externals_p, svn_dirent_dirname(local_abspath, iterpool),
+              external_desc->data, FALSE, iterpool);
+        }
+
       if (err)
         {
           svn_wc_notify_t *notify =
@@ -410,25 +408,30 @@ upgrade_externals_from_properties(svn_cl
           continue;
         }
 
+      externals_parent_url = svn_path_url_add_component2(
+          externals_parent_repos_root_url,
+          externals_parent_repos_relpath,
+          iterpool);
+
       for (i = 0; i < externals_p->nelts; i++)
         {
           svn_wc_external_item2_t *item;
 
           item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*);
 
-          svn_pool_clear(iterpool2);
+          svn_pool_clear(inner_iterpool);
           err = upgrade_external_item(ctx, wc_format,
                                       externals_parent_abspath,
                                       externals_parent_url,
                                       externals_parent_repos_root_url,
-                                      item, info_baton, iterpool2);
+                                      item, info_baton, inner_iterpool);
 
           if (err)
             {
               svn_wc_notify_t *notify =
                   svn_wc_create_notify(svn_dirent_join(externals_parent_abspath,
                                                        item->target_dir,
-                                                       iterpool2),
+                                                       inner_iterpool),
                                        svn_wc_notify_failed_external,
                                        scratch_pool);
               notify->err = err;
@@ -440,8 +443,8 @@ upgrade_externals_from_properties(svn_cl
         }
     }
 
+  svn_pool_destroy(inner_iterpool);
   svn_pool_destroy(iterpool);
-  svn_pool_destroy(iterpool2);
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/multi-wc-format/subversion/libsvn_client/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_client/util.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_client/util.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_client/util.c Fri Jan 14 14:01:45 2022
@@ -93,6 +93,7 @@ svn_client__pathrev_create_with_session(
   pathrev->rev = rev;
   pathrev->url = apr_pstrdup(result_pool, url);
   *pathrev_p = pathrev;
+  SVN_ERR_ASSERT(svn_uri__is_ancestor(pathrev->repos_root_url, url));
   return SVN_NO_ERROR;
 }
 

Modified: subversion/branches/multi-wc-format/subversion/libsvn_delta/branch.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_delta/branch.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_delta/branch.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_delta/branch.c Fri Jan 14 14:01:45 2022
@@ -157,7 +157,7 @@ branch_txn_delete_branch(svn_branch__txn
 
       if (strcmp(b->bid, bid) == 0)
         {
-          svn_sort__array_delete(txn->priv->branches, i, 1);
+          SVN_ERR(svn_sort__array_delete2(txn->priv->branches, i, 1));
           break;
         }
     }

Modified: subversion/branches/multi-wc-format/subversion/libsvn_delta/branch_compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/libsvn_delta/branch_compat.c?rev=1897034&r1=1897033&r2=1897034&view=diff
==============================================================================
--- subversion/branches/multi-wc-format/subversion/libsvn_delta/branch_compat.c (original)
+++ subversion/branches/multi-wc-format/subversion/libsvn_delta/branch_compat.c Fri Jan 14 14:01:45 2022
@@ -875,6 +875,8 @@ drive_ev1_props(const char *repos_relpat
  */
 static svn_error_t *
 apply_change(void **dir_baton,
+             const svn_delta_editor_t *editor,
+             void *edit_baton,
              void *parent_baton,
              void *callback_baton,
              const char *ev1_relpath,
@@ -905,7 +907,7 @@ apply_change(void **dir_baton,
       /* Only property edits are allowed on the root.  */
       SVN_ERR_ASSERT(change->action == RESTRUCTURE_NONE);
       SVN_ERR(drive_ev1_props(ev1_relpath, change, base_props,
-                              eb->deditor, *dir_baton, scratch_pool));
+                              editor, *dir_baton, scratch_pool));
 
       /* No further action possible for the root.  */
       return SVN_NO_ERROR;
@@ -913,10 +915,10 @@ apply_change(void **dir_baton,
 
   if (change->action == RESTRUCTURE_DELETE)
     {
-      SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting_rev,
-                                        parent_baton, scratch_pool));
+      SVN_ERR(editor->delete_entry(ev1_relpath, change->deleting_rev,
+                                   parent_baton, scratch_pool));
 
-      /* No futher action possible for this node.  */
+      /* No further action possible for this node.  */
       return SVN_NO_ERROR;
     }
 
@@ -927,11 +929,11 @@ apply_change(void **dir_baton,
   if (change->action == RESTRUCTURE_ADD_ABSENT)
     {
       if (change->kind == svn_node_dir)
-        SVN_ERR(eb->deditor->absent_directory(ev1_relpath, parent_baton,
-                                              scratch_pool));
-      else if (change->kind == svn_node_file)
-        SVN_ERR(eb->deditor->absent_file(ev1_relpath, parent_baton,
+        SVN_ERR(editor->absent_directory(ev1_relpath, parent_baton,
                                          scratch_pool));
+      else if (change->kind == svn_node_file)
+        SVN_ERR(editor->absent_file(ev1_relpath, parent_baton,
+                                    scratch_pool));
       else
         SVN_ERR_MALFUNCTION();
 
@@ -948,8 +950,8 @@ apply_change(void **dir_baton,
 
       /* Do we have an old node to delete first? If so, delete it. */
       if (change->deleting)
-        SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting_rev,
-                                          parent_baton, scratch_pool));
+        SVN_ERR(editor->delete_entry(ev1_relpath, change->deleting_rev,
+                                     parent_baton, scratch_pool));
 
       /* If it's a copy, determine the copy source location. */
       if (change->copyfrom_path)
@@ -974,13 +976,13 @@ apply_change(void **dir_baton,
         }
 
       if (change->kind == svn_node_dir)
-        SVN_ERR(eb->deditor->add_directory(ev1_relpath, parent_baton,
-                                           copyfrom_url, copyfrom_rev,
-                                           result_pool, dir_baton));
-      else if (change->kind == svn_node_file)
-        SVN_ERR(eb->deditor->add_file(ev1_relpath, parent_baton,
+        SVN_ERR(editor->add_directory(ev1_relpath, parent_baton,
                                       copyfrom_url, copyfrom_rev,
-                                      result_pool, &file_baton));
+                                      result_pool, dir_baton));
+      else if (change->kind == svn_node_file)
+        SVN_ERR(editor->add_file(ev1_relpath, parent_baton,
+                                 copyfrom_url, copyfrom_rev,
+                                 result_pool, &file_baton));
       else
         SVN_ERR_MALFUNCTION();
     }
@@ -993,13 +995,13 @@ apply_change(void **dir_baton,
          when we fetch the base properties.) */
 
       if (change->kind == svn_node_dir)
-        SVN_ERR(eb->deditor->open_directory(ev1_relpath, parent_baton,
-                                            change->changing_rev,
-                                            result_pool, dir_baton));
-      else if (change->kind == svn_node_file)
-        SVN_ERR(eb->deditor->open_file(ev1_relpath, parent_baton,
+        SVN_ERR(editor->open_directory(ev1_relpath, parent_baton,
                                        change->changing_rev,
-                                       result_pool, &file_baton));
+                                       result_pool, dir_baton));
+      else if (change->kind == svn_node_file)
+        SVN_ERR(editor->open_file(ev1_relpath, parent_baton,
+                                  change->changing_rev,
+                                  result_pool, &file_baton));
       else
         SVN_ERR_MALFUNCTION();
     }
@@ -1007,10 +1009,10 @@ apply_change(void **dir_baton,
   /* Apply any properties in CHANGE to the node.  */
   if (change->kind == svn_node_dir)
     SVN_ERR(drive_ev1_props(ev1_relpath, change, base_props,
-                            eb->deditor, *dir_baton, scratch_pool));
+                            editor, *dir_baton, scratch_pool));
   else
     SVN_ERR(drive_ev1_props(ev1_relpath, change, base_props,
-                            eb->deditor, file_baton, scratch_pool));
+                            editor, file_baton, scratch_pool));
 
   /* Send the text content delta, if new text content is provided. */
   if (change->contents_text)
@@ -1023,7 +1025,7 @@ apply_change(void **dir_baton,
                                               scratch_pool);
       /* ### would be nice to have a BASE_CHECKSUM, but hey: this is the
          ### shim code...  */
-      SVN_ERR(eb->deditor->apply_textdelta(file_baton, NULL, scratch_pool,
+      SVN_ERR(editor->apply_textdelta(file_baton, NULL, scratch_pool,
                                            &handler, &handler_baton));
       /* ### it would be nice to send a true txdelta here, but whatever.  */
       SVN_ERR(svn_txdelta_send_stream(read_stream, handler, handler_baton,
@@ -1033,7 +1035,7 @@ apply_change(void **dir_baton,
 
   if (file_baton)
     {
-      SVN_ERR(eb->deditor->close_file(file_baton, NULL, scratch_pool));
+      SVN_ERR(editor->close_file(file_baton, NULL, scratch_pool));
     }
 
   return SVN_NO_ERROR;
@@ -1740,7 +1742,7 @@ drive_changes(svn_branch__txn_priv_t *eb
 
   /* Apply the appropriate Ev1 change to each Ev1-relative path. */
   paths = get_unsorted_paths(eb->changes, scratch_pool);
-  SVN_ERR(svn_delta_path_driver2(eb->deditor, eb->dedit_baton,
+  SVN_ERR(svn_delta_path_driver3(eb->deditor, eb->dedit_baton,
                                  paths, TRUE /*sort*/,
                                  apply_change, (void *)eb,
                                  scratch_pool));