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 2018/11/07 12:30:11 UTC

svn commit: r1846002 [12/44] - in /subversion/branches/ra-git: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ build/generator/util/ build/win32/ contrib/client-side/ contrib/client-side/svn_load_dirs/ contr...

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/batch_fsync.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/batch_fsync.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/batch_fsync.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/batch_fsync.c Wed Nov  7 12:30:06 2018
@@ -29,6 +29,7 @@
 #include "svn_dirent_uri.h"
 #include "svn_private_config.h"
 
+#include "private/svn_atomic.h"
 #include "private/svn_dep_compat.h"
 #include "private/svn_mutex.h"
 #include "private/svn_subr_private.h"
@@ -230,28 +231,38 @@ static apr_thread_pool_t *thread_pool =
 
 #endif
 
+/* Keep track on whether we already created the THREAD_POOL . */
+static svn_atomic_t thread_pool_initialized = FALSE;
+
 /* We open non-directory files with these flags. */
 #define FILE_FLAGS (APR_READ | APR_WRITE | APR_BUFFERED | APR_CREATE)
 
 #if APR_HAS_THREADS
 
 /* Destructor function that implicitly cleans up any running threads
-   in the thread_pool given as DATA and releases their memory pools
-   before they get destroyed themselves.
+   in the TRHEAD_POOL *once*.
 
    Must be run as a pre-cleanup hook.
  */
 static apr_status_t
 thread_pool_pre_cleanup(void *data)
 {
-  apr_thread_pool_t *tp = data;
+  apr_thread_pool_t *tp = thread_pool;
+  if (!thread_pool)
+    return APR_SUCCESS;
+
+  thread_pool = NULL;
+  thread_pool_initialized = FALSE;
+
   return apr_thread_pool_destroy(tp);
 }
 
 #endif
 
-svn_error_t *
-svn_fs_x__batch_fsync_init(void)
+/* Core implementation of svn_fs_x__batch_fsync_init. */
+static svn_error_t *
+create_thread_pool(void *baton,
+                   apr_pool_t *owning_pool)
 {
 #if APR_HAS_THREADS
   /* The thread-pool must be allocated from a thread-safe pool.
@@ -266,7 +277,8 @@ svn_fs_x__batch_fsync_init(void)
   /* Work around an APR bug:  The cleanup must happen in the pre-cleanup
      hook instead of the normal cleanup hook.  Otherwise, the sub-pools
      containing the thread objects would already be invalid. */
-  apr_pool_pre_cleanup_register(pool, thread_pool, thread_pool_pre_cleanup);
+  apr_pool_pre_cleanup_register(pool, NULL, thread_pool_pre_cleanup);
+  apr_pool_pre_cleanup_register(owning_pool, NULL, thread_pool_pre_cleanup);
 
   /* let idle threads linger for a while in case more requests are
      coming in */
@@ -280,6 +292,15 @@ svn_fs_x__batch_fsync_init(void)
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_fs_x__batch_fsync_init(apr_pool_t *owning_pool)
+{
+  /* Protect against multiple calls. */
+  return svn_error_trace(svn_atomic__init_once(&thread_pool_initialized,
+                                               create_thread_pool,
+                                               NULL, owning_pool));
+}
+
 /* Destructor for svn_fs_x__batch_fsync_t.  Releases all global pool memory
  * and closes all open file handles. */
 static apr_status_t
@@ -510,6 +531,10 @@ svn_fs_x__batch_fsync_run(svn_fs_x__batc
 
 #if APR_HAS_THREADS
 
+          /* Forgot to call _init() or cleaned up the owning pool too early?
+           */
+          SVN_ERR_ASSERT(thread_pool);
+
           /* If there are multiple fsyncs to perform, run them in parallel.
            * Otherwise, skip the thread-pool and synchronization overhead. */
           if (apr_hash_count(batch->files) > 1)

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/batch_fsync.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/batch_fsync.h?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/batch_fsync.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/batch_fsync.h Wed Nov  7 12:30:06 2018
@@ -44,13 +44,14 @@
  */
 typedef struct svn_fs_x__batch_fsync_t svn_fs_x__batch_fsync_t;
 
-/* Initialize the concurrent fsync infrastructure.
+/* Initialize the concurrent fsync infrastructure.  Clean it up when
+ * OWNING_POOL gets cleared.
  *
  * This function must be called before using any of the other functions in
  * in this module.  It should only be called once.
  */
 svn_error_t *
-svn_fs_x__batch_fsync_init(void);
+svn_fs_x__batch_fsync_init(apr_pool_t *owning_pool);
 
 /* Set *RESULT_P to a new batch fsync structure, allocated in RESULT_POOL.
  * If FLUSH_TO_DISK is not set, the resulting struct will not actually use

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/cached_data.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/cached_data.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/cached_data.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/cached_data.c Wed Nov  7 12:30:06 2018
@@ -69,7 +69,7 @@ block_read(void **result,
  * contents if not NULL.  Use SCRATCH_POOL for temporary allocations.
  */
 static svn_error_t *
-dgb__log_access(svn_fs_t *fs,
+dbg__log_access(svn_fs_t *fs,
                 const svn_fs_x__id_t *id,
                 void *item,
                 apr_uint32_t item_type,
@@ -379,7 +379,7 @@ svn_fs_x__get_node_revision(svn_fs_x__no
                                id_string->data);
     }
 
-  SVN_ERR(dgb__log_access(fs, id, *noderev_p,
+  SVN_ERR(dbg__log_access(fs, id, *noderev_p,
                           SVN_FS_X__ITEM_TYPE_NODEREV, scratch_pool));
 
   return svn_error_trace(err);
@@ -708,7 +708,7 @@ create_rep_state_body(rep_state_t **rep_
     }
 
   /* finalize */
-  SVN_ERR(dgb__log_access(fs, &rs->rep_id, rh, SVN_FS_X__ITEM_TYPE_ANY_REP,
+  SVN_ERR(dbg__log_access(fs, &rs->rep_id, rh, SVN_FS_X__ITEM_TYPE_ANY_REP,
                           scratch_pool));
 
   rs->header_size = rh->header_size;
@@ -1187,7 +1187,8 @@ build_rep_list(apr_array_header_t **list
       /* for txn reps and containered reps, there won't be a cached
        * combined window */
       if (svn_fs_x__is_revision(rep.id.change_set)
-          && rep_header->type != svn_fs_x__rep_container)
+          && rep_header->type != svn_fs_x__rep_container
+          && rs->combined_cache)
         SVN_ERR(get_cached_combined_window(window_p, rs, &is_cached,
                                            result_pool));
 
@@ -1289,17 +1290,23 @@ read_delta_window(svn_txdelta_window_t *
   apr_pool_t *iterpool;
   svn_stream_t *stream;
   svn_fs_x__revision_file_t *file;
+  svn_boolean_t cacheable = rs->chunk_index == 0
+                         && svn_fs_x__is_revision(rs->rep_id.change_set)
+                         && rs->window_cache;
 
   SVN_ERR_ASSERT(rs->chunk_index <= this_chunk);
 
-  SVN_ERR(dgb__log_access(rs->sfile->fs, &rs->rep_id, NULL,
+  SVN_ERR(dbg__log_access(rs->sfile->fs, &rs->rep_id, NULL,
                           SVN_FS_X__ITEM_TYPE_ANY_REP, scratch_pool));
 
   /* Read the next window.  But first, try to find it in the cache. */
-  SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached,
-                            result_pool, scratch_pool));
-  if (is_cached)
-    return SVN_NO_ERROR;
+  if (cacheable)
+    {
+      SVN_ERR(get_cached_window(nwin, rs, this_chunk, &is_cached,
+                                result_pool, scratch_pool));
+      if (is_cached)
+        return SVN_NO_ERROR;
+    }
 
   /* someone has to actually read the data from file.  Open it */
   SVN_ERR(auto_open_shared_file(rs->sfile));
@@ -1308,9 +1315,7 @@ read_delta_window(svn_txdelta_window_t *
   /* invoke the 'block-read' feature for non-txn data.
      However, don't do that if we are in the middle of some representation,
      because the block is unlikely to contain other data. */
-  if (   rs->chunk_index == 0
-      && svn_fs_x__is_revision(rs->rep_id.change_set)
-      && rs->window_cache)
+  if (cacheable)
     {
       SVN_ERR(block_read(NULL, rs->sfile->fs, &rs->rep_id, file, NULL,
                          result_pool, scratch_pool));
@@ -1367,7 +1372,7 @@ read_delta_window(svn_txdelta_window_t *
 
   /* the window has not been cached before, thus cache it now
    * (if caching is used for them at all) */
-  if (svn_fs_x__is_revision(rs->rep_id.change_set))
+  if (cacheable)
     SVN_ERR(set_cached_window(*nwin, rs, start_offset, scratch_pool));
 
   return SVN_NO_ERROR;
@@ -1495,7 +1500,8 @@ get_combined_window(svn_stringbuf_t **re
          single chunk.  Only then will no other chunk need a deeper RS
          list than the cached chunk. */
       if (   (rb->chunk_index == 0) && (rs->current == rs->size)
-          && svn_fs_x__is_revision(rs->rep_id.change_set))
+          && svn_fs_x__is_revision(rs->rep_id.change_set)
+          && rs->combined_cache)
         SVN_ERR(set_cached_combined_window(buf, rs, new_pool));
 
       rs->chunk_index++;
@@ -1974,6 +1980,16 @@ skip_contents(rep_read_baton_t *baton,
           len -= to_read;
           buffer += to_read;
         }
+
+      /* Make the MD5 calculation catch up with the data delivered
+       * (we did not run MD5 on the data that we took from the cache). */
+      if (!err)
+        {
+          SVN_ERR(svn_checksum_update(baton->md5_checksum_ctx,
+                                      baton->current_fulltext->data,
+                                      baton->current_fulltext->len));
+          baton->off += baton->current_fulltext->len;
+        }
     }
   else if (len > 0)
     {
@@ -1989,6 +2005,15 @@ skip_contents(rep_read_baton_t *baton,
 
           err = get_contents_from_windows(baton, buffer, &to_read);
           len -= to_read;
+
+          /* Make the MD5 calculation catch up with the data delivered
+           * (we did not run MD5 on the data that we took from the cache). */
+          if (!err)
+            {
+              SVN_ERR(svn_checksum_update(baton->md5_checksum_ctx,
+                                          buffer, to_read));
+              baton->off += to_read;
+            }
         }
 
       svn_pool_destroy(subpool);
@@ -2034,8 +2059,13 @@ rep_read_contents(void *baton,
       SVN_ERR(skip_contents(rb, rb->fulltext_delivered));
     }
 
-  /* Get the next block of data. */
-  SVN_ERR(get_contents_from_windows(rb, buf, len));
+  /* Get the next block of data.
+   * Keep in mind that the representation might be empty and leave us
+   * already positioned at the end of the rep. */
+  if (rb->off == rb->len)
+    *len = 0;
+  else
+    SVN_ERR(get_contents_from_windows(rb, buf, len));
 
   if (rb->current_fulltext)
     svn_stringbuf_appendbytes(rb->current_fulltext, buf, *len);
@@ -2130,6 +2160,86 @@ svn_fs_x__get_contents(svn_stream_t **co
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_fs_x__get_contents_from_file(svn_stream_t **contents_p,
+                                 svn_fs_t *fs,
+                                 svn_fs_x__representation_t *rep,
+                                 apr_file_t *file,
+                                 apr_off_t offset,
+                                 apr_pool_t *pool)
+{
+  rep_read_baton_t *rb;
+  svn_fs_x__pair_cache_key_t fulltext_cache_key = { SVN_INVALID_REVNUM, 0 };
+  rep_state_t *rs = apr_pcalloc(pool, sizeof(*rs));
+  svn_fs_x__rep_header_t *rh;
+  svn_stream_t *stream;
+
+  /* Initialize the reader baton.  Some members may added lazily
+   * while reading from the stream. */
+  SVN_ERR(rep_read_get_baton(&rb, fs, rep, fulltext_cache_key, pool));
+
+  /* Continue constructing RS. Leave caches as NULL. */
+  rs->size = rep->size;
+  rs->rep_id = rep->id;
+  rs->ver = -1;
+  rs->start = -1;
+
+  /* Provide just enough file access info to allow for a basic read from
+   * FILE but leave all index / footer info with empty values b/c FILE
+   * probably is not a complete revision file. */
+  rs->sfile = apr_pcalloc(pool, sizeof(*rs->sfile));
+  rs->sfile->revision = SVN_INVALID_REVNUM;
+  rs->sfile->pool = pool;
+  rs->sfile->fs = fs;
+  SVN_ERR(svn_fs_x__rev_file_wrap_temp(&rs->sfile->rfile, fs, file, pool));
+
+  /* Read the rep header. */
+  SVN_ERR(svn_fs_x__rev_file_seek(rs->sfile->rfile, NULL, offset));
+  SVN_ERR(svn_fs_x__rev_file_stream(&stream, rs->sfile->rfile));
+  SVN_ERR(svn_fs_x__read_rep_header(&rh, stream, pool, pool));
+  SVN_ERR(svn_fs_x__rev_file_offset(&rs->start, rs->sfile->rfile));
+  rs->header_size = rh->header_size;
+
+  /* Log the access. */
+  SVN_ERR(dbg__log_access(fs, &rep->id, rh,
+                          SVN_FS_X__ITEM_TYPE_ANY_REP, pool));
+
+  /* Build the representation list (delta chain). */
+  if (rh->type == svn_fs_x__rep_self_delta)
+    {
+      rb->rs_list = apr_array_make(pool, 1, sizeof(rep_state_t *));
+      APR_ARRAY_PUSH(rb->rs_list, rep_state_t *) = rs;
+      rb->src_state = NULL;
+    }
+  else
+    {
+      svn_fs_x__representation_t next_rep = { 0 };
+
+      /* skip "SVNx" diff marker */
+      rs->current = 4;
+
+      /* REP's base rep is inside a proper revision.
+       * It can be reconstructed in the usual way.  */
+      next_rep.id.change_set = svn_fs_x__change_set_by_rev(rh->base_revision);
+      next_rep.id.number = rh->base_item_index;
+      next_rep.size = rh->base_length;
+
+      SVN_ERR(build_rep_list(&rb->rs_list, &rb->base_window,
+                             &rb->src_state, rb->fs, &next_rep,
+                             rb->filehandle_pool, rb->scratch_pool));
+
+      /* Insert the access to REP as the first element of the delta chain. */
+      svn_sort__array_insert(rb->rs_list, &rs, 0);
+    }
+
+  /* Now, the baton is complete and we can assemble the stream around it. */
+  *contents_p = svn_stream_create(rb, pool);
+  svn_stream_set_read2(*contents_p, NULL /* only full read support */,
+                       rep_read_contents);
+  svn_stream_set_close(*contents_p, rep_read_contents_close);
+
+  return SVN_NO_ERROR;
+}
 
 /* Baton for cache_access_wrapper. Wraps the original parameters of
  * svn_fs_x__try_process_file_content().
@@ -2893,7 +3003,7 @@ svn_fs_x__get_changes(apr_array_header_t
 
   context->next += (*changes)->nelts;
 
-  SVN_ERR(dgb__log_access(context->fs, &id, *changes,
+  SVN_ERR(dbg__log_access(context->fs, &id, *changes,
                           SVN_FS_X__ITEM_TYPE_CHANGES, scratch_pool));
 
   return SVN_NO_ERROR;
@@ -2972,8 +3082,8 @@ read_item(svn_stream_t **stream,
   return svn_checksum_mismatch_err(expected, actual, result_pool,
                  _("Low-level checksum mismatch while reading\n"
                    "%s bytes of meta data at offset %s "),
-                 apr_psprintf(result_pool, "%" APR_OFF_T_FMT, entry->size),
-                 apr_psprintf(result_pool, "%" APR_OFF_T_FMT, entry->offset));
+                 apr_off_t_toa(result_pool, entry->size),
+                 apr_off_t_toa(result_pool, entry->offset));
 }
 
 /* If not already cached or if MUST_READ is set, read the changed paths

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/cached_data.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/cached_data.h?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/cached_data.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/cached_data.h Wed Nov  7 12:30:06 2018
@@ -67,7 +67,7 @@ svn_fs_x__rep_chain_length(int *chain_le
                            svn_fs_t *fs,
                            apr_pool_t *scratch_pool);
 
-/* Set *CONTENTS to be a readable svn_stream_t that receives the text
+/* Set *CONTENTS_P to be a readable svn_stream_t that receives the text
    representation REP as seen in filesystem FS.  If CACHE_FULLTEXT is
    not set, bypass fulltext cache lookup for this rep and don't put the
    reconstructed fulltext into cache.
@@ -79,6 +79,18 @@ svn_fs_x__get_contents(svn_stream_t **co
                        svn_boolean_t cache_fulltext,
                        apr_pool_t *result_pool);
 
+/* Set *CONTENTS_P to be a readable svn_stream_t that receives the text
+   representation REP as seen in filesystem FS.  Read the latest element
+   of the delta chain from FILE at offset OFFSET.
+   Use POOL for allocations. */
+svn_error_t *
+svn_fs_x__get_contents_from_file(svn_stream_t **contents_p,
+                                 svn_fs_t *fs,
+                                 svn_fs_x__representation_t *rep,
+                                 apr_file_t *file,
+                                 apr_off_t offset,
+                                 apr_pool_t *pool);
+
 /* Determine on-disk and expanded sizes of the representation identified
  * by ENTRY in FS and return the result in PACKED_LEN and EXPANDED_LEN,
  * respectively.  FILE must point to the start of the representation and

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/caching.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/caching.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/caching.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/caching.c Wed Nov  7 12:30:06 2018
@@ -412,7 +412,7 @@ svn_fs_x__initialize_caches(svn_fs_t *fs
   /* General rules for assigning cache priorities:
    *
    * - Data that can be reconstructed from other elements has low prio
-   *   (e.g. fulltexts, directories etc.)
+   *   (e.g. fulltexts etc.)
    * - Index data required to find any of the other data has high prio
    *   (e.g. noderevs, L2P and P2L index pages)
    * - everthing else should use default prio

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/changes.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/changes.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/changes.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/changes.c Wed Nov  7 12:30:06 2018
@@ -184,7 +184,7 @@ svn_fs_x__changes_append_list(apr_size_t
 
   /* simply append the list and all changes */
   for (i = 0; i < list->nelts; ++i)
-    append_change(changes, APR_ARRAY_IDX(list, i, svn_fs_x__change_t *));
+    SVN_ERR(append_change(changes, APR_ARRAY_IDX(list, i, svn_fs_x__change_t *)));
 
   /* terminate the list by storing the next changes offset */
   APR_ARRAY_PUSH(changes->offsets, int) = changes->changes->nelts;

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/dag_cache.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/dag_cache.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/dag_cache.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/dag_cache.c Wed Nov  7 12:30:06 2018
@@ -890,6 +890,16 @@ svn_fs_x__get_dag_path(svn_fs_x__dag_pat
     {
       svn_pool_clear(iterpool);
 
+      /* If the current node is not a directory and we are just here to
+       * check for the path's existence, then that's o.k.
+       * Otherwise, non-dir nodes will cause an error in dag_step. */
+      if (   (flags & svn_fs_x__dag_path_allow_null)
+          && (svn_fs_x__dag_node_kind(dag_path->node) != svn_node_dir))
+        {
+          dag_path = NULL;
+          break;
+        }
+
       /* Find the sub-node. */
       SVN_ERR(dag_step(&here, root, dag_path->node, entry, &path, change_set,
                        TRUE, iterpool));

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/fs.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/fs.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/fs.c Wed Nov  7 12:30:06 2018
@@ -508,7 +508,7 @@ x_pack(svn_fs_t *fs,
        apr_pool_t *common_pool)
 {
   SVN_ERR(x_open(fs, path, common_pool_lock, scratch_pool, common_pool));
-  return svn_fs_x__pack(fs, notify_func, notify_baton,
+  return svn_fs_x__pack(fs, 0, notify_func, notify_baton,
                         cancel_func, cancel_baton, scratch_pool);
 }
 
@@ -665,7 +665,7 @@ svn_fs_x__init(const svn_version_t *load
                              loader_version->major);
   SVN_ERR(svn_ver_check_list2(x_version(), checklist, svn_ver_equal));
 
-  SVN_ERR(svn_fs_x__batch_fsync_init());
+  SVN_ERR(svn_fs_x__batch_fsync_init(common_pool));
 
   *vtable = &library_vtable;
   return SVN_NO_ERROR;

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/low_level.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/low_level.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/low_level.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/low_level.c Wed Nov  7 12:30:06 2018
@@ -1131,7 +1131,7 @@ svn_fs_x__write_changes(svn_stream_t *st
     }
 
   if (terminate_list)
-    svn_stream_puts(stream, "\n");
+    SVN_ERR(svn_stream_puts(stream, "\n"));
 
   svn_pool_destroy(iterpool);
 

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/pack.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/pack.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/pack.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/pack.c Wed Nov  7 12:30:06 2018
@@ -216,7 +216,7 @@ typedef struct pack_context_t
    * to NULL that we already processed. */
   apr_array_header_t *reps;
 
-  /* array of int, marking for each revision, the which offset their items
+  /* array of int, marking for each revision, at which offset their items
    * begin in REPS.  Will be filled in phase 2 and be cleared after
    * each revision range. */
   apr_array_header_t *rev_offsets;
@@ -344,6 +344,7 @@ reset_pack_context(pack_context_t *conte
   SVN_ERR(svn_io_file_trunc(context->reps_file, 0, scratch_pool));
 
   svn_pool_clear(context->info_pool);
+  context->paths = svn_prefix_tree__create(context->info_pool);
 
   return SVN_NO_ERROR;
 }
@@ -1839,15 +1840,17 @@ append_revision(pack_context_t *context,
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
   svn_fs_x__revision_file_t *rev_file;
   apr_file_t *file;
-  svn_filesize_t revfile_size;
+  svn_filesize_t revdata_size;
 
-  /* Copy all the bits from the rev file to the end of the pack file. */
+  /* Copy all non-index contents the rev file to the end of the pack file. */
   SVN_ERR(svn_fs_x__rev_file_init(&rev_file, context->fs, context->start_rev,
                                   scratch_pool));
-  SVN_ERR(svn_fs_x__rev_file_get(&file, rev_file));
+  SVN_ERR(svn_fs_x__rev_file_data_size(&revdata_size, rev_file));
 
-  SVN_ERR(svn_io_file_size_get(&revfile_size, file, scratch_pool));
-  SVN_ERR(copy_file_data(context, context->pack_file, file, revfile_size,
+  SVN_ERR(svn_fs_x__rev_file_get(&file, rev_file));
+  SVN_ERR(svn_io_file_aligned_seek(file, ffd->block_size, NULL, 0,
+                                   iterpool));
+  SVN_ERR(copy_file_data(context, context->pack_file, file, revdata_size,
                          iterpool));
 
   /* mark the start of a new revision */
@@ -1856,7 +1859,7 @@ append_revision(pack_context_t *context,
 
   /* read the phys-to-log index file until we covered the whole rev file.
    * That index contains enough info to build both target indexes from it. */
-  while (offset < revfile_size)
+  while (offset < revdata_size)
     {
       /* read one cluster */
       int i;
@@ -1878,7 +1881,7 @@ append_revision(pack_context_t *context,
 
           /* process entry while inside the rev file */
           offset = entry->offset;
-          if (offset < revfile_size)
+          if (offset < revdata_size)
             {
               /* there should be true containers */
               SVN_ERR_ASSERT(entry->item_count == 1);
@@ -1897,7 +1900,7 @@ append_revision(pack_context_t *context,
     }
 
   svn_pool_destroy(iterpool);
-  context->pack_offset += revfile_size;
+  context->pack_offset += revdata_size;
 
   return SVN_NO_ERROR;
 }
@@ -1956,6 +1959,7 @@ pack_log_addressed(svn_fs_t *fs,
     if (   APR_ARRAY_IDX(max_ids, i, apr_uint64_t)
         <= (apr_uint64_t)max_items - item_count)
       {
+        item_count += APR_ARRAY_IDX(max_ids, i, apr_uint64_t);
         context.end_rev++;
       }
     else
@@ -2048,6 +2052,7 @@ pack_rev_shard(svn_fs_t *fs,
 /* In the file system at FS_PATH, pack the SHARD in DIR containing exactly
  * MAX_FILES_PER_DIR revisions, using SCRATCH_POOL temporary for allocations.
  * COMPRESSION_LEVEL and MAX_PACK_SIZE will be ignored in that case.
+ * An attempt will be made to keep memory usage below MAX_MEM.
  *
  * CANCEL_FUNC and CANCEL_BATON are what you think they are; similarly
  * NOTIFY_FUNC and NOTIFY_BATON.
@@ -2062,6 +2067,7 @@ pack_shard(const char *dir,
            int max_files_per_dir,
            apr_off_t max_pack_size,
            int compression_level,
+           apr_size_t max_mem,
            svn_fs_pack_notify_t notify_func,
            void *notify_baton,
            svn_cancel_func_t cancel_func,
@@ -2093,7 +2099,7 @@ pack_shard(const char *dir,
 
   /* pack the revision content */
   SVN_ERR(pack_rev_shard(fs, pack_file_dir, shard_path,
-                         shard, max_files_per_dir, DEFAULT_MAX_MEM, batch,
+                         shard, max_files_per_dir, max_mem, batch,
                          cancel_func, cancel_baton, scratch_pool));
 
   /* pack the revprops in an equivalent way */
@@ -2158,6 +2164,7 @@ get_pack_status(svn_boolean_t *fully_pac
 typedef struct pack_baton_t
 {
   svn_fs_t *fs;
+  apr_size_t max_mem;
   svn_fs_pack_notify_t notify_func;
   void *notify_baton;
   svn_cancel_func_t cancel_func;
@@ -2197,9 +2204,9 @@ pack_body(void *baton,
   if (fully_packed)
     {
       if (pb->notify_func)
-        (*pb->notify_func)(pb->notify_baton,
-                           ffd->min_unpacked_rev / ffd->max_files_per_dir,
-                           svn_fs_pack_notify_noop, scratch_pool);
+        SVN_ERR(pb->notify_func(pb->notify_baton,
+                                ffd->min_unpacked_rev / ffd->max_files_per_dir,
+                                svn_fs_pack_notify_noop, scratch_pool));
 
       return SVN_NO_ERROR;
     }
@@ -2223,6 +2230,7 @@ pack_body(void *baton,
                          ffd->compress_packed_revprops
                            ? SVN__COMPRESSION_ZLIB_DEFAULT
                            : SVN__COMPRESSION_NONE,
+                         pb->max_mem,
                          pb->notify_func, pb->notify_baton,
                          pb->cancel_func, pb->cancel_baton, iterpool));
     }
@@ -2233,6 +2241,7 @@ pack_body(void *baton,
 
 svn_error_t *
 svn_fs_x__pack(svn_fs_t *fs,
+               apr_size_t max_mem,
                svn_fs_pack_notify_t notify_func,
                void *notify_baton,
                svn_cancel_func_t cancel_func,
@@ -2249,9 +2258,9 @@ svn_fs_x__pack(svn_fs_t *fs,
       svn_fs_x__data_t *ffd = fs->fsap_data;
 
       if (notify_func)
-        (*notify_func)(notify_baton,
-                       ffd->min_unpacked_rev / ffd->max_files_per_dir,
-                       svn_fs_pack_notify_noop, scratch_pool);
+        SVN_ERR(notify_func(notify_baton,
+                            ffd->min_unpacked_rev / ffd->max_files_per_dir,
+                            svn_fs_pack_notify_noop, scratch_pool));
 
       return SVN_NO_ERROR;
     }
@@ -2262,5 +2271,7 @@ svn_fs_x__pack(svn_fs_t *fs,
   pb.notify_baton = notify_baton;
   pb.cancel_func = cancel_func;
   pb.cancel_baton = cancel_baton;
+  pb.max_mem = max_mem ? max_mem : DEFAULT_MAX_MEM;
+
   return svn_fs_x__with_pack_lock(fs, pack_body, &pb, scratch_pool);
 }

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/pack.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/pack.h?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/pack.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/pack.h Wed Nov  7 12:30:06 2018
@@ -26,13 +26,19 @@
 #include "fs.h"
 
 /* Possibly pack the repository at PATH.  This just take full shards, and
-   combines all the revision files into a single one, with a manifest header.
+   combines all the revision files into a single one, with a manifest header
+   when required by the repository format.
+
+   MAX_MEM limits the size of in-memory data structures needed for reordering
+   items.  0 means use the built-in default.
+
    Use optional CANCEL_FUNC/CANCEL_BATON for cancellation support.
    Use SCRATCH_POOL for temporary allocations.
 
    Existing filesystem references need not change.  */
 svn_error_t *
 svn_fs_x__pack(svn_fs_t *fs,
+               apr_size_t max_mem,
                svn_fs_pack_notify_t notify_func,
                void *notify_baton,
                svn_cancel_func_t cancel_func,

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/rep-cache.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/rep-cache.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/rep-cache.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/rep-cache.c Wed Nov  7 12:30:06 2018
@@ -126,7 +126,11 @@ svn_fs_x__open_rep_cache(svn_fs_t *fs,
   svn_fs_x__data_t *ffd = fs->fsap_data;
   svn_error_t *err = svn_atomic__init_once(&ffd->rep_cache_db_opened,
                                            open_rep_cache, fs, scratch_pool);
-  return svn_error_quick_wrap(err, _("Couldn't open rep-cache database"));
+  return svn_error_quick_wrapf(err,
+                               _("Couldn't open rep-cache database '%s'"),
+                               svn_dirent_local_style(
+                                 path_rep_cache_db(fs->path, scratch_pool),
+                                 scratch_pool));
 }
 
 svn_error_t *

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/rev_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/rev_file.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/rev_file.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/rev_file.c Wed Nov  7 12:30:06 2018
@@ -474,6 +474,16 @@ svn_fs_x__rev_file_p2l_info(svn_fs_x__in
 }
 
 svn_error_t *
+svn_fs_x__rev_file_data_size(svn_filesize_t *size,
+                             svn_fs_x__revision_file_t *file)
+{
+  SVN_ERR(auto_read_footer(file));
+  *size = file->l2p_info.start;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_fs_x__rev_file_seek(svn_fs_x__revision_file_t *file,
                         apr_off_t *buffer_start,
                         apr_off_t offset)

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/rev_file.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/rev_file.h?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/rev_file.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/rev_file.h Wed Nov  7 12:30:06 2018
@@ -166,6 +166,12 @@ svn_error_t *
 svn_fs_x__rev_file_p2l_info(svn_fs_x__index_info_t *info,
                             svn_fs_x__revision_file_t *file);
 
+/* Set *SIZE to the length of the revision data in FILE.
+ */
+svn_error_t *
+svn_fs_x__rev_file_data_size(svn_filesize_t *size,
+                             svn_fs_x__revision_file_t *file);
+
 /* File manipulation. */
 
 /* Convenience wrapper around svn_io_file_aligned_seek. */

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/transaction.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/transaction.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/transaction.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/transaction.c Wed Nov  7 12:30:06 2018
@@ -721,7 +721,8 @@ get_writable_proto_rev(apr_file_t **file
   /* Now open the prototype revision file and seek to the end. */
   err = svn_io_file_open(file,
                          svn_fs_x__path_txn_proto_rev(fs, txn_id, pool),
-                         APR_WRITE | APR_BUFFERED, APR_OS_DEFAULT, pool);
+                         APR_READ | APR_WRITE | APR_BUFFERED, APR_OS_DEFAULT,
+                         pool);
 
   /* You might expect that we could dispense with the following seek
      and achieve the same thing by opening the file using APR_APPEND.
@@ -1258,7 +1259,7 @@ get_and_increment_txn_key_body(void *bat
       SVN_ERR(svn_io_check_path(txn_dir, &kind, iterpool));
       if (kind == svn_node_none)
         {
-          svn_io_dir_make(txn_dir, APR_OS_DEFAULT, iterpool);
+          SVN_ERR(svn_io_dir_make(txn_dir, APR_OS_DEFAULT, iterpool));
           break;
         }
 
@@ -1538,7 +1539,7 @@ store_l2p_index_entry(svn_fs_t *fs,
 static svn_error_t *
 store_p2l_index_entry(svn_fs_t *fs,
                       svn_fs_x__txn_id_t txn_id,
-                      svn_fs_x__p2l_entry_t *entry,
+                      const svn_fs_x__p2l_entry_t *entry,
                       apr_pool_t *scratch_pool)
 {
   const char *path = svn_fs_x__path_p2l_proto_index(fs, txn_id, scratch_pool);
@@ -2329,13 +2330,21 @@ rep_write_get_baton(rep_write_baton_t **
    there may be new duplicate representations within the same uncommitted
    revision, those can be passed in REPS_HASH (maps a sha1 digest onto
    svn_fs_x__representation_t*), otherwise pass in NULL for REPS_HASH.
+
+   The content of both representations will be compared, taking REP's content
+   from FILE at OFFSET.  Only if they actually match, will *OLD_REP not be
+   NULL.
+
    Use RESULT_POOL for *OLD_REP  allocations and SCRATCH_POOL for temporaries.
    The lifetime of *OLD_REP is limited by both, RESULT_POOL and REP lifetime.
  */
 static svn_error_t *
 get_shared_rep(svn_fs_x__representation_t **old_rep,
                svn_fs_t *fs,
+               svn_fs_x__txn_id_t txn_id,
                svn_fs_x__representation_t *rep,
+               apr_file_t *file,
+               apr_off_t offset,
                apr_hash_t *reps_hash,
                apr_pool_t *result_pool,
                apr_pool_t *scratch_pool)
@@ -2343,6 +2352,10 @@ get_shared_rep(svn_fs_x__representation_
   svn_error_t *err;
   svn_fs_x__data_t *ffd = fs->fsap_data;
 
+  svn_checksum_t checksum;
+  checksum.digest = rep->sha1_digest;
+  checksum.kind = svn_checksum_sha1;
+
   /* Return NULL, if rep sharing has been disabled. */
   *old_rep = NULL;
   if (!ffd->rep_sharing_allowed)
@@ -2363,9 +2376,6 @@ get_shared_rep(svn_fs_x__representation_
   /* If we haven't found anything yet, try harder and consult our DB. */
   if (*old_rep == NULL)
     {
-      svn_checksum_t checksum;
-      checksum.digest = rep->sha1_digest;
-      checksum.kind = svn_checksum_sha1;
       err = svn_fs_x__get_rep_reference(old_rep, fs, &checksum, result_pool,
                                         scratch_pool);
 
@@ -2435,10 +2445,6 @@ get_shared_rep(svn_fs_x__representation_
          Because not sharing reps is always a safe option,
          terminating the request would be inappropriate.
        */
-      svn_checksum_t checksum;
-      checksum.digest = rep->sha1_digest;
-      checksum.kind = svn_checksum_sha1;
-
       err = svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
                               "Rep size %s mismatches rep-cache.db value %s "
                               "for SHA1 %s.\n"
@@ -2467,6 +2473,81 @@ get_shared_rep(svn_fs_x__representation_
       memcpy((*old_rep)->md5_digest, rep->md5_digest, sizeof(rep->md5_digest));
     }
 
+  /* If we (very likely) found a matching representation, compare the actual
+   * contents such that we can be sure that no rep-cache.db corruption or
+   * hash collision produced a false positive. */
+  if (*old_rep)
+    {
+      apr_off_t old_position;
+      svn_stream_t *contents;
+      svn_stream_t *old_contents;
+      svn_boolean_t same;
+
+      /* Make sure we can later restore FILE's current position. */
+      SVN_ERR(svn_io_file_get_offset(&old_position, file, scratch_pool));
+
+      /* Compare the two representations.
+       * Note that the stream comparison might also produce MD5 checksum
+       * errors or other failures in case of SHA1 collisions. */
+      SVN_ERR(svn_fs_x__get_contents_from_file(&contents, fs, rep, file,
+                                               offset, scratch_pool));
+      if ((*old_rep)->id.change_set == rep->id.change_set)
+        {
+          /* Comparing with contents from the same transaction means
+           * reading the same prote-rev FILE.  In the commit stage,
+           * the file will already have been moved and the IDs already
+           * bumped to the final revision.  Hence, we must determine
+           * the OFFSET "manually". */
+          svn_fs_x__revision_file_t *rev_file;
+          apr_uint32_t sub_item = 0;
+          svn_fs_x__id_t id;
+          id.change_set = svn_fs_x__change_set_by_txn(txn_id);
+          id.number = (*old_rep)->id.number;
+
+          SVN_ERR(svn_fs_x__rev_file_wrap_temp(&rev_file, fs, file,
+                                               scratch_pool));
+          SVN_ERR(svn_fs_x__item_offset(&offset, &sub_item, fs, rev_file,
+                                        &id, scratch_pool));
+
+          SVN_ERR(svn_fs_x__get_contents_from_file(&old_contents, fs,
+                                                   *old_rep, file,
+                                                   offset, scratch_pool));
+        }
+      else
+        {
+          SVN_ERR(svn_fs_x__get_contents(&old_contents, fs, *old_rep,
+                                         FALSE, scratch_pool));
+        }
+      err = svn_stream_contents_same2(&same, contents, old_contents,
+                                      scratch_pool);
+
+      /* A mismatch should be extremely rare.
+       * If it does happen, reject the commit. */
+      if (!same || err)
+        {
+          /* SHA1 collision or worse. */
+          svn_stringbuf_t *old_rep_str
+            = svn_fs_x__unparse_representation(*old_rep, FALSE,
+                                               scratch_pool,
+                                               scratch_pool);
+          svn_stringbuf_t *rep_str
+            = svn_fs_x__unparse_representation(rep, FALSE,
+                                               scratch_pool,
+                                               scratch_pool);
+          const char *checksum__str
+            = svn_checksum_to_cstring_display(&checksum, scratch_pool);
+
+          return svn_error_createf(SVN_ERR_FS_AMBIGUOUS_CHECKSUM_REP,
+                                   err, "SHA1 of reps '%s' and '%s' "
+                                   "matches (%s) but contents differ",
+                                   old_rep_str->data, rep_str->data,
+                                   checksum__str);
+        }
+
+      /* Restore FILE's read / write position. */
+      SVN_ERR(svn_io_file_seek(file, APR_SET, &old_position, scratch_pool));
+    }
+
   return SVN_NO_ERROR;
 }
 
@@ -2527,8 +2608,8 @@ rep_write_contents_close(void *baton)
 
   /* Check and see if we already have a representation somewhere that's
      identical to the one we just wrote out. */
-  SVN_ERR(get_shared_rep(&old_rep, b->fs, rep, NULL, b->result_pool,
-                         b->local_pool));
+  SVN_ERR(get_shared_rep(&old_rep, b->fs, txn_id, rep, b->file, b->rep_offset,
+                         NULL, b->result_pool, b->local_pool));
 
   if (old_rep)
     {
@@ -2572,11 +2653,16 @@ rep_write_contents_close(void *baton)
       entry.items = &noderev_id;
       entry.fnv1_checksum = b->fnv1a_checksum;
 
-      SVN_ERR(store_sha1_rep_mapping(b->fs, b->noderev, b->local_pool));
       SVN_ERR(store_p2l_index_entry(b->fs, txn_id, &entry, b->local_pool));
     }
 
   SVN_ERR(svn_io_file_close(b->file, b->local_pool));
+
+  /* Write the sha1->rep mapping *after* we successfully written node
+   * revision to disk. */
+  if (!old_rep)
+    SVN_ERR(store_sha1_rep_mapping(b->fs, b->noderev, b->local_pool));
+
   SVN_ERR(unlock_proto_rev(b->fs, txn_id, b->lockcookie, b->local_pool));
   svn_pool_destroy(b->local_pool);
 
@@ -2754,11 +2840,14 @@ write_directory_to_stream(svn_stream_t *
 /* Write out the COLLECTION pertaining to the NODEREV in FS as a deltified
    text representation to file FILE using WRITER.  In the process, record the
    total size and the md5 digest in REP and add the representation of type
-   ITEM_TYPE to the indexes if necessary.  If rep sharing has been enabled and
-   REPS_HASH is not NULL, it will be used in addition to the on-disk cache to
-   find earlier reps with the same content.  When such existing reps can be
-   found, we will truncate the one just written from the file and return the
-   existing rep.
+   ITEM_TYPE to the indexes if necessary.
+
+   If ALLOW_REP_SHARING is FALSE, rep-sharing will not be used, regardless
+   of any other option and rep-sharing settings.  If rep sharing has been
+   enabled and REPS_HASH is not NULL, it will be used in addition to the
+   on-disk cache to find earlier reps with the same content.  If such
+   existing reps can be found, we will truncate the one just written from
+   the file and return the existing rep.
 
    If ITEM_TYPE is IS_PROPS equals SVN_FS_FS__ITEM_TYPE_*_PROPS, assume
    that we want to a props representation as the base for our delta.
@@ -2775,6 +2864,7 @@ write_container_delta_rep(svn_fs_x__repr
                           svn_fs_x__txn_id_t txn_id,
                           svn_fs_x__noderev_t *noderev,
                           apr_hash_t *reps_hash,
+                          svn_boolean_t allow_rep_sharing,
                           apr_uint32_t item_type,
                           svn_revnum_t final_revision,
                           apr_pool_t *scratch_pool)
@@ -2786,7 +2876,7 @@ write_container_delta_rep(svn_fs_x__repr
   svn_stream_t *file_stream;
   svn_stream_t *stream;
   svn_fs_x__representation_t *base_rep;
-  svn_fs_x__representation_t *old_rep;
+  svn_fs_x__representation_t *old_rep = NULL;
   svn_fs_x__p2l_entry_t entry;
   svn_stream_t *source;
   svn_fs_x__rep_header_t header = { 0 };
@@ -2852,12 +2942,17 @@ write_container_delta_rep(svn_fs_x__repr
 
   /* Store the results. */
   SVN_ERR(digests_final(rep, whb->md5_ctx, whb->sha1_ctx, scratch_pool));
+
+  /* Update size info. */
+  SVN_ERR(svn_io_file_get_offset(&rep_end, file, scratch_pool));
+  rep->size = rep_end - delta_start;
   rep->expanded_size = whb->size;
 
   /* Check and see if we already have a representation somewhere that's
      identical to the one we just wrote out. */
-  SVN_ERR(get_shared_rep(&old_rep, fs, rep, reps_hash, scratch_pool,
-                         scratch_pool));
+  if (allow_rep_sharing)
+    SVN_ERR(get_shared_rep(&old_rep, fs, txn_id, rep, file, offset, reps_hash,
+                           scratch_pool, scratch_pool));
 
   if (old_rep)
     {
@@ -2874,7 +2969,6 @@ write_container_delta_rep(svn_fs_x__repr
       svn_fs_x__id_t noderev_id;
 
       /* Write out our cosmetic end marker. */
-      SVN_ERR(svn_io_file_get_offset(&rep_end, file, scratch_pool));
       SVN_ERR(svn_stream_puts(file_stream, "ENDREP\n"));
       SVN_ERR(svn_stream_close(file_stream));
 
@@ -3076,7 +3170,7 @@ write_final_rev(svn_fs_x__id_t *new_id_p
           SVN_ERR(write_container_delta_rep(noderev->data_rep, file,
                                             entries,
                                             write_directory_to_stream,
-                                            fs, txn_id, noderev, NULL,
+                                            fs, txn_id, noderev, NULL, FALSE,
                                             SVN_FS_X__ITEM_TYPE_DIR_REP,
                                             rev, scratch_pool));
 
@@ -3128,8 +3222,8 @@ write_final_rev(svn_fs_x__id_t *new_id_p
 
       SVN_ERR(write_container_delta_rep(noderev->prop_rep, file, proplist,
                                         write_hash_to_stream, fs, txn_id,
-                                        noderev, reps_hash, item_type, rev,
-                                        scratch_pool));
+                                        noderev, reps_hash, TRUE, item_type,
+                                        rev, scratch_pool));
     }
 
   /* Convert our temporary ID into a permanent revision one. */
@@ -3558,10 +3652,8 @@ get_writable_final_rev(apr_file_t **file
   SVN_ERR(svn_io_file_seek(*file, APR_END, &end_offset, scratch_pool));
 
   /* We don't want unused sections (such as leftovers from failed delta
-     stream) in our file.  If we use log addressing, we would need an
-     index entry for the unused section and that section would need to
-     be all NUL by convention.  So, detect and fix those cases by truncating
-     the protorev file. */
+     stream) in our file.  Detect and fix those cases by truncating the
+     protorev file. */
   SVN_ERR(auto_truncate_proto_rev(fs, *file, end_offset, txn_id,
                                   scratch_pool));
 

Modified: subversion/branches/ra-git/subversion/libsvn_fs_x/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_x/tree.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_x/tree.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_x/tree.c Wed Nov  7 12:30:06 2018
@@ -1251,7 +1251,7 @@ svn_fs_x__commit_txn(const char **confli
 
   if (ffd->pack_after_commit)
     {
-      SVN_ERR(svn_fs_x__pack(fs, NULL, NULL, NULL, NULL, pool));
+      SVN_ERR(svn_fs_x__pack(fs, 0, NULL, NULL, NULL, NULL, pool));
     }
 
   return SVN_NO_ERROR;
@@ -2060,7 +2060,7 @@ typedef struct text_baton_t
  * svn_fs_apply_text()      ==> ... ==> txn_body_fulltext_finalize_edits()
  */
 
-/* Write function for the publically returned stream. */
+/* Write function for the publicly returned stream. */
 static svn_error_t *
 text_stream_writer(void *baton,
                    const char *data,
@@ -2904,21 +2904,19 @@ assemble_history(svn_fs_t *fs,
 
 /* DIR_DAG is a directory DAG node which has mergeinfo in its
    descendants.  This function iterates over its children.  For each
-   child with immediate mergeinfo, it adds its mergeinfo to
-   RESULT_CATALOG.  appropriate arguments.  For each child with
-   descendants with mergeinfo, it recurses.  Note that it does *not*
-   call the action on the path for DIR_DAG itself.
-
-   POOL is used for temporary allocations, including the mergeinfo
-   hashes passed to actions; RESULT_POOL is used for the mergeinfo added
-   to RESULT_CATALOG.
+   child with immediate mergeinfo, call RECEIVER with it and BATON.
+   For each child with descendants with mergeinfo, it recurses.  Note
+   that it does *not* call the action on the path for DIR_DAG itself.
+
+   SCRATCH_POOL is used for temporary allocations, including the mergeinfo
+   hashes passed to actions.
  */
 static svn_error_t *
 crawl_directory_dag_for_mergeinfo(svn_fs_root_t *root,
                                   const char *this_path,
                                   dag_node_t *dir_dag,
-                                  svn_mergeinfo_catalog_t result_catalog,
-                                  apr_pool_t *result_pool,
+                                  svn_fs_mergeinfo_receiver_t receiver,
+                                  void *baton,
                                   apr_pool_t *scratch_pool)
 {
   apr_array_header_t *entries;
@@ -2966,7 +2964,7 @@ crawl_directory_dag_for_mergeinfo(svn_fs
              error. */
           err = svn_mergeinfo_parse(&kid_mergeinfo,
                                     mergeinfo_string->data,
-                                    result_pool);
+                                    iterpool);
           if (err)
             {
               if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
@@ -2976,8 +2974,7 @@ crawl_directory_dag_for_mergeinfo(svn_fs
               }
           else
             {
-              svn_hash_sets(result_catalog, apr_pstrdup(result_pool, kid_path),
-                            kid_mergeinfo);
+              SVN_ERR(receiver(kid_path, kid_mergeinfo, baton, iterpool));
             }
         }
 
@@ -2985,8 +2982,8 @@ crawl_directory_dag_for_mergeinfo(svn_fs
         SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
                                                   kid_path,
                                                   kid_dag,
-                                                  result_catalog,
-                                                  result_pool,
+                                                  receiver,
+                                                  baton,
                                                   iterpool));
     }
 
@@ -3095,14 +3092,13 @@ get_mergeinfo_for_path(svn_mergeinfo_t *
   return SVN_NO_ERROR;
 }
 
-/* Adds mergeinfo for each descendant of PATH (but not PATH itself)
-   under ROOT to RESULT_CATALOG.  Returned values are allocated in
-   RESULT_POOL; temporary values in POOL. */
+/* Invoke RECEIVER with BATON for each mergeinfo found on descendants of
+   PATH (but not PATH itself).  Use SCRATCH_POOL for temporary values. */
 static svn_error_t *
-add_descendant_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
-                         svn_fs_root_t *root,
+add_descendant_mergeinfo(svn_fs_root_t *root,
                          const char *path,
-                         apr_pool_t *result_pool,
+                         svn_fs_mergeinfo_receiver_t receiver,
+                         void *baton,
                          apr_pool_t *scratch_pool)
 {
   dag_node_t *this_dag;
@@ -3112,27 +3108,28 @@ add_descendant_mergeinfo(svn_mergeinfo_c
     SVN_ERR(crawl_directory_dag_for_mergeinfo(root,
                                               path,
                                               this_dag,
-                                              result_catalog,
-                                              result_pool,
+                                              receiver,
+                                              baton,
                                               scratch_pool));
   return SVN_NO_ERROR;
 }
 
 
-/* Get the mergeinfo for a set of paths, returned in
-   *MERGEINFO_CATALOG.  Returned values are allocated in
-   POOL, while temporary values are allocated in a sub-pool. */
+/* Find all the mergeinfo for a set of PATHS under ROOT and report it
+   through RECEIVER with BATON.  INHERITED, INCLUDE_DESCENDANTS and
+   ADJUST_INHERITED_MERGEINFO are the same as in the FS API.
+
+   Allocate temporary values are allocated in SCRATCH_POOL. */
 static svn_error_t *
 get_mergeinfos_for_paths(svn_fs_root_t *root,
-                         svn_mergeinfo_catalog_t *mergeinfo_catalog,
                          const apr_array_header_t *paths,
                          svn_mergeinfo_inheritance_t inherit,
                          svn_boolean_t include_descendants,
                          svn_boolean_t adjust_inherited_mergeinfo,
-                         apr_pool_t *result_pool,
+                         svn_fs_mergeinfo_receiver_t receiver,
+                         void *baton,
                          apr_pool_t *scratch_pool)
 {
-  svn_mergeinfo_catalog_t result_catalog = svn_hash__make(result_pool);
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
   int i;
 
@@ -3146,7 +3143,7 @@ get_mergeinfos_for_paths(svn_fs_root_t *
 
       err = get_mergeinfo_for_path(&path_mergeinfo, root, path,
                                    inherit, adjust_inherited_mergeinfo,
-                                   result_pool, iterpool);
+                                   iterpool, iterpool);
       if (err)
         {
           if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
@@ -3162,27 +3159,26 @@ get_mergeinfos_for_paths(svn_fs_root_t *
         }
 
       if (path_mergeinfo)
-        svn_hash_sets(result_catalog, path, path_mergeinfo);
+        SVN_ERR(receiver(path, path_mergeinfo, baton, iterpool));
       if (include_descendants)
-        SVN_ERR(add_descendant_mergeinfo(result_catalog, root, path,
-                                         result_pool, scratch_pool));
+        SVN_ERR(add_descendant_mergeinfo(root, path, receiver, baton,
+                                         iterpool));
     }
   svn_pool_destroy(iterpool);
 
-  *mergeinfo_catalog = result_catalog;
   return SVN_NO_ERROR;
 }
 
 
 /* Implements svn_fs_get_mergeinfo. */
 static svn_error_t *
-x_get_mergeinfo(svn_mergeinfo_catalog_t *catalog,
-                svn_fs_root_t *root,
+x_get_mergeinfo(svn_fs_root_t *root,
                 const apr_array_header_t *paths,
                 svn_mergeinfo_inheritance_t inherit,
                 svn_boolean_t include_descendants,
                 svn_boolean_t adjust_inherited_mergeinfo,
-                apr_pool_t *result_pool,
+                svn_fs_mergeinfo_receiver_t receiver,
+                void *baton,
                 apr_pool_t *scratch_pool)
 {
   /* We require a revision root. */
@@ -3190,11 +3186,11 @@ x_get_mergeinfo(svn_mergeinfo_catalog_t
     return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
 
   /* Retrieve a path -> mergeinfo hash mapping. */
-  return get_mergeinfos_for_paths(root, catalog, paths,
-                                  inherit,
+  return get_mergeinfos_for_paths(root, paths, inherit,
                                   include_descendants,
                                   adjust_inherited_mergeinfo,
-                                  result_pool, scratch_pool);
+                                  receiver, baton,
+                                  scratch_pool);
 }
 
 

Modified: subversion/branches/ra-git/subversion/libsvn_ra/ra_loader.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra/ra_loader.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra/ra_loader.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra/ra_loader.c Wed Nov  7 12:30:06 2018
@@ -658,7 +658,7 @@ svn_error_t *
 svn_ra_list(svn_ra_session_t *session,
             const char *path,
             svn_revnum_t revision,
-            apr_array_header_t *patterns,
+            const apr_array_header_t *patterns,
             svn_depth_t depth,
             apr_uint32_t dirent_fields,
             svn_ra_dirent_receiver_t receiver,

Modified: subversion/branches/ra-git/subversion/libsvn_ra/ra_loader.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra/ra_loader.h?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra/ra_loader.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra/ra_loader.h Wed Nov  7 12:30:06 2018
@@ -336,7 +336,7 @@ typedef struct svn_ra__vtable_t {
   svn_error_t *(*list)(svn_ra_session_t *session,
                        const char *path,
                        svn_revnum_t revision,
-                       apr_array_header_t *patterns,
+                       const apr_array_header_t *patterns,
                        svn_depth_t depth,
                        apr_uint32_t dirent_fields,
                        svn_ra_dirent_receiver_t receiver,

Modified: subversion/branches/ra-git/subversion/libsvn_ra_local/ra_plugin.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_local/ra_plugin.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_local/ra_plugin.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_local/ra_plugin.c Wed Nov  7 12:30:06 2018
@@ -909,6 +909,28 @@ svn_ra_local__get_commit_editor(svn_ra_s
 }
 
 
+/* Implements svn_repos_mergeinfo_receiver_t.
+ * It add MERGEINFO for PATH to the svn_mergeinfo_catalog_t BATON.
+ */
+static svn_error_t *
+mergeinfo_receiver(const char *path,
+                   svn_mergeinfo_t mergeinfo,
+                   void *baton,
+                   apr_pool_t *scratch_pool)
+{
+  svn_mergeinfo_catalog_t catalog = baton;
+  apr_pool_t *result_pool = apr_hash_pool_get(catalog);
+  apr_size_t len = strlen(path);
+
+  apr_hash_set(catalog,
+               apr_pstrmemdup(result_pool, path, len),
+               len,
+               svn_mergeinfo_dup(mergeinfo, result_pool));
+
+  return SVN_NO_ERROR;
+}
+
+
 static svn_error_t *
 svn_ra_local__get_mergeinfo(svn_ra_session_t *session,
                             svn_mergeinfo_catalog_t *catalog,
@@ -919,7 +941,7 @@ svn_ra_local__get_mergeinfo(svn_ra_sessi
                             apr_pool_t *pool)
 {
   svn_ra_local__session_baton_t *sess = session->priv;
-  svn_mergeinfo_catalog_t tmp_catalog;
+  svn_mergeinfo_catalog_t tmp_catalog = svn_hash__make(pool);
   int i;
   apr_array_header_t *abs_paths =
     apr_array_make(pool, 0, sizeof(const char *));
@@ -931,9 +953,11 @@ svn_ra_local__get_mergeinfo(svn_ra_sessi
         svn_fspath__join(sess->fs_path->data, relative_path, pool);
     }
 
-  SVN_ERR(svn_repos_fs_get_mergeinfo(&tmp_catalog, sess->repos, abs_paths,
-                                     revision, inherit, include_descendants,
-                                     NULL, NULL, pool));
+  SVN_ERR(svn_repos_fs_get_mergeinfo2(sess->repos, abs_paths, revision,
+                                      inherit, include_descendants,
+                                      NULL, NULL,
+                                      mergeinfo_receiver, tmp_catalog,
+                                      pool));
   if (apr_hash_count(tmp_catalog) > 0)
     SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(catalog,
                                                       tmp_catalog,
@@ -1363,7 +1387,7 @@ svn_ra_local__get_dir(svn_ra_session_t *
             {
               /* size  */
               if (fs_entry->kind == svn_node_dir)
-                entry->size = 0;
+                entry->size = SVN_INVALID_FILESIZE;
               else
                 SVN_ERR(svn_fs_file_length(&(entry->size), root,
                                            fullpath, iterpool));
@@ -1813,7 +1837,7 @@ static svn_error_t *
 svn_ra_local__list(svn_ra_session_t *session,
                    const char *path,
                    svn_revnum_t revision,
-                   apr_array_header_t *patterns,
+                   const apr_array_header_t *patterns,
                    svn_depth_t depth,
                    apr_uint32_t dirent_fields,
                    svn_ra_dirent_receiver_t receiver,

Modified: subversion/branches/ra-git/subversion/libsvn_ra_serf/blame.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_serf/blame.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_serf/blame.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_serf/blame.c Wed Nov  7 12:30:06 2018
@@ -81,6 +81,8 @@ typedef struct blame_context_t {
 
   svn_stream_t *stream;
 
+  svn_ra_serf__session_t *session;
+
 } blame_context_t;
 
 
@@ -318,6 +320,20 @@ create_file_revs_body(serf_bucket_t **bo
   return SVN_NO_ERROR;
 }
 
+/* Implements svn_ra_serf__request_header_delegate_t */
+static svn_error_t *
+setup_headers(serf_bucket_t *headers,
+              void *baton,
+              apr_pool_t *request_pool,
+              apr_pool_t *scratch_pool)
+{
+  blame_context_t *blame_ctx = baton;
+
+  svn_ra_serf__setup_svndiff_accept_encoding(headers, blame_ctx->session);
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_ra_serf__get_file_revs(svn_ra_session_t *ra_session,
                            const char *path,
@@ -343,6 +359,7 @@ svn_ra_serf__get_file_revs(svn_ra_sessio
   blame_ctx->start = start;
   blame_ctx->end = end;
   blame_ctx->include_merged_revisions = include_merged_revisions;
+  blame_ctx->session = session;
 
   /* Since Subversion 1.8 we allow retrieving blames backwards. So we can't
      just unconditionally use end_rev as the peg revision as before */
@@ -369,6 +386,9 @@ svn_ra_serf__get_file_revs(svn_ra_sessio
   handler->body_type = "text/xml";
   handler->body_delegate = create_file_revs_body;
   handler->body_delegate_baton = blame_ctx;
+  handler->custom_accept_encoding = TRUE;
+  handler->header_delegate = setup_headers;
+  handler->header_delegate_baton = blame_ctx;
 
   SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
 

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=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_serf/commit.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_serf/commit.c Wed Nov  7 12:30:06 2018
@@ -176,12 +176,18 @@ typedef struct file_context_t {
   /* Buffer holding the svndiff (can spill to disk). */
   svn_ra_serf__request_body_t *svndiff;
 
+  /* Did we send the svndiff in apply_textdelta_stream()? */
+  svn_boolean_t svndiff_sent;
+
   /* Our base checksum as reported by the WC. */
   const char *base_checksum;
 
   /* Our resulting checksum as reported by the WC. */
   const char *result_checksum;
 
+  /* Our resulting checksum as reported by the server. */
+  svn_checksum_t *remote_result_checksum;
+
   /* Changed properties (const char * -> svn_prop_t *) */
   apr_hash_t *prop_changes;
 
@@ -305,7 +311,7 @@ checkout_node(const char **working_url,
    fails due to an SVN_ERR_APMOD_BAD_BASELINE error return from the
    server.
 
-   See http://subversion.tigris.org/issues/show_bug.cgi?id=4127 for
+   See https://issues.apache.org/jira/browse/SVN-4127 for
    details.
 */
 static svn_error_t *
@@ -671,7 +677,7 @@ write_prop_xml(const proppatch_context_t
    explicitly deleted in this commit already, then mod_dav removed its
    lock token when it fielded the DELETE request, so we don't want to
    set the lock precondition again.  (See
-   http://subversion.tigris.org/issues/show_bug.cgi?id=3674 for details.)
+   https://issues.apache.org/jira/browse/SVN-3674 for details.)
 */
 static svn_error_t *
 maybe_set_lock_token_header(serf_bucket_t *headers,
@@ -681,7 +687,7 @@ maybe_set_lock_token_header(serf_bucket_
 {
   const char *token;
 
-  if (! (*relpath && commit_ctx->lock_tokens))
+  if (! commit_ctx->lock_tokens)
     return SVN_NO_ERROR;
 
   if (! svn_hash_gets(commit_ctx->deleted_entries, relpath))
@@ -1859,6 +1865,75 @@ open_file(const char *path,
   return SVN_NO_ERROR;
 }
 
+static void
+negotiate_put_encoding(int *svndiff_version_p,
+                       int *svndiff_compression_level_p,
+                       svn_ra_serf__session_t *session)
+{
+  int svndiff_version;
+  int compression_level;
+
+  if (session->using_compression == svn_tristate_unknown)
+    {
+      /* With http-compression=auto, prefer svndiff2 to svndiff1 with a
+       * low latency connection (assuming the underlying network has high
+       * bandwidth), as it is faster and in this case, we don't care about
+       * worse compression ratio.
+       *
+       * Note: For future compatibility, we also handle a theoretically
+       * possible case where the server has advertised only svndiff2 support.
+       */
+      if (session->supports_svndiff2 &&
+          svn_ra_serf__is_low_latency_connection(session))
+        svndiff_version = 2;
+      else if (session->supports_svndiff1)
+        svndiff_version = 1;
+      else if (session->supports_svndiff2)
+        svndiff_version = 2;
+      else
+        svndiff_version = 0;
+    }
+  else if (session->using_compression == svn_tristate_true)
+    {
+      /* Otherwise, prefer svndiff1, as svndiff2 is not a reasonable
+       * substitute for svndiff1 with default compression level.  (It gives
+       * better speed and compression ratio comparable to svndiff1 with
+       * compression level 1, but not 5).
+       *
+       * Note: For future compatibility, we also handle a theoretically
+       * possible case where the server has advertised only svndiff2 support.
+       */
+      if (session->supports_svndiff1)
+        svndiff_version = 1;
+      else if (session->supports_svndiff2)
+        svndiff_version = 2;
+      else
+        svndiff_version = 0;
+    }
+  else
+    {
+      /* Difference between svndiff formats 0 and 1/2 that format 1/2 allows
+       * compression.  Uncompressed svndiff0 should also be slightly more
+       * effective if the compression is not required at all.
+       *
+       * If the server cannot handle svndiff1/2, or compression is disabled
+       * with the 'http-compression = no' client configuration option, fall
+       * back to uncompressed svndiff0 format.  As a bonus, users can force
+       * the usage of the uncompressed format by setting the corresponding
+       * client configuration option, if they want to.
+       */
+      svndiff_version = 0;
+    }
+
+  if (svndiff_version == 0)
+    compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE;
+  else
+    compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
+
+  *svndiff_version_p = svndiff_version;
+  *svndiff_compression_level_p = compression_level;
+}
+
 static svn_error_t *
 apply_textdelta(void *file_baton,
                 const char *base_checksum,
@@ -1873,49 +1948,21 @@ apply_textdelta(void *file_baton,
   /* 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
-   * 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
-   *     trigger the operation?
-   *     Having a request (body) bucket return EAGAIN until done stalls
-   *     the entire HTTP pipeline after writing the first part of the
-   *     request. It is not like we can interrupt some part of a request
-   *     and continue later. Or somebody else must use tempfiles and
-   *     always assume that clients work this bad... as it only knows
-   *     for sure after the request is completely available.
+   * Please note that if this callback is used, large request bodies will
+   * be spilled into temporary files (that requires disk space and prevents
+   * simultaneous processing by the server and the client).  A better approach
+   * that streams the request body is implemented in apply_textdelta_stream().
+   * It will be used with most recent servers having the "send result checksum
+   * in response to a PUT" capability, and only if the editor driver uses the
+   * new callback.
    */
-
   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)
-    {
-      /* Use compressed svndiff1 format, if possible. */
-      svndiff_version = 1;
-      compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
-    }
-  else
-    {
-      /* Difference between svndiff formats 0 and 1 that format 1 allows
-       * compression.  Uncompressed svndiff0 should also be slightly more
-       * effective if the compression is not required at all.
-       *
-       * If the server cannot handle svndiff1, or compression is disabled
-       * with the 'http-compression = no' client configuration option, fall
-       * back to uncompressed svndiff0 format.  As a bonus, users can force
-       * the usage of the uncompressed format by setting the corresponding
-       * client configuration option, if they want to.
-       */
-      svndiff_version = 0;
-      compression_level = SVN_DELTA_COMPRESSION_LEVEL_NONE;
-    }
-
+  negotiate_put_encoding(&svndiff_version, &compression_level,
+                         ctx->commit_ctx->session);
   /* Disown the stream; we'll close it explicitly in close_file(). */
   svn_txdelta_to_svndiff3(handler, handler_baton,
                           svn_stream_disown(ctx->stream, pool),
@@ -1927,6 +1974,146 @@ apply_textdelta(void *file_baton,
   return SVN_NO_ERROR;
 }
 
+typedef struct open_txdelta_baton_t
+{
+  svn_ra_serf__session_t *session;
+  svn_txdelta_stream_open_func_t open_func;
+  void *open_baton;
+  svn_error_t *err;
+} open_txdelta_baton_t;
+
+static void
+txdelta_stream_errfunc(void *baton, svn_error_t *err)
+{
+  open_txdelta_baton_t *b = baton;
+
+  /* Remember extended error info from the stream bucket.  Note that
+   * theoretically this errfunc could be called multiple times -- say,
+   * if the request gets restarted after an error.  Compose the errors
+   * so we don't leak one of them if this happens. */
+  b->err = svn_error_compose_create(b->err, svn_error_dup(err));
+}
+
+/* Implements svn_ra_serf__request_body_delegate_t */
+static svn_error_t *
+create_body_from_txdelta_stream(serf_bucket_t **body_bkt,
+                                void *baton,
+                                serf_bucket_alloc_t *alloc,
+                                apr_pool_t *pool /* request pool */,
+                                apr_pool_t *scratch_pool)
+{
+  open_txdelta_baton_t *b = baton;
+  svn_txdelta_stream_t *txdelta_stream;
+  svn_stream_t *stream;
+  int svndiff_version;
+  int compression_level;
+
+  SVN_ERR(b->open_func(&txdelta_stream, b->open_baton, pool, scratch_pool));
+
+  negotiate_put_encoding(&svndiff_version, &compression_level, b->session);
+  stream = svn_txdelta_to_svndiff_stream(txdelta_stream, svndiff_version,
+                                         compression_level, pool);
+  *body_bkt = svn_ra_serf__create_stream_bucket(stream, alloc,
+                                                txdelta_stream_errfunc, b);
+
+  return SVN_NO_ERROR;
+}
+
+/* Handler baton for PUT request. */
+typedef struct put_response_ctx_t
+{
+  svn_ra_serf__handler_t *handler;
+  file_context_t *file_ctx;
+} put_response_ctx_t;
+
+/* Implements svn_ra_serf__response_handler_t */
+static svn_error_t *
+put_response_handler(serf_request_t *request,
+                     serf_bucket_t *response,
+                     void *baton,
+                     apr_pool_t *scratch_pool)
+{
+  put_response_ctx_t *prc = baton;
+  serf_bucket_t *hdrs;
+  const char *val;
+
+  hdrs = serf_bucket_response_get_headers(response);
+  val = serf_bucket_headers_get(hdrs, SVN_DAV_RESULT_FULLTEXT_MD5_HEADER);
+  SVN_ERR(svn_checksum_parse_hex(&prc->file_ctx->remote_result_checksum,
+                                 svn_checksum_md5, val, prc->file_ctx->pool));
+
+  return svn_error_trace(
+           svn_ra_serf__expect_empty_body(request, response,
+                                          prc->handler, scratch_pool));
+}
+
+static svn_error_t *
+apply_textdelta_stream(const svn_delta_editor_t *editor,
+                       void *file_baton,
+                       const char *base_checksum,
+                       svn_txdelta_stream_open_func_t open_func,
+                       void *open_baton,
+                       apr_pool_t *scratch_pool)
+{
+  file_context_t *ctx = file_baton;
+  open_txdelta_baton_t open_txdelta_baton = {0};
+  svn_ra_serf__handler_t *handler;
+  put_response_ctx_t *prc;
+  int expected_result;
+  svn_error_t *err;
+
+  /* Remember that we have sent the svndiff.  A case when we need to
+   * perform a zero-byte file PUT (during add_file, close_file editor
+   * sequences) is handled in close_file().
+   */
+  ctx->svndiff_sent = TRUE;
+  ctx->base_checksum = base_checksum;
+
+  handler = svn_ra_serf__create_handler(ctx->commit_ctx->session,
+                                        scratch_pool);
+  handler->method = "PUT";
+  handler->path = ctx->url;
+
+  prc = apr_pcalloc(scratch_pool, sizeof(*prc));
+  prc->handler = handler;
+  prc->file_ctx = ctx;
+
+  handler->response_handler = put_response_handler;
+  handler->response_baton = prc;
+
+  open_txdelta_baton.session = ctx->commit_ctx->session;
+  open_txdelta_baton.open_func = open_func;
+  open_txdelta_baton.open_baton = open_baton;
+  open_txdelta_baton.err = SVN_NO_ERROR;
+
+  handler->body_delegate = create_body_from_txdelta_stream;
+  handler->body_delegate_baton = &open_txdelta_baton;
+  handler->body_type = SVN_SVNDIFF_MIME_TYPE;
+
+  handler->header_delegate = setup_put_headers;
+  handler->header_delegate_baton = ctx;
+
+  err = svn_ra_serf__context_run_one(handler, scratch_pool);
+  /* Do we have an error from the stream bucket?  If yes, use it. */
+  if (open_txdelta_baton.err)
+    {
+      svn_error_clear(err);
+      return svn_error_trace(open_txdelta_baton.err);
+    }
+  else if (err)
+    return svn_error_trace(err);
+
+  if (ctx->added && !ctx->copy_path)
+    expected_result = 201; /* Created */
+  else
+    expected_result = 204; /* Updated */
+
+  if (handler->sline.code != expected_result)
+    return svn_error_trace(svn_ra_serf__unexpected_status(handler));
+
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *
 change_file_prop(void *file_baton,
                  const char *name,
@@ -1962,8 +2149,8 @@ close_file(void *file_baton,
   if ((!ctx->svndiff) && ctx->added && (!ctx->copy_path))
     put_empty_file = TRUE;
 
-  /* If we had a stream of changes, push them to the server... */
-  if (ctx->svndiff || put_empty_file)
+  /* If we have a stream of changes, push them to the server... */
+  if ((ctx->svndiff || put_empty_file) && !ctx->svndiff_sent)
     {
       svn_ra_serf__handler_t *handler;
       int expected_result;
@@ -2028,6 +2215,22 @@ close_file(void *file_baton,
                                  proppatch, scratch_pool));
     }
 
+  if (ctx->result_checksum && ctx->remote_result_checksum)
+    {
+      svn_checksum_t *result_checksum;
+
+      SVN_ERR(svn_checksum_parse_hex(&result_checksum, svn_checksum_md5,
+                                     ctx->result_checksum, scratch_pool));
+
+      if (!svn_checksum_match(result_checksum, ctx->remote_result_checksum))
+        return svn_checksum_mismatch_err(result_checksum,
+                                         ctx->remote_result_checksum,
+                                         scratch_pool,
+                                         _("Checksum mismatch for '%s'"),
+                                         svn_dirent_local_style(ctx->relpath,
+                                                                scratch_pool));
+    }
+
   ctx->commit_ctx->open_batons--;
 
   return SVN_NO_ERROR;
@@ -2203,6 +2406,12 @@ svn_ra_serf__get_commit_editor(svn_ra_se
   editor->close_file = close_file;
   editor->close_edit = close_edit;
   editor->abort_edit = abort_edit;
+  /* Only install the callback that allows streaming PUT request bodies
+   * if the server has the necessary capability.  Otherwise, this will
+   * fallback to the default implementation using the temporary files.
+   * See default_editor.c:apply_textdelta_stream(). */
+  if (session->supports_put_result_checksum)
+    editor->apply_textdelta_stream = apply_textdelta_stream;
 
   *ret_editor = editor;
   *edit_baton = ctx;

Modified: subversion/branches/ra-git/subversion/libsvn_ra_serf/get_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_serf/get_file.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_serf/get_file.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_serf/get_file.c Wed Nov  7 12:30:06 2018
@@ -60,7 +60,7 @@ typedef struct stream_ctx_t {
   /* Have we read our response headers yet? */
   svn_boolean_t read_headers;
 
-  svn_boolean_t using_compression;
+  svn_ra_serf__session_t *session;
 
   /* This flag is set when our response is aborted before we reach the
    * end and we decide to requeue this request.
@@ -88,7 +88,7 @@ headers_fetch(serf_bucket_t *headers,
 {
   stream_ctx_t *fetch_ctx = baton;
 
-  if (fetch_ctx->using_compression)
+  if (fetch_ctx->session->using_compression != svn_tristate_false)
     {
       serf_bucket_headers_setn(headers, "Accept-Encoding", "gzip");
     }
@@ -396,7 +396,7 @@ svn_ra_serf__get_file(svn_ra_session_t *
           /* Create the fetch context. */
           stream_ctx = apr_pcalloc(scratch_pool, sizeof(*stream_ctx));
           stream_ctx->result_stream = stream;
-          stream_ctx->using_compression = session->using_compression;
+          stream_ctx->session = session;
 
           handler = svn_ra_serf__create_handler(session, scratch_pool);
 

Modified: subversion/branches/ra-git/subversion/libsvn_ra_serf/merge.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_serf/merge.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_serf/merge.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_serf/merge.c Wed Nov  7 12:30:06 2018
@@ -79,6 +79,7 @@ typedef struct merge_context_t
 
   apr_hash_t *lock_tokens;
   svn_boolean_t keep_locks;
+  svn_boolean_t disable_merge_response;
 
   const char *merge_resource_url; /* URL of resource to be merged. */
   const char *merge_url; /* URL at which the MERGE request is aimed. */
@@ -275,12 +276,17 @@ setup_merge_headers(serf_bucket_t *heade
                     apr_pool_t *scratch_pool)
 {
   merge_context_t *ctx = baton;
+  apr_array_header_t *vals = apr_array_make(scratch_pool, 2,
+                                            sizeof(const char *));
 
   if (!ctx->keep_locks)
-    {
-      serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER,
-                              SVN_DAV_OPTION_RELEASE_LOCKS);
-    }
+    APR_ARRAY_PUSH(vals, const char *) = SVN_DAV_OPTION_RELEASE_LOCKS;
+  if (ctx->disable_merge_response)
+    APR_ARRAY_PUSH(vals, const char *) = SVN_DAV_OPTION_NO_MERGE_RESPONSE;
+
+  if (vals->nelts > 0)
+    serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER,
+                            svn_cstring_join2(vals, " ", FALSE, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -412,6 +418,13 @@ svn_ra_serf__run_merge(const svn_commit_
   merge_ctx->lock_tokens = lock_tokens;
   merge_ctx->keep_locks = keep_locks;
 
+  /* We don't need the full merge response when working over HTTPv2.
+   * Over HTTPv1, this response is only required with a non-null
+   * svn_ra_push_wc_prop_func_t callback. */
+  merge_ctx->disable_merge_response =
+    SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session) ||
+    session->wc_callbacks->push_wc_prop == NULL;
+
   merge_ctx->commit_info = svn_create_commit_info(result_pool);
 
   merge_ctx->merge_url = session->session_url.path;

Modified: subversion/branches/ra-git/subversion/libsvn_ra_serf/options.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_serf/options.c?rev=1846002&r1=1846001&r2=1846002&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_serf/options.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_serf/options.c Wed Nov  7 12:30:06 2018
@@ -71,6 +71,9 @@ typedef struct options_context_t {
   svn_ra_serf__response_handler_t inner_handler;
   void *inner_baton;
 
+  /* Have we received any DAV headers at all? */
+  svn_boolean_t received_dav_header;
+
   const char *activity_collection;
   svn_revnum_t youngest_rev;
 
@@ -165,6 +168,8 @@ capabilities_headers_iterator_callback(v
       apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE,
                                                    opt_ctx->pool);
 
+      opt_ctx->received_dav_header = TRUE;
+
       /* Right now we only have a few capabilities to detect, so just
          seek for them directly.  This could be written slightly more
          efficiently, but that wouldn't be worth it until we have many
@@ -232,6 +237,20 @@ capabilities_headers_iterator_callback(v
              advertise this capability (Subversion 1.10 and greater). */
           session->supports_svndiff1 = TRUE;
         }
+      if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_LIST, vals))
+        {
+          svn_hash_sets(session->capabilities,
+                        SVN_RA_CAPABILITY_LIST, capability_yes);
+        }
+      if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_SVNDIFF2, vals))
+        {
+          /* Same for svndiff2. */
+          session->supports_svndiff2 = TRUE;
+        }
+      if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_PUT_RESULT_CHECKSUM, vals))
+        {
+          session->supports_put_result_checksum = TRUE;
+        }
     }
 
   /* SVN-specific headers -- if present, server supports HTTP protocol v2 */
@@ -249,21 +268,6 @@ capabilities_headers_iterator_callback(v
           apr_hash_set(session->supported_posts, "create-txn", 10, (void *)1);
         }
 
-      /* Use compressed svndiff1 format for servers that speak HTTPv2,
-         in addition to servers that send SVN_DAV_NS_DAV_SVN_SVNDIFF1.
-
-         Apache HTTPd + mod_dav_svn servers support svndiff1, beginning
-         from Subversion 1.4, but they do not advertise this capability.
-         Compressing data can have a noticeable impact if the connection
-         is slow, and we want to use it even for existing servers, so we
-         send svndiff1 data to every HTTPv2 server (Subversion 1.7 and
-         greater).
-
-         The reasoning behind enabling it with HTTPv2 is that if the user
-         is stuck with the old Subversion's HTTPv1 protocol, she probably
-         doesn't really care about performance. */
-      session->supports_svndiff1 = TRUE;
-
       if (svn_cstring_casecmp(key, SVN_DAV_ROOT_URI_HEADER) == 0)
         {
           session->repos_root = session->session_url;
@@ -371,6 +375,7 @@ options_response_handler(serf_request_t
     {
       svn_ra_serf__session_t *session = opt_ctx->session;
       serf_bucket_t *hdrs = serf_bucket_response_get_headers(response);
+      serf_connection_t *conn;
 
       /* Start out assuming all capabilities are unsupported. */
       svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY,
@@ -389,17 +394,36 @@ options_response_handler(serf_request_t
                     capability_no);
       svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
                     capability_no);
+      svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_LIST,
+                    capability_no);
 
       /* Then see which ones we can discover. */
       serf_bucket_headers_do(hdrs, capabilities_headers_iterator_callback,
                              opt_ctx);
 
+      /* Bail out early if we're not talking to a DAV server.
+         Note that this check is only valid if we've received a success
+         response; redirects and errors don't count. */
+      if (opt_ctx->handler->sline.code >= 200
+          && opt_ctx->handler->sline.code < 300
+          && !opt_ctx->received_dav_header)
+        {
+          return svn_error_createf
+            (SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
+             _("The server at '%s' does not support the HTTP/DAV protocol"),
+             session->session_url_str);
+        }
+
       /* Assume mergeinfo capability unsupported, if didn't receive information
          about server or repository mergeinfo capability. */
       if (!svn_hash_gets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO))
         svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
                       capability_no);
 
+      /* Remember our latency. */
+      conn = serf_request_get_conn(request);
+      session->conn_latency = serf_connection_get_latency(conn);
+
       opt_ctx->headers_processed = TRUE;
     }