subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From julianf...@apache.org
Subject svn commit: r1853396 - in /subversion/branches/shelving-v3: notes/shelving/shelf-structure.txt subversion/include/svn_client.h subversion/libsvn_client/shelf.c subversion/tests/cmdline/shelf_tests.py subversion/tests/cmdline/svntest/sandbox.py
Date Mon, 11 Feb 2019 17:30:24 GMT
Author: julianfoad
Date: Mon Feb 11 17:30:24 2019
New Revision: 1853396

URL: http://svn.apache.org/viewvc?rev=1853396&view=rev
Log:
Re-implement shelving to use a separate WC for the storage of each shelf.

* subversion/include/svn_client.h
  (svn_client__shelf_mods_editor): New.
  (svn_client__shelf_replay): Add a notification callback.

* subversion/libsvn_client/shelf.c
  (shelf_version_files_dir_abspath): Tweak the directory name to end in
    ".wc", for clarity.
  (get_metadata_abspath,
   get_base_file_abspath,
   get_working_file_abspath,
   get_base_props_abspath,
   get_working_props_abspath): Delete.
  (status_create,
   kind_to_char,
   char_to_kind,
   status_to_char,
   char_to_status,
   wc_status_serialize,
   wc_status_unserialize,
   status_write): Delete.
  (status_augment): New.
  (status_read): Read WC status directly from shelf-storage WC.
  (shelf_status_visitor_t,
   shelf_status_baton_t,
   shelf_status_visitor,
   shelf_status_visit_path,
   shelf_status_walk): Adjust to use a WC status walk directly.
  (write_changes_baton_t,
   notify_shelved,
   notify_not_shelved,
   read_props_from_wc,
   write_props_to_shelf,
   read_props_from_shelf,
   store_file,
   write_changes_visitor,
   changelist_filter_baton_t,
   changelist_filter_func,
   wc_walk_status_multi,
   shelf_write_changes): Delete.
  (get_shelves_dir): New.
  (shelf_construct): Use a different storage dir.
  (paths_changed_walk_baton_t,
   paths_changed_visitor,
   shelf_paths_changed,
   svn_client__shelf_paths_changed): Remove unneeded 'as abspath' option.
  (send_notification,
   apply_prop_mods,
   apply_file_mods,
   path_driver_cb_baton_t,
   path_driver_cb_func,
   shelf_replay_path_baton_t,
   shelf_replay_path_visitor): Delete.
  (svn_client__shelf_replay): Use the WC replay API instead of a shelf-
    specific implementation.
  (test_apply_file_visitor): Tweak constness.
  (file_changed,
   file_deleted,
   file_added,
   diff_baton_t,
   diff_visitor): Delete.
  (wc_mods_editor): Add explicit notification callback and result pool.
  (svn_client__shelf_mods_editor): Expose in the semi-public API.
  (svn_client__shelf_apply): Adjust calls.
  (unapply_walk_baton_t,
   unapply_visitor): New.
  (svn_client__shelf_unapply): Rewrite, using a shelf status walk that
    reverts each visited path.
  (svn_client__shelf_diff): Rewrite, using a WC diff instead of a
    shelf-specific implementation.
  (shelf_copy_base): New.
  (shelf_save_notifer_baton_t,
   shelf_save_notifier): New.
  (svn_client__shelf_save_new_version3): Rewrite, using a WC replay piped
    into a shelf mods editor.
  (svn_client__shelf_list): Adjust the shelf storage dir.

* subversion/tests/cmdline/shelf_tests.py
  (shelve_mkdir,
   shelve_replace_dir): Don't expect "cannot shelve". Do expect failure, for
    now.
  (shelve_rmdir,
   shelve_file_copy,
   shelve_dir_copy): Don't expect "cannot shelve".
  (refuse_to_shelve_conflict): Change the test case to avoid a known,
    unrelated bug.

* subversion/tests/cmdline/svntest/sandbox.py
  (Sandbox.build): Expect each sandbox to use an additional WC directory
    "<wc-dir-basename>.shelves".

* notes/shelving/shelf-structure.txt
  Update to this version.

Modified:
    subversion/branches/shelving-v3/notes/shelving/shelf-structure.txt
    subversion/branches/shelving-v3/subversion/include/svn_client.h
    subversion/branches/shelving-v3/subversion/libsvn_client/shelf.c
    subversion/branches/shelving-v3/subversion/tests/cmdline/shelf_tests.py
    subversion/branches/shelving-v3/subversion/tests/cmdline/svntest/sandbox.py

Modified: subversion/branches/shelving-v3/notes/shelving/shelf-structure.txt
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/notes/shelving/shelf-structure.txt?rev=1853396&r1=1853395&r2=1853396&view=diff
==============================================================================
--- subversion/branches/shelving-v3/notes/shelving/shelf-structure.txt (original)
+++ subversion/branches/shelving-v3/notes/shelving/shelf-structure.txt Mon Feb 11 17:30:24 2019
@@ -1,6 +1,26 @@
 == Storage Structure for Shelving ==
 
 
+=== Shelving v3 (trunk) ===
+
+In development in trunk (1.12-dev), marked "experimental".
+
+Each shelf-version is stored in an entire copy of the WC. The shelves are
+stored in a sibling to the user's WC, at '<WC-root-dir>.shelves'. Inside it:
+
+* .../<encoded-name>.current
+  Holds an ASCII-decimal number stating the latest shelf-version of the
+  shelf <encoded-name>, followed by a newline character (LF).
+
+* .../<encoded-name>.log
+  Holds the 'revision' properties of the shelf, in svn_hash_write2() format
+  terminated with "PROPS-END". Created when any revprop is set; need not
+  exist.
+
+* .../<encoded-name>-<version>.wc
+  Directory holding a shelf-version as a real WC.
+
+
 === Shelving v2 ===
 
 Released in Subversion 1.11.0, marked "experimental".

Modified: subversion/branches/shelving-v3/subversion/include/svn_client.h
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/include/svn_client.h?rev=1853396&r1=1853395&r2=1853396&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/include/svn_client.h (original)
+++ subversion/branches/shelving-v3/subversion/include/svn_client.h Mon Feb 11 17:30:24 2019
@@ -7098,6 +7098,20 @@ svn_client__shelf_delete(const char *nam
                         svn_client_ctx_t *ctx,
                         apr_pool_t *scratch_pool);
 
+/** Get an editor that, when driven, will store changes in @a shelf_version.
+ *
+ * @warning EXPERIMENTAL.
+ */
+SVN_EXPERIMENTAL
+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);
+
 /** Save the local modifications found by @a paths, @a depth,
  * @a changelists as a new version of @a shelf.
  *
@@ -7264,6 +7278,8 @@ svn_client__shelf_replay(svn_client__she
                          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);
 
 /** Set @a *affected_paths to a hash with one entry for each path affected

Modified: subversion/branches/shelving-v3/subversion/libsvn_client/shelf.c
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/libsvn_client/shelf.c?rev=1853396&r1=1853395&r2=1853396&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/libsvn_client/shelf.c (original)
+++ subversion/branches/shelving-v3/subversion/libsvn_client/shelf.c Mon Feb 11 17:30:24 2019
@@ -128,7 +128,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 +154,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 +336,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 +361,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 +425,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 +443,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*/,
+                           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 +470,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 +478,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,428 +522,30 @@ 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.
+/* Get the WC root and corresponding shelves dir.
  *
- * PATHS are absolute, or relative to CWD.
+ * ### Presently, this returns a shelves dir OUTSIDE the WC; this is a
+ * (temporary) convenience for shelf_copy_base() which would need to be
+ * smarter if it were inside '.svn'.
  */
 static svn_error_t *
-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)
+get_shelves_dir(const char **wc_root_abspath_p,
+                char **shelves_dir_abspath_p,
+                const char *local_abspath,
+                svn_client_ctx_t *ctx,
+                apr_pool_t *result_pool)
 {
-  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;
+  SVN_ERR(svn_client_get_wc_root(wc_root_abspath_p,
+                                 local_abspath, ctx,
+                                 result_pool, result_pool));
+  /*SVN_ERR(svn_wc__get_shelves_dir(shelves_dir_abspath_p,
+                                  ctx->wc_ctx, local_abspath,
+                                  result_pool, result_pool));*/
+  *shelves_dir_abspath_p = apr_pstrcat(result_pool,
+                                       *wc_root_abspath_p, ".shelves",
+                                       SVN_VA_NULL);
+  /* Ensure the directory exists. */
+  SVN_ERR(svn_io_make_dir_recursively(*shelves_dir_abspath_p, result_pool));
   return SVN_NO_ERROR;
 }
 
@@ -1172,12 +562,8 @@ shelf_construct(svn_client__shelf_t **sh
   svn_client__shelf_t *shelf = apr_palloc(result_pool, sizeof(*shelf));
   char *shelves_dir;
 
-  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(&shelf->wc_root_abspath, &shelves_dir,
+                          local_abspath, ctx, result_pool));
   shelf->shelves_dir = shelves_dir;
   shelf->ctx = ctx;
   shelf->pool = result_pool;
@@ -1273,7 +659,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 +668,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 +685,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 +693,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,333 +714,31 @@ 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;
-}
-
-/* Apply a set of property changes to one node through EDITOR. If
- * FILE_BATON is non-null, apply to that file, else to the dir at DIR_BATON.
- *
- * When any of the properties inputs is null, it means no properties.
- */
-static svn_error_t *
-apply_prop_mods(apr_hash_t *base_props,
-                apr_hash_t *work_props,
-                const svn_delta_editor_t *editor,
-                void *file_baton,
-                void *dir_baton,
-                apr_pool_t *scratch_pool)
-{
-  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-  int i;
-  apr_array_header_t *propmods;
-
-  /* Apply property changes */
-  SVN_ERR(svn_prop_diffs(&propmods,
-                         work_props ? work_props : apr_hash_make(scratch_pool),
-                         base_props ? base_props : apr_hash_make(scratch_pool),
-                         scratch_pool));
-  for (i = 0; i < propmods->nelts; i++)
-    {
-      const svn_prop_t *p = &APR_ARRAY_IDX(propmods, i, svn_prop_t);
-
-      svn_pool_clear(iterpool);
-
-      if (file_baton)
-        SVN_ERR(editor->change_file_prop(file_baton, p->name, p->value,
-                                         iterpool));
-      else
-        SVN_ERR(editor->change_dir_prop(dir_baton, p->name, p->value,
-                                        iterpool));
-    }
-  svn_pool_destroy(iterpool);
-
-  return SVN_NO_ERROR;
-}
-
-/* Send edits to EDITOR : FILE_BATON representing the delta from BASE_PROPS
- * to WORK_PROPS and the delta from the file at STORED_BASE_ABSPATH to the
- * file at STORED_WORK_ABSPATH.
- *
- * When any of the inputs is null, it means no properties or empty text.
- */
-static svn_error_t *
-apply_file_mods(apr_hash_t *base_props,
-                apr_hash_t *work_props,
-                char *stored_base_abspath,
-                char *stored_work_abspath,
-                const svn_delta_editor_t *editor,
-                void *file_baton,
-                apr_pool_t *scratch_pool)
-{
-  svn_txdelta_window_handler_t handler;
-  void *handler_baton;
-  svn_stream_t *source_stream, *target_stream;
-  svn_txdelta_stream_t *txdelta_stream;
-
-  /* text mods */
-  if (stored_base_abspath)
-    SVN_ERR(svn_stream_open_readonly(&source_stream, stored_base_abspath,
-                                     scratch_pool, scratch_pool));
-  else
-    source_stream = svn_stream_empty(scratch_pool);
-  if (stored_work_abspath)
-    SVN_ERR(svn_stream_open_readonly(&target_stream, stored_work_abspath,
-                                     scratch_pool, scratch_pool));
-  else
-    target_stream = svn_stream_empty(scratch_pool);
-  SVN_ERR(editor->apply_textdelta(file_baton, NULL /*base_checksum*/,
-                                  scratch_pool, &handler, &handler_baton));
-  svn_txdelta2(&txdelta_stream, source_stream, target_stream,
-               FALSE /*calculate_checksum*/, scratch_pool);
-  SVN_ERR(svn_txdelta_send_txstream(txdelta_stream,
-                                    handler, handler_baton, scratch_pool));
-
-  /* prop mods */
-  SVN_ERR(apply_prop_mods(base_props, work_props,
-                          editor, file_baton, NULL,
-                          scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* Baton for apply_file_visitor(). */
-struct path_driver_cb_baton_t
-{
-  svn_client__shelf_version_t *shelf_version;
-  svn_client_ctx_t *ctx;
-};
-
-/* Apply the changes for RELPATH from shelf storage to the WC.
- *
- * Implements svn_delta_path_driver_cb_func_t. */
-static svn_error_t *
-path_driver_cb_func(void **dir_baton_p,
-                    const svn_delta_editor_t *editor,
-                    void *edit_baton,
-                    void *parent_baton,
-                    void *callback_baton,
-                    const char *relpath,
-                    apr_pool_t *scratch_pool)
-{
-  struct path_driver_cb_baton_t *b = callback_baton;
-  svn_wc_status3_t *s;
-  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);
-
-  *dir_baton_p = NULL;
-
-  SVN_ERR(status_read(&s, b->shelf_version, relpath,
-                      scratch_pool, 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));
-
-  /* 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(editor->delete_entry(relpath, SVN_INVALID_REVNUM,
-                                   parent_baton, 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));
-        }
-    }
-
-  /* Modifications to a file or dir. */
-  if (s->node_status == svn_wc_status_modified)
-    {
-      if (s->kind == svn_node_dir)
-        {
-          SVN_ERR(editor->open_directory(relpath, parent_baton,
-                                         SVN_INVALID_REVNUM,
-                                         scratch_pool, dir_baton_p));
-          SVN_ERR(apply_prop_mods(base_props, work_props,
-                                  editor, NULL, *dir_baton_p,
-                                  scratch_pool));
-        }
-      else if (s->kind == svn_node_file)
-        {
-          void *file_baton;
-
-          /* open */
-          SVN_ERR(editor->open_file(relpath, parent_baton,
-                                    SVN_INVALID_REVNUM,
-                                    scratch_pool, &file_baton));
-          /* modifications */
-          SVN_ERR(apply_file_mods(base_props, work_props,
-                                  stored_base_abspath, stored_work_abspath,
-                                  editor, file_baton,
-                                  scratch_pool));
-          /* close */
-          SVN_ERR(editor->close_file(file_baton, NULL, 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));
-    }
-
-  /* Added file or dir. */
-  if (s->node_status == svn_wc_status_added
-      || s->node_status == svn_wc_status_replaced)
-    {
-      if (s->kind == svn_node_dir)
-        {
-          SVN_ERR(editor->add_directory(relpath, parent_baton,
-                                        NULL, SVN_INVALID_REVNUM,
-                                        scratch_pool, dir_baton_p));
-          SVN_ERR(apply_prop_mods(NULL /*base*/, work_props,
-                                  editor, NULL, *dir_baton_p,
-                                  scratch_pool));
-        }
-      else if (s->kind == svn_node_file)
-        {
-          void *file_baton = NULL;
-
-          /* add */
-          SVN_ERR(editor->add_file(relpath, parent_baton,
-                                   NULL, SVN_INVALID_REVNUM,
-                                   scratch_pool, &file_baton));
-          /* modifications */
-          SVN_ERR(apply_file_mods(NULL /*base*/, work_props,
-                                  NULL /*base*/, stored_work_abspath,
-                                  editor, file_baton,
-                                  scratch_pool));
-          /* close */
-          SVN_ERR(editor->close_file(file_baton, NULL, 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;
-}
-
-/* Baton for shelf_replay_path_visitor(). */
-struct shelf_replay_path_baton_t
-{
-  const char *top_relpath;
-  const char *walk_root_abspath;
-  svn_delta_path_driver_state_t *path_driver_state;
-};
-
-/* Call BATON->walk_func(BATON->walk_baton, relpath, ...) for the shelved
- * 'binary' file stored at ABSPATH.
- * Implements svn_io_walk_func_t. */
-static svn_error_t *
-shelf_replay_path_visitor(void *baton,
-                          const char *abspath,
-                          const apr_finfo_t *finfo,
-                          apr_pool_t *scratch_pool)
-{
-  struct shelf_replay_path_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))
-    {
-      relpath = apr_pstrndup(scratch_pool, relpath, strlen(relpath) - 5);
-      if (!svn_relpath_skip_ancestor(b->top_relpath, relpath))
-        return SVN_NO_ERROR;
-
-      SVN_ERR(svn_delta_path_driver_step(b->path_driver_state, relpath,
-                                         scratch_pool));
-    }
-  return SVN_NO_ERROR;
-}
-
 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)
 {
-  struct shelf_replay_path_baton_t baton;
-  struct path_driver_cb_baton_t pdb = {0};
-  svn_error_t *err;
-
-  baton.top_relpath = top_relpath;
-  baton.walk_root_abspath = shelf_version->files_dir_abspath;
-
-  pdb.shelf_version = shelf_version;
-  pdb.ctx = shelf_version->shelf->ctx;
-
-  SVN_ERR(svn_delta_path_driver_start(&baton.path_driver_state,
-                                      editor, edit_baton,
-                                      path_driver_cb_func, &pdb,
-                                      scratch_pool));
-
-  err = svn_io_dir_walk2(baton.walk_root_abspath, 0 /*wanted*/,
-                         shelf_replay_path_visitor, &baton,
-                         scratch_pool);
-  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
-    svn_error_clear(err);
-  else if (err)
-    {
-      SVN_ERR(editor->abort_edit(edit_baton, scratch_pool));
-      SVN_ERR(err);
-    }
-
-  SVN_ERR(svn_delta_path_driver_finish(baton.path_driver_state, 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;
 }
 
@@ -1679,7 +758,7 @@ struct test_apply_files_baton_t
 static svn_error_t *
 test_apply_file_visitor(void *baton,
                         const char *relpath,
-                        svn_wc_status3_t *s,
+                        const svn_wc_status3_t *s,
                         apr_pool_t *scratch_pool)
 {
   struct test_apply_files_baton_t *b = baton;
@@ -1702,207 +781,6 @@ test_apply_file_visitor(void *baton,
   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_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));
-    }
-
-  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,
@@ -1926,7 +804,10 @@ 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;
@@ -1940,11 +821,27 @@ wc_mods_editor(const svn_delta_editor_t
   dst_wc_url = base->url;
   SVN_ERR(svn_client_open_ra_session2(&ra_session,
                                       dst_wc_url, dst_wc_abspath,
-                                      ctx, scratch_pool, scratch_pool));
+                                      ctx, result_pool, scratch_pool));
   SVN_ERR(svn_client__wc_editor(editor_p, edit_baton_p,
                                 dst_wc_abspath,
-                                NULL, NULL,
-                                ra_session, ctx, scratch_pool));
+                                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;
 }
 
@@ -1959,10 +856,12 @@ svn_client__shelf_apply(svn_client__shel
 
   SVN_ERR(wc_mods_editor(&editor, &edit_baton,
                          shelf->wc_root_abspath,
-                         shelf->ctx, scratch_pool));
+                         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->wc_root_abspath,
@@ -1970,25 +869,92 @@ svn_client__shelf_apply(svn_client__shel
   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_DBG(("unapply: depth %d at '%s'", depth, relpath));
+      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));
-    }
+  SVN_DBG(("unapply: starting"));
+
+  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);
+  SVN_DBG(("unapply: done"));
   return SVN_NO_ERROR;
 }
 
@@ -2019,22 +985,104 @@ 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 *real_wc_abspath = new_shelf_version->shelf->wc_root_abspath;
+  apr_array_header_t *targets = apr_array_make(scratch_pool, 1, sizeof(char *));
+
+  SVN_DBG(("cp-base: cp-dir"));
+  SVN_ERR(svn_io_copy_dir_recursively(
+            real_wc_abspath,
+            svn_dirent_dirname(new_shelf_version->files_dir_abspath, scratch_pool),
+            svn_dirent_basename(new_shelf_version->files_dir_abspath, scratch_pool),
+            TRUE, /* copy_perms */
+            NULL, NULL, /*cancellation*/
+            scratch_pool));
+
+  SVN_DBG(("cp-base: revert"));
+  APR_ARRAY_PUSH(targets, const char *) = new_shelf_version->files_dir_abspath;
+  SVN_ERR(svn_client_revert4(targets, svn_depth_infinity, NULL /*changelists*/,
+                             TRUE /*clear_changelists*/,
+                             FALSE /*metadata_only*/,
+                             FALSE /*added_keep_local*/,
+                             ctx, scratch_pool));
+  SVN_DBG(("revert4: remove unversioned"));
+  /* ### Work around the bug in "revert added-keep-local=false".
+         (We could do other clean-ups here too, but that's not the point.) */
+  SVN_ERR(svn_client_vacuum(new_shelf_version->files_dir_abspath,
+                            TRUE /*remove_unversioned*/,
+                            FALSE /*remove_ignored*/,
+                            FALSE /*fix_timestamps */,
+                            FALSE /*vacuum_pristines*/,
+                            FALSE /*include_externals*/,
+                            ctx, scratch_pool));
+  SVN_DBG(("cp-base: done"));
   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,
@@ -2047,28 +1095,43 @@ 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));
+
+  SVN_DBG(("save: wc-replay starting"));
+  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));
+  SVN_DBG(("save: wc-replay done"));
 
-  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
     {
@@ -2117,10 +1180,8 @@ svn_client__shelf_list(apr_hash_t **shel
   apr_hash_t *dirents;
   apr_hash_index_t *hi;
 
-  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(&wc_root_abspath, &shelves_dir,
+                          local_abspath, ctx, scratch_pool));
   SVN_ERR(svn_io_get_dirents3(&dirents, shelves_dir, FALSE /*only_check_type*/,
                               result_pool, scratch_pool));
 

Modified: subversion/branches/shelving-v3/subversion/tests/cmdline/shelf_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/shelving-v3/subversion/tests/cmdline/shelf_tests.py?rev=1853396&r1=1853395&r2=1853396&view=diff
==============================================================================
--- subversion/branches/shelving-v3/subversion/tests/cmdline/shelf_tests.py (original)
+++ subversion/branches/shelving-v3/subversion/tests/cmdline/shelf_tests.py Mon Feb 11 17:30:24 2019
@@ -115,6 +115,7 @@ def shelve_unshelve_verify(sbox, modifie
     return
 
   # Shelve; check there are no longer any modifications
+  raise
   svntest.actions.run_and_verify_svn(None, [],
                                      'x-shelve', 'foo')
   check_wc_state(wc_dir, virginal_state)
@@ -525,6 +526,7 @@ def shelf_status(sbox):
 
 #----------------------------------------------------------------------
 
+@XFail()
 def shelve_mkdir(sbox):
   "shelve mkdir"
 
@@ -534,7 +536,7 @@ def shelve_mkdir(sbox):
     sbox.simple_mkdir('D', 'D/D2')
     sbox.simple_propset('p', 'v', 'D', 'D/D2')
 
-  shelve_unshelve(sbox, modifier, cannot_shelve=True)
+  shelve_unshelve(sbox, modifier)
 
 #----------------------------------------------------------------------
 
@@ -548,10 +550,11 @@ def shelve_rmdir(sbox):
   def modifier(sbox):
     sbox.simple_rm('A/C', 'A/D/G')
 
-  shelve_unshelve(sbox, modifier, cannot_shelve=True)
+  shelve_unshelve(sbox, modifier)
 
 #----------------------------------------------------------------------
 
+@XFail()
 def shelve_replace_dir(sbox):
   "shelve replace dir"
 
@@ -563,7 +566,7 @@ def shelve_replace_dir(sbox):
     sbox.simple_rm('A/C', 'A/D/G')
     sbox.simple_mkdir('A/C', 'A/C/D2')
 
-  shelve_unshelve(sbox, modifier, cannot_shelve=True)
+  shelve_unshelve(sbox, modifier)
 
 #----------------------------------------------------------------------
 
@@ -576,7 +579,7 @@ def shelve_file_copy(sbox):
     sbox.simple_copy('iota', 'A/ii')
     sbox.simple_propset('p', 'v', 'A/ii')
 
-  shelve_unshelve(sbox, modifier, cannot_shelve=True)
+  shelve_unshelve(sbox, modifier)
 
 #----------------------------------------------------------------------
 
@@ -589,7 +592,7 @@ def shelve_dir_copy(sbox):
     sbox.simple_copy('A/B', 'BB')
     sbox.simple_propset('p', 'v', 'BB')
 
-  shelve_unshelve(sbox, modifier, cannot_shelve=True)
+  shelve_unshelve(sbox, modifier)
 
 #----------------------------------------------------------------------
 
@@ -643,31 +646,21 @@ def refuse_to_shelve_conflict(sbox):
   os.chdir(sbox.wc_dir)
   sbox.wc_dir = ''
 
-  # create a tree conflict victim at an unversioned path
+  # create a conflict
   sbox.simple_mkdir('topdir')
   sbox.simple_commit()
-  sbox.simple_mkdir('topdir/subdir')
-  sbox.simple_commit()
-  sbox.simple_update()
-  sbox.simple_rm('topdir')
-  sbox.simple_commit()
   sbox.simple_update()
   svntest.actions.run_and_verify_svn(
     None, [],
-    'merge', '-c2', '.', '--ignore-ancestry', '--accept', 'postpone')
+    'merge', '-c1', '.', '--ignore-ancestry', '--accept', 'postpone')
+  # check that we did create a conflict
   svntest.actions.run_and_verify_svn(
-    None, 'svn: E155015:.*existing.*conflict.*',
+    None, 'svn: E155035:.*conflict.*',
     'merge', '-c1', '.', '--ignore-ancestry', '--accept', 'postpone')
 
   # attempt to shelve
-  expected_out = svntest.verify.RegexListOutput([
-    r'--- .*',
-    r'--- .*',
-    r'\?     C topdir',
-    r'      > .*',
-    r'      >   not shelved'])
-  svntest.actions.run_and_verify_svn(expected_out,
-                                     '.* 1 path could not be shelved',
+  expected_err = "svn: E155015: .* '.*topdir' remains in conflict"
+  svntest.actions.run_and_verify_svn(None, expected_err,
                                      'x-shelf-save', 'foo')
 
   os.chdir(was_cwd)

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



Mime
View raw message