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 2015/11/30 11:24:23 UTC

svn commit: r1717223 [17/50] - in /subversion/branches/ra-git: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ contrib/hook-scripts/ notes/ notes/api-errata/1.9/ notes/move-tracking/ subversion/ subversion/bindings/ctypes-python/...

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/transaction.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/transaction.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/transaction.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/transaction.c Mon Nov 30 10:24:16 2015
@@ -25,6 +25,7 @@
 #include <assert.h>
 #include <apr_sha1.h>
 
+#include "svn_error_codes.h"
 #include "svn_hash.h"
 #include "svn_props.h"
 #include "svn_sorts.h"
@@ -88,15 +89,6 @@ path_txn_props(svn_fs_t *fs,
 }
 
 static APR_INLINE const char *
-path_txn_props_final(svn_fs_t *fs,
-                     const svn_fs_fs__id_part_t *txn_id,
-                     apr_pool_t *pool)
-{
-  return svn_dirent_join(svn_fs_fs__path_txn_dir(fs, txn_id, pool),
-                         PATH_TXN_PROPS_FINAL, pool);
-}
-
-static APR_INLINE const char *
 path_txn_next_ids(svn_fs_t *fs,
                   const svn_fs_fs__id_part_t *txn_id,
                   apr_pool_t *pool)
@@ -584,16 +576,62 @@ unparse_dir_entry(svn_fs_dirent_t *diren
                   svn_stream_t *stream,
                   apr_pool_t *pool)
 {
-  const char *val
-    = apr_psprintf(pool, "%s %s",
-                   (dirent->kind == svn_node_file) ? SVN_FS_FS__KIND_FILE
-                                                   : SVN_FS_FS__KIND_DIR,
-                   svn_fs_fs__id_unparse(dirent->id, pool)->data);
-
-  SVN_ERR(svn_stream_printf(stream, pool, "K %" APR_SIZE_T_FMT "\n%s\n"
-                            "V %" APR_SIZE_T_FMT "\n%s\n",
-                            strlen(dirent->name), dirent->name,
-                            strlen(val), val));
+  apr_size_t to_write;
+  svn_string_t *id_str = svn_fs_fs__id_unparse(dirent->id, pool);
+  apr_size_t name_len = strlen(dirent->name);
+
+  /* Note that sizeof == len + 1, i.e. accounts for the space between
+   * type and ID. */
+  apr_size_t type_len = (dirent->kind == svn_node_file)
+                      ? sizeof(SVN_FS_FS__KIND_FILE)
+                      : sizeof(SVN_FS_FS__KIND_DIR);
+  apr_size_t value_len = type_len + id_str->len;
+
+  /* A buffer with sufficient space for 
+   * - both string lines
+   * - 4 newlines
+   * - 2 lines K/V lines containing a number each
+   */
+  char *buffer = apr_palloc(pool,   name_len + value_len
+                                  + 4
+                                  + 2 * (2 + SVN_INT64_BUFFER_SIZE));
+
+  /* Now construct the value. */
+  char *p = buffer;
+
+  /* The "K length(name)\n" line. */
+  p[0] = 'K';
+  p[1] = ' ';
+  p += 2;
+  p += svn__i64toa(p, name_len);
+  *(p++) = '\n';
+
+  /* The line with the key, i.e. dir entry name. */
+  memcpy(p, dirent->name, name_len);
+  p += name_len;
+  *(p++) = '\n';
+
+  /* The "V length(type+id)\n" line. */
+  p[0] = 'V';
+  p[1] = ' ';
+  p += 2;
+  p += svn__i64toa(p, value_len);
+  *(p++) = '\n';
+
+  /* The line with the type and ID. */
+  memcpy(p,
+         (dirent->kind == svn_node_file) ? SVN_FS_FS__KIND_FILE
+                                         : SVN_FS_FS__KIND_DIR,
+         type_len - 1);
+  p += type_len - 1;
+  *(p++) = ' ';
+  memcpy(p, id_str->data, id_str->len);
+  p+=id_str->len;
+  *(p++) = '\n';
+
+  /* Add the entry to the output stream. */
+  to_write = p - buffer;
+  SVN_ERR(svn_stream_write(stream, buffer, &to_write));
   return SVN_NO_ERROR;
 }
 
@@ -976,10 +1014,10 @@ get_and_increment_txn_key_body(void *bat
 
   /* Increment the key and add a trailing \n to the string so the
      txn-current file has a newline in it. */
-  SVN_ERR(svn_io_write_atomic(txn_current_filename, new_id_str,
-                              line_length + 1,
-                              txn_current_filename /* copy_perms path */,
-                              pool));
+  SVN_ERR(svn_io_write_atomic2(txn_current_filename, new_id_str,
+                               line_length + 1,
+                               txn_current_filename /* copy_perms path */,
+                               TRUE, pool));
 
   return SVN_NO_ERROR;
 }
@@ -1127,6 +1165,7 @@ get_txn_proplist(apr_hash_t *proplist,
                  apr_pool_t *pool)
 {
   svn_stream_t *stream;
+  svn_error_t *err;
 
   /* Check for issue #3696. (When we find and fix the cause, we can change
    * this to an assertion.) */
@@ -1140,7 +1179,14 @@ get_txn_proplist(apr_hash_t *proplist,
                                    pool, pool));
 
   /* Read in the property list. */
-  SVN_ERR(svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool));
+  err = svn_hash_read2(proplist, stream, SVN_HASH_TERMINATOR, pool);
+  if (err)
+    {
+      err = svn_error_compose_create(err, svn_stream_close(stream));
+      return svn_error_quick_wrapf(err,
+               _("malformed property list in transaction '%s'"),
+               path_txn_props(fs, txn_id, pool));
+    }
 
   return svn_stream_close(stream);
 }
@@ -1151,24 +1197,24 @@ static svn_error_t *
 set_txn_proplist(svn_fs_t *fs,
                  const svn_fs_fs__id_part_t *txn_id,
                  apr_hash_t *props,
-                 svn_boolean_t final,
                  apr_pool_t *pool)
 {
-  svn_stringbuf_t *buf;
-  svn_stream_t *stream;
+  svn_stream_t *tmp_stream;
+  const char *tmp_path;
+  const char *final_path = path_txn_props(fs, txn_id, pool);
+
+  /* Write the new contents into a temporary file. */
+  SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmp_path,
+                                 svn_dirent_dirname(final_path, pool),
+                                 svn_io_file_del_none,
+                                 pool, pool));
+
+  /* Replace the old file with the new one. */
+  SVN_ERR(svn_hash_write2(props, tmp_stream, SVN_HASH_TERMINATOR, pool));
+  SVN_ERR(svn_stream_close(tmp_stream));
 
-  /* Write out the new file contents to BUF. */
-  buf = svn_stringbuf_create_ensure(1024, pool);
-  stream = svn_stream_from_stringbuf(buf, pool);
-  SVN_ERR(svn_hash_write2(props, stream, SVN_HASH_TERMINATOR, pool));
-  SVN_ERR(svn_stream_close(stream));
+  SVN_ERR(svn_io_file_rename2(tmp_path, final_path, FALSE, pool));
 
-  /* Open the transaction properties file and write new contents to it. */
-  SVN_ERR(svn_io_write_atomic((final
-                               ? path_txn_props_final(fs, txn_id, pool)
-                               : path_txn_props(fs, txn_id, pool)),
-                              buf->data, buf->len,
-                              NULL /* copy_perms_path */, pool));
   return SVN_NO_ERROR;
 }
 
@@ -1223,7 +1269,7 @@ svn_fs_fs__change_txn_props(svn_fs_txn_t
 
   /* Create a new version of the file and write out the new props. */
   /* Open the transaction properties file. */
-  SVN_ERR(set_txn_proplist(txn->fs, &ftd->txn_id, txn_prop, FALSE, pool));
+  SVN_ERR(set_txn_proplist(txn->fs, &ftd->txn_id, txn_prop, pool));
 
   return SVN_NO_ERROR;
 }
@@ -1239,9 +1285,6 @@ svn_fs_fs__get_txn(transaction_t **txn_p
   svn_fs_id_t *root_id;
 
   txn = apr_pcalloc(pool, sizeof(*txn));
-  txn->proplist = apr_hash_make(pool);
-
-  SVN_ERR(get_txn_proplist(txn->proplist, fs, txn_id, pool));
   root_id = svn_fs_fs__id_txn_create_root(txn_id, pool);
 
   SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, root_id, pool, pool));
@@ -1485,6 +1528,7 @@ svn_fs_fs__set_entry(svn_fs_t *fs,
     = svn_fs_fs__path_txn_node_children(fs, parent_noderev->id, pool);
   apr_file_t *file;
   svn_stream_t *out;
+  svn_filesize_t filesize;
   fs_fs_data_t *ffd = fs->fsap_data;
   apr_pool_t *subpool = svn_pool_create(pool);
 
@@ -1502,8 +1546,6 @@ svn_fs_fs__set_entry(svn_fs_t *fs,
       out = svn_stream_from_aprfile2(file, TRUE, pool);
       SVN_ERR(unparse_dir_entries(entries, out, subpool));
 
-      svn_pool_clear(subpool);
-
       /* Mark the node-rev's data rep as mutable. */
       rep = apr_pcalloc(pool, sizeof(*rep));
       rep->revision = SVN_INVALID_REVNUM;
@@ -1512,25 +1554,105 @@ svn_fs_fs__set_entry(svn_fs_t *fs,
       parent_noderev->data_rep = rep;
       SVN_ERR(svn_fs_fs__put_node_revision(fs, parent_noderev->id,
                                            parent_noderev, FALSE, pool));
+
+      /* Immediately populate the txn dir cache to avoid re-reading
+       * the file we just wrote. */
+      if (ffd->txn_dir_cache)
+        {
+          const char *key
+            = svn_fs_fs__id_unparse(parent_noderev->id, subpool)->data;
+          svn_fs_fs__dir_data_t dir_data;
+
+          /* Flush APR buffers. */
+          SVN_ERR(svn_io_file_flush(file, subpool));
+
+          /* Obtain final file size to update txn_dir_cache. */
+          SVN_ERR(svn_io_file_size_get(&filesize, file, subpool));
+
+          /* Store in the cache. */
+          dir_data.entries = entries;
+          dir_data.txn_filesize = filesize;
+          SVN_ERR(svn_cache__set(ffd->txn_dir_cache, key, &dir_data,
+                                 subpool));
+        }
+
+      svn_pool_clear(subpool);
     }
   else
     {
       /* The directory rep is already mutable, so just open it for append. */
       SVN_ERR(svn_io_file_open(&file, filename, APR_WRITE | APR_APPEND,
-                               APR_OS_DEFAULT, pool));
-      out = svn_stream_from_aprfile2(file, TRUE, pool);
+                               APR_OS_DEFAULT, subpool));
+      out = svn_stream_from_aprfile2(file, TRUE, subpool);
+
+      /* If the cache contents is stale, drop it.
+       *
+       * Note that the directory file is append-only, i.e. if the size
+       * did not change, the contents didn't either. */
+      if (ffd->txn_dir_cache)
+        {
+          const char *key
+            = svn_fs_fs__id_unparse(parent_noderev->id, subpool)->data;
+          svn_boolean_t found;
+          svn_filesize_t cached_filesize;
+
+          /* Get the file size that corresponds to the cached contents
+           * (if any). */
+          SVN_ERR(svn_cache__get_partial((void **)&cached_filesize, &found,
+                                         ffd->txn_dir_cache, key,
+                                         svn_fs_fs__extract_dir_filesize,
+                                         NULL, subpool));
+
+          /* File size info still matches?
+           * If not, we need to drop the cache entry. */
+          if (found)
+            {
+              SVN_ERR(svn_io_file_size_get(&filesize, file, subpool));
+
+              if (cached_filesize != filesize)
+                SVN_ERR(svn_cache__set(ffd->txn_dir_cache, key, NULL,
+                                       subpool));
+            }
+        }
+    }
+
+  /* Append an incremental hash entry for the entry change. */
+  if (id)
+    {
+      svn_fs_dirent_t entry;
+      entry.name = name;
+      entry.id = id;
+      entry.kind = kind;
+
+      SVN_ERR(unparse_dir_entry(&entry, out, subpool));
     }
+  else
+    {
+      SVN_ERR(svn_stream_printf(out, subpool, "D %" APR_SIZE_T_FMT "\n%s\n",
+                                strlen(name), name));
+    }
+
+  /* Flush APR buffers. */
+  SVN_ERR(svn_io_file_flush(file, subpool));
+
+  /* Obtain final file size to update txn_dir_cache. */
+  SVN_ERR(svn_io_file_size_get(&filesize, file, subpool));
+
+  /* Close file. */
+  SVN_ERR(svn_io_file_close(file, subpool));
+  svn_pool_clear(subpool);
 
   /* if we have a directory cache for this transaction, update it */
   if (ffd->txn_dir_cache)
     {
-      /* build parameters: (name, new entry) pair */
+      /* build parameters: name, new entry, new file size  */
       const char *key =
           svn_fs_fs__id_unparse(parent_noderev->id, subpool)->data;
       replace_baton_t baton;
 
       baton.name = name;
       baton.new_entry = NULL;
+      baton.txn_filesize = filesize;
 
       if (id)
         {
@@ -1545,25 +1667,7 @@ svn_fs_fs__set_entry(svn_fs_t *fs,
                                      svn_fs_fs__replace_dir_entry, &baton,
                                      subpool));
     }
-  svn_pool_clear(subpool);
-
-  /* Append an incremental hash entry for the entry change. */
-  if (id)
-    {
-      svn_fs_dirent_t entry;
-      entry.name = name;
-      entry.id = id;
-      entry.kind = kind;
-
-      SVN_ERR(unparse_dir_entry(&entry, out, subpool));
-    }
-  else
-    {
-      SVN_ERR(svn_stream_printf(out, subpool, "D %" APR_SIZE_T_FMT "\n%s\n",
-                                strlen(name), name));
-    }
 
-  SVN_ERR(svn_io_file_close(file, subpool));
   svn_pool_destroy(subpool);
   return SVN_NO_ERROR;
 }
@@ -1675,17 +1779,23 @@ allocate_item_index(apr_uint64_t *item_i
       char buffer[SVN_INT64_BUFFER_SIZE] = { 0 };
       svn_boolean_t eof = FALSE;
       apr_size_t to_write;
-      apr_size_t read;
+      apr_size_t bytes_read;
       apr_off_t offset = 0;
 
       /* read number, increment it and write it back to disk */
       SVN_ERR(svn_io_file_open(&file,
                          svn_fs_fs__path_txn_item_index(fs, txn_id, pool),
-                         APR_READ | APR_WRITE | APR_CREATE | APR_BUFFERED,
+                         APR_READ | APR_WRITE | APR_CREATE,
                          APR_OS_DEFAULT, pool));
       SVN_ERR(svn_io_file_read_full2(file, buffer, sizeof(buffer)-1,
-                                     &read, &eof, pool));
-      if (read)
+                                     &bytes_read, &eof, pool));
+
+      /* Item index file should be shorter than SVN_INT64_BUFFER_SIZE,
+         otherwise we truncate data. */
+      if (!eof)
+          return svn_error_create(SVN_ERR_FS_CORRUPT, NULL,
+                                  _("Unexpected itemidx file length"));
+      else if (bytes_read)
         SVN_ERR(svn_cstring_atoui64(item_index, buffer));
       else
         *item_index = SVN_FS_FS__ITEM_INDEX_FIRST_USER;
@@ -1963,9 +2073,7 @@ choose_delta_base(representation_t **rep
 
       /* Very short rep bases are simply not worth it as we are unlikely
        * to re-coup the deltification space overhead of 20+ bytes. */
-      svn_filesize_t rep_size = (*rep)->expanded_size
-                              ? (*rep)->expanded_size
-                              : (*rep)->size;
+      svn_filesize_t rep_size = (*rep)->expanded_size;
       if (rep_size < 64)
         {
           *rep = NULL;
@@ -2118,7 +2226,7 @@ rep_write_get_baton(struct rep_write_bat
 }
 
 /* For REP->SHA1_CHECKSUM, try to find an already existing representation
-   in FS and return it in *OUT_REP.  If no such representation exists or
+   in FS and return it in *OLD_REP.  If no such representation exists or
    if rep sharing has been disabled for FS, NULL will be returned.  Since
    there may be new duplicate representations within the same uncommitted
    revision, those can be passed in REPS_HASH (maps a sha1 digest onto
@@ -2142,9 +2250,13 @@ get_shared_rep(representation_t **old_re
   if (!ffd->rep_sharing_allowed)
     return SVN_NO_ERROR;
 
+  /* Can't look up if we don't know the key (happens for directories). */
+  if (!rep->has_sha1)
+    return SVN_NO_ERROR;
+
   /* Check and see if we already have a representation somewhere that's
      identical to the one we just wrote out.  Start with the hash lookup
-     because it is cheepest. */
+     because it is cheapest. */
   if (reps_hash)
     *old_rep = apr_hash_get(reps_hash,
                             rep->sha1_digest,
@@ -2213,12 +2325,37 @@ get_shared_rep(representation_t **old_re
   if (!*old_rep)
     return SVN_NO_ERROR;
 
-  /* We don't want 0-length PLAIN representations to replace non-0-length
-     ones (see issue #4554).  Also, this doubles as a simple guard against
-     general rep-cache induced corruption. */
-  if (   ((*old_rep)->expanded_size != rep->expanded_size)
-      || ((*old_rep)->size != rep->size))
+  /* A simple guard against general rep-cache induced corruption. */
+  if ((*old_rep)->expanded_size != rep->expanded_size)
     {
+      /* Make the problem show up in the server log.
+
+         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"
+                              "You should delete the rep-cache.db and "
+                              "verify the repository. The cached rep will "
+                              "not be shared.",
+                              apr_psprintf(scratch_pool,
+                                           "%" SVN_FILESIZE_T_FMT,
+                                           rep->expanded_size),
+                              apr_psprintf(scratch_pool,
+                                           "%" SVN_FILESIZE_T_FMT,
+                                           (*old_rep)->expanded_size),
+                              svn_checksum_to_cstring_display(&checksum,
+                                                              scratch_pool));
+
+      (fs->warning)(fs->warning_baton, err);
+      svn_error_clear(err);
+
+      /* Ignore the shared rep. */
       *old_rep = NULL;
     }
   else
@@ -2233,6 +2370,7 @@ get_shared_rep(representation_t **old_re
 }
 
 /* Copy the hash sum calculation results from MD5_CTX, SHA1_CTX into REP.
+ * SHA1 results are only be set if SHA1_CTX is not NULL.
  * Use POOL for allocations.
  */
 static svn_error_t *
@@ -2245,10 +2383,12 @@ digests_final(representation_t *rep,
 
   SVN_ERR(svn_checksum_final(&checksum, md5_ctx, pool));
   memcpy(rep->md5_digest, checksum->digest, svn_checksum_size(checksum));
-  SVN_ERR(svn_checksum_final(&checksum, sha1_ctx, pool));
-  rep->has_sha1 = checksum != NULL;
+  rep->has_sha1 = sha1_ctx != NULL;
   if (rep->has_sha1)
-    memcpy(rep->sha1_digest, checksum->digest, svn_checksum_size(checksum));
+    {
+      SVN_ERR(svn_checksum_final(&checksum, sha1_ctx, pool));
+      memcpy(rep->sha1_digest, checksum->digest, svn_checksum_size(checksum));
+    }
 
   return SVN_NO_ERROR;
 }
@@ -2453,6 +2593,8 @@ struct write_container_baton
   apr_size_t size;
 
   svn_checksum_ctx_t *md5_ctx;
+
+  /* SHA1 calculation is optional. If not needed, this will be NULL. */
   svn_checksum_ctx_t *sha1_ctx;
 };
 
@@ -2467,7 +2609,8 @@ write_container_handler(void *baton,
   struct write_container_baton *whb = baton;
 
   SVN_ERR(svn_checksum_update(whb->md5_ctx, data, *len));
-  SVN_ERR(svn_checksum_update(whb->sha1_ctx, data, *len));
+  if (whb->sha1_ctx)
+    SVN_ERR(svn_checksum_update(whb->sha1_ctx, data, *len));
 
   SVN_ERR(svn_stream_write(whb->stream, data, len));
   whb->size += *len;
@@ -2539,7 +2682,8 @@ write_container_rep(representation_t *re
                                   scratch_pool);
   whb->size = 0;
   whb->md5_ctx = svn_checksum_ctx_create(svn_checksum_md5, scratch_pool);
-  whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, scratch_pool);
+  if (item_type != SVN_FS_FS__ITEM_TYPE_DIR_REP)
+    whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, scratch_pool);
 
   stream = svn_stream_create(whb, scratch_pool);
   svn_stream_set_write(stream, write_container_handler);
@@ -2553,6 +2697,7 @@ write_container_rep(representation_t *re
 
   /* Check and see if we already have a representation somewhere that's
      identical to the one we just wrote out. */
+  rep->expanded_size = whb->size;
   SVN_ERR(get_shared_rep(&old_rep, fs, rep, reps_hash, scratch_pool,
                          scratch_pool));
 
@@ -2588,7 +2733,6 @@ write_container_rep(representation_t *re
 
       /* update the representation */
       rep->size = whb->size;
-      rep->expanded_size = whb->size;
     }
 
   return SVN_NO_ERROR;
@@ -2678,7 +2822,8 @@ write_container_delta_rep(representation
                                         scratch_pool);
   whb->size = 0;
   whb->md5_ctx = svn_checksum_ctx_create(svn_checksum_md5, scratch_pool);
-  whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, scratch_pool);
+  if (item_type != SVN_FS_FS__ITEM_TYPE_DIR_REP)
+    whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, scratch_pool);
 
   /* serialize the hash */
   stream = svn_stream_create(whb, scratch_pool);
@@ -2692,6 +2837,7 @@ write_container_delta_rep(representation
 
   /* Check and see if we already have a representation somewhere that's
      identical to the one we just wrote out. */
+  rep->expanded_size = whb->size;
   SVN_ERR(get_shared_rep(&old_rep, fs, rep, reps_hash, scratch_pool,
                          scratch_pool));
 
@@ -2727,7 +2873,6 @@ write_container_delta_rep(representation
       SVN_ERR(store_p2l_index_entry(fs, &rep->txn_id, &entry, scratch_pool));
 
       /* update the representation */
-      rep->expanded_size = whb->size;
       rep->size = rep_end - delta_start;
     }
 
@@ -2773,7 +2918,7 @@ validate_root_noderev(svn_fs_t *fs,
 
      Normally (rev == root_noderev->predecessor_count), but here we
      use a more roundabout check that should only trigger on new instances
-     of the corruption, rather then trigger on each and every new commit
+     of the corruption, rather than trigger on each and every new commit
      to a repository that has triggered the bug somewhere in its root
      noderev's history.
    */
@@ -2832,6 +2977,9 @@ get_final_id(svn_fs_fs__id_part_t *part,
    INITIAL_OFFSET is the offset of the proto-rev-file on entry to
    commit_body.
 
+   Collect the pair_cache_key_t of all directories written to the
+   committed cache in DIRECTORY_IDS.
+
    If REPS_TO_CACHE is not NULL, append to it a copy (allocated in
    REPS_POOL) of each data rep that is new in this revision.
 
@@ -2853,6 +3001,7 @@ write_final_rev(const svn_fs_id_t **new_
                 apr_uint64_t start_node_id,
                 apr_uint64_t start_copy_id,
                 apr_off_t initial_offset,
+                apr_array_header_t *directory_ids,
                 apr_array_header_t *reps_to_cache,
                 apr_hash_t *reps_hash,
                 apr_pool_t *reps_pool,
@@ -2895,14 +3044,17 @@ write_final_rev(const svn_fs_id_t **new_
           svn_pool_clear(subpool);
           SVN_ERR(write_final_rev(&new_id, file, rev, fs, dirent->id,
                                   start_node_id, start_copy_id, initial_offset,
-                                  reps_to_cache, reps_hash, reps_pool, FALSE,
-                                  subpool));
+                                  directory_ids, reps_to_cache, reps_hash,
+                                  reps_pool, FALSE, subpool));
           if (new_id && (svn_fs_fs__id_rev(new_id) == rev))
             dirent->id = svn_fs_fs__id_copy(new_id, pool);
         }
 
       if (noderev->data_rep && is_txn_rep(noderev->data_rep))
         {
+          pair_cache_key_t *key;
+          svn_fs_fs__dir_data_t dir_data;
+
           /* Write out the contents of this directory as a text rep. */
           noderev->data_rep->revision = rev;
           if (ffd->deltify_directories)
@@ -2918,6 +3070,23 @@ write_final_rev(const svn_fs_id_t **new_
                                         SVN_FS_FS__ITEM_TYPE_DIR_REP, pool));
 
           reset_txn_in_rep(noderev->data_rep);
+
+          /* Cache the new directory contents.  Otherwise, subsequent reads
+           * or commits will likely have to reconstruct, verify and parse
+           * it again. */
+          key = apr_array_push(directory_ids);
+          key->revision = noderev->data_rep->revision;
+          key->second = noderev->data_rep->item_index;
+
+          /* Store directory contents under the new revision number but mark
+           * it as "stale" by setting the file length to 0.  Committed dirs
+           * will report -1, in-txn dirs will report > 0, so that this can
+           * never match.  We reset that to -1 after the commit is complete.
+           */
+          dir_data.entries = entries;
+          dir_data.txn_filesize = 0;
+
+          SVN_ERR(svn_cache__set(ffd->dir_cache, key, &dir_data, subpool));
         }
     }
   else
@@ -3265,41 +3434,31 @@ verify_locks(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
-/* Return in *PATH the path to a file containing the properties that
-   make up the final revision properties file.  This involves setting
-   svn:date and removing any temporary properties associated with the
-   commit flags. */
+/* Writes final revision properties to file PATH applying permissions
+   from file PERMS_REFERENCE. This involves setting svn:date and
+   removing any temporary properties associated with the commit flags. */
 static svn_error_t *
-write_final_revprop(const char **path,
+write_final_revprop(const char *path,
+                    const char *perms_reference,
                     svn_fs_txn_t *txn,
-                    const svn_fs_fs__id_part_t *txn_id,
                     apr_pool_t *pool)
 {
   apr_hash_t *txnprops;
-  svn_boolean_t final_mods = FALSE;
   svn_string_t date;
   svn_string_t *client_date;
+  apr_file_t *revprop_file;
+  svn_stream_t *stream;
 
   SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, txn, pool));
 
   /* Remove any temporary txn props representing 'flags'. */
-  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
-    {
-      svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD, NULL);
-      final_mods = TRUE;
-    }
-
-  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
-    {
-      svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL);
-      final_mods = TRUE;
-    }
+  svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD, NULL);
+  svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS, NULL);
 
   client_date = svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE);
   if (client_date)
     {
       svn_hash_sets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE, NULL);
-      final_mods = TRUE;
     }
 
   /* Update commit time to ensure that svn:date revprops remain ordered if
@@ -3309,18 +3468,22 @@ write_final_revprop(const char **path,
       date.data = svn_time_to_cstring(apr_time_now(), pool);
       date.len = strlen(date.data);
       svn_hash_sets(txnprops, SVN_PROP_REVISION_DATE, &date);
-      final_mods = TRUE;
     }
 
-  if (final_mods)
-    {
-      SVN_ERR(set_txn_proplist(txn->fs, txn_id, txnprops, TRUE, pool));
-      *path = path_txn_props_final(txn->fs, txn_id, pool);
-    }
-  else
-    {
-      *path = path_txn_props(txn->fs, txn_id, pool);
-    }
+  /* Create new revprops file. Tell OS to truncate existing file,
+     since  file may already exists from failed transaction. */
+  SVN_ERR(svn_io_file_open(&revprop_file, path,
+                           APR_WRITE | APR_CREATE | APR_TRUNCATE
+                           | APR_BUFFERED, APR_OS_DEFAULT, pool));
+
+  stream = svn_stream_from_aprfile2(revprop_file, TRUE, pool);
+  SVN_ERR(svn_hash_write2(txnprops, stream, SVN_HASH_TERMINATOR, pool));
+  SVN_ERR(svn_stream_close(stream));
+
+  SVN_ERR(svn_io_file_flush_to_disk(revprop_file, pool));
+  SVN_ERR(svn_io_file_close(revprop_file, pool));
+
+  SVN_ERR(svn_io_copy_perms(perms_reference, path, pool));
 
   return SVN_NO_ERROR;
 }
@@ -3366,6 +3529,41 @@ svn_fs_fs__add_index_data(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
+/* Mark the directories cached in FS with the keys from DIRECTORY_IDS
+ * as "valid" now.  Use SCRATCH_POOL for temporaries. */
+static svn_error_t *
+promote_cached_directories(svn_fs_t *fs,
+                           apr_array_header_t *directory_ids,
+                           apr_pool_t *scratch_pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  apr_pool_t *iterpool;
+  int i;
+
+  if (!ffd->dir_cache)
+    return SVN_NO_ERROR;
+
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; i < directory_ids->nelts; ++i)
+    {
+      const pair_cache_key_t *key
+        = &APR_ARRAY_IDX(directory_ids, i, pair_cache_key_t);
+
+      svn_pool_clear(iterpool);
+
+      /* Currently, the entry for KEY - if it still exists - is marked
+       * as "stale" and would not be used.  Mark it as current for in-
+       * revison data. */
+      SVN_ERR(svn_cache__set_partial(ffd->dir_cache, key,
+                                     svn_fs_fs__reset_txn_filesize, NULL,
+                                     iterpool));
+    }
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
 /* Baton used for commit_body below. */
 struct commit_baton {
   svn_revnum_t *new_rev_p;
@@ -3385,7 +3583,7 @@ commit_body(void *baton, apr_pool_t *poo
   struct commit_baton *cb = baton;
   fs_fs_data_t *ffd = cb->fs->fsap_data;
   const char *old_rev_filename, *rev_filename, *proto_filename;
-  const char *revprop_filename, *final_revprop;
+  const char *revprop_filename;
   const svn_fs_id_t *root_id, *new_root_id;
   apr_uint64_t start_node_id;
   apr_uint64_t start_copy_id;
@@ -3395,6 +3593,8 @@ commit_body(void *baton, apr_pool_t *poo
   apr_off_t initial_offset, changed_path_offset;
   const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(cb->txn);
   apr_hash_t *changed_paths;
+  apr_array_header_t *directory_ids = apr_array_make(pool, 4,
+                                                     sizeof(pair_cache_key_t));
 
   /* Re-Read the current repository format.  All our repo upgrade and
      config evaluation strategies are such that existing information in
@@ -3448,8 +3648,8 @@ commit_body(void *baton, apr_pool_t *poo
   root_id = svn_fs_fs__id_txn_create_root(txn_id, pool);
   SVN_ERR(write_final_rev(&new_root_id, proto_file, new_rev, cb->fs, root_id,
                           start_node_id, start_copy_id, initial_offset,
-                          cb->reps_to_cache, cb->reps_hash, cb->reps_pool,
-                          TRUE, pool));
+                          directory_ids, cb->reps_to_cache, cb->reps_hash,
+                          cb->reps_pool, TRUE, pool));
 
   /* Write the changed-path information. */
   SVN_ERR(write_final_changed_path_info(&changed_path_offset, proto_file,
@@ -3538,12 +3738,11 @@ commit_body(void *baton, apr_pool_t *poo
      remove the transaction directory later. */
   SVN_ERR(unlock_proto_rev(cb->fs, txn_id, proto_file_lockcookie, pool));
 
-  /* Move the revprops file into place. */
+  /* Write final revprops file. */
   SVN_ERR_ASSERT(! svn_fs_fs__is_packed_revprop(cb->fs, new_rev));
-  SVN_ERR(write_final_revprop(&revprop_filename, cb->txn, txn_id, pool));
-  final_revprop = svn_fs_fs__path_revprops(cb->fs, new_rev, pool);
-  SVN_ERR(svn_fs_fs__move_into_place(revprop_filename, final_revprop,
-                                     old_rev_filename, pool));
+  revprop_filename = svn_fs_fs__path_revprops(cb->fs, new_rev, pool);
+  SVN_ERR(write_final_revprop(revprop_filename, old_rev_filename,
+                              cb->txn, pool));
 
   /* Update the 'current' file. */
   SVN_ERR(verify_as_revision_before_current_plus_plus(cb->fs, new_rev, pool));
@@ -3559,6 +3758,10 @@ commit_body(void *baton, apr_pool_t *poo
 
   ffd->youngest_rev_cache = new_rev;
 
+  /* Make the directory contents alreday cached for the new revision
+   * visible. */
+  SVN_ERR(promote_cached_directories(cb->fs, directory_ids, pool));
+
   /* Remove this transaction directory. */
   SVN_ERR(svn_fs_fs__purge_txn(cb->fs, cb->txn->id, pool));
 
@@ -3847,6 +4050,5 @@ svn_fs_fs__begin_txn(svn_fs_txn_t **txn_
                   svn_string_create("0", pool));
 
   ftd = (*txn_p)->fsap_data;
-  return svn_error_trace(set_txn_proplist(fs, &ftd->txn_id, props, FALSE,
-                                          pool));
+  return svn_error_trace(set_txn_proplist(fs, &ftd->txn_id, props, pool));
 }

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/tree.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/tree.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/tree.c Mon Nov 30 10:24:16 2015
@@ -209,17 +209,15 @@ auto_clear_dag_cache(fs_fs_dag_cache_t*
     }
 }
 
-/* For the given REVISION and PATH, return the respective entry in CACHE.
-   If the entry is empty, its NODE member will be NULL and the caller
-   may then set it to the corresponding DAG node allocated in CACHE->POOL.
+/* Returns a 32 bit hash value for the given REVISION and PATH of exactly
+ * PATH_LEN chars.
  */
-static cache_entry_t *
-cache_lookup( fs_fs_dag_cache_t *cache
-            , svn_revnum_t revision
-            , const char *path)
+static apr_uint32_t
+hash_func(svn_revnum_t revision,
+          const char *path,
+          apr_size_t path_len)
 {
-  apr_size_t i, bucket_index;
-  apr_size_t path_len = strlen(path);
+  apr_size_t i;
   apr_uint32_t hash_value = (apr_uint32_t)revision;
 
 #if SVN_UNALIGNED_ACCESS_IS_OK
@@ -227,20 +225,7 @@ cache_lookup( fs_fs_dag_cache_t *cache
   const apr_uint32_t factor = 0xd1f3da69;
 #endif
 
-  /* optimistic lookup: hit the same bucket again? */
-  cache_entry_t *result = &cache->buckets[cache->last_hit];
-  if (   (result->revision == revision)
-      && (result->path_len == path_len)
-      && !memcmp(result->path, path, path_len))
-    {
-      /* Remember the position of the last node we found in this cache. */
-      if (result->node)
-        cache->last_non_empty = cache->last_hit;
-
-      return result;
-    }
-
-  /* need to do a full lookup.  Calculate the hash value
+  /* Calculate the hash value
      (HASH_VALUE has been initialized to REVISION).
 
      Note that the actual hash function is arbitrary as long as its result
@@ -283,6 +268,37 @@ cache_lookup( fs_fs_dag_cache_t *cache
      */
     hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
 
+  return hash_value;
+}
+
+/* For the given REVISION and PATH, return the respective node found in
+ * CACHE.  If there is none, return NULL.
+ */
+static dag_node_t *
+cache_lookup( fs_fs_dag_cache_t *cache
+            , svn_revnum_t revision
+            , const char *path)
+{
+  apr_size_t bucket_index;
+  apr_size_t path_len = strlen(path);
+  apr_uint32_t hash_value;
+
+  /* optimistic lookup: hit the same bucket again? */
+  cache_entry_t *result = &cache->buckets[cache->last_hit];
+  if (   (result->revision == revision)
+      && (result->path_len == path_len)
+      && !memcmp(result->path, path, path_len))
+    {
+      /* Remember the position of the last node we found in this cache. */
+      if (result->node)
+        cache->last_non_empty = cache->last_hit;
+
+      return result->node;
+    }
+
+  /* need to do a full lookup. */
+  hash_value = hash_func(revision, path, path_len);
+
   bucket_index = hash_value + (hash_value >> 16);
   bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
 
@@ -297,16 +313,7 @@ cache_lookup( fs_fs_dag_cache_t *cache
       || (result->path_len != path_len)
       || memcmp(result->path, path, path_len))
     {
-      result->hash_value = hash_value;
-      result->revision = revision;
-      if (result->path_len < path_len)
-        result->path = apr_palloc(cache->pool, path_len + 1);
-      result->path_len = path_len;
-      memcpy(result->path, path, path_len + 1);
-
-      result->node = NULL;
-
-      cache->insertions++;
+      return NULL;
     }
   else if (result->node)
     {
@@ -315,7 +322,46 @@ cache_lookup( fs_fs_dag_cache_t *cache
       cache->last_non_empty = bucket_index;
     }
 
-  return result;
+  return result->node;
+}
+
+/* Store a copy of NODE in CACHE, taking  REVISION and PATH as key.
+ * This function will clean the cache at regular intervals.
+ */
+static void
+cache_insert(fs_fs_dag_cache_t *cache,
+             svn_revnum_t revision,
+             const char *path,
+             dag_node_t *node)
+{
+  apr_size_t bucket_index;
+  apr_size_t path_len = strlen(path);
+  apr_uint32_t hash_value;
+  cache_entry_t *entry;
+
+  auto_clear_dag_cache(cache);
+
+  /* calculate the bucket index to use */
+  hash_value = hash_func(revision, path, path_len);
+
+  bucket_index = hash_value + (hash_value >> 16);
+  bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
+
+  /* access the corresponding bucket and remember its location */
+  entry = &cache->buckets[bucket_index];
+  cache->last_hit = bucket_index;
+
+  /* if it is *NOT* a match,  clear the bucket, expect the caller to fill
+     in the node and count it as an insertion */
+  entry->hash_value = hash_value;
+  entry->revision = revision;
+  if (entry->path_len < path_len)
+    entry->path = apr_palloc(cache->pool, path_len + 1);
+  entry->path_len = path_len;
+  memcpy(entry->path, path, path_len + 1);
+
+  entry->node = svn_fs_fs__dag_dup(node, cache->pool);
+  cache->insertions++;
 }
 
 /* Optimistic lookup using the last seen non-empty location in CACHE.
@@ -393,11 +439,9 @@ dag_node_cache_get(dag_node_t **node_p,
       /* immutable DAG node. use the global caches for it */
 
       fs_fs_data_t *ffd = root->fs->fsap_data;
-      cache_entry_t *bucket;
 
-      auto_clear_dag_cache(ffd->dag_node_cache);
-      bucket = cache_lookup(ffd->dag_node_cache, root->rev, path);
-      if (bucket->node == NULL)
+      node = cache_lookup(ffd->dag_node_cache, root->rev, path);
+      if (node == NULL)
         {
           locate_cache(&cache, &key, root, path, pool);
           SVN_ERR(svn_cache__get((void **)&node, &found, cache, key, pool));
@@ -408,14 +452,13 @@ dag_node_cache_get(dag_node_t **node_p,
               svn_fs_fs__dag_set_fs(node, root->fs);
 
               /* Retain the DAG node in L1 cache. */
-              bucket->node = svn_fs_fs__dag_dup(node,
-                                                ffd->dag_node_cache->pool);
+              cache_insert(ffd->dag_node_cache, root->rev, path, node);
             }
         }
       else
         {
           /* Copy the node from L1 cache into the passed-in POOL. */
-          node = svn_fs_fs__dag_dup(bucket->node, pool);
+          node = svn_fs_fs__dag_dup(node, pool);
         }
     }
   else
@@ -1232,11 +1275,15 @@ get_dag(dag_node_t **dag_node_p,
     {
       /* Canonicalize the input PATH.  As it turns out, >95% of all paths
        * seen here during e.g. svnadmin verify are non-canonical, i.e.
-       * miss the leading '/'.  Unconditional canonicalization has a net
-       * performance benefit over previously checking path for being
-       * canonical. */
-      path = svn_fs__canonicalize_abspath(path, pool);
-      SVN_ERR(dag_node_cache_get(&node, root, path, pool));
+       * miss the leading '/'.  Check for those quickly.
+       *
+       * For normalized paths, it is much faster to check the path than
+       * to attempt a second cache lookup (which would fail). */
+      if (*path != '/' || !svn_fs__is_canonical_abspath(path))
+        {
+          path = svn_fs__canonicalize_abspath(path, pool);
+          SVN_ERR(dag_node_cache_get(&node, root, path, pool));
+        }
 
       if (! node)
         {
@@ -1328,8 +1375,8 @@ fs_node_relation(svn_fs_node_relation_t
                  apr_pool_t *pool)
 {
   dag_node_t *node;
-  const svn_fs_id_t *id;
-  svn_fs_fs__id_part_t rev_item_a, rev_item_b, node_id_a, node_id_b;
+  const svn_fs_id_t *id_a, *id_b;
+  svn_fs_fs__id_part_t node_id_a, node_id_b;
 
   /* Root paths are a common special case. */
   svn_boolean_t a_is_root_dir
@@ -1337,6 +1384,11 @@ fs_node_relation(svn_fs_node_relation_t
   svn_boolean_t b_is_root_dir
     = (path_b[0] == '\0') || ((path_b[0] == '/') && (path_b[1] == '\0'));
 
+  /* Another useful thing to know: Both are txns but not the same txn. */
+  svn_boolean_t different_txn
+    = root_a->is_txn_root && root_b->is_txn_root
+        && strcmp(root_a->txn, root_b->txn);
+
   /* Path from different repository are always unrelated. */
   if (root_a->fs != root_b->fs)
     {
@@ -1344,20 +1396,15 @@ fs_node_relation(svn_fs_node_relation_t
       return SVN_NO_ERROR;
     }
 
-  /* Nodes from different transactions are never related. */
-  if (root_a->is_txn_root && root_b->is_txn_root
-      && strcmp(root_a->txn, root_b->txn))
-    {
-      *relation = svn_fs_node_unrelated;
-      return SVN_NO_ERROR;
-    }
-
   /* Are both (!) root paths? Then, they are related and we only test how
    * direct the relation is. */
   if (a_is_root_dir && b_is_root_dir)
     {
-      *relation = root_a->rev == root_b->rev
-                ? svn_fs_node_same
+      /* For txn roots, root->REV is the base revision of that TXN. */
+      *relation = (   (root_a->rev == root_b->rev)
+                   && (root_a->is_txn_root == root_b->is_txn_root)
+                   && !different_txn)
+                ? svn_fs_node_unchanged
                 : svn_fs_node_common_ancestor;
       return SVN_NO_ERROR;
     }
@@ -1365,21 +1412,35 @@ fs_node_relation(svn_fs_node_relation_t
   /* We checked for all separations between ID spaces (repos, txn).
    * Now, we can simply test for the ID values themselves. */
   SVN_ERR(get_dag(&node, root_a, path_a, pool));
-  id = svn_fs_fs__dag_get_id(node);
-  rev_item_a = *svn_fs_fs__id_rev_item(id);
-  node_id_a = *svn_fs_fs__id_node_id(id);
+  id_a = svn_fs_fs__dag_get_id(node);
+  node_id_a = *svn_fs_fs__id_node_id(id_a);
 
   SVN_ERR(get_dag(&node, root_b, path_b, pool));
-  id = svn_fs_fs__dag_get_id(node);
-  rev_item_b = *svn_fs_fs__id_rev_item(id);
-  node_id_b = *svn_fs_fs__id_node_id(id);
-
-  if (svn_fs_fs__id_part_eq(&rev_item_a, &rev_item_b))
-    *relation = svn_fs_node_same;
-  else if (svn_fs_fs__id_part_eq(&node_id_a, &node_id_b))
-    *relation = svn_fs_node_common_ancestor;
+  id_b = svn_fs_fs__dag_get_id(node);
+  node_id_b = *svn_fs_fs__id_node_id(id_b);
+
+  /* Noderevs from different nodes are unrelated. */
+  if (!svn_fs_fs__id_part_eq(&node_id_a, &node_id_b))
+    {
+      *relation = svn_fs_node_unrelated;
+      return SVN_NO_ERROR;
+    }
+
+  /* Noderevs have the same node-ID now. So, they *seem* to be related.
+   *
+   * Special case: Different txns may create the same (txn-local) node ID.
+   * These are not related to each other, nor to any other node ID so far. */
+  if (different_txn && node_id_a.revision == SVN_INVALID_REVNUM)
+    {
+      *relation = svn_fs_node_unrelated;
+      return SVN_NO_ERROR;
+    }
+
+  /* The noderevs are actually related.  Are they the same? */
+  if (svn_fs_fs__id_eq(id_a, id_b))
+    *relation = svn_fs_node_unchanged;
   else
-    *relation = svn_fs_node_unrelated;
+    *relation = svn_fs_node_common_ancestor;
 
   return SVN_NO_ERROR;
 }
@@ -1501,6 +1562,19 @@ fs_node_proplist(apr_hash_t **table_p,
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+fs_node_has_props(svn_boolean_t *has_props,
+                  svn_fs_root_t *root,
+                  const char *path,
+                  apr_pool_t *scratch_pool)
+{
+  dag_node_t *node;
+
+  SVN_ERR(get_dag(&node, root, path, scratch_pool));
+
+  return svn_error_trace(svn_fs_fs__dag_has_props(has_props, node,
+                                                  scratch_pool));
+}
 
 static svn_error_t *
 increment_mergeinfo_up_tree(parent_path_t *pp,
@@ -1873,13 +1947,13 @@ merge(svn_stringbuf_t *conflict_p,
     /* Now compare the prop-keys of the skels.  Note that just because
        the keys are different -doesn't- mean the proplists have
        different contents. */
-    SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, src_nr, anc_nr, TRUE, pool));
+    SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, src_nr, anc_nr, pool));
     if (! same)
       return conflict_err(conflict_p, target_path);
 
     /* The directory entries got changed in the repository but the directory
        properties did not. */
-    SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, tgt_nr, anc_nr, TRUE, pool));
+    SVN_ERR(svn_fs_fs__prop_rep_equal(&same, fs, tgt_nr, anc_nr, pool));
     if (! same)
       {
         /* There is an incoming prop change for this directory.
@@ -2378,9 +2452,11 @@ static svn_error_t *
 fs_dir_optimal_order(apr_array_header_t **ordered_p,
                      svn_fs_root_t *root,
                      apr_hash_t *entries,
-                     apr_pool_t *pool)
+                     apr_pool_t *result_pool,
+                     apr_pool_t *scratch_pool)
 {
-  *ordered_p = svn_fs_fs__order_dir_entries(root->fs, entries, pool);
+  *ordered_p = svn_fs_fs__order_dir_entries(root->fs, entries, result_pool,
+                                            scratch_pool);
 
   return SVN_NO_ERROR;
 }
@@ -3159,23 +3235,18 @@ fs_contents_changed(svn_boolean_t *chang
       (SVN_ERR_FS_GENERAL, NULL,
        _("Cannot compare file contents between two different filesystems"));
 
-  /* Check that both paths are files. */
-  {
-    svn_node_kind_t kind;
-
-    SVN_ERR(svn_fs_fs__check_path(&kind, root1, path1, pool));
-    if (kind != svn_node_file)
-      return svn_error_createf
-        (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path1);
-
-    SVN_ERR(svn_fs_fs__check_path(&kind, root2, path2, pool));
-    if (kind != svn_node_file)
-      return svn_error_createf
-        (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
-  }
-
   SVN_ERR(get_dag(&node1, root1, path1, pool));
+  /* Make sure that path is file. */
+  if (svn_fs_fs__dag_node_kind(node1) != svn_node_file)
+    return svn_error_createf
+      (SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file"), path1);
+
   SVN_ERR(get_dag(&node2, root2, path2, pool));
+  /* Make sure that path is file. */
+  if (svn_fs_fs__dag_node_kind(node2) != svn_node_file)
+    return svn_error_createf
+      (SVN_ERR_FS_NOT_FILE, NULL, _("'%s' is not a file"), path2);
+
   return svn_fs_fs__dag_things_different(NULL, changed_p,
                                          node1, node2, strict, pool);
 }
@@ -3244,6 +3315,14 @@ typedef struct fs_history_data_t
 
   /* FALSE until the first call to svn_fs_history_prev(). */
   svn_boolean_t is_interesting;
+
+  /* If not SVN_INVALID_REVISION, we know that the next copy operation
+     is at this revision. */
+  svn_revnum_t next_copy;
+
+  /* If not NULL, this is the noderev ID of PATH@REVISION. */
+  const svn_fs_id_t *current_id;
+
 } fs_history_data_t;
 
 static svn_fs_history_t *
@@ -3253,6 +3332,8 @@ assemble_history(svn_fs_t *fs,
                  svn_boolean_t is_interesting,
                  const char *path_hint,
                  svn_revnum_t rev_hint,
+                 svn_revnum_t next_copy,
+                 const svn_fs_id_t *current_id,
                  apr_pool_t *pool);
 
 
@@ -3279,7 +3360,8 @@ fs_node_history(svn_fs_history_t **histo
 
   /* Okay, all seems well.  Build our history object and return it. */
   *history_p = assemble_history(root->fs, path, root->rev, FALSE, NULL,
-                                SVN_INVALID_REVNUM, result_pool);
+                                SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
+                                NULL, result_pool);
   return SVN_NO_ERROR;
 }
 
@@ -3580,10 +3662,50 @@ history_prev(svn_fs_history_t **prev_his
   svn_boolean_t reported = fhd->is_interesting;
   svn_revnum_t copyroot_rev;
   const char *copyroot_path;
+  const svn_fs_id_t *pred_id = NULL;
 
   /* Initialize our return value. */
   *prev_history = NULL;
 
+  /* When following history, there tend to be long sections of linear
+     history where there are no copies at PATH or its parents.  Within
+     these sections, we only need to follow the node history. */
+  if (   SVN_IS_VALID_REVNUM(fhd->next_copy)
+      && revision > fhd->next_copy
+      && fhd->current_id)
+    {
+      /* We know the last reported node (CURRENT_ID) and the NEXT_COPY
+         revision is somewhat further in the past. */
+      node_revision_t *noderev;
+      assert(reported);
+
+      /* Get the previous node change.  If there is none, then we already
+         reported the initial addition and this history traversal is done. */
+      SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, fhd->current_id,
+                                           scratch_pool, scratch_pool));
+      if (! noderev->predecessor_id)
+        return SVN_NO_ERROR;
+
+      /* If the previous node change is younger than the next copy, it is
+         part of the linear history section. */
+      commit_rev = svn_fs_fs__id_rev(noderev->predecessor_id);
+      if (commit_rev > fhd->next_copy)
+        {
+          /* Within the linear history, simply report all node changes and
+             continue with the respective predecessor. */
+          *prev_history = assemble_history(fs, noderev->created_path,
+                                           commit_rev, TRUE, NULL,
+                                           SVN_INVALID_REVNUM,
+                                           fhd->next_copy,
+                                           noderev->predecessor_id,
+                                           result_pool);
+
+          return SVN_NO_ERROR;
+        }
+
+     /* We hit a copy. Fall back to the standard code path. */
+    }
+
   /* If our last history report left us hints about where to pickup
      the chase, then our last report was on the destination of a
      copy.  If we are crossing copies, start from those locations,
@@ -3622,7 +3744,9 @@ history_prev(svn_fs_history_t **prev_his
              need now to do so) ... */
           *prev_history = assemble_history(fs, commit_path,
                                            commit_rev, TRUE, NULL,
-                                           SVN_INVALID_REVNUM, result_pool);
+                                           SVN_INVALID_REVNUM,
+                                           SVN_INVALID_REVNUM, NULL,
+                                           result_pool);
           return SVN_NO_ERROR;
         }
       else
@@ -3630,8 +3754,6 @@ history_prev(svn_fs_history_t **prev_his
           /* ... or we *have* reported on this revision, and must now
              progress toward this node's predecessor (unless there is
              no predecessor, in which case we're all done!). */
-          const svn_fs_id_t *pred_id;
-
           SVN_ERR(svn_fs_fs__dag_get_predecessor_id(&pred_id, node));
           if (! pred_id)
             return SVN_NO_ERROR;
@@ -3702,12 +3824,18 @@ history_prev(svn_fs_history_t **prev_his
         retry = TRUE;
 
       *prev_history = assemble_history(fs, path, dst_rev, ! retry,
-                                       src_path, src_rev, result_pool);
+                                       src_path, src_rev,
+                                       SVN_INVALID_REVNUM, NULL,
+                                       result_pool);
     }
   else
     {
+      /* We know the next copy revision.  If we are not at the copy rev
+         itself, we will also know the predecessor node ID and the next
+         invocation will use the optimized "linear history" code path. */
       *prev_history = assemble_history(fs, commit_path, commit_rev, TRUE,
-                                       NULL, SVN_INVALID_REVNUM, result_pool);
+                                       NULL, SVN_INVALID_REVNUM,
+                                       copyroot_rev, pred_id, result_pool);
     }
 
   return SVN_NO_ERROR;
@@ -3738,10 +3866,12 @@ fs_history_prev(svn_fs_history_t **prev_
       if (! fhd->is_interesting)
         prev_history = assemble_history(fs, "/", fhd->revision,
                                         1, NULL, SVN_INVALID_REVNUM,
+                                        SVN_INVALID_REVNUM, NULL,
                                         result_pool);
       else if (fhd->revision > 0)
         prev_history = assemble_history(fs, "/", fhd->revision - 1,
                                         1, NULL, SVN_INVALID_REVNUM,
+                                        SVN_INVALID_REVNUM, NULL,
                                         result_pool);
     }
   else
@@ -3801,6 +3931,8 @@ assemble_history(svn_fs_t *fs,
                  svn_boolean_t is_interesting,
                  const char *path_hint,
                  svn_revnum_t rev_hint,
+                 svn_revnum_t next_copy,
+                 const svn_fs_id_t *current_id,
                  apr_pool_t *pool)
 {
   svn_fs_history_t *history = apr_pcalloc(pool, sizeof(*history));
@@ -3811,6 +3943,8 @@ assemble_history(svn_fs_t *fs,
   fhd->path_hint = path_hint ? svn_fs__canonicalize_abspath(path_hint, pool)
                              : NULL;
   fhd->rev_hint = rev_hint;
+  fhd->next_copy = next_copy;
+  fhd->current_id = current_id ? svn_fs_fs__id_copy(current_id, pool) : NULL;
   fhd->fs = fs;
 
   history->vtable = &history_vtable;
@@ -4222,6 +4356,7 @@ static root_vtable_t root_vtable = {
   fs_closest_copy,
   fs_node_prop,
   fs_node_proplist,
+  fs_node_has_props,
   fs_change_node_prop,
   fs_props_changed,
   fs_dir_entries,

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/util.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/util.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/util.c Mon Nov 30 10:24:16 2015
@@ -435,8 +435,9 @@ svn_fs_fs__write_min_unpacked_rev(svn_fs
 
   final_path = svn_fs_fs__path_min_unpacked_rev(fs, scratch_pool);
 
-  SVN_ERR(svn_io_write_atomic(final_path, buf, len + 1,
-                              final_path /* copy_perms */, scratch_pool));
+  SVN_ERR(svn_io_write_atomic2(final_path, buf, len + 1,
+                               final_path /* copy_perms */, TRUE,
+                               scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -517,8 +518,8 @@ svn_fs_fs__write_current(svn_fs_t *fs,
     }
 
   name = svn_fs_fs__path_current(fs, pool);
-  SVN_ERR(svn_io_write_atomic(name, buf, strlen(buf),
-                              name /* copy_perms_path */, pool));
+  SVN_ERR(svn_io_write_atomic2(name, buf, strlen(buf),
+                               name /* copy_perms_path */, TRUE, pool));
 
   return SVN_NO_ERROR;
 }
@@ -637,51 +638,46 @@ svn_fs_fs__move_into_place(const char *o
                            apr_pool_t *pool)
 {
   svn_error_t *err;
+  apr_file_t *file;
 
+  /* Copying permissions is a no-op on WIN32. */
   SVN_ERR(svn_io_copy_perms(perms_reference, old_filename, pool));
 
   /* Move the file into place. */
-  err = svn_io_file_rename(old_filename, new_filename, pool);
+  err = svn_io_file_rename2(old_filename, new_filename, TRUE, pool);
   if (err && APR_STATUS_IS_EXDEV(err->apr_err))
     {
-      apr_file_t *file;
-
       /* Can't rename across devices; fall back to copying. */
       svn_error_clear(err);
-      err = SVN_NO_ERROR;
       SVN_ERR(svn_io_copy_file(old_filename, new_filename, TRUE, pool));
 
-      /* Flush the target of the copy to disk. */
-      SVN_ERR(svn_io_file_open(&file, new_filename, APR_READ,
+      /* Flush the target of the copy to disk.
+         ### The code below is duplicates svn_io_file_rename2(), because
+             currently we don't have the svn_io_copy_file2() function with
+             a flush_to_disk argument. */
+      SVN_ERR(svn_io_file_open(&file, new_filename, APR_WRITE,
                                APR_OS_DEFAULT, pool));
-      /* ### BH: Does this really guarantee a flush of the data written
-         ### via a completely different handle on all operating systems?
-         ###
-         ### Maybe we should perform the copy ourselves instead of making
-         ### apr do that and flush the real handle? */
       SVN_ERR(svn_io_file_flush_to_disk(file, pool));
       SVN_ERR(svn_io_file_close(file, pool));
-    }
-  if (err)
-    return svn_error_trace(err);
 
-#ifdef __linux__
-  {
-    /* Linux has the unusual feature that fsync() on a file is not
-       enough to ensure that a file's directory entries have been
-       flushed to disk; you have to fsync the directory as well.
-       On other operating systems, we'd only be asking for trouble
-       by trying to open and fsync a directory. */
-    const char *dirname;
-    apr_file_t *file;
-
-    dirname = svn_dirent_dirname(new_filename, pool);
-    SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
-                             pool));
-    SVN_ERR(svn_io_file_flush_to_disk(file, pool));
-    SVN_ERR(svn_io_file_close(file, pool));
-  }
+#ifdef SVN_ON_POSIX
+      {
+        /* On POSIX, the file name is stored in the file's directory entry.
+           Hence, we need to fsync() that directory as well.
+           On other operating systems, we'd only be asking for trouble
+           by trying to open and fsync a directory. */
+        const char *dirname;
+
+        dirname = svn_dirent_dirname(new_filename, pool);
+        SVN_ERR(svn_io_file_open(&file, dirname, APR_READ, APR_OS_DEFAULT,
+                                 pool));
+        SVN_ERR(svn_io_file_flush_to_disk(file, pool));
+        SVN_ERR(svn_io_file_close(file, pool));
+      }
 #endif
+    }
+  else if (err)
+    return svn_error_trace(err);
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/ra-git/subversion/libsvn_fs_fs/verify.c
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_fs_fs/verify.c?rev=1717223&r1=1717222&r2=1717223&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_fs_fs/verify.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_fs_fs/verify.c Mon Nov 30 10:24:16 2015
@@ -30,6 +30,7 @@
 
 #include "cached_data.h"
 #include "rep-cache.h"
+#include "revprops.h"
 #include "util.h"
 #include "index.h"
 
@@ -463,7 +464,8 @@ expect_buffer_nul(apr_file_t *file,
 
   /* read the whole data block; error out on failure */
   data.chunks[(size - 1)/ sizeof(apr_uint64_t)] = 0;
-  SVN_ERR(svn_io_file_read_full2(file, data.buffer, size, NULL, NULL, pool));
+  SVN_ERR(svn_io_file_read_full2(file, data.buffer, (apr_size_t)size, NULL,
+                                 NULL, pool));
 
   /* chunky check */
   for (i = 0; i < size / sizeof(apr_uint64_t); ++i)
@@ -720,6 +722,10 @@ verify_revprops(svn_fs_t *fs,
   svn_revnum_t revision;
   apr_pool_t *iterpool = svn_pool_create(pool);
 
+  /* Invalidate the revprop cache once.
+   * Use the cache inside the loop to speed up packed revprop access. */
+  svn_fs_fs__reset_revprop_cache(fs);
+
   for (revision = start; revision < end; ++revision)
     {
       svn_string_t *date;
@@ -730,7 +736,8 @@ verify_revprops(svn_fs_t *fs,
       /* Access the svn:date revprop.
        * This implies parsing all revprops for that revision. */
       SVN_ERR(svn_fs_fs__revision_prop(&date, fs, revision,
-                                       SVN_PROP_REVISION_DATE, iterpool));
+                                       SVN_PROP_REVISION_DATE, FALSE,
+                                       iterpool, iterpool));
 
       /* The time stamp is the only revprop that, if given, needs to
        * have a valid content. */
@@ -815,8 +822,15 @@ verify_f7_metadata_consistency(svn_fs_t
       /* concurrent packing is one of the reasons why verification may fail.
          Make sure, we operate on up-to-date information. */
       if (err)
-        SVN_ERR(svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev,
-                                                 fs, pool));
+        {
+          svn_error_t *err2
+            = svn_fs_fs__read_min_unpacked_rev(&ffd->min_unpacked_rev,
+                                               fs, pool);
+
+          /* Be careful to not leak ERR. */
+          if (err2)
+            return svn_error_trace(svn_error_compose_create(err, err2));
+        }
 
       /* retry the whole shard if it got packed in the meantime */
       if (err && count != pack_size(fs, revision))
@@ -850,13 +864,15 @@ svn_fs_fs__verify(svn_fs_t *fs,
                   apr_pool_t *pool)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
-  svn_revnum_t youngest = ffd->youngest_rev_cache; /* cache is current */
 
   /* Input validation. */
   if (! SVN_IS_VALID_REVNUM(start))
     start = 0;
   if (! SVN_IS_VALID_REVNUM(end))
-    end = youngest;
+    {
+      SVN_ERR(svn_fs_fs__youngest_rev(&end, fs, pool));
+    }
+
   SVN_ERR(svn_fs_fs__ensure_revision_exists(start, fs, pool));
   SVN_ERR(svn_fs_fs__ensure_revision_exists(end, fs, pool));
 

Propchange: subversion/branches/ra-git/subversion/libsvn_fs_x/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Mon Nov 30 10:24:16 2015
@@ -1,3 +1,4 @@
+/subversion/branches/1.10-cache-improvements/subversion/libsvn_fs_x:1669168-1694487
 /subversion/branches/1.5.x-r30215/subversion/libsvn_fs_x:870312
 /subversion/branches/1.7.x-fs-verify/subversion/libsvn_fs_x:1146708,1161180
 /subversion/branches/10Gb/subversion/libsvn_fs_x:1388102,1388163-1388190,1388195,1388202,1388205,1388211,1388276,1388362,1388375,1388394,1388636,1388639-1388640,1388643-1388644,1388654,1388720,1388789,1388795,1388801,1388805,1388807,1388810,1388816,1389044,1389276,1389289,1389662,1389867,1390017,1390209,1390216,1390407,1390409,1390414,1390419,1390955
@@ -17,10 +18,11 @@
 /subversion/branches/fs-rep-sharing/subversion/libsvn_fs_x:869036-873803
 /subversion/branches/fsfs-format7/subversion/libsvn_fs_x:1426304,1430673,1433848,1438408,1438982,1441129,1442051,1442068,1442504,1442910,1443171,1443803,1444690,1444693,1444695,1445040,1445080,1446103,1451129,1453590,1454307,1460579,1461851,1461865,1462837,1462904,1463120,1467362,1467382,1469487,1471208,1477166,1478055,1481447,1489817,1489949,1490673-1490674,1491784,1493042,1498029
 /subversion/branches/fsfs-improvements/subversion/libsvn_fs_fs:1499981-1593795
-/subversion/branches/fsfs-improvements/subversion/libsvn_fs_x:1499981-1517476
+/subversion/branches/fsfs-improvements/subversion/libsvn_fs_x:1499981-1517476,1532583,1536949,1545955,1546837
 /subversion/branches/fsfs-lock-many/subversion/libsvn_fs_x:1571740-1577217
 /subversion/branches/fsfs-pack/subversion/libsvn_fs_x:873717-874575
 /subversion/branches/fsx/subversion/libsvn_fs_x:1507845-1508040
+/subversion/branches/fsx-1.10/subversion/libsvn_fs_x:1658219-1694500
 /subversion/branches/fsx-id/subversion/libsvn_fs_x:1645603-1649011
 /subversion/branches/gnome-keyring/subversion/libsvn_fs_x:870558-871410
 /subversion/branches/gpg-agent-password-store/subversion/libsvn_fs_x:1005036-1150766
@@ -56,9 +58,11 @@
 /subversion/branches/log-addressing/subversion/libsvn_fs_x:1511324
 /subversion/branches/log-g-performance/subversion/libsvn_fs_x:870941-871032
 /subversion/branches/merge-skips-obstructions/subversion/libsvn_fs_x:874525-874615
+/subversion/branches/move-tracking-2/subversion/libsvn_fs_x:1606692-1714632
 /subversion/branches/multi-layer-moves/subversion/libsvn_fs_x:1239019-1300930
 /subversion/branches/nfc-nfd-aware-client/subversion/libsvn_fs_x:870276,870376
 /subversion/branches/node_pool/subversion/libsvn_fs_x:1304828-1305388
+/subversion/branches/patch-exec/subversion/libsvn_fs_x:1692717-1705390
 /subversion/branches/performance/subversion/libsvn_fs_x:979193,980118,981087,981090,981189,981194,981287,981684,981827,982043,982355,983398,983406,983430,983474,983488,983490,983760,983764,983766,983770,984927,984973,984984,985014,985037,985046,985472,985477,985482,985487-985488,985493,985497,985500,985514,985601,985603,985606,985669,985673,985695,985697,986453,986465,986485,986491-986492,986517,986521,986605,986608,986817,986832,987865,987868-987869,987872,987886-987888,987893,988319,988898,990330,990533,990535-990537,990541,990568,990572,990574-990575,990600,990759,992899,992904,992911,993127,993141,994956,995478,995507,995603,998012,998858,999098,1001413,1001417,1004291,1022668,1022670,1022676,1022715,1022719,1025660,1025672,1027193,1027203,1027206,1027214,1027227,1028077,1028092,1028094,1028104,1028107,1028111,1028354,1029038,1029042-1029043,1029054-1029055,1029062-1029063,1029078,1029080,1029090,1029092-1029093,1029111,1029151,1029158,1029229-1029230,1029232,1029335-1029336,102
 9339-1029340,1029342,1029344,1030763,1030827,1031203,1031235,1032285,1032333,1033040,1033057,1033294,1035869,1035882,1039511,1043705,1053735,1056015,1066452,1067683,1067697-1078365
 /subversion/branches/pin-externals/subversion/libsvn_fs_x:1643757-1659392
 /subversion/branches/py-tests-as-modules/subversion/libsvn_fs_x:956579-1033052
@@ -72,6 +76,7 @@
 /subversion/branches/subtree-mergeinfo/subversion/libsvn_fs_x:876734-878766
 /subversion/branches/svn-auth-x509/subversion/libsvn_fs_x:1603509-1655900
 /subversion/branches/svn-mergeinfo-enhancements/subversion/libsvn_fs_x:870119-870195,870197-870288
+/subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_fs_x:1642232-1695991
 /subversion/branches/svn-patch-improvements/subversion/libsvn_fs_x:918519-934609
 /subversion/branches/svn_mutex/subversion/libsvn_fs_x:1141683-1182099
 /subversion/branches/svnpatch-diff/subversion/libsvn_fs_x:865738-876477
@@ -89,5 +94,5 @@
 /subversion/branches/verify-at-commit/subversion/libsvn_fs_x:1462039-1462408
 /subversion/branches/verify-keep-going/subversion/libsvn_fs_x:1439280-1492639,1546002-1546110
 /subversion/branches/wc-collate-path/subversion/libsvn_fs_x:1402685-1480384
-/subversion/trunk/subversion/libsvn_fs_fs:1415133-1596500,1596567,1597414,1597989,1598273,1599140,1600872,1601633,1603485-1603487,1603499,1603605,1604128,1604188,1604413-1604414,1604416-1604417,1604421,1604442,1604700,1604717,1604720,1604726,1604755,1604794,1604802,1604824,1604836,1604844,1604902-1604903,1604911,1604925,1604933,1604947,1605059-1605060,1605064-1605065,1605068,1605071-1605073,1605075,1605123,1605188-1605189,1605191,1605197,1605444,1605633,1606132,1606142,1606144,1606514,1606526,1606528,1606551,1606554,1606564,1606598-1606599,1606656,1606658,1606662,1606744,1606840,1607085,1607572,1612407,1612810,1613339,1613872,1614611,1615348,1615351-1615352,1615356,1616338-1616339,1616613,1617586,1617688,1618138,1618151,1618153,1618226,1618641,1618653,1618662,1619068,1619358,1619413,1619769,1619774,1620602,1620909,1620912,1620928,1620930,1621275,1621635,1622931,1622937,1622942,1622946,1622959-1622960,1622963,1622987,1623007,1623368,1623373,1623377,1623379,1623381,1623398,1623402,162
 4011,1624265,1624512,1626246,1626871,1626873,1626886,1627497-1627498,1627502,1627947-1627949,1627966,1628083,1628093,1628158-1628159,1628161,1628392-1628393,1628415,1628427,1628676,1628738,1628762,1628764,1629854-1629855,1629857,1629865,1629873,1629875,1629879,1630067,1630070,1631049-1631051,1631075,1631115,1631171,1631180,1631185-1631186,1631196-1631197,1631239-1631240,1631548,1631550,1631563,1631567,1631588,1631598,1632646,1632776,1632849,1632851-1632853,1632856-1632857,1632868,1632908,1632926,1633232,1633617-1633618,1634872,1634875,1634879-1634880,1634920,1636478,1636483,1636629,1636644,1637184,1637186,1637330,1637358,1637363,1637393,1639319,1639322,1639335,1639348,1639352,1639355,1639358,1639414,1639419,1639426,1639430,1639436,1639440,1639549,1640061-1640062,1640197,1640915,1640966,1641013,1643139,1643233,1645567,1646021,1646712,1646716,1647537,1647540-1647541,1647820,1647905,1648230,1648238,1648241-1648243,1648253,1648272,1648532,1648537-1648539,1648542,1648591,1648612,1653608,
 1658482
-/subversion/trunk/subversion/libsvn_fs_x:1414756-1509914,1583624-1664077
+/subversion/trunk/subversion/libsvn_fs_fs:1415133-1596500,1596567,1597414,1597989,1598273,1599140,1600872,1601633,1603485-1603487,1603499,1603605,1604128,1604188,1604413-1604414,1604416-1604417,1604421,1604442,1604700,1604717,1604720,1604726,1604755,1604794,1604802,1604824,1604836,1604844,1604902-1604903,1604911,1604925,1604933,1604947,1605059-1605060,1605064-1605065,1605068,1605071-1605073,1605075,1605123,1605188-1605189,1605191,1605197,1605444,1605633,1606132,1606142,1606144,1606514,1606526,1606528,1606551,1606554,1606564,1606598-1606599,1606656,1606658,1606662,1606744,1606840,1607085,1607572,1612407,1612810,1613339,1613872,1614611,1615348,1615351-1615352,1615356,1616338-1616339,1616613,1617586,1617688,1618138,1618151,1618153,1618226,1618641,1618653,1618662,1619068,1619358,1619413,1619769,1619774,1620602,1620909,1620912,1620928,1620930,1621275,1621635,1622931,1622937,1622942,1622946,1622959-1622960,1622963,1622987,1623007,1623368,1623373,1623377,1623379,1623381,1623398,1623402,162
 4011,1624265,1624512,1626246,1626871,1626873,1626886,1627497-1627498,1627502,1627947-1627949,1627966,1628083,1628093,1628158-1628159,1628161,1628392-1628393,1628415,1628427,1628676,1628738,1628762,1628764,1629854-1629855,1629857,1629865,1629873,1629875,1629879,1630067,1630070,1631049-1631051,1631075,1631115,1631171,1631180,1631185-1631186,1631196-1631197,1631239-1631240,1631548,1631550,1631563,1631567,1631588,1631598,1632646,1632776,1632849,1632851-1632853,1632856-1632857,1632868,1632908,1632926,1633232,1633617-1633618,1634872,1634875,1634879-1634880,1634920,1636478,1636483,1636629,1636644,1637184,1637186,1637330,1637358,1637363,1637393,1639319,1639322,1639335,1639348,1639352,1639355,1639358,1639414,1639419,1639426,1639430,1639436,1639440,1639549,1640061-1640062,1640197,1640915,1640966,1641013,1643139,1643233,1645567,1646021,1646712,1646716,1647537,1647540-1647541,1647820,1647905,1648230,1648238,1648241-1648243,1648253,1648272,1648532,1648537-1648539,1648542,1648591,1648612,1649590,
 1651567,1652068,1652076,1652441,1652451,1653608,1654932,1654934,1654937,1655635,1655649,1655651,1655664,1656176,1657525,1657972,1657978,1658482,1659212,1659217,1659314,1659509,1662668,1665318,1665854,1665894,1667090,1667101,1667538,1669743,1669746,1669749,1669945,1670139,1670953,1673170,1673197,1673202,1673204,1673445,1673454,1673685,1673689,1673875,1674165,1674341,1674400,1674404,1674631,1674669,1674673,1675396,1676667,1677431,1678149,1678151,1678718,1678725,1679169,1679907,1679920-1679924,1679926,1680347,1680460,1680464,1680476,1680819,1681949,1681966,1681974,1681994,1682008,1682076,1682086,1682093,1682259,1682265,1682739,1682864,1683311,1683330,1683378,1683544,1683553,1684047,1686232,1686542,1686546,1686554,1686557,1687061,1687064,1687070-1687071,1687074,1687078-1687079,1688270,1688425,1692650,1693886,1694489,1694848,1696171,1696185,1696627-1696628,1696630,1696758,1697372,1697381,1697387,1697393,1697403,1697405,1701017,1701053,1702600,1702922,1703069,1703142,1703237,1703240,17052
 66,1705638,1705643,1705646,1705724,1705730,1705739,1706612,1706615,1706617,1706619,1706675-1706676,1706679,1706979-1706980,1707308,1707971-1707973,1707986,1707988-1707989,1708004
+/subversion/trunk/subversion/libsvn_fs_x:1414756-1509914,1583624-1717222