You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2016/10/11 09:11:54 UTC

svn commit: r1764214 [9/21] - in /subversion/branches/ra-git: ./ build/ build/ac-macros/ build/generator/ build/win32/ contrib/client-side/ contrib/client-side/svnmerge/ contrib/hook-scripts/ contrib/server-side/ contrib/server-side/fsfsfixer/fixer/ co...

Modified: subversion/branches/ra-git/subversion/libsvn_ra_serf/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_serf/commit.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_serf/commit.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_serf/commit.c Tue Oct 11 09:11:50 2016
@@ -170,11 +170,11 @@ typedef struct file_context_t {
   const char *copy_path;
   svn_revnum_t copy_revision;
 
-  /* stream */
+  /* Stream for collecting the svndiff. */
   svn_stream_t *stream;
 
-  /* Temporary file containing the svndiff. */
-  apr_file_t *svndiff;
+  /* Buffer holding the svndiff (can spill to disk). */
+  svn_ra_serf__request_body_t *svndiff;
 
   /* Our base checksum as reported by the WC. */
   const char *base_checksum;
@@ -869,35 +869,6 @@ proppatch_resource(svn_ra_serf__session_
 
 /* Implements svn_ra_serf__request_body_delegate_t */
 static svn_error_t *
-create_put_body(serf_bucket_t **body_bkt,
-                void *baton,
-                serf_bucket_alloc_t *alloc,
-                apr_pool_t *pool /* request pool */,
-                apr_pool_t *scratch_pool)
-{
-  file_context_t *ctx = baton;
-  apr_off_t offset;
-
-  /* We need to flush the file, make it unbuffered (so that it can be
-   * zero-copied via mmap), and reset the position before attempting to
-   * deliver the file.
-   *
-   * N.B. If we have APR 1.3+, we can unbuffer the file to let us use mmap
-   * and zero-copy the PUT body.  However, on older APR versions, we can't
-   * check the buffer status; but serf will fall through and create a file
-   * bucket for us on the buffered svndiff handle.
-   */
-  SVN_ERR(svn_io_file_flush(ctx->svndiff, pool));
-  apr_file_buffer_set(ctx->svndiff, NULL, 0);
-  offset = 0;
-  SVN_ERR(svn_io_file_seek(ctx->svndiff, APR_SET, &offset, pool));
-
-  *body_bkt = serf_bucket_file_create(ctx->svndiff, alloc);
-  return SVN_NO_ERROR;
-}
-
-/* Implements svn_ra_serf__request_body_delegate_t */
-static svn_error_t *
 create_empty_put_body(serf_bucket_t **body_bkt,
                       void *baton,
                       serf_bucket_alloc_t *alloc,
@@ -1444,7 +1415,6 @@ delete_entry(const char *path,
   delete_context_t *delete_ctx;
   svn_ra_serf__handler_t *handler;
   const char *delete_target;
-  svn_error_t *err;
 
   if (USING_HTTPV2_COMMIT_SUPPORT(dir->commit_ctx))
     {
@@ -1478,24 +1448,36 @@ delete_entry(const char *path,
 
   handler->method = "DELETE";
   handler->path = delete_target;
+  handler->no_fail_on_http_failure_status = TRUE;
 
-  err = svn_ra_serf__context_run_one(handler, pool);
-  if (err && err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED
-      && handler->sline.code == 400)
-    {
-      svn_error_clear(err);
+  SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
 
+  if (handler->sline.code == 400)
+    {
       /* Try again with non-standard body to overcome Apache Httpd
          header limit */
       delete_ctx->non_recursive_if = TRUE;
+
+      handler = svn_ra_serf__create_handler(dir->commit_ctx->session, pool);
+
+      handler->response_handler = svn_ra_serf__expect_empty_body;
+      handler->response_baton = handler;
+
+      handler->header_delegate = setup_delete_headers;
+      handler->header_delegate_baton = delete_ctx;
+
+      handler->method = "DELETE";
+      handler->path = delete_target;
+
       handler->body_type = "text/xml";
       handler->body_delegate = create_delete_body;
       handler->body_delegate_baton = delete_ctx;
 
       SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
     }
-  else
-    SVN_ERR(err);
+
+  if (handler->server_error)
+    return svn_ra_serf__server_error_create(handler, pool);
 
   /* 204 No Content: item successfully deleted */
   if (handler->sline.code != 204)
@@ -1877,23 +1859,6 @@ open_file(const char *path,
   return SVN_NO_ERROR;
 }
 
-/* Implements svn_stream_lazyopen_func_t for apply_textdelta */
-static svn_error_t *
-delayed_commit_stream_open(svn_stream_t **stream,
-                           void *baton,
-                           apr_pool_t *result_pool,
-                           apr_pool_t *scratch_pool)
-{
-  file_context_t *file_ctx = baton;
-
-  SVN_ERR(svn_io_open_unique_file3(&file_ctx->svndiff, NULL, NULL,
-                                   svn_io_file_del_on_pool_cleanup,
-                                   file_ctx->pool, scratch_pool));
-
-  *stream = svn_stream_from_aprfile2(file_ctx->svndiff, TRUE, result_pool);
-  return SVN_NO_ERROR;
-}
-
 static svn_error_t *
 apply_textdelta(void *file_baton,
                 const char *base_checksum,
@@ -1905,12 +1870,12 @@ apply_textdelta(void *file_baton,
   int svndiff_version;
   int compression_level;
 
-  /* Store the stream in a temporary file; we'll give it to serf when we
+  /* Construct a holder for the request body; we'll give it to serf when we
    * close this file.
    *
    * TODO: There should be a way we can stream the request body instead of
-   * writing to a temporary file (ugh). A special svn stream serf bucket
-   * that returns EAGAIN until we receive the done call?  But, when
+   * possibly writing to a temporary file (ugh). A special svn stream serf
+   * bucket that returns EAGAIN until we receive the done call?  But, when
    * would we run through the serf context?  Grr.
    *
    * BH: If you wait to a specific event... why not use that event to
@@ -1923,8 +1888,10 @@ apply_textdelta(void *file_baton,
    *     for sure after the request is completely available.
    */
 
-  ctx->stream = svn_stream_lazyopen_create(delayed_commit_stream_open,
-                                           ctx, FALSE, ctx->pool);
+  ctx->svndiff =
+    svn_ra_serf__request_body_create(SVN_RA_SERF__REQUEST_BODY_IN_MEM_SIZE,
+                                     ctx->pool);
+  ctx->stream = svn_ra_serf__request_body_get_stream(ctx->svndiff);
 
   if (ctx->commit_ctx->session->supports_svndiff1 &&
       ctx->commit_ctx->session->using_compression)
@@ -1949,7 +1916,9 @@ apply_textdelta(void *file_baton,
       compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE;
     }
 
-  svn_txdelta_to_svndiff3(handler, handler_baton, ctx->stream,
+  /* Disown the stream; we'll close it explicitly in close_file(). */
+  svn_txdelta_to_svndiff3(handler, handler_baton,
+                          svn_stream_disown(ctx->stream, pool),
                           svndiff_version, compression_level, pool);
 
   if (base_checksum)
@@ -2016,8 +1985,11 @@ close_file(void *file_baton,
         }
       else
         {
-          handler->body_delegate = create_put_body;
-          handler->body_delegate_baton = ctx;
+          SVN_ERR(svn_stream_close(ctx->stream));
+
+          svn_ra_serf__request_body_get_delegate(&handler->body_delegate,
+                                                 &handler->body_delegate_baton,
+                                                 ctx->svndiff);
           handler->body_type = SVN_SVNDIFF_MIME_TYPE;
         }
 
@@ -2035,8 +2007,9 @@ close_file(void *file_baton,
         return svn_error_trace(svn_ra_serf__unexpected_status(handler));
     }
 
+  /* Don't keep open file handles longer than necessary. */
   if (ctx->svndiff)
-    SVN_ERR(svn_io_file_close(ctx->svndiff, scratch_pool));
+    SVN_ERR(svn_ra_serf__request_body_cleanup(ctx->svndiff, scratch_pool));
 
   /* If we had any prop changes, push them via PROPPATCH. */
   if (apr_hash_count(ctx->prop_changes))

Modified: subversion/branches/ra-git/subversion/libsvn_ra_serf/ra_serf.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_serf/ra_serf.h?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_serf/ra_serf.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_serf/ra_serf.h Tue Oct 11 09:11:50 2016
@@ -1570,6 +1570,39 @@ svn_ra_serf__uri_parse(apr_uri_t *uri,
                        apr_pool_t *result_pool);
 
 
+/* Default limit for in-memory size of a request body. */
+#define SVN_RA_SERF__REQUEST_BODY_IN_MEM_SIZE 256 * 1024
+
+/* An opaque structure used to prepare a request body. */
+typedef struct svn_ra_serf__request_body_t svn_ra_serf__request_body_t;
+
+/* Constructor for svn_ra_serf__request_body_t.  Creates a new writable
+   buffer for the request body.  Request bodies under IN_MEMORY_SIZE
+   bytes will be held in memory, otherwise, the body content will be
+   spilled to a temporary file. */
+svn_ra_serf__request_body_t *
+svn_ra_serf__request_body_create(apr_size_t in_memory_size,
+                                 apr_pool_t *result_pool);
+
+/* Get the writable stream associated with BODY. */
+svn_stream_t *
+svn_ra_serf__request_body_get_stream(svn_ra_serf__request_body_t *body);
+
+/* Get a svn_ra_serf__request_body_delegate_t and baton for BODY. */
+void
+svn_ra_serf__request_body_get_delegate(svn_ra_serf__request_body_delegate_t *del,
+                                       void **baton,
+                                       svn_ra_serf__request_body_t *body);
+
+/* Release intermediate resources associated with BODY.  These resources
+   (such as open file handles) will be automatically released when the
+   pool used to construct BODY is cleared or destroyed, but this optional
+   function allows doing that explicitly. */
+svn_error_t *
+svn_ra_serf__request_body_cleanup(svn_ra_serf__request_body_t *body,
+                                  apr_pool_t *scratch_pool);
+
+
 #if defined(SVN_DEBUG)
 /* Wrapper macros to collect file and line information */
 #define svn_ra_serf__wrap_err \

Modified: subversion/branches/ra-git/subversion/libsvn_ra_serf/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_serf/update.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_serf/update.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_serf/update.c Tue Oct 11 09:11:50 2016
@@ -433,13 +433,11 @@ struct report_context_t {
   const svn_delta_editor_t *editor;
   void *editor_baton;
 
-  /* The file holding request body for the REPORT.
-   *
-   * ### todo: It will be better for performance to store small
-   * request bodies (like 4k) in memory and bigger bodies on disk.
-   */
+  /* Stream for collecting the request body. */
   svn_stream_t *body_template;
-  body_create_baton_t *body;
+
+  /* Buffer holding request body for the REPORT (can spill to disk). */
+  svn_ra_serf__request_body_t *body;
 
   /* number of pending GET requests */
   unsigned int num_active_fetches;
@@ -457,148 +455,6 @@ struct report_context_t {
   svn_boolean_t closed_root;
 };
 
-/* Baton for collecting REPORT body. Depending on the size this
-   work is backed by a memory buffer (via serf buckets) or by
-   a file */
-struct body_create_baton_t
-{
-  apr_pool_t *result_pool;
-  apr_size_t total_bytes;
-
-  apr_pool_t *scratch_pool;
-
-  serf_bucket_alloc_t *alloc;
-  serf_bucket_t *collect_bucket;
-
-  const void *all_data;
-  apr_file_t *file;
-};
-
-
-#define MAX_BODY_IN_RAM (256*1024)
-
-/* Fold all previously collected data in a single buffer allocated in
-   RESULT_POOL and clear all intermediate state */
-static const char *
-body_allocate_all(body_create_baton_t *body,
-                  apr_pool_t *result_pool)
-{
-  char *buffer = apr_pcalloc(result_pool, body->total_bytes);
-  const char *data;
-  apr_size_t sz;
-  apr_status_t s;
-  apr_size_t remaining = body->total_bytes;
-  char *next = buffer;
-
-  while (!(s = serf_bucket_read(body->collect_bucket, remaining, &data, &sz)))
-    {
-      memcpy(next, data, sz);
-      remaining -= sz;
-      next += sz;
-
-      if (! remaining)
-        break;
-    }
-
-  if (!SERF_BUCKET_READ_ERROR(s))
-    {
-      memcpy(next, data, sz);
-    }
-
-  serf_bucket_destroy(body->collect_bucket);
-  body->collect_bucket = NULL;
-
-  return (s != APR_EOF) ? NULL : buffer;
-}
-
-/* Noop function. Make serf take care of freeing in error situations */
-static void serf_free_no_error(void *unfreed_baton, void *block) {}
-
-/* Stream write function for body creation */
-static svn_error_t *
-body_write_fn(void *baton,
-              const char *data,
-              apr_size_t *len)
-{
-  body_create_baton_t *bcb = baton;
-
-  if (!bcb->scratch_pool)
-    bcb->scratch_pool = svn_pool_create(bcb->result_pool);
-
-  if (bcb->file)
-    {
-      SVN_ERR(svn_io_file_write_full(bcb->file, data, *len, NULL,
-                                     bcb->scratch_pool));
-      svn_pool_clear(bcb->scratch_pool);
-
-      bcb->total_bytes += *len;
-    }
-  else if (*len + bcb->total_bytes > MAX_BODY_IN_RAM)
-    {
-      SVN_ERR(svn_io_open_unique_file3(&bcb->file, NULL, NULL,
-                                       svn_io_file_del_on_pool_cleanup,
-                                       bcb->result_pool, bcb->scratch_pool));
-
-      if (bcb->total_bytes)
-        {
-          const char *all = body_allocate_all(bcb, bcb->scratch_pool);
-
-          SVN_ERR(svn_io_file_write_full(bcb->file, all, bcb->total_bytes,
-                                         NULL, bcb->scratch_pool));
-        }
-
-      SVN_ERR(svn_io_file_write_full(bcb->file, data, *len, NULL,
-                                     bcb->scratch_pool));
-      bcb->total_bytes += *len;
-    }
-  else
-    {
-      if (!bcb->alloc)
-        bcb->alloc = serf_bucket_allocator_create(bcb->scratch_pool,
-                                                  serf_free_no_error, NULL);
-
-      if (!bcb->collect_bucket)
-        bcb->collect_bucket = serf_bucket_aggregate_create(bcb->alloc);
-
-      serf_bucket_aggregate_append(bcb->collect_bucket,
-                                   serf_bucket_simple_copy_create(data, *len,
-                                                                  bcb->alloc));
-
-      bcb->total_bytes += *len;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Stream close function for collecting body */
-static svn_error_t *
-body_done_fn(void *baton)
-{
-  body_create_baton_t *bcb = baton;
-  if (bcb->file)
-    {
-      /* We need to flush the file, make it unbuffered (so that it can be
-        * zero-copied via mmap), and reset the position before attempting
-        * to deliver the file.
-        *
-        * N.B. If we have APR 1.3+, we can unbuffer the file to let us use
-        * mmap and zero-copy the PUT body.  However, on older APR versions,
-        * we can't check the buffer status; but serf will fall through and
-        * create a file bucket for us on the buffered handle.
-        */
-
-      SVN_ERR(svn_io_file_flush(bcb->file, bcb->scratch_pool));
-      apr_file_buffer_set(bcb->file, NULL, 0);
-    }
-  else if (bcb->collect_bucket)
-    bcb->all_data = body_allocate_all(bcb, bcb->result_pool);
-
-  if (bcb->scratch_pool)
-    svn_pool_destroy(bcb->scratch_pool);
-
-  return SVN_NO_ERROR;
-}
-
 static svn_error_t *
 create_dir_baton(dir_baton_t **new_dir,
                  report_context_t *ctx,
@@ -2313,37 +2169,6 @@ link_path(void *report_baton,
   return APR_SUCCESS;
 }
 
-/* Serf callback to create update request body bucket.
-   Implements svn_ra_serf__request_body_delegate_t */
-static svn_error_t *
-create_update_report_body(serf_bucket_t **body_bkt,
-                          void *baton,
-                          serf_bucket_alloc_t *alloc,
-                          apr_pool_t *pool /* request pool */,
-                          apr_pool_t *scratch_pool)
-{
-  report_context_t *report = baton;
-  body_create_baton_t *body = report->body;
-
-  if (body->file)
-    {
-      apr_off_t offset;
-
-      offset = 0;
-      SVN_ERR(svn_io_file_seek(body->file, APR_SET, &offset, pool));
-
-      *body_bkt = serf_bucket_file_create(report->body->file, alloc);
-    }
-  else
-    {
-      *body_bkt = serf_bucket_simple_create(body->all_data,
-                                            body->total_bytes,
-                                            NULL, NULL, alloc);
-    }
-
-  return SVN_NO_ERROR;
-}
-
 /* Serf callback to setup update request headers. */
 static svn_error_t *
 setup_update_report_headers(serf_bucket_t *headers,
@@ -2684,10 +2509,11 @@ finish_report(void *report_baton,
   handler = svn_ra_serf__create_expat_handler(sess, xmlctx, NULL,
                                               scratch_pool);
 
+  svn_ra_serf__request_body_get_delegate(&handler->body_delegate,
+                                         &handler->body_delegate_baton,
+                                         report->body);
   handler->method = "REPORT";
   handler->path = report_target;
-  handler->body_delegate = create_update_report_body;
-  handler->body_delegate_baton = report;
   handler->body_type = "text/xml";
   handler->custom_accept_encoding = TRUE;
   handler->header_delegate = setup_update_report_headers;
@@ -2802,11 +2628,10 @@ make_update_reporter(svn_ra_session_t *r
   *reporter = &ra_serf_reporter;
   *report_baton = report;
 
-  report->body = apr_pcalloc(report->pool, sizeof(*report->body));
-  report->body->result_pool = report->pool;
-  report->body_template = svn_stream_create(report->body, report->pool);
-  svn_stream_set_write(report->body_template, body_write_fn);
-  svn_stream_set_close(report->body_template, body_done_fn);
+  report->body =
+    svn_ra_serf__request_body_create(SVN_RA_SERF__REQUEST_BODY_IN_MEM_SIZE,
+                                     report->pool);
+  report->body_template = svn_ra_serf__request_body_get_stream(report->body);
 
   if (sess->bulk_updates == svn_tristate_true)
     {

Modified: subversion/branches/ra-git/subversion/libsvn_ra_svn/cyrus_auth.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_svn/cyrus_auth.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_svn/cyrus_auth.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_svn/cyrus_auth.c Tue Oct 11 09:11:50 2016
@@ -68,7 +68,7 @@ static apr_status_t sasl_done_cb(void *d
      apr_initialize()/apr_terminate() more than once. */
   svn_ra_svn__sasl_status = 0;
   if (svn_atomic_dec(&sasl_ctx_count) == 0)
-    sasl_done();
+    svn_sasl__done();
   return APR_SUCCESS;
 }
 
@@ -174,10 +174,10 @@ svn_ra_svn__sasl_common_init(apr_pool_t
   apr_pool_cleanup_register(sasl_pool, NULL, sasl_done_cb,
                             apr_pool_cleanup_null);
 #if APR_HAS_THREADS
-  sasl_set_mutex(sasl_mutex_alloc_cb,
-                 sasl_mutex_lock_cb,
-                 sasl_mutex_unlock_cb,
-                 sasl_mutex_free_cb);
+  svn_sasl__set_mutex(sasl_mutex_alloc_cb,
+                      sasl_mutex_lock_cb,
+                      sasl_mutex_unlock_cb,
+                      sasl_mutex_free_cb);
   free_mutexes = apr_array_make(sasl_pool, 0, sizeof(svn_mutex__t *));
   SVN_ERR(svn_mutex__init(&array_mutex, TRUE, sasl_pool));
 
@@ -223,7 +223,7 @@ get_sasl_error(sasl_conn_t *sasl_ctx, in
 
   return apr_psprintf(result_pool,
                       _("SASL authentication error: %s%s"),
-                      sasl_errdetail(sasl_ctx), sasl_errno_msg);
+                      svn_sasl__errdetail(sasl_ctx), sasl_errno_msg);
 }
 
 static svn_error_t *sasl_init_cb(void *baton, apr_pool_t *pool)
@@ -232,7 +232,7 @@ static svn_error_t *sasl_init_cb(void *b
 
   SVN_ERR(svn_ra_svn__sasl_common_init(pool));
   clear_sasl_errno();
-  result = sasl_client_init(NULL);
+  result = svn_sasl__client_init(NULL);
   if (result != SASL_OK)
     {
       const char *sasl_errno_msg = get_sasl_errno_msg(result, pool);
@@ -240,7 +240,7 @@ static svn_error_t *sasl_init_cb(void *b
       return svn_error_createf
         (SVN_ERR_RA_NOT_AUTHORIZED, NULL,
          _("Could not initialized the SASL library: %s%s"),
-         sasl_errstring(result, NULL, NULL),
+         svn_sasl__errstring(result, NULL, NULL),
          sasl_errno_msg);
     }
 
@@ -257,9 +257,9 @@ svn_error_t *svn_ra_svn__sasl_init(void)
 static apr_status_t sasl_dispose_cb(void *data)
 {
   sasl_conn_t *sasl_ctx = data;
-  sasl_dispose(&sasl_ctx);
+  svn_sasl__dispose(&sasl_ctx);
   if (svn_atomic_dec(&sasl_ctx_count) == 0)
-    sasl_done();
+    svn_sasl__done();
   return APR_SUCCESS;
 }
 
@@ -403,17 +403,17 @@ static svn_error_t *new_sasl_ctx(sasl_co
   int result;
 
   clear_sasl_errno();
-  result = sasl_client_new(SVN_RA_SVN_SASL_NAME,
-                           hostname, local_addrport, remote_addrport,
-                           callbacks, SASL_SUCCESS_DATA,
-                           sasl_ctx);
+  result = svn_sasl__client_new(SVN_RA_SVN_SASL_NAME,
+                                hostname, local_addrport, remote_addrport,
+                                callbacks, SASL_SUCCESS_DATA,
+                                sasl_ctx);
   if (result != SASL_OK)
     {
       const char *sasl_errno_msg = get_sasl_errno_msg(result, pool);
 
       return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                                _("Could not create SASL context: %s%s"),
-                               sasl_errstring(result, NULL, NULL),
+                               svn_sasl__errstring(result, NULL, NULL),
                                sasl_errno_msg);
     }
   svn_atomic_inc(&sasl_ctx_count);
@@ -427,8 +427,8 @@ static svn_error_t *new_sasl_ctx(sasl_co
          should be the username, but since SASL doesn't seem
          to use it on the client side, any non-empty string will do. */
       clear_sasl_errno();
-      result = sasl_setprop(*sasl_ctx,
-                            SASL_AUTH_EXTERNAL, " ");
+      result = svn_sasl__setprop(*sasl_ctx,
+                                 SASL_AUTH_EXTERNAL, " ");
       if (result != SASL_OK)
         return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                                 get_sasl_error(*sasl_ctx, result, pool));
@@ -436,7 +436,7 @@ static svn_error_t *new_sasl_ctx(sasl_co
 
   /* Set security properties. */
   svn_ra_svn__default_secprops(&secprops);
-  sasl_setprop(*sasl_ctx, SASL_SEC_PROPS, &secprops);
+  svn_sasl__setprop(*sasl_ctx, SASL_SEC_PROPS, &secprops);
 
   return SVN_NO_ERROR;
 }
@@ -460,12 +460,12 @@ static svn_error_t *try_auth(svn_ra_svn_
     {
       again = FALSE;
       clear_sasl_errno();
-      result = sasl_client_start(sasl_ctx,
-                                 mechstring,
-                                 &client_interact,
-                                 &out,
-                                 &outlen,
-                                 &mech);
+      result = svn_sasl__client_start(sasl_ctx,
+                                      mechstring,
+                                      &client_interact,
+                                      &out,
+                                      &outlen,
+                                      &mech);
       switch (result)
         {
           case SASL_OK:
@@ -531,12 +531,12 @@ static svn_error_t *try_auth(svn_ra_svn_
         in = svn_base64_decode_string(in, pool);
 
       clear_sasl_errno();
-      result = sasl_client_step(sasl_ctx,
-                                in->data,
-                                (const unsigned int) in->len,
-                                &client_interact,
-                                &out, /* Filled in by SASL. */
-                                &outlen);
+      result = svn_sasl__client_step(sasl_ctx,
+                                     in->data,
+                                     (const unsigned int) in->len,
+                                     &client_interact,
+                                     &out, /* Filled in by SASL. */
+                                     &outlen);
 
       if (result != SASL_OK && result != SASL_CONTINUE)
         return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
@@ -620,9 +620,9 @@ static svn_error_t *sasl_read_cb(void *b
           return SVN_NO_ERROR;
         }
       clear_sasl_errno();
-      result = sasl_decode(sasl_baton->ctx, buffer, (unsigned int) len2,
-                           &sasl_baton->read_buf,
-                           &sasl_baton->read_len);
+      result = svn_sasl__decode(sasl_baton->ctx, buffer, (unsigned int) len2,
+                                &sasl_baton->read_buf,
+                                &sasl_baton->read_len);
       if (result != SASL_OK)
         return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                                 get_sasl_error(sasl_baton->ctx, result,
@@ -662,9 +662,9 @@ sasl_write_cb(void *baton, const char *b
       /* Make sure we don't write too much. */
       *len = (*len > sasl_baton->maxsize) ? sasl_baton->maxsize : *len;
       clear_sasl_errno();
-      result = sasl_encode(sasl_baton->ctx, buffer, (unsigned int) *len,
-                           &sasl_baton->write_buf,
-                           &sasl_baton->write_len);
+      result = svn_sasl__encode(sasl_baton->ctx, buffer, (unsigned int) *len,
+                                &sasl_baton->write_buf,
+                                &sasl_baton->write_len);
 
       if (result != SASL_OK)
         return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
@@ -725,7 +725,7 @@ svn_error_t *svn_ra_svn__enable_sasl_enc
 
       /* Get the strength of the security layer. */
       clear_sasl_errno();
-      result = sasl_getprop(sasl_ctx, SASL_SSF, (void*) &ssfp);
+      result = svn_sasl__getprop(sasl_ctx, SASL_SSF, (void*) &ssfp);
       if (result != SASL_OK)
         return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                                 get_sasl_error(sasl_ctx, result, pool));
@@ -745,7 +745,7 @@ svn_error_t *svn_ra_svn__enable_sasl_enc
 
           /* Find out the maximum input size for sasl_encode. */
           clear_sasl_errno();
-          result = sasl_getprop(sasl_ctx, SASL_MAXOUTBUF, &maxsize);
+          result = svn_sasl__getprop(sasl_ctx, SASL_MAXOUTBUF, &maxsize);
           if (result != SASL_OK)
             return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                                     get_sasl_error(sasl_ctx, result, pool));
@@ -756,9 +756,10 @@ svn_error_t *svn_ra_svn__enable_sasl_enc
           if (conn->read_end > conn->read_ptr)
             {
               clear_sasl_errno();
-              result = sasl_decode(sasl_ctx, conn->read_ptr,
-                             (unsigned int) (conn->read_end - conn->read_ptr),
-                             &sasl_baton->read_buf, &sasl_baton->read_len);
+              result = svn_sasl__decode(
+                  sasl_ctx, conn->read_ptr,
+                  (unsigned int) (conn->read_end - conn->read_ptr),
+                  &sasl_baton->read_buf, &sasl_baton->read_len);
               if (result != SASL_OK)
                 return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                                         get_sasl_error(sasl_ctx, result, pool));

Modified: subversion/branches/ra-git/subversion/libsvn_ra_svn/marshal.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_svn/marshal.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_svn/marshal.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_svn/marshal.c Tue Oct 11 09:11:50 2016
@@ -2769,19 +2769,19 @@ changed_path_flags(svn_node_kind_t node_
                    svn_boolean_t text_modified,
                    svn_boolean_t props_modified)
 {
-  const static svn_string_t file_flags[4]
+  static const svn_string_t file_flags[4]
     = { STATIC_SVN_STRING(" ) ( 4:file false false ) ) "),
         STATIC_SVN_STRING(" ) ( 4:file false true ) ) "),
         STATIC_SVN_STRING(" ) ( 4:file true false ) ) "),
         STATIC_SVN_STRING(" ) ( 4:file true true ) ) ") };
 
-  const static svn_string_t dir_flags[4]
+  static const svn_string_t dir_flags[4]
     = { STATIC_SVN_STRING(" ) ( 3:dir false false ) ) "),
         STATIC_SVN_STRING(" ) ( 3:dir false true ) ) "),
         STATIC_SVN_STRING(" ) ( 3:dir true false ) ) "),
         STATIC_SVN_STRING(" ) ( 3:dir true true ) ) ") };
 
-  const static svn_string_t no_flags = STATIC_SVN_STRING("");
+  static const svn_string_t no_flags = STATIC_SVN_STRING("");
 
   /* Select the array based on the NODE_KIND. */
   const svn_string_t *flags;
@@ -2804,7 +2804,7 @@ changed_path_flags(svn_node_kind_t node_
 svn_error_t *
 svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
                                         apr_pool_t *pool,
-                                        const char *path,
+                                        const svn_string_t *path,
                                         char action,
                                         const char *copyfrom_path,
                                         svn_revnum_t copyfrom_rev,
@@ -2812,7 +2812,7 @@ svn_ra_svn__write_data_log_changed_path(
                                         svn_boolean_t text_modified,
                                         svn_boolean_t props_modified)
 {
-  apr_size_t path_len = strlen(path);
+  apr_size_t path_len = path->len;
   apr_size_t copyfrom_len = copyfrom_path ? strlen(copyfrom_path) : 0;
   const svn_string_t *flags_str = changed_path_flags(node_kind,
                                                      text_modified,
@@ -2845,7 +2845,7 @@ svn_ra_svn__write_data_log_changed_path(
       p[1] = ' ';
 
       /* Write path. */
-      p = write_ncstring_quick(p + 2, path, path_len);
+      p = write_ncstring_quick(p + 2, path->data, path_len);
 
       /* Action */
       p[0] = action;
@@ -2873,7 +2873,7 @@ svn_ra_svn__write_data_log_changed_path(
       /* Standard code path (fallback). */
       SVN_ERR(write_tuple_start_list(conn, pool));
 
-      SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, path, path_len));
+      SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, path->data, path_len));
       SVN_ERR(writebuf_writechar(conn, pool, action));
       SVN_ERR(writebuf_writechar(conn, pool, ' '));
       SVN_ERR(write_tuple_start_list(conn, pool));

Modified: subversion/branches/ra-git/subversion/libsvn_repos/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/commit.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_repos/commit.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_repos/commit.c Tue Oct 11 09:11:50 2016
@@ -210,15 +210,16 @@ invoke_commit_cb(svn_commit_callback2_t
   /* const */ svn_string_t *date;
   /* const */ svn_string_t *author;
   svn_commit_info_t *commit_info;
+  apr_hash_t *revprops;
 
   if (commit_cb == NULL)
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_fs_revision_prop2(&date, fs, revision, SVN_PROP_REVISION_DATE,
-                                TRUE, scratch_pool, scratch_pool));
-  SVN_ERR(svn_fs_revision_prop2(&author, fs, revision,
-                                SVN_PROP_REVISION_AUTHOR,
-                                TRUE, scratch_pool, scratch_pool));
+  SVN_ERR(svn_fs_revision_proplist2(&revprops, fs, revision,
+                                    TRUE, scratch_pool, scratch_pool));
+
+  date = svn_hash_gets(revprops, SVN_PROP_REVISION_DATE);
+  author = svn_hash_gets(revprops, SVN_PROP_REVISION_AUTHOR);
 
   commit_info = svn_create_commit_info(scratch_pool);
 

Modified: subversion/branches/ra-git/subversion/libsvn_repos/config_pool.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/config_pool.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_repos/config_pool.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_repos/config_pool.c Tue Oct 11 09:11:50 2016
@@ -199,10 +199,8 @@ auto_parse(svn_config_t **cfg,
   apr_pool_t *cfg_pool;
 
   /* calculate SHA1 over the whole file contents */
-  SVN_ERR(svn_stream_close
-              (svn_stream_checksummed2
-                  (svn_stream_from_stringbuf(contents, scratch_pool),
-                   &checksum, NULL, svn_checksum_sha1, TRUE, scratch_pool)));
+  SVN_ERR(svn_checksum(&checksum, svn_checksum_sha1,
+                       contents->data, contents->len, scratch_pool));
 
   /* return reference to suitable config object if that already exists */
   *key = checksum_as_key(checksum, result_pool);

Modified: subversion/branches/ra-git/subversion/libsvn_repos/delta.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/delta.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_repos/delta.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_repos/delta.c Tue Oct 11 09:11:50 2016
@@ -603,19 +603,6 @@ send_text_delta(struct context *c,
     }
 }
 
-svn_error_t *
-svn_repos__compare_files(svn_boolean_t *changed_p,
-                         svn_fs_root_t *root1,
-                         const char *path1,
-                         svn_fs_root_t *root2,
-                         const char *path2,
-                         apr_pool_t *pool)
-{
-  return svn_error_trace(svn_fs_contents_different(changed_p, root1, path1,
-                                                   root2, path2, pool));
-}
-
-
 /* Make the appropriate edits on FILE_BATON to change its contents and
    properties from those in SOURCE_PATH to those in TARGET_PATH. */
 static svn_error_t *

Modified: subversion/branches/ra-git/subversion/libsvn_repos/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/deprecated.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_repos/deprecated.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_repos/deprecated.c Tue Oct 11 09:11:50 2016
@@ -35,6 +35,8 @@
 
 #include "repos.h"
 
+#include "private/svn_repos_private.h"
+
 
 
 
@@ -506,6 +508,30 @@ svn_repos_fs_get_locks(apr_hash_t **lock
 
 /*** From logs.c ***/
 svn_error_t *
+svn_repos_get_logs4(svn_repos_t *repos,
+                    const apr_array_header_t *paths,
+                    svn_revnum_t start,
+                    svn_revnum_t end,
+                    int limit,
+                    svn_boolean_t discover_changed_paths,
+                    svn_boolean_t strict_node_history,
+                    svn_boolean_t include_merged_revisions,
+                    const apr_array_header_t *revprops,
+                    svn_repos_authz_func_t authz_read_func,
+                    void *authz_read_baton,
+                    svn_log_entry_receiver_t receiver,
+                    void *receiver_baton,
+                    apr_pool_t *pool)
+{
+  return svn_repos__get_logs_compat(repos, paths, start, end, limit,
+                                    discover_changed_paths,
+                                    strict_node_history,
+                                    include_merged_revisions, revprops,
+                                    authz_read_func, authz_read_baton,
+                                    receiver, receiver_baton, pool);
+}
+
+svn_error_t *
 svn_repos_get_logs3(svn_repos_t *repos,
                     const apr_array_header_t *paths,
                     svn_revnum_t start,

Modified: subversion/branches/ra-git/subversion/libsvn_repos/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_repos/log.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_repos/log.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_repos/log.c Tue Oct 11 09:11:50 2016
@@ -46,6 +46,18 @@
 #include "private/svn_string_private.h"
 
 
+/* This is a mere convenience struct such that we don't need to pass that
+   many parameters around individually. */
+typedef struct log_callbacks_t
+{
+  svn_repos_path_change_receiver_t path_change_receiver;
+  void *path_change_receiver_baton;
+  svn_repos_log_entry_receiver_t revision_receiver;
+  void *revision_receiver_baton;
+  svn_repos_authz_func_t authz_read_func;
+  void *authz_read_baton;
+} log_callbacks_t;
+
 
 svn_error_t *
 svn_repos_check_revision_access(svn_repos_revision_access_level_t *access_level,
@@ -57,11 +69,11 @@ svn_repos_check_revision_access(svn_repo
 {
   svn_fs_t *fs = svn_repos_fs(repos);
   svn_fs_root_t *rev_root;
-  apr_hash_t *changes;
-  apr_hash_index_t *hi;
+  svn_fs_path_change_iterator_t *iterator;
+  svn_fs_path_change3_t *change;
   svn_boolean_t found_readable = FALSE;
   svn_boolean_t found_unreadable = FALSE;
-  apr_pool_t *subpool;
+  apr_pool_t *iterpool;
 
   /* By default, we'll grant full read access to REVISION. */
   *access_level = svn_repos_revision_access_full;
@@ -72,25 +84,27 @@ svn_repos_check_revision_access(svn_repo
 
   /* Fetch the changes associated with REVISION. */
   SVN_ERR(svn_fs_revision_root(&rev_root, fs, revision, pool));
-  SVN_ERR(svn_fs_paths_changed2(&changes, rev_root, pool));
+  SVN_ERR(svn_fs_paths_changed3(&iterator, rev_root, pool, pool));
+  SVN_ERR(svn_fs_path_change_get(&change, iterator));
 
-  /* No changed paths?  We're done. */
-  if (apr_hash_count(changes) == 0)
+  /* No changed paths?  We're done.
+
+     Note that the check at "decision:" assumes that at least one
+     path has been processed.  So, this actually affects functionality. */
+  if (!change)
     return SVN_NO_ERROR;
 
   /* Otherwise, we have to check the readability of each changed
      path, or at least enough to answer the question asked. */
-  subpool = svn_pool_create(pool);
-  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
+  iterpool = svn_pool_create(pool);
+  while (change)
     {
-      const char *key = apr_hash_this_key(hi);
-      svn_fs_path_change2_t *change = apr_hash_this_val(hi);
       svn_boolean_t readable;
 
-      svn_pool_clear(subpool);
+      svn_pool_clear(iterpool);
 
-      SVN_ERR(authz_read_func(&readable, rev_root, key,
-                              authz_read_baton, subpool));
+      SVN_ERR(authz_read_func(&readable, rev_root, change->path.data,
+                              authz_read_baton, iterpool));
       if (! readable)
         found_unreadable = TRUE;
       else
@@ -110,15 +124,16 @@ svn_repos_check_revision_access(svn_repo
             svn_revnum_t copyfrom_rev;
 
             SVN_ERR(svn_fs_copied_from(&copyfrom_rev, &copyfrom_path,
-                                       rev_root, key, subpool));
+                                       rev_root, change->path.data,
+                                       iterpool));
             if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
               {
                 svn_fs_root_t *copyfrom_root;
                 SVN_ERR(svn_fs_revision_root(&copyfrom_root, fs,
-                                             copyfrom_rev, subpool));
+                                             copyfrom_rev, iterpool));
                 SVN_ERR(authz_read_func(&readable,
                                         copyfrom_root, copyfrom_path,
-                                        authz_read_baton, subpool));
+                                        authz_read_baton, iterpool));
                 if (! readable)
                   found_unreadable = TRUE;
 
@@ -135,10 +150,12 @@ svn_repos_check_revision_access(svn_repo
         default:
           break;
         }
+
+      SVN_ERR(svn_fs_path_change_get(&change, iterator));
     }
 
  decision:
-  svn_pool_destroy(subpool);
+  svn_pool_destroy(iterpool);
 
   /* Either every changed path was unreadable... */
   if (! found_readable)
@@ -153,21 +170,14 @@ svn_repos_check_revision_access(svn_repo
 }
 
 
-/* Store as keys in CHANGED the paths of all node in ROOT that show a
- * significant change.  "Significant" means that the text or
- * properties of the node were changed, or that the node was added or
- * deleted.
- *
- * The CHANGED hash set and its keys and values are allocated in POOL;
- * keys are const char * paths and values are svn_log_changed_path_t.
- *
- * To prevent changes from being processed over and over again, the
- * changed paths for ROOT may be passed in PREFETCHED_CHANGES.  If the
- * latter is NULL, we will request the list inside this function.
+/* Find all significant changes under ROOT and, if not NULL, report them
+ * to the CALLBACKS->PATH_CHANGE_RECEIVER.  "Significant" means that the
+ * text or properties of the node were changed, or that the node was added
+ * or deleted.
  *
- * If optional AUTHZ_READ_FUNC is non-NULL, then use it (with
- * AUTHZ_READ_BATON and FS) to check whether each changed-path (and
- * copyfrom_path) is readable:
+ * If optional CALLBACKS->AUTHZ_READ_FUNC is non-NULL, then use it (with
+ * CALLBACKS->AUTHZ_READ_BATON and FS) to check whether each changed-path
+ * (and copyfrom_path) is readable:
  *
  *     - If absolutely every changed-path (and copyfrom_path) is
  *     readable, then return the full CHANGED hash, and set
@@ -185,40 +195,22 @@ svn_repos_check_revision_access(svn_repo
  */
 static svn_error_t *
 detect_changed(svn_repos_revision_access_level_t *access_level,
-               apr_hash_t **changed,
                svn_fs_root_t *root,
                svn_fs_t *fs,
-               apr_hash_t *prefetched_changes,
-               svn_repos_authz_func_t authz_read_func,
-               void *authz_read_baton,
-               apr_pool_t *pool)
+               const log_callbacks_t *callbacks,
+               apr_pool_t *scratch_pool)
 {
-  apr_hash_t *changes = prefetched_changes;
-  apr_hash_index_t *hi;
+  svn_fs_path_change_iterator_t *iterator;
+  svn_fs_path_change3_t *change;
   apr_pool_t *iterpool;
   svn_boolean_t found_readable = FALSE;
   svn_boolean_t found_unreadable = FALSE;
 
-  /* If we create the CHANGES hash ourselves, we can reuse it as the
-   * result hash as it contains the exact same keys - but with _all_
-   * values being replaced by structs of a different type. */
-  if (changes == NULL)
-    {
-      SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
-
-      /* If we are going to filter the results, we won't use the exact
-       * same keys but put them into a new hash. */
-      if (authz_read_func)
-        *changed = svn_hash__make(pool);
-      else
-        *changed = changes;
-    }
-  else
-    {
-      *changed = svn_hash__make(pool);
-    }
+  /* Retrieve the first change in the list. */
+  SVN_ERR(svn_fs_paths_changed3(&iterator, root, scratch_pool, scratch_pool));
+  SVN_ERR(svn_fs_path_change_get(&change, iterator));
 
-  if (apr_hash_count(changes) == 0)
+  if (!change)
     {
       /* No paths changed in this revision?  Uh, sure, I guess the
          revision is readable, then.  */
@@ -226,30 +218,26 @@ detect_changed(svn_repos_revision_access
       return SVN_NO_ERROR;
     }
 
-  iterpool = svn_pool_create(pool);
-  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
+  iterpool = svn_pool_create(scratch_pool);
+  while (change)
     {
       /* NOTE:  Much of this loop is going to look quite similar to
          svn_repos_check_revision_access(), but we have to do more things
          here, so we'll live with the duplication. */
-      const char *path = apr_hash_this_key(hi);
-      apr_ssize_t path_len = apr_hash_this_key_len(hi);
-      svn_fs_path_change2_t *change = apr_hash_this_val(hi);
-      char action;
-      svn_log_changed_path2_t *item;
-
+      const char *path = change->path.data;
       svn_pool_clear(iterpool);
 
       /* Skip path if unreadable. */
-      if (authz_read_func)
+      if (callbacks->authz_read_func)
         {
           svn_boolean_t readable;
-          SVN_ERR(authz_read_func(&readable,
-                                  root, path,
-                                  authz_read_baton, iterpool));
+          SVN_ERR(callbacks->authz_read_func(&readable, root, path,
+                                             callbacks->authz_read_baton,
+                                             iterpool));
           if (! readable)
             {
               found_unreadable = TRUE;
+              SVN_ERR(svn_fs_path_change_get(&change, iterator));
               continue;
             }
         }
@@ -257,41 +245,9 @@ detect_changed(svn_repos_revision_access
       /* At least one changed-path was readable. */
       found_readable = TRUE;
 
-      switch (change->change_kind)
-        {
-        case svn_fs_path_change_reset:
-          continue;
-
-        case svn_fs_path_change_add:
-          action = 'A';
-          break;
-
-        case svn_fs_path_change_replace:
-          action = 'R';
-          break;
-
-        case svn_fs_path_change_delete:
-          action = 'D';
-          break;
-
-        case svn_fs_path_change_modify:
-        default:
-          action = 'M';
-          break;
-        }
-
-      item = svn_log_changed_path2_create(pool);
-      item->action = action;
-      item->node_kind = change->node_kind;
-      item->copyfrom_rev = SVN_INVALID_REVNUM;
-      item->text_modified = change->text_mod ? svn_tristate_true
-                                             : svn_tristate_false;
-      item->props_modified = change->prop_mod ? svn_tristate_true
-                                              : svn_tristate_false;
-
       /* Pre-1.6 revision files don't store the change path kind, so fetch
          it manually. */
-      if (item->node_kind == svn_node_unknown)
+      if (change->node_kind == svn_node_unknown)
         {
           svn_fs_root_t *check_root = root;
           const char *check_path = path;
@@ -316,18 +272,19 @@ detect_changed(svn_repos_revision_access
               SVN_ERR(svn_fs_history_prev2(&history, history, TRUE, iterpool,
                                            iterpool));
 
-              SVN_ERR(svn_fs_history_location(&parent_path, &prev_rev, history,
-                                              iterpool));
-              SVN_ERR(svn_fs_revision_root(&check_root, fs, prev_rev, iterpool));
+              SVN_ERR(svn_fs_history_location(&parent_path, &prev_rev,
+                                              history, iterpool));
+              SVN_ERR(svn_fs_revision_root(&check_root, fs, prev_rev,
+                                           iterpool));
               check_path = svn_fspath__join(parent_path, name, iterpool);
             }
 
-          SVN_ERR(svn_fs_check_path(&item->node_kind, check_root, check_path,
+          SVN_ERR(svn_fs_check_path(&change->node_kind, check_root, check_path,
                                     iterpool));
         }
 
-
-      if ((action == 'A') || (action == 'R'))
+      if (   (change->change_kind == svn_fs_path_change_add)
+          || (change->change_kind == svn_fs_path_change_replace))
         {
           const char *copyfrom_path = change->copyfrom_path;
           svn_revnum_t copyfrom_rev = change->copyfrom_rev;
@@ -339,35 +296,44 @@ detect_changed(svn_repos_revision_access
             {
               SVN_ERR(svn_fs_copied_from(&copyfrom_rev, &copyfrom_path,
                                         root, path, iterpool));
-              copyfrom_path = apr_pstrdup(pool, copyfrom_path);
+              change->copyfrom_known = TRUE;
             }
 
           if (copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
             {
               svn_boolean_t readable = TRUE;
 
-              if (authz_read_func)
+              if (callbacks->authz_read_func)
                 {
                   svn_fs_root_t *copyfrom_root;
 
                   SVN_ERR(svn_fs_revision_root(&copyfrom_root, fs,
                                                copyfrom_rev, iterpool));
-                  SVN_ERR(authz_read_func(&readable,
-                                          copyfrom_root, copyfrom_path,
-                                          authz_read_baton, iterpool));
+                  SVN_ERR(callbacks->authz_read_func(&readable,
+                                                     copyfrom_root,
+                                                     copyfrom_path,
+                                                     callbacks->authz_read_baton,
+                                                     iterpool));
                   if (! readable)
                     found_unreadable = TRUE;
                 }
 
               if (readable)
                 {
-                  item->copyfrom_path = copyfrom_path;
-                  item->copyfrom_rev = copyfrom_rev;
+                  change->copyfrom_path = copyfrom_path;
+                  change->copyfrom_rev = copyfrom_rev;
                 }
             }
         }
 
-      apr_hash_set(*changed, path, path_len, item);
+      if (callbacks->path_change_receiver)
+        SVN_ERR(callbacks->path_change_receiver(
+                                     callbacks->path_change_receiver_baton,
+                                     change,
+                                     iterpool));
+
+      /* Next changed path. */
+      SVN_ERR(svn_fs_path_change_get(&change, iterator));
     }
 
   svn_pool_destroy(iterpool);
@@ -594,23 +560,21 @@ next_history_rev(const apr_array_header_
 
 /* Set *DELETED_MERGEINFO_CATALOG and *ADDED_MERGEINFO_CATALOG to
    catalogs describing how mergeinfo values on paths (which are the
-   keys of those catalogs) were changed in REV.  If *PREFETCHED_CHANGES
-   already contains the changed paths for REV, use that.  Otherwise,
-   request that data and return it in *PREFETCHED_CHANGES. */
+   keys of those catalogs) were changed in REV. */
 /* ### TODO: This would make a *great*, useful public function,
    ### svn_repos_fs_mergeinfo_changed()!  -- cmpilato  */
 static svn_error_t *
 fs_mergeinfo_changed(svn_mergeinfo_catalog_t *deleted_mergeinfo_catalog,
                      svn_mergeinfo_catalog_t *added_mergeinfo_catalog,
-                     apr_hash_t **prefetched_changes,
                      svn_fs_t *fs,
                      svn_revnum_t rev,
                      apr_pool_t *result_pool,
                      apr_pool_t *scratch_pool)
 {
   svn_fs_root_t *root;
-  apr_pool_t *iterpool;
-  apr_hash_index_t *hi;
+  apr_pool_t *iterpool, *iterator_pool;
+  svn_fs_path_change_iterator_t *iterator;
+  svn_fs_path_change3_t *change;
   svn_boolean_t any_mergeinfo = FALSE;
   svn_boolean_t any_copy = FALSE;
 
@@ -622,56 +586,69 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
   if (rev == 0)
     return SVN_NO_ERROR;
 
+  /* FS iterators are potentially heavy objects.
+   * Hold them in a separate pool to clean them up asap. */
+  iterator_pool = svn_pool_create(scratch_pool);
+
   /* We're going to use the changed-paths information for REV to
      narrow down our search. */
   SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool));
-  if (*prefetched_changes == NULL)
-    SVN_ERR(svn_fs_paths_changed2(prefetched_changes, root, scratch_pool));
+  SVN_ERR(svn_fs_paths_changed3(&iterator, root, iterator_pool,
+                                iterator_pool));
+  SVN_ERR(svn_fs_path_change_get(&change, iterator));
 
   /* Look for copies and (potential) mergeinfo changes.
-     We will use both flags to take shortcuts further down the road. */
-  for (hi = apr_hash_first(scratch_pool, *prefetched_changes);
-       hi;
-       hi = apr_hash_next(hi))
-    {
-      svn_fs_path_change2_t *change = apr_hash_this_val(hi);
+     We will use both flags to take shortcuts further down the road.
 
+     The critical information here is whether there are any copies
+     because that greatly influences the costs for log processing.
+     So, it is faster to iterate over the changes twice - in the worst
+     case b/c most times there is no m/i at all and we exit out early
+     without any overhead. 
+   */
+  while (change && (!any_mergeinfo || !any_copy))
+    {
       /* If there was a prop change and we are not positive that _no_
          mergeinfo change happened, we must assume that it might have. */
       if (change->mergeinfo_mod != svn_tristate_false && change->prop_mod)
         any_mergeinfo = TRUE;
 
-      switch (change->change_kind)
-        {
-        case svn_fs_path_change_add:
-        case svn_fs_path_change_replace:
-          any_copy = TRUE;
-          break;
+      if (   (change->change_kind == svn_fs_path_change_add)
+          || (change->change_kind == svn_fs_path_change_replace))
+        any_copy = TRUE;
 
-        default:
-          break;
-        }
+      SVN_ERR(svn_fs_path_change_get(&change, iterator));
     }
 
   /* No potential mergeinfo changes?  We're done. */
   if (! any_mergeinfo)
-    return SVN_NO_ERROR;
+    {
+      svn_pool_destroy(iterator_pool);
+      return SVN_NO_ERROR;
+    }
+
+  /* There is or may be some m/i change. Look closely now. */
+  svn_pool_clear(iterator_pool);
+  SVN_ERR(svn_fs_paths_changed3(&iterator, root, iterator_pool,
+                                iterator_pool));
 
   /* Loop over changes, looking for anything that might carry an
      svn:mergeinfo change and is one of our paths of interest, or a
      child or [grand]parent directory thereof. */
   iterpool = svn_pool_create(scratch_pool);
-  for (hi = apr_hash_first(scratch_pool, *prefetched_changes);
-       hi;
-       hi = apr_hash_next(hi))
+  while (TRUE)
     {
       const char *changed_path;
-      svn_fs_path_change2_t *change = apr_hash_this_val(hi);
       const char *base_path = NULL;
       svn_revnum_t base_rev = SVN_INVALID_REVNUM;
       svn_fs_root_t *base_root = NULL;
       svn_string_t *prev_mergeinfo_value = NULL, *mergeinfo_value;
 
+      /* Next change. */
+      SVN_ERR(svn_fs_path_change_get(&change, iterator));
+      if (!change)
+        break;
+
       /* Cheap pre-checks that don't require memory allocation etc. */
 
       /* No mergeinfo change? -> nothing to do here. */
@@ -683,7 +660,7 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
         continue;
 
       /* Begin actual processing */
-      changed_path = apr_hash_this_key(hi);
+      changed_path = change->path.data;
       svn_pool_clear(iterpool);
 
       switch (change->change_kind)
@@ -841,20 +818,18 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
     }
 
   svn_pool_destroy(iterpool);
+  svn_pool_destroy(iterator_pool);
+
   return SVN_NO_ERROR;
 }
 
 
 /* Determine what (if any) mergeinfo for PATHS was modified in
    revision REV, returning the differences for added mergeinfo in
-   *ADDED_MERGEINFO and deleted mergeinfo in *DELETED_MERGEINFO.
-   If *PREFETCHED_CHANGES already contains the changed paths for
-   REV, use that.  Otherwise, request that data and return it in
-   *PREFETCHED_CHANGES. */
+   *ADDED_MERGEINFO and deleted mergeinfo in *DELETED_MERGEINFO. */
 static svn_error_t *
 get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo,
                                svn_mergeinfo_t *deleted_mergeinfo,
-                               apr_hash_t **prefetched_changes,
                                svn_fs_t *fs,
                                const apr_array_header_t *paths,
                                svn_revnum_t rev,
@@ -883,7 +858,6 @@ get_combined_mergeinfo_changes(svn_merge
   /* Fetch the mergeinfo changes for REV. */
   err = fs_mergeinfo_changed(&deleted_mergeinfo_catalog,
                              &added_mergeinfo_catalog,
-                             prefetched_changes,
                              fs, rev,
                              scratch_pool, scratch_pool);
   if (err)
@@ -1072,38 +1046,31 @@ get_combined_mergeinfo_changes(svn_merge
 
 /* Fill LOG_ENTRY with history information in FS at REV. */
 static svn_error_t *
-fill_log_entry(svn_log_entry_t *log_entry,
+fill_log_entry(svn_repos_log_entry_t *log_entry,
                svn_revnum_t rev,
                svn_fs_t *fs,
-               apr_hash_t *prefetched_changes,
-               svn_boolean_t discover_changed_paths,
                const apr_array_header_t *revprops,
-               svn_repos_authz_func_t authz_read_func,
-               void *authz_read_baton,
+               const log_callbacks_t *callbacks,
                apr_pool_t *pool)
 {
-  apr_hash_t *r_props, *changed_paths = NULL;
+  apr_hash_t *r_props;
   svn_boolean_t get_revprops = TRUE, censor_revprops = FALSE;
   svn_boolean_t want_revprops = !revprops || revprops->nelts;
 
   /* Discover changed paths if the user requested them
      or if we need to check that they are readable. */
   if ((rev > 0)
-      && (authz_read_func || discover_changed_paths))
+      && (callbacks->authz_read_func || callbacks->path_change_receiver))
     {
       svn_fs_root_t *newroot;
       svn_repos_revision_access_level_t access_level;
 
       SVN_ERR(svn_fs_revision_root(&newroot, fs, rev, pool));
-      SVN_ERR(detect_changed(&access_level, &changed_paths,
-                             newroot, fs, prefetched_changes,
-                             authz_read_func, authz_read_baton,
-                             pool));
+      SVN_ERR(detect_changed(&access_level, newroot, fs, callbacks, pool));
 
       if (access_level == svn_repos_revision_access_none)
         {
           /* All changed-paths are unreadable, so clear all fields. */
-          changed_paths = NULL;
           get_revprops = FALSE;
         }
       else if (access_level == svn_repos_revision_access_partial)
@@ -1113,11 +1080,6 @@ fill_log_entry(svn_log_entry_t *log_entr
              missing from the hash.) */
           censor_revprops = TRUE;
         }
-
-      /* It may be the case that an authz func was passed in, but
-         the user still doesn't want to see any changed-paths. */
-      if (! discover_changed_paths)
-        changed_paths = NULL;
     }
 
   if (get_revprops && want_revprops)
@@ -1194,20 +1156,83 @@ fill_log_entry(svn_log_entry_t *log_entr
         }
     }
 
-  log_entry->changed_paths = changed_paths;
-  log_entry->changed_paths2 = changed_paths;
   log_entry->revision = rev;
 
   return SVN_NO_ERROR;
 }
 
-/* Send a log message for REV to RECEIVER with its RECEIVER_BATON.
+/* Baton type to be used with the interesting_merge callback. */
+typedef struct interesting_merge_baton_t
+{
+  /* What we are looking for. */
+  svn_revnum_t rev;
+  svn_mergeinfo_t log_target_history_as_mergeinfo;
+
+  /* Set to TRUE if we found it. */
+  svn_boolean_t found_rev_of_interest;
+
+  /* We need to invoke this user-provided callback if not NULL. */
+  svn_repos_path_change_receiver_t inner;
+  void *inner_baton;
+} interesting_merge_baton_t;
+
+/* Implements svn_repos_path_change_receiver_t. 
+ * *BATON is a interesting_merge_baton_t.
+ *
+ * If BATON->REV a merged revision that is not already part of
+ * BATON->LOG_TARGET_HISTORY_AS_MERGEINFO, set BATON->FOUND_REV_OF_INTEREST.
+ */
+static svn_error_t *
+interesting_merge(void *baton,
+                  svn_fs_path_change3_t *change,
+                  apr_pool_t *scratch_pool)
+{
+  interesting_merge_baton_t *b = baton;
+  apr_hash_index_t *hi;
+
+  if (b->inner)
+    SVN_ERR(b->inner(b->inner_baton, change, scratch_pool));
+
+  if (b->found_rev_of_interest)
+    return SVN_NO_ERROR;
+
+  /* Look at each path on the log target's mergeinfo. */
+  for (hi = apr_hash_first(scratch_pool, b->log_target_history_as_mergeinfo);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *mergeinfo_path = apr_hash_this_key(hi);
+      svn_rangelist_t *rangelist = apr_hash_this_val(hi);
+
+      /* Check whether CHANGED_PATH at revision REV is a child of
+          a (path, revision) tuple in LOG_TARGET_HISTORY_AS_MERGEINFO. */
+      if (svn_fspath__skip_ancestor(mergeinfo_path, change->path.data))
+        {
+          int i;
+
+          for (i = 0; i < rangelist->nelts; i++)
+            {
+              svn_merge_range_t *range
+                = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
+              if (b->rev > range->start && b->rev <= range->end)
+               return SVN_NO_ERROR;
+            }
+        }
+    }
+
+  b->found_rev_of_interest = TRUE;
+
+  return SVN_NO_ERROR;
+}
+
+/* Send a log message for REV to the CALLBACKS.
 
    FS is used with REV to fetch the interesting history information,
    such as changed paths, revprops, etc.
 
-   The detect_changed function is used if either AUTHZ_READ_FUNC is
-   not NULL, or if DISCOVER_CHANGED_PATHS is TRUE.  See it for details.
+   The detect_changed function is used if either CALLBACKS->AUTHZ_READ_FUNC
+   is not NULL, or if CALLBACKS->PATH_CHANGE_RECEIVER is not NULL.
+   See it for details.
 
    If DESCENDING_ORDER is true, send child messages in descending order.
 
@@ -1229,107 +1254,51 @@ fill_log_entry(svn_log_entry_t *log_entr
 static svn_error_t *
 send_log(svn_revnum_t rev,
          svn_fs_t *fs,
-         apr_hash_t *prefetched_changes,
          svn_mergeinfo_t log_target_history_as_mergeinfo,
          svn_bit_array__t *nested_merges,
-         svn_boolean_t discover_changed_paths,
          svn_boolean_t subtractive_merge,
          svn_boolean_t handling_merged_revision,
          const apr_array_header_t *revprops,
          svn_boolean_t has_children,
-         svn_log_entry_receiver_t receiver,
-         void *receiver_baton,
-         svn_repos_authz_func_t authz_read_func,
-         void *authz_read_baton,
+         const log_callbacks_t *callbacks,
          apr_pool_t *pool)
 {
-  svn_log_entry_t *log_entry;
-  /* Assume we want to send the log for REV. */
-  svn_boolean_t found_rev_of_interest = TRUE;
-
-  log_entry = svn_log_entry_create(pool);
-  SVN_ERR(fill_log_entry(log_entry, rev, fs, prefetched_changes,
-                         discover_changed_paths || handling_merged_revision,
-                         revprops, authz_read_func, authz_read_baton, pool));
-  log_entry->has_children = has_children;
-  log_entry->subtractive_merge = subtractive_merge;
+  svn_repos_log_entry_t log_entry = { 0 };
+  log_callbacks_t my_callbacks = *callbacks;
+
+  interesting_merge_baton_t baton;
 
   /* Is REV a merged revision that is already part of
      LOG_TARGET_HISTORY_AS_MERGEINFO?  If so then there is no
-     need to send it, since it already was (or will be) sent. */
+     need to send it, since it already was (or will be) sent.
+
+     Use our callback to snoop through the changes. */
   if (handling_merged_revision
-      && log_entry->changed_paths2
       && log_target_history_as_mergeinfo
       && apr_hash_count(log_target_history_as_mergeinfo))
     {
-      apr_hash_index_t *hi;
-      apr_pool_t *iterpool = svn_pool_create(pool);
-
-      /* REV was merged in, but it might already be part of the log target's
-         natural history, so change our starting assumption. */
-      found_rev_of_interest = FALSE;
-
-      /* Look at each changed path in REV. */
-      for (hi = apr_hash_first(pool, log_entry->changed_paths2);
-           hi;
-           hi = apr_hash_next(hi))
-        {
-          svn_boolean_t path_is_in_history = FALSE;
-          const char *changed_path = apr_hash_this_key(hi);
-          apr_hash_index_t *hi2;
-
-          /* Look at each path on the log target's mergeinfo. */
-          for (hi2 = apr_hash_first(iterpool,
-                                    log_target_history_as_mergeinfo);
-               hi2;
-               hi2 = apr_hash_next(hi2))
-            {
-              const char *mergeinfo_path = apr_hash_this_key(hi2);
-              svn_rangelist_t *rangelist = apr_hash_this_val(hi2);
-
-              /* Check whether CHANGED_PATH at revision REV is a child of
-                 a (path, revision) tuple in LOG_TARGET_HISTORY_AS_MERGEINFO. */
-              if (svn_fspath__skip_ancestor(mergeinfo_path, changed_path))
-                {
-                  int i;
-
-                  for (i = 0; i < rangelist->nelts; i++)
-                    {
-                      svn_merge_range_t *range =
-                        APR_ARRAY_IDX(rangelist, i,
-                                      svn_merge_range_t *);
-                      if (rev > range->start && rev <= range->end)
-                        {
-                          path_is_in_history = TRUE;
-                          break;
-                        }
-                    }
-                }
-              if (path_is_in_history)
-                break;
-            }
-          svn_pool_clear(iterpool);
-
-          if (!path_is_in_history)
-            {
-              /* If even one path in LOG_ENTRY->CHANGED_PATHS2 is not part of
-                 LOG_TARGET_HISTORY_AS_MERGEINFO, then we want to send the
-                 log for REV. */
-              found_rev_of_interest = TRUE;
-              break;
-            }
-        }
-      svn_pool_destroy(iterpool);
+      baton.found_rev_of_interest = FALSE;
+      baton.rev = rev;
+      baton.log_target_history_as_mergeinfo = log_target_history_as_mergeinfo;
+      baton.inner = callbacks->path_change_receiver;
+      baton.inner_baton = callbacks->path_change_receiver_baton;
+
+      my_callbacks.path_change_receiver = interesting_merge;
+      my_callbacks.path_change_receiver_baton = &baton;
+      callbacks = &my_callbacks;
+    }
+  else
+    {
+      baton.found_rev_of_interest = TRUE;
     }
 
-  /* If we only got changed paths the sake of detecting redundant merged
-     revisions, then be sure we don't send that info to the receiver. */
-  if (!discover_changed_paths && handling_merged_revision)
-    log_entry->changed_paths = log_entry->changed_paths2 = NULL;
+  SVN_ERR(fill_log_entry(&log_entry, rev, fs, revprops, callbacks, pool));
+  log_entry.has_children = has_children;
+  log_entry.subtractive_merge = subtractive_merge;
 
   /* Send the entry to the receiver, unless it is a redundant merged
      revision. */
-  if (found_rev_of_interest)
+  if (baton.found_rev_of_interest)
     {
       apr_pool_t *scratch_pool;
 
@@ -1353,7 +1322,8 @@ send_log(svn_revnum_t rev,
       /* Pass a scratch pool to ensure no temporary state stored
          by the receiver callback persists. */
       scratch_pool = svn_pool_create(pool);
-      SVN_ERR(receiver(receiver_baton, log_entry, scratch_pool));
+      SVN_ERR(callbacks->revision_receiver(callbacks->revision_receiver_baton,
+                                           &log_entry, scratch_pool));
       svn_pool_destroy(scratch_pool);
     }
 
@@ -1702,7 +1672,6 @@ do_logs(svn_fs_t *fs,
         svn_revnum_t hist_start,
         svn_revnum_t hist_end,
         int limit,
-        svn_boolean_t discover_changed_paths,
         svn_boolean_t strict_node_history,
         svn_boolean_t include_merged_revisions,
         svn_boolean_t handling_merged_revisions,
@@ -1710,10 +1679,7 @@ do_logs(svn_fs_t *fs,
         svn_boolean_t ignore_missing_locations,
         const apr_array_header_t *revprops,
         svn_boolean_t descending_order,
-        svn_log_entry_receiver_t receiver,
-        void *receiver_baton,
-        svn_repos_authz_func_t authz_read_func,
-        void *authz_read_baton,
+        log_callbacks_t *callbacks,
         apr_pool_t *pool);
 
 /* Comparator function for handle_merged_revisions().  Sorts path_list_range
@@ -1755,17 +1721,13 @@ handle_merged_revisions(svn_revnum_t rev
                         svn_mergeinfo_t processed,
                         svn_mergeinfo_t added_mergeinfo,
                         svn_mergeinfo_t deleted_mergeinfo,
-                        svn_boolean_t discover_changed_paths,
                         svn_boolean_t strict_node_history,
                         const apr_array_header_t *revprops,
-                        svn_log_entry_receiver_t receiver,
-                        void *receiver_baton,
-                        svn_repos_authz_func_t authz_read_func,
-                        void *authz_read_baton,
+                        log_callbacks_t *callbacks,
                         apr_pool_t *pool)
 {
   apr_array_header_t *combined_list = NULL;
-  svn_log_entry_t *empty_log_entry;
+  svn_repos_log_entry_t empty_log_entry = { 0 };
   apr_pool_t *iterpool;
   int i;
 
@@ -1796,17 +1758,16 @@ handle_merged_revisions(svn_revnum_t rev
       SVN_ERR(do_logs(fs, pl_range->paths, log_target_history_as_mergeinfo,
                       processed, nested_merges,
                       pl_range->range.start, pl_range->range.end, 0,
-                      discover_changed_paths, strict_node_history,
+                      strict_node_history,
                       TRUE, pl_range->reverse_merge, TRUE, TRUE,
-                      revprops, TRUE, receiver, receiver_baton,
-                      authz_read_func, authz_read_baton, iterpool));
+                      revprops, TRUE, callbacks, iterpool));
     }
   svn_pool_destroy(iterpool);
 
   /* Send the empty revision.  */
-  empty_log_entry = svn_log_entry_create(pool);
-  empty_log_entry->revision = SVN_INVALID_REVNUM;
-  return (*receiver)(receiver_baton, empty_log_entry, pool);
+  empty_log_entry.revision = SVN_INVALID_REVNUM;
+  return (callbacks->revision_receiver)(callbacks->revision_receiver_baton,
+                                        &empty_log_entry, pool);
 }
 
 /* This is used by do_logs to differentiate between forward and
@@ -1923,10 +1884,10 @@ store_search(svn_mergeinfo_t processed,
   return SVN_NO_ERROR;
 }
 
-/* Find logs for PATHS from HIST_START to HIST_END in FS, and invoke
-   RECEIVER with RECEIVER_BATON on them.  If DESCENDING_ORDER is TRUE, send
-   the logs back as we find them, else buffer the logs and send them back
-   in youngest->oldest order.
+/* Find logs for PATHS from HIST_START to HIST_END in FS, and invoke the
+   CALLBACKS on them.  If DESCENDING_ORDER is TRUE, send the logs back as
+   we find them, else buffer the logs and send them back in youngest->oldest
+   order.
 
    If IGNORE_MISSING_LOCATIONS is set, don't treat requests for bogus
    repository locations as fatal -- just ignore them.
@@ -1936,7 +1897,7 @@ store_search(svn_mergeinfo_t processed,
 
    If HANDLING_MERGED_REVISIONS is TRUE then this is a recursive call for
    merged revisions, see INCLUDE_MERGED_REVISIONS argument to
-   svn_repos_get_logs4().  If SUBTRACTIVE_MERGE is true, then this is a
+   svn_repos_get_logs5().  If SUBTRACTIVE_MERGE is true, then this is a
    recursive call for reverse merged revisions.
 
    If NESTED_MERGES is not NULL then it is a hash of revisions (svn_revnum_t *
@@ -1951,7 +1912,7 @@ store_search(svn_mergeinfo_t processed,
    revisions that have already been searched.  Allocated like
    NESTED_MERGES above.
 
-   All other parameters are the same as svn_repos_get_logs4().
+   All other parameters are the same as svn_repos_get_logs5().
  */
 static svn_error_t *
 do_logs(svn_fs_t *fs,
@@ -1962,7 +1923,6 @@ do_logs(svn_fs_t *fs,
         svn_revnum_t hist_start,
         svn_revnum_t hist_end,
         int limit,
-        svn_boolean_t discover_changed_paths,
         svn_boolean_t strict_node_history,
         svn_boolean_t include_merged_revisions,
         svn_boolean_t subtractive_merge,
@@ -1970,10 +1930,7 @@ do_logs(svn_fs_t *fs,
         svn_boolean_t ignore_missing_locations,
         const apr_array_header_t *revprops,
         svn_boolean_t descending_order,
-        svn_log_entry_receiver_t receiver,
-        void *receiver_baton,
-        svn_repos_authz_func_t authz_read_func,
-        void *authz_read_baton,
+        log_callbacks_t *callbacks,
         apr_pool_t *pool)
 {
   apr_pool_t *iterpool, *iterpool2;
@@ -2006,7 +1963,8 @@ do_logs(svn_fs_t *fs,
      revisions contain real changes to at least one of our paths.  */
   SVN_ERR(get_path_histories(&histories, fs, paths, hist_start, hist_end,
                              strict_node_history, ignore_missing_locations,
-                             authz_read_func, authz_read_baton, pool));
+                             callbacks->authz_read_func,
+                             callbacks->authz_read_baton, pool));
 
   /* Loop through all the revisions in the range and add any
      where a path was changed to the array, or if they wanted
@@ -2030,9 +1988,10 @@ do_logs(svn_fs_t *fs,
 
           /* Check history for this path in current rev. */
           SVN_ERR(check_history(&changed, info, fs, current,
-                                strict_node_history, authz_read_func,
-                                authz_read_baton, hist_start, pool,
-                                iterpool2));
+                                strict_node_history,
+                                callbacks->authz_read_func,
+                                callbacks->authz_read_baton,
+                                hist_start, pool, iterpool2));
           if (! info->done)
             any_histories_left = TRUE;
         }
@@ -2045,7 +2004,6 @@ do_logs(svn_fs_t *fs,
           svn_mergeinfo_t added_mergeinfo = NULL;
           svn_mergeinfo_t deleted_mergeinfo = NULL;
           svn_boolean_t has_children = FALSE;
-          apr_hash_t *changes = NULL;
 
           /* If we're including merged revisions, we need to calculate
              the mergeinfo deltas committed in this revision to our
@@ -2066,7 +2024,6 @@ do_logs(svn_fs_t *fs,
                 }
               SVN_ERR(get_combined_mergeinfo_changes(&added_mergeinfo,
                                                      &deleted_mergeinfo,
-                                                     &changes,
                                                      fs, cur_paths,
                                                      current,
                                                      iterpool, iterpool));
@@ -2079,13 +2036,10 @@ do_logs(svn_fs_t *fs,
              in anyway). */
           if (descending_order)
             {
-              SVN_ERR(send_log(current, fs, changes,
+              SVN_ERR(send_log(current, fs,
                                log_target_history_as_mergeinfo, nested_merges,
-                               discover_changed_paths,
                                subtractive_merge, handling_merged_revisions,
-                               revprops, has_children,
-                               receiver, receiver_baton,
-                               authz_read_func, authz_read_baton, iterpool));
+                               revprops, has_children, callbacks, iterpool));
 
               if (has_children) /* Implies include_merged_revisions == TRUE */
                 {
@@ -2104,12 +2058,9 @@ do_logs(svn_fs_t *fs,
                     log_target_history_as_mergeinfo, nested_merges,
                     processed,
                     added_mergeinfo, deleted_mergeinfo,
-                    discover_changed_paths,
                     strict_node_history,
                     revprops,
-                    receiver, receiver_baton,
-                    authz_read_func,
-                    authz_read_baton,
+                    callbacks,
                     iterpool));
                 }
               if (limit && ++send_count >= limit)
@@ -2128,7 +2079,7 @@ do_logs(svn_fs_t *fs,
               if (added_mergeinfo || deleted_mergeinfo)
                 {
                   svn_revnum_t *cur_rev =
-                    apr_pmemdup(pool, &current, sizeof(cur_rev));
+                    apr_pmemdup(pool, &current, sizeof(*cur_rev));
                   struct added_deleted_mergeinfo *add_and_del_mergeinfo =
                     apr_palloc(pool, sizeof(*add_and_del_mergeinfo));
 
@@ -2184,13 +2135,10 @@ do_logs(svn_fs_t *fs,
                               || apr_hash_count(deleted_mergeinfo) > 0);
             }
 
-          SVN_ERR(send_log(current, fs, NULL,
+          SVN_ERR(send_log(current, fs,
                            log_target_history_as_mergeinfo, nested_merges,
-                           discover_changed_paths, subtractive_merge,
-                           handling_merged_revisions,
-                           revprops, has_children,
-                           receiver, receiver_baton, authz_read_func,
-                           authz_read_baton, iterpool));
+                           subtractive_merge, handling_merged_revisions,
+                           revprops, has_children, callbacks, iterpool));
           if (has_children)
             {
               if (!nested_merges)
@@ -2205,12 +2153,8 @@ do_logs(svn_fs_t *fs,
                                               processed,
                                               added_mergeinfo,
                                               deleted_mergeinfo,
-                                              discover_changed_paths,
                                               strict_node_history,
-                                              revprops,
-                                              receiver, receiver_baton,
-                                              authz_read_func,
-                                              authz_read_baton,
+                                              revprops, callbacks,
                                               iterpool));
             }
           if (limit && i + 1 >= limit)
@@ -2228,7 +2172,7 @@ struct location_segment_baton
   apr_pool_t *pool;
 };
 
-/* svn_location_segment_receiver_t implementation for svn_repos_get_logs4. */
+/* svn_location_segment_receiver_t implementation for svn_repos_get_logs5. */
 static svn_error_t *
 location_segment_receiver(svn_location_segment_t *segment,
                           void *baton,
@@ -2248,7 +2192,7 @@ location_segment_receiver(svn_location_s
    filesystem.  START_REV and END_REV must be valid revisions.  RESULT_POOL
    is used to allocate *PATHS_HISTORY_MERGEINFO, SCRATCH_POOL is used for all
    other (temporary) allocations.  Other parameters are the same as
-   svn_repos_get_logs4(). */
+   svn_repos_get_logs5(). */
 static svn_error_t *
 get_paths_history_as_mergeinfo(svn_mergeinfo_t *paths_history_mergeinfo,
                                svn_repos_t *repos,
@@ -2309,45 +2253,56 @@ get_paths_history_as_mergeinfo(svn_merge
 }
 
 svn_error_t *
-svn_repos_get_logs4(svn_repos_t *repos,
+svn_repos_get_logs5(svn_repos_t *repos,
                     const apr_array_header_t *paths,
                     svn_revnum_t start,
                     svn_revnum_t end,
                     int limit,
-                    svn_boolean_t discover_changed_paths,
                     svn_boolean_t strict_node_history,
                     svn_boolean_t include_merged_revisions,
                     const apr_array_header_t *revprops,
                     svn_repos_authz_func_t authz_read_func,
                     void *authz_read_baton,
-                    svn_log_entry_receiver_t receiver,
-                    void *receiver_baton,
-                    apr_pool_t *pool)
+                    svn_repos_path_change_receiver_t path_change_receiver,
+                    void *path_change_receiver_baton,
+                    svn_repos_log_entry_receiver_t revision_receiver,
+                    void *revision_receiver_baton,
+                    apr_pool_t *scratch_pool)
 {
   svn_revnum_t head = SVN_INVALID_REVNUM;
   svn_fs_t *fs = repos->fs;
   svn_boolean_t descending_order;
   svn_mergeinfo_t paths_history_mergeinfo = NULL;
+  log_callbacks_t callbacks;
+
+  callbacks.path_change_receiver = path_change_receiver;
+  callbacks.path_change_receiver_baton = path_change_receiver_baton;
+  callbacks.revision_receiver = revision_receiver;
+  callbacks.revision_receiver_baton = revision_receiver_baton;
+  callbacks.authz_read_func = authz_read_func;
+  callbacks.authz_read_baton = authz_read_baton;
 
   if (revprops)
     {
       int i;
       apr_array_header_t *new_revprops
-        = apr_array_make(pool, revprops->nelts, sizeof(svn_string_t *));
+        = apr_array_make(scratch_pool, revprops->nelts,
+                         sizeof(svn_string_t *));
 
       for (i = 0; i < revprops->nelts; ++i)
         APR_ARRAY_PUSH(new_revprops, svn_string_t *)
-          = svn_string_create(APR_ARRAY_IDX(revprops, i, const char *), pool);
+          = svn_string_create(APR_ARRAY_IDX(revprops, i, const char *),
+                              scratch_pool);
 
       revprops = new_revprops;
     }
 
   /* Make sure we catch up on the latest revprop changes.  This is the only
    * time we will refresh the revprop data in this query. */
-  SVN_ERR(svn_fs_refresh_revision_props(fs, pool));
+  SVN_ERR(svn_fs_refresh_revision_props(fs, scratch_pool));
 
   /* Setup log range. */
-  SVN_ERR(svn_fs_youngest_rev(&head, fs, pool));
+  SVN_ERR(svn_fs_youngest_rev(&head, fs, scratch_pool));
 
   if (! SVN_IS_VALID_REVNUM(start))
     start = head;
@@ -2376,7 +2331,7 @@ svn_repos_get_logs4(svn_repos_t *repos,
     }
 
   if (! paths)
-    paths = apr_array_make(pool, 0, sizeof(const char *));
+    paths = apr_array_make(scratch_pool, 0, sizeof(const char *));
 
   /* If we're not including merged revisions, and we were given no
      paths or a single empty (or "/") path, then we can bypass a bunch
@@ -2391,7 +2346,7 @@ svn_repos_get_logs4(svn_repos_t *repos,
     {
       apr_uint64_t send_count = 0;
       int i;
-      apr_pool_t *iterpool = svn_pool_create(pool);
+      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
       /* If we are provided an authz callback function, use it to
          verify that the user has read access to the root path in the
@@ -2407,9 +2362,10 @@ svn_repos_get_logs4(svn_repos_t *repos,
           svn_fs_root_t *rev_root;
 
           SVN_ERR(svn_fs_revision_root(&rev_root, fs,
-                                       descending_order ? end : start, pool));
+                                       descending_order ? end : start,
+                                       scratch_pool));
           SVN_ERR(authz_read_func(&readable, rev_root, "",
-                                  authz_read_baton, pool));
+                                  authz_read_baton, scratch_pool));
           if (! readable)
             return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE, NULL, NULL);
         }
@@ -2427,10 +2383,9 @@ svn_repos_get_logs4(svn_repos_t *repos,
             rev = end - i;
           else
             rev = start + i;
-          SVN_ERR(send_log(rev, fs, NULL, NULL, NULL,
-                           discover_changed_paths, FALSE,
-                           FALSE, revprops, FALSE, receiver, receiver_baton,
-                           authz_read_func, authz_read_baton, iterpool));
+          SVN_ERR(send_log(rev, fs, NULL, NULL,
+                           FALSE, FALSE, revprops, FALSE,
+                           &callbacks, iterpool));
         }
       svn_pool_destroy(iterpool);
 
@@ -2444,19 +2399,18 @@ svn_repos_get_logs4(svn_repos_t *repos,
      http://subversion.tigris.org/issues/show_bug.cgi?id=3650#desc5 */
   if (include_merged_revisions)
     {
-      apr_pool_t *subpool = svn_pool_create(pool);
+      apr_pool_t *subpool = svn_pool_create(scratch_pool);
 
       SVN_ERR(get_paths_history_as_mergeinfo(&paths_history_mergeinfo,
                                              repos, paths, start, end,
                                              authz_read_func,
                                              authz_read_baton,
-                                             pool, subpool));
+                                             scratch_pool, subpool));
       svn_pool_destroy(subpool);
     }
 
-  return do_logs(repos->fs, paths, paths_history_mergeinfo, NULL, NULL, start, end,
-                 limit, discover_changed_paths, strict_node_history,
+  return do_logs(repos->fs, paths, paths_history_mergeinfo, NULL, NULL,
+                 start, end, limit, strict_node_history,
                  include_merged_revisions, FALSE, FALSE, FALSE,
-                 revprops, descending_order, receiver, receiver_baton,
-                 authz_read_func, authz_read_baton, pool);
+                 revprops, descending_order, &callbacks, scratch_pool);
 }