You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/10/15 10:52:18 UTC

svn commit: r1532250 [16/37] - in /subversion/branches/cache-server: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/emacs/ contrib/hook-scripts/ contrib/server-side/fsfsfixer/ contrib/se...

Modified: subversion/branches/cache-server/subversion/libsvn_fs_fs/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_fs_fs/tree.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_fs_fs/tree.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_fs_fs/tree.c Tue Oct 15 08:52:06 2013
@@ -41,8 +41,8 @@
 #include <apr_pools.h>
 #include <apr_hash.h>
 
-#include "svn_hash.h"
 #include "svn_private_config.h"
+#include "svn_hash.h"
 #include "svn_pools.h"
 #include "svn_error.h"
 #include "svn_path.h"
@@ -51,13 +51,16 @@
 #include "svn_props.h"
 
 #include "fs.h"
-#include "key-gen.h"
+#include "cached_data.h"
 #include "dag.h"
 #include "lock.h"
 #include "tree.h"
 #include "fs_fs.h"
 #include "id.h"
+#include "pack.h"
 #include "temp_serializer.h"
+#include "transaction.h"
+#include "util.h"
 
 #include "private/svn_mergeinfo_private.h"
 #include "private/svn_subr_private.h"
@@ -100,24 +103,13 @@
    kept in the FS object and shared among multiple revision root
    objects.
 */
-typedef struct fs_rev_root_data_t
-{
-  /* A dag node for the revision's root directory. */
-  dag_node_t *root_dir;
-
-  /* Cache structure for mapping const char * PATH to const char
-     *COPYFROM_STRING, so that paths_changed can remember all the
-     copyfrom information in the changes file.
-     COPYFROM_STRING has the format "REV PATH", or is the empty string if
-     the path was added without history. */
-  apr_hash_t *copyfrom_cache;
-
-} fs_rev_root_data_t;
+typedef dag_node_t fs_rev_root_data_t;
 
 typedef struct fs_txn_root_data_t
 {
-  const char *txn_id;
-
+  /* TXN_ID value from the main struct but as a struct instead of a string */
+  svn_fs_fs__id_part_t txn_id;
+  
   /* Cache of txn DAG nodes (without their nested noderevs, because
    * it's mutable). Same keys/values as ffd->rev_node_cache. */
   svn_cache__t *txn_node_cache;
@@ -135,10 +127,18 @@ static svn_fs_root_t *make_revision_root
                                          apr_pool_t *pool);
 
 static svn_error_t *make_txn_root(svn_fs_root_t **root_p,
-                                  svn_fs_t *fs, const char *txn,
-                                  svn_revnum_t base_rev, apr_uint32_t flags,
+                                  svn_fs_t *fs,
+                                  const svn_fs_fs__id_part_t *txn,
+                                  svn_revnum_t base_rev,
+                                  apr_uint32_t flags,
                                   apr_pool_t *pool);
 
+static svn_error_t *fs_closest_copy(svn_fs_root_t **root_p,
+                                    const char **path_p,
+                                    svn_fs_root_t *root,
+                                    const char *path,
+                                    apr_pool_t *pool);
+
 
 /*** Node Caching ***/
 
@@ -151,7 +151,7 @@ typedef struct cache_entry_t
 {
   /* hash value derived from PATH, REVISION.
      Used to short-circuit failed lookups. */
-  long int hash_value;
+  apr_uint32_t hash_value;
 
   /* revision to which the NODE belongs */
   svn_revnum_t revision;
@@ -340,7 +340,12 @@ cache_lookup( fs_fs_dag_cache_t *cache
 {
   apr_size_t i, bucket_index;
   apr_size_t path_len = strlen(path);
-  long int hash_value = revision;
+  apr_uint32_t hash_value = (apr_uint32_t)revision;
+
+#if SVN_UNALIGNED_ACCESS_IS_OK
+  /* "randomizing" / distributing factor used in our hash function */
+  const apr_uint32_t factor = 0xd1f3da69;
+#endif
 
   /* optimistic lookup: hit the same bucket again? */
   cache_entry_t *result = &cache->buckets[cache->last_hit];
@@ -352,12 +357,47 @@ cache_lookup( fs_fs_dag_cache_t *cache
     }
 
   /* need to do a full lookup.  Calculate the hash value
-     (HASH_VALUE has been initialized to REVISION). */
-  for (i = 0; i + 4 <= path_len; i += 4)
-    hash_value = hash_value * 0xd1f3da69 + *(const apr_uint32_t*)(path + i);
+     (HASH_VALUE has been initialized to REVISION).
+
+     Note that the actual hash function is arbitrary as long as its result
+     in HASH_VALUE only depends on REVISION and *PATH.  However, we try to
+     make as much of *PATH influence the result as possible to get an "even"
+     spread across the hash buckets (maximizes our cache retention rate and
+     thus the hit rates).
+
+     When chunked access is possible (independent of the PATH pointer's
+     value!), we read 4 bytes at once and multiply the hash value with a
+     FACTOR that mirror / pattern / shift all 4 input bytes to various bits
+     of the result.  The final result will be taken from the MSBs.
+
+     When chunked access is not possible (not supported by CPU or odd bytes
+     at the end of *PATH), we use the simple traditional "* 33" hash
+     function that works very well with texts / paths and that e.g. APR uses.
+
+     Please note that the bytewise and the chunked calculation are *NOT*
+     interchangeable as they will yield different results for the same input.
+     For any given machine and *PATH, we must use a fixed combination of the
+     two functions.
+   */
+  i = 0;
+#if SVN_UNALIGNED_ACCESS_IS_OK
+  /* We relax the dependency chain between iterations by processing
+     two chunks from the input per hash_value self-multiplication.
+     The HASH_VALUE update latency is now 1 MUL latency + 1 ADD latency
+     per 2 chunks instead of 1 chunk.
+   */
+  for (; i + 8 <= path_len; i += 8)
+    hash_value = hash_value * factor * factor
+               + (  *(const apr_uint32_t*)(path + i) * factor
+                  + *(const apr_uint32_t*)(path + i + 4));
+#endif
 
   for (; i < path_len; ++i)
-    hash_value = hash_value * 33 + path[i];
+    /* Help GCC to minimize the HASH_VALUE update latency by splitting the
+       MUL 33 of the naive implementation: h = h * 33 + path[i].  This
+       shortens the dependency chain from 1 shift + 2 ADDs to 1 shift + 1 ADD.
+     */
+    hash_value = hash_value * 32 + (hash_value + (unsigned char)path[i]);
 
   bucket_index = hash_value + (hash_value >> 16);
   bucket_index = (bucket_index + (bucket_index >> 8)) % BUCKET_COUNT;
@@ -595,7 +635,8 @@ svn_fs_fs__txn_root(svn_fs_root_t **root
         flags |= SVN_FS_TXN_CHECK_LOCKS;
     }
 
-  return make_txn_root(root_p, txn->fs, txn->id, txn->base_rev, flags, pool);
+  return make_txn_root(root_p, txn->fs, svn_fs_fs__txn_get_id(txn),
+                       txn->base_rev, flags, pool);
 }
 
 
@@ -620,6 +661,15 @@ svn_fs_fs__revision_root(svn_fs_root_t *
 
 /* Getting dag nodes for roots.  */
 
+/* Return the transaction ID to a given transaction ROOT. */
+static const svn_fs_fs__id_part_t *
+root_txn_id(svn_fs_root_t *root)
+{
+  fs_txn_root_data_t *frd = root->fsap_data;
+  assert(root->is_txn_root);
+  
+  return &frd->txn_id;
+}
 
 /* Set *NODE_P to a freshly opened dag node referring to the root
    directory of ROOT, allocating from POOL.  */
@@ -631,14 +681,15 @@ root_node(dag_node_t **node_p,
   if (root->is_txn_root)
     {
       /* It's a transaction root.  Open a fresh copy.  */
-      return svn_fs_fs__dag_txn_root(node_p, root->fs, root->txn, pool);
+      return svn_fs_fs__dag_txn_root(node_p, root->fs, root_txn_id(root),
+                                     pool);
     }
   else
     {
       /* It's a revision root, so we already have its root directory
          opened.  */
-      fs_rev_root_data_t *frd = root->fsap_data;
-      *node_p = svn_fs_fs__dag_dup(frd->root_dir, pool);
+      dag_node_t *root_dir = root->fsap_data;
+      *node_p = svn_fs_fs__dag_dup(root_dir, pool);
       return SVN_NO_ERROR;
     }
 }
@@ -654,7 +705,11 @@ mutable_root_node(dag_node_t **node_p,
                   apr_pool_t *pool)
 {
   if (root->is_txn_root)
-    return svn_fs_fs__dag_clone_root(node_p, root->fs, root->txn, pool);
+    {
+      /* It's a transaction root.  Open a fresh copy.  */
+      return svn_fs_fs__dag_clone_root(node_p, root->fs, root_txn_id(root),
+                                       pool);
+    }
   else
     /* If it's not a transaction root, we can't change its contents.  */
     return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
@@ -742,25 +797,23 @@ parent_path_relpath(parent_path_t *child
    the inheritance method is copy_id_inherit_new, also return a
    *COPY_SRC_PATH on which to base the new copy ID (else return NULL
    for that path).  CHILD must have a parent (it cannot be the root
-   node).  TXN_ID is the transaction in which these items might be
-   mutable.  Allocations are taken from POOL. */
+   node).  Allocations are taken from POOL. */
 static svn_error_t *
 get_copy_inheritance(copy_id_inherit_t *inherit_p,
                      const char **copy_src_path,
                      svn_fs_t *fs,
                      parent_path_t *child,
-                     const char *txn_id,
                      apr_pool_t *pool)
 {
   const svn_fs_id_t *child_id, *parent_id, *copyroot_id;
-  const char *child_copy_id, *parent_copy_id;
+  const svn_fs_fs__id_part_t *child_copy_id, *parent_copy_id;
   const char *id_path = NULL;
   svn_fs_root_t *copyroot_root;
   dag_node_t *copyroot_node;
   svn_revnum_t copyroot_rev;
   const char *copyroot_path;
 
-  SVN_ERR_ASSERT(child && child->parent && txn_id);
+  SVN_ERR_ASSERT(child && child->parent);
 
   /* Initialize some convenience variables. */
   child_id = svn_fs_fs__dag_get_id(child->node);
@@ -769,7 +822,7 @@ get_copy_inheritance(copy_id_inherit_t *
   parent_copy_id = svn_fs_fs__id_copy_id(parent_id);
 
   /* If this child is already mutable, we have nothing to do. */
-  if (svn_fs_fs__id_txn_id(child_id))
+  if (svn_fs_fs__id_is_txn(child_id))
     {
       *inherit_p = copy_id_inherit_self;
       *copy_src_path = NULL;
@@ -783,14 +836,14 @@ get_copy_inheritance(copy_id_inherit_t *
 
   /* Special case: if the child's copy ID is '0', use the parent's
      copy ID. */
-  if (strcmp(child_copy_id, "0") == 0)
+  if (svn_fs_fs__id_part_is_root(child_copy_id))
     return SVN_NO_ERROR;
 
   /* Compare the copy IDs of the child and its parent.  If they are
      the same, then the child is already on the same branch as the
      parent, and should use the same mutability copy ID that the
      parent will use. */
-  if (svn_fs_fs__key_compare(child_copy_id, parent_copy_id) == 0)
+  if (svn_fs_fs__id_part_eq(child_copy_id, parent_copy_id))
     return SVN_NO_ERROR;
 
   /* If the child is on the same branch that the parent is on, the
@@ -870,10 +923,9 @@ typedef enum open_path_flags_t {
    *element, for the root directory.  PATH must be in canonical form.
 
    If resulting *PARENT_PATH_P will eventually be made mutable and
-   modified, or if copy ID inheritance information is otherwise
-   needed, TXN_ID should be the ID of the mutability transaction.  If
-   TXN_ID is NULL, no copy ID inheritance information will be
-   calculated for the *PARENT_PATH_P chain.
+   modified, or if copy ID inheritance information is otherwise needed,
+   IS_TXN_PATH must be set.  If IS_TXN_PATH is FALSE, no copy ID
+   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
@@ -897,36 +949,45 @@ open_path(parent_path_t **parent_path_p,
           svn_fs_root_t *root,
           const char *path,
           int flags,
-          const char *txn_id,
+          svn_boolean_t is_txn_path,
           apr_pool_t *pool)
 {
   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 *path_so_far = "/";
   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. */
-  const char *directory = NULL;
   assert(svn_fs__is_canonical_abspath(path));
+  path_so_far->len = 0; /* "" */
   if (flags & open_path_node_only)
     {
-      directory = svn_dirent_dirname(path, pool);
+      const char *directory = svn_dirent_dirname(path, pool);
       if (directory[1] != 0) /* root nodes are covered anyway */
-        SVN_ERR(dag_node_cache_get(&here, root, directory, TRUE, pool));
+        {
+          SVN_ERR(dag_node_cache_get(&here, root, directory, TRUE, pool));
+          /* did the shortcut work? */
+          if (here)
+            {
+              apr_size_t dirname_len = strlen(directory);
+              path_so_far->len = dirname_len;
+              rest = path + dirname_len + 1;
+            }
+        }
     }
 
   /* did the shortcut work? */
-  if (here)
-    {
-      path_so_far = directory;
-      rest = path + strlen(directory) + 1;
-    }
-  else
+  if (!here)
     {
       /* Make a parent_path item for the root node, using its own current
          copy id.  */
@@ -934,6 +995,7 @@ open_path(parent_path_t **parent_path_p,
       rest = path + 1; /* skip the leading '/', it saves in iteration */
     }
 
+  path_so_far->data[path_so_far->len] = '\0';
   parent_path = make_parent_path(here, 0, 0, pool);
   parent_path->copy_inherit = copy_id_inherit_self;
 
@@ -953,8 +1015,10 @@ open_path(parent_path_t **parent_path_p,
       /* Parse out the next entry from the path.  */
       entry = svn_fs__next_entry_name(&next, rest, pool);
 
-      /* Calculate the path traversed thus far. */
-      path_so_far = svn_fspath__join(path_so_far, entry, 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';
 
       if (*entry == '\0')
         {
@@ -977,7 +1041,7 @@ open_path(parent_path_t **parent_path_p,
              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,
+            SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far->data,
                                        TRUE, pool));
           if (cached_node)
             child = cached_node;
@@ -1020,10 +1084,10 @@ open_path(parent_path_t **parent_path_p,
             {
               /* Now, make a parent_path item for CHILD. */
               parent_path = make_parent_path(child, entry, parent_path, pool);
-              if (txn_id)
+              if (is_txn_path)
                 {
                   SVN_ERR(get_copy_inheritance(&inherit, &copy_path, fs,
-                                               parent_path, txn_id, iterpool));
+                                               parent_path, iterpool));
                   parent_path->copy_inherit = inherit;
                   parent_path->copy_src_path = apr_pstrdup(pool, copy_path);
                 }
@@ -1031,7 +1095,8 @@ 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, child, iterpool));
+            SVN_ERR(dag_node_cache_set(root, path_so_far->data, child,
+                                       iterpool));
         }
 
       /* Are we finished traversing the path?  */
@@ -1040,7 +1105,7 @@ open_path(parent_path_t **parent_path_p,
 
       /* 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),
+        SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far->data),
                   apr_psprintf(iterpool, _("Failure opening '%s'"), path));
 
       rest = next;
@@ -1065,7 +1130,7 @@ make_path_mutable(svn_fs_root_t *root,
                   apr_pool_t *pool)
 {
   dag_node_t *clone;
-  const char *txn_id = root->txn;
+  const svn_fs_fs__id_part_t *txn_id = root_txn_id(root);
 
   /* Is the node mutable already?  */
   if (svn_fs_fs__dag_check_mutable(parent_path->node))
@@ -1075,7 +1140,8 @@ make_path_mutable(svn_fs_root_t *root,
   if (parent_path->parent)
     {
       const svn_fs_id_t *parent_id, *child_id, *copyroot_id;
-      const char *copy_id = NULL;
+      svn_fs_fs__id_part_t copy_id = { SVN_INVALID_REVNUM, 0 };
+      svn_fs_fs__id_part_t *copy_id_ptr = &copy_id;
       copy_id_inherit_t inherit = parent_path->copy_inherit;
       const char *clone_path, *copyroot_path;
       svn_revnum_t copyroot_rev;
@@ -1092,7 +1158,7 @@ make_path_mutable(svn_fs_root_t *root,
         {
         case copy_id_inherit_parent:
           parent_id = svn_fs_fs__dag_get_id(parent_path->parent->node);
-          copy_id = svn_fs_fs__id_copy_id(parent_id);
+          copy_id = *svn_fs_fs__id_copy_id(parent_id);
           break;
 
         case copy_id_inherit_new:
@@ -1101,7 +1167,7 @@ make_path_mutable(svn_fs_root_t *root,
           break;
 
         case copy_id_inherit_self:
-          copy_id = NULL;
+          copy_id_ptr = NULL;
           break;
 
         case copy_id_inherit_unknown:
@@ -1120,8 +1186,8 @@ make_path_mutable(svn_fs_root_t *root,
 
       child_id = svn_fs_fs__dag_get_id(parent_path->node);
       copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
-      if (strcmp(svn_fs_fs__id_node_id(child_id),
-                 svn_fs_fs__id_node_id(copyroot_id)) != 0)
+      if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(child_id),
+                                 svn_fs_fs__id_node_id(copyroot_id)))
         is_parent_copyroot = TRUE;
 
       /* Now make this node mutable.  */
@@ -1130,7 +1196,7 @@ make_path_mutable(svn_fs_root_t *root,
                                          parent_path->parent->node,
                                          clone_path,
                                          parent_path->entry,
-                                         copy_id, txn_id,
+                                         copy_id_ptr, txn_id,
                                          is_parent_copyroot,
                                          pool));
 
@@ -1191,7 +1257,7 @@ get_dag(dag_node_t **dag_node_p,
            * error if the node for which we are searching doesn't exist. */
           SVN_ERR(open_path(&parent_path, root, path,
                             open_path_uncached | open_path_node_only,
-                            NULL, pool));
+                            FALSE, pool));
           node = parent_path->node;
 
           /* No need to cache our find -- open_path() will do that for us. */
@@ -1216,7 +1282,7 @@ get_dag(dag_node_t **dag_node_p,
    be SVN_INVALID_REVNUM.  Do all this as part of POOL.  */
 static svn_error_t *
 add_change(svn_fs_t *fs,
-           const char *txn_id,
+           const svn_fs_fs__id_part_t *txn_id,
            const char *path,
            const svn_fs_id_t *noderev_id,
            svn_fs_path_change_kind_t change_kind,
@@ -1253,8 +1319,8 @@ svn_fs_fs__node_id(const svn_fs_id_t **i
          The root directory ("" or "/") node is stored in the
          svn_fs_root_t object, and never changes when it's a revision
          root, so we can just reach in and grab it directly. */
-      fs_rev_root_data_t *frd = root->fsap_data;
-      *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(frd->root_dir), pool);
+      dag_node_t *root_dir = root->fsap_data;
+      *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(root_dir), pool);
     }
   else
     {
@@ -1412,14 +1478,14 @@ fs_change_node_prop(svn_fs_root_t *root,
 {
   parent_path_t *parent_path;
   apr_hash_t *proplist;
-  const char *txn_id;
+  const svn_fs_fs__id_part_t *txn_id;
 
   if (! root->is_txn_root)
     return SVN_FS__NOT_TXN(root);
-  txn_id = root->txn;
+  txn_id = root_txn_id(root);
 
   path = svn_fs__canonicalize_abspath(path, pool);
-  SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
+  SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
 
   /* Check (non-recursively) to see if path is locked; if so, check
      that we can use it. */
@@ -1554,7 +1620,7 @@ merge(svn_stringbuf_t *conflict_p,
       dag_node_t *target,
       dag_node_t *source,
       dag_node_t *ancestor,
-      const char *txn_id,
+      const svn_fs_fs__id_part_t *txn_id,
       apr_int64_t *mergeinfo_increment_out,
       apr_pool_t *pool)
 {
@@ -1808,14 +1874,14 @@ merge(svn_stringbuf_t *conflict_p,
 
           /* If either SOURCE-ENTRY or TARGET-ENTRY is not a direct
              modification of ANCESTOR-ENTRY, declare a conflict. */
-          if (strcmp(svn_fs_fs__id_node_id(s_entry->id),
-                     svn_fs_fs__id_node_id(a_entry->id)) != 0
-              || strcmp(svn_fs_fs__id_copy_id(s_entry->id),
-                        svn_fs_fs__id_copy_id(a_entry->id)) != 0
-              || strcmp(svn_fs_fs__id_node_id(t_entry->id),
-                        svn_fs_fs__id_node_id(a_entry->id)) != 0
-              || strcmp(svn_fs_fs__id_copy_id(t_entry->id),
-                        svn_fs_fs__id_copy_id(a_entry->id)) != 0)
+          if (!svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(s_entry->id),
+                                     svn_fs_fs__id_node_id(a_entry->id))
+              || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(s_entry->id),
+                                        svn_fs_fs__id_copy_id(a_entry->id))
+              || !svn_fs_fs__id_part_eq(svn_fs_fs__id_node_id(t_entry->id),
+                                        svn_fs_fs__id_node_id(a_entry->id))
+              || !svn_fs_fs__id_part_eq(svn_fs_fs__id_copy_id(t_entry->id),
+                                        svn_fs_fs__id_copy_id(a_entry->id)))
             return conflict_err(conflict_p,
                                 svn_fspath__join(target_path,
                                                  a_entry->name,
@@ -1918,8 +1984,8 @@ merge_changes(dag_node_t *ancestor_node,
 {
   dag_node_t *txn_root_node;
   svn_fs_t *fs = txn->fs;
-  const char *txn_id = txn->id;
-
+  const svn_fs_fs__id_part_t *txn_id = svn_fs_fs__txn_get_id(txn);
+  
   SVN_ERR(svn_fs_fs__dag_txn_root(&txn_root_node, fs, txn_id, pool));
 
   if (ancestor_node == NULL)
@@ -1948,6 +2014,7 @@ 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?
@@ -2044,7 +2111,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, iterpool);
+      err = svn_fs_fs__commit(new_rev, fs, txn, set_timestamp, iterpool);
       if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
         {
           /* Did someone else finish committing a new revision while we
@@ -2176,51 +2243,15 @@ fs_dir_entries(apr_hash_t **table_p,
   return svn_fs_fs__dag_dir_entries(table_p, node, pool);
 }
 
-/* Return a copy of PATH, allocated from POOL, for which newlines
-   have been escaped using the form \NNN (where NNN is the
-   octal representation of the byte's ordinal value).  */
-static const char *
-escape_newline(const char *path, apr_pool_t *pool)
+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)
 {
-  svn_stringbuf_t *retstr;
-  apr_size_t i, copied = 0;
-  int c;
-
-  /* At least one control character:
-      strlen - 1 (control) + \ + N + N + N + null . */
-  retstr = svn_stringbuf_create_ensure(strlen(path) + 4, pool);
-  for (i = 0; path[i]; i++)
-    {
-      c = (unsigned char)path[i];
-      if (c != '\n')
-        continue;
-
-      /* First things first, copy all the good stuff that we haven't
-         yet copied into our output buffer. */
-      if (i - copied)
-        svn_stringbuf_appendbytes(retstr, path + copied,
-                                  i - copied);
-
-      /* Make sure buffer is big enough for '\' 'N' 'N' 'N' (and NUL) */
-      svn_stringbuf_ensure(retstr, retstr->len + 4);
-      /*### The backslash separator doesn't work too great with Windows,
-         but it's what we'll use for consistency with invalid utf8
-         formatting (until someone has a better idea) */
-      apr_snprintf(retstr->data + retstr->len, 5, "\\%03o", (unsigned char)c);
-      retstr->len += 4;
-
-      /* Finally, update our copy counter. */
-      copied = i + 1;
-    }
-
-  /* Anything left to copy? */
-  if (i - copied)
-    svn_stringbuf_appendbytes(retstr, path + copied, i - copied);
-
-  /* retstr is null-terminated either by apr_snprintf or the svn_stringbuf
-     functions. */
+  *ordered_p = svn_fs_fs__order_dir_entries(root->fs, entries, pool);
 
-  return retstr->data;
+  return SVN_NO_ERROR;
 }
 
 /* Raise an error if PATH contains a newline because FSFS cannot handle
@@ -2233,7 +2264,7 @@ check_newline(const char *path, apr_pool
   if (c)
     return svn_error_createf(SVN_ERR_FS_PATH_SYNTAX, NULL,
        _("Invalid control character '0x%02x' in path '%s'"),
-       (unsigned char)*c, escape_newline(path, pool));
+       (unsigned char)*c, svn_path_illegal_path_escape(path, pool));
 
   return SVN_NO_ERROR;
 }
@@ -2249,13 +2280,13 @@ fs_make_dir(svn_fs_root_t *root,
 {
   parent_path_t *parent_path;
   dag_node_t *sub_dir;
-  const char *txn_id = root->txn;
+  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,
-                    txn_id, pool));
+                    TRUE, pool));
 
   /* Check (recursively) to see if some lock is 'reserving' a path at
      that location, or even some child-path; if so, check that we can
@@ -2298,15 +2329,16 @@ fs_delete_node(svn_fs_root_t *root,
                apr_pool_t *pool)
 {
   parent_path_t *parent_path;
-  const char *txn_id = root->txn;
+  const svn_fs_fs__id_part_t *txn_id;
   apr_int64_t mergeinfo_count = 0;
   svn_node_kind_t kind;
 
   if (! root->is_txn_root)
     return SVN_FS__NOT_TXN(root);
 
+  txn_id = root_txn_id(root);
   path = svn_fs__canonicalize_abspath(path, pool);
-  SVN_ERR(open_path(&parent_path, root, path, 0, txn_id, pool));
+  SVN_ERR(open_path(&parent_path, root, path, 0, TRUE, pool));
   kind = svn_fs_fs__dag_node_kind(parent_path->node);
 
   /* We can't remove the root of the filesystem.  */
@@ -2360,20 +2392,94 @@ fs_same_p(svn_boolean_t *same_p,
   return SVN_NO_ERROR;
 }
 
+/* Type to select the various behavioral modes of copy_helper.
+ */
+typedef enum copy_type_t
+{
+  /* add without history */
+  copy_type_plain_add,
+
+  /* add with history */
+  copy_type_add_with_history,
+
+  /* move (always with history) */
+  copy_type_move
+} copy_type_t;
+
+/* Set CHANGES to TRUE if PATH in ROOT is unchanged in REVISION if the
+   same files system.  If the content is identical, parent path copies and
+   deletions still count as changes.  Use POOL for temporary allocations.
+   Not that we will return an error if PATH does not exist in ROOT or
+   REVISION- */
+static svn_error_t *
+is_changed_node(svn_boolean_t *changed,
+                svn_fs_root_t *root,
+                const char *path,
+                svn_revnum_t revision,
+                apr_pool_t *pool)
+{
+  dag_node_t *node, *rev_node;
+  svn_fs_root_t *rev_root;
+  svn_fs_root_t *copy_from_root1, *copy_from_root2;
+  const char *copy_from_path1, *copy_from_path2;
+
+  SVN_ERR(svn_fs_fs__revision_root(&rev_root, root->fs, revision, pool));
+
+  /* Get the NODE for FROM_PATH in FROM_ROOT.*/
+  SVN_ERR(get_dag(&node, root, path, TRUE, pool));
+  SVN_ERR(get_dag(&rev_node, rev_root, path, TRUE, pool));
+
+  /* different ID -> got changed */
+  if (!svn_fs_fs__id_eq(svn_fs_fs__dag_get_id(node),
+                        svn_fs_fs__dag_get_id(rev_node)))
+    {
+      *changed = TRUE;
+       return SVN_NO_ERROR;
+    }
+
+  /* same node. might still be a lazy copy with separate history */
+  SVN_ERR(fs_closest_copy(&copy_from_root1, &copy_from_path1, root,
+                          path, pool));
+  SVN_ERR(fs_closest_copy(&copy_from_root2, &copy_from_path2, rev_root,
+                          path, pool));
+
+  if (copy_from_root1 == NULL && copy_from_root2 == NULL)
+    {
+      /* never copied -> same line of history */
+      *changed = FALSE;
+    }
+  else if (copy_from_root1 != NULL && copy_from_root2 != NULL)
+    {
+      /* both got copied. At the same time & location? */
+      *changed = (copy_from_root1->rev != copy_from_root2->rev)
+                 || strcmp(copy_from_path1, copy_from_path2);
+    }
+  else
+    {
+      /* one is a copy while the other one is not
+       * -> different lines of history */
+      *changed = TRUE;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
 /* Copy the node at FROM_PATH under FROM_ROOT to TO_PATH under
-   TO_ROOT.  If PRESERVE_HISTORY is set, then the copy is recorded in
-   the copies table.  Perform temporary allocations in POOL. */
+   TO_ROOT.  COPY_TYPE determines whether then the copy is recorded in
+   the copies table and whether it is being marked as a move.
+   Perform temporary allocations in POOL. */
 static svn_error_t *
 copy_helper(svn_fs_root_t *from_root,
             const char *from_path,
             svn_fs_root_t *to_root,
             const char *to_path,
-            svn_boolean_t preserve_history,
+            copy_type_t copy_type,
             apr_pool_t *pool)
 {
   dag_node_t *from_node;
   parent_path_t *to_parent_path;
-  const char *txn_id = to_root->txn;
+  const svn_fs_fs__id_part_t *txn_id = root_txn_id(to_root);
   svn_boolean_t same_p;
 
   /* Use an error check, not an assert, because even the caller cannot
@@ -2385,11 +2491,52 @@ copy_helper(svn_fs_root_t *from_root,
        _("Cannot copy between two different filesystems ('%s' and '%s')"),
        from_root->fs->path, to_root->fs->path);
 
+  /* more things that we can't do ATM */
   if (from_root->is_txn_root)
     return svn_error_create
       (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
        _("Copy from mutable tree not currently supported"));
 
+  if (! to_root->is_txn_root)
+    return svn_error_create
+      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+       _("Copy immutable tree not supported"));
+
+  /* move support comes with a number of conditions ... */
+  if (copy_type == copy_type_move)
+    {
+      /* if we don't copy from the TXN's base rev, check that the path has
+         not been touched in that revision range */
+      if (from_root->rev != txn_id->revision)
+        {
+          svn_boolean_t changed = TRUE;
+          svn_error_t *err = is_changed_node(&changed, from_root,
+                                             from_path, txn_id->revision,
+                                             pool);
+
+          /* Only "not found" is considered to be caused by out-of-date-ness.
+             Everything else means something got very wrong ... */
+          if (err && err->apr_err != SVN_ERR_FS_NOT_FOUND)
+            return svn_error_trace(err);
+
+          /* here error means "out of data" */
+          if (err || changed)
+            {
+              svn_error_clear(err);
+              return svn_error_create(SVN_ERR_FS_OUT_OF_DATE, NULL,
+                                      _("Move-from node is out-of-date"));
+            }
+
+          /* always move from the txn's base rev */
+          SVN_ERR(svn_fs_fs__revision_root(&from_root, from_root->fs,
+                                           txn_id->revision, pool));
+        }
+
+      /* does the FS support moves at all? */
+      if (!svn_fs_fs__supports_move(to_root->fs))
+        copy_type = copy_type_add_with_history;
+    }
+
   /* Get the NODE for FROM_PATH in FROM_ROOT.*/
   SVN_ERR(get_dag(&from_node, from_root, from_path, TRUE, pool));
 
@@ -2397,7 +2544,7 @@ copy_helper(svn_fs_root_t *from_root,
      component does not exist, it's not that big a deal.  We'll just
      make one there. */
   SVN_ERR(open_path(&to_parent_path, to_root, to_path,
-                    open_path_last_optional, txn_id, pool));
+                    open_path_last_optional, TRUE, pool));
 
   /* Check to see if path (or any child thereof) is locked; if so,
      check that we can use the existing lock(s). */
@@ -2426,14 +2573,20 @@ copy_helper(svn_fs_root_t *from_root,
          operation is a replacement, not an addition. */
       if (to_parent_path->node)
         {
-          kind = svn_fs_path_change_replace;
+          kind = copy_type == copy_type_move
+               ? svn_fs_path_change_movereplace
+               : svn_fs_path_change_replace;
+
           if (svn_fs_fs__fs_supports_mergeinfo(to_root->fs))
             SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&mergeinfo_start,
                                                        to_parent_path->node));
         }
       else
         {
-          kind = svn_fs_path_change_add;
+          kind = copy_type == copy_type_move
+               ? svn_fs_path_change_move
+               : svn_fs_path_change_add;
+
           mergeinfo_start = 0;
         }
 
@@ -2451,12 +2604,12 @@ copy_helper(svn_fs_root_t *from_root,
       SVN_ERR(svn_fs_fs__dag_copy(to_parent_path->parent->node,
                                   to_parent_path->entry,
                                   from_node,
-                                  preserve_history,
+                                  copy_type != copy_type_plain_add,
                                   from_root->rev,
                                   from_canonpath,
                                   txn_id, pool));
 
-      if (kind == svn_fs_path_change_replace)
+      if (kind != svn_fs_path_change_add)
         SVN_ERR(dag_node_cache_invalidate(to_root,
                                           parent_path_path(to_parent_path,
                                                            pool), pool));
@@ -2513,7 +2666,7 @@ fs_copy(svn_fs_root_t *from_root,
                                      to_root,
                                      svn_fs__canonicalize_abspath(to_path,
                                                                   pool),
-                                     TRUE, pool));
+                                     copy_type_add_with_history, pool));
 }
 
 
@@ -2531,7 +2684,29 @@ fs_revision_link(svn_fs_root_t *from_roo
 
   path = svn_fs__canonicalize_abspath(path, pool);
   return svn_error_trace(copy_helper(from_root, path, to_root, path,
-                                     FALSE, pool));
+                                     copy_type_plain_add, pool));
+}
+
+
+/* Create a copy of FROM_PATH in FROM_ROOT named TO_PATH in TO_ROOT and mark
+   it as a Move.  If FROM_PATH is a directory, copy it recursively. 
+   Temporary allocations are from POOL.*/
+static svn_error_t *
+fs_move(svn_fs_root_t *from_root,
+        const char *from_path,
+        svn_fs_root_t *to_root,
+        const char *to_path,
+        apr_pool_t *pool)
+{
+  SVN_ERR(check_newline(to_path, pool));
+
+  return svn_error_trace(copy_helper(from_root,
+                                     svn_fs__canonicalize_abspath(from_path,
+                                                                  pool),
+                                     to_root,
+                                     svn_fs__canonicalize_abspath(to_path,
+                                                                  pool),
+                                     copy_type_move, pool));
 }
 
 
@@ -2546,46 +2721,12 @@ fs_copied_from(svn_revnum_t *rev_p,
                apr_pool_t *pool)
 {
   dag_node_t *node;
-  const char *copyfrom_path, *copyfrom_str = NULL;
-  svn_revnum_t copyfrom_rev;
-  char *str, *buf;
-
-  /* Check to see if there is a cached version of this copyfrom
-     entry. */
-  if (! root->is_txn_root) {
-    fs_rev_root_data_t *frd = root->fsap_data;
-    copyfrom_str = svn_hash_gets(frd->copyfrom_cache, path);
-  }
-
-  if (copyfrom_str)
-    {
-      if (*copyfrom_str == 0)
-        {
-          /* We have a cached entry that says there is no copyfrom
-             here. */
-          copyfrom_rev = SVN_INVALID_REVNUM;
-          copyfrom_path = NULL;
-        }
-      else
-        {
-          /* Parse the copyfrom string for our cached entry. */
-          buf = apr_pstrdup(pool, copyfrom_str);
-          str = svn_cstring_tokenize(" ", &buf);
-          copyfrom_rev = SVN_STR_TO_REV(str);
-          copyfrom_path = buf;
-        }
-    }
-  else
-    {
-      /* There is no cached entry, look it up the old-fashioned
-         way. */
-      SVN_ERR(get_dag(&node, root, path, TRUE, pool));
-      SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&copyfrom_rev, node));
-      SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copyfrom_path, node));
-    }
 
-  *rev_p  = copyfrom_rev;
-  *path_p = copyfrom_path;
+  /* There is no cached entry, look it up the old-fashioned
+      way. */
+  SVN_ERR(get_dag(&node, root, path, TRUE, pool));
+  SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(rev_p, node));
+  SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(path_p, node));
 
   return SVN_NO_ERROR;
 }
@@ -2603,13 +2744,13 @@ fs_make_file(svn_fs_root_t *root,
 {
   parent_path_t *parent_path;
   dag_node_t *child;
-  const char *txn_id = root->txn;
+  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,
-                   txn_id, pool));
+                    TRUE, pool));
 
   /* If there's already a file by that name, complain.
      This also catches the case of trying to make a file named `/'.  */
@@ -2839,11 +2980,11 @@ apply_textdelta(void *baton, apr_pool_t 
 {
   txdelta_baton_t *tb = (txdelta_baton_t *) baton;
   parent_path_t *parent_path;
-  const char *txn_id = tb->root->txn;
+  const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
 
   /* Call open_path with no flags, as we want this to return an error
      if the node for which we are searching doesn't exist. */
-  SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
+  SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
 
   /* Check (non-recursively) to see if path is locked; if so, check
      that we can use it. */
@@ -3004,11 +3145,11 @@ apply_text(void *baton, apr_pool_t *pool
 {
   struct text_baton_t *tb = baton;
   parent_path_t *parent_path;
-  const char *txn_id = tb->root->txn;
+  const svn_fs_fs__id_part_t *txn_id = root_txn_id(tb->root);
 
   /* Call open_path with no flags, as we want this to return an error
      if the node for which we are searching doesn't exist. */
-  SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, txn_id, pool));
+  SVN_ERR(open_path(&parent_path, tb->root, tb->path, 0, TRUE, pool));
 
   /* Check (non-recursively) to see if path is locked; if so, check
      that we can use it. */
@@ -3142,14 +3283,11 @@ fs_paths_changed(apr_hash_t **changed_pa
                  apr_pool_t *pool)
 {
   if (root->is_txn_root)
-    return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs, root->txn,
-                                        pool);
+    return svn_fs_fs__txn_changes_fetch(changed_paths_p, root->fs,
+                                        root_txn_id(root), pool);
   else
-    {
-      fs_rev_root_data_t *frd = root->fsap_data;
-      return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
-                                      frd->copyfrom_cache, pool);
-    }
+    return svn_fs_fs__paths_changed(changed_paths_p, root->fs, root->rev,
+                                    pool);
 }
 
 
@@ -3271,7 +3409,7 @@ static svn_error_t *fs_closest_copy(svn_
   *path_p = NULL;
 
   path = svn_fs__canonicalize_abspath(path, pool);
-  SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
+  SVN_ERR(open_path(&parent_path, root, path, 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
@@ -3289,7 +3427,7 @@ static svn_error_t *fs_closest_copy(svn_
   if (kind == svn_node_none)
     return SVN_NO_ERROR;
   SVN_ERR(open_path(&copy_dst_parent_path, copy_dst_root, path,
-                    open_path_node_only, NULL, pool));
+                    open_path_node_only, FALSE, pool));
   copy_dst_node = copy_dst_parent_path->node;
   if (! svn_fs_fs__id_check_related(svn_fs_fs__dag_get_id(copy_dst_node),
                                     svn_fs_fs__dag_get_id(parent_path->node)))
@@ -3385,7 +3523,7 @@ fs_node_origin_rev(svn_revnum_t *revisio
 {
   svn_fs_t *fs = root->fs;
   const svn_fs_id_t *given_noderev_id, *cached_origin_id;
-  const char *node_id, *dash;
+  const svn_fs_fs__id_part_t *node_id;
 
   path = svn_fs__canonicalize_abspath(path, pool);
 
@@ -3393,19 +3531,13 @@ fs_node_origin_rev(svn_revnum_t *revisio
   SVN_ERR(svn_fs_fs__node_id(&given_noderev_id, root, path, pool));
   node_id = svn_fs_fs__id_node_id(given_noderev_id);
 
-  /* Is it a brand new uncommitted node? */
-  if (node_id[0] == '_')
+  /* Is it a brand new uncommitted node or a new-style node ID?
+   * (committed old-style nodes will have a 0 revision value;
+   * rev 0, number 0 is rev 0 root node). Note that != 0 includes
+   * SVN_INVALID_REVNUM for uncommitted nodes. */
+  if (node_id->revision != 0 || node_id->number == 0)
     {
-      *revision = SVN_INVALID_REVNUM;
-      return SVN_NO_ERROR;
-    }
-
-  /* Maybe this is a new-style node ID that just has the revision
-     sitting right in it. */
-  dash = strchr(node_id, '-');
-  if (dash && *(dash+1))
-    {
-      *revision = SVN_STR_TO_REV(dash + 1);
+      *revision = node_id->revision;
       return SVN_NO_ERROR;
     }
 
@@ -3485,7 +3617,7 @@ fs_node_origin_rev(svn_revnum_t *revisio
 
     /* Wow, I don't want to have to do all that again.  Let's cache
        the result. */
-    if (node_id[0] != '_')
+    if (node_id->revision != SVN_INVALID_REVNUM)
       SVN_ERR(svn_fs_fs__set_node_origin(fs, node_id,
                                          svn_fs_fs__dag_get_id(node), pool));
 
@@ -3496,26 +3628,17 @@ fs_node_origin_rev(svn_revnum_t *revisio
 }
 
 
-struct history_prev_args
-{
-  svn_fs_history_t **prev_history_p;
-  svn_fs_history_t *history;
-  svn_boolean_t cross_copies;
-  apr_pool_t *pool;
-};
-
-
 static svn_error_t *
-history_prev(void *baton, apr_pool_t *pool)
+history_prev(svn_fs_history_t **prev_history,
+             svn_fs_history_t *history,
+             svn_boolean_t cross_copies,
+             apr_pool_t *result_pool,
+             apr_pool_t *scratch_pool)
 {
-  struct history_prev_args *args = baton;
-  svn_fs_history_t **prev_history = args->prev_history_p;
-  svn_fs_history_t *history = args->history;
   fs_history_data_t *fhd = history->fsap_data;
   const char *commit_path, *src_path, *path = fhd->path;
   svn_revnum_t commit_rev, src_rev, dst_rev;
   svn_revnum_t revision = fhd->revision;
-  apr_pool_t *retpool = args->pool;
   svn_fs_t *fs = fhd->fs;
   parent_path_t *parent_path;
   dag_node_t *node;
@@ -3534,21 +3657,21 @@ history_prev(void *baton, apr_pool_t *po
   if (fhd->path_hint && SVN_IS_VALID_REVNUM(fhd->rev_hint))
     {
       reported = FALSE;
-      if (! args->cross_copies)
+      if (! cross_copies)
         return SVN_NO_ERROR;
       path = fhd->path_hint;
       revision = fhd->rev_hint;
     }
 
   /* Construct a ROOT for the current revision. */
-  SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, pool));
+  SVN_ERR(svn_fs_fs__revision_root(&root, fs, revision, scratch_pool));
 
   /* Open PATH/REVISION, and get its node and a bunch of other
      goodies.  */
-  SVN_ERR(open_path(&parent_path, root, path, 0, NULL, pool));
+  SVN_ERR(open_path(&parent_path, root, path, 0, FALSE, scratch_pool));
   node = parent_path->node;
   commit_path = svn_fs_fs__dag_get_created_path(node);
-  SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
+  SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
 
   /* The Subversion filesystem is written in such a way that a given
      line of history may have at most one interesting history point
@@ -3564,9 +3687,9 @@ history_prev(void *baton, apr_pool_t *po
           /* ... we either have not yet reported on this revision (and
              need now to do so) ... */
           *prev_history = assemble_history(fs,
-                                           apr_pstrdup(retpool, commit_path),
+                                           apr_pstrdup(result_pool, commit_path),
                                            commit_rev, TRUE, NULL,
-                                           SVN_INVALID_REVNUM, retpool);
+                                           SVN_INVALID_REVNUM, result_pool);
           return SVN_NO_ERROR;
         }
       else
@@ -3582,16 +3705,16 @@ history_prev(void *baton, apr_pool_t *po
 
           /* Replace NODE and friends with the information from its
              predecessor. */
-          SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, pool));
+          SVN_ERR(svn_fs_fs__dag_get_node(&node, fs, pred_id, scratch_pool));
           commit_path = svn_fs_fs__dag_get_created_path(node);
-          SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, pool));
+          SVN_ERR(svn_fs_fs__dag_get_revision(&commit_rev, node, scratch_pool));
         }
     }
 
   /* Find the youngest copyroot in the path of this node, including
      itself. */
   SVN_ERR(find_youngest_copyroot(&copyroot_rev, &copyroot_path, fs,
-                                 parent_path, pool));
+                                 parent_path, scratch_pool));
 
   /* Initialize some state variables. */
   src_path = NULL;
@@ -3605,8 +3728,8 @@ history_prev(void *baton, apr_pool_t *po
       svn_fs_root_t *copyroot_root;
 
       SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev,
-                                       pool));
-      SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, FALSE, pool));
+                                       scratch_pool));
+      SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, FALSE, scratch_pool));
       copy_dst = svn_fs_fs__dag_get_created_path(node);
 
       /* If our current path was the very destination of the copy,
@@ -3628,7 +3751,7 @@ history_prev(void *baton, apr_pool_t *po
           SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copy_src, node));
 
           dst_rev = copyroot_rev;
-          src_path = svn_fspath__join(copy_src, remainder_path, pool);
+          src_path = svn_fspath__join(copy_src, remainder_path, scratch_pool);
         }
     }
 
@@ -3645,15 +3768,15 @@ history_prev(void *baton, apr_pool_t *po
       if ((dst_rev == revision) && reported)
         retry = TRUE;
 
-      *prev_history = assemble_history(fs, apr_pstrdup(retpool, path),
+      *prev_history = assemble_history(fs, apr_pstrdup(result_pool, path),
                                        dst_rev, ! retry,
-                                       src_path, src_rev, retpool);
+                                       src_path, src_rev, result_pool);
     }
   else
     {
-      *prev_history = assemble_history(fs, apr_pstrdup(retpool, commit_path),
+      *prev_history = assemble_history(fs, apr_pstrdup(result_pool, commit_path),
                                        commit_rev, TRUE, NULL,
-                                       SVN_INVALID_REVNUM, retpool);
+                                       SVN_INVALID_REVNUM, result_pool);
     }
 
   return SVN_NO_ERROR;
@@ -3689,16 +3812,12 @@ fs_history_prev(svn_fs_history_t **prev_
     }
   else
     {
-      struct history_prev_args args;
       prev_history = history;
 
       while (1)
         {
-          args.prev_history_p = &prev_history;
-          args.history = prev_history;
-          args.cross_copies = cross_copies;
-          args.pool = pool;
-          SVN_ERR(history_prev(&args, pool));
+          SVN_ERR(history_prev(&prev_history, prev_history, cross_copies,
+                               pool, pool));
 
           if (! prev_history)
             break;
@@ -3899,7 +4018,7 @@ get_mergeinfo_for_path_internal(svn_merg
 
   path = svn_fs__canonicalize_abspath(path, scratch_pool);
 
-  SVN_ERR(open_path(&parent_path, rev_root, path, 0, NULL, scratch_pool));
+  SVN_ERR(open_path(&parent_path, rev_root, path, 0, FALSE, scratch_pool));
 
   if (inherit == svn_mergeinfo_nearest_ancestor && ! parent_path->parent)
     return SVN_NO_ERROR;
@@ -4162,6 +4281,9 @@ static root_vtable_t root_vtable = {
   fs_node_origin_rev,
   fs_node_created_path,
   fs_delete_node,
+  fs_copy,
+  fs_revision_link,
+  fs_move,
   fs_copied_from,
   fs_closest_copy,
   fs_node_prop,
@@ -4169,9 +4291,8 @@ static root_vtable_t root_vtable = {
   fs_change_node_prop,
   fs_props_changed,
   fs_dir_entries,
+  fs_dir_optimal_order,
   fs_make_dir,
-  fs_copy,
-  fs_revision_link,
   fs_file_length,
   fs_file_checksum,
   fs_file_contents,
@@ -4209,15 +4330,10 @@ make_revision_root(svn_fs_t *fs,
                    apr_pool_t *pool)
 {
   svn_fs_root_t *root = make_root(fs, pool);
-  fs_rev_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
 
   root->is_txn_root = FALSE;
   root->rev = rev;
-
-  frd->root_dir = root_dir;
-  frd->copyfrom_cache = svn_hash__make(root->pool);
-
-  root->fsap_data = frd;
+  root->fsap_data = root_dir;
 
   return root;
 }
@@ -4229,21 +4345,20 @@ make_revision_root(svn_fs_t *fs,
 static svn_error_t *
 make_txn_root(svn_fs_root_t **root_p,
               svn_fs_t *fs,
-              const char *txn,
+              const svn_fs_fs__id_part_t *txn,
               svn_revnum_t base_rev,
               apr_uint32_t flags,
               apr_pool_t *pool)
 {
   svn_fs_root_t *root = make_root(fs, pool);
   fs_txn_root_data_t *frd = apr_pcalloc(root->pool, sizeof(*frd));
+  frd->txn_id = *txn;
 
   root->is_txn_root = TRUE;
-  root->txn = apr_pstrdup(root->pool, txn);
+  root->txn = svn_fs_fs__id_txn_unparse(txn, root->pool);
   root->txn_flags = flags;
   root->rev = base_rev;
 
-  frd->txn_id = txn;
-
   /* Because this cache actually tries to invalidate elements, keep
      the number of elements per page down.
 
@@ -4262,7 +4377,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, txn, pool));
+  SVN_ERR(svn_fs_fs__initialize_txn_caches(fs, root->txn, pool));
 
   root->fsap_data = frd;
 
@@ -4359,19 +4474,28 @@ verify_node(dag_node_t *node,
         {
           svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
           dag_node_t *child;
-          svn_revnum_t child_rev;
           apr_int64_t child_mergeinfo;
 
           svn_pool_clear(iterpool);
 
           /* Compute CHILD_REV. */
-          SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id, iterpool));
-          SVN_ERR(svn_fs_fs__dag_get_revision(&child_rev, child, iterpool));
-
-          if (child_rev == rev)
-            SVN_ERR(verify_node(child, rev, iterpool));
+          if (svn_fs_fs__id_rev(dirent->id) == rev)
+            {
+              SVN_ERR(svn_fs_fs__dag_get_node(&child, fs, dirent->id,
+                                              iterpool));
+              SVN_ERR(verify_node(child, rev, iterpool));
+              SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo,
+                                                         child));
+            }
+          else
+            {
+              /* access mergeinfo counter with minimal overhead */
+              node_revision_t *noderev;
+              SVN_ERR(svn_fs_fs__get_node_revision(&noderev, fs, dirent->id,
+                                                   iterpool));
+              child_mergeinfo = noderev->mergeinfo_count;
+            }
 
-          SVN_ERR(svn_fs_fs__dag_get_mergeinfo_count(&child_mergeinfo, child));
           children_mergeinfo += child_mergeinfo;
         }
 
@@ -4411,12 +4535,11 @@ svn_fs_fs__verify_root(svn_fs_root_t *ro
   if (root->is_txn_root)
     {
       fs_txn_root_data_t *frd = root->fsap_data;
-      SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, frd->txn_id, pool));
+      SVN_ERR(svn_fs_fs__dag_txn_root(&root_dir, fs, &frd->txn_id, pool));
     }
   else
     {
-      fs_rev_root_data_t *frd = root->fsap_data;
-      root_dir = frd->root_dir;
+      root_dir = root->fsap_data;
     }
 
   /* Recursively verify ROOT_DIR. */

Modified: subversion/branches/cache-server/subversion/libsvn_fs_fs/tree.h
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_fs_fs/tree.h?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_fs_fs/tree.h (original)
+++ subversion/branches/cache-server/subversion/libsvn_fs_fs/tree.h Tue Oct 15 08:52:06 2013
@@ -48,10 +48,12 @@ 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.  Perform temporary
-   allocations in POOL. */
+   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. */
 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

Modified: subversion/branches/cache-server/subversion/libsvn_fs_util/fs-util.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_fs_util/fs-util.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_fs_util/fs-util.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_fs_util/fs-util.c Tue Oct 15 08:52:06 2013
@@ -26,11 +26,11 @@
 #include <apr_pools.h>
 #include <apr_strings.h>
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_fs.h"
 #include "svn_dirent_uri.h"
 #include "svn_path.h"
-#include "svn_private_config.h"
 
 #include "private/svn_fs_util.h"
 #include "private/svn_fspath.h"

Propchange: subversion/branches/cache-server/subversion/libsvn_fs_x/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Oct 15 08:52:06 2013
@@ -0,0 +1,11 @@
+Debug
+Release
+*.lo
+*.la
+.libs
+*.la-a
+*.o
+*~
+.*~
+rep-cache-db.h
+revprops-db.h

Propchange: subversion/branches/cache-server/subversion/libsvn_fs_x/
------------------------------------------------------------------------------
--- svn:mergeinfo (added)
+++ svn:mergeinfo Tue Oct 15 08:52:06 2013
@@ -0,0 +1,81 @@
+/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
+/subversion/branches/atomic-revprop/subversion/libsvn_fs_x:965046-1000689
+/subversion/branches/auto-props-sdc/subversion/libsvn_fs_x:1384106-1401643
+/subversion/branches/bdb-reverse-deltas/subversion/libsvn_fs_x:872050-872529
+/subversion/branches/cache-server/subversion/libsvn_fs_x:1458643-1476567
+/subversion/branches/diff-callbacks3/subversion/libsvn_fs_x:870059-870761
+/subversion/branches/diff-optimizations/subversion/libsvn_fs_x:1031270-1037352
+/subversion/branches/diff-optimizations-bytes/subversion/libsvn_fs_x:1037353-1067789
+/subversion/branches/dont-save-plaintext-passwords-by-default/subversion/libsvn_fs_x:870728-871118
+/subversion/branches/double-delete/subversion/libsvn_fs_x:870511-872970
+/subversion/branches/ev2-export/subversion/libsvn_fs_x:1325914,1332738,1413107
+/subversion/branches/explore-wc/subversion/libsvn_fs_x:875486,875493,875497,875507,875511,875514,875559,875580-875581,875584,875587,875611,875627,875647,875667-875668,875711-875712,875733-875734,875736,875744-875748,875751,875758,875782,875795-875796,875830,875836,875838,875842,875852,875855,875864,875870,875873,875880,875885-875888,875890,875897-875898,875905,875907-875909,875935,875943-875944,875946,875979,875982-875983,875985-875986,875990,875997
+/subversion/branches/file-externals/subversion/libsvn_fs_x:871779-873302
+/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,1498103,1498155,1500054,1507729-1507731,1507735-1507736
+/subversion/branches/fsfs-improvements/subversion/libsvn_fs_x:1499981-1517476
+/subversion/branches/fsfs-pack/subversion/libsvn_fs_x:873717-874575
+/subversion/branches/fsx/subversion/libsvn_fs_x:1507845-1509914
+/subversion/branches/gnome-keyring/subversion/libsvn_fs_x:870558-871410
+/subversion/branches/gpg-agent-password-store/subversion/libsvn_fs_x:1005036-1150766
+/subversion/branches/gtest_addition/subversion/libsvn_fs_x:1452117-1502138
+/subversion/branches/http-protocol-v2/subversion/libsvn_fs_x:874395-876041
+/subversion/branches/in-memory-cache/subversion/libsvn_fs_x:869829-871452
+/subversion/branches/in-repo-authz/subversion/libsvn_fs_x:1414342-1424779
+/subversion/branches/inheritable-props/subversion/libsvn_fs_x:1297080-1395089
+/subversion/branches/integrate-cache-item-serialization/subversion/libsvn_fs_x:1068724-1068739
+/subversion/branches/integrate-cache-membuffer/subversion/libsvn_fs_x:998649-998852
+/subversion/branches/integrate-compression-level/subversion/libsvn_fs_x:1068651-1072287
+/subversion/branches/integrate-io-improvements/subversion/libsvn_fs_x:1068684-1072297
+/subversion/branches/integrate-is-cachable/subversion/libsvn_fs_x:1072568-1074082
+/subversion/branches/integrate-partial-getter/subversion/libsvn_fs_x:1072558-1076552
+/subversion/branches/integrate-readline-speedup/subversion/libsvn_fs_x:1072553-1072555
+/subversion/branches/integrate-stream-api-extensions/subversion/libsvn_fs_x:1068695-1072516
+/subversion/branches/integrate-string-improvements/subversion/libsvn_fs_x:1068251-1190617
+/subversion/branches/integrate-txdelta-caching/subversion/libsvn_fs_x:1072541-1078213
+/subversion/branches/issue-2779-dev/subversion/libsvn_fs_x:965496-984198
+/subversion/branches/issue-2843-dev/subversion/libsvn_fs_x:871432-874179
+/subversion/branches/issue-3000/subversion/libsvn_fs_x:871713,871716-871719,871721-871726,871728,871734
+/subversion/branches/issue-3067-deleted-subtrees/subversion/libsvn_fs_x:873375-874084
+/subversion/branches/issue-3148-dev/subversion/libsvn_fs_x:875193-875204
+/subversion/branches/issue-3220-dev/subversion/libsvn_fs_x:872210-872226
+/subversion/branches/issue-3242-dev/subversion/libsvn_fs_x:879653-896436
+/subversion/branches/issue-3334-dirs/subversion/libsvn_fs_x:875156-875867
+/subversion/branches/issue-3975/subversion/libsvn_fs_x:1152931-1160746
+/subversion/branches/issue-4116-dev/subversion/libsvn_fs_x:1424719-1425040
+/subversion/branches/issue-4194-dev/subversion/libsvn_fs_x:1410507-1414880
+/subversion/branches/javahl-ra/subversion/libsvn_fs_x:991978-1494640
+/subversion/branches/kwallet/subversion/libsvn_fs_x:870785-871314
+/subversion/branches/log-g-performance/subversion/libsvn_fs_x:870941-871032
+/subversion/branches/merge-skips-obstructions/subversion/libsvn_fs_x:874525-874615
+/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/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/py-tests-as-modules/subversion/libsvn_fs_x:956579-1033052
+/subversion/branches/ra_serf-digest-authn/subversion/libsvn_fs_x:875693-876404
+/subversion/branches/reintegrate-improvements/subversion/libsvn_fs_x:873853-874164
+/subversion/branches/revprop-cache/subversion/libsvn_fs_x:1298521-1326293
+/subversion/branches/revprop-packing/subversion/libsvn_fs_x:1143907,1143971,1143997,1144017,1144499,1144568,1146145
+/subversion/branches/subtree-mergeinfo/subversion/libsvn_fs_x:876734-878766
+/subversion/branches/svn-mergeinfo-enhancements/subversion/libsvn_fs_x:870119-870195,870197-870288
+/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
+/subversion/branches/svnraisetc/subversion/libsvn_fs_x:874709-875149
+/subversion/branches/svnserve-logging/subversion/libsvn_fs_x:869828-870893
+/subversion/branches/tc-issue-3334/subversion/libsvn_fs_x:874697-874773
+/subversion/branches/tc-merge-notify/subversion/libsvn_fs_x:874017-874062
+/subversion/branches/tc-resolve/subversion/libsvn_fs_x:874191-874239
+/subversion/branches/tc_url_rev/subversion/libsvn_fs_x:874351-874483
+/subversion/branches/tree-conflicts/subversion/libsvn_fs_x:868291-873154
+/subversion/branches/tree-conflicts-notify/subversion/libsvn_fs_x:873926-874008
+/subversion/branches/tristate-chunked-request/subversion/libsvn_fs_x:1502394-1502681
+/subversion/branches/tweak-build-take-two/subversion/libsvn_fs_x:1424288-1425049,1425051-1425613
+/subversion/branches/uris-as-urls/subversion/libsvn_fs_x:1060426-1064427
+/subversion/branches/verify-at-commit/subversion/libsvn_fs_x:1462039-1462408
+/subversion/branches/verify-keep-going/subversion/libsvn_fs_x:1439280-1492639
+/subversion/branches/wc-collate-path/subversion/libsvn_fs_x:1402685-1480384
+/subversion/trunk/subversion/libsvn_fs_x:1413450-1532240

Modified: subversion/branches/cache-server/subversion/libsvn_ra/compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra/compat.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra/compat.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra/compat.c Tue Oct 15 08:52:06 2013
@@ -23,6 +23,7 @@
 
 #include <apr_pools.h>
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_error.h"
 #include "svn_pools.h"
@@ -36,7 +37,6 @@
 
 #include "private/svn_fspath.h"
 #include "ra_loader.h"
-#include "svn_private_config.h"
 
 
 
@@ -363,8 +363,9 @@ svn_ra__locations_from_log(svn_ra_sessio
      Notice that we always run on the youngest rev of the 3 inputs. */
   targets = apr_array_make(pool, 1, sizeof(const char *));
   APR_ARRAY_PUSH(targets, const char *) = path;
-  SVN_ERR(svn_ra_get_log2(session, targets, youngest, oldest, 0,
+  SVN_ERR(svn_ra_get_log3(session, targets, youngest, oldest, 0,
                           TRUE, FALSE, FALSE,
+                          svn_move_behavior_explicit_moves,
                           apr_array_make(pool, 0, sizeof(const char *)),
                           log_receiver, &lrb, pool));
 
@@ -585,8 +586,9 @@ svn_ra__location_segments_from_log(svn_r
      Notice that we always run on the youngest rev of the 3 inputs. */
   targets = apr_array_make(pool, 1, sizeof(const char *));
   APR_ARRAY_PUSH(targets, const char *) = path;
-  SVN_ERR(svn_ra_get_log2(session, targets, peg_revision, end_rev, 0,
+  SVN_ERR(svn_ra_get_log3(session, targets, peg_revision, end_rev, 0,
                           TRUE, FALSE, FALSE,
+                          svn_move_behavior_explicit_moves,
                           apr_array_make(pool, 0, sizeof(const char *)),
                           gls_log_receiver, &lrb, pool));
 
@@ -684,10 +686,11 @@ svn_ra__file_revs_from_log(svn_ra_sessio
   /* Accumulate revision metadata by walking the revisions
      backwards; this allows us to follow moves/copies
      correctly. */
-  SVN_ERR(svn_ra_get_log2(ra_session,
+  SVN_ERR(svn_ra_get_log3(ra_session,
                           condensed_targets,
                           end, start, 0, /* no limit */
                           TRUE, FALSE, FALSE,
+                          svn_move_behavior_explicit_moves,
                           NULL, fr_log_message_receiver, &lmb,
                           pool));
 
@@ -853,8 +856,9 @@ svn_ra__get_deleted_rev_from_log(svn_ra_
 
   /* Examine the logs of SESSION's URL to find when DELETED_PATH was first
      deleted or replaced. */
-  SVN_ERR(svn_ra_get_log2(session, NULL, peg_revision, end_revision, 0,
+  SVN_ERR(svn_ra_get_log3(session, NULL, peg_revision, end_revision, 0,
                           TRUE, TRUE, FALSE,
+                          svn_move_behavior_explicit_moves,
                           apr_array_make(pool, 0, sizeof(char *)),
                           log_path_del_receiver, &log_path_deleted_baton,
                           pool));

Modified: subversion/branches/cache-server/subversion/libsvn_ra/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra/deprecated.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra/deprecated.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra/deprecated.c Tue Oct 15 08:52:06 2013
@@ -26,6 +26,7 @@
    deprecated functions in this file. */
 #define SVN_DEPRECATED
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_ra.h"
 #include "svn_path.h"
@@ -36,9 +37,6 @@
 #include "ra_loader.h"
 #include "deprecated.h"
 
-#include "svn_private_config.h"
-
-
 
 
 /*** From ra_loader.c ***/
@@ -294,6 +292,25 @@ svn_error_t *svn_ra_do_diff(svn_ra_sessi
                          versus_url, diff_editor, diff_baton, pool);
 }
 
+svn_error_t *svn_ra_get_log2(svn_ra_session_t *session,
+                             const apr_array_header_t *paths,
+                             svn_revnum_t start,
+                             svn_revnum_t end,
+                             int limit,
+                             svn_boolean_t discover_changed_paths,
+                             svn_boolean_t strict_node_history,
+                             svn_boolean_t include_merged_revisions,
+                             const apr_array_header_t *revprops,
+                             svn_log_entry_receiver_t receiver,
+                             void *receiver_baton,
+                             apr_pool_t *pool)
+{
+  return svn_ra_get_log3(session, paths, start, end, limit,
+                         discover_changed_paths, strict_node_history,
+                         include_merged_revisions, svn_move_behavior_no_moves,
+                         revprops, receiver, receiver_baton, pool);
+}
+
 svn_error_t *svn_ra_get_log(svn_ra_session_t *session,
                             const apr_array_header_t *paths,
                             svn_revnum_t start,

Modified: subversion/branches/cache-server/subversion/libsvn_ra/ra_loader.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra/ra_loader.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra/ra_loader.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra/ra_loader.c Tue Oct 15 08:52:06 2013
@@ -33,6 +33,7 @@
 #include <apr_hash.h>
 #include <apr_uri.h>
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_version.h"
 #include "svn_types.h"
@@ -52,7 +53,6 @@
 #include "deprecated.h"
 
 #include "private/svn_ra_private.h"
-#include "svn_private_config.h"
 
 
 
@@ -877,7 +877,7 @@ svn_error_t *svn_ra_do_diff3(svn_ra_sess
                                   diff_baton, pool);
 }
 
-svn_error_t *svn_ra_get_log2(svn_ra_session_t *session,
+svn_error_t *svn_ra_get_log3(svn_ra_session_t *session,
                              const apr_array_header_t *paths,
                              svn_revnum_t start,
                              svn_revnum_t end,
@@ -885,6 +885,7 @@ svn_error_t *svn_ra_get_log2(svn_ra_sess
                              svn_boolean_t discover_changed_paths,
                              svn_boolean_t strict_node_history,
                              svn_boolean_t include_merged_revisions,
+                             svn_move_behavior_t move_behavior,
                              const apr_array_header_t *revprops,
                              svn_log_entry_receiver_t receiver,
                              void *receiver_baton,
@@ -905,8 +906,8 @@ svn_error_t *svn_ra_get_log2(svn_ra_sess
 
   return session->vtable->get_log(session, paths, start, end, limit,
                                   discover_changed_paths, strict_node_history,
-                                  include_merged_revisions, revprops,
-                                  receiver, receiver_baton, pool);
+                                  include_merged_revisions, move_behavior,
+                                  revprops, receiver, receiver_baton, pool);
 }
 
 svn_error_t *svn_ra_check_path(svn_ra_session_t *session,
@@ -1030,6 +1031,13 @@ svn_error_t *svn_ra_get_file_revs2(svn_r
   if (include_merged_revisions)
     SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool));
 
+  if (start > end)
+    SVN_ERR(
+     svn_ra__assert_capable_server(session,
+                                   SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
+                                   NULL,
+                                   pool));
+
   err = session->vtable->get_file_revs(session, path, start, end,
                                        include_merged_revisions,
                                        handler, handler_baton, pool);
@@ -1429,7 +1437,7 @@ svn_ra_print_modules(svn_stringbuf_t *ou
              built with SASL. */
           line = apr_psprintf(iterpool, "* ra_%s : %s\n",
                               defn->ra_name,
-                              vtable->get_description());
+                              vtable->get_description(iterpool));
           svn_stringbuf_appendcstr(output, line);
 
           for (schemes = vtable->get_schemes(iterpool); *schemes != NULL;

Modified: subversion/branches/cache-server/subversion/libsvn_ra/ra_loader.h
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra/ra_loader.h?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra/ra_loader.h (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra/ra_loader.h Tue Oct 15 08:52:06 2013
@@ -45,7 +45,7 @@ typedef struct svn_ra__vtable_t {
 
   /* Return a short description of the RA implementation, as a localized
    * string. */
-  const char *(*get_description)(void);
+  const char *(*get_description)(apr_pool_t *pool);
 
   /* Return a list of actual URI schemes supported by this implementation.
    * The returned array is NULL-terminated. */
@@ -194,6 +194,7 @@ typedef struct svn_ra__vtable_t {
                           svn_boolean_t discover_changed_paths,
                           svn_boolean_t strict_node_history,
                           svn_boolean_t include_merged_revisions,
+                          svn_move_behavior_t move_behavior,
                           const apr_array_header_t *revprops,
                           svn_log_entry_receiver_t receiver,
                           void *receiver_baton,

Modified: subversion/branches/cache-server/subversion/libsvn_ra/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra/util.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra/util.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra/util.c Tue Oct 15 08:52:06 2013
@@ -38,6 +38,26 @@
 #include "svn_private_config.h"
 #include "private/svn_ra_private.h"
 
+static const char *
+get_path(const char *path_or_url,
+         svn_ra_session_t *ra_session,
+         apr_pool_t *pool)
+{
+  if (path_or_url == NULL)
+    {
+      svn_error_t *err = svn_ra_get_session_url(ra_session, &path_or_url,
+                                                pool);
+      if (err)
+        {
+          /* The SVN_ERR_UNSUPPORTED_FEATURE error in the caller is more
+             important, so dummy up the session's URL and chuck this error. */
+          svn_error_clear(err);
+          return _("<repository>");
+        }
+    }
+  return path_or_url;
+}
+
 svn_error_t *
 svn_ra__assert_mergeinfo_capable_server(svn_ra_session_t *ra_session,
                                         const char *path_or_url,
@@ -48,18 +68,7 @@ svn_ra__assert_mergeinfo_capable_server(
                                 SVN_RA_CAPABILITY_MERGEINFO, pool));
   if (! mergeinfo_capable)
     {
-      if (path_or_url == NULL)
-        {
-          svn_error_t *err = svn_ra_get_session_url(ra_session, &path_or_url,
-                                                    pool);
-          if (err)
-            {
-              /* The SVN_ERR_UNSUPPORTED_FEATURE error is more important,
-                 so dummy up the session's URL and chuck this error. */
-              svn_error_clear(err);
-              path_or_url = "<repository>";
-            }
-        }
+      path_or_url = get_path(path_or_url, ra_session, pool);
       return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
                                _("Retrieval of mergeinfo unsupported by '%s'"),
                                svn_path_is_url(path_or_url)
@@ -69,6 +78,35 @@ svn_ra__assert_mergeinfo_capable_server(
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_ra__assert_capable_server(svn_ra_session_t *ra_session,
+                              const char *capability,
+                              const char *path_or_url,
+                              apr_pool_t *pool)
+{
+  if (!strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO))
+    return svn_ra__assert_mergeinfo_capable_server(ra_session, path_or_url,
+                                                   pool);
+
+  else
+    {
+      svn_boolean_t has;
+      SVN_ERR(svn_ra_has_capability(ra_session, &has, capability, pool));
+      if (! has)
+        {
+          path_or_url = get_path(path_or_url, ra_session, pool);
+          return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+                                 _("The '%s' feature is not supported by '%s'"),
+                                 capability,
+                                 svn_path_is_url(path_or_url)
+                                    ? path_or_url
+                                    : svn_dirent_local_style(path_or_url,
+                                                             pool));
+        }
+    }
+  return SVN_NO_ERROR;
+}
+
 /* Does ERR mean "the current value of the revprop isn't equal to
    the *OLD_VALUE_P you gave me"?
  */

Modified: subversion/branches/cache-server/subversion/libsvn_ra/wrapper_template.h
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra/wrapper_template.h?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra/wrapper_template.h (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra/wrapper_template.h Tue Oct 15 08:52:06 2013
@@ -395,6 +395,7 @@ static svn_error_t *compat_get_log(void 
   return VTBL.get_log(session_baton, paths, start, end, 0, /* limit */
                       discover_changed_paths, strict_node_history,
                       FALSE, /* include_merged_revisions */
+                      svn_move_behavior_no_moves,
                       svn_compat_log_revprops_in(pool), /* revprops */
                       receiver2, receiver2_baton, pool);
 }

Modified: subversion/branches/cache-server/subversion/libsvn_ra_local/ra_local.h
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra_local/ra_local.h?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra_local/ra_local.h (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra_local/ra_local.h Tue Oct 15 08:52:06 2013
@@ -62,6 +62,8 @@ typedef struct svn_ra_local__session_bat
   /* Callbacks/baton passed to svn_ra_open. */
   const svn_ra_callbacks2_t *callbacks;
   void *callback_baton;
+
+  const char *useragent;
 } svn_ra_local__session_baton_t;
 
 

Modified: subversion/branches/cache-server/subversion/libsvn_ra_local/ra_plugin.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra_local/ra_plugin.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra_local/ra_plugin.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra_local/ra_plugin.c Tue Oct 15 08:52:06 2013
@@ -21,6 +21,7 @@
  * ====================================================================
  */
 
+#include "svn_private_config.h"
 #include "ra_local.h"
 #include "svn_hash.h"
 #include "svn_ra.h"
@@ -35,7 +36,6 @@
 #include "svn_version.h"
 #include "svn_cache_config.h"
 
-#include "svn_private_config.h"
 #include "../libsvn_ra/ra_loader.h"
 #include "private/svn_mergeinfo_private.h"
 #include "private/svn_repos_private.h"
@@ -502,7 +502,7 @@ apply_lock_tokens(svn_fs_t *fs,
         N_("Module for accessing a repository on local disk.")
 
 static const char *
-svn_ra_local__get_description(void)
+svn_ra_local__get_description(apr_pool_t *pool)
 {
   return _(RA_LOCAL_DESCRIPTION);
 }
@@ -535,11 +535,15 @@ ignore_warnings(void *baton,
                 svn_error_t *err)
 {
 #ifdef SVN_DEBUG
-  SVN_DBG(("Ignoring FS warning %d\n", err ? err->apr_err : 0));
+  SVN_DBG(("Ignoring FS warning %s\n",
+           svn_error_symbolic_name(err ? err->apr_err : 0)));
 #endif
   return;
 }
 
+#define USER_AGENT "SVN/" SVN_VER_NUMBER " (" SVN_BUILD_TARGET ")" \
+                   " ra_local"
+
 static svn_error_t *
 svn_ra_local__open(svn_ra_session_t *session,
                    const char **corrected_url,
@@ -549,6 +553,7 @@ svn_ra_local__open(svn_ra_session_t *ses
                    apr_hash_t *config,
                    apr_pool_t *pool)
 {
+  const char *client_string;
   svn_ra_local__session_baton_t *sess;
   const char *fs_path;
   static volatile svn_atomic_t cache_init_state = 0;
@@ -591,6 +596,18 @@ svn_ra_local__open(svn_ra_session_t *ses
   /* Be sure username is NULL so we know to look it up / ask for it */
   sess->username = NULL;
 
+  if (sess->callbacks->get_client_string != NULL)
+    SVN_ERR(sess->callbacks->get_client_string(sess->callback_baton,
+                                               &client_string, pool));
+  else
+    client_string = NULL;
+
+  if (client_string)
+    sess->useragent = apr_pstrcat(pool, USER_AGENT " ",
+                                  client_string, (char *)NULL);
+  else
+    sess->useragent = USER_AGENT;
+
   session->priv = sess;
   return SVN_NO_ERROR;
 }
@@ -764,6 +781,8 @@ svn_ra_local__get_commit_editor(svn_ra_s
                 svn_string_create(sess->username, pool));
   svn_hash_sets(revprop_table, SVN_PROP_TXN_CLIENT_COMPAT_VERSION,
                 svn_string_create(SVN_VER_NUMBER, pool));
+  svn_hash_sets(revprop_table, SVN_PROP_TXN_USER_AGENT,
+                svn_string_create(sess->useragent, pool));
 
   /* Get the repos commit-editor */
   return svn_repos_get_commit_editor5
@@ -970,6 +989,7 @@ svn_ra_local__get_log(svn_ra_session_t *
                       svn_boolean_t discover_changed_paths,
                       svn_boolean_t strict_node_history,
                       svn_boolean_t include_merged_revisions,
+                      svn_move_behavior_t move_behavior,
                       const apr_array_header_t *revprops,
                       svn_log_entry_receiver_t receiver,
                       void *receiver_baton,
@@ -998,7 +1018,7 @@ svn_ra_local__get_log(svn_ra_session_t *
   receiver = log_receiver_wrapper;
   receiver_baton = &lb;
 
-  return svn_repos_get_logs4(sess->repos,
+  return svn_repos_get_logs5(sess->repos,
                              abs_paths,
                              start,
                              end,
@@ -1006,6 +1026,7 @@ svn_ra_local__get_log(svn_ra_session_t *
                              discover_changed_paths,
                              strict_node_history,
                              include_merged_revisions,
+                             move_behavior,
                              revprops,
                              NULL, NULL,
                              receiver,
@@ -1745,7 +1766,7 @@ svn_ra_local__init(const svn_version_t *
                                "ra_local"),
                              loader_version->major);
 
-  SVN_ERR(svn_ver_check_list(ra_local_version(), checklist));
+  SVN_ERR(svn_ver_check_list2(ra_local_version(), checklist, svn_ver_equal));
 
 #ifndef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL
   /* This assumes that POOL was the pool used to load the dso. */

Modified: subversion/branches/cache-server/subversion/libsvn_ra_local/split_url.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/subversion/libsvn_ra_local/split_url.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/subversion/libsvn_ra_local/split_url.c (original)
+++ subversion/branches/cache-server/subversion/libsvn_ra_local/split_url.c Tue Oct 15 08:52:06 2013
@@ -23,6 +23,7 @@
 
 #include "ra_local.h"
 #include <string.h>
+#include "svn_path.h"
 #include "svn_dirent_uri.h"
 #include "svn_private_config.h"
 
@@ -37,6 +38,8 @@ svn_ra_local__split_URL(svn_repos_t **re
   svn_error_t *err = SVN_NO_ERROR;
   const char *repos_dirent;
   const char *repos_root_dirent;
+  svn_stringbuf_t *urlbuf;
+  apr_size_t root_end;
 
   SVN_ERR(svn_uri_get_dirent_from_file_url(&repos_dirent, URL, pool));
 
@@ -62,15 +65,38 @@ svn_ra_local__split_URL(svn_repos_t **re
   /* = apr_pstrcat(pool,
                    "/",
                    svn_dirent_skip_ancestor(repos_root_dirent, repos_dirent),
-                   (const char *)NULL */
-  *fs_path = &repos_dirent[strlen(repos_root_dirent)];
-
-  if (**fs_path == '\0')
+                   (const char *)NULL); */
+  root_end = strlen(repos_root_dirent);
+  if (! repos_dirent[root_end])
     *fs_path = "/";
-
-  /* Create a url to the repository root. */
-  SVN_ERR(svn_uri_get_file_url_from_dirent(repos_root_url, repos_root_dirent,
-                                           pool));
+  else if (repos_dirent[root_end] == '/')
+    *fs_path = &repos_dirent[root_end];
+  else
+    {
+      /* On Windows "C:/" is the parent directory of "C:/dir" */
+      *fs_path = &repos_dirent[root_end-1];
+      SVN_ERR_ASSERT((*fs_path)[0] == '/');
+    }
+
+  /* Remove the path components after the root dirent from the original URL,
+     to get a URL to the repository root.
+
+     We don't use svn_uri_get_file_url_from_dirent() here as that would
+     transform several uris to form a differently formed url than
+     svn_uri_canonicalize would.
+
+     E.g. file://localhost/C:/dir -> file:///C:/dir
+          (a transform that was originally supported directly by this function,
+           before the implementation moved)
+
+          On on Windows:
+          file:///dir -> file:///E:/dir  (When E: is the current disk)
+     */
+  urlbuf = svn_stringbuf_create(URL, pool);
+  svn_path_remove_components(urlbuf,
+                             svn_path_component_count(repos_dirent)
+                             - svn_path_component_count(repos_root_dirent));
+  *repos_root_url = urlbuf->data;
 
   /* Configure hook script environment variables. */
   SVN_ERR(svn_repos_hooks_setenv(*repos, NULL, pool));