You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by fu...@apache.org on 2019/07/08 15:19:05 UTC

svn commit: r1862754 [6/9] - in /subversion/branches/swig-py3: ./ build/ac-macros/ build/generator/ doc/ doc/programmer/ notes/ notes/shelving/ subversion/bindings/cxx/ subversion/bindings/cxxhl/ subversion/bindings/javahl/native/ subversion/bindings/j...

Modified: subversion/branches/swig-py3/subversion/libsvn_client/wc_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_client/wc_editor.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_client/wc_editor.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_client/wc_editor.c Mon Jul  8 15:19:03 2019
@@ -1,5 +1,5 @@
 /*
- * copy_foreign.c:  copy from other repository support.
+ * wc_editor.c: editing the local modifications in the WC.
  *
  * ====================================================================
  *    Licensed to the Apache Software Foundation (ASF) under one
@@ -48,20 +48,34 @@
 
 /* WC Modifications Editor.
  *
+ * This editor applies incoming modifications onto the current working state
+ * of the working copy, to produce a new working state.
+ *
+ * Currently, it assumes the working state matches what the edit driver
+ * expects to find, and may throw an error if not.
+ *
+ * For simplicity, we apply incoming edits as they arrive, rather than
+ * queueing them up to apply in a batch.
+ *
  * TODO:
  *   - tests
  *   - use for all existing scenarios ('svn add', 'svn propset', etc.)
- *   - copy-from (half done: in dir_add only, untested)
- *   - text-delta
  *   - Instead of 'root_dir_add' option, probably the driver should anchor
  *     at the parent dir.
  *   - Instead of 'ignore_mergeinfo' option, implement that as a wrapper.
+ *   - Option to quietly accept changes that seem to be already applied
+ *     in the versioned state and/or on disk.
+ *     Consider 'svn add' which assumes items to be added are found on disk.
+ *   - Notification.
  */
 
+/* Everything we need to know about the edit session.
+ */
 struct edit_baton_t
 {
-  apr_pool_t *pool;
   const char *anchor_abspath;
+  svn_boolean_t manage_wc_write_lock;
+  const char *lock_root_abspath;  /* the path locked, when locked */
 
   /* True => 'open_root' method will act as 'add_directory' */
   svn_boolean_t root_dir_add;
@@ -76,22 +90,20 @@ struct edit_baton_t
   void *notify_baton;
 };
 
+/* Everything we need to know about a directory that's open for edits.
+ */
 struct dir_baton_t
 {
   apr_pool_t *pool;
 
-  struct dir_baton_t *pb;
   struct edit_baton_t *eb;
 
   const char *local_abspath;
-
-  svn_boolean_t created;  /* already under version control in the WC */
-  apr_hash_t *properties;
-
-  int users;
 };
 
-/*  */
+/* Join PATH onto ANCHOR_ABSPATH.
+ * Throw an error if the result is outside ANCHOR_ABSPATH.
+ */
 static svn_error_t *
 get_path(const char **local_abspath_p,
          const char *anchor_abspath,
@@ -112,6 +124,72 @@ get_path(const char **local_abspath_p,
   return SVN_NO_ERROR;
 }
 
+/* Create a directory on disk and add it to version control,
+ * with no properties.
+ */
+static svn_error_t *
+mkdir(const char *abspath,
+      struct edit_baton_t *eb,
+      apr_pool_t *scratch_pool)
+{
+  SVN_ERR(svn_io_make_dir_recursively(abspath, scratch_pool));
+  SVN_ERR(svn_wc_add_from_disk3(eb->wc_ctx, abspath,
+                                NULL /*properties*/,
+                                TRUE /* skip checks */,
+                                eb->notify_func, eb->notify_baton,
+                                scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+/* Prepare to open or add a directory: initialize a new dir baton.
+ *
+ * If PATH is "" and PB is null, it represents the root directory of
+ * the edit; otherwise PATH is not "" and PB is not null.
+ */
+static svn_error_t *
+dir_open_or_add(struct dir_baton_t **child_dir_baton,
+                const char *path,
+                struct dir_baton_t *pb,
+                struct edit_baton_t *eb,
+                apr_pool_t *dir_pool)
+{
+  struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
+
+  db->pool = dir_pool;
+  db->eb = eb;
+
+  SVN_ERR(get_path(&db->local_abspath,
+                   eb->anchor_abspath, path, dir_pool));
+
+  *child_dir_baton = db;
+  return SVN_NO_ERROR;
+}
+
+/*  */
+static svn_error_t *
+release_write_lock(struct edit_baton_t *eb,
+                   apr_pool_t *scratch_pool)
+{
+  if (eb->lock_root_abspath)
+    {
+      SVN_ERR(svn_wc__release_write_lock(
+                eb->ctx->wc_ctx, eb->lock_root_abspath, scratch_pool));
+      eb->lock_root_abspath = NULL;
+    }
+  return SVN_NO_ERROR;
+}
+
+/*  */
+static apr_status_t
+pool_cleanup_handler(void *root_baton)
+{
+  struct dir_baton_t *db = root_baton;
+  struct edit_baton_t *eb = db->eb;
+
+  svn_error_clear(release_write_lock(eb, db->pool));
+  return APR_SUCCESS;
+}
+
 /* svn_delta_editor_t function */
 static svn_error_t *
 edit_open(void *edit_baton,
@@ -120,28 +198,38 @@ edit_open(void *edit_baton,
           void **root_baton)
 {
   struct edit_baton_t *eb = edit_baton;
-  apr_pool_t *dir_pool = svn_pool_create(eb->pool);
-  struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
+  struct dir_baton_t *db;
 
-  db->pool = dir_pool;
-  db->eb = eb;
-  db->users = 1;
-  db->local_abspath = eb->anchor_abspath;
+  SVN_ERR(dir_open_or_add(&db, "", NULL, eb, result_pool));
+
+  /* Acquire a WC write lock */
+  if (eb->manage_wc_write_lock)
+    {
+      apr_pool_cleanup_register(db->pool, db,
+                                pool_cleanup_handler,
+                                apr_pool_cleanup_null);
+      SVN_ERR(svn_wc__acquire_write_lock(&eb->lock_root_abspath,
+                                         eb->ctx->wc_ctx,
+                                         eb->anchor_abspath,
+                                         FALSE /*lock_anchor*/,
+                                         db->pool, db->pool));
+    }
 
-  db->created = !(eb->root_dir_add);
   if (eb->root_dir_add)
-    SVN_ERR(svn_io_make_dir_recursively(eb->anchor_abspath, dir_pool));
+    {
+      SVN_ERR(mkdir(db->local_abspath, eb, result_pool));
+    }
 
   *root_baton = db;
-
   return SVN_NO_ERROR;
 }
 
 /* svn_delta_editor_t function */
 static svn_error_t *
-edit_close(void *edit_baton,
-           apr_pool_t *scratch_pool)
+edit_close_or_abort(void *edit_baton,
+                    apr_pool_t *scratch_pool)
 {
+  SVN_ERR(release_write_lock(edit_baton, scratch_pool));
   return SVN_NO_ERROR;
 }
 
@@ -167,31 +255,6 @@ delete_entry(const char *path,
   return SVN_NO_ERROR;
 }
 
-/*  */
-static svn_error_t *
-dir_open_or_add(const char *path,
-                void *parent_baton,
-                struct dir_baton_t **child_baton)
-{
-  struct dir_baton_t *pb = parent_baton;
-  struct edit_baton_t *eb = pb->eb;
-  apr_pool_t *dir_pool = svn_pool_create(pb->pool);
-  struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
-
-  pb->users++;
-
-  db->pb = pb;
-  db->eb = pb->eb;
-  db->pool = dir_pool;
-  db->users = 1;
-
-  SVN_ERR(get_path(&db->local_abspath,
-                   eb->anchor_abspath, path, db->pool));
-
-  *child_baton = db;
-  return SVN_NO_ERROR;
-}
-
 /* An svn_delta_editor_t function. */
 static svn_error_t *
 dir_open(const char *path,
@@ -200,10 +263,11 @@ dir_open(const char *path,
          apr_pool_t *result_pool,
          void **child_baton)
 {
+  struct dir_baton_t *pb = parent_baton;
+  struct edit_baton_t *eb = pb->eb;
   struct dir_baton_t *db;
 
-  SVN_ERR(dir_open_or_add(path, parent_baton, &db));
-  db->created = TRUE;
+  SVN_ERR(dir_open_or_add(&db, path, pb, eb, result_pool));
 
   *child_baton = db;
   return SVN_NO_ERROR;
@@ -217,9 +281,13 @@ dir_add(const char *path,
         apr_pool_t *result_pool,
         void **child_baton)
 {
+  struct dir_baton_t *pb = parent_baton;
+  struct edit_baton_t *eb = pb->eb;
   struct dir_baton_t *db;
+  /* ### Our caller should be providing a scratch pool */
+  apr_pool_t *scratch_pool = svn_pool_create(result_pool);
 
-  SVN_ERR(dir_open_or_add(path, parent_baton, &db));
+  SVN_ERR(dir_open_or_add(&db, path, pb, eb, result_pool));
 
   if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_revision))
     {
@@ -229,15 +297,16 @@ dir_add(const char *path,
                                            copyfrom_revision,
                                            db->local_abspath,
                                            db->eb->ra_session,
-                                           db->eb->ctx, db->pool));
-      db->created = TRUE;
+                                           db->eb->ctx,
+                                           scratch_pool));
     }
   else
     {
-      SVN_ERR(svn_io_make_dir_recursively(db->local_abspath, db->pool));
+      SVN_ERR(mkdir(db->local_abspath, eb, result_pool));
     }
 
   *child_baton = db;
+  svn_pool_destroy(scratch_pool);
   return SVN_NO_ERROR;
 }
 
@@ -249,78 +318,19 @@ dir_change_prop(void *dir_baton,
 {
   struct dir_baton_t *db = dir_baton;
   struct edit_baton_t *eb = db->eb;
-  svn_prop_kind_t prop_kind;
-
-  prop_kind = svn_property_kind2(name);
 
-  if (prop_kind != svn_prop_regular_kind
+  if (svn_property_kind2(name) != svn_prop_regular_kind
       || (eb->ignore_mergeinfo_changes && ! strcmp(name, SVN_PROP_MERGEINFO)))
     {
       /* We can't handle DAV, ENTRY and merge specific props here */
       return SVN_NO_ERROR;
     }
 
-  if (! db->created)
-    {
-      /* Store properties to be added later in svn_wc_add_from_disk3() */
-      if (! db->properties)
-        db->properties = apr_hash_make(db->pool);
-
-      if (value != NULL)
-        svn_hash_sets(db->properties, apr_pstrdup(db->pool, name),
-                      svn_string_dup(value, db->pool));
-    }
-  else
-    {
-      SVN_ERR(svn_wc_prop_set4(eb->wc_ctx, db->local_abspath, name, value,
-                               svn_depth_empty, FALSE, NULL,
-                               NULL, NULL, /* Cancellation */
-                               NULL, NULL, /* Notification */
-                               scratch_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Releases the directory baton if there are no more users */
-static svn_error_t *
-maybe_done(struct dir_baton_t *db)
-{
-  db->users--;
-
-  if (db->users == 0)
-    {
-      struct dir_baton_t *pb = db->pb;
-
-      svn_pool_clear(db->pool);
-
-      if (pb)
-        SVN_ERR(maybe_done(pb));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-ensure_added(struct dir_baton_t *db,
-             apr_pool_t *scratch_pool)
-{
-  if (db->created)
-    return SVN_NO_ERROR;
-
-  if (db->pb)
-    SVN_ERR(ensure_added(db->pb, scratch_pool));
-
-  db->created = TRUE;
-
-  /* Add the directory with all the already collected properties */
-  SVN_ERR(svn_wc_add_from_disk3(db->eb->wc_ctx,
-                                db->local_abspath,
-                                db->properties,
-                                TRUE /* skip checks */,
-                                db->eb->notify_func,
-                                db->eb->notify_baton,
-                                scratch_pool));
+  SVN_ERR(svn_wc_prop_set4(eb->wc_ctx, db->local_abspath, name, value,
+                           svn_depth_empty, FALSE, NULL,
+                           NULL, NULL, /* Cancellation */
+                           NULL, NULL, /* Notification */
+                           scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -329,50 +339,57 @@ static svn_error_t *
 dir_close(void *dir_baton,
           apr_pool_t *scratch_pool)
 {
-  struct dir_baton_t *db = dir_baton;
-  /*struct edit_baton_t *eb = db->eb;*/
-
-  SVN_ERR(ensure_added(db, scratch_pool));
-
-  SVN_ERR(maybe_done(db));
-
   return SVN_NO_ERROR;
 }
 
+/* Everything we need to know about a file that's open for edits.
+ */
 struct file_baton_t
 {
   apr_pool_t *pool;
 
-  struct dir_baton_t *pb;
   struct edit_baton_t *eb;
 
   const char *local_abspath;
-  svn_boolean_t created;  /* already under version control in the WC */
-  apr_hash_t *properties;
-
-  svn_boolean_t writing;
-  unsigned char digest[APR_MD5_DIGESTSIZE];
 
+  /* fields for the transfer of text changes */
+  const char *writing_file;
+  unsigned char digest[APR_MD5_DIGESTSIZE];  /* MD5 digest of new fulltext */
+  svn_stream_t *wc_file_read_stream, *tmp_file_write_stream;
   const char *tmp_path;
 };
 
+/* Create a new file on disk and add it to version control.
+ *
+ * The file is empty and has no properties.
+ */
+static svn_error_t *
+mkfile(const char *abspath,
+       struct edit_baton_t *eb,
+       apr_pool_t *scratch_pool)
+{
+  SVN_ERR(svn_io_file_create_empty(abspath, scratch_pool));
+  SVN_ERR(svn_wc_add_from_disk3(eb->wc_ctx, abspath,
+                                NULL /*properties*/,
+                                TRUE /* skip checks */,
+                                eb->notify_func, eb->notify_baton,
+                                scratch_pool));
+  return SVN_NO_ERROR;
+}
+
 /*  */
 static svn_error_t *
 file_open_or_add(const char *path,
                  void *parent_baton,
-                 struct file_baton_t **file_baton)
+                 struct file_baton_t **file_baton,
+                 apr_pool_t *file_pool)
 {
   struct dir_baton_t *pb = parent_baton;
   struct edit_baton_t *eb = pb->eb;
-  apr_pool_t *file_pool = svn_pool_create(pb->pool);
   struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb));
 
-  pb->users++;
-
   fb->pool = file_pool;
   fb->eb = eb;
-  fb->pb = pb;
-
   SVN_ERR(get_path(&fb->local_abspath,
                    eb->anchor_abspath, path, fb->pool));
 
@@ -389,8 +406,7 @@ file_open(const char *path,
 {
   struct file_baton_t *fb;
 
-  SVN_ERR(file_open_or_add(path, parent_baton, &fb));
-  fb->created = TRUE;
+  SVN_ERR(file_open_or_add(path, parent_baton, &fb, result_pool));
 
   *file_baton = fb;
   return SVN_NO_ERROR;
@@ -406,7 +422,7 @@ file_add(const char *path,
 {
   struct file_baton_t *fb;
 
-  SVN_ERR(file_open_or_add(path, parent_baton, &fb));
+  SVN_ERR(file_open_or_add(path, parent_baton, &fb, result_pool));
 
   if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_revision))
     {
@@ -417,7 +433,10 @@ file_add(const char *path,
                                            fb->local_abspath,
                                            fb->eb->ra_session,
                                            fb->eb->ctx, fb->pool));
-      fb->created = TRUE;
+    }
+  else
+    {
+      SVN_ERR(mkfile(fb->local_abspath, fb->eb, result_pool));
     }
 
   *file_baton = fb;
@@ -432,35 +451,19 @@ file_change_prop(void *file_baton,
 {
   struct file_baton_t *fb = file_baton;
   struct edit_baton_t *eb = fb->eb;
-  svn_prop_kind_t prop_kind;
 
-  prop_kind = svn_property_kind2(name);
-
-  if (prop_kind != svn_prop_regular_kind
+  if (svn_property_kind2(name) != svn_prop_regular_kind
       || (eb->ignore_mergeinfo_changes && ! strcmp(name, SVN_PROP_MERGEINFO)))
     {
       /* We can't handle DAV, ENTRY and merge specific props here */
       return SVN_NO_ERROR;
     }
 
-  if (! fb->created)
-    {
-      /* Store properties to be added later in svn_wc_add_from_disk3() */
-      if (! fb->properties)
-        fb->properties = apr_hash_make(fb->pool);
-
-      if (value != NULL)
-        svn_hash_sets(fb->properties, apr_pstrdup(fb->pool, name),
-                      svn_string_dup(value, fb->pool));
-    }
-  else
-    {
-      SVN_ERR(svn_wc_prop_set4(eb->wc_ctx, fb->local_abspath, name, value,
-                               svn_depth_empty, FALSE, NULL,
-                               NULL, NULL, /* Cancellation */
-                               NULL, NULL, /* Notification */
-                               scratch_pool));
-    }
+  SVN_ERR(svn_wc_prop_set4(eb->wc_ctx, fb->local_abspath, name, value,
+                           svn_depth_empty, FALSE, NULL,
+                           NULL, NULL, /* Cancellation */
+                           NULL, NULL, /* Notification */
+                           scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -473,16 +476,27 @@ file_textdelta(void *file_baton,
                void **handler_baton)
 {
   struct file_baton_t *fb = file_baton;
-  svn_stream_t *target;
+  const char *target_dir = svn_dirent_dirname(fb->local_abspath, fb->pool);
+  svn_error_t *err;
+
+  SVN_ERR_ASSERT(! fb->writing_file);
 
-  SVN_ERR_ASSERT(! fb->writing);
+  err = svn_stream_open_readonly(&fb->wc_file_read_stream, fb->local_abspath,
+                                 fb->pool, fb->pool);
+  if (err && APR_STATUS_IS_ENOENT(err->apr_err))
+    {
+      svn_error_clear(err);
+      fb->wc_file_read_stream = svn_stream_empty(fb->pool);
+    }
+  else
+    SVN_ERR(err);
 
-  SVN_ERR(svn_stream_open_writable(&target, fb->local_abspath, fb->pool,
-                                   fb->pool));
+  SVN_ERR(svn_stream_open_unique(&fb->tmp_file_write_stream, &fb->writing_file,
+                                 target_dir, svn_io_file_del_none,
+                                 fb->pool, fb->pool));
 
-  fb->writing = TRUE;
-  svn_txdelta_apply(svn_stream_empty(fb->pool) /* source */,
-                    target,
+  svn_txdelta_apply(fb->wc_file_read_stream,
+                    fb->tmp_file_write_stream,
                     fb->digest,
                     fb->local_abspath,
                     fb->pool,
@@ -493,35 +507,19 @@ file_textdelta(void *file_baton,
 }
 
 static svn_error_t *
-ensure_added_file(struct file_baton_t *fb,
-                  apr_pool_t *scratch_pool)
-{
-  struct edit_baton_t *eb = fb->eb;
-
-  if (fb->created)
-    return SVN_NO_ERROR;
-
-  if (fb->pb)
-    SVN_ERR(ensure_added(fb->pb, scratch_pool));
-
-  fb->created = TRUE;
-
-  /* Add the file with all the already collected properties */
-  SVN_ERR(svn_wc_add_from_disk3(eb->wc_ctx, fb->local_abspath, fb->properties,
-                                TRUE /* skip checks */,
-                                eb->notify_func, eb->notify_baton,
-                                fb->pool));
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
 file_close(void *file_baton,
            const char *text_checksum,
            apr_pool_t *scratch_pool)
 {
   struct file_baton_t *fb = file_baton;
-  struct dir_baton_t *pb = fb->pb;
+
+  /* If we have text changes, write them to disk */
+  if (fb->writing_file)
+    {
+      SVN_ERR(svn_stream_close(fb->wc_file_read_stream));
+      SVN_ERR(svn_io_file_rename2(fb->writing_file, fb->local_abspath,
+                                  FALSE /*flush*/, scratch_pool));
+    }
 
   if (text_checksum)
     {
@@ -543,11 +541,6 @@ file_close(void *file_baton,
                                                     fb->pool)));
     }
 
-  SVN_ERR(ensure_added_file(fb, fb->pool));
-
-  svn_pool_destroy(fb->pool);
-  SVN_ERR(maybe_done(pb));
-
   return SVN_NO_ERROR;
 }
 
@@ -557,6 +550,7 @@ svn_client__wc_editor_internal(const svn
                                const char *dst_abspath,
                                svn_boolean_t root_dir_add,
                                svn_boolean_t ignore_mergeinfo_changes,
+                               svn_boolean_t manage_wc_write_lock,
                                svn_wc_notify_func2_t notify_func,
                                void *notify_baton,
                                svn_ra_session_t *ra_session,
@@ -566,8 +560,9 @@ svn_client__wc_editor_internal(const svn
   svn_delta_editor_t *editor = svn_delta_default_editor(result_pool);
   struct edit_baton_t *eb = apr_pcalloc(result_pool, sizeof(*eb));
 
-  eb->pool = result_pool;
   eb->anchor_abspath = apr_pstrdup(result_pool, dst_abspath);
+  eb->manage_wc_write_lock = manage_wc_write_lock;
+  eb->lock_root_abspath = NULL;
   eb->root_dir_add = root_dir_add;
   eb->ignore_mergeinfo_changes = ignore_mergeinfo_changes;
 
@@ -578,7 +573,8 @@ svn_client__wc_editor_internal(const svn
   eb->notify_baton  = notify_baton;
 
   editor->open_root = edit_open;
-  editor->close_edit = edit_close;
+  editor->close_edit = edit_close_or_abort;
+  editor->abort_edit = edit_close_or_abort;
 
   editor->delete_entry = delete_entry;
 
@@ -612,8 +608,48 @@ svn_client__wc_editor(const svn_delta_ed
                                          dst_abspath,
                                          FALSE /*root_dir_add*/,
                                          FALSE /*ignore_mergeinfo_changes*/,
+                                         TRUE /*manage_wc_write_lock*/,
                                          notify_func, notify_baton,
                                          ra_session,
                                          ctx, result_pool));
   return SVN_NO_ERROR;
 }
+
+svn_error_t *
+svn_client__wc_copy_mods(const char *src_wc_abspath,
+                         const char *dst_wc_abspath,
+                         svn_wc_notify_func2_t notify_func,
+                         void *notify_baton,
+                         svn_client_ctx_t *ctx,
+                         apr_pool_t *scratch_pool)
+{
+  svn_client__pathrev_t *base;
+  const char *dst_wc_url;
+  svn_ra_session_t *ra_session;
+  const svn_delta_editor_t *editor;
+  void *edit_baton;
+  apr_array_header_t *src_targets = apr_array_make(scratch_pool, 1,
+                                                   sizeof(char *));
+
+  /* We'll need an RA session to obtain the base of any copies */
+  SVN_ERR(svn_client__wc_node_get_base(&base,
+                                       src_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, scratch_pool, scratch_pool));
+  SVN_ERR(svn_client__wc_editor(&editor, &edit_baton,
+                                dst_wc_abspath,
+                                NULL, NULL, /*notification*/
+                                ra_session, ctx, 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;
+}

Modified: subversion/branches/swig-py3/subversion/libsvn_delta/branch_compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_delta/branch_compat.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_delta/branch_compat.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_delta/branch_compat.c Mon Jul  8 15:19:03 2019
@@ -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,8 +915,8 @@ 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.  */
       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));

Modified: subversion/branches/swig-py3/subversion/libsvn_delta/compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_delta/compat.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_delta/compat.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_delta/compat.c Mon Jul  8 15:19:03 2019
@@ -1577,6 +1577,8 @@ drive_ev1_props(const struct editor_bato
 /* Conforms to svn_delta_path_driver_cb_func_t  */
 static svn_error_t *
 apply_change(void **dir_baton,
+             const svn_delta_editor_t *deditor,
+             void *dedit_baton,
              void *parent_baton,
              void *callback_baton,
              const char *ev1_relpath,
@@ -1614,8 +1616,8 @@ apply_change(void **dir_baton,
 
   if (change->action == RESTRUCTURE_DELETE)
     {
-      SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting,
-                                        parent_baton, scratch_pool));
+      SVN_ERR(deditor->delete_entry(ev1_relpath, change->deleting,
+                                    parent_baton, scratch_pool));
 
       /* No futher action possible for this node.  */
       return SVN_NO_ERROR;
@@ -1627,11 +1629,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));
+        SVN_ERR(deditor->absent_directory(ev1_relpath, parent_baton,
+                                          scratch_pool));
       else
-        SVN_ERR(eb->deditor->absent_file(ev1_relpath, parent_baton,
-                                         scratch_pool));
+        SVN_ERR(deditor->absent_file(ev1_relpath, parent_baton,
+                                     scratch_pool));
 
       /* No further action possible for this node.  */
       return SVN_NO_ERROR;
@@ -1645,8 +1647,8 @@ apply_change(void **dir_baton,
 
       /* Do we have an old node to delete first?  */
       if (SVN_IS_VALID_REVNUM(change->deleting))
-        SVN_ERR(eb->deditor->delete_entry(ev1_relpath, change->deleting,
-                                          parent_baton, scratch_pool));
+        SVN_ERR(deditor->delete_entry(ev1_relpath, change->deleting,
+                                      parent_baton, scratch_pool));
 
       /* Are we copying the node from somewhere?  */
       if (change->copyfrom_path)
@@ -1669,24 +1671,24 @@ 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));
+        SVN_ERR(deditor->add_directory(ev1_relpath, parent_baton,
+                                       copyfrom_url, copyfrom_rev,
+                                       result_pool, dir_baton));
       else
-        SVN_ERR(eb->deditor->add_file(ev1_relpath, parent_baton,
-                                      copyfrom_url, copyfrom_rev,
-                                      result_pool, &file_baton));
+        SVN_ERR(deditor->add_file(ev1_relpath, parent_baton,
+                                  copyfrom_url, copyfrom_rev,
+                                  result_pool, &file_baton));
     }
   else
     {
       if (change->kind == svn_node_dir)
-        SVN_ERR(eb->deditor->open_directory(ev1_relpath, parent_baton,
-                                            change->changing,
-                                            result_pool, dir_baton));
+        SVN_ERR(deditor->open_directory(ev1_relpath, parent_baton,
+                                        change->changing,
+                                        result_pool, dir_baton));
       else
-        SVN_ERR(eb->deditor->open_file(ev1_relpath, parent_baton,
-                                       change->changing,
-                                       result_pool, &file_baton));
+        SVN_ERR(deditor->open_file(ev1_relpath, parent_baton,
+                                   change->changing,
+                                   result_pool, &file_baton));
     }
 
   /* Apply any properties in CHANGE to the node.  */
@@ -1703,8 +1705,8 @@ apply_change(void **dir_baton,
 
       /* ### 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,
-                                           &handler, &handler_baton));
+      SVN_ERR(deditor->apply_textdelta(file_baton, NULL, scratch_pool,
+                                       &handler, &handler_baton));
       SVN_ERR(svn_stream_open_readonly(&contents, change->contents_abspath,
                                        scratch_pool, scratch_pool));
       /* ### it would be nice to send a true txdelta here, but whatever.  */
@@ -1718,7 +1720,7 @@ apply_change(void **dir_baton,
       const char *digest = svn_checksum_to_cstring(change->checksum,
                                                    scratch_pool);
 
-      SVN_ERR(eb->deditor->close_file(file_baton, digest, scratch_pool));
+      SVN_ERR(deditor->close_file(file_baton, digest, scratch_pool));
     }
 
   return SVN_NO_ERROR;
@@ -1747,7 +1749,7 @@ drive_changes(const struct editor_baton
 
   /* Get a sorted list of Ev1-relative paths.  */
   paths = get_sorted_paths(eb->changes, eb->base_relpath, scratch_pool);
-  SVN_ERR(svn_delta_path_driver2(eb->deditor, eb->dedit_baton, paths,
+  SVN_ERR(svn_delta_path_driver3(eb->deditor, eb->dedit_baton, paths,
                                  FALSE, apply_change, (void *)eb,
                                  scratch_pool));
 

Modified: subversion/branches/swig-py3/subversion/libsvn_delta/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_delta/deprecated.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_delta/deprecated.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_delta/deprecated.c Mon Jul  8 15:19:03 2019
@@ -30,6 +30,79 @@
 #include "svn_sorts.h"
 
 
+struct path_driver_2_to_3_baton_t
+{
+  svn_delta_path_driver_cb_func_t callback_func;
+  void *callback_baton;
+  svn_boolean_t slash_prefix;
+};
+
+/* Convert from a newer to older callback
+ */
+static svn_error_t *
+path_driver_2_to_3_func(void **dir_baton,
+                        const svn_delta_editor_t *editor,
+                        void *edit_baton,
+                        void *parent_baton,
+                        void *callback_baton,
+                        const char *path,
+                        apr_pool_t *pool)
+{
+  struct path_driver_2_to_3_baton_t *b = callback_baton;
+
+  if (b->slash_prefix)
+    path = apr_pstrcat(pool, "/", path, SVN_VA_NULL);
+
+  /* Just drop the 'editor' parameters */
+  SVN_ERR(b->callback_func(dir_baton, parent_baton,
+                           b->callback_baton,
+                           path, pool));
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_delta_path_driver2(const svn_delta_editor_t *editor,
+                       void *edit_baton,
+                       const apr_array_header_t *paths,
+                       svn_boolean_t sort_paths,
+                       svn_delta_path_driver_cb_func_t callback_func,
+                       void *callback_baton,
+                       apr_pool_t *pool)
+{
+  struct path_driver_2_to_3_baton_t b;
+  int i;
+
+  b.callback_func = callback_func;
+  b.callback_baton = callback_baton;
+  b.slash_prefix = FALSE;
+
+  /* Remove any '/' prefix from incoming paths. Arrange to add a '/'
+     prefix to all paths for the callback, if any incoming path had one. */
+  for (i = 0; i < paths->nelts; i++)
+    {
+      const char *path = APR_ARRAY_IDX(paths, i, const char *);
+
+      if (path[0] == '/')
+        {
+          /* Re-allocate the array and note that we found a '/' prefix. */
+          if (!b.slash_prefix)
+            {
+              paths = apr_array_copy(pool, paths);
+              b.slash_prefix = TRUE;
+            }
+
+          /* Modify each array element that had a '/' prefix */
+          APR_ARRAY_IDX(paths, i, const char *) = path + 1;
+        }
+    }
+
+  SVN_ERR(svn_delta_path_driver3(editor, edit_baton,
+                                 paths, sort_paths,
+                                 path_driver_2_to_3_func, &b,
+                                 pool));
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_delta_path_driver(const svn_delta_editor_t *editor,
                       void *edit_baton,

Modified: subversion/branches/swig-py3/subversion/libsvn_delta/path_driver.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_delta/path_driver.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_delta/path_driver.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_delta/path_driver.c Mon Jul  8 15:19:03 2019
@@ -31,7 +31,6 @@
 #include "svn_dirent_uri.h"
 #include "svn_path.h"
 #include "svn_sorts.h"
-#include "private/svn_fspath.h"
 #include "private/svn_sorts_private.h"
 
 
@@ -45,6 +44,22 @@ typedef struct dir_stack_t
 } dir_stack_t;
 
 
+/* Push onto dir_stack a new item allocated in POOL and containing
+ * DIR_BATON and POOL.
+ */
+static void
+push_dir_stack_item(apr_array_header_t *db_stack,
+                    void *dir_baton,
+                    apr_pool_t *pool)
+{
+  dir_stack_t *item = apr_pcalloc(pool, sizeof(*item));
+
+  item->dir_baton = dir_baton;
+  item->pool = pool;
+  APR_ARRAY_PUSH(db_stack, dir_stack_t *) = item;
+}
+
+
 /* Call EDITOR's open_directory() function with the PATH argument, then
  * add the resulting dir baton to the dir baton stack.
  */
@@ -72,10 +87,7 @@ open_dir(apr_array_header_t *db_stack,
                                  &db));
 
   /* Now add the dir baton to the stack. */
-  item = apr_pcalloc(subpool, sizeof(*item));
-  item->dir_baton = db;
-  item->pool = subpool;
-  APR_ARRAY_PUSH(db_stack, dir_stack_t *) = item;
+  push_dir_stack_item(db_stack, db, subpool);
 
   return SVN_NO_ERROR;
 }
@@ -131,168 +143,215 @@ count_components(const char *path)
 
 /*** Public interfaces ***/
 svn_error_t *
-svn_delta_path_driver2(const svn_delta_editor_t *editor,
+svn_delta_path_driver3(const svn_delta_editor_t *editor,
                        void *edit_baton,
-                       const apr_array_header_t *paths,
+                       const apr_array_header_t *relpaths,
                        svn_boolean_t sort_paths,
-                       svn_delta_path_driver_cb_func_t callback_func,
+                       svn_delta_path_driver_cb_func2_t callback_func,
                        void *callback_baton,
                        apr_pool_t *pool)
 {
-  apr_array_header_t *db_stack = apr_array_make(pool, 4, sizeof(void *));
-  const char *last_path = NULL;
-  int i = 0;
-  void *parent_db = NULL, *db = NULL;
-  const char *path;
+  svn_delta_path_driver_state_t *state;
+  int i;
   apr_pool_t *subpool, *iterpool;
-  dir_stack_t *item;
 
   /* Do nothing if there are no paths. */
-  if (! paths->nelts)
+  if (! relpaths->nelts)
     return SVN_NO_ERROR;
 
   subpool = svn_pool_create(pool);
   iterpool = svn_pool_create(pool);
 
   /* sort paths if necessary */
-  if (sort_paths && paths->nelts > 1)
+  if (sort_paths && relpaths->nelts > 1)
     {
-      apr_array_header_t *sorted = apr_array_copy(subpool, paths);
+      apr_array_header_t *sorted = apr_array_copy(subpool, relpaths);
       svn_sort__array(sorted, svn_sort_compare_paths);
-      paths = sorted;
+      relpaths = sorted;
     }
 
-  item = apr_pcalloc(subpool, sizeof(*item));
-
-  /* If the root of the edit is also a target path, we want to call
-     the callback function to let the user open the root directory and
-     do what needs to be done.  Otherwise, we'll do the open_root()
-     ourselves. */
-  path = APR_ARRAY_IDX(paths, 0, const char *);
-  if (svn_path_is_empty(path))
-    {
-      SVN_ERR(callback_func(&db, NULL, callback_baton, path, subpool));
-      last_path = path;
-      i++;
-    }
-  else
-    {
-      SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM, subpool, &db));
-    }
-  item->pool = subpool;
-  item->dir_baton = db;
-  APR_ARRAY_PUSH(db_stack, void *) = item;
+  SVN_ERR(svn_delta_path_driver_start(&state,
+                                      editor, edit_baton,
+                                      callback_func, callback_baton,
+                                      pool));
 
   /* Now, loop over the commit items, traversing the URL tree and
      driving the editor. */
-  for (; i < paths->nelts; i++)
+  for (i = 0; i < relpaths->nelts; i++)
     {
-      const char *pdir;
-      const char *common = "";
-      size_t common_len;
+      const char *relpath;
 
       /* Clear the iteration pool. */
       svn_pool_clear(iterpool);
 
       /* Get the next path. */
-      path = APR_ARRAY_IDX(paths, i, const char *);
+      relpath = APR_ARRAY_IDX(relpaths, i, const char *);
 
-      /*** Step A - Find the common ancestor of the last path and the
-           current one.  For the first iteration, this is just the
-           empty string. ***/
-      if (i > 0)
-        common = (last_path[0] == '/')
-          ? svn_fspath__get_longest_ancestor(last_path, path, iterpool)
-          : svn_relpath_get_longest_ancestor(last_path, path, iterpool);
-      common_len = strlen(common);
-
-      /*** Step B - Close any directories between the last path and
-           the new common ancestor, if any need to be closed.
-           Sometimes there is nothing to do here (like, for the first
-           iteration, or when the last path was an ancestor of the
-           current one). ***/
-      if ((i > 0) && (strlen(last_path) > common_len))
-        {
-          const char *rel = last_path + (common_len ? (common_len + 1) : 0);
-          int count = count_components(rel);
-          while (count--)
-            {
-              SVN_ERR(pop_stack(db_stack, editor));
-            }
-        }
+      SVN_ERR(svn_delta_path_driver_step(state, relpath, iterpool));
+    }
 
-      /*** Step C - Open any directories between the common ancestor
-           and the parent of the current path. ***/
-      if (*path == '/')
-        pdir = svn_fspath__dirname(path, iterpool);
-      else
-        pdir = svn_relpath_dirname(path, iterpool);
+  /* Destroy the iteration subpool. */
+  svn_pool_destroy(iterpool);
 
-      if (strlen(pdir) > common_len)
-        {
-          const char *piece = pdir + common_len + 1;
+  SVN_ERR(svn_delta_path_driver_finish(state, pool));
 
-          while (1)
-            {
-              const char *rel = pdir;
-
-              /* Find the first separator. */
-              piece = strchr(piece, '/');
-
-              /* Calculate REL as the portion of PDIR up to (but not
-                 including) the location to which PIECE is pointing. */
-              if (piece)
-                rel = apr_pstrmemdup(iterpool, pdir, piece - pdir);
-
-              /* Open the subdirectory. */
-              SVN_ERR(open_dir(db_stack, editor, rel, pool));
-
-              /* If we found a '/', advance our PIECE pointer to
-                 character just after that '/'.  Otherwise, we're
-                 done.  */
-              if (piece)
-                piece++;
-              else
-                break;
-            }
-        }
+  return SVN_NO_ERROR;
+}
 
-      /*** Step D - Tell our caller to handle the current path. ***/
-      item = APR_ARRAY_IDX(db_stack, db_stack->nelts - 1, void *);
-      parent_db = item->dir_baton;
-      subpool = svn_pool_create(pool);
-      SVN_ERR(callback_func(&db, parent_db, callback_baton, path, subpool));
-      if (db)
+struct svn_delta_path_driver_state_t
+{
+  const svn_delta_editor_t *editor;
+  void *edit_baton;
+  svn_delta_path_driver_cb_func2_t callback_func;
+  void *callback_baton;
+  apr_array_header_t *db_stack;
+  const char *last_path;
+  apr_pool_t *pool;  /* at least the lifetime of the entire drive */
+};
+
+svn_error_t *
+svn_delta_path_driver_start(svn_delta_path_driver_state_t **state_p,
+                            const svn_delta_editor_t *editor,
+                            void *edit_baton,
+                            svn_delta_path_driver_cb_func2_t callback_func,
+                            void *callback_baton,
+                            apr_pool_t *pool)
+{
+  svn_delta_path_driver_state_t *state = apr_pcalloc(pool, sizeof(*state));
+
+  state->editor = editor;
+  state->edit_baton = edit_baton;
+  state->callback_func = callback_func;
+  state->callback_baton = callback_baton;
+  state->db_stack = apr_array_make(pool, 4, sizeof(void *));
+  state->last_path = NULL;
+  state->pool = pool;
+
+  *state_p = state;
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_delta_path_driver_step(svn_delta_path_driver_state_t *state,
+                           const char *relpath,
+                           apr_pool_t *scratch_pool)
+{
+  const char *pdir;
+  const char *common = "";
+  size_t common_len;
+  apr_pool_t *subpool;
+  dir_stack_t *item;
+  void *parent_db, *db;
+
+  SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
+
+  /* If the first target path is not the root of the edit, we must first
+     call open_root() ourselves. (If the first target path is the root of
+     the edit, then we expect the user's callback to do so.) */
+  if (!state->last_path && !svn_path_is_empty(relpath))
+    {
+      subpool = svn_pool_create(state->pool);
+      SVN_ERR(state->editor->open_root(state->edit_baton, SVN_INVALID_REVNUM,
+                                       subpool, &db));
+      push_dir_stack_item(state->db_stack, db, subpool);
+    }
+
+  /*** Step A - Find the common ancestor of the last path and the
+       current one.  For the first iteration, this is just the
+       empty string. ***/
+  if (state->last_path)
+    common = svn_relpath_get_longest_ancestor(state->last_path, relpath,
+                                              scratch_pool);
+  common_len = strlen(common);
+
+  /*** Step B - Close any directories between the last path and
+       the new common ancestor, if any need to be closed.
+       Sometimes there is nothing to do here (like, for the first
+       iteration, or when the last path was an ancestor of the
+       current one). ***/
+  if ((state->last_path) && (strlen(state->last_path) > common_len))
+    {
+      const char *rel = state->last_path + (common_len ? (common_len + 1) : 0);
+      int count = count_components(rel);
+      while (count--)
         {
-          item = apr_pcalloc(subpool, sizeof(*item));
-          item->dir_baton = db;
-          item->pool = subpool;
-          APR_ARRAY_PUSH(db_stack, void *) = item;
+          SVN_ERR(pop_stack(state->db_stack, state->editor));
         }
-      else
+    }
+
+  /*** Step C - Open any directories between the common ancestor
+       and the parent of the current path. ***/
+  pdir = svn_relpath_dirname(relpath, scratch_pool);
+
+  if (strlen(pdir) > common_len)
+    {
+      const char *piece = pdir + common_len + 1;
+
+      while (1)
         {
-          svn_pool_destroy(subpool);
+          const char *rel = pdir;
+
+          /* Find the first separator. */
+          piece = strchr(piece, '/');
+
+          /* Calculate REL as the portion of PDIR up to (but not
+             including) the location to which PIECE is pointing. */
+          if (piece)
+            rel = apr_pstrmemdup(scratch_pool, pdir, piece - pdir);
+
+          /* Open the subdirectory. */
+          SVN_ERR(open_dir(state->db_stack, state->editor, rel, state->pool));
+
+          /* If we found a '/', advance our PIECE pointer to
+             character just after that '/'.  Otherwise, we're
+             done.  */
+          if (piece)
+            piece++;
+          else
+            break;
         }
+    }
 
-      /*** Step E - Save our state for the next iteration.  If our
-           caller opened or added PATH as a directory, that becomes
-           our LAST_PATH.  Otherwise, we use PATH's parent
-           directory. ***/
-
-      /* NOTE:  The variable LAST_PATH needs to outlive the loop. */
-      if (db)
-        last_path = path; /* lives in a pool outside our control. */
-      else
-        last_path = apr_pstrdup(pool, pdir); /* duping into POOL. */
+  /*** Step D - Tell our caller to handle the current path. ***/
+  if (state->db_stack->nelts)
+    {
+      item = APR_ARRAY_IDX(state->db_stack, state->db_stack->nelts - 1, void *);
+      parent_db = item->dir_baton;
+    }
+  else
+    parent_db = NULL;
+  db = NULL;  /* predictable behaviour for callbacks that don't set it */
+  subpool = svn_pool_create(state->pool);
+  SVN_ERR(state->callback_func(&db,
+                               state->editor, state->edit_baton, parent_db,
+                               state->callback_baton,
+                               relpath, subpool));
+  if (db)
+    {
+      push_dir_stack_item(state->db_stack, db, subpool);
+    }
+  else
+    {
+      svn_pool_destroy(subpool);
     }
 
-  /* Destroy the iteration subpool. */
-  svn_pool_destroy(iterpool);
+  /*** Step E - Save our state for the next iteration.  If our
+       caller opened or added PATH as a directory, that becomes
+       our LAST_PATH.  Otherwise, we use PATH's parent
+       directory. ***/
+  state->last_path = apr_pstrdup(state->pool, db ? relpath : pdir);
+
+  return SVN_NO_ERROR;
+}
 
+svn_error_t *
+svn_delta_path_driver_finish(svn_delta_path_driver_state_t *state,
+                             apr_pool_t *scratch_pool)
+{
   /* Close down any remaining open directory batons. */
-  while (db_stack->nelts)
+  while (state->db_stack->nelts)
     {
-      SVN_ERR(pop_stack(db_stack, editor));
+      SVN_ERR(pop_stack(state->db_stack, state->editor));
     }
 
   return SVN_NO_ERROR;

Modified: subversion/branches/swig-py3/subversion/libsvn_fs/fs-loader.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_fs/fs-loader.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_fs/fs-loader.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_fs/fs-loader.c Mon Jul  8 15:19:03 2019
@@ -2221,3 +2221,42 @@ svn_fs_info_dup(const void *info_void,
     return apr_pmemdup(result_pool, info, sizeof(*info));
 }
 
+svn_error_t *
+svn_fs_ioctl(svn_fs_t *fs,
+             svn_fs_ioctl_code_t ctlcode,
+             void *input,
+             void **output_p,
+             svn_cancel_func_t cancel_func,
+             void *cancel_baton,
+             apr_pool_t *result_pool,
+             apr_pool_t *scratch_pool)
+{
+  void *output;
+
+  if (fs)
+    {
+      if (!fs->vtable->ioctl)
+        return svn_error_create(SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE, NULL, NULL);
+
+      SVN_ERR(fs->vtable->ioctl(fs, ctlcode, input, &output,
+                                cancel_func, cancel_baton,
+                                result_pool, scratch_pool));
+    }
+  else
+    {
+      fs_library_vtable_t *vtable;
+
+      SVN_ERR(get_library_vtable(&vtable, ctlcode.fs_type, scratch_pool));
+
+      if (!vtable->ioctl)
+        return svn_error_create(SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE, NULL, NULL);
+
+      SVN_ERR(vtable->ioctl(ctlcode, input, &output,
+                            cancel_func, cancel_baton,
+                            result_pool, scratch_pool));
+    }
+
+  if (output_p)
+    *output_p = output;
+  return SVN_NO_ERROR;
+}

Modified: subversion/branches/swig-py3/subversion/libsvn_fs/fs-loader.h
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_fs/fs-loader.h?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_fs/fs-loader.h (original)
+++ subversion/branches/swig-py3/subversion/libsvn_fs/fs-loader.h Mon Jul  8 15:19:03 2019
@@ -159,6 +159,13 @@ typedef struct fs_library_vtable_t
   /* For svn_fs_info_fsfs_dup(). */
   void *(*info_fsap_dup)(const void *fsap_info,
                          apr_pool_t *result_pool);
+
+  svn_error_t *(*ioctl)(svn_fs_ioctl_code_t ctlcode,
+                        void *input, void **output_p,
+                        svn_cancel_func_t cancel_func,
+                        void *cancel_baton,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool);
 } fs_library_vtable_t;
 
 /* This is the type of symbol an FS module defines to fetch the
@@ -266,6 +273,12 @@ typedef struct fs_vtable_t
   svn_error_t *(*bdb_set_errcall)(svn_fs_t *fs,
                                   void (*handler)(const char *errpfx,
                                                   char *msg));
+  svn_error_t *(*ioctl)(svn_fs_t *fs, svn_fs_ioctl_code_t ctlcode,
+                        void *input, void **output_p,
+                        svn_cancel_func_t cancel_func,
+                        void *cancel_baton,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool);
 } fs_vtable_t;
 
 

Modified: subversion/branches/swig-py3/subversion/libsvn_fs_base/fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_fs_base/fs.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_fs_base/fs.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_fs_base/fs.c Mon Jul  8 15:19:03 2019
@@ -574,6 +574,7 @@ static fs_vtable_t fs_vtable = {
   base_bdb_verify_root,
   base_bdb_freeze,
   base_bdb_set_errcall,
+  NULL /* ioctl */
 };
 
 /* Where the format number is stored. */
@@ -1515,7 +1516,8 @@ static fs_library_vtable_t library_vtabl
   base_bdb_logfiles,
   svn_fs_base__id_parse,
   base_set_svn_fs_open,
-  NULL /* info_fsap_dup */
+  NULL /* info_fsap_dup */,
+  NULL /* ioctl */
 };
 
 svn_error_t *

Modified: subversion/branches/swig-py3/subversion/libsvn_fs_fs/dump-index.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_fs_fs/dump-index.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_fs_fs/dump-index.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_fs_fs/dump-index.c Mon Jul  8 15:19:03 2019
@@ -21,8 +21,8 @@
  */
 
 #include "svn_pools.h"
-#include "private/svn_fs_fs_private.h"
 
+#include "fs_fs.h"
 #include "index.h"
 #include "rev_file.h"
 #include "util.h"

Modified: subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs.c Mon Jul  8 15:19:03 2019
@@ -47,6 +47,7 @@
 #include "verify.h"
 #include "svn_private_config.h"
 #include "private/svn_fs_util.h"
+#include "private/svn_fs_fs_private.h"
 
 #include "../libsvn_fs/fs-loader.h"
 
@@ -254,6 +255,67 @@ fs_set_uuid(svn_fs_t *fs,
 }
 
 
+static svn_error_t *
+fs_ioctl(svn_fs_t *fs, svn_fs_ioctl_code_t ctlcode,
+         void *input_void, void **output_p,
+         svn_cancel_func_t cancel_func,
+         void *cancel_baton,
+         apr_pool_t *result_pool,
+         apr_pool_t *scratch_pool)
+{
+  if (strcmp(ctlcode.fs_type, SVN_FS_TYPE_FSFS) == 0)
+    {
+      if (ctlcode.code == SVN_FS_FS__IOCTL_GET_STATS.code)
+        {
+          svn_fs_fs__ioctl_get_stats_input_t *input = input_void;
+          svn_fs_fs__ioctl_get_stats_output_t *output;
+
+          output = apr_pcalloc(result_pool, sizeof(*output));
+          SVN_ERR(svn_fs_fs__get_stats(&output->stats, fs,
+                                       input->progress_func,
+                                       input->progress_baton,
+                                       cancel_func, cancel_baton,
+                                       result_pool, scratch_pool));
+          *output_p = output;
+        }
+      else if (ctlcode.code == SVN_FS_FS__IOCTL_DUMP_INDEX.code)
+        {
+          svn_fs_fs__ioctl_dump_index_input_t *input = input_void;
+
+          SVN_ERR(svn_fs_fs__dump_index(fs, input->revision,
+                                        input->callback_func,
+                                        input->callback_baton,
+                                        cancel_func, cancel_baton,
+                                        scratch_pool));
+          *output_p = NULL;
+        }
+      else if (ctlcode.code == SVN_FS_FS__IOCTL_LOAD_INDEX.code)
+        {
+          svn_fs_fs__ioctl_load_index_input_t *input = input_void;
+
+          SVN_ERR(svn_fs_fs__load_index(fs, input->revision, input->entries,
+                                        scratch_pool));
+          *output_p = NULL;
+        }
+      else if (ctlcode.code == SVN_FS_FS__IOCTL_REVISION_SIZE.code)
+        {
+          svn_fs_fs__ioctl_revision_size_input_t *input = input_void;
+          svn_fs_fs__ioctl_revision_size_output_t *output
+            = apr_pcalloc(result_pool, sizeof(*output));
+
+          SVN_ERR(svn_fs_fs__revision_size(&output->rev_size,
+                                           fs, input->revision,
+                                           scratch_pool));
+          *output_p = output;
+        }
+      else
+        return svn_error_create(SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE, NULL, NULL);
+    }
+  else
+    return svn_error_create(SVN_ERR_FS_UNRECOGNIZED_IOCTL_CODE, NULL, NULL);
+
+  return SVN_NO_ERROR;
+}
 
 /* The vtable associated with a specific open filesystem. */
 static fs_vtable_t fs_vtable = {
@@ -279,7 +341,8 @@ static fs_vtable_t fs_vtable = {
   fs_info,
   svn_fs_fs__verify_root,
   fs_freeze,
-  fs_set_errcall
+  fs_set_errcall,
+  fs_ioctl
 };
 
 
@@ -602,7 +665,8 @@ static fs_library_vtable_t library_vtabl
   fs_logfiles,
   NULL /* parse_id */,
   fs_set_svn_fs_open,
-  fs_info_dup
+  fs_info_dup,
+  NULL /* ioctl */
 };
 
 svn_error_t *

Modified: subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs_fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs_fs.h?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs_fs.h (original)
+++ subversion/branches/swig-py3/subversion/libsvn_fs_fs/fs_fs.h Mon Jul  8 15:19:03 2019
@@ -304,4 +304,56 @@ svn_fs_fs__initialize_txn_caches(svn_fs_
 void
 svn_fs_fs__reset_txn_caches(svn_fs_t *fs);
 
+/* Scan all contents of the repository FS and return statistics in *STATS,
+ * allocated in RESULT_POOL.  Report progress through PROGRESS_FUNC with
+ * PROGRESS_BATON, if PROGRESS_FUNC is not NULL.
+ * Use SCRATCH_POOL for temporary allocations.
+ */
+svn_error_t *
+svn_fs_fs__get_stats(svn_fs_fs__stats_t **stats,
+                     svn_fs_t *fs,
+                     svn_fs_progress_notify_func_t progress_func,
+                     void *progress_baton,
+                     svn_cancel_func_t cancel_func,
+                     void *cancel_baton,
+                     apr_pool_t *result_pool,
+                     apr_pool_t *scratch_pool);
+
+/* Read the P2L index for the rev / pack file containing REVISION in FS.
+ * For each index entry, invoke CALLBACK_FUNC with CALLBACK_BATON.
+ * If not NULL, call CANCEL_FUNC with CANCEL_BATON from time to time.
+ * Use SCRATCH_POOL for temporary allocations.
+ */
+svn_error_t *
+svn_fs_fs__dump_index(svn_fs_t *fs,
+                      svn_revnum_t revision,
+                      svn_fs_fs__dump_index_func_t callback_func,
+                      void *callback_baton,
+                      svn_cancel_func_t cancel_func,
+                      void *cancel_baton,
+                      apr_pool_t *scratch_pool);
+
+
+/* Rewrite the respective index information of the rev / pack file in FS
+ * containing REVISION and use the svn_fs_fs__p2l_entry_t * array ENTRIES
+ * as the new index contents.  Allocate temporaries from SCRATCH_POOL.
+ *
+ * Note that this becomes a no-op if ENTRIES is empty.  You may use a zero-
+ * sized empty entry instead.
+ */
+svn_error_t *
+svn_fs_fs__load_index(svn_fs_t *fs,
+                      svn_revnum_t revision,
+                      apr_array_header_t *entries,
+                      apr_pool_t *scratch_pool);
+
+/* Set *REV_SIZE to the total size of objects belonging to revision REVISION
+ * in FS. The size includes revision properties and excludes indexes.
+ */
+svn_error_t *
+svn_fs_fs__revision_size(apr_off_t *rev_size,
+                         svn_fs_t *fs,
+                         svn_revnum_t revision,
+                         apr_pool_t *scratch_pool);
+
 #endif

Modified: subversion/branches/swig-py3/subversion/libsvn_fs_fs/load-index.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_fs_fs/load-index.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_fs_fs/load-index.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_fs_fs/load-index.c Mon Jul  8 15:19:03 2019
@@ -22,9 +22,9 @@
 
 #include "svn_pools.h"
 
-#include "private/svn_fs_fs_private.h"
 #include "private/svn_sorts_private.h"
 
+#include "fs_fs.h"
 #include "index.h"
 #include "util.h"
 #include "transaction.h"

Modified: subversion/branches/swig-py3/subversion/libsvn_fs_fs/revprops.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_fs_fs/revprops.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_fs_fs/revprops.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_fs_fs/revprops.c Mon Jul  8 15:19:03 2019
@@ -672,6 +672,64 @@ read_pack_revprop(packed_revprops_t **re
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_fs_fs__get_revision_props_size(apr_off_t *props_size_p,
+                                   svn_fs_t *fs,
+                                   svn_revnum_t rev,
+                                   apr_pool_t *scratch_pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+
+  /* should they be available at all? */
+  SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, scratch_pool));
+
+  /* if REV had not been packed when we began, try reading it from the
+   * non-packed shard.  If that fails, we will fall through to packed
+   * shard reads. */
+  if (!svn_fs_fs__is_packed_revprop(fs, rev))
+    {
+      const char *path = svn_fs_fs__path_revprops(fs, rev, scratch_pool);
+      svn_error_t *err;
+      apr_file_t *file;
+      svn_filesize_t file_size;
+
+      err = svn_io_file_open(&file, path, APR_FOPEN_READ, APR_OS_DEFAULT,
+                             scratch_pool);
+      if (!err)
+        err = svn_io_file_size_get(&file_size, file, scratch_pool);
+      if (!err)
+        {
+          *props_size_p = (apr_off_t)file_size;
+          return SVN_NO_ERROR;
+        }
+      else if (!APR_STATUS_IS_ENOENT(err->apr_err)
+               || ffd->format < SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT)
+        {
+          return svn_error_trace(err);
+        }
+
+      /* fall through: maybe the revision got packed while we were looking */
+      svn_error_clear(err);
+    }
+
+  /* Try reading packed revprops.  If that fails, REV is most
+   * likely invalid (or its revprops highly contested). */
+  {
+    packed_revprops_t *revprops;
+
+    /* ### This is inefficient -- reading all the revprops in a pack. We
+       should just read the index. */
+    SVN_ERR(read_pack_revprop(&revprops, fs, rev,
+                              TRUE /*read_all*/, FALSE /*populate_cache*/,
+                              scratch_pool));
+    *props_size_p = (apr_off_t)APR_ARRAY_IDX(revprops->sizes,
+                                             rev - revprops->start_revision,
+                                             apr_size_t);
+  }
+
+  return SVN_NO_ERROR;
+}
+
 /* Read the revprops for revision REV in FS and return them in *PROPERTIES_P.
  *
  * Allocations will be done in POOL.

Modified: subversion/branches/swig-py3/subversion/libsvn_fs_fs/revprops.h
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_fs_fs/revprops.h?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_fs_fs/revprops.h (original)
+++ subversion/branches/swig-py3/subversion/libsvn_fs_fs/revprops.h Mon Jul  8 15:19:03 2019
@@ -62,6 +62,15 @@ svn_fs_fs__upgrade_cleanup_pack_revprops
 void
 svn_fs_fs__reset_revprop_cache(svn_fs_t *fs);
 
+/* Set *PROPS_SIZE_P to the size in bytes on disk of the revprops for
+ * revision REV in FS. The size excludes indexes.
+ */
+svn_error_t *
+svn_fs_fs__get_revision_props_size(apr_off_t *props_size_p,
+                                   svn_fs_t *fs,
+                                   svn_revnum_t rev,
+                                   apr_pool_t *scratch_pool);
+
 /* Read the revprops for revision REV in FS and return them in *PROPERTIES_P.
  * If REFRESH is set, clear the revprop cache before accessing the data.
  *

Modified: subversion/branches/swig-py3/subversion/libsvn_fs_fs/stats.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_fs_fs/stats.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_fs_fs/stats.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_fs_fs/stats.c Mon Jul  8 15:19:03 2019
@@ -28,7 +28,6 @@
 #include "private/svn_cache.h"
 #include "private/svn_sorts_private.h"
 #include "private/svn_string_private.h"
-#include "private/svn_fs_fs_private.h"
 
 #include "index.h"
 #include "pack.h"
@@ -37,6 +36,7 @@
 #include "fs_fs.h"
 #include "cached_data.h"
 #include "low_level.h"
+#include "revprops.h"
 
 #include "../libsvn_fs/fs-loader.h"
 
@@ -1397,3 +1397,96 @@ svn_fs_fs__get_stats(svn_fs_fs__stats_t
 
   return SVN_NO_ERROR;
 }
+
+/* Baton for rev_size_index_entry_cb. */
+struct rev_size_baton_t {
+  svn_revnum_t revision;
+  apr_off_t rev_size;
+};
+
+/* Implements svn_fs_fs__dump_index_func_t, summing object sizes for
+ * revision BATON->revision into BATON->rev_size.
+ */
+static svn_error_t *
+rev_size_index_entry_cb(const svn_fs_fs__p2l_entry_t *entry,
+                        void *baton,
+                        apr_pool_t *scratch_pool)
+{
+  struct rev_size_baton_t *b = baton;
+
+  if (entry->item.revision == b->revision)
+    b->rev_size += entry->size;
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_fs__revision_size(apr_off_t *rev_size,
+                         svn_fs_t *fs,
+                         svn_revnum_t revision,
+                         apr_pool_t *scratch_pool)
+{
+  /* Get the size of the revision (excluding rev-props) */
+  if (svn_fs_fs__use_log_addressing(fs))
+    {
+      /* This works for a packed or a non-packed revision.
+         We could provide an optimized case for a non-packed revision
+         using svn_fs_fs__p2l_get_max_offset(). */
+      struct rev_size_baton_t b = { 0, 0 };
+
+      b.revision = revision;
+      SVN_ERR(svn_fs_fs__dump_index(fs, revision,
+                                    rev_size_index_entry_cb, &b,
+                                    NULL, NULL, scratch_pool));
+      *rev_size = b.rev_size;
+    }
+  else
+    {
+      svn_fs_fs__revision_file_t *rev_file;
+      svn_revnum_t min_unpacked_rev;
+
+      SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, fs, revision,
+                                               scratch_pool, scratch_pool));
+      SVN_ERR(svn_fs_fs__min_unpacked_rev(&min_unpacked_rev, fs,
+                                          scratch_pool));
+      if (revision < min_unpacked_rev)
+        {
+          int shard_size = svn_fs_fs__shard_size(fs);
+          apr_off_t start_offset, end_offset;
+
+          SVN_ERR(svn_fs_fs__get_packed_offset(&start_offset, fs, revision,
+                                               scratch_pool));
+          if (((revision + 1) % shard_size) == 0)
+            {
+              svn_filesize_t file_size;
+
+              SVN_ERR(svn_io_file_size_get(&file_size, rev_file->file, scratch_pool));
+              end_offset = (apr_off_t)file_size;
+            }
+          else
+            {
+              SVN_ERR(svn_fs_fs__get_packed_offset(&end_offset, fs,
+                                                   revision + 1, scratch_pool));
+            }
+          *rev_size = (end_offset - start_offset);
+        }
+      else
+        {
+          svn_filesize_t file_size;
+
+          SVN_ERR(svn_io_file_size_get(&file_size, rev_file->file, scratch_pool));
+          *rev_size = (apr_off_t)file_size;
+        }
+
+      SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
+    }
+
+  /* Add the size of the rev-props */
+  {
+    apr_off_t size;
+
+    SVN_ERR(svn_fs_fs__get_revision_props_size(&size, fs, revision, scratch_pool));
+    *rev_size += size;
+  }
+
+  return SVN_NO_ERROR;
+}

Propchange: subversion/branches/swig-py3/subversion/libsvn_fs_x/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Jul  8 15:19:03 2019
@@ -96,4 +96,4 @@
 /subversion/branches/verify-keep-going/subversion/libsvn_fs_x:1439280-1492639,1546002-1546110
 /subversion/branches/wc-collate-path/subversion/libsvn_fs_x:1402685-1480384
 /subversion/trunk/subversion/libsvn_fs_fs:1415133-1596500,1596567,1597414,1597989,1598273,1599140,1600872,1601633,1603485-1603487,1603499,1603605,1604128,1604188,1604413-1604414,1604416-1604417,1604421,1604442,1604700,1604717,1604720,1604726,1604755,1604794,1604802,1604824,1604836,1604844,1604902-1604903,1604911,1604925,1604933,1604947,1605059-1605060,1605064-1605065,1605068,1605071-1605073,1605075,1605123,1605188-1605189,1605191,1605197,1605444,1605633,1606132,1606142,1606144,1606514,1606526,1606528,1606551,1606554,1606564,1606598-1606599,1606656,1606658,1606662,1606744,1606840,1607085,1607572,1612407,1612810,1613339,1613872,1614611,1615348,1615351-1615352,1615356,1616338-1616339,1616613,1617586,1617688,1618138,1618151,1618153,1618226,1618641,1618653,1618662,1619068,1619358,1619413,1619769,1619774,1620602,1620909,1620912,1620928,1620930,1621275,1621635,1622931,1622937,1622942,1622946,1622959-1622960,1622963,1622987,1623007,1623368,1623373,1623377,1623379,1623381,1623398,1623402,162
 4011,1624265,1624512,1626246,1626871,1626873,1626886,1627497-1627498,1627502,1627947-1627949,1627966,1628083,1628093,1628158-1628159,1628161,1628392-1628393,1628415,1628427,1628676,1628738,1628762,1628764,1629854-1629855,1629857,1629865,1629873,1629875,1629879,1630067,1630070,1631049-1631051,1631075,1631115,1631171,1631180,1631185-1631186,1631196-1631197,1631239-1631240,1631548,1631550,1631563,1631567,1631588,1631598,1632646,1632776,1632849,1632851-1632853,1632856-1632857,1632868,1632908,1632926,1633232,1633617-1633618,1634872,1634875,1634879-1634880,1634920,1636478,1636483,1636629,1636644,1637184,1637186,1637330,1637358,1637363,1637393,1639319,1639322,1639335,1639348,1639352,1639355,1639358,1639414,1639419,1639426,1639430,1639436,1639440,1639549,1640061-1640062,1640197,1640915,1640966,1641013,1643139,1643233,1645567,1646021,1646712,1646716,1647537,1647540-1647541,1647820,1647905,1648230,1648238,1648241-1648243,1648253,1648272,1648532,1648537-1648539,1648542,1648591,1648612,1649590,
 1651567,1652068,1652076,1652441,1652451,1653608,1654932,1654934,1654937,1655635,1655649,1655651,1655664,1656176,1657525,1657972,1657978,1658482,1659212,1659217,1659314,1659509,1662668,1665318,1665854,1665894,1667090,1667101,1667538,1669743,1669746,1669749,1669945,1670139,1670953,1673170,1673197,1673202,1673204,1673445,1673454,1673685,1673689,1673875,1674165,1674341,1674400,1674404,1674631,1674669,1674673,1675396,1676667,1677431,1678149,1678151,1678718,1678725,1679169,1679907,1679920-1679924,1679926,1680347,1680460,1680464,1680476,1680819,1681949,1681966,1681974,1681994,1682008,1682076,1682086,1682093,1682259,1682265,1682739,1682864,1683311,1683330,1683378,1683544,1683553,1684047,1686232,1686542,1686546,1686554,1686557,1687061,1687064,1687070-1687071,1687074,1687078-1687079,1688270,1688425,1692650,1693886,1694489,1694848,1696171,1696185,1696627-1696628,1696630,1696758,1697372,1697381,1697387,1697393,1697403,1697405,1701017,1701053,1702600,1702922,1703069,1703142,1703237,1703240,17052
 66,1705638,1705643,1705646,1705724,1705730,1705739,1706612,1706615,1706617,1706619,1706675-1706676,1706679,1706979-1706980,1707308,1707971-1707973,1707986,1707988-1707989,1708004,1709388,1709799,1710017,1710359,1710368,1710370,1711507,1711582,1711672,1712927,1715793,1715947,1716047,1716067,1716784,1716973-1716974,1717332,1717334,1717864,1719269,1719336,1719413,1719730,1720015,1721285,1723715,1723720,1723834,1723839,1725179-1725180,1726004,1726099,1726116,1726897,1726995,1727006-1727007,1727028,1727040,1727707,1727822,1730491,1735916,1736357,1736359,1737355-1737356,1740721-1740722,1741096,1741200,1741206,1741214,1741224,1742540,1745055,1745107,1745852,1746006,1746012,1746026,1756258-1756266,1756364,1756377,1759117,1759122-1759126,1759135,1759404-1759405,1759686,1764340,1764481,1764676,1766352,1780810,1781655,1781694,1785053,1785737-1785738,1785741,1785754,1785904,1786445-1786446,1786515
-/subversion/trunk/subversion/libsvn_fs_x:1414756-1509914,1813660-1847674
+/subversion/trunk/subversion/libsvn_fs_x:1414756-1509914,1813660-1862712

Modified: subversion/branches/swig-py3/subversion/libsvn_fs_x/fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_fs_x/fs.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_fs_x/fs.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_fs_x/fs.c Mon Jul  8 15:19:03 2019
@@ -310,7 +310,8 @@ static fs_vtable_t fs_vtable = {
   x_info,
   svn_fs_x__verify_root,
   x_freeze,
-  x_set_errcall
+  x_set_errcall,
+  NULL /* ioctl */
 };
 
 
@@ -641,7 +642,8 @@ static fs_library_vtable_t library_vtabl
   x_logfiles,
   NULL /* parse_id */,
   x_set_svn_fs_open,
-  x_info_dup
+  x_info_dup,
+  NULL /* ioctl */
 };
 
 svn_error_t *

Modified: subversion/branches/swig-py3/subversion/libsvn_ra_svn/client.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_ra_svn/client.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_ra_svn/client.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_ra_svn/client.c Mon Jul  8 15:19:03 2019
@@ -3105,6 +3105,7 @@ ra_svn_get_deleted_rev(svn_ra_session_t
 {
   svn_ra_svn__session_baton_t *sess_baton = session->priv;
   svn_ra_svn_conn_t *conn = sess_baton->conn;
+  svn_error_t *err;
 
   path = reparent_path(session, path, pool);
 
@@ -3116,8 +3117,20 @@ ra_svn_get_deleted_rev(svn_ra_session_t
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
                                  N_("'get-deleted-rev' not implemented")));
 
-  return svn_error_trace(svn_ra_svn__read_cmd_response(conn, pool, "r",
-                                                       revision_deleted));
+  err = svn_error_trace(svn_ra_svn__read_cmd_response(conn, pool, "r",
+                                                      revision_deleted));
+  /* The protocol does not allow for a reply of SVN_INVALID_REVNUM directly.
+     Instead, a new enough server returns SVN_ERR_ENTRY_MISSING_REVISION to
+     indicate the answer to the query is SVN_INVALID_REVNUM. (An older server
+     closes the connection and returns SVN_ERR_RA_SVN_CONNECTION_CLOSED.) */
+  if (err && err->apr_err == SVN_ERR_ENTRY_MISSING_REVISION)
+    {
+      *revision_deleted = SVN_INVALID_REVNUM;
+      svn_error_clear(err);
+    }
+  else
+    SVN_ERR(err);
+  return SVN_NO_ERROR;
 }
 
 static svn_error_t *

Modified: subversion/branches/swig-py3/subversion/libsvn_repos/authz.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_repos/authz.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_repos/authz.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_repos/authz.c Mon Jul  8 15:19:03 2019
@@ -1548,6 +1548,8 @@ authz_read(authz_full_t **authz_p,
            const char *groups_path,
            svn_boolean_t must_exist,
            svn_repos_t *repos_hint,
+           svn_repos_authz_warning_func_t warning_func,
+           void *warning_baton,
            apr_pool_t *result_pool,
            apr_pool_t *scratch_pool)
 {
@@ -1587,7 +1589,8 @@ authz_read(authz_full_t **authz_p,
           /* Parse the configuration(s) and construct the full authz model
            * from it. */
           err = svn_authz__parse(authz_p, rules_stream, groups_stream,
-                                item_pool, scratch_pool);
+                                 warning_func, warning_baton,
+                                 item_pool, scratch_pool);
           if (err != SVN_NO_ERROR)
             {
               /* That pool would otherwise never get destroyed. */
@@ -1611,11 +1614,11 @@ authz_read(authz_full_t **authz_p,
     {
       /* Parse the configuration(s) and construct the full authz model from
        * it. */
-      err = svn_error_quick_wrapf(svn_authz__parse(authz_p, rules_stream,
-                                                   groups_stream,
-                                                   result_pool, scratch_pool),
-                                  "Error while parsing authz file: '%s':",
-                                  path);
+      err = svn_error_quick_wrapf(
+          svn_authz__parse(authz_p, rules_stream, groups_stream,
+                           warning_func, warning_baton,
+                           result_pool, scratch_pool),
+          "Error while parsing authz file: '%s':", path);
     }
 
   svn_repos__destroy_config_access(config_access);
@@ -1628,11 +1631,13 @@ authz_read(authz_full_t **authz_p,
 /*** Public functions. ***/
 
 svn_error_t *
-svn_repos_authz_read3(svn_authz_t **authz_p,
+svn_repos_authz_read4(svn_authz_t **authz_p,
                       const char *path,
                       const char *groups_path,
                       svn_boolean_t must_exist,
                       svn_repos_t *repos_hint,
+                      svn_repos_authz_warning_func_t warning_func,
+                      void *warning_baton,
                       apr_pool_t *result_pool,
                       apr_pool_t *scratch_pool)
 {
@@ -1640,7 +1645,8 @@ svn_repos_authz_read3(svn_authz_t **auth
   authz->pool = result_pool;
 
   SVN_ERR(authz_read(&authz->full, &authz->authz_id, path, groups_path,
-                     must_exist, repos_hint, result_pool, scratch_pool));
+                     must_exist, repos_hint, warning_func, warning_baton,
+                     result_pool, scratch_pool));
 
   *authz_p = authz;
   return SVN_NO_ERROR;
@@ -1648,18 +1654,21 @@ svn_repos_authz_read3(svn_authz_t **auth
 
 
 svn_error_t *
-svn_repos_authz_parse(svn_authz_t **authz_p, svn_stream_t *stream,
-                      svn_stream_t *groups_stream, apr_pool_t *pool)
+svn_repos_authz_parse2(svn_authz_t **authz_p,
+                       svn_stream_t *stream,
+                       svn_stream_t *groups_stream,
+                       svn_repos_authz_warning_func_t warning_func,
+                       void *warning_baton,
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool)
 {
-  apr_pool_t *scratch_pool = svn_pool_create(pool);
-  svn_authz_t *authz = apr_pcalloc(pool, sizeof(*authz));
-  authz->pool = pool;
+  svn_authz_t *authz = apr_pcalloc(result_pool, sizeof(*authz));
+  authz->pool = result_pool;
 
   /* Parse the configuration and construct the full authz model from it. */
-  SVN_ERR(svn_authz__parse(&authz->full, stream, groups_stream, pool,
-                           scratch_pool));
-
-  svn_pool_destroy(scratch_pool);
+  SVN_ERR(svn_authz__parse(&authz->full, stream, groups_stream,
+                           warning_func, warning_baton,
+                           result_pool, scratch_pool));
 
   *authz_p = authz;
   return SVN_NO_ERROR;

Modified: subversion/branches/swig-py3/subversion/libsvn_repos/authz.h
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_repos/authz.h?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_repos/authz.h (original)
+++ subversion/branches/swig-py3/subversion/libsvn_repos/authz.h Mon Jul  8 15:19:03 2019
@@ -312,6 +312,8 @@ svn_error_t *
 svn_authz__parse(authz_full_t **authz,
                  svn_stream_t *rules,
                  svn_stream_t *groups,
+                 svn_repos_authz_warning_func_t warning_func,
+                 void *warning_baton,
                  apr_pool_t *result_pool,
                  apr_pool_t *scratch_pool);
 

Modified: subversion/branches/swig-py3/subversion/libsvn_repos/authz_parse.c
URL: http://svn.apache.org/viewvc/subversion/branches/swig-py3/subversion/libsvn_repos/authz_parse.c?rev=1862754&r1=1862753&r2=1862754&view=diff
==============================================================================
--- subversion/branches/swig-py3/subversion/libsvn_repos/authz_parse.c (original)
+++ subversion/branches/swig-py3/subversion/libsvn_repos/authz_parse.c Mon Jul  8 15:19:03 2019
@@ -127,6 +127,10 @@ typedef struct ctor_baton_t
   svn_membuf_t rule_path_buffer;
   svn_stringbuf_t *rule_string_buffer;
 
+  /* The warning callback and its baton. */
+  svn_repos_authz_warning_func_t warning_func;
+  void *warning_baton;
+
   /* The parser's scratch pool. This may not be the same pool as
      passed to the constructor callbacks, that is supposed to be an
      iteration pool maintained by the generic parser.
@@ -203,7 +207,9 @@ insert_default_acl(ctor_baton_t *cb)
 
 /* Initialize a constuctor baton. */
 static ctor_baton_t *
-create_ctor_baton(apr_pool_t *result_pool,
+create_ctor_baton(svn_repos_authz_warning_func_t warning_func,
+                  void *warning_baton,
+                  apr_pool_t *result_pool,
                   apr_pool_t *scratch_pool)
 {
   apr_pool_t *const parser_pool = svn_pool_create(scratch_pool);
@@ -234,6 +240,9 @@ create_ctor_baton(apr_pool_t *result_poo
   svn_membuf__create(&cb->rule_path_buffer, 0, parser_pool);
   cb->rule_string_buffer = svn_stringbuf_create_empty(parser_pool);
 
+  cb->warning_func = warning_func;
+  cb->warning_baton = warning_baton;
+
   cb->parser_pool = parser_pool;
 
   insert_default_acl(cb);
@@ -242,6 +251,25 @@ create_ctor_baton(apr_pool_t *result_poo
 }
 
 
+/* Emit a warning. Clears ERROR */
+static void
+emit_parser_warning(const ctor_baton_t *cb,
+                    svn_error_t *error,
+                    apr_pool_t *scratch_pool)
+{
+  if (cb->warning_func)
+    cb->warning_func(cb->warning_baton, error, scratch_pool);
+  svn_error_clear(error);
+}
+
+/* Avoid creating an error struct if there is no warning function. */
+#define SVN_AUTHZ_PARSE_WARN(cb, err, pool)     \
+  do {                                          \
+    if ((cb) && (cb)->warning_func)             \
+      emit_parser_warning((cb), (err), (pool)); \
+  } while(0)
+
+
 /* Create and store per-user global rights.
    The USER string must be interned or statically initialized. */
 static void
@@ -1011,7 +1039,8 @@ close_section(void *baton, svn_stringbuf
 
 
 /* Add a user to GROUP.
-   GROUP is never internalized, but USER always is. */
+   GROUP is never internalized, but USER always is.
+   Adding a NULL user will create an empty group, if it doesn't exist. */
 static void
 add_to_group(ctor_baton_t *cb, const char *group, const char *user)
 {
@@ -1022,7 +1051,8 @@ add_to_group(ctor_baton_t *cb, const cha
       members = svn_hash__make(cb->authz->pool);
       svn_hash_sets(cb->expanded_groups, group, members);
     }
-  svn_hash_sets(members, user, interned_empty_string);
+  if (user)
+    svn_hash_sets(members, user, interned_empty_string);
 }
 
 
@@ -1038,8 +1068,15 @@ expand_group_callback(void *baton,
   ctor_baton_t *const cb = baton;
   const char *const group = key;
   apr_array_header_t *members = value;
-
   int i;
+
+  if (0 == members->nelts)
+    {
+      /* Create the group with no members. */
+      add_to_group(cb, group, NULL);
+      return SVN_NO_ERROR;
+    }
+
   for (i = 0; i < members->nelts; ++i)
     {
       const char *member = APR_ARRAY_IDX(members, i, const char*);
@@ -1169,10 +1206,24 @@ array_insert_ace(void *baton,
       SVN_ERR_ASSERT(ace->members == NULL);
       ace->members = svn_hash_gets(iab->cb->expanded_groups, ace->name);
       if (!ace->members)
-        return svn_error_createf(
-            SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
-            _("Access entry refers to undefined group '%s'"),
-            ace->name);
+        {
+          return svn_error_createf(
+              SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
+              _("Access entry refers to undefined group '%s'"),
+              ace->name);
+        }
+      else if (0 == apr_hash_count(ace->members))
+        {
+          /* An ACE for an empty group has no effect, so ignore it. */
+          SVN_AUTHZ_PARSE_WARN(
+              iab->cb,
+              svn_error_createf(
+                  SVN_ERR_AUTHZ_INVALID_CONFIG, NULL,
+                  _("Ignoring access entry for empty group '%s'"),
+                  ace->name),
+              scratch_pool);
+          return SVN_NO_ERROR;
+        }
     }
 
   APR_ARRAY_PUSH(iab->ace_array, authz_ace_t) = *ace;
@@ -1318,10 +1369,13 @@ svn_error_t *
 svn_authz__parse(authz_full_t **authz,
                  svn_stream_t *rules,
                  svn_stream_t *groups,
+                 svn_repos_authz_warning_func_t warning_func,
+                 void *warning_baton,
                  apr_pool_t *result_pool,
                  apr_pool_t *scratch_pool)
 {
-  ctor_baton_t *const cb = create_ctor_baton(result_pool, scratch_pool);
+  ctor_baton_t *const cb = create_ctor_baton(warning_func, warning_baton,
+                                             result_pool, scratch_pool);
 
   /*
    * Pass 1: Parse the authz file.