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 [5/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_diff/diff3.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_diff/diff3.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_diff/diff3.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_diff/diff3.c Tue Oct 11 09:11:50 2016
@@ -29,6 +29,7 @@
 #include "svn_pools.h"
 #include "svn_error.h"
 #include "svn_diff.h"
+#include "svn_sorts.h"
 #include "svn_types.h"
 
 #include "diff.h"
@@ -324,6 +325,7 @@ svn_diff_diff3_2(svn_diff_t **diff,
   /* Produce a merged diff */
   {
     svn_diff_t **diff_ref = diff;
+    svn_diff_t *diff_last = NULL;
 
     apr_off_t original_start = 1;
     apr_off_t modified_start = 1;
@@ -433,6 +435,7 @@ svn_diff_diff3_2(svn_diff_t **diff,
 
         if (is_modified || is_latest)
           {
+            svn_boolean_t add_diff = TRUE;
             modified_length = modified_sync - modified_start;
             latest_length = latest_sync - latest_start;
 
@@ -453,17 +456,41 @@ svn_diff_diff3_2(svn_diff_t **diff,
                                            &position_list[2],
                                            num_tokens,
                                            pool);
+                /* add_diff = TRUE */
               }
-            else if (is_modified)
+            else if (is_modified
+                     && (!diff_last
+                         || diff_last->type != svn_diff__type_diff_latest))
               {
                 (*diff_ref)->type = svn_diff__type_diff_modified;
+                /* add_diff = TRUE */
               }
-            else
+            else if (is_latest
+                     && (!diff_last
+                         || diff_last->type != svn_diff__type_diff_modified))
               {
                 (*diff_ref)->type = svn_diff__type_diff_latest;
+                /* add_diff = TRUE */
               }
+            else
+              {
+                /* We have a latest and a modified region that touch each other,
+                   but not directly change the same location. Create a single
+                   conflict region to properly mark a conflict, and to ease
+                   resolving. */
+                diff_last->type = svn_diff__type_conflict;
+                diff_last->original_length += (*diff_ref)->original_length;
+                diff_last->modified_length += (*diff_ref)->modified_length;
+                diff_last->latest_length += (*diff_ref)->latest_length;
 
-            diff_ref = &(*diff_ref)->next;
+                add_diff = FALSE;
+              }
+
+            if (add_diff)
+              {
+                diff_last = *diff_ref;
+                diff_ref = &(*diff_ref)->next;
+              }
           }
 
         /* Detect EOF */
@@ -474,21 +501,24 @@ svn_diff_diff3_2(svn_diff_t **diff,
                           - (original_sync - lcs_om->position[0]->offset);
         latest_length = lcs_ol->length
                         - (original_sync - lcs_ol->position[0]->offset);
-        common_length = modified_length < latest_length
-                        ? modified_length : latest_length;
+        common_length = MIN(modified_length, latest_length);
 
-        (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
+        if (common_length > 0)
+          {
+            (*diff_ref) = apr_palloc(pool, sizeof(**diff_ref));
 
-        (*diff_ref)->type = svn_diff__type_common;
-        (*diff_ref)->original_start = original_sync - 1;
-        (*diff_ref)->original_length = common_length;
-        (*diff_ref)->modified_start = modified_sync - 1;
-        (*diff_ref)->modified_length = common_length;
-        (*diff_ref)->latest_start = latest_sync - 1;
-        (*diff_ref)->latest_length = common_length;
-        (*diff_ref)->resolved_diff = NULL;
+            (*diff_ref)->type = svn_diff__type_common;
+            (*diff_ref)->original_start = original_sync - 1;
+            (*diff_ref)->original_length = common_length;
+            (*diff_ref)->modified_start = modified_sync - 1;
+            (*diff_ref)->modified_length = common_length;
+            (*diff_ref)->latest_start = latest_sync - 1;
+            (*diff_ref)->latest_length = common_length;
+            (*diff_ref)->resolved_diff = NULL;
 
-        diff_ref = &(*diff_ref)->next;
+            diff_last = *diff_ref;
+            diff_ref = &(*diff_ref)->next;
+          }
 
         /* Set the new offsets */
         original_start = original_sync + common_length;

Modified: subversion/branches/ra-git/subversion/libsvn_diff/parse-diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_diff/parse-diff.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_diff/parse-diff.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_diff/parse-diff.c Tue Oct 11 09:11:50 2016
@@ -89,6 +89,10 @@ struct svn_diff_hunk_t {
   /* Did we see a 'file does not end with eol' marker in this hunk? */
   svn_boolean_t original_no_final_eol;
   svn_boolean_t modified_no_final_eol;
+
+  /* Fuzz penalty, triggered by bad patch targets */
+  svn_linenum_t original_fuzz;
+  svn_linenum_t modified_fuzz;
 };
 
 struct svn_diff_binary_patch_t {
@@ -122,7 +126,7 @@ add_or_delete_single_line(svn_diff_hunk_
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool)
 {
-  svn_diff_hunk_t *hunk = apr_palloc(result_pool, sizeof(*hunk));
+  svn_diff_hunk_t *hunk = apr_pcalloc(result_pool, sizeof(*hunk));
   static const char *hunk_header[] = { "@@ -1 +0,0 @@\n", "@@ -0,0 +1 @@\n" };
   const apr_size_t header_len = strlen(hunk_header[add]);
   const apr_size_t len = strlen(line);
@@ -285,6 +289,12 @@ svn_diff_hunk_get_trailing_context(const
   return hunk->trailing_context;
 }
 
+svn_linenum_t
+svn_diff_hunk__get_fuzz_penalty(const svn_diff_hunk_t *hunk)
+{
+  return hunk->patch->reverse ? hunk->original_fuzz : hunk->modified_fuzz;
+}
+
 /* Baton for the base85 stream implementation */
 struct base85_baton_t
 {
@@ -1183,24 +1193,38 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
             }
 
           c = line->data[0];
-          if (original_lines > 0 && modified_lines > 0 &&
-              ((c == ' ')
+          if (c == ' '
+              || ((original_lines > 0 && modified_lines > 0)
+                  && ( 
                /* Tolerate chopped leading spaces on empty lines. */
-               || (! eof && line->len == 0)
+                      (! eof && line->len == 0)
                /* Maybe tolerate chopped leading spaces on non-empty lines. */
-               || (ignore_whitespace && c != del && c != add)))
+                      || (ignore_whitespace && c != del && c != add))))
             {
               /* It's a "context" line in the hunk. */
               hunk_seen = TRUE;
-              original_lines--;
-              modified_lines--;
+              if (original_lines > 0)
+                original_lines--;
+              else
+                {
+                  (*hunk)->original_length++;
+                  (*hunk)->original_fuzz++;
+                }
+              if (modified_lines > 0)
+                modified_lines--;
+              else
+                {
+                  (*hunk)->modified_length++;
+                  (*hunk)->modified_fuzz++;
+                }
               if (changed_line_seen)
                 trailing_context++;
               else
                 leading_context++;
               last_line_type = context_line;
             }
-          else if (original_lines > 0 && c == del)
+          else if (c == del
+                   && (original_lines > 0 || line->data[1] != del))
             {
               /* It's a "deleted" line in the hunk. */
               hunk_seen = TRUE;
@@ -1211,10 +1235,17 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
               if (trailing_context > 0)
                 trailing_context = 0;
 
-              original_lines--;
+              if (original_lines > 0)
+                original_lines--;
+              else
+                {
+                  (*hunk)->original_length++;
+                  (*hunk)->original_fuzz++;
+                }
               last_line_type = original_line;
             }
-          else if (modified_lines > 0 && c == add)
+          else if (c == add
+                   && (modified_lines > 0 || line->data[1] != add))
             {
               /* It's an "added" line in the hunk. */
               hunk_seen = TRUE;
@@ -1225,7 +1256,13 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
               if (trailing_context > 0)
                 trailing_context = 0;
 
-              modified_lines--;
+              if (modified_lines > 0)
+                modified_lines--;
+              else
+                {
+                  (*hunk)->modified_length++;
+                  (*hunk)->modified_fuzz++;
+                }
               last_line_type = modified_line;
             }
           else
@@ -1241,7 +1278,6 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
                    * after the hunk text. */
                   end = last_line;
                 }
-
               if (original_end == 0)
                 original_end = end;
               if (modified_end == 0)
@@ -1318,6 +1354,21 @@ parse_next_hunk(svn_diff_hunk_t **hunk,
 
   if (hunk_seen && start < end)
     {
+      /* Did we get the number of context lines announced in the header?
+
+         If not... let's limit the number from the header to what we
+         actually have, and apply a fuzz penalty */
+      if (original_lines)
+        {
+          (*hunk)->original_length -= original_lines;
+          (*hunk)->original_fuzz += original_lines;
+        }
+      if (modified_lines)
+        {
+          (*hunk)->modified_length -= modified_lines;
+          (*hunk)->modified_fuzz += modified_lines;
+        }
+
       (*hunk)->patch = patch;
       (*hunk)->apr_file = apr_file;
       (*hunk)->leading_context = leading_context;
@@ -2224,11 +2275,14 @@ svn_diff_parse_next_patch(svn_patch_t **
             patch->operation = svn_diff_op_added;
             break;
 
-          /* ### case svn_diff_op_copied:
-             ### case svn_diff_op_moved:*/
-
           case svn_diff_op_modified:
-            break; /* Stays modify */
+            break; /* Stays modified. */
+
+          case svn_diff_op_copied:
+          case svn_diff_op_moved:
+            break; /* Stays copied or moved, just in the other direction. */
+          case svn_diff_op_unchanged:
+            break; /* Stays unchanged, of course. */
         }
 
       ts_tmp = patch->old_executable_bit;

Modified: subversion/branches/ra-git/subversion/libsvn_fs/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs/deprecated.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs/deprecated.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs/deprecated.c Tue Oct 11 09:11:50 2016
@@ -26,6 +26,7 @@
    deprecated functions in this file. */
 #define SVN_DEPRECATED
 
+#include <apr_md5.h>
 #include "svn_fs.h"
 
 
@@ -144,6 +145,51 @@ svn_fs_get_mergeinfo(svn_mergeinfo_catal
 }
 
 svn_error_t *
+svn_fs_paths_changed(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
+                     apr_pool_t *pool)
+{
+  apr_hash_t *changed_paths_new_structs;
+  apr_hash_index_t *hi;
+
+  SVN_ERR(svn_fs_paths_changed2(&changed_paths_new_structs, root, pool));
+  *changed_paths_p = apr_hash_make(pool);
+  for (hi = apr_hash_first(pool, changed_paths_new_structs);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const void *vkey;
+      apr_ssize_t klen;
+      void *vval;
+      svn_fs_path_change2_t *val;
+      svn_fs_path_change_t *change;
+      apr_hash_this(hi, &vkey, &klen, &vval);
+      val = vval;
+      change = apr_palloc(pool, sizeof(*change));
+      change->node_rev_id = val->node_rev_id;
+      change->change_kind = val->change_kind;
+      change->text_mod = val->text_mod;
+      change->prop_mod = val->prop_mod;
+      apr_hash_set(*changed_paths_p, vkey, klen, change);
+    }
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_fs_file_md5_checksum(unsigned char digest[],
+                         svn_fs_root_t *root,
+                         const char *path,
+                         apr_pool_t *pool)
+{
+  svn_checksum_t *md5sum;
+
+  SVN_ERR(svn_fs_file_checksum(&md5sum, svn_checksum_md5, root, path, TRUE,
+                               pool));
+  memcpy(digest, md5sum->digest, APR_MD5_DIGESTSIZE);
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_fs_history_prev(svn_fs_history_t **prev_history_p,
                     svn_fs_history_t *history, svn_boolean_t cross_copies,
                     apr_pool_t *pool)

Modified: subversion/branches/ra-git/subversion/libsvn_fs/fs-loader.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs/fs-loader.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs/fs-loader.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs/fs-loader.c Tue Oct 11 09:11:50 2016
@@ -26,7 +26,6 @@
 #include <apr.h>
 #include <apr_atomic.h>
 #include <apr_hash.h>
-#include <apr_md5.h>
 #include <apr_uuid.h>
 #include <apr_strings.h>
 
@@ -46,6 +45,7 @@
 #include "private/svn_atomic.h"
 #include "private/svn_fs_private.h"
 #include "private/svn_fs_util.h"
+#include "private/svn_fspath.h"
 #include "private/svn_utf_private.h"
 #include "private/svn_mutex.h"
 #include "private/svn_subr_private.h"
@@ -60,6 +60,22 @@
 
 #define FS_TYPE_FILENAME "fs-type"
 
+/* If a FS backend does not implement the PATHS_CHANGED vtable function,
+   it will get emulated.  However, if this macro is defined to non-null
+   then the API will always be emulated when feasible, i.e. the calls
+   get "re-directed" to the new API implementation. */
+#ifndef SVN_FS_EMULATE_PATHS_CHANGED
+#define SVN_FS_EMULATE_PATHS_CHANGED FALSE
+#endif
+
+/* If a FS backend does not implement the REPORT_CHANGES vtable function,
+   it will get emulated.  However, if this macro is defined to non-null
+   then the API will always be emulated when feasible, i.e. the calls
+   get "re-directed" to the old API implementation. */
+#ifndef SVN_FS_EMULATE_REPORT_CHANGES
+#define SVN_FS_EMULATE_REPORT_CHANGES FALSE
+#endif
+
 /* A pool common to all FS objects.  See the documentation on the
    open/create functions in fs-loader.h and for svn_fs_initialize(). */
 static apr_pool_t *common_pool = NULL;
@@ -1052,40 +1068,163 @@ svn_fs_revision_root_revision(svn_fs_roo
 }
 
 svn_error_t *
+svn_fs_path_change_get(svn_fs_path_change3_t **change,
+                       svn_fs_path_change_iterator_t *iterator)
+{
+  return iterator->vtable->get(change, iterator);
+}
+
+svn_error_t *
 svn_fs_paths_changed2(apr_hash_t **changed_paths_p,
                       svn_fs_root_t *root,
                       apr_pool_t *pool)
 {
-  return root->vtable->paths_changed(changed_paths_p, root, pool);
+  svn_boolean_t emulate =    !root->vtable->paths_changed
+                          || SVN_FS_EMULATE_PATHS_CHANGED;
+
+  if (emulate)
+    {
+      apr_pool_t *scratch_pool = svn_pool_create(pool);
+      apr_hash_t *changes = svn_hash__make(pool);
+
+      svn_fs_path_change_iterator_t *iterator;
+      svn_fs_path_change3_t *change;
+
+      SVN_ERR(svn_fs_paths_changed3(&iterator, root, scratch_pool,
+                                    scratch_pool));
+
+      SVN_ERR(svn_fs_path_change_get(&change, iterator));
+      while (change)
+        {
+          svn_fs_path_change2_t *copy;
+          const svn_fs_id_t *id_copy;
+          const char *change_path = change->path.data;
+          svn_fs_root_t *change_root = root;
+
+          /* Copy CHANGE to old API struct. */
+          if (change->change_kind == svn_fs_path_change_delete)
+            SVN_ERR(svn_fs__get_deleted_node(&change_root, &change_path,
+                                             change_root, change_path,
+                                             scratch_pool, scratch_pool));
+
+          SVN_ERR(svn_fs_node_id(&id_copy, change_root, change_path, pool));
+
+          copy = svn_fs_path_change2_create(id_copy, change->change_kind,
+                                            pool);
+          copy->copyfrom_known = change->copyfrom_known;
+          if (   copy->copyfrom_known
+              && SVN_IS_VALID_REVNUM(change->copyfrom_rev))
+            {
+              copy->copyfrom_rev = change->copyfrom_rev;
+              copy->copyfrom_path = apr_pstrdup(pool, change->copyfrom_path);
+            }
+          copy->mergeinfo_mod = change->mergeinfo_mod;
+          copy->node_kind = change->node_kind;
+          copy->prop_mod = change->prop_mod;
+          copy->text_mod = change->text_mod;
+
+          svn_hash_sets(changes, apr_pstrmemdup(pool, change->path.data,
+                                                change->path.len), copy);
+
+          /* Next change. */
+          SVN_ERR(svn_fs_path_change_get(&change, iterator));
+        }
+      svn_pool_destroy(scratch_pool);
+
+      *changed_paths_p = changes;
+    }
+  else
+    {
+      SVN_ERR(root->vtable->paths_changed(changed_paths_p, root, pool));
+    }
+
+  return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_fs_paths_changed(apr_hash_t **changed_paths_p, svn_fs_root_t *root,
-                     apr_pool_t *pool)
+/* Implement svn_fs_path_change_iterator_t on top of svn_fs_paths_changed2. */
+
+/* The iterator builds upon a hash iterator, which in turn operates on the
+   full prefetched changes list. */
+typedef struct fsap_iterator_data_t
 {
-  apr_hash_t *changed_paths_new_structs;
   apr_hash_index_t *hi;
 
-  SVN_ERR(svn_fs_paths_changed2(&changed_paths_new_structs, root, pool));
-  *changed_paths_p = apr_hash_make(pool);
-  for (hi = apr_hash_first(pool, changed_paths_new_structs);
-       hi;
-       hi = apr_hash_next(hi))
-    {
-      const void *vkey;
-      apr_ssize_t klen;
-      void *vval;
-      svn_fs_path_change2_t *val;
-      svn_fs_path_change_t *change;
-      apr_hash_this(hi, &vkey, &klen, &vval);
-      val = vval;
-      change = apr_palloc(pool, sizeof(*change));
-      change->node_rev_id = val->node_rev_id;
-      change->change_kind = val->change_kind;
-      change->text_mod = val->text_mod;
-      change->prop_mod = val->prop_mod;
-      apr_hash_set(*changed_paths_p, vkey, klen, change);
+  /* For efficicency such that we don't need to dynamically allocate
+     yet another copy of that data. */
+  svn_fs_path_change3_t change;
+} fsap_iterator_data_t;
+
+static svn_error_t *
+changes_iterator_get(svn_fs_path_change3_t **change,
+                     svn_fs_path_change_iterator_t *iterator)
+{
+  fsap_iterator_data_t *data = iterator->fsap_data;
+
+  if (data->hi)
+    {
+      const char *path = apr_hash_this_key(data->hi);
+      svn_fs_path_change2_t *entry = apr_hash_this_val(data->hi);
+
+      data->change.path.data = path;
+      data->change.path.len = apr_hash_this_key_len(data->hi);
+      data->change.change_kind = entry->change_kind;
+      data->change.node_kind = entry->node_kind;
+      data->change.text_mod = entry->text_mod;
+      data->change.prop_mod = entry->prop_mod;
+      data->change.mergeinfo_mod = entry->mergeinfo_mod;
+      data->change.copyfrom_known = entry->copyfrom_known;
+      data->change.copyfrom_rev = entry->copyfrom_rev;
+      data->change.copyfrom_path = entry->copyfrom_path;
+
+      *change = &data->change;
+      data->hi = apr_hash_next(data->hi);
+    }
+  else
+    {
+      *change = NULL;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+static changes_iterator_vtable_t iterator_vtable =
+{
+  changes_iterator_get
+};
+
+svn_error_t *
+svn_fs_paths_changed3(svn_fs_path_change_iterator_t **iterator,
+                      svn_fs_root_t *root,
+                      apr_pool_t *result_pool,
+                      apr_pool_t *scratch_pool)
+{
+  svn_boolean_t emulate =    !root->vtable->report_changes
+                          || (   SVN_FS_EMULATE_REPORT_CHANGES
+                              && root->vtable->paths_changed);
+
+  if (emulate)
+    {
+      svn_fs_path_change_iterator_t *result;
+      fsap_iterator_data_t *data;
+
+      apr_hash_t *changes;
+      SVN_ERR(root->vtable->paths_changed(&changes, root, result_pool));
+
+      data = apr_pcalloc(result_pool, sizeof(*data));
+      data->hi = apr_hash_first(result_pool, changes);
+
+      result = apr_pcalloc(result_pool, sizeof(*result));
+      result->fsap_data = data;
+      result->vtable = &iterator_vtable;
+
+      *iterator = result;
     }
+  else
+    {
+      SVN_ERR(root->vtable->report_changes(iterator, root, result_pool,
+                                           scratch_pool));
+    }
+
   return SVN_NO_ERROR;
 }
 
@@ -1374,36 +1513,17 @@ svn_fs_file_checksum(svn_checksum_t **ch
 
   if (force && (*checksum == NULL || (*checksum)->kind != kind))
     {
-      svn_stream_t *contents, *checksum_contents;
+      svn_stream_t *contents;
 
       SVN_ERR(svn_fs_file_contents(&contents, root, path, pool));
-      checksum_contents = svn_stream_checksummed2(contents, checksum, NULL,
-                                                  kind, TRUE, pool);
-
-      /* This will force a read of any remaining data (which is all of it in
-         this case) and dump the checksum into checksum->digest. */
-      SVN_ERR(svn_stream_close(checksum_contents));
+      SVN_ERR(svn_stream_contents_checksum(checksum, contents, kind,
+                                           pool, pool));
     }
 
   return SVN_NO_ERROR;
 }
 
 svn_error_t *
-svn_fs_file_md5_checksum(unsigned char digest[],
-                         svn_fs_root_t *root,
-                         const char *path,
-                         apr_pool_t *pool)
-{
-  svn_checksum_t *md5sum;
-
-  SVN_ERR(svn_fs_file_checksum(&md5sum, svn_checksum_md5, root, path, TRUE,
-                               pool));
-  memcpy(digest, md5sum->digest, APR_MD5_DIGESTSIZE);
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
 svn_fs_file_contents(svn_stream_t **contents, svn_fs_root_t *root,
                      const char *path, apr_pool_t *pool)
 {
@@ -1597,6 +1717,64 @@ svn_fs_get_file_delta_stream(svn_txdelta
 }
 
 svn_error_t *
+svn_fs__get_deleted_node(svn_fs_root_t **node_root,
+                         const char **node_path,
+                         svn_fs_root_t *root,
+                         const char *path,
+                         apr_pool_t *result_pool,
+                         apr_pool_t *scratch_pool)
+{
+  const char *parent_path, *name;
+  svn_fs_root_t *copy_root;
+  const char *copy_path;
+
+  /* History traversal does not work with transaction roots.
+   * Therefore, do it "by hand". */
+
+  /* If the parent got copied in ROOT, PATH got copied with it.
+   * Otherwise, we will find the node at PATH in the revision prior to ROOT.
+   */
+  svn_fspath__split(&parent_path, &name, path, scratch_pool);
+  SVN_ERR(svn_fs_closest_copy(&copy_root, &copy_path, root, parent_path,
+                              scratch_pool));
+
+  /* Copied in ROOT? */
+  if (   copy_root
+      && (   svn_fs_revision_root_revision(copy_root)
+          == svn_fs_revision_root_revision(root)))
+    {
+      svn_revnum_t copyfrom_rev;
+      const char *copyfrom_path;
+      const char *rel_path;
+      SVN_ERR(svn_fs_copied_from(&copyfrom_rev, &copyfrom_path,
+                                 copy_root, copy_path, scratch_pool));
+
+      SVN_ERR(svn_fs_revision_root(node_root, svn_fs_root_fs(root),
+                                   copyfrom_rev, result_pool));
+      rel_path = svn_fspath__skip_ancestor(copy_path, path);
+      *node_path = svn_fspath__join(copyfrom_path, rel_path, result_pool);
+    }
+  else
+    {
+      svn_revnum_t revision;
+      svn_revnum_t previous_rev;
+
+      /* Determine the latest revision before ROOT. */
+      revision = svn_fs_revision_root_revision(root);
+      if (SVN_IS_VALID_REVNUM(revision))
+        previous_rev = revision - 1;
+      else
+        previous_rev = svn_fs_txn_root_base_revision(root);
+
+      SVN_ERR(svn_fs_revision_root(node_root, svn_fs_root_fs(root),
+                                   previous_rev, result_pool));
+      *node_path = apr_pstrdup(result_pool, path);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_fs_get_uuid(svn_fs_t *fs, const char **uuid, apr_pool_t *pool)
 {
   /* If you change this, consider changing svn_fs__identifier(). */
@@ -1963,6 +2141,28 @@ svn_fs_path_change2_create(const svn_fs_
   return svn_fs__path_change_create_internal(node_rev_id, change_kind, pool);
 }
 
+svn_fs_path_change3_t *
+svn_fs_path_change3_create(svn_fs_path_change_kind_t change_kind,
+                           apr_pool_t *result_pool)
+{
+  return svn_fs__path_change_create_internal2(change_kind, result_pool);
+}
+
+svn_fs_path_change3_t *
+svn_fs_path_change3_dup(svn_fs_path_change3_t *change,
+                        apr_pool_t *result_pool)
+{
+  svn_fs_path_change3_t *copy = apr_pmemdup(result_pool, change,
+                                            sizeof(*copy));
+
+  copy->path.data = apr_pstrmemdup(result_pool, copy->path.data,
+                                   copy->path.len);
+  if (copy->copyfrom_path)
+    copy->copyfrom_path = apr_pstrdup(result_pool, change->copyfrom_path);
+
+  return copy;
+}
+
 /* Return the library version number. */
 const svn_version_t *
 svn_fs_version(void)

Modified: subversion/branches/ra-git/subversion/libsvn_fs/fs-loader.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs/fs-loader.h?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs/fs-loader.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs/fs-loader.h Tue Oct 11 09:11:50 2016
@@ -302,6 +302,10 @@ typedef struct root_vtable_t
   svn_error_t *(*paths_changed)(apr_hash_t **changed_paths_p,
                                 svn_fs_root_t *root,
                                 apr_pool_t *pool);
+  svn_error_t *(*report_changes)(svn_fs_path_change_iterator_t **iterator,
+                                 svn_fs_root_t *root,
+                                 apr_pool_t *result_pool,
+                                 apr_pool_t *scratch_pool);
 
   /* Generic node operations */
   svn_error_t *(*check_path)(svn_node_kind_t *kind_p, svn_fs_root_t *root,
@@ -427,6 +431,13 @@ typedef struct root_vtable_t
 } root_vtable_t;
 
 
+typedef struct changes_iterator_vtable_t
+{
+  svn_error_t *(*get)(svn_fs_path_change3_t **change,
+                      svn_fs_path_change_iterator_t *iterator);
+} changes_iterator_vtable_t;
+
+
 typedef struct history_vtable_t
 {
   svn_error_t *(*prev)(svn_fs_history_t **prev_history_p,
@@ -529,6 +540,12 @@ struct svn_fs_root_t
   void *fsap_data;
 };
 
+struct svn_fs_path_change_iterator_t
+{
+  /* FSAP-specific vtable and private data */
+  const changes_iterator_vtable_t *vtable;
+  void *fsap_data;
+};
 
 struct svn_fs_history_t
 {

Modified: subversion/branches/ra-git/subversion/libsvn_fs_base/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_base/tree.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_base/tree.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_base/tree.c Tue Oct 11 09:11:50 2016
@@ -2661,7 +2661,7 @@ txn_body_commit(void *baton, trail_t *tr
 
   svn_revnum_t youngest_rev;
   const svn_fs_id_t *y_rev_root_id;
-  dag_node_t *txn_base_root_node;
+  dag_node_t *txn_base_root_node, *txn_root_node;
 
   /* Getting the youngest revision locks the revisions table until
      this trail is done. */
@@ -2694,6 +2694,19 @@ txn_body_commit(void *baton, trail_t *tr
      discovered locks. */
   SVN_ERR(verify_locks(txn_name, trail, trail->pool));
 
+  /* Ensure every txn has a mutable root as then the new revision will
+     have a distinct root node-revision-id.  This is necessary as
+     future transactions use the root node-revision-id as a proxy for
+     the transaction base revision. */
+  SVN_ERR(svn_fs_base__dag_txn_root(&txn_root_node, fs, txn_name,
+                                    trail, trail->pool));
+  if (!svn_fs_base__dag_check_mutable(txn_root_node, txn->id))
+    {
+      dag_node_t *clone;
+      SVN_ERR(svn_fs_base__dag_clone_root(&clone, fs, txn->id,
+                                          trail, trail->pool));
+    }
+
   /* Else, commit the txn. */
   return svn_fs_base__dag_commit_txn(&(args->new_rev), txn, trail,
                                      trail->pool);
@@ -5493,6 +5506,7 @@ base_get_mergeinfo(svn_mergeinfo_catalog
 
 static root_vtable_t root_vtable = {
   base_paths_changed,
+  NULL,
   base_check_path,
   base_node_history,
   base_node_id,

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/cached_data.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/cached_data.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/cached_data.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/cached_data.c Tue Oct 11 09:11:50 2016
@@ -2042,7 +2042,7 @@ skip_contents(struct rep_read_baton *bat
   else if (len > 0)
     {
       /* Simply drain LEN bytes from the window stream. */
-      apr_pool_t *subpool = subpool = svn_pool_create(baton->pool);
+      apr_pool_t *subpool = svn_pool_create(baton->pool);
       char *buffer = apr_palloc(subpool, SVN__STREAM_CHUNK_SIZE);
 
       while (len > 0 && !err)
@@ -2453,8 +2453,11 @@ read_dir_entries(apr_array_header_t **en
       char *str;
 
       svn_pool_clear(iterpool);
-      SVN_ERR(svn_hash__read_entry(&entry, stream, terminator,
-                                   incremental, iterpool));
+      SVN_ERR_W(svn_hash__read_entry(&entry, stream, terminator,
+                                     incremental, iterpool),
+                apr_psprintf(iterpool,
+                             _("Directory representation corrupt in '%s'"),
+                             svn_fs_fs__id_unparse(id, scratch_pool)->data));
 
       /* End of directory? */
       if (entry.key == NULL)
@@ -2715,8 +2718,12 @@ svn_fs_fs__rep_contents_dir(apr_array_he
   SVN_ERR(get_dir_contents(dir, fs, noderev, result_pool, scratch_pool));
   *entries_p = dir->entries;
 
-  /* Update the cache, if we are to use one. */
-  if (cache)
+  /* Update the cache, if we are to use one.
+   *
+   * Don't even attempt to serialize very large directories; it would cause
+   * an unnecessary memory allocation peak.  150 bytes/entry is about right.
+   */
+  if (cache && svn_cache__is_cachable(cache, 150 * dir->entries->nelts))
     SVN_ERR(svn_cache__set(cache, key, dir, scratch_pool));
 
   return SVN_NO_ERROR;
@@ -2740,6 +2747,7 @@ svn_fs_fs__rep_contents_dir_entry(svn_fs
                                   apr_pool_t *result_pool,
                                   apr_pool_t *scratch_pool)
 {
+  extract_dir_entry_baton_t baton;
   svn_boolean_t found = FALSE;
 
   /* find the cache we may use */
@@ -2749,8 +2757,6 @@ svn_fs_fs__rep_contents_dir_entry(svn_fs
                                          scratch_pool);
   if (cache)
     {
-      extract_dir_entry_baton_t baton;
-
       svn_filesize_t filesize;
       SVN_ERR(get_txn_dir_info(&filesize, fs, noderev, scratch_pool));
 
@@ -2767,7 +2773,7 @@ svn_fs_fs__rep_contents_dir_entry(svn_fs
     }
 
   /* fetch data from disk if we did not find it in the cache */
-  if (! found)
+  if (! found || baton.out_of_date)
     {
       svn_fs_dirent_t *entry;
       svn_fs_dirent_t *entry_copy = NULL;
@@ -2777,8 +2783,12 @@ svn_fs_fs__rep_contents_dir_entry(svn_fs
       SVN_ERR(get_dir_contents(&dir, fs, noderev, scratch_pool,
                                scratch_pool));
 
-      /* Update the cache, if we are to use one. */
-      if (cache)
+      /* Update the cache, if we are to use one.
+       *
+       * Don't even attempt to serialize very large directories; it would
+       * cause an unnecessary memory allocation peak.  150 bytes / entry is
+       * about right. */
+      if (cache && svn_cache__is_cachable(cache, 150 * dir.entries->nelts))
         SVN_ERR(svn_cache__set(cache, key, &dir, scratch_pool));
 
       /* find desired entry and return a copy in POOL, if found */
@@ -2874,23 +2884,42 @@ svn_fs_fs__get_proplist(apr_hash_t **pro
 }
 
 svn_error_t *
+svn_fs_fs__create_changes_context(svn_fs_fs__changes_context_t **context,
+                                  svn_fs_t *fs,
+                                  svn_revnum_t rev,
+                                  apr_pool_t *result_pool)
+{
+  svn_fs_fs__changes_context_t *result = apr_pcalloc(result_pool,
+                                                     sizeof(*result));
+  result->fs = fs;
+  result->revision = rev;
+  result->rev_file_pool = result_pool;
+
+  *context = result;
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_fs_fs__get_changes(apr_array_header_t **changes,
-                       svn_fs_t *fs,
-                       svn_revnum_t rev,
-                       apr_pool_t *result_pool)
+                       svn_fs_fs__changes_context_t *context,
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool)
 {
   apr_off_t item_index = SVN_FS_FS__ITEM_INDEX_CHANGES;
-  svn_fs_fs__revision_file_t *revision_file;
   svn_boolean_t found;
-  fs_fs_data_t *ffd = fs->fsap_data;
-  apr_pool_t *scratch_pool = svn_pool_create(result_pool);
+  fs_fs_data_t *ffd = context->fs->fsap_data;
+  svn_fs_fs__changes_list_t *changes_list;
+
+  pair_cache_key_t key;
+  key.revision = context->revision;
+  key.second = context->next;
 
   /* try cache lookup first */
 
   if (ffd->changes_cache)
     {
-      SVN_ERR(svn_cache__get((void **) changes, &found, ffd->changes_cache,
-                             &rev, result_pool));
+      SVN_ERR(svn_cache__get((void **)&changes_list, &found,
+                             ffd->changes_cache, &key, result_pool));
     }
   else
     {
@@ -2901,34 +2930,54 @@ svn_fs_fs__get_changes(apr_array_header_
     {
       /* read changes from revision file */
 
-      SVN_ERR(svn_fs_fs__ensure_revision_exists(rev, fs, scratch_pool));
-      SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&revision_file, fs, rev,
-                                               scratch_pool, scratch_pool));
-
-      if (use_block_read(fs))
-        {
-          /* 'block-read' will also provide us with the desired data */
-          SVN_ERR(block_read((void **)changes, fs,
-                             rev, SVN_FS_FS__ITEM_INDEX_CHANGES,
-                             revision_file, result_pool, scratch_pool));
+      if (!context->revision_file)
+        {
+          SVN_ERR(svn_fs_fs__ensure_revision_exists(context->revision,
+                                                    context->fs,
+                                                    scratch_pool));
+          SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&context->revision_file,
+                                                   context->fs,
+                                                   context->revision,
+                                                   context->rev_file_pool,
+                                                   scratch_pool));
+        }
+
+      if (use_block_read(context->fs))
+        {
+          /* 'block-read' will probably populate the cache with the data
+           * that we want.  However, we won't want to force it to process
+           * very large change lists as part of this prefetching mechanism.
+           * Those would be better handled by the iterative code below. */
+          SVN_ERR(block_read(NULL, context->fs,
+                             context->revision, SVN_FS_FS__ITEM_INDEX_CHANGES,
+                             context->revision_file, scratch_pool,
+                             scratch_pool));
+
+          /* This may succeed now ... */
+          SVN_ERR(svn_cache__get((void **)&changes_list, &found,
+                                 ffd->changes_cache, &key, result_pool));
         }
-      else
+
+      /* If we still have no data, read it here. */
+      if (!found)
         {
           apr_off_t changes_offset;
 
           /* Addressing is very different for old formats
            * (needs to read the revision trailer). */
-          if (svn_fs_fs__use_log_addressing(fs))
+          if (svn_fs_fs__use_log_addressing(context->fs))
             {
-              SVN_ERR(svn_fs_fs__item_offset(&changes_offset, fs,
-                                             revision_file, rev, NULL,
+              SVN_ERR(svn_fs_fs__item_offset(&changes_offset, context->fs,
+                                             context->revision_file,
+                                             context->revision, NULL,
                                              SVN_FS_FS__ITEM_INDEX_CHANGES,
                                              scratch_pool));
             }
           else
             {
               SVN_ERR(get_root_changes_offset(NULL, &changes_offset,
-                                              revision_file, fs, rev,
+                                              context->revision_file,
+                                              context->fs, context->revision,
                                               scratch_pool));
 
               /* This variable will be used for debug logging only. */
@@ -2936,35 +2985,58 @@ svn_fs_fs__get_changes(apr_array_header_
             }
 
           /* Actual reading and parsing are the same, though. */
-          SVN_ERR(aligned_seek(fs, revision_file->file, NULL, changes_offset,
+          SVN_ERR(aligned_seek(context->fs, context->revision_file->file,
+                               NULL, changes_offset + context->next_offset,
                                scratch_pool));
-          SVN_ERR(svn_fs_fs__read_changes(changes, revision_file->stream,
+
+          SVN_ERR(svn_fs_fs__read_changes(changes,
+                                          context->revision_file->stream,
+                                          SVN_FS_FS__CHANGES_BLOCK_SIZE,
                                           result_pool, scratch_pool));
 
+          /* Construct the info object for the entries block we just read. */
+          changes_list = apr_pcalloc(scratch_pool, sizeof(*changes_list));
+          SVN_ERR(svn_io_file_get_offset(&changes_list->end_offset,
+                                         context->revision_file->file,
+                                         scratch_pool));
+          changes_list->end_offset -= changes_offset;
+          changes_list->start_offset = context->next_offset;
+          changes_list->count = (*changes)->nelts;
+          changes_list->changes = (change_t **)(*changes)->elts;
+          changes_list->eol = changes_list->count < SVN_FS_FS__CHANGES_BLOCK_SIZE;
+
           /* cache for future reference */
 
           if (ffd->changes_cache)
-            {
-              /* Guesstimate for the size of the in-cache representation. */
-              apr_size_t estimated_size = (apr_size_t)250 * (*changes)->nelts;
-
-              /* Don't even serialize data that probably won't fit into the
-               * cache.  This often implies that either CHANGES is very
-               * large, memory is scarce or both.  Having a huge temporary
-               * copy would not be a good thing in either case. */
-              if (svn_cache__is_cachable(ffd->changes_cache, estimated_size))
-                SVN_ERR(svn_cache__set(ffd->changes_cache, &rev, *changes,
-                                       scratch_pool));
-            }
+            SVN_ERR(svn_cache__set(ffd->changes_cache, &key, changes_list,
+                                   scratch_pool));
         }
+    }
+
+  if (found)
+    {
+      /* Return the block as a "proper" APR array. */
+      (*changes) = apr_array_make(result_pool, 0, sizeof(void *));
+      (*changes)->elts = (char *)changes_list->changes;
+      (*changes)->nelts = changes_list->count;
+      (*changes)->nalloc = changes_list->count;
+    }
+
+  /* Where to look next - if there is more data. */
+  context->next += (*changes)->nelts;
+  context->next_offset = changes_list->end_offset;
+  context->eol = changes_list->eol;
 
-      SVN_ERR(svn_fs_fs__close_revision_file(revision_file));
+  /* Close the revision file after we read all data. */
+  if (context->eol && context->revision_file)
+    {
+      SVN_ERR(svn_fs_fs__close_revision_file(context->revision_file));
+      context->revision_file = NULL;
     }
 
-  SVN_ERR(dbg_log_access(fs, rev, item_index, *changes,
+  SVN_ERR(dbg_log_access(context->fs, context->revision, item_index, *changes,
                          SVN_FS_FS__ITEM_TYPE_CHANGES, scratch_pool));
 
-  svn_pool_destroy(scratch_pool);
   return SVN_NO_ERROR;
 }
 
@@ -3238,9 +3310,8 @@ read_rep_header(svn_fs_fs__rep_header_t
 
 /* Fetch the representation data (header, txdelta / plain windows)
  * addressed by ENTRY->ITEM in FS and cache it if caches are enabled.
- * Read the data from the already open FILE and the wrapping
- * STREAM object.  If MAX_OFFSET is not -1, don't read windows that start
- * at or beyond that offset.
+ * Read the data from REV_FILE.  If MAX_OFFSET is not -1, don't read
+ * windows that start at or beyond that offset.
  * Use SCRATCH_POOL for temporary allocations.
  */
 static svn_error_t *
@@ -3248,7 +3319,6 @@ block_read_contents(svn_fs_t *fs,
                     svn_fs_fs__revision_file_t *rev_file,
                     svn_fs_fs__p2l_entry_t* entry,
                     apr_off_t max_offset,
-                    apr_pool_t *result_pool,
                     apr_pool_t *scratch_pool)
 {
   pair_cache_key_t header_key = { 0 };
@@ -3258,9 +3328,9 @@ block_read_contents(svn_fs_t *fs,
   header_key.second = entry->item.number;
 
   SVN_ERR(read_rep_header(&rep_header, fs, rev_file->stream, &header_key,
-                          result_pool, scratch_pool));
+                          scratch_pool, scratch_pool));
   SVN_ERR(block_read_windows(rep_header, fs, rev_file, entry, max_offset,
-                             result_pool, scratch_pool));
+                             scratch_pool, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -3309,37 +3379,39 @@ read_item(svn_stream_t **stream,
                  _("Low-level checksum mismatch while reading\n"
                    "%s bytes of meta data at offset %s "
                    "for item %s in revision %ld"),
-                 apr_psprintf(pool, "%" APR_OFF_T_FMT, entry->size),
-                 apr_psprintf(pool, "%" APR_OFF_T_FMT, entry->offset),
+                 apr_off_t_toa(pool, entry->size),
+                 apr_off_t_toa(pool, entry->offset),
                  apr_psprintf(pool, "%" APR_UINT64_T_FMT, entry->item.number),
                  entry->item.revision);
 }
 
-/* If not already cached or if MUST_READ is set, read the changed paths
- * list addressed by ENTRY in FS and retúrn it in *CHANGES.  Cache the
- * result if caching is enabled.  Read the data from the already open
- * FILE and wrapping FILE_STREAM.  Use POOL for allocations.
+/* If not already cached, read the changed paths list addressed by ENTRY in
+ * FS and cache it if it has no more than SVN_FS_FS__CHANGES_BLOCK_SIZE
+ * entries and caching is enabled.  Read the data from REV_FILE.
+ * Allocate temporaries in SCRATCH_POOL.
  */
 static svn_error_t *
-block_read_changes(apr_array_header_t **changes,
-                   svn_fs_t *fs,
+block_read_changes(svn_fs_t *fs,
                    svn_fs_fs__revision_file_t *rev_file,
                    svn_fs_fs__p2l_entry_t *entry,
-                   svn_boolean_t must_read,
-                   apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
   svn_stream_t *stream;
-  if (!must_read && !ffd->changes_cache)
+  apr_array_header_t *changes;
+
+  pair_cache_key_t key;
+  key.revision = entry->item.revision;
+  key.second = 0;
+
+  if (!ffd->changes_cache)
     return SVN_NO_ERROR;
 
   /* already in cache? */
-  if (!must_read && ffd->changes_cache)
+  if (ffd->changes_cache)
     {
       svn_boolean_t is_cached;
-      SVN_ERR(svn_cache__has_key(&is_cached, ffd->changes_cache,
-                                 &entry->item.revision,
+      SVN_ERR(svn_cache__has_key(&is_cached, ffd->changes_cache, &key,
                                  scratch_pool));
       if (is_cached)
         return SVN_NO_ERROR;
@@ -3347,22 +3419,40 @@ block_read_changes(apr_array_header_t **
 
   SVN_ERR(read_item(&stream, fs, rev_file, entry, scratch_pool));
 
-  /* read changes from revision file */
-  SVN_ERR(svn_fs_fs__read_changes(changes, stream, result_pool,
-                                  scratch_pool));
+  /* Read changes from revision file.  But read just past the first block to
+     enable us to determine whether the first block already hit the EOL.
 
-  /* cache for future reference */
-  if (ffd->changes_cache)
-    SVN_ERR(svn_cache__set(ffd->changes_cache, &entry->item.revision,
-                           *changes, scratch_pool));
+     Note: A 100 entries block is already > 10kB on disk.  With a 4kB default
+           disk block size, this function won't even be called for larger
+           changed paths lists. */
+  SVN_ERR(svn_fs_fs__read_changes(&changes, stream,
+                                  SVN_FS_FS__CHANGES_BLOCK_SIZE + 1,
+                                  scratch_pool, scratch_pool));
+
+  /* We can only cache small lists that don't need to be split up.
+     For longer lists, we miss the file offset info for the respective */
+  if (changes->nelts <= SVN_FS_FS__CHANGES_BLOCK_SIZE)
+    {
+      svn_fs_fs__changes_list_t changes_list;
+
+      /* Construct the info object for the entries block we just read. */
+      changes_list.end_offset = entry->size;
+      changes_list.start_offset = 0;
+      changes_list.count = changes->nelts;
+      changes_list.changes = (change_t **)changes->elts;
+      changes_list.eol = TRUE;
+
+      SVN_ERR(svn_cache__set(ffd->changes_cache, &key, &changes_list,
+                             scratch_pool));
+    }
 
   return SVN_NO_ERROR;
 }
 
-/* If not already cached or if MUST_READ is set, read the nod revision
+/* If not already cached or if MUST_READ is set, read the node revision
  * addressed by ENTRY in FS and retúrn it in *NODEREV_P.  Cache the
- * result if caching is enabled.  Read the data from the already open
- * FILE and wrapping FILE_STREAM. Use SCRATCH_POOL for temporary allocations.
+ * result if caching is enabled.  Read the data from REV_FILE.  Allocate
+ * *NODEREV_P in RESUSLT_POOL and allocate temporaries in SCRATCH_POOL.
  */
 static svn_error_t *
 block_read_noderev(node_revision_t **noderev_p,
@@ -3512,7 +3602,7 @@ block_read(void **result,
                                                 is_wanted
                                                   ? -1
                                                   : block_start + ffd->block_size,
-                                                pool, iterpool));
+                                                iterpool));
                     break;
 
                   case SVN_FS_FS__ITEM_TYPE_NODEREV:
@@ -3524,10 +3614,8 @@ block_read(void **result,
                     break;
 
                   case SVN_FS_FS__ITEM_TYPE_CHANGES:
-                    SVN_ERR(block_read_changes((apr_array_header_t **)&item,
-                                               fs, revision_file,
-                                               entry, is_result,
-                                               pool, iterpool));
+                    SVN_ERR(block_read_changes(fs, revision_file,
+                                               entry, iterpool));
                     break;
 
                   default:

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/cached_data.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/cached_data.h?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/cached_data.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/cached_data.h Tue Oct 11 09:11:50 2016
@@ -158,13 +158,22 @@ svn_fs_fs__get_proplist(apr_hash_t **pro
                         node_revision_t *noderev,
                         apr_pool_t *pool);
 
-/* Fetch the list of change in revision REV in FS and return it in *CHANGES.
- * Allocate the result in POOL.
+/* Create a changes retrieval context object in *RESULT_POOL and return it
+ * in *CONTEXT.  It will allow svn_fs_x__get_changes to fetch consecutive
+ * blocks (one per invocation) from REV's changed paths list in FS. */
+svn_error_t *
+svn_fs_fs__create_changes_context(svn_fs_fs__changes_context_t **context,
+                                  svn_fs_t *fs,
+                                  svn_revnum_t rev,
+                                  apr_pool_t *result_pool);
+
+/* Fetch the block of changes from the CONTEXT and return it in *CHANGES.
+ * Allocate the result in RESULT_POOL and use SCRATCH_POOL for temporaries.
  */
 svn_error_t *
 svn_fs_fs__get_changes(apr_array_header_t **changes,
-                       svn_fs_t *fs,
-                       svn_revnum_t rev,
-                       apr_pool_t *pool);
+                       svn_fs_fs__changes_context_t *context,
+                       apr_pool_t *result_pool,
+                       apr_pool_t *scratch_pool);
 
 #endif

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/caching.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/caching.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/caching.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/caching.c Tue Oct 11 09:11:50 2016
@@ -66,8 +66,9 @@ normalize_key_part(const char *original,
   return normalized->data;
 }
 
-/* *CACHE_TXDELTAS, *CACHE_FULLTEXTS flags will be set according to
-   FS->CONFIG.  *CACHE_NAMESPACE receives the cache prefix to use.
+/* *CACHE_TXDELTAS, *CACHE_FULLTEXTS, *CACHE_NODEPROPS flags will be set
+   according to FS->CONFIG. *CACHE_NAMESPACE receives the cache prefix to
+   use.
 
    Use FS->pool for allocating the memcache and CACHE_NAMESPACE, and POOL
    for temporary allocations. */
@@ -75,6 +76,7 @@ static svn_error_t *
 read_config(const char **cache_namespace,
             svn_boolean_t *cache_txdeltas,
             svn_boolean_t *cache_fulltexts,
+            svn_boolean_t *cache_nodeprops,
             svn_fs_t *fs,
             apr_pool_t *pool)
 {
@@ -117,6 +119,14 @@ read_config(const char **cache_namespace
                          SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS,
                          TRUE);
 
+  /* by default, cache nodeprops: this will match pre-1.10
+   * behavior where node properties caching was controlled
+   * by SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS configuration option.
+   */
+  *cache_nodeprops
+    = svn_hash__get_bool(fs->config,
+                         SVN_FS_CONFIG_FSFS_CACHE_NODEPROPS,
+                         TRUE);
   return SVN_NO_ERROR;
 }
 
@@ -353,6 +363,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
   svn_boolean_t no_handler = ffd->fail_stop;
   svn_boolean_t cache_txdeltas;
   svn_boolean_t cache_fulltexts;
+  svn_boolean_t cache_nodeprops;
   const char *cache_namespace;
   svn_boolean_t has_namespace;
 
@@ -360,6 +371,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
   SVN_ERR(read_config(&cache_namespace,
                       &cache_txdeltas,
                       &cache_fulltexts,
+                      &cache_nodeprops,
                       fs,
                       pool));
 
@@ -438,7 +450,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                        svn_fs_fs__deserialize_dir_entries,
                        sizeof(pair_cache_key_t),
                        apr_pstrcat(pool, prefix, "DIR", SVN_VA_NULL),
-                       SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
+                       SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
                        has_namespace,
                        fs,
                        no_handler,
@@ -498,7 +510,7 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                        1, 8, /* 1k / entry; 8 entries total, rarely used */
                        svn_fs_fs__serialize_changes,
                        svn_fs_fs__deserialize_changes,
-                       sizeof(svn_revnum_t),
+                       sizeof(pair_cache_key_t),
                        apr_pstrcat(pool, prefix, "CHANGES", SVN_VA_NULL),
                        0,
                        has_namespace,
@@ -538,21 +550,6 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
                            no_handler,
                            fs->pool, pool));
 
-      SVN_ERR(create_cache(&(ffd->properties_cache),
-                           NULL,
-                           membuffer,
-                           0, 0, /* Do not use the inprocess cache */
-                           svn_fs_fs__serialize_properties,
-                           svn_fs_fs__deserialize_properties,
-                           sizeof(pair_cache_key_t),
-                           apr_pstrcat(pool, prefix, "PROP",
-                                       SVN_VA_NULL),
-                           SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
-                           has_namespace,
-                           fs,
-                           no_handler,
-                           fs->pool, pool));
-
       SVN_ERR(create_cache(&(ffd->mergeinfo_cache),
                            NULL,
                            membuffer,
@@ -586,11 +583,33 @@ svn_fs_fs__initialize_caches(svn_fs_t *f
   else
     {
       ffd->fulltext_cache = NULL;
-      ffd->properties_cache = NULL;
       ffd->mergeinfo_cache = NULL;
       ffd->mergeinfo_existence_cache = NULL;
     }
 
+  /* if enabled, cache node properties */
+  if (cache_nodeprops)
+    {
+      SVN_ERR(create_cache(&(ffd->properties_cache),
+                           NULL,
+                           membuffer,
+                           0, 0, /* Do not use the inprocess cache */
+                           svn_fs_fs__serialize_properties,
+                           svn_fs_fs__deserialize_properties,
+                           sizeof(pair_cache_key_t),
+                           apr_pstrcat(pool, prefix, "PROP",
+                                       SVN_VA_NULL),
+                           SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
+                           has_namespace,
+                           fs,
+                           no_handler,
+                           fs->pool, pool));
+    }
+  else
+    {
+      ffd->properties_cache = NULL;
+    }
+
   /* if enabled, cache text deltas and their combinations */
   if (cache_txdeltas)
     {
@@ -830,27 +849,37 @@ svn_fs_fs__initialize_txn_caches(svn_fs_
 
   /* Transaction content needs to be carefully prefixed to virtually
      eliminate any chance for conflicts. The (repo, txn_id) pair
-     should be unique but if a transaction fails, it might be possible
-     to start a new transaction later that receives the same id.
-     Therefore, throw in a uuid as well - just to be sure. */
-  prefix = apr_pstrcat(pool,
-                       "fsfs:", fs->uuid,
-                       "/", fs->path,
-                       ":", txn_id,
-                       ":", svn_uuid_generate(pool),
-                       ":", "TXNDIR",
-                       SVN_VA_NULL);
+     should be unique but if the filesystem format doesn't store the
+     global transaction ID via the txn-current file, and a transaction
+     fails, it might be possible to start a new transaction later that
+     receives the same id.  For such older formats, throw in an uuid as
+     well -- just to be sure. */
+  if (ffd->format >= SVN_FS_FS__MIN_TXN_CURRENT_FORMAT)
+    prefix = apr_pstrcat(pool,
+                         "fsfs:", fs->uuid,
+                         "/", fs->path,
+                         ":", txn_id,
+                         ":", "TXNDIR",
+                         SVN_VA_NULL);
+  else
+    prefix = apr_pstrcat(pool,
+                         "fsfs:", fs->uuid,
+                         "/", fs->path,
+                         ":", txn_id,
+                         ":", svn_uuid_generate(pool),
+                         ":", "TXNDIR",
+                         SVN_VA_NULL);
 
   /* create a txn-local directory cache */
   SVN_ERR(create_cache(&ffd->txn_dir_cache,
                        NULL,
                        svn_cache__get_global_membuffer_cache(),
                        1024, 8,
-                       svn_fs_fs__serialize_dir_entries,
+                       svn_fs_fs__serialize_txndir_entries,
                        svn_fs_fs__deserialize_dir_entries,
                        APR_HASH_KEY_STRING,
                        prefix,
-                       0,
+                       SVN_CACHE__MEMBUFFER_HIGH_PRIORITY,
                        TRUE, /* The TXN-ID is our namespace. */
                        fs,
                        TRUE,

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/dag.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/dag.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/dag.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/dag.c Tue Oct 11 09:11:50 2016
@@ -753,8 +753,7 @@ svn_fs_fs__dag_clone_child(dag_node_t **
       noderev->copyfrom_rev = SVN_INVALID_REVNUM;
 
       noderev->predecessor_id = svn_fs_fs__id_copy(cur_entry->id, pool);
-      if (noderev->predecessor_count != -1)
-        noderev->predecessor_count++;
+      noderev->predecessor_count++;
       noderev->created_path = svn_fspath__join(parent_path, name, pool);
 
       SVN_ERR(svn_fs_fs__create_successor(&new_node_id, fs, cur_entry->id,
@@ -1267,8 +1266,7 @@ svn_fs_fs__dag_copy(dag_node_t *to_node,
       /* Create a successor with its predecessor pointing at the copy
          source. */
       to_noderev->predecessor_id = svn_fs_fs__id_copy(src_id, pool);
-      if (to_noderev->predecessor_count != -1)
-        to_noderev->predecessor_count++;
+      to_noderev->predecessor_count++;
       to_noderev->created_path =
         svn_fspath__join(svn_fs_fs__dag_get_created_path(to_node), entry,
                      pool);
@@ -1423,8 +1421,7 @@ svn_fs_fs__dag_update_ancestry(dag_node_
 
   target_noderev->predecessor_id = source->id;
   target_noderev->predecessor_count = source_noderev->predecessor_count;
-  if (target_noderev->predecessor_count != -1)
-    target_noderev->predecessor_count++;
+  target_noderev->predecessor_count++;
 
   return svn_fs_fs__put_node_revision(target->fs, target->id, target_noderev,
                                       FALSE, pool);

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/fs.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/fs.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/fs.c Tue Oct 11 09:11:50 2016
@@ -292,6 +292,7 @@ initialize_fs_struct(svn_fs_t *fs)
   fs_fs_data_t *ffd = apr_pcalloc(fs->pool, sizeof(*ffd));
   ffd->use_log_addressing = FALSE;
   ffd->revprop_prefix = 0;
+  ffd->flush_to_disk = TRUE;
 
   fs->vtable = &fs_vtable;
   fs->fsap_data = ffd;
@@ -474,7 +475,7 @@ fs_pack(svn_fs_t *fs,
         apr_pool_t *common_pool)
 {
   SVN_ERR(fs_open(fs, path, common_pool_lock, pool, common_pool));
-  return svn_fs_fs__pack(fs, notify_func, notify_baton,
+  return svn_fs_fs__pack(fs, 0, notify_func, notify_baton,
                          cancel_func, cancel_baton, pool);
 }
 

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/fs.h?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/fs.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/fs.h Tue Oct 11 09:11:50 2016
@@ -37,7 +37,7 @@
 #include "private/svn_sqlite.h"
 #include "private/svn_mutex.h"
 
-#include "id.h"
+#include "rev_file.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -193,6 +193,11 @@ extern "C" {
 #define SVN_FS_FS__USE_LOCK_MUTEX 0
 #endif
 
+/* Maximum number of changes we deliver per request when listing the
+   changed paths for a given revision.   Anything > 0 will do.
+   At 100..300 bytes per entry, this limits the allocation to ~30kB. */
+#define SVN_FS_FS__CHANGES_BLOCK_SIZE 100
+
 /* Private FSFS-specific data shared between all svn_txn_t objects that
    relate to a particular transaction in a filesystem (as identified
    by transaction id and filesystem UUID).  Objects of this type are
@@ -383,8 +388,8 @@ typedef struct fs_fs_data_t
   /* Cache for node_revision_t objects; the key is (revision, item_index) */
   svn_cache__t *node_revision_cache;
 
-  /* Cache for change lists as APR arrays of change_t * objects; the key
-     is the revision */
+  /* Cache for change lists n blocks as svn_fs_fs__changes_list_t * objects;
+     the key is the (revision, first-element-in-block) pair. */
   svn_cache__t *changes_cache;
 
   /* Cache for svn_fs_fs__rep_header_t objects; the key is a
@@ -476,6 +481,9 @@ typedef struct fs_fs_data_t
      or dump / load cycles). */
   const char *instance_id;
 
+  /* Ensure that all filesystem changes are written to disk. */
+  svn_boolean_t flush_to_disk;
+
   /* Pointer to svn_fs_open. */
   svn_error_t *(*svn_fs_open_)(svn_fs_t **, const char *, apr_hash_t *,
                                apr_pool_t *, apr_pool_t *);
@@ -582,8 +590,8 @@ typedef struct node_revision_t
   svn_revnum_t copyroot_rev;
   const char *copyroot_path;
 
-  /* number of predecessors this node revision has (recursively), or
-     -1 if not known (for backward compatibility). */
+  /* Number of predecessors this node revision has (recursively).
+     A difference from the BDB backend is that it cannot be -1. */
   int predecessor_count;
 
   /* representation key for this node's properties.  may be NULL if
@@ -621,6 +629,33 @@ typedef struct change_t
 } change_t;
 
 
+/*** Context for reading changed paths lists iteratively. */
+typedef struct svn_fs_fs__changes_context_t
+{
+  /* Repository to fetch from. */
+  svn_fs_t *fs;
+
+  /* Revision that we read from. */
+  svn_revnum_t revision;
+
+  /* Revision file object to use when needed.  NULL until the first access. */
+  svn_fs_fs__revision_file_t *revision_file;
+
+  /* Pool to create REVISION_FILE in. */
+  apr_pool_t *rev_file_pool;
+
+  /* Index of the next change to fetch. */
+  apr_size_t next;
+
+  /* Offset, within the changed paths list on disk, of the next change to
+     fetch. */
+  apr_off_t next_offset;
+
+  /* Has the end of the list been reached? */
+  svn_boolean_t eol;
+
+} svn_fs_fs__changes_context_t;
+
 /*** Directory (only used at the cache interface) ***/
 typedef struct svn_fs_fs__dir_data_t
 {

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/fs_fs.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/fs_fs.c Tue Oct 11 09:11:50 2016
@@ -492,6 +492,7 @@ read_format(int *pformat,
       svn_error_clear(err);
       *pformat = 1;
       *max_files_per_dir = 0;
+      *use_log_addressing = FALSE;
 
       return SVN_NO_ERROR;
     }
@@ -623,7 +624,8 @@ svn_fs_fs__write_format(svn_fs_t *fs,
   else
     {
       SVN_ERR(svn_io_write_atomic2(path, sb->data, sb->len,
-                                   NULL /* copy_perms_path */, TRUE, pool));
+                                   NULL /* copy_perms_path */,
+                                   ffd->flush_to_disk, pool));
     }
 
   /* And set the perms to make it read only */
@@ -1031,13 +1033,12 @@ read_global_config(svn_fs_t *fs)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
 
-  /* Providing a config hash is optional. */
-  if (fs->config)
-    ffd->use_block_read = svn_hash__get_bool(fs->config,
-                                             SVN_FS_CONFIG_FSFS_BLOCK_READ,
-                                             FALSE);
-  else
-    ffd->use_block_read = FALSE;
+  ffd->use_block_read = svn_hash__get_bool(fs->config,
+                                           SVN_FS_CONFIG_FSFS_BLOCK_READ,
+                                           FALSE);
+  ffd->flush_to_disk = !svn_hash__get_bool(fs->config,
+                                           SVN_FS_CONFIG_NO_FLUSH_TO_DISK,
+                                           FALSE);
 
   /* Ignore the user-specified larger block size if we don't use block-read.
      Defaulting to 4k gives us the same access granularity in format 7 as in
@@ -1868,7 +1869,7 @@ svn_fs_fs__set_uuid(svn_fs_t *fs,
      file does not exist during repository creation. */
   SVN_ERR(svn_io_write_atomic2(uuid_path, contents->data, contents->len,
                                svn_fs_fs__path_current(fs, pool) /* perms */,
-                               TRUE, pool));
+                               ffd->flush_to_disk, pool));
 
   fs->uuid = apr_pstrdup(fs->pool, uuid);
 

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/hotcopy.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/hotcopy.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/hotcopy.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/hotcopy.c Tue Oct 11 09:11:50 2016
@@ -795,7 +795,7 @@ struct hotcopy_body_baton {
  * An incremental hotcopy copies only changed or new files to the destination,
  * and removes files from the destination no longer present in the source.
  * While the incremental hotcopy is running, readers should still be able
- * to access the destintation repository without error and should not see
+ * to access the destination repository without error and should not see
  * revisions currently in progress of being copied. Readers are able to see
  * new fully copied revisions even if the entire incremental hotcopy procedure
  * has not yet completed.

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/id.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/id.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/id.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/id.c Tue Oct 11 09:11:50 2016
@@ -82,9 +82,11 @@ locale_independent_strtol(long *result_p
 
       next = result * 10 + c;
 
-      /* Overflow check.  In case of an overflow, NEXT is 0..9.
-       * In the non-overflow case, RESULT is either >= 10 or RESULT and NEXT
-       * are both 0. */
+      /* Overflow check.  In case of an overflow, NEXT is 0..9 and RESULT
+       * is much larger than 10.  We will then return FALSE.
+       *
+       * In the non-overflow case, NEXT is >= 10 * RESULT but never smaller.
+       * We will continue the loop in that case. */
       if (next < result)
         return FALSE;
 

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.c?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.c Tue Oct 11 09:11:50 2016
@@ -482,10 +482,10 @@ read_change(change_t **change_p,
 svn_error_t *
 svn_fs_fs__read_changes(apr_array_header_t **changes,
                         svn_stream_t *stream,
+                        int max_count,
                         apr_pool_t *result_pool,
                         apr_pool_t *scratch_pool)
 {
-  change_t *change;
   apr_pool_t *iterpool;
 
   /* Pre-allocate enough room for most change lists.
@@ -498,13 +498,16 @@ svn_fs_fs__read_changes(apr_array_header
    */
   *changes = apr_array_make(result_pool, 63, sizeof(change_t *));
 
-  SVN_ERR(read_change(&change, stream, result_pool, scratch_pool));
   iterpool = svn_pool_create(scratch_pool);
-  while (change)
+  for (; max_count > 0; --max_count)
     {
-      APR_ARRAY_PUSH(*changes, change_t*) = change;
-      SVN_ERR(read_change(&change, stream, result_pool, iterpool));
+      change_t *change;
       svn_pool_clear(iterpool);
+      SVN_ERR(read_change(&change, stream, result_pool, iterpool));
+      if (!change)
+        break;
+ 
+      APR_ARRAY_PUSH(*changes, change_t*) = change;
     }
   svn_pool_destroy(iterpool);
 
@@ -797,7 +800,11 @@ svn_fs_fs__parse_representation(represen
 
   SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_md5, str,
                                  scratch_pool));
-  memcpy(rep->md5_digest, checksum->digest, sizeof(rep->md5_digest));
+
+  /* If STR is a all-zero checksum, CHECKSUM will be NULL and REP already
+     contains the correct value. */
+  if (checksum)
+    memcpy(rep->md5_digest, checksum->digest, sizeof(rep->md5_digest));
 
   /* The remaining fields are only used for formats >= 4, so check that. */
   str = svn_cstring_tokenize(" ", &string);
@@ -811,8 +818,16 @@ svn_fs_fs__parse_representation(represen
 
   SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1, str,
                                  scratch_pool));
+
+  /* We do have a valid SHA1 but it might be all 0.
+     We cannot be sure where that came from (Alas! legacy), so let's not
+     claim we know the SHA1 in that case. */
   rep->has_sha1 = checksum != NULL;
-  memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest));
+
+  /* If STR is a all-zero checksum, CHECKSUM will be NULL and REP already
+     contains the correct value. */
+  if (checksum)
+    memcpy(rep->sha1_digest, checksum->digest, sizeof(rep->sha1_digest));
 
   /* Read the uniquifier. */
   str = svn_cstring_tokenize("/", &string);

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.h
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.h?rev=1764214&r1=1764213&r2=1764214&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.h (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/low_level.h Tue Oct 11 09:11:50 2016
@@ -97,11 +97,13 @@ svn_fs_fs__unparse_footer(apr_off_t l2p_
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool);
 
-/* Read all the changes from STREAM and store them in *CHANGES,
-   allocated in RESULT_POOL. Do temporary allocations in SCRATCH_POOL. */
+/* Read up to MAX_COUNT of the changes from STREAM and store them in
+   *CHANGES, allocated in RESULT_POOL.  Do temporary allocations in
+   SCRATCH_POOL. */
 svn_error_t *
 svn_fs_fs__read_changes(apr_array_header_t **changes,
                         svn_stream_t *stream,
+                        int max_count,
                         apr_pool_t *result_pool,
                         apr_pool_t *scratch_pool);