subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From br...@apache.org
Subject svn commit: r1556765 [4/12] - in /subversion/branches/fsfs-ucsnorm: ./ contrib/server-side/fsfsfixer/fixer/ subversion/bindings/javahl/native/ subversion/bindings/javahl/native/jniwrapper/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ su...
Date Thu, 09 Jan 2014 09:31:15 GMT
Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/transaction.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/transaction.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/transaction.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/transaction.c Thu Jan  9 09:31:10 2014
@@ -43,6 +43,7 @@
 #include "rep-cache.h"
 
 #include "private/svn_fs_util.h"
+#include "private/svn_sorts_private.h"
 #include "private/svn_subr_private.h"
 #include "private/svn_string_private.h"
 #include "../libsvn_fs/fs-loader.h"
@@ -86,6 +87,15 @@ 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)
@@ -554,49 +564,45 @@ store_sha1_rep_mapping(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
-static const char *
-unparse_dir_entry(svn_node_kind_t kind, const svn_fs_id_t *id,
+static svn_error_t *
+unparse_dir_entry(svn_fs_dirent_t *dirent,
+                  svn_stream_t *stream,
                   apr_pool_t *pool)
 {
-  return apr_psprintf(pool, "%s %s",
-                      (kind == svn_node_file) ? SVN_FS_FS__KIND_FILE
-                                              : SVN_FS_FS__KIND_DIR,
-                      svn_fs_fs__id_unparse(id, pool)->data);
+  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));
+  return SVN_NO_ERROR;
 }
 
-/* Given a hash ENTRIES of dirent structions, return a hash in
-   *STR_ENTRIES_P, that has svn_string_t as the values in the format
-   specified by the fs_fs directory contents file.  Perform
-   allocations in POOL. */
+/* Write the directory given as array of dirent structs in ENTRIES to STREAM.
+   Perform temporary allocations in POOL. */
 static svn_error_t *
-unparse_dir_entries(apr_hash_t **str_entries_p,
-                    apr_hash_t *entries,
+unparse_dir_entries(apr_array_header_t *entries,
+                    svn_stream_t *stream,
                     apr_pool_t *pool)
 {
-  apr_hash_index_t *hi;
-
-  /* For now, we use a our own hash function to ensure that we get a
-   * (largely) stable order when serializing the data.  It also gives
-   * us some performance improvement.
-   *
-   * ### TODO ###
-   * Use some sorted or other fixed order data container.
-   */
-  *str_entries_p = svn_hash__make(pool);
-
-  for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
+  apr_pool_t *iterpool = svn_pool_create(pool);
+  int i;
+  for (i = 0; i < entries->nelts; ++i)
     {
-      const void *key;
-      apr_ssize_t klen;
-      svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
-      const char *new_val;
-
-      apr_hash_this(hi, &key, &klen, NULL);
-      new_val = unparse_dir_entry(dirent->kind, dirent->id, pool);
-      apr_hash_set(*str_entries_p, key, klen,
-                   svn_string_create(new_val, pool));
+      svn_fs_dirent_t *dirent;
+
+      svn_pool_clear(iterpool);
+      dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
+      SVN_ERR(unparse_dir_entry(dirent, stream, iterpool));
     }
 
+  SVN_ERR(svn_stream_printf(stream, pool, "%s\n", SVN_HASH_TERMINATOR));
+
+  svn_pool_destroy(iterpool);
   return SVN_NO_ERROR;
 }
 
@@ -1144,6 +1150,7 @@ 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;
@@ -1156,7 +1163,9 @@ set_txn_proplist(svn_fs_t *fs,
   SVN_ERR(svn_stream_close(stream));
 
   /* Open the transaction properties file and write new contents to it. */
-  SVN_ERR(svn_io_write_atomic(path_txn_props(fs, txn_id, pool),
+  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;
@@ -1203,12 +1212,17 @@ svn_fs_fs__change_txn_props(svn_fs_txn_t
     {
       svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
 
+      if (svn_hash_gets(txn_prop, SVN_FS__PROP_TXN_CLIENT_DATE)
+          && !strcmp(prop->name, SVN_PROP_REVISION_DATE))
+        svn_hash_sets(txn_prop, SVN_FS__PROP_TXN_CLIENT_DATE,
+                      svn_string_create("1", pool));
+
       svn_hash_sets(txn_prop, prop->name, prop->value);
     }
 
   /* 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, pool));
+  SVN_ERR(set_txn_proplist(txn->fs, &ftd->txn_id, txn_prop, FALSE, pool));
 
   return SVN_NO_ERROR;
 }
@@ -1470,18 +1484,17 @@ svn_fs_fs__set_entry(svn_fs_t *fs,
 
   if (!rep || !is_txn_rep(rep))
     {
-      apr_hash_t *entries;
+      apr_array_header_t *entries;
 
       /* Before we can modify the directory, we need to dump its old
          contents into a mutable representation file. */
       SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, parent_noderev,
-                                          subpool));
-      SVN_ERR(unparse_dir_entries(&entries, entries, subpool));
+                                          subpool, subpool));
       SVN_ERR(svn_io_file_open(&file, filename,
                                APR_WRITE | APR_CREATE | APR_BUFFERED,
                                APR_OS_DEFAULT, pool));
       out = svn_stream_from_aprfile2(file, TRUE, pool);
-      SVN_ERR(svn_hash_write2(entries, out, SVN_HASH_TERMINATOR, subpool));
+      SVN_ERR(unparse_dir_entries(entries, out, subpool));
 
       svn_pool_clear(subpool);
 
@@ -1531,12 +1544,12 @@ svn_fs_fs__set_entry(svn_fs_t *fs,
   /* Append an incremental hash entry for the entry change. */
   if (id)
     {
-      const char *val = unparse_dir_entry(kind, id, subpool);
+      svn_fs_dirent_t entry;
+      entry.name = name;
+      entry.id = id;
+      entry.kind = kind;
 
-      SVN_ERR(svn_stream_printf(out, subpool, "K %" APR_SIZE_T_FMT "\n%s\n"
-                                "V %" APR_SIZE_T_FMT "\n%s\n",
-                                strlen(name), name,
-                                strlen(val), val));
+      SVN_ERR(unparse_dir_entry(&entry, out, subpool));
     }
   else
     {
@@ -1827,6 +1840,37 @@ rep_write_contents(void *baton,
     return svn_stream_write(b->rep_stream, data, len);
 }
 
+/* Set *SPANNED to the number of shards touched when walking WALK steps on
+ * NODEREV's predecessor chain in FS.  Use POOL for temporary allocations.
+ */
+static svn_error_t *
+shards_spanned(int *spanned,
+               svn_fs_t *fs,
+               node_revision_t *noderev,
+               int walk,
+               apr_pool_t *pool)
+{
+  fs_fs_data_t *ffd = fs->fsap_data;
+  int shard_size = ffd->max_files_per_dir ? ffd->max_files_per_dir : 1;
+
+  int count = 0;
+  int shard, last_shard = ffd->youngest_rev_cache / shard_size;
+  while (walk-- && noderev->predecessor_count)
+    {
+      SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs,
+                                           noderev->predecessor_id, pool));
+      shard = svn_fs_fs__id_rev(noderev->id) / shard_size;
+      if (shard != last_shard)
+        {
+          ++count;
+          last_shard = shard;
+        }
+    }
+
+  *spanned = count;
+  return SVN_NO_ERROR;
+}
+
 /* Given a node-revision NODEREV in filesystem FS, return the
    representation in *REP to use as the base for a text representation
    delta if PROPS is FALSE.  If PROPS has been set, a suitable props
@@ -1848,10 +1892,9 @@ choose_delta_base(representation_t **rep
   int walk;
   node_revision_t *base;
   fs_fs_data_t *ffd = fs->fsap_data;
-  svn_boolean_t maybe_shared_rep = FALSE;
 
-  /* If we have no predecessors, then use the empty stream as a
-     base. */
+  /* If we have no predecessors, or that one is empty, then use the empty
+   * stream as a base. */
   if (! noderev->predecessor_count)
     {
       *rep = NULL;
@@ -1865,73 +1908,80 @@ choose_delta_base(representation_t **rep
   count = noderev->predecessor_count;
   count = count & (count - 1);
 
-  /* We use skip delta for limiting the number of delta operations
-     along very long node histories.  Close to HEAD however, we create
-     a linear history to minimize delta size.  */
-  walk = noderev->predecessor_count - count;
-  if (walk < (int)ffd->max_linear_deltification)
-    count = noderev->predecessor_count - 1;
-
   /* Finding the delta base over a very long distance can become extremely
      expensive for very deep histories, possibly causing client timeouts etc.
      OTOH, this is a rare operation and its gains are minimal. Lets simply
      start deltification anew close every other 1000 changes or so.  */
+  walk = noderev->predecessor_count - count;
   if (walk > (int)ffd->max_deltification_walk)
     {
       *rep = NULL;
       return SVN_NO_ERROR;
     }
 
+  /* We use skip delta for limiting the number of delta operations
+     along very long node histories.  Close to HEAD however, we create
+     a linear history to minimize delta size.  */
+  if (walk < (int)ffd->max_linear_deltification)
+    {
+      int shards;
+      SVN_ERR(shards_spanned(&shards, fs, noderev, walk, pool));
+
+      /* We also don't want the linear deltification to span more shards
+         than deltas would be used in the simple skip-delta schme. */
+      if ((1 << (--shards)) <= walk)
+        count = noderev->predecessor_count - 1;
+    }
+
   /* Walk back a number of predecessors equal to the difference
      between count and the original predecessor count.  (For example,
      if noderev has ten predecessors and we want the eighth file rev,
      walk back two predecessors.) */
   base = noderev;
   while ((count++) < noderev->predecessor_count)
-    {
-      svn_revnum_t base_revision;
-      SVN_ERR(svn_fs_fs__get_node_revision(&base, fs,
-                                           base->predecessor_id, pool));
-
-      /* If there is a shared rep along the way, we need to limit the
-       * length of the deltification chain.
-       *
-       * Please note that copied nodes - such as branch directories - will
-       * look the same (false positive) while reps shared within the same
-       * revision will not be caught (false negative).
-       *
-       * Message-ID: <CA+t0gk1wzitkih3GRCLDvK-bTEm=hgppGb_7xXMtvuXDYPfL+Q@mail.gmail.com>
-       */
-      base_revision = svn_fs_fs__id_rev(base->id);
-      if (props)
-        {
-          if (base->prop_rep && base_revision > base->prop_rep->revision)
-            maybe_shared_rep = TRUE;
-        }
-      else
-        {
-          if (base->data_rep && base_revision > base->data_rep->revision)
-            maybe_shared_rep = TRUE;
-        }
-    }
+    SVN_ERR(svn_fs_fs__get_node_revision(&base, fs,
+                                         base->predecessor_id, pool));
 
   /* return a suitable base representation */
   *rep = props ? base->prop_rep : base->data_rep;
 
   /* if we encountered a shared rep, its parent chain may be different
    * from the node-rev parent chain. */
-  if (*rep && maybe_shared_rep)
+  if (*rep)
     {
+      int chain_length = 0;
+      int shard_count = 0;
+
+      /* 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;
+      if (rep_size < 64)
+        {
+          *rep = NULL;
+          return SVN_NO_ERROR;
+        }
+
       /* Check whether the length of the deltification chain is acceptable.
        * Otherwise, shared reps may form a non-skipping delta chain in
        * extreme cases. */
-      int chain_length = 0;
-      SVN_ERR(svn_fs_fs__rep_chain_length(&chain_length, *rep, fs, pool));
+      SVN_ERR(svn_fs_fs__rep_chain_length(&chain_length, &shard_count,
+                                          *rep, fs, pool));
 
       /* Some reasonable limit, depending on how acceptable longer linear
        * chains are in this repo.  Also, allow for some minimal chain. */
       if (chain_length >= 2 * (int)ffd->max_linear_deltification + 2)
         *rep = NULL;
+      else
+        /* To make it worth opening additional shards / pack files, we
+         * require that the reps have a certain minimal size.  To deltify
+         * against a rep in different shard, the lower limit is 512 bytes
+         * and doubles with every extra shard to visit along the delta
+         * chain. */
+        if (   shard_count > 1
+            && ((svn_filesize_t)128 << shard_count) >= rep_size)
+          *rep = NULL;
     }
 
   return SVN_NO_ERROR;
@@ -2406,8 +2456,8 @@ get_next_revision_ids(apr_uint64_t *node
   return SVN_NO_ERROR;
 }
 
-/* This baton is used by the stream created for write_hash_rep. */
-struct write_hash_baton
+/* This baton is used by the stream created for write_container_rep. */
+struct write_container_baton
 {
   svn_stream_t *stream;
 
@@ -2417,15 +2467,15 @@ struct write_hash_baton
   svn_checksum_ctx_t *sha1_ctx;
 };
 
-/* The handler for the write_hash_rep stream.  BATON is a
-   write_hash_baton, DATA has the data to write and *LEN is the number
+/* The handler for the write_container_rep stream.  BATON is a
+   write_container_baton, DATA has the data to write and *LEN is the number
    of bytes to write. */
 static svn_error_t *
-write_hash_handler(void *baton,
-                   const char *data,
-                   apr_size_t *len)
+write_container_handler(void *baton,
+                        const char *data,
+                        apr_size_t *len)
 {
-  struct write_hash_baton *whb = 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));
@@ -2436,9 +2486,39 @@ write_hash_handler(void *baton,
   return SVN_NO_ERROR;
 }
 
-/* Write out the hash HASH as a text representation to file FILE.  In
-   the process, record position, the total size of the dump and MD5 as
-   well as SHA1 in REP.   Add the representation of type ITEM_TYPE to
+/* Callback function type.  Write the data provided by BATON into STREAM. */
+typedef svn_error_t *
+(* collection_writer_t)(svn_stream_t *stream, void *baton, apr_pool_t *pool);
+
+/* Implement collection_writer_t writing the C string->svn_string_t hash
+   given as BATON. */
+static svn_error_t *
+write_hash_to_stream(svn_stream_t *stream,
+                     void *baton,
+                     apr_pool_t *pool)
+{
+  apr_hash_t *hash = baton;
+  SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Implement collection_writer_t writing the svn_fs_dirent_t* array given
+   as BATON. */
+static svn_error_t *
+write_directory_to_stream(svn_stream_t *stream,
+                          void *baton,
+                          apr_pool_t *pool)
+{
+  apr_array_header_t *dir = baton;
+  SVN_ERR(unparse_dir_entries(dir, stream, pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Write out the COLLECTION as a text representation to file FILE using
+   WRITER.  In the process, record position, the total size of the dump and
+   MD5 as well as SHA1 in REP.   Add the representation of type ITEM_TYPE to
    the indexes if necessary.  If rep sharing has been enabled and REPS_HASH
    is not NULL, it will be used in addition to the on-disk cache to find
    earlier reps with the same content.  When such existing reps can be
@@ -2447,17 +2527,18 @@ write_hash_handler(void *baton,
    to determine whether to write to the proto-index files.
    Perform temporary allocations in POOL. */
 static svn_error_t *
-write_hash_rep(representation_t *rep,
-               apr_file_t *file,
-               apr_hash_t *hash,
-               svn_fs_t *fs,
-               apr_hash_t *reps_hash,
-               int item_type,
-               svn_revnum_t final_revision,
-               apr_pool_t *pool)
+write_container_rep(representation_t *rep,
+                    apr_file_t *file,
+                    void *collection,
+                    collection_writer_t writer,
+                    svn_fs_t *fs,
+                    apr_hash_t *reps_hash,
+                    int item_type,
+                    svn_revnum_t final_revision,
+                    apr_pool_t *pool)
 {
   svn_stream_t *stream;
-  struct write_hash_baton *whb;
+  struct write_container_baton *whb;
   svn_checksum_ctx_t *fnv1a_checksum_ctx;
   representation_t *old_rep;
   apr_off_t offset = 0;
@@ -2474,11 +2555,11 @@ write_hash_rep(representation_t *rep,
   whb->sha1_ctx = svn_checksum_ctx_create(svn_checksum_sha1, pool);
 
   stream = svn_stream_create(whb, pool);
-  svn_stream_set_write(stream, write_hash_handler);
+  svn_stream_set_write(stream, write_container_handler);
 
   SVN_ERR(svn_stream_puts(whb->stream, "PLAIN\n"));
 
-  SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool));
+  SVN_ERR(writer(stream, collection, pool));
 
   /* Store the results. */
   SVN_ERR(digests_final(rep, whb->md5_ctx, whb->sha1_ctx, pool));
@@ -2526,28 +2607,31 @@ write_hash_rep(representation_t *rep,
   return SVN_NO_ERROR;
 }
 
-/* Write out the hash HASH pertaining to the NODEREV in FS as a deltified
-   text representation to file FILE.  In the process, record the total size
-   and the md5 digest in REP and add the representation of type ITEM_TYPE
-   to the indexes if necessary.  If rep sharing has been enabled and REPS_HASH
-   is not NULL, it will be used in addition to the on-disk cache to find
-   earlier reps with the same content.  When such existing reps can be found,
-   we will truncate the one just written from the file and return the existing
-   rep.  If ITEM_TYPE is IS_PROPS equals SVN_FS_FS__ITEM_TYPE_*_PROPS, assume
+/* Write out the COLLECTION pertaining to the NODEREV in FS as a deltified
+   text representation to file FILE using WRITER.  In the process, record the
+   total size and the md5 digest in REP and add the representation of type
+   ITEM_TYPE to the indexes if necessary.  If rep sharing has been enabled and
+   REPS_HASH is not NULL, it will be used in addition to the on-disk cache to
+   find earlier reps with the same content.  When such existing reps can be
+   found, we will truncate the one just written from the file and return the
+   existing rep.
+
+   If ITEM_TYPE is IS_PROPS equals SVN_FS_FS__ITEM_TYPE_*_PROPS, assume
    that we want to a props representation as the base for our delta.
    If FINAL_REVISION is not SVN_INVALID_REVNUM, use it to determine whether
    to write to the proto-index files.  Perform temporary allocations in POOL.
  */
 static svn_error_t *
-write_hash_delta_rep(representation_t *rep,
-                     apr_file_t *file,
-                     apr_hash_t *hash,
-                     svn_fs_t *fs,
-                     node_revision_t *noderev,
-                     apr_hash_t *reps_hash,
-                     int item_type,
-                     svn_revnum_t final_revision,
-                     apr_pool_t *pool)
+write_container_delta_rep(representation_t *rep,
+                          apr_file_t *file,
+                          void *collection,
+                          collection_writer_t writer,
+                          svn_fs_t *fs,
+                          node_revision_t *noderev,
+                          apr_hash_t *reps_hash,
+                          int item_type,
+                          svn_revnum_t final_revision,
+                          apr_pool_t *pool)
 {
   svn_txdelta_window_handler_t diff_wh;
   void *diff_whb;
@@ -2564,7 +2648,7 @@ write_hash_delta_rep(representation_t *r
   apr_off_t delta_start = 0;
   apr_off_t offset = 0;
 
-  struct write_hash_baton *whb;
+  struct write_container_baton *whb;
   fs_fs_data_t *ffd = fs->fsap_data;
   int diff_version = ffd->format >= SVN_FS_FS__MIN_SVNDIFF1_FORMAT ? 1 : 0;
   svn_boolean_t is_props = (item_type == SVN_FS_FS__ITEM_TYPE_FILE_PROPS)
@@ -2611,9 +2695,9 @@ write_hash_delta_rep(representation_t *r
 
   /* serialize the hash */
   stream = svn_stream_create(whb, pool);
-  svn_stream_set_write(stream, write_hash_handler);
+  svn_stream_set_write(stream, write_container_handler);
 
-  SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool));
+  SVN_ERR(writer(stream, collection, pool));
   SVN_ERR(svn_stream_close(whb->stream));
 
   /* Store the results. */
@@ -2809,24 +2893,18 @@ write_final_rev(const svn_fs_id_t **new_
   if (noderev->kind == svn_node_dir)
     {
       apr_pool_t *subpool;
-      apr_hash_t *entries, *str_entries;
-      apr_array_header_t *sorted_entries;
+      apr_array_header_t *entries;
       int i;
 
       /* This is a directory.  Write out all the children first. */
       subpool = svn_pool_create(pool);
 
-      SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev, pool));
-      /* For the sake of the repository administrator sort the entries
-         so that the final file is deterministic and repeatable,
-         however the rest of the FSFS code doesn't require any
-         particular order here. */
-      sorted_entries = svn_sort__hash(entries, svn_sort_compare_items_lexically,
-                                      pool);
-      for (i = 0; i < sorted_entries->nelts; ++i)
+      SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev, pool,
+                                          subpool));
+      for (i = 0; i < entries->nelts; ++i)
         {
-          svn_fs_dirent_t *dirent = APR_ARRAY_IDX(sorted_entries, i,
-                                                  svn_sort__item_t).value;
+          svn_fs_dirent_t *dirent
+            = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
 
           svn_pool_clear(subpool);
           SVN_ERR(write_final_rev(&new_id, file, rev, fs, dirent->id,
@@ -2841,19 +2919,19 @@ write_final_rev(const svn_fs_id_t **new_
       if (noderev->data_rep && is_txn_rep(noderev->data_rep))
         {
           /* Write out the contents of this directory as a text rep. */
-          SVN_ERR(unparse_dir_entries(&str_entries, entries, pool));
-
           noderev->data_rep->revision = rev;
-
           if (ffd->deltify_directories)
-            SVN_ERR(write_hash_delta_rep(noderev->data_rep, file,
-                                         str_entries, fs, noderev, NULL,
-                                         SVN_FS_FS__ITEM_TYPE_DIR_REP,
-                                         rev, pool));
+            SVN_ERR(write_container_delta_rep(noderev->data_rep, file,
+                                              entries,
+                                              write_directory_to_stream,
+                                              fs, noderev, NULL,
+                                              SVN_FS_FS__ITEM_TYPE_DIR_REP,
+                                              rev, pool));
           else
-            SVN_ERR(write_hash_rep(noderev->data_rep, file, str_entries,
-                                   fs, NULL, SVN_FS_FS__ITEM_TYPE_DIR_REP,
-                                   rev, pool));
+            SVN_ERR(write_container_rep(noderev->data_rep, file, entries,
+                                        write_directory_to_stream, fs, NULL,
+                                        SVN_FS_FS__ITEM_TYPE_DIR_REP, rev,
+                                        pool));
 
           reset_txn_in_rep(noderev->data_rep);
         }
@@ -2894,12 +2972,13 @@ write_final_rev(const svn_fs_id_t **new_
       noderev->prop_rep->revision = rev;
 
       if (ffd->deltify_properties)
-        SVN_ERR(write_hash_delta_rep(noderev->prop_rep, file,
-                                     proplist, fs, noderev, reps_hash,
-                                     item_type, rev, pool));
+        SVN_ERR(write_container_delta_rep(noderev->prop_rep, file, proplist,
+                                          write_hash_to_stream, fs, noderev,
+                                          reps_hash, item_type, rev, pool));
       else
-        SVN_ERR(write_hash_rep(noderev->prop_rep, file, proplist,
-                               fs, reps_hash, item_type, rev, pool));
+        SVN_ERR(write_container_rep(noderev->prop_rep, file, proplist,
+                                    write_hash_to_stream, fs, reps_hash,
+                                    item_type, rev, pool));
 
       reset_txn_in_rep(noderev->prop_rep);
     }
@@ -3309,7 +3388,7 @@ verify_moves(svn_fs_t *fs,
     {
       const char *deleted_path = APR_ARRAY_IDX(deletions, i, const char*);
       int closest_move_idx
-        = svn_sort__bsearch_lower_bound(deleted_path, moves,
+        = svn_sort__bsearch_lower_bound(moves, deleted_path,
                                         svn_sort_compare_paths);
 
       if (closest_move_idx < moves->nelts)
@@ -3365,7 +3444,7 @@ verify_moves(svn_fs_t *fs,
          (or any of its parents) */
 
       int closest_deletion_idx
-        = svn_sort__bsearch_lower_bound(change->copyfrom_path, deletions,
+        = svn_sort__bsearch_lower_bound(deletions, change->copyfrom_path,
                                         svn_sort_compare_paths);
       if (closest_deletion_idx < deletions->nelts)
         {
@@ -3620,12 +3699,71 @@ upgrade_transaction(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. */
+static svn_error_t *
+write_final_revprop(const char **path,
+                    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;
+
+  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;
+    }
+
+  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
+     requested. */
+  if (!client_date || strcmp(client_date->data, "1"))
+    {
+      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);
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Baton used for commit_body below. */
 struct commit_baton {
   svn_revnum_t *new_rev_p;
   svn_fs_t *fs;
   svn_fs_txn_t *txn;
-  svn_boolean_t set_timestamp;
   apr_array_header_t *reps_to_cache;
   apr_hash_t *reps_hash;
   apr_pool_t *reps_pool;
@@ -3648,9 +3786,6 @@ commit_body(void *baton, apr_pool_t *poo
   apr_file_t *proto_file;
   void *proto_file_lockcookie;
   apr_off_t initial_offset, changed_path_offset;
-  apr_hash_t *txnprops;
-  apr_array_header_t *txnprop_list;
-  svn_prop_t prop;
   const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(cb->txn);
   apr_hash_t *changed_paths;
 
@@ -3736,30 +3871,6 @@ commit_body(void *baton, apr_pool_t *poo
      race with another caller writing to the prototype revision file
      before we commit it. */
 
-  /* Remove any temporary txn props representing 'flags'. 
-
-     ### This is a permanent change to the transaction.  If this
-     ### commit does not complete for any reason the transaction will
-     ### still exist but will have lost these properties. */
-  SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, cb->txn, pool));
-  txnprop_list = apr_array_make(pool, 3, sizeof(svn_prop_t));
-  prop.value = NULL;
-
-  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
-    {
-      prop.name = SVN_FS__PROP_TXN_CHECK_OOD;
-      APR_ARRAY_PUSH(txnprop_list, svn_prop_t) = prop;
-    }
-
-  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
-    {
-      prop.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
-      APR_ARRAY_PUSH(txnprop_list, svn_prop_t) = prop;
-    }
-
-  if (! apr_is_empty_array(txnprop_list))
-    SVN_ERR(svn_fs_fs__change_txn_props(cb->txn, txnprop_list, pool));
-
   /* Create the shard for the rev and revprop file, if we're sharding and
      this is the first revision of a new shard.  We don't care if this
      fails because the shard already existed for some reason. */
@@ -3828,22 +3939,9 @@ 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));
 
-  /* Update commit time to ensure that svn:date revprops remain ordered if
-     requested. */
-  if (cb->set_timestamp)
-    {
-      svn_string_t date;
-
-      date.data = svn_time_to_cstring(apr_time_now(), pool);
-      date.len = strlen(date.data);
-
-      SVN_ERR(svn_fs_fs__change_txn_prop(cb->txn, SVN_PROP_REVISION_DATE,
-                                         &date, pool));
-    }
-
   /* Move the revprops file into place. */
   SVN_ERR_ASSERT(! svn_fs_fs__is_packed_revprop(cb->fs, new_rev));
-  revprop_filename = path_txn_props(cb->fs, txn_id, pool);
+  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));
@@ -3893,7 +3991,6 @@ svn_error_t *
 svn_fs_fs__commit(svn_revnum_t *new_rev_p,
                   svn_fs_t *fs,
                   svn_fs_txn_t *txn,
-                  svn_boolean_t set_timestamp,
                   apr_pool_t *pool)
 {
   struct commit_baton cb;
@@ -3902,7 +3999,6 @@ svn_fs_fs__commit(svn_revnum_t *new_rev_
   cb.new_rev_p = new_rev_p;
   cb.fs = fs;
   cb.txn = txn;
-  cb.set_timestamp = set_timestamp;
 
   if (ffd->rep_sharing_allowed)
     {
@@ -4149,6 +4245,11 @@ svn_fs_fs__begin_txn(svn_fs_txn_t **txn_
     svn_hash_sets(props, SVN_FS__PROP_TXN_CHECK_LOCKS,
                   svn_string_create("true", pool));
 
+  if (flags & SVN_FS_TXN_CLIENT_DATE)
+    svn_hash_sets(props, SVN_FS__PROP_TXN_CLIENT_DATE,
+                  svn_string_create("0", pool));
+
   ftd = (*txn_p)->fsap_data;
-  return svn_error_trace(set_txn_proplist(fs, &ftd->txn_id, props, pool));
+  return svn_error_trace(set_txn_proplist(fs, &ftd->txn_id, props, FALSE,
+                                          pool));
 }

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/transaction.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/transaction.h?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/transaction.h (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/transaction.h Thu Jan  9 09:31:10 2014
@@ -199,14 +199,12 @@ svn_fs_fs__set_proplist(svn_fs_t *fs,
 
 /* Commit the transaction TXN in filesystem FS and return its new
    revision number in *REV.  If the transaction is out of date, return
-   the error SVN_ERR_FS_TXN_OUT_OF_DATE. Update commit time to ensure that
-   svn:date revprops remain ordered if SET_TIMESTAMP is non-zero. Use POOL for
-   temporary allocations. */
+   the error SVN_ERR_FS_TXN_OUT_OF_DATE. Use POOL for temporary
+   allocations. */
 svn_error_t *
 svn_fs_fs__commit(svn_revnum_t *new_rev_p,
                   svn_fs_t *fs,
                   svn_fs_txn_t *txn,
-                  svn_boolean_t set_timestamp,
                   apr_pool_t *pool);
 
 /* Set *NAMES_P to an array of names which are all the active

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/tree.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/tree.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/tree.c Thu Jan  9 09:31:10 2014
@@ -49,6 +49,7 @@
 #include "svn_mergeinfo.h"
 #include "svn_fs.h"
 #include "svn_props.h"
+#include "svn_sorts.h"
 
 #include "fs.h"
 #include "cached_data.h"
@@ -461,7 +462,9 @@ locate_cache(svn_cache__t **cache,
 
    Since locking can be expensive and POOL may be long-living, for
    nodes that will not need to survive the next call to this function,
-   set NEEDS_LOCK_CACHE to FALSE. */
+   set NEEDS_LOCK_CACHE to FALSE.
+
+   If normalized lookup is enabled, PATH *must* be normalized. */
 static svn_error_t *
 dag_node_cache_get(dag_node_t **node_p,
                    svn_fs_root_t *root,
@@ -529,7 +532,9 @@ dag_node_cache_get(dag_node_t **node_p,
 }
 
 
-/* Add the NODE for PATH to ROOT's node cache. */
+/* Add the NODE for PATH to ROOT's node cache.
+
+   If normalized lookup is enabled, PATH *must* be normalized. */
 static svn_error_t *
 dag_node_cache_set(svn_fs_root_t *root,
                    const char *path,
@@ -614,28 +619,6 @@ dag_node_cache_invalidate(svn_fs_root_t 
 }
 
 
-/* Create a key for path lookup. The path in the key struct will
-   always be canonical. */
-static svn_error_t *
-create_path_key(fs_fs_path_t *result,
-                const char *path,
-                svn_boolean_t normalize,
-                apr_pool_t *result_pool)
-{
-  result->path = svn_fs__canonicalize_abspath(path, result_pool);
-  if (normalize)
-    {
-      svn_membuf_t buffer;
-      svn_membuf__create(&buffer, 0, result_pool);
-      SVN_ERR(svn_utf__normalize(&result->keypath, result->path,
-                                 SVN_UTF__UNKNOWN_LENGTH, &buffer));
-    }
-  else
-    result->keypath = result->path;
-  return SVN_NO_ERROR;
-}
-
-
 
 /* Creating transaction and revision root nodes.  */
 
@@ -751,6 +734,57 @@ typedef enum copy_id_inherit_t
 
 } copy_id_inherit_t;
 
+/* Structure used to propagate paths throught the FSFS implementation. */
+typedef struct path_lookup_t
+{
+  /* The original path, as found on disk or received by the API. */
+  const char *path;
+
+  /* The representation of PATH used for cache keys and
+     lookups. Depending on whether normalized lookups are enabled,
+     this will either be exactly the same pointer as PATH (i.e.,
+     normalization is disabled), or it will be a normalized
+     representation of PATH (which may turn out to be exactly the same
+     as PATH). The invariant is:
+
+         (normalized_lookup == false) ==> (path == key)
+
+     Note that when normalized lookup is enabled and PATH is already
+     normalized, KEY will still be the same as PATH, in order to make
+     lookups slightly faster.
+  */
+  const char *key;
+} path_lookup_t;
+
+
+/* Initialize *LOOKUP as a key for path lookup.
+   LOOKUP->path will always be canonical. */
+static svn_error_t *
+create_path_lookup(path_lookup_t *lookup,
+                   const char *path,
+                   svn_fs_root_t *root,
+                   apr_pool_t *result_pool)
+{
+  const svn_boolean_t normalized_lookup =
+    ((fs_fs_data_t*)root->fs->fsap_data)->normalized_lookup;
+
+  lookup->path = svn_fs__canonicalize_abspath(path, result_pool);
+  if (normalized_lookup)
+    {
+      svn_membuf_t buffer;
+      svn_membuf__create(&buffer, 0, result_pool);
+      SVN_ERR(svn_utf__normalize(&lookup->key, lookup->path,
+                                 SVN_UTF__UNKNOWN_LENGTH, &buffer));
+      if (0 == strcmp(lookup->key, lookup->path))
+        lookup->key = lookup->path;
+    }
+  else
+    {
+      lookup->key = lookup->path;
+    }
+  return SVN_NO_ERROR;
+}
+
 /* A linked list representing the path from a node up to a root
    directory.  We use this for cloning, and for operations that need
    to deal with both a node and its parent directory.  For example, a
@@ -768,6 +802,10 @@ typedef struct parent_path_t
      root directory, which (obviously) has no name in its parent.  */
   char *entry;
 
+  /* The normalized form of ENTRY. The same invariant obtains as for
+     path_lookup_t::key. */
+  char *entry_key;
+
   /* The parent of NODE, or zero if NODE is the root directory.  */
   struct parent_path_t *parent;
 
@@ -794,8 +832,7 @@ parent_path_path(parent_path_t *parent_p
     : path_so_far;
 }
 
-
-/* Return the FS path for the parent path chain object CHILD relative
+/* return the FS path for the parent path chain object CHILD relative
    to its ANCESTOR in the same chain, allocated in POOL.  */
 static const char *
 parent_path_relpath(parent_path_t *child,
@@ -903,16 +940,18 @@ get_copy_inheritance(copy_id_inherit_t *
 }
 
 /* Allocate a new parent_path_t node from POOL, referring to NODE,
-   ENTRY, PARENT, and COPY_ID.  */
+   ENTRY, ENTRY_KEY, PARENT, and COPY_ID.  */
 static parent_path_t *
 make_parent_path(dag_node_t *node,
                  char *entry,
+                 char *entry_key,
                  parent_path_t *parent,
                  apr_pool_t *pool)
 {
   parent_path_t *parent_path = apr_pcalloc(pool, sizeof(*parent_path));
   parent_path->node = node;
   parent_path->entry = entry;
+  parent_path->entry_key = entry_key;
   parent_path->parent = parent;
   parent_path->copy_inherit = copy_id_inherit_unknown;
   parent_path->copy_src_path = NULL;
@@ -940,7 +979,7 @@ typedef enum open_path_flags_t {
 } open_path_flags_t;
 
 
-/* Open the node identified by PATH in ROOT, allocating in POOL.  Set
+/* Open the node identified by LOOKUP in ROOT, allocating in POOL.  Set
    *PARENT_PATH_P to a path from the node up to ROOT.  The resulting
    **PARENT_PATH_P value is guaranteed to contain at least one
    *element, for the root directory.  PATH must be in canonical form.
@@ -951,7 +990,7 @@ typedef enum open_path_flags_t {
    inheritance information will be calculated for the *PARENT_PATH_P chain.
 
    If FLAGS & open_path_last_optional is zero, return the error
-   SVN_ERR_FS_NOT_FOUND if the node PATH refers to does not exist.  If
+   SVN_ERR_FS_NOT_FOUND if the node LOOKUP refers to does not exist.  If
    non-zero, require all the parent directories to exist as normal,
    but if the final path component doesn't exist, simply return a path
    whose bottom `node' member is zero.  This option is useful for
@@ -970,32 +1009,39 @@ typedef enum open_path_flags_t {
 static svn_error_t *
 open_path(parent_path_t **parent_path_p,
           svn_fs_root_t *root,
-          const char *path,
+          const path_lookup_t *lookup,
           int flags,
           svn_boolean_t is_txn_path,
           apr_pool_t *pool)
 {
+  const svn_boolean_t denormalized = (lookup->path != lookup->key);
   svn_fs_t *fs = root->fs;
   dag_node_t *here = NULL; /* The directory we're currently looking at.  */
   parent_path_t *parent_path; /* The path from HERE up to the root. */
-  const char *rest; /* The portion of PATH we haven't traversed yet.  */
+  const char *rest_path;      /* The portion of LOOKUP->path we
+                                 haven't traversed yet. */
+  const char *rest_key;       /* The portion of LOOKUP->key we haven't
+                                 traversed yet. */
   apr_pool_t *iterpool = svn_pool_create(pool);
 
-  /* path to the currently processed entry without trailing '/'.
-     We will reuse this across iterations by simply putting a NUL terminator
-     at the respective position and replacing that with a '/' in the next
-     iteration.  This is correct as we assert() PATH to be canonical. */
-  svn_stringbuf_t *path_so_far = svn_stringbuf_create(path, pool);
-
-  /* callers often traverse the tree in some path-based order.  That means
-     a sibling of PATH has been presently accessed.  Try to start the lookup
-     directly at the parent node, if the caller did not requested the full
-     parent chain. */
-  assert(svn_fs__is_canonical_abspath(path));
-  path_so_far->len = 0; /* "" */
+  /* path to the currently processed entry without trailing '/'.  We
+     will reuse this across iterations by simply putting a NUL
+     terminator at the respective position and replacing that with a
+     '/' in the next iteration.  This is correct as we assert()
+     LOOKUP->key to be canonical. */
+  svn_stringbuf_t *key_so_far = svn_stringbuf_create(lookup->key, pool);
+
+  /* callers often traverse the tree in some path-based order.  That
+     means a sibling of LOOKUP->key has been presently accessed.  Try
+     to start the lookup directly at the parent node, if the caller
+     did not requested the full parent chain. */
+  assert(svn_fs__is_canonical_abspath(lookup->key));
+  assert(lookup->path != lookup->key
+         && svn_fs__is_canonical_abspath(lookup->path));
+  key_so_far->len = 0; /* "" */
   if (flags & open_path_node_only)
     {
-      const char *directory = svn_dirent_dirname(path, pool);
+      const char *directory = svn_dirent_dirname(lookup->key, pool);
       if (directory[1] != 0) /* root nodes are covered anyway */
         {
           SVN_ERR(dag_node_cache_get(&here, root, directory, TRUE, pool));
@@ -1003,8 +1049,17 @@ open_path(parent_path_t **parent_path_p,
           if (here)
             {
               apr_size_t dirname_len = strlen(directory);
-              path_so_far->len = dirname_len;
-              rest = path + dirname_len + 1;
+              key_so_far->len = dirname_len;
+              rest_key = lookup->key + dirname_len + 1;
+
+              /* Adjust the rest_path */
+              if (!denormalized)
+                rest_path = rest_key;
+              else
+                {
+                  svn_dirent_dirname(lookup->path, pool);
+                  rest_path = lookup->path + strlen(dirname) + 1;
+                }
             }
         }
     }
@@ -1015,11 +1070,13 @@ open_path(parent_path_t **parent_path_p,
       /* Make a parent_path item for the root node, using its own current
          copy id.  */
       SVN_ERR(root_node(&here, root, pool));
-      rest = path + 1; /* skip the leading '/', it saves in iteration */
+      /* Skip the leading '/', saving an iteration. */
+      rest_key = lookup->key + 1;
+      rest_path = lookup->path + 1;
     }
 
-  path_so_far->data[path_so_far->len] = '\0';
-  parent_path = make_parent_path(here, 0, 0, pool);
+  key_so_far->data[key_so_far->len] = '\0';
+  parent_path = make_parent_path(here, NULL, NULL, NULL, pool);
   parent_path->copy_inherit = copy_id_inherit_self;
 
   /* Whenever we are at the top of this loop:
@@ -1029,21 +1086,27 @@ open_path(parent_path_t **parent_path_p,
      - PARENT_PATH includes HERE and all its parents.  */
   for (;;)
     {
-      const char *next;
-      char *entry;
+      const char *next_key;
+      const char *next_path;
+      char *entry_key;
+      char *entry_path;
       dag_node_t *child;
 
       svn_pool_clear(iterpool);
 
       /* Parse out the next entry from the path.  */
-      entry = svn_fs__next_entry_name(&next, rest, pool);
+      entry_key = svn_fs__next_entry_name(&next_key, rest_key, pool);
+      if (!denormalized)
+        entry_path = entry_key;
+      else
+        entry_path = svn_fs__next_entry_name(&next_path, rest_path, pool);
 
       /* Update the path traversed thus far. */
-      path_so_far->data[path_so_far->len] = '/';
-      path_so_far->len += strlen(entry) + 1;
-      path_so_far->data[path_so_far->len] = '\0';
+      key_so_far->data[key_so_far->len] = '/';
+      key_so_far->len += strlen(entry_key) + 1;
+      key_so_far->data[key_so_far->len] = '\0';
 
-      if (*entry == '\0')
+      if (*entry_key == '\0')
         {
           /* Given the behavior of svn_fs__next_entry_name(), this
              happens when the path either starts or ends with a slash.
@@ -1063,12 +1126,12 @@ open_path(parent_path_t **parent_path_p,
              layer.  Don't bother to contact the cache for the last
              element if we already know the lookup to fail for the
              complete path. */
-          if (next || !(flags & open_path_uncached))
-            SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far->data,
+          if (next_key || !(flags & open_path_uncached))
+            SVN_ERR(dag_node_cache_get(&cached_node, root, key_so_far->data,
                                        TRUE, pool));
           if (cached_node)
             child = cached_node;
-          else
+          else /**TODOXXX**/
             err = svn_fs_fs__dag_open(&child, here, entry, pool, iterpool);
 
           /* "file not found" requires special handling.  */
@@ -1081,17 +1144,17 @@ open_path(parent_path_t **parent_path_p,
               svn_error_clear(err);
 
               if ((flags & open_path_last_optional)
-                  && (! next || *next == '\0'))
+                  && (! next_key || *next_key == '\0'))
                 {
-                  parent_path = make_parent_path(NULL, entry, parent_path,
-                                                 pool);
+                  parent_path = make_parent_path(NULL, entry_path, entry_key,
+                                                 parent_path, pool);
                   break;
                 }
               else
                 {
                   /* Build a better error message than svn_fs_fs__dag_open
                      can provide, giving the root and full path name.  */
-                  return SVN_FS__NOT_FOUND(root, path);
+                  return SVN_FS__NOT_FOUND(root, lookup->path);
                 }
             }
 
@@ -1106,7 +1169,8 @@ open_path(parent_path_t **parent_path_p,
           else
             {
               /* Now, make a parent_path item for CHILD. */
-              parent_path = make_parent_path(child, entry, parent_path, pool);
+              parent_path = make_parent_path(child, entry_path, entry_key,
+                                             parent_path, pool);
               if (is_txn_path)
                 {
                   SVN_ERR(get_copy_inheritance(&inherit, &copy_path, fs,
@@ -1118,20 +1182,22 @@ open_path(parent_path_t **parent_path_p,
 
           /* Cache the node we found (if it wasn't already cached). */
           if (! cached_node)
-            SVN_ERR(dag_node_cache_set(root, path_so_far->data, child,
+            SVN_ERR(dag_node_cache_set(root, key_so_far->data, child,
                                        iterpool));
         }
 
       /* Are we finished traversing the path?  */
-      if (! next)
+      if (! next_key)
         break;
 
       /* The path isn't finished yet; we'd better be in a directory.  */
       if (svn_fs_fs__dag_node_kind(child) != svn_node_dir)
-        SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far->data),
-                  apr_psprintf(iterpool, _("Failure opening '%s'"), path));
+        SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, key_so_far->data),
+                  apr_psprintf(iterpool, _("Failure opening '%s'"),
+                               lookup->path));
 
-      rest = next;
+      rest_path = next_path;
+      rest_key = next_key;
       here = child;
     }
 
@@ -1247,7 +1313,7 @@ make_path_mutable(svn_fs_root_t *root,
    Since locking can be expensive and POOL may be long-living, for
    nodes that will not need to survive the next call to this function,
    set NEEDS_LOCK_CACHE to FALSE. */
-static svn_error_t *
+static svn_error_t * /**TODOXXX**/
 get_dag(dag_node_t **dag_node_p,
         svn_fs_root_t *root,
         const char *path,
@@ -1354,6 +1420,69 @@ svn_fs_fs__node_id(const svn_fs_id_t **i
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+fs_node_relation(svn_fs_node_relation_t *relation,
+                 svn_fs_root_t *root_a, const char *path_a,
+                 svn_fs_root_t *root_b, const char *path_b,
+                 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;
+
+  /* Root paths are a common special case. */
+  svn_boolean_t a_is_root_dir
+    = (path_a[0] == '\0') || ((path_a[0] == '/') && (path_a[1] == '\0'));
+  svn_boolean_t b_is_root_dir
+    = (path_b[0] == '\0') || ((path_b[0] == '/') && (path_b[1] == '\0'));
+
+  /* Root paths are never related to non-root paths and path from different
+   * repository are always unrelated. */
+  if (a_is_root_dir ^ b_is_root_dir || root_a->fs != root_b->fs)
+    {
+      *relation = svn_fs_node_unrelated;
+      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)
+    {
+      *relation = root_a->rev == root_b->rev
+                ? svn_fs_node_same
+                : svn_fs_node_common_anchestor;
+      return SVN_NO_ERROR;
+    }
+
+  /* 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, FALSE, 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);
+
+  SVN_ERR(get_dag(&node, root_b, path_b, FALSE, 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_anchestor;
+  else
+    *relation = svn_fs_node_unrelated;
+
+  return SVN_NO_ERROR;
+}
 
 svn_error_t *
 svn_fs_fs__node_created_rev(svn_revnum_t *revision,
@@ -1500,16 +1629,15 @@ fs_change_node_prop(svn_fs_root_t *root,
 {
   parent_path_t *parent_path;
   apr_hash_t *proplist;
-  fs_fs_path_t path_key;
+  path_lookup_t lookup;
   const svn_fs_fs__id_part_t *txn_id;
 
   if (! root->is_txn_root)
     return SVN_FS__NOT_TXN(root);
   txn_id = root_txn_id(root);
 
-  SVN_ERR(create_path_key(&path_key, path, XXXXX
-  path = svn_fs__canonicalize_abspath(path, pool);
-  SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
+  SVN_ERR(create_path_lookup(&lookup, path, root, pool));
+  SVN_ERR(open_path(&parent_path, root, &lookup, 0, TRUE, pool));
 
   /* Check (non-recursively) to see if path is locked; if so, check
      that we can use it. */
@@ -1649,8 +1777,8 @@ merge(svn_stringbuf_t *conflict_p,
       apr_pool_t *pool)
 {
   const svn_fs_id_t *source_id, *target_id, *ancestor_id;
-  apr_hash_t *s_entries, *t_entries, *a_entries;
-  apr_hash_index_t *hi;
+  apr_array_header_t *s_entries, *t_entries, *a_entries;
+  int i, s_idx = -1, t_idx = -1;
   svn_fs_t *fs;
   apr_pool_t *iterpool;
   apr_int64_t mergeinfo_increment = 0;
@@ -1806,27 +1934,19 @@ merge(svn_stringbuf_t *conflict_p,
 
   /* for each entry E in a_entries... */
   iterpool = svn_pool_create(pool);
-  for (hi = apr_hash_first(pool, a_entries);
-       hi;
-       hi = apr_hash_next(hi))
+  for (i = 0; i < a_entries->nelts; ++i)
     {
       svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
-      const char *name;
-      apr_ssize_t klen;
-
       svn_pool_clear(iterpool);
 
-      name = svn__apr_hash_index_key(hi);
-      klen = svn__apr_hash_index_klen(hi);
-      a_entry = svn__apr_hash_index_val(hi);
-
-      s_entry = apr_hash_get(s_entries, name, klen);
-      t_entry = apr_hash_get(t_entries, name, klen);
+      a_entry = APR_ARRAY_IDX(a_entries, i, svn_fs_dirent_t *);
+      s_entry = svn_fs_fs__find_dir_entry(s_entries, a_entry->name, &s_idx);
+      t_entry = svn_fs_fs__find_dir_entry(t_entries, a_entry->name, &t_idx);
 
       /* No changes were made to this entry while the transaction was
          in progress, so do nothing to the target. */
       if (s_entry && svn_fs_fs__id_eq(a_entry->id, s_entry->id))
-        goto end;
+        continue;
 
       /* A change was made to this entry while the transaction was in
          process, but the transaction did not touch this entry. */
@@ -1857,7 +1977,7 @@ merge(svn_stringbuf_t *conflict_p,
                   mergeinfo_increment += mergeinfo_end;
                 }
 
-              SVN_ERR(svn_fs_fs__dag_set_entry(target, name,
+              SVN_ERR(svn_fs_fs__dag_set_entry(target, a_entry->name,
                                                s_entry->id,
                                                s_entry->kind,
                                                txn_id,
@@ -1865,7 +1985,8 @@ merge(svn_stringbuf_t *conflict_p,
             }
           else
             {
-              SVN_ERR(svn_fs_fs__dag_delete(target, name, txn_id, iterpool));
+              SVN_ERR(svn_fs_fs__dag_delete(target, a_entry->name, txn_id,
+                                            iterpool));
             }
         }
 
@@ -1929,29 +2050,23 @@ merge(svn_stringbuf_t *conflict_p,
           if (fs_supports_mergeinfo)
             mergeinfo_increment += sub_mergeinfo_increment;
         }
-
-      /* We've taken care of any possible implications E could have.
-         Remove it from source_entries, so it's easy later to loop
-         over all the source entries that didn't exist in
-         ancestor_entries. */
-    end:
-      apr_hash_set(s_entries, name, klen, NULL);
     }
 
   /* For each entry E in source but not in ancestor */
-  for (hi = apr_hash_first(pool, s_entries);
-       hi;
-       hi = apr_hash_next(hi))
-    {
-      svn_fs_dirent_t *s_entry, *t_entry;
-      const char *name = svn__apr_hash_index_key(hi);
-      apr_ssize_t klen = svn__apr_hash_index_klen(hi);
+  for (i = 0; i < s_entries->nelts; ++i)
+    {
+      svn_fs_dirent_t *a_entry, *s_entry, *t_entry;
       dag_node_t *s_ent_node;
 
       svn_pool_clear(iterpool);
 
-      s_entry = svn__apr_hash_index_val(hi);
-      t_entry = apr_hash_get(t_entries, name, klen);
+      s_entry = APR_ARRAY_IDX(s_entries, i, svn_fs_dirent_t *);
+      a_entry = svn_fs_fs__find_dir_entry(a_entries, s_entry->name, &s_idx);
+      t_entry = svn_fs_fs__find_dir_entry(t_entries, s_entry->name, &t_idx);
+
+      /* Process only entries in source that are NOT in ancestor. */
+      if (a_entry)
+        continue;
 
       /* If NAME exists in TARGET, declare a conflict. */
       if (t_entry)
@@ -2038,7 +2153,6 @@ svn_error_t *
 svn_fs_fs__commit_txn(const char **conflict_p,
                       svn_revnum_t *new_rev,
                       svn_fs_txn_t *txn,
-                      svn_boolean_t set_timestamp,
                       apr_pool_t *pool)
 {
   /* How do commits work in Subversion?
@@ -2135,7 +2249,7 @@ svn_fs_fs__commit_txn(const char **confl
       txn->base_rev = youngish_rev;
 
       /* Try to commit. */
-      err = svn_fs_fs__commit(new_rev, fs, txn, set_timestamp, iterpool);
+      err = svn_fs_fs__commit(new_rev, fs, txn, iterpool);
       if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
         {
           /* Did someone else finish committing a new revision while we
@@ -2261,10 +2375,23 @@ fs_dir_entries(apr_hash_t **table_p,
                apr_pool_t *pool)
 {
   dag_node_t *node;
+  apr_hash_t *hash = svn_hash__make(pool);
+  apr_array_header_t *table;
+  int i;
 
   /* Get the entries for this path in the caller's pool. */
   SVN_ERR(get_dag(&node, root, path, FALSE, pool));
-  return svn_fs_fs__dag_dir_entries(table_p, node, pool);
+  SVN_ERR(svn_fs_fs__dag_dir_entries(&table, node, pool));
+
+  /* Convert directory array to hash. */
+  for (i = 0; i < table->nelts; ++i)
+    {
+      svn_fs_dirent_t *entry = APR_ARRAY_IDX(table, i, svn_fs_dirent_t *);
+      svn_hash_sets(hash, entry->name, entry);
+    }
+
+  *table_p = hash;
+  return SVN_NO_ERROR;
 }
 
 static svn_error_t *
@@ -2307,12 +2434,13 @@ fs_make_dir(svn_fs_root_t *root,
 {
   parent_path_t *parent_path;
   dag_node_t *sub_dir;
+  path_lookup_t lookup;
   const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
 
   SVN_ERR(check_newline(path, pool));
 
-  path = svn_fs__canonicalize_abspath(path, pool);
-  SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
+  SVN_ERR(create_path_lookup(&lookup, path, root, pool));
+  SVN_ERR(open_path(&parent_path, root, &lookup, open_path_last_optional,
                     TRUE, pool));
 
   /* Check (recursively) to see if some lock is 'reserving' a path at
@@ -2356,6 +2484,7 @@ fs_delete_node(svn_fs_root_t *root,
                apr_pool_t *pool)
 {
   parent_path_t *parent_path;
+  path_lookup_t lookup;
   const svn_fs_fs__id_part_t *txn_id;
   apr_int64_t mergeinfo_count = 0;
   svn_node_kind_t kind;
@@ -2364,7 +2493,7 @@ fs_delete_node(svn_fs_root_t *root,
     return SVN_FS__NOT_TXN(root);
 
   txn_id = root_txn_id(root);
-  path = svn_fs__canonicalize_abspath(path, pool);
+  SVN_ERR(create_path_lookup(&lookup, path, root, pool));
   SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
   kind = svn_fs_fs__dag_node_kind(parent_path->node);
 
@@ -2503,7 +2632,7 @@ copy_helper(svn_fs_root_t *from_root,
             const char *to_path,
             copy_type_t copy_type,
             apr_pool_t *pool)
-{
+{ /**TODOXXX**/
   dag_node_t *from_node;
   parent_path_t *to_parent_path;
   const svn_fs_fs__id_part_t *txn_id = root_txn_id(to_root);
@@ -2770,13 +2899,14 @@ fs_make_file(svn_fs_root_t *root,
              apr_pool_t *pool)
 {
   parent_path_t *parent_path;
+  path_lookup_t lookup;
   dag_node_t *child;
   const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
 
   SVN_ERR(check_newline(path, pool));
 
-  path = svn_fs__canonicalize_abspath(path, pool);
-  SVN_ERR(open_path(&parent_path, root, path, open_path_last_optional,
+  SVN_ERR(create_path_lookup(&lookup, path, root, pool));
+  SVN_ERR(open_path(&parent_path, root, &lookup, open_path_last_optional,
                     TRUE, pool));
 
   /* If there's already a file by that name, complain.
@@ -3004,7 +3134,7 @@ window_consumer(svn_txdelta_window_t *wi
    txdelta_baton_t. */
 static svn_error_t *
 apply_textdelta(void *baton, apr_pool_t *pool)
-{
+{ /**TODOXXX**/
   txdelta_baton_t *tb = (txdelta_baton_t *) baton;
   parent_path_t *parent_path;
   const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
@@ -3169,7 +3299,7 @@ text_stream_closer(void *baton)
    text_baton_t. */
 static svn_error_t *
 apply_text(void *baton, apr_pool_t *pool)
-{
+{/**TODOXXX**/
   struct text_baton_t *tb = baton;
   parent_path_t *parent_path;
   const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
@@ -3422,9 +3552,10 @@ static svn_error_t *fs_closest_copy(svn_
                                     svn_fs_root_t *root,
                                     const char *path,
                                     apr_pool_t *pool)
-{
+{/**TODOXXX**/
   svn_fs_t *fs = root->fs;
   parent_path_t *parent_path, *copy_dst_parent_path;
+  path_lookup_t lookup, dst_lookup;
   svn_revnum_t copy_dst_rev, created_rev;
   const char *copy_dst_path;
   svn_fs_root_t *copy_dst_root;
@@ -3435,8 +3566,8 @@ static svn_error_t *fs_closest_copy(svn_
   *root_p = NULL;
   *path_p = NULL;
 
-  path = svn_fs__canonicalize_abspath(path, pool);
-  SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, pool));
+  SVN_ERR(create_path_lookup(&lookup, path, root, pool));
+  SVN_ERR(open_path(&parent_path, root, &lookup, 0, FALSE, pool));
 
   /* Find the youngest copyroot in the path of this node-rev, which
      will indicate the target of the innermost copy affecting the
@@ -3930,18 +4061,14 @@ crawl_directory_dag_for_mergeinfo(svn_fs
                                   apr_pool_t *result_pool,
                                   apr_pool_t *scratch_pool)
 {
-  apr_hash_t *entries;
-  apr_hash_index_t *hi;
+  apr_array_header_t *entries;
+  int i;
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
-  SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag,
-                                     scratch_pool));
-
-  for (hi = apr_hash_first(scratch_pool, entries);
-       hi;
-       hi = apr_hash_next(hi))
+  SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, dir_dag, scratch_pool));
+  for (i = 0; i < entries->nelts; ++i)
     {
-      svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
+      svn_fs_dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
       const char *kid_path;
       dag_node_t *kid_dag;
       svn_boolean_t has_mergeinfo, go_down;
@@ -4304,6 +4431,7 @@ static root_vtable_t root_vtable = {
   svn_fs_fs__check_path,
   fs_node_history,
   svn_fs_fs__node_id,
+  fs_node_relation,
   svn_fs_fs__node_created_rev,
   fs_node_origin_rev,
   fs_node_created_path,
@@ -4404,7 +4532,7 @@ make_txn_root(svn_fs_root_t **root_p,
 
      Note that we cannot put those caches in frd because that content
      fs root object is not available where we would need it. */
-  SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, root->txn, pool));
+  SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, root->txn, root->pool));
 
   root->fsap_data = frd;
 
@@ -4415,7 +4543,7 @@ make_txn_root(svn_fs_root_t **root_p,
 
 
 /* Verify. */
-static APR_INLINE const char *
+static const char *
 stringify_node(dag_node_t *node,
                apr_pool_t *pool)
 {
@@ -4488,18 +4616,17 @@ verify_node(dag_node_t *node,
     }
   if (kind == svn_node_dir)
     {
-      apr_hash_t *entries;
-      apr_hash_index_t *hi;
+      apr_array_header_t *entries;
+      int i;
       apr_int64_t children_mergeinfo = 0;
 
       SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
 
       /* Compute CHILDREN_MERGEINFO. */
-      for (hi = apr_hash_first(pool, entries);
-           hi;
-           hi = apr_hash_next(hi))
+      for (i = 0; i < entries->nelts; ++i)
         {
-          svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
+          svn_fs_dirent_t *dirent
+            = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
           dag_node_t *child;
           apr_int64_t child_mergeinfo;
 

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/tree.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/tree.h?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/tree.h (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/tree.h Thu Jan  9 09:31:10 2014
@@ -48,12 +48,9 @@ svn_error_t *svn_fs_fs__deltify(svn_fs_t
 /* Commit the transaction TXN as a new revision.  Return the new
    revision in *NEW_REV.  If the transaction conflicts with other
    changes return SVN_ERR_FS_CONFLICT and set *CONFLICT_P to a string
-   that details the cause of the conflict.
-   Update commit time to ensure that svn:date revprops remain ordered if
-   SET_TIMESTAMP is non-zero. Perform temporary allocations in POOL. */
+   that details the cause of the conflict. */
 svn_error_t *svn_fs_fs__commit_txn(const char **conflict_p,
                                    svn_revnum_t *new_rev, svn_fs_txn_t *txn,
-                                   svn_boolean_t set_timestamp,
                                    apr_pool_t *pool);
 
 /* Set ROOT_P to the root directory of transaction TXN.  Allocate the

Propchange: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/
------------------------------------------------------------------------------
  Merged /subversion/trunk/subversion/libsvn_fs_x:r1549771-1556763

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/cached_data.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/cached_data.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/cached_data.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/cached_data.c Thu Jan  9 09:31:10 2014
@@ -1924,31 +1924,6 @@ struct delta_read_baton
   unsigned char md5_digest[APR_MD5_DIGESTSIZE];
 };
 
-/* This implements the svn_txdelta_next_window_fn_t interface. */
-static svn_error_t *
-delta_read_next_window(svn_txdelta_window_t **window, void *baton,
-                       apr_pool_t *pool)
-{
-  struct delta_read_baton *drb = baton;
-
-  *window = NULL;
-  if (drb->rs->current < drb->rs->size)
-    {
-      SVN_ERR(read_delta_window(window, drb->rs->chunk_index, drb->rs, pool));
-      drb->rs->chunk_index++;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* This implements the svn_txdelta_md5_digest_fn_t interface. */
-static const unsigned char *
-delta_read_md5_digest(void *baton)
-{
-  struct delta_read_baton *drb = baton;
-  return drb->md5_digest;
-}
-
 svn_error_t *
 svn_fs_x__get_file_delta_stream(svn_txdelta_stream_t **stream_p,
                                 svn_fs_t *fs,
@@ -1958,37 +1933,6 @@ svn_fs_x__get_file_delta_stream(svn_txde
 {
   svn_stream_t *source_stream, *target_stream;
 
-  /* Try a shortcut: if the target is stored as a delta against the source,
-     then just use that delta. */
-  if (source && source->data_rep && target->data_rep)
-    {
-      rep_state_t *rep_state;
-      svn_fs_x__rep_header_t *rep_header;
-
-      /* Read target's base rep if any. */
-      SVN_ERR(create_rep_state(&rep_state, &rep_header, NULL,
-                               target->data_rep, fs, pool));
-      /* If that matches source, then use this delta as is. */
-      if (rep_header->type == svn_fs_x__rep_self_delta
-          || (rep_header->type == svn_fs_x__rep_delta
-              && rep_header->base_revision == source->data_rep->revision
-              && rep_header->base_item_index == source->data_rep->item_index))
-        {
-          /* Create the delta read baton. */
-          struct delta_read_baton *drb = apr_pcalloc(pool, sizeof(*drb));
-
-          drb->rs = rep_state;
-          memcpy(drb->md5_digest, target->data_rep->md5_digest,
-                 sizeof(drb->md5_digest));
-          *stream_p = svn_txdelta_stream_create(drb, delta_read_next_window,
-                                                delta_read_md5_digest, pool);
-          return SVN_NO_ERROR;
-        }
-      else
-        if (rep_state->file->file)
-          SVN_ERR(svn_io_file_close(rep_state->file->file, pool));
-    }
-
   /* Read both fulltexts and construct a delta. */
   if (source)
     SVN_ERR(svn_fs_x__get_contents(&source_stream, fs, source->data_rep, pool));

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/fs.h?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/fs.h (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/fs.h Thu Jan  9 09:31:10 2014
@@ -81,6 +81,8 @@ extern "C" {
 /* Names of special files and file extensions for transactions */
 #define PATH_CHANGES       "changes"       /* Records changes made so far */
 #define PATH_TXN_PROPS     "props"         /* Transaction properties */
+#define PATH_TXN_PROPS_FINAL "props-final" /* Final transaction properties
+                                              before moving to revprops */
 #define PATH_NEXT_IDS      "next-ids"      /* Next temporary ID assignments */
 #define PATH_PREFIX_NODE   "node."         /* Prefix for node filename */
 #define PATH_EXT_TXN       ".txn"          /* Extension of txn dir */

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/index.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/index.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/index.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/index.c Thu Jan  9 09:31:10 2014
@@ -31,6 +31,7 @@
 #include "pack.h"
 
 #include "private/svn_dep_compat.h"
+#include "private/svn_sorts_private.h"
 #include "private/svn_subr_private.h"
 #include "private/svn_temp_serializer.h"
 
@@ -2431,29 +2432,24 @@ get_p2l_entry_from_cached_page(const voi
   /* resolve all pointer values of in-cache data */
   const apr_array_header_t *page = data;
   apr_array_header_t *entries = apr_pmemdup(pool, page, sizeof(*page));
-  int idx;
+  svn_fs_x__p2l_entry_t *entry;
 
   entries->elts = (char *)svn_temp_deserializer__ptr(page,
                                      (const void *const *)&page->elts);
 
   /* search of the offset we want */
-  idx = svn_sort__bsearch_lower_bound(&offset, entries,
+  entry = svn_sort__array_lookup(entries, &offset, NULL,
       (int (*)(const void *, const void *))compare_p2l_entry_offsets);
 
   /* return it, if it is a perfect match */
-  if (idx < entries->nelts)
+  if (entry)
     {
-      svn_fs_x__p2l_entry_t *entry
-        = &APR_ARRAY_IDX(entries, idx, svn_fs_x__p2l_entry_t);
-      if (entry->offset == offset)
-        {
-          svn_fs_x__p2l_entry_t *result
-            = apr_pmemdup(pool, entry, sizeof(*result));
-          result->items
-            = (svn_fs_x__id_part_t *)svn_temp_deserializer__ptr(entries->elts,
+      svn_fs_x__p2l_entry_t *result
+        = apr_pmemdup(pool, entry, sizeof(*result));
+      result->items
+        = (svn_fs_x__id_part_t *)svn_temp_deserializer__ptr(entries->elts,
                                      (const void *const *)&entry->items);
-          return result;
-        }
+      return result;
     }
 
   return NULL;
@@ -2493,8 +2489,6 @@ p2l_entry_lookup(svn_fs_x__p2l_entry_t *
   svn_boolean_t is_cached = FALSE;
   p2l_page_info_baton_t page_info;
 
-  *entry_p = NULL;
-
   /* look for this info in our cache */
   SVN_ERR(get_p2l_keys(&page_info, &key, stream, fs, revision, offset, pool));
   SVN_ERR(svn_cache__get_partial((void**)entry_p, &is_cached,
@@ -2502,25 +2496,14 @@ p2l_entry_lookup(svn_fs_x__p2l_entry_t *
                                  p2l_entry_lookup_func, &offset, pool));
   if (!is_cached)
     {
-      int idx;
-
       /* do a standard index lookup.  This is will automatically prefetch
        * data to speed up future lookups. */
       apr_array_header_t *entries;
       SVN_ERR(p2l_index_lookup(&entries, stream, fs, revision, offset, pool));
 
       /* Find the entry that we want. */
-      idx = svn_sort__bsearch_lower_bound(&offset, entries, 
+      *entry_p = svn_sort__array_lookup(entries, &offset, NULL,
           (int (*)(const void *, const void *))compare_p2l_entry_offsets);
-
-      /* return it, if it is a perfect match */
-      if (idx < entries->nelts)
-        {
-          svn_fs_x__p2l_entry_t *entry
-            = &APR_ARRAY_IDX(entries, idx, svn_fs_x__p2l_entry_t);
-          if (entry->offset == offset)
-            *entry_p = entry;
-        }
     }
 
   return SVN_NO_ERROR;

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/low_level.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/low_level.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/low_level.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/low_level.c Thu Jan  9 09:31:10 2014
@@ -24,6 +24,7 @@
 #include "svn_hash.h"
 #include "svn_pools.h"
 #include "svn_sorts.h"
+#include "private/svn_sorts_private.h"
 #include "private/svn_string_private.h"
 
 #include "../libsvn_fs/fs-loader.h"

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/pack.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/pack.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/pack.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/pack.c Thu Jan  9 09:31:10 2014
@@ -24,9 +24,10 @@
 #include "svn_pools.h"
 #include "svn_dirent_uri.h"
 #include "svn_sorts.h"
-#include "private/svn_temp_serializer.h"
+#include "private/svn_sorts_private.h"
 #include "private/svn_subr_private.h"
 #include "private/svn_string_private.h"
+#include "private/svn_temp_serializer.h"
 
 #include "fs_x.h"
 #include "pack.h"

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/structure
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/structure?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/structure (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/structure Thu Jan  9 09:31:10 2014
@@ -226,6 +226,7 @@ Transaction layout
 A transaction directory has the following layout:
 
   props                      Transaction props
+  props-final                Final transaction props (optional)
   next-ids                   Next temporary node-ID and copy-ID
   changes                    Changed-path information so far
   node.<nid>.<cid>           New node-rev data for node
@@ -241,9 +242,10 @@ they are received from the client.  To e
 writing to the file at a given time, the "rev-lock" file is locked for
 the duration of each write.
 
-The two kinds of props files are all in hash dump format.  The "props"
+The three kinds of props files are all in hash dump format.  The "props"
 file will always be present.  The "node.<nid>.<cid>.props" file will
-only be present if the node-rev properties have been changed.
+only be present if the node-rev properties have been changed.  The
+"props-final" only exists while converting the transaction into a revision.
 
 The <sha1> files' content is that of text rep references:
 "<rev> <offset> <length> <size> <digest>"

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/transaction.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/transaction.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/transaction.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/transaction.c Thu Jan  9 09:31:10 2014
@@ -43,8 +43,9 @@
 #include "index.h"
 
 #include "private/svn_fs_util.h"
-#include "private/svn_subr_private.h"
+#include "private/svn_sorts_private.h"
 #include "private/svn_string_private.h"
+#include "private/svn_subr_private.h"
 #include "../libsvn_fs/fs-loader.h"
 
 #include "svn_private_config.h"
@@ -1141,10 +1142,11 @@ svn_fs_x__change_txn_prop(svn_fs_txn_t *
   return svn_fs_x__change_txn_props(txn, props, pool);
 }
 
-svn_error_t *
-svn_fs_x__change_txn_props(svn_fs_txn_t *txn,
-                           const apr_array_header_t *props,
-                           apr_pool_t *pool)
+static svn_error_t *
+change_txn_props(svn_fs_txn_t *txn,
+                 const apr_array_header_t *props,
+                 svn_boolean_t final,
+                 apr_pool_t *pool)
 {
   fs_txn_data_t *ftd = txn->fsap_data;
   const char *txn_prop_filename;
@@ -1168,6 +1170,11 @@ svn_fs_x__change_txn_props(svn_fs_txn_t 
     {
       svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
 
+      if (svn_hash_gets(txn_prop, SVN_FS__PROP_TXN_CLIENT_DATE)
+          && !strcmp(prop->name, SVN_PROP_REVISION_DATE))
+        svn_hash_sets(txn_prop, SVN_FS__PROP_TXN_CLIENT_DATE,
+                      svn_string_create("1", pool));
+
       svn_hash_sets(txn_prop, prop->name, prop->value);
     }
 
@@ -1184,8 +1191,20 @@ svn_fs_x__change_txn_props(svn_fs_txn_t 
                       svn_io_file_del_none,
                       pool));
   return svn_io_file_rename(txn_prop_filename,
-                      svn_fs_x__path_txn_props(txn->fs, &ftd->txn_id, pool),
-                      pool);
+                (final 
+                 ? svn_fs_x__path_txn_props_final(txn->fs, &ftd->txn_id, pool)
+                 : svn_fs_x__path_txn_props(txn->fs, &ftd->txn_id, pool)),
+                pool);
+}
+
+svn_error_t *
+svn_fs_x__change_txn_props(svn_fs_txn_t *txn,
+                           const apr_array_header_t *props,
+                           apr_pool_t *pool)
+{
+  SVN_ERR(change_txn_props(txn, props, FALSE, pool));
+
+  return SVN_NO_ERROR;
 }
 
 svn_error_t *
@@ -2939,7 +2958,7 @@ verify_moves(svn_fs_t *fs,
     {
       const char *deleted_path = APR_ARRAY_IDX(deletions, i, const char*);
       int closest_move_idx
-        = svn_sort__bsearch_lower_bound(deleted_path, moves,
+        = svn_sort__bsearch_lower_bound(moves, deleted_path,
                                         svn_sort_compare_paths);
 
       if (closest_move_idx < moves->nelts)
@@ -2995,7 +3014,7 @@ verify_moves(svn_fs_t *fs,
          (or any of its parents) */
 
       int closest_deletion_idx
-        = svn_sort__bsearch_lower_bound(change->copyfrom_path, deletions,
+        = svn_sort__bsearch_lower_bound(deletions, change->copyfrom_path,
                                         svn_sort_compare_paths);
       if (closest_deletion_idx < deletions->nelts)
         {
@@ -3014,12 +3033,82 @@ verify_moves(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. */
+static svn_error_t *
+write_final_revprop(const char **path,
+                    svn_fs_txn_t *txn,
+                    const svn_fs_x__id_part_t *txn_id,
+                    apr_pool_t *pool)
+{
+  apr_hash_t *txnprops;
+  apr_array_header_t *final_mods = NULL;
+  svn_string_t date;
+  svn_prop_t prop;
+  svn_string_t *client_date;
+
+  SVN_ERR(svn_fs_x__txn_proplist(&txnprops, txn, pool));
+
+  /* Remove any temporary txn props representing 'flags'. */
+  prop.value = NULL;
+  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
+    {
+      if (!final_mods)
+        final_mods = apr_array_make(pool, 3, sizeof(svn_prop_t));
+      prop.name = SVN_FS__PROP_TXN_CHECK_OOD;
+      APR_ARRAY_PUSH(final_mods, svn_prop_t) = prop;
+    }
+
+  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
+    {
+      if (!final_mods)
+        final_mods = apr_array_make(pool, 3, sizeof(svn_prop_t));
+      prop.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
+      APR_ARRAY_PUSH(final_mods, svn_prop_t) = prop;
+    }
+
+  client_date = svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CLIENT_DATE);
+  if (client_date)
+    {
+      if (!final_mods)
+        final_mods = apr_array_make(pool, 3, sizeof(svn_prop_t));
+      prop.name = SVN_FS__PROP_TXN_CLIENT_DATE;
+      APR_ARRAY_PUSH(final_mods, svn_prop_t) = prop;
+    }
+
+  /* Update commit time to ensure that svn:date revprops remain ordered if
+     requested. */
+  if (!client_date || strcmp(client_date->data, "1"))
+    {
+      if (!final_mods)
+        final_mods = apr_array_make(pool, 3, sizeof(svn_prop_t));
+      date.data = svn_time_to_cstring(apr_time_now(), pool);
+      date.len = strlen(date.data);
+      prop.name = SVN_PROP_REVISION_DATE;
+      prop.value = &date;
+      APR_ARRAY_PUSH(final_mods, svn_prop_t) = prop;
+    }
+
+  if (final_mods)
+    {
+      SVN_ERR(change_txn_props(txn, final_mods, TRUE, pool));
+      *path = svn_fs_x__path_txn_props_final(txn->fs, txn_id, pool);
+    }
+  else
+    {
+      *path = svn_fs_x__path_txn_props(txn->fs, txn_id, pool);
+    }
+
+  return SVN_NO_ERROR;
+}
+
 /* Baton used for commit_body below. */
 struct commit_baton {
   svn_revnum_t *new_rev_p;
   svn_fs_t *fs;
   svn_fs_txn_t *txn;
-  svn_boolean_t set_timestamp;
   apr_array_header_t *reps_to_cache;
   apr_hash_t *reps_hash;
   apr_pool_t *reps_pool;
@@ -3042,9 +3131,6 @@ commit_body(void *baton, apr_pool_t *poo
   apr_file_t *proto_file;
   void *proto_file_lockcookie;
   apr_off_t initial_offset, changed_path_offset;
-  apr_hash_t *txnprops;
-  apr_array_header_t *txnprop_list;
-  svn_prop_t prop;
   const svn_fs_x__id_part_t *txn_id = svn_fs_x__txn_get_id(cb->txn);
   apr_hash_t *changed_paths;
 
@@ -3097,26 +3183,6 @@ commit_body(void *baton, apr_pool_t *poo
      race with another caller writing to the prototype revision file
      before we commit it. */
 
-  /* Remove any temporary txn props representing 'flags'. */
-  SVN_ERR(svn_fs_x__txn_proplist(&txnprops, cb->txn, pool));
-  txnprop_list = apr_array_make(pool, 3, sizeof(svn_prop_t));
-  prop.value = NULL;
-
-  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_OOD))
-    {
-      prop.name = SVN_FS__PROP_TXN_CHECK_OOD;
-      APR_ARRAY_PUSH(txnprop_list, svn_prop_t) = prop;
-    }
-
-  if (svn_hash_gets(txnprops, SVN_FS__PROP_TXN_CHECK_LOCKS))
-    {
-      prop.name = SVN_FS__PROP_TXN_CHECK_LOCKS;
-      APR_ARRAY_PUSH(txnprop_list, svn_prop_t) = prop;
-    }
-
-  if (! apr_is_empty_array(txnprop_list))
-    SVN_ERR(svn_fs_x__change_txn_props(cb->txn, txnprop_list, pool));
-
   /* Create the shard for the rev and revprop file, if we're sharding and
      this is the first revision of a new shard.  We don't care if this
      fails because the shard already existed for some reason. */
@@ -3177,22 +3243,9 @@ 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));
 
-  /* Update commit time to ensure that svn:date revprops remain ordered if
-     requested. */
-  if (cb->set_timestamp)
-    {
-      svn_string_t date;
-
-      date.data = svn_time_to_cstring(apr_time_now(), pool);
-      date.len = strlen(date.data);
-
-      SVN_ERR(svn_fs_x__change_txn_prop(cb->txn, SVN_PROP_REVISION_DATE,
-                                        &date, pool));
-    }
-
   /* Move the revprops file into place. */
   SVN_ERR_ASSERT(! svn_fs_x__is_packed_revprop(cb->fs, new_rev));
-  revprop_filename = svn_fs_x__path_txn_props(cb->fs, txn_id, pool);
+  SVN_ERR(write_final_revprop(&revprop_filename, cb->txn, txn_id, pool));
   final_revprop = svn_fs_x__path_revprops(cb->fs, new_rev, pool);
   SVN_ERR(svn_fs_x__move_into_place(revprop_filename, final_revprop,
                                     old_rev_filename, pool));
@@ -3241,7 +3294,6 @@ svn_error_t *
 svn_fs_x__commit(svn_revnum_t *new_rev_p,
                  svn_fs_t *fs,
                  svn_fs_txn_t *txn,
-                 svn_boolean_t set_timestamp,
                  apr_pool_t *pool)
 {
   struct commit_baton cb;
@@ -3250,7 +3302,6 @@ svn_fs_x__commit(svn_revnum_t *new_rev_p
   cb.new_rev_p = new_rev_p;
   cb.fs = fs;
   cb.txn = txn;
-  cb.set_timestamp = set_timestamp;
 
   if (ffd->rep_sharing_allowed)
     {
@@ -3506,5 +3557,12 @@ svn_fs_x__begin_txn(svn_fs_txn_t **txn_p
       APR_ARRAY_PUSH(props, svn_prop_t) = prop;
     }
 
+  if (flags & SVN_FS_TXN_CLIENT_DATE)
+    {
+      prop.name = SVN_FS__PROP_TXN_CLIENT_DATE;
+      prop.value = svn_string_create("0", pool);
+      APR_ARRAY_PUSH(props, svn_prop_t) = prop;
+    }
+
   return svn_fs_x__change_txn_props(*txn_p, props, pool);
 }

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/transaction.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/transaction.h?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/transaction.h (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_x/transaction.h Thu Jan  9 09:31:10 2014
@@ -209,14 +209,12 @@ svn_fs_x__set_proplist(svn_fs_t *fs,
 
 /* Commit the transaction TXN in filesystem FS and return its new
    revision number in *REV.  If the transaction is out of date, return
-   the error SVN_ERR_FS_TXN_OUT_OF_DATE. Update commit time to ensure that
-   svn:date revprops remain ordered if SET_TIMESTAMP is non-zero. Use POOL
-   for temporary allocations. */
+   the error SVN_ERR_FS_TXN_OUT_OF_DATE. Use POOL for temporary
+   allocations. */
 svn_error_t *
 svn_fs_x__commit(svn_revnum_t *new_rev_p,
                  svn_fs_t *fs,
                  svn_fs_txn_t *txn,
-                 svn_boolean_t set_timestamp,
                  apr_pool_t *pool);
 
 /* Set *NAMES_P to an array of names which are all the active



Mime
View raw message