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/11/27 12:52:46 UTC

svn commit: r1546002 [17/39] - in /subversion/branches/verify-keep-going: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/server-side/ contrib/server-side/svnc...

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/authz.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/authz.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/authz.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/authz.c Wed Nov 27 11:52:35 2013
@@ -26,6 +26,7 @@
 #include <apr_pools.h>
 #include <apr_file_io.h>
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_pools.h"
 #include "svn_error.h"
@@ -35,6 +36,7 @@
 #include "svn_config.h"
 #include "svn_ctype.h"
 #include "private/svn_fspath.h"
+#include "private/svn_repos_private.h"
 #include "repos.h"
 
 
@@ -76,8 +78,8 @@ struct authz_validate_baton {
                            enumerator, if any. */
 };
 
-/* Currently this structure is just a wrapper around a
-   svn_config_t. */
+/* Currently this structure is just a wrapper around a svn_config_t.
+   Please update authz_pool if you modify this structure. */
 struct svn_authz_t
 {
   svn_config_t *cfg;
@@ -351,7 +353,7 @@ authz_get_path_access(svn_config_t *cfg,
   baton.user = user;
 
   /* Try to locate a repository-specific block first. */
-  qualified_path = apr_pstrcat(pool, repos_name, ":", path, (char *)NULL);
+  qualified_path = apr_pstrcat(pool, repos_name, ":", path, SVN_VA_NULL);
   svn_config_enumerate2(cfg, qualified_path,
                         authz_parse_line, &baton, pool);
 
@@ -394,7 +396,7 @@ authz_get_tree_access(svn_config_t *cfg,
   baton.required_access = required_access;
   baton.repos_path = path;
   baton.qualified_repos_path = apr_pstrcat(pool, repos_name,
-                                           ":", path, (char *)NULL);
+                                           ":", path, SVN_VA_NULL);
   /* Default to access granted if no rules say otherwise. */
   baton.access = TRUE;
 
@@ -453,7 +455,7 @@ authz_get_any_access(svn_config_t *cfg, 
   baton.access = FALSE; /* Deny access by default. */
   baton.repos_path = "/";
   baton.qualified_repos_path = apr_pstrcat(pool, repos_name,
-                                           ":/", (char *)NULL);
+                                           ":/", SVN_VA_NULL);
 
   /* We could have used svn_config_enumerate2 for "repos_name:/".
    * However, this requires access for root explicitly (which the user
@@ -748,9 +750,8 @@ static svn_boolean_t authz_validate_sect
 }
 
 
-/* Walk the configuration in AUTHZ looking for any errors. */
-static svn_error_t *
-authz_validate(svn_authz_t *authz, apr_pool_t *pool)
+svn_error_t *
+svn_repos__authz_validate(svn_authz_t *authz, apr_pool_t *pool)
 {
   struct authz_validate_baton baton = { 0 };
 
@@ -771,13 +772,17 @@ authz_validate(svn_authz_t *authz, apr_p
  *
  * If DIRENT cannot be parsed as a config file then an error is returned.  The
  * contents of CFG_P is then undefined.  If MUST_EXIST is TRUE, a missing
- * authz file is also an error.
+ * authz file is also an error.  The CASE_SENSITIVE controls the lookup
+ * behavior for section and option names alike.
  *
  * SCRATCH_POOL will be used for temporary allocations. */
 static svn_error_t *
-authz_retrieve_config_repo(svn_config_t **cfg_p, const char *dirent,
-                          svn_boolean_t must_exist,
-                          apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+authz_retrieve_config_repo(svn_config_t **cfg_p,
+                           const char *dirent,
+                           svn_boolean_t must_exist,
+                           svn_boolean_t case_sensitive,
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool)
 {
   svn_error_t *err;
   svn_repos_t *repos;
@@ -824,7 +829,8 @@ authz_retrieve_config_repo(svn_config_t 
     {
       if (!must_exist)
         {
-          SVN_ERR(svn_config_create2(cfg_p, TRUE, TRUE, result_pool));
+          SVN_ERR(svn_config_create2(cfg_p, case_sensitive, case_sensitive,
+                                     result_pool));
           return SVN_NO_ERROR;
         }
       else
@@ -842,7 +848,8 @@ authz_retrieve_config_repo(svn_config_t 
     }
 
   SVN_ERR(svn_fs_file_contents(&contents, root, fs_path, scratch_pool));
-  err = svn_config_parse(cfg_p, contents, TRUE, TRUE, result_pool);
+  err = svn_config_parse(cfg_p, contents, case_sensitive, case_sensitive,
+                         result_pool);
 
   /* Add the URL to the error stack since the parser doesn't have it. */
   if (err != SVN_NO_ERROR)
@@ -853,23 +860,12 @@ authz_retrieve_config_repo(svn_config_t 
   return SVN_NO_ERROR;
 }
 
-/* Given a PATH which might be a relative repo URL (^/), an absolute
- * local repo URL (file://), an absolute path outside of the repo
- * or a location in the Windows registry.
- *
- * Retrieve the configuration data that PATH points at and parse it into
- * CFG_P allocated in POOL.
- *
- * If PATH cannot be parsed as a config file then an error is returned.  The
- * contents of CFG_P is then undefined.  If MUST_EXIST is TRUE, a missing
- * authz file is also an error.
- *
- * REPOS_ROOT points at the root of the repos you are
- * going to apply the authz against, can be NULL if you are sure that you
- * don't have a repos relative URL in PATH. */
-static svn_error_t *
-authz_retrieve_config(svn_config_t **cfg_p, const char *path,
-                      svn_boolean_t must_exist, apr_pool_t *pool)
+svn_error_t *
+svn_repos__retrieve_config(svn_config_t **cfg_p,
+                           const char *path,
+                           svn_boolean_t must_exist,
+                           svn_boolean_t case_sensitive,
+                           apr_pool_t *pool)
 {
   if (svn_path_is_url(path))
     {
@@ -880,8 +876,8 @@ authz_retrieve_config(svn_config_t **cfg
       err = svn_uri_get_dirent_from_file_url(&dirent, path, scratch_pool);
 
       if (err == SVN_NO_ERROR)
-        err = authz_retrieve_config_repo(cfg_p, dirent, must_exist, pool,
-                                         scratch_pool);
+        err = authz_retrieve_config_repo(cfg_p, dirent, must_exist,
+                                         case_sensitive, pool, scratch_pool);
 
       /* Close the repos and streams we opened. */
       svn_pool_destroy(scratch_pool);
@@ -891,7 +887,8 @@ authz_retrieve_config(svn_config_t **cfg
   else
     {
       /* Outside of repo file or Windows registry*/
-      SVN_ERR(svn_config_read3(cfg_p, path, must_exist, TRUE, TRUE, pool));
+      SVN_ERR(svn_config_read3(cfg_p, path, must_exist, case_sensitive,
+                               case_sensitive, pool));
     }
 
   return SVN_NO_ERROR;
@@ -942,9 +939,11 @@ svn_repos__authz_read(svn_authz_t **auth
 
   /* Load the authz file */
   if (accept_urls)
-    SVN_ERR(authz_retrieve_config(&authz->cfg, path, must_exist, pool));
+    SVN_ERR(svn_repos__retrieve_config(&authz->cfg, path, must_exist, TRUE,
+                                       pool));
   else
-    SVN_ERR(svn_config_read3(&authz->cfg, path, must_exist, TRUE, TRUE, pool));
+    SVN_ERR(svn_config_read3(&authz->cfg, path, must_exist, TRUE, TRUE,
+                             pool));
 
   if (groups_path)
     {
@@ -953,8 +952,8 @@ svn_repos__authz_read(svn_authz_t **auth
 
       /* Load the groups file */
       if (accept_urls)
-        SVN_ERR(authz_retrieve_config(&groups_cfg, groups_path, must_exist,
-                                      pool));
+        SVN_ERR(svn_repos__retrieve_config(&groups_cfg, groups_path,
+                                           must_exist, TRUE, pool));
       else
         SVN_ERR(svn_config_read3(&groups_cfg, groups_path, must_exist,
                                  TRUE, TRUE, pool));
@@ -971,7 +970,7 @@ svn_repos__authz_read(svn_authz_t **auth
     }
 
   /* Make sure there are no errors in the configuration. */
-  SVN_ERR(authz_validate(authz, pool));
+  SVN_ERR(svn_repos__authz_validate(authz, pool));
 
   *authz_p = authz;
   return SVN_NO_ERROR;
@@ -1011,7 +1010,7 @@ svn_repos_authz_parse(svn_authz_t **auth
     }
 
   /* Make sure there are no errors in the configuration. */
-  SVN_ERR(authz_validate(authz, pool));
+  SVN_ERR(svn_repos__authz_validate(authz, pool));
 
   *authz_p = authz;
   return SVN_NO_ERROR;

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/commit.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/commit.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/commit.c Wed Nov 27 11:52:35 2013
@@ -26,6 +26,7 @@
 #include <apr_pools.h>
 #include <apr_file_io.h>
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_compat.h"
 #include "svn_pools.h"
@@ -39,7 +40,6 @@
 #include "svn_ctype.h"
 #include "svn_props.h"
 #include "svn_mergeinfo.h"
-#include "svn_private_config.h"
 
 #include "repos.h"
 
@@ -1013,7 +1013,7 @@ ev2_check_authz(const struct ev2_baton *
     return SVN_NO_ERROR;
 
   if (relpath)
-    fspath = apr_pstrcat(scratch_pool, "/", relpath, NULL);
+    fspath = apr_pstrcat(scratch_pool, "/", relpath, SVN_VA_NULL);
   else
     fspath = NULL;
 
@@ -1198,20 +1198,6 @@ move_cb(void *baton,
 }
 
 
-/* This implements svn_editor_cb_rotate_t */
-static svn_error_t *
-rotate_cb(void *baton,
-          const apr_array_header_t *relpaths,
-          const apr_array_header_t *revisions,
-          apr_pool_t *scratch_pool)
-{
-  struct ev2_baton *eb = baton;
-
-  SVN_ERR(svn_editor_rotate(eb->inner, relpaths, revisions));
-  return SVN_NO_ERROR;
-}
-
-
 /* This implements svn_editor_cb_complete_t */
 static svn_error_t *
 complete_cb(void *baton,
@@ -1333,7 +1319,6 @@ svn_repos__get_commit_ev2(svn_editor_t *
     delete_cb,
     copy_cb,
     move_cb,
-    rotate_cb,
     complete_cb,
     abort_cb
   };

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/delta.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/delta.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/delta.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/delta.c Wed Nov 27 11:52:35 2013
@@ -24,6 +24,7 @@
 
 #include <apr_hash.h>
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_types.h"
 #include "svn_delta.h"
@@ -33,7 +34,6 @@
 #include "svn_repos.h"
 #include "svn_pools.h"
 #include "svn_props.h"
-#include "svn_private_config.h"
 #include "repos.h"
 
 

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/deprecated.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/deprecated.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/deprecated.c Wed Nov 27 11:52:35 2013
@@ -26,13 +26,12 @@
    deprecated functions in this file. */
 #define SVN_DEPRECATED
 
+#include "svn_private_config.h"
 #include "svn_repos.h"
 #include "svn_compat.h"
 #include "svn_hash.h"
 #include "svn_props.h"
 
-#include "svn_private_config.h"
-
 #include "repos.h"
 
 
@@ -473,6 +472,30 @@ svn_repos_fs_get_locks(apr_hash_t **lock
 
 /*** From logs.c ***/
 svn_error_t *
+svn_repos_get_logs4(svn_repos_t *repos,
+                    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_repos_authz_func_t authz_read_func,
+                    void *authz_read_baton,
+                    svn_log_entry_receiver_t receiver,
+                    void *receiver_baton,
+                    apr_pool_t *pool)
+{
+  return svn_repos_get_logs5(repos, paths, start, end, limit,
+                             discover_changed_paths, strict_node_history,
+                             include_merged_revisions,
+                             svn_move_behavior_no_moves, revprops,
+                             authz_read_func, authz_read_baton,
+                             receiver, receiver_baton, pool);
+}
+
+svn_error_t *
 svn_repos_get_logs3(svn_repos_t *repos,
                     const apr_array_header_t *paths,
                     svn_revnum_t start,
@@ -740,6 +763,7 @@ svn_repos_verify_fs2(svn_repos_t *repos,
                                               start_rev,
                                               end_rev,
                                               FALSE,
+                                              FALSE,
                                               notify_func,
                                               notify_baton,
                                               cancel_func,

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/dump.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/dump.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/dump.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/dump.c Wed Nov 27 11:52:35 2013
@@ -38,12 +38,277 @@
 
 #include "private/svn_mergeinfo_private.h"
 #include "private/svn_fs_private.h"
+#include "private/svn_utf_private.h"
+#include "private/svn_cache.h"
 
 #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
 
 /*----------------------------------------------------------------------*/
 
 
+/* To be able to check whether a path exists in the current revision
+   (as changes come in), we need to track the relevant tree changes.
+
+   In particular, we remember deletions, additions and copies including
+   their copy-from info.  Since the dump performs a pre-order tree walk,
+   we only need to store the data for the stack of parent folders.
+
+   The problem that we are trying to solve is that the dump receives
+   transforming operations whose validity depends on previous operations
+   in the same revision but cannot be checked against the final state
+   as stored in the repository as that is the state *after* we applied
+   the respective tree changes.
+
+   Note that the tracker functions don't perform any sanity or validity
+   checks.  Those higher-level tests have to be done in the calling code.
+   However, there is no way to corrupt the data structure using the
+   provided functions.
+ */
+
+/* Single entry in the path tracker.  Not all levels along the path
+   hierarchy do need to have an instance of this struct but only those
+   that got changed by a tree modification.
+
+   Please note that the path info in this struct is stored in re-usable
+   stringbuf objects such that we don't need to allocate more memory than
+   the longest path we encounter.
+ */
+typedef struct path_tracker_entry_t
+{
+  /* path in the current tree */
+  svn_stringbuf_t *path;
+
+  /* copy-from path (must be empty if COPYFROM_REV is SVN_INVALID_REVNUM) */
+  svn_stringbuf_t *copyfrom_path;
+
+  /* copy-from revision (SVN_INVALID_REVNUM for additions / replacements
+     that don't copy history, i.e. with no sub-tree) */
+  svn_revnum_t copyfrom_rev;
+
+  /* if FALSE, PATH has been deleted */
+  svn_boolean_t exists;
+} path_tracker_entry_t;
+
+/* Tracks all tree modifications above the current path.
+ */
+typedef struct path_tracker_t
+{
+  /* Container for all relevant tree changes in depth order.
+     May contain more entries than DEPTH to allow for reusing memory.
+     Only entries 0 .. DEPTH-1 are valid.
+   */
+  apr_array_header_t *stack;
+
+  /* Number of relevant entries in STACK.  May be 0 */
+  int depth;
+
+  /* Revision that we current track.  If DEPTH is 0, paths are exist in
+     REVISION exactly when they exist in REVISION-1.  This applies only
+     to the current state of our tree walk.
+   */
+  svn_revnum_t revision;
+
+  /* Allocate container entries here. */
+  apr_pool_t *pool;
+} path_tracker_t;
+
+/* Return a new path tracker object for REVISION, allocated in POOL.
+ */
+static path_tracker_t *
+tracker_create(svn_revnum_t revision,
+               apr_pool_t *pool)
+{
+  path_tracker_t *result = apr_pcalloc(pool, sizeof(*result));
+  result->stack = apr_array_make(pool, 16, sizeof(path_tracker_entry_t));
+  result->revision = revision;
+  result->pool = pool;
+
+  return result;
+}
+
+/* Remove all entries from TRACKER that are not relevant to PATH anymore.
+ * If ALLOW_EXACT_MATCH is FALSE, keep only entries that pertain to
+ * parent folders but not to PATH itself.
+ *
+ * This internal function implicitly updates the tracker state during the
+ * tree by removing "past" entries.  Other functions will add entries when
+ * we encounter a new tree change.
+ */
+static void
+tracker_trim(path_tracker_t *tracker,
+             const char *path,
+             svn_boolean_t allow_exact_match)
+{
+  /* remove everything that is unrelated to PATH.
+     Note that TRACKER->STACK is depth-ordered,
+     i.e. stack[N] is a (maybe indirect) parent of stack[N+1]
+     for N+1 < DEPTH.
+   */
+  for (; tracker->depth; --tracker->depth)
+    {
+      path_tracker_entry_t *parent = &APR_ARRAY_IDX(tracker->stack,
+                                                    tracker->depth - 1,
+                                                    path_tracker_entry_t);
+      const char *rel_path
+        = svn_dirent_skip_ancestor(parent->path->data, path);
+
+      /* always keep parents.  Keep exact matches when allowed. */
+      if (rel_path && (allow_exact_match || *rel_path != '\0'))
+        break;
+    }
+}
+
+/* Using TRACKER, check what path at what revision in the repository must
+   be checked to decide that whether PATH exists.  Return the info in
+   *ORIG_PATH and *ORIG_REV, respectively.
+
+   If the path is known to not exist, *ORIG_PATH will be NULL and *ORIG_REV
+   will be SVN_INVALID_REVNUM.  If *ORIG_REV is SVN_INVALID_REVNUM, PATH
+   has just been added in the revision currently being tracked.
+
+   Use POOL for allocations.  Note that *ORIG_PATH may be allocated in POOL,
+   a reference to internal data with the same lifetime as TRACKER or just
+   PATH.
+ */
+static void
+tracker_lookup(const char **orig_path,
+               svn_revnum_t *orig_rev,
+               path_tracker_t *tracker,
+               const char *path,
+               apr_pool_t *pool)
+{
+  tracker_trim(tracker, path, TRUE);
+  if (tracker->depth == 0)
+    {
+      /* no tree changes -> paths are the same as in the previous rev. */
+      *orig_path = path;
+      *orig_rev = tracker->revision - 1;
+    }
+  else
+    {
+      path_tracker_entry_t *parent = &APR_ARRAY_IDX(tracker->stack,
+                                                    tracker->depth - 1,
+                                                    path_tracker_entry_t);
+      if (parent->exists)
+        {
+          const char *rel_path
+            = svn_dirent_skip_ancestor(parent->path->data, path);
+
+          if (parent->copyfrom_rev != SVN_INVALID_REVNUM)
+            {
+              /* parent is a copy with history. Translate path. */
+              *orig_path = svn_dirent_join(parent->copyfrom_path->data,
+                                           rel_path, pool);
+              *orig_rev = parent->copyfrom_rev;
+            }
+          else if (*rel_path == '\0')
+            {
+              /* added in this revision with no history */
+              *orig_path = path;
+              *orig_rev = tracker->revision;
+            }
+          else
+            {
+              /* parent got added but not this path */
+              *orig_path = NULL;
+              *orig_rev = SVN_INVALID_REVNUM;
+            }
+        }
+      else
+        {
+          /* (maybe parent) path has been deleted */
+          *orig_path = NULL;
+          *orig_rev = SVN_INVALID_REVNUM;
+        }
+    }
+}
+
+/* Return a reference to the stack entry in TRACKER for PATH.  If no
+   suitable entry exists, add one.  Implicitly updates the tracked tree
+   location.
+
+   Only the PATH member of the result is being updated.  All other members
+   will have undefined values.
+ */
+static path_tracker_entry_t *
+tracker_add_entry(path_tracker_t *tracker,
+                  const char *path)
+{
+  path_tracker_entry_t *entry;
+  tracker_trim(tracker, path, FALSE);
+
+  if (tracker->depth == tracker->stack->nelts)
+    {
+      entry = apr_array_push(tracker->stack);
+      entry->path = svn_stringbuf_create_empty(tracker->pool);
+      entry->copyfrom_path = svn_stringbuf_create_empty(tracker->pool);
+    }
+  else
+    {
+      entry = &APR_ARRAY_IDX(tracker->stack, tracker->depth,
+                             path_tracker_entry_t);
+    }
+
+  svn_stringbuf_set(entry->path, path);
+  ++tracker->depth;
+
+  return entry;
+}
+
+/* Update the TRACKER with a copy from COPYFROM_PATH@COPYFROM_REV to
+   PATH in the tracked revision.
+ */
+static void
+tracker_path_copy(path_tracker_t *tracker,
+                  const char *path,
+                  const char *copyfrom_path,
+                  svn_revnum_t copyfrom_rev)
+{
+  path_tracker_entry_t *entry = tracker_add_entry(tracker, path);
+
+  svn_stringbuf_set(entry->copyfrom_path, copyfrom_path);
+  entry->copyfrom_rev = copyfrom_rev;
+  entry->exists = TRUE;
+}
+
+/* Update the TRACKER with a plain addition of PATH (without history).
+ */
+static void
+tracker_path_add(path_tracker_t *tracker,
+                 const char *path)
+{
+  path_tracker_entry_t *entry = tracker_add_entry(tracker, path);
+
+  svn_stringbuf_setempty(entry->copyfrom_path);
+  entry->copyfrom_rev = SVN_INVALID_REVNUM;
+  entry->exists = TRUE;
+}
+
+/* Update the TRACKER with a replacement of PATH with a plain addition
+   (without history).
+ */
+static void
+tracker_path_replace(path_tracker_t *tracker,
+                     const char *path)
+{
+  /* this will implicitly purge all previous sub-tree info from STACK.
+     Thus, no need to tack the deletion explicitly. */
+  tracker_path_add(tracker, path);
+}
+
+/* Update the TRACKER with a deletion of PATH.
+ */
+static void
+tracker_path_delete(path_tracker_t *tracker,
+                    const char *path)
+{
+  path_tracker_entry_t *entry = tracker_add_entry(tracker, path);
+
+  svn_stringbuf_setempty(entry->copyfrom_path);
+  entry->copyfrom_rev = SVN_INVALID_REVNUM;
+  entry->exists = FALSE;
+}
+
 
 /* Compute the delta between OLDROOT/OLDPATH and NEWROOT/NEWPATH and
    store it into a new temporary file *TEMPFILE.  OLDROOT may be NULL,
@@ -116,6 +381,9 @@ struct edit_baton
   /* True if this "dump" is in fact a verify. */
   svn_boolean_t verify;
 
+  /* True if checking UCS normalization during a verify. */
+  svn_boolean_t check_ucs_norm;
+
   /* The first revision dumped in this dumpstream. */
   svn_revnum_t oldest_dumped_rev;
 
@@ -130,6 +398,17 @@ struct edit_baton
   /* reusable buffer for writing file contents */
   char buffer[SVN__STREAM_CHUNK_SIZE];
   apr_size_t bufsize;
+
+  /* map nodeID -> node kind.  May be NULL.
+     The key is the string representation of the node ID given in
+     directory entries.  If we find an entry in this cache, the
+     respective node has already been verified as readable and being
+     of the type stored as value in the cache. */
+  svn_cache__t *verified_dirents_cache;
+
+  /* Structure allows us to verify the paths currently being dumped.
+     If NULL, validity checks are being skipped. */
+  path_tracker_t *path_tracker;
 };
 
 struct dir_baton
@@ -159,6 +438,12 @@ struct dir_baton
      really, they're all within this directory.) */
   apr_hash_t *deleted_entries;
 
+  /* A flag indicating that new entries have been added to this
+     directory in this revision. Used to optimize detection of UCS
+     representation collisions; we will only check for that in
+     revisions where new names appear in the directory. */
+  svn_boolean_t check_name_collision;
+
   /* pool to be used for deleting the hash items */
   apr_pool_t *pool;
 };
@@ -211,11 +496,199 @@ make_dir_baton(const char *path,
   new_db->added = added;
   new_db->written_out = FALSE;
   new_db->deleted_entries = apr_hash_make(pool);
+  new_db->check_name_collision = FALSE;
   new_db->pool = pool;
 
   return new_db;
 }
 
+static svn_error_t *
+fetch_kind_func(svn_node_kind_t *kind,
+                void *baton,
+                const char *path,
+                svn_revnum_t base_revision,
+                apr_pool_t *scratch_pool);
+
+/* Return an error when PATH in REVISION does not exist or is of a
+   different kind than EXPECTED_KIND.  If the latter is svn_node_unknown,
+   skip that check.  Use EB for context information.  If REVISION is the
+   current revision, use EB's path tracker to follow renames, deletions,
+   etc.
+
+   Use SCRATCH_POOL for temporary allocations.
+   No-op if EB's path tracker has not been initialized.
+ */
+static svn_error_t *
+node_must_exist(struct edit_baton *eb,
+                const char *path,
+                svn_revnum_t revision,
+                svn_node_kind_t expected_kind,
+                apr_pool_t *scratch_pool)
+{
+  svn_node_kind_t kind = svn_node_none;
+
+  /* in case the caller is trying something stupid ... */
+  if (eb->path_tracker == NULL)
+    return SVN_NO_ERROR;
+
+  /* paths pertaining to the revision currently being processed must
+     be translated / checked using our path tracker. */
+  if (revision == eb->path_tracker->revision)
+    tracker_lookup(&path, &revision, eb->path_tracker, path, scratch_pool);
+
+  /* determine the node type (default: no such node) */
+  if (path)
+    SVN_ERR(fetch_kind_func(&kind, eb, path, revision, scratch_pool));
+
+  /* check results */
+  if (kind == svn_node_none)
+    return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+                             _("Path '%s' not found in r%ld."),
+                             path, revision);
+
+  if (expected_kind != kind && expected_kind != svn_node_unknown)
+    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+                             _("Unexpected node kind %d for '%s' at r%ld. "
+                               "Expected kind was %d."),
+                             kind, path, revision, expected_kind);
+
+  return SVN_NO_ERROR;
+}
+
+/* Return an error when PATH exists in REVISION.  Use EB for context
+   information.  If REVISION is the current revision, use EB's path
+   tracker to follow renames, deletions, etc.
+
+   Use SCRATCH_POOL for temporary allocations.
+   No-op if EB's path tracker has not been initialized.
+ */
+static svn_error_t *
+node_must_not_exist(struct edit_baton *eb,
+                    const char *path,
+                    svn_revnum_t revision,
+                    apr_pool_t *scratch_pool)
+{
+  svn_node_kind_t kind = svn_node_none;
+
+  /* in case the caller is trying something stupid ... */
+  if (eb->path_tracker == NULL)
+    return SVN_NO_ERROR;
+
+  /* paths pertaining to the revision currently being processed must
+     be translated / checked using our path tracker. */
+  if (revision == eb->path_tracker->revision)
+    tracker_lookup(&path, &revision, eb->path_tracker, path, scratch_pool);
+
+  /* determine the node type (default: no such node) */
+  if (path)
+    SVN_ERR(fetch_kind_func(&kind, eb, path, revision, scratch_pool));
+
+  /* check results */
+  if (kind != svn_node_none)
+    return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
+                             _("Path '%s' exists in r%ld."),
+                             path, revision);
+
+  return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
+check_ucs_normalization(const char *path,
+                        svn_node_kind_t kind,
+                        svn_repos_notify_func_t notify_func,
+                        void *notify_baton,
+                        apr_pool_t *scratch_pool)
+{
+  const char *const name = svn_relpath_basename(path, scratch_pool);
+  if (!svn_utf__is_normalized(name, scratch_pool))
+    {
+      svn_repos_notify_t *const notify =
+        svn_repos_notify_create(svn_repos_notify_warning, scratch_pool);
+      notify->warning = svn_repos_notify_warning_denormalized_name;
+      switch (kind)
+        {
+        case svn_node_dir:
+          notify->warning_str = apr_psprintf(
+              scratch_pool, _("Denormalized directory name '%s'"), path);
+          break;
+        case svn_node_file:
+          notify->warning_str = apr_psprintf(
+              scratch_pool, _("Denormalized file name '%s'"), path);
+          break;
+        default:
+          notify->warning_str = apr_psprintf(
+              scratch_pool, _("Denormalized entry name '%s'"), path);
+        }
+      notify_func(notify_baton, notify, scratch_pool);
+    }
+  return SVN_NO_ERROR;
+}
+
+
+/* Baton used by the check_mergeinfo_normalization hash iterator. */
+struct check_mergeinfo_normalization_baton
+{
+  const char* path;
+  apr_hash_t *normalized;
+  svn_membuf_t buffer;
+  svn_repos_notify_func_t notify_func;
+  void *notify_baton;
+};
+
+/* Hash iterator that verifies normalization and collision of paths in
+   an svn:mergeinfo property. */
+static svn_error_t *
+check_mergeinfo_normalization(void *baton, const void *key, apr_ssize_t klen,
+                              void *val, apr_pool_t *pool)
+{
+  static const char unique[] = "unique";
+  static const char collision[] = "collision";
+
+  struct check_mergeinfo_normalization_baton *const check_baton = baton;
+
+  const char *const path = key;
+  const char *normpath;
+  const char *found;
+
+  SVN_ERR(svn_utf__normalize(&normpath, path, klen, &check_baton->buffer));
+
+  found = svn_hash_gets(check_baton->normalized, normpath);
+  if (!found)
+    {
+      if (0 != strcmp(path, normpath))
+        {
+          /* Report denormlized mergeinfo path */
+          svn_repos_notify_t *const notify =
+            svn_repos_notify_create(svn_repos_notify_warning, pool);
+          notify->warning = svn_repos_notify_warning_denormalized_mergeinfo;
+          notify->warning_str = apr_psprintf(
+              pool, _("Denormalized path '%s' in %s property of '%s'"),
+              path, SVN_PROP_MERGEINFO, check_baton->path);
+          check_baton->notify_func(check_baton->notify_baton, notify, pool);
+        }
+      svn_hash_sets(check_baton->normalized,
+                    apr_pstrdup(pool, normpath), unique);
+    }
+  else if (found == collision)
+    /* Skip already reported collision */;
+  else
+    {
+      /* Report path collision in mergeinfo */
+      svn_repos_notify_t *const notify =
+        svn_repos_notify_create(svn_repos_notify_warning, pool);
+      notify->warning = svn_repos_notify_warning_mergeinfo_collision;
+      notify->warning_str = apr_psprintf(
+          pool, _("Duplicate representation of path '%s'"
+                  " in %s property of '%s'"),
+          normpath, SVN_PROP_MERGEINFO, check_baton->path);
+      check_baton->notify_func(check_baton->notify_baton, notify, pool);
+      svn_hash_sets(check_baton->normalized,
+                    apr_pstrdup(pool, normpath), collision);
+    }
+  return SVN_NO_ERROR;
+}
+
 
 /* This helper is the main "meat" of the editor -- it does all the
    work of writing a node record.
@@ -303,6 +776,11 @@ dump_node(struct edit_baton *eb,
 
   if (action == svn_node_action_change)
     {
+      if (eb->path_tracker)
+        SVN_ERR_W(node_must_exist(eb, path, eb->current_rev, kind, pool),
+                  apr_psprintf(pool, _("Change invalid path '%s' in r%ld"),
+                               path, eb->current_rev));
+
       SVN_ERR(svn_stream_puts(eb->stream,
                               SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n"));
 
@@ -321,8 +799,18 @@ dump_node(struct edit_baton *eb,
     }
   else if (action == svn_node_action_replace)
     {
+      if (eb->path_tracker)
+        SVN_ERR_W(node_must_exist(eb, path, eb->current_rev,
+                                  svn_node_unknown, pool),
+                  apr_psprintf(pool,
+                               _("Replacing non-existent path '%s' in r%ld"),
+                               path, eb->current_rev));
+
       if (! is_copy)
         {
+          if (eb->path_tracker)
+            tracker_path_replace(eb->path_tracker, path);
+
           /* a simple delete+add, implied by a single 'replace' action. */
           SVN_ERR(svn_stream_puts(eb->stream,
                                   SVN_REPOS_DUMPFILE_NODE_ACTION
@@ -335,6 +823,20 @@ dump_node(struct edit_baton *eb,
         }
       else
         {
+          if (eb->path_tracker)
+            {
+              SVN_ERR_W(node_must_exist(eb, compare_path, compare_rev,
+                                        kind, pool),
+                        apr_psprintf(pool,
+                                     _("Replacing path '%s' in r%ld "
+                                       "with invalid path"),
+                                     path, eb->current_rev));
+
+              /* we will call dump_node again with an addition further
+                 down the road */
+              tracker_path_delete(eb->path_tracker, path);
+            }
+
           /* more complex:  delete original, then add-with-history.  */
 
           /* the path & kind headers have already been printed;  just
@@ -355,6 +857,14 @@ dump_node(struct edit_baton *eb,
     }
   else if (action == svn_node_action_delete)
     {
+      if (eb->path_tracker)
+        {
+          SVN_ERR_W(node_must_exist(eb, path, eb->current_rev, kind, pool),
+                    apr_psprintf(pool, _("Deleting invalid path '%s' in r%ld"),
+                                 path, eb->current_rev));
+          tracker_path_delete(eb->path_tracker, path);
+        }
+
       SVN_ERR(svn_stream_puts(eb->stream,
                               SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n"));
 
@@ -365,11 +875,20 @@ dump_node(struct edit_baton *eb,
     }
   else if (action == svn_node_action_add)
     {
+      if (eb->path_tracker)
+        SVN_ERR_W(node_must_not_exist(eb, path, eb->current_rev, pool),
+                  apr_psprintf(pool,
+                               _("Adding already existing path '%s' in r%ld"),
+                               path, eb->current_rev));
+
       SVN_ERR(svn_stream_puts(eb->stream,
                               SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
 
       if (! is_copy)
         {
+          if (eb->path_tracker)
+            tracker_path_add(eb->path_tracker, path);
+
           /* Dump all contents for a simple 'add'. */
           if (kind == svn_node_file)
             must_dump_text = TRUE;
@@ -377,6 +896,18 @@ dump_node(struct edit_baton *eb,
         }
       else
         {
+          if (eb->path_tracker)
+            {
+              SVN_ERR_W(node_must_exist(eb, compare_path, compare_rev,
+                                        kind, pool),
+                        apr_psprintf(pool,
+                                     _("Copying from invalid path to "
+                                       "'%s' in r%ld"),
+                                     path, eb->current_rev));
+              tracker_path_copy(eb->path_tracker, path, compare_path,
+                                compare_rev);
+            }
+
           if (!eb->verify && cmp_rev < eb->oldest_dumped_rev
               && eb->notify_func)
             {
@@ -504,6 +1035,33 @@ dump_node(struct edit_baton *eb,
             }
         }
 
+      /* If we're checking UCS normalization, also parse any changed
+         mergeinfo and warn about denormalized paths and name
+         collisions there. */
+      if (eb->verify && eb->check_ucs_norm && eb->notify_func)
+        {
+          /* N.B.: This hash lookup happens only once; the conditions
+             for verifying historic mergeinfo references and checking
+             UCS normalization are mutually exclusive. */
+          svn_string_t *mergeinfo_str = svn_hash_gets(prophash,
+                                                      SVN_PROP_MERGEINFO);
+          if (mergeinfo_str)
+            {
+              svn_mergeinfo_t mergeinfo;
+              struct check_mergeinfo_normalization_baton check_baton;
+              check_baton.path = path;
+              check_baton.normalized = apr_hash_make(pool);
+              svn_membuf__create(&check_baton.buffer, 0, pool);
+              check_baton.notify_func = eb->notify_func;
+              check_baton.notify_baton = eb->notify_baton;
+              SVN_ERR(svn_mergeinfo_parse(&mergeinfo,
+                                          mergeinfo_str->data, pool));
+              SVN_ERR(svn_iter_apr_hash(NULL, mergeinfo,
+                                        check_mergeinfo_normalization,
+                                        &check_baton, pool));
+            }
+        }
+
       if (eb->use_deltas && compare_root)
         {
           /* Fetch the old property hash to diff against and output a header
@@ -694,6 +1252,16 @@ add_directory(const char *path,
     /* Delete the path, it's now been dumped. */
     svn_hash_sets(pb->deleted_entries, path, NULL);
 
+  /* Check for UCS normalization and name clashes, but only if this is
+     actually a new name in the parent, not a replacement. */
+  if (!val && eb->verify && eb->check_ucs_norm && eb->notify_func)
+    {
+      pb->check_name_collision = TRUE;
+      SVN_ERR(check_ucs_normalization(
+                  path, svn_node_dir,
+                  eb->notify_func, eb->notify_baton, pool));
+    }
+
   new_db->written_out = TRUE;
 
   *child_baton = new_db;
@@ -793,6 +1361,16 @@ add_file(const char *path,
                     is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
                     pool));
 
+  /* Check for UCS normalization and name clashes, but only if this is
+     actually a new name in the parent, not a replacement. */
+  if (!val && eb->verify && eb->check_ucs_norm && eb->notify_func)
+    {
+      pb->check_name_collision = TRUE;
+      SVN_ERR(check_ucs_normalization(
+                  path, svn_node_file,
+                  eb->notify_func, eb->notify_baton, pool));
+    }
+
   if (val)
     /* delete the path, it's now been dumped. */
     svn_hash_sets(pb->deleted_entries, path, NULL);
@@ -960,6 +1538,8 @@ get_dump_editor(const svn_delta_editor_t
                 svn_revnum_t oldest_dumped_rev,
                 svn_boolean_t use_deltas,
                 svn_boolean_t verify,
+                svn_boolean_t check_ucs_norm,
+                svn_cache__t *verified_dirents_cache,
                 apr_pool_t *pool)
 {
   /* Allocate an edit baton to be stored in every directory baton.
@@ -982,8 +1562,18 @@ get_dump_editor(const svn_delta_editor_t
   eb->current_rev = to_rev;
   eb->use_deltas = use_deltas;
   eb->verify = verify;
+  eb->check_ucs_norm = check_ucs_norm;
   eb->found_old_reference = found_old_reference;
   eb->found_old_mergeinfo = found_old_mergeinfo;
+  eb->verified_dirents_cache = verified_dirents_cache;
+
+  /* In non-verification mode, we will allow anything to be dumped because
+     it might be an incremental dump with possible manual intervention.
+     Also, this might be the last resort when it comes to data recovery.
+
+     Else, make sure that all paths exists at their respective revisions.
+  */
+  eb->path_tracker = verify ? tracker_create(to_rev, pool) : NULL;
 
   /* Set up the editor. */
   dump_editor->open_root = open_root;
@@ -1180,7 +1770,8 @@ svn_repos_dump_fs3(svn_repos_t *repos,
                               "", stream, &found_old_reference,
                               &found_old_mergeinfo, NULL,
                               notify_func, notify_baton,
-                              start_rev, use_deltas_for_rev, FALSE, subpool));
+                              start_rev, use_deltas_for_rev, FALSE, FALSE,
+                              NULL, subpool));
 
       /* Drive the editor in one way or another. */
       SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, subpool));
@@ -1292,9 +1883,42 @@ verify_directory_entry(void *baton, cons
 {
   struct dir_baton *db = baton;
   svn_fs_dirent_t *dirent = (svn_fs_dirent_t *)val;
-  char *path = svn_relpath_join(db->path, (const char *)key, pool);
+  char *path;
   apr_hash_t *dirents;
   svn_filesize_t len;
+  svn_string_t *unparsed_id;
+
+  /* most directory entries will be unchanged from previous revs.
+     We should find those in the cache and they must match the
+     type defined in the DIRENT. */
+  if (db->edit_baton->verified_dirents_cache)
+    {
+      svn_node_kind_t *kind;
+      svn_boolean_t found;
+      unparsed_id = svn_fs_unparse_id(dirent->id, pool);
+
+      SVN_ERR(svn_cache__get((void **)&kind, &found,
+                             db->edit_baton->verified_dirents_cache,
+                             unparsed_id->data, pool));
+
+      if (found)
+        {
+          if (*kind == dirent->kind)
+            return SVN_NO_ERROR;
+          else
+            {
+              path = svn_relpath_join(db->path, (const char *)key, pool);
+
+              return
+                  svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+                                    _("Unexpected node kind %d for '%s'. "
+                                      "Expected kind was %d."),
+                                    dirent->kind, path, *kind);
+            }
+        }
+    }
+
+  path = svn_relpath_join(db->path, (const char *)key, pool);
 
   /* since we can't access the directory entries directly by their ID,
      we need to navigate from the FS_ROOT to them (relatively expensive
@@ -1316,12 +1940,67 @@ verify_directory_entry(void *baton, cons
                              dirent->kind, path);
   }
 
+  /* remember ID, kind pair */
+  if (db->edit_baton->verified_dirents_cache)
+    SVN_ERR(svn_cache__set(db->edit_baton->verified_dirents_cache,
+                           unparsed_id->data, &dirent->kind, pool));
+
   return SVN_NO_ERROR;
 }
 
+/* Baton used by the check_name_collision hash iterator. */
+struct check_name_collision_baton
+{
+  struct dir_baton *dir_baton;
+  apr_hash_t *normalized;
+  svn_membuf_t buffer;
+};
+
+/* Scan the directory and report all entry names that differ only in
+   Unicode character representaiton. */
 static svn_error_t *
-verify_close_directory(void *dir_baton,
-                apr_pool_t *pool)
+check_name_collision(void *baton, const void *key, apr_ssize_t klen,
+                     void *val, apr_pool_t *pool)
+{
+  static const char unique[] = "unique";
+  static const char collision[] = "collision";
+
+  struct check_name_collision_baton *const check_baton = baton;
+  const char *name;
+  const char *found;
+
+  SVN_ERR(svn_utf__normalize(&name, key, klen, &check_baton->buffer));
+
+  found = svn_hash_gets(check_baton->normalized, name);
+  if (!found)
+    svn_hash_sets(check_baton->normalized,
+                  apr_pstrdup(pool, name), unique);
+  else if (found == collision)
+    /* Skip already reported collision */;
+  else
+    {
+      struct dir_baton *const db = check_baton->dir_baton;
+      struct edit_baton *const eb = db->edit_baton;
+      svn_repos_notify_t *notify;
+      const char* normpath;
+
+      svn_hash_sets(check_baton->normalized,
+                    apr_pstrdup(pool, name), collision);
+      SVN_ERR(svn_utf__normalize(
+                  &normpath, svn_relpath_join(db->path, name, pool),
+                  SVN_UTF__UNKNOWN_LENGTH, &check_baton->buffer));
+      notify = svn_repos_notify_create(svn_repos_notify_warning, pool);
+      notify->warning = svn_repos_notify_warning_name_collision;
+      notify->warning_str = apr_psprintf(
+          pool, _("Duplicate representation of path '%s'"), normpath);
+      eb->notify_func(eb->notify_baton, notify, pool);
+    }
+  return SVN_NO_ERROR;
+}
+
+
+static svn_error_t *
+verify_close_directory(void *dir_baton, apr_pool_t *pool)
 {
   struct dir_baton *db = dir_baton;
   apr_hash_t *dirents;
@@ -1329,6 +2008,17 @@ verify_close_directory(void *dir_baton,
                              db->path, pool));
   SVN_ERR(svn_iter_apr_hash(NULL, dirents, verify_directory_entry,
                             dir_baton, pool));
+
+  if (db->check_name_collision)
+    {
+      struct check_name_collision_baton check_baton;
+      check_baton.dir_baton = db;
+      check_baton.normalized = apr_hash_make(pool);
+      svn_membuf__create(&check_baton.buffer, 0, pool);
+      SVN_ERR(svn_iter_apr_hash(NULL, dirents, check_name_collision,
+                                &check_baton, pool));
+    }
+
   return close_directory(dir_baton, pool);
 }
 
@@ -1399,8 +2089,10 @@ verify_one_revision(svn_fs_t *fs,
                     svn_repos_notify_func_t notify_func,
                     void *notify_baton,
                     svn_revnum_t start_rev,
+                    svn_boolean_t check_ucs_norm,
                     svn_cancel_func_t cancel_func,
                     void *cancel_baton,
+                    svn_cache__t *verified_dirents_cache,
                     apr_pool_t *scratch_pool)
 {
   const svn_delta_editor_t *dump_editor;
@@ -1419,6 +2111,8 @@ verify_one_revision(svn_fs_t *fs,
                           notify_func, notify_baton,
                           start_rev,
                           FALSE, TRUE, /* use_deltas, verify */
+                          check_ucs_norm,
+                          verified_dirents_cache,
                           scratch_pool));
   SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
                                             dump_editor, dump_edit_baton,
@@ -1426,6 +2120,7 @@ verify_one_revision(svn_fs_t *fs,
                                             &cancel_edit_baton,
                                             scratch_pool));
   SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, scratch_pool));
+  SVN_ERR(svn_fs_verify_root(to_root, scratch_pool));
   SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE,
                             cancel_editor, cancel_edit_baton,
                             NULL, NULL, scratch_pool));
@@ -1497,11 +2192,36 @@ populate_summary(apr_array_header_t **er
   APR_ARRAY_PUSH(*error_summary, struct error_list *) = el;
 }
 
+/* cache entry (de-)serialization support for svn_node_kind_t. */
+static svn_error_t *
+serialize_node_kind(void **data,
+                    apr_size_t *data_len,
+                    void *in,
+                    apr_pool_t *pool)
+{
+  *data_len = sizeof(svn_node_kind_t);
+  *data = in;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+deserialize_node_kind(void **out,
+                      void *data,
+                      apr_size_t data_len,
+                      apr_pool_t *pool)
+{
+  *out = data;
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_repos_verify_fs3(svn_repos_t *repos,
                      svn_revnum_t start_rev,
                      svn_revnum_t end_rev,
                      svn_boolean_t keep_going,
+                     svn_boolean_t check_ucs_norm,
                      svn_repos_notify_func_t notify_func,
                      void *notify_baton,
                      svn_cancel_func_t cancel_func,
@@ -1519,6 +2239,7 @@ svn_repos_verify_fs3(svn_repos_t *repos,
   apr_array_header_t *error_summary;
   int i;
   svn_boolean_t found_corruption = FALSE;
+  svn_cache__t *verified_dirents_cache = NULL;
 
   /* Determine the current youngest revision of the filesystem. */
   SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
@@ -1580,6 +2301,18 @@ svn_repos_verify_fs3(svn_repos_t *repos,
       svn_error_clear(err);
     }
 
+  if (svn_cache__get_global_membuffer_cache())
+    SVN_ERR(svn_cache__create_membuffer_cache
+                                 (&verified_dirents_cache,
+                                  svn_cache__get_global_membuffer_cache(),
+                                  serialize_node_kind,
+                                  deserialize_node_kind,
+                                  APR_HASH_KEY_STRING,
+                                  svn_uuid_generate(pool),
+                                  SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
+                                  FALSE,
+                                  pool));
+
   error_summary = apr_array_make(pool, 0, sizeof(struct error_list *));
           
   for (rev = start_rev; rev <= end_rev; rev++)
@@ -1587,8 +2320,10 @@ svn_repos_verify_fs3(svn_repos_t *repos,
       svn_pool_clear(iterpool);
 
       /* Wrapper function to catch the possible errors. */
-      err = verify_one_revision(fs, rev, notify_func, notify_baton, start_rev,
-                                cancel_func, cancel_baton, iterpool);
+      err = verify_one_revision(fs, rev, notify_func, notify_baton,
+                                start_rev, check_ucs_norm,
+                                cancel_func, cancel_baton,
+                                verified_dirents_cache, iterpool);
 
       if (err)
         {

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/fs-wrap.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/fs-wrap.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/fs-wrap.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/fs-wrap.c Wed Nov 27 11:52:35 2013
@@ -24,6 +24,7 @@
 #include <string.h>
 #include <ctype.h>
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_pools.h"
 #include "svn_error.h"
@@ -34,7 +35,7 @@
 #include "svn_time.h"
 #include "svn_sorts.h"
 #include "repos.h"
-#include "svn_private_config.h"
+
 #include "private/svn_repos_private.h"
 #include "private/svn_utf_private.h"
 #include "private/svn_fspath.h"

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/hooks.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/hooks.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/hooks.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/hooks.c Wed Nov 27 11:52:35 2013
@@ -27,6 +27,7 @@
 #include <apr_pools.h>
 #include <apr_file_io.h>
 
+#include "svn_private_config.h"
 #include "svn_config.h"
 #include "svn_hash.h"
 #include "svn_error.h"
@@ -36,7 +37,7 @@
 #include "svn_repos.h"
 #include "svn_utf.h"
 #include "repos.h"
-#include "svn_private_config.h"
+
 #include "private/svn_fs_private.h"
 #include "private/svn_repos_private.h"
 #include "private/svn_string_private.h"
@@ -334,7 +335,7 @@ check_hook_cmd(const char *hook, svn_boo
   for (extn = check_extns; *extn; ++extn)
     {
       const char *const hook_path =
-        (**extn ? apr_pstrcat(pool, hook, *extn, (char *)NULL) : hook);
+        (**extn ? apr_pstrcat(pool, hook, *extn, SVN_VA_NULL) : hook);
 
       svn_node_kind_t kind;
       if (!(err = svn_io_check_resolved_path(hook_path, &kind, pool))
@@ -416,17 +417,25 @@ svn_repos__parse_hooks_env(apr_hash_t **
                            apr_pool_t *result_pool,
                            apr_pool_t *scratch_pool)
 {
-  svn_config_t *cfg;
   struct parse_hooks_env_section_baton b;
-
   if (local_abspath)
     {
-      SVN_ERR(svn_config_read3(&cfg, local_abspath, FALSE,
-                               TRUE, TRUE, scratch_pool));
-      b.cfg = cfg;
+      svn_node_kind_t kind;
+      SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
+
       b.hooks_env = apr_hash_make(result_pool);
-      (void)svn_config_enumerate_sections2(cfg, parse_hooks_env_section, &b,
-                                           scratch_pool);
+
+      if (kind != svn_node_none)
+        {
+          svn_config_t *cfg;
+          SVN_ERR(svn_config_read3(&cfg, local_abspath, FALSE,
+                                  TRUE, TRUE, scratch_pool));
+          b.cfg = cfg;
+
+          (void)svn_config_enumerate_sections2(cfg, parse_hooks_env_section,
+                                               &b, scratch_pool);
+        }
+
       *hooks_env_p = b.hooks_env;
     }
   else

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/load-fs-vtable.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/load-fs-vtable.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/load-fs-vtable.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/load-fs-vtable.c Wed Nov 27 11:52:35 2013
@@ -99,6 +99,9 @@ struct revision_baton
   apr_int32_t rev_offset;
   svn_boolean_t skipped;
 
+  /* Array of svn_prop_t with revision properties. */
+  apr_array_header_t *revprops;
+
   struct parse_baton *pb;
   apr_pool_t *pool;
 };
@@ -448,6 +451,7 @@ make_revision_baton(apr_hash_t *headers,
   rb->pb = pb;
   rb->pool = pool;
   rb->rev = SVN_INVALID_REVNUM;
+  rb->revprops = apr_array_make(rb->pool, 8, sizeof(svn_prop_t));
 
   if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_REVISION_NUMBER)))
     {
@@ -698,10 +702,12 @@ set_revision_property(void *baton,
 
   if (rb->rev > 0)
     {
-      if (rb->pb->validate_props)
-        SVN_ERR(svn_repos_fs_change_txn_prop(rb->txn, name, value, rb->pool));
-      else
-        SVN_ERR(svn_fs_change_txn_prop(rb->txn, name, value, rb->pool));
+      svn_prop_t *prop = &APR_ARRAY_PUSH(rb->revprops, svn_prop_t);
+
+      /* Collect property changes to apply them in one FS call in
+         close_revision. */
+      prop->name = apr_pstrdup(rb->pool, name);
+      prop->value = svn_string_dup(value, rb->pool);
 
       /* Remember any datestamp that passes through!  (See comment in
          close_revision() below.) */
@@ -920,6 +926,21 @@ close_revision(void *baton)
   if (rb->skipped || (rb->rev <= 0))
     return SVN_NO_ERROR;
 
+  if (!rb->datestamp)
+    {
+      /* Remove 'svn:date' revision property that was set by FS layer when TXN
+         created if source dump doesn't have 'svn:date' property. */
+      svn_prop_t *prop = &APR_ARRAY_PUSH(rb->revprops, svn_prop_t);
+      prop->name = SVN_PROP_REVISION_DATE;
+      prop->value = NULL;
+    }
+
+  /* Apply revision property changes. */
+  if (rb->pb->validate_props)
+    SVN_ERR(svn_repos_fs_change_txn_props(rb->txn, rb->revprops, rb->pool));
+  else
+    SVN_ERR(svn_fs_change_txn_props(rb->txn, rb->revprops, rb->pool));
+
   /* Get the txn name and hooks environment if they will be needed. */
   if (pb->use_pre_commit_hook || pb->use_post_commit_hook)
     {
@@ -947,7 +968,8 @@ close_revision(void *baton)
     }
 
   /* Commit. */
-  err = svn_fs_commit_txn(&conflict_msg, &committed_rev, rb->txn, rb->pool);
+  err = svn_fs_commit_txn2(&conflict_msg, &committed_rev, rb->txn, FALSE,
+                           rb->pool);
   if (SVN_IS_VALID_REVNUM(committed_rev))
     {
       if (err)
@@ -1005,15 +1027,6 @@ close_revision(void *baton)
   /* Deltify the predecessors of paths changed in this revision. */
   SVN_ERR(svn_fs_deltify_revision(pb->fs, committed_rev, rb->pool));
 
-  /* Grrr, svn_fs_commit_txn rewrites the datestamp property to the
-     current clock-time.  We don't want that, we want to preserve
-     history exactly.  Good thing revision props aren't versioned!
-     Note that if rb->datestamp is NULL, that's fine -- if the dump
-     data doesn't carry a datestamp, we want to preserve that fact in
-     the load. */
-  SVN_ERR(change_rev_prop(pb->repos, committed_rev, SVN_PROP_REVISION_DATE,
-                          rb->datestamp, pb->validate_props, rb->pool));
-
   if (pb->notify_func)
     {
       pb->notify->action = svn_repos_notify_load_txn_committed;

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/log.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/log.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/log.c Wed Nov 27 11:52:35 2013
@@ -25,8 +25,8 @@
 #define APR_WANT_STRFUNC
 #include <apr_want.h>
 
-#include "svn_compat.h"
 #include "svn_private_config.h"
+#include "svn_compat.h"
 #include "svn_hash.h"
 #include "svn_pools.h"
 #include "svn_error.h"
@@ -42,7 +42,6 @@
 #include "private/svn_mergeinfo_private.h"
 #include "private/svn_subr_private.h"
 
-
 
 svn_error_t *
 svn_repos_check_revision_access(svn_repos_revision_access_level_t *access_level,
@@ -105,6 +104,8 @@ svn_repos_check_revision_access(svn_repo
         {
         case svn_fs_path_change_add:
         case svn_fs_path_change_replace:
+        case svn_fs_path_change_move:
+        case svn_fs_path_change_movereplace:
           {
             const char *copyfrom_path;
             svn_revnum_t copyfrom_rev;
@@ -152,6 +153,147 @@ svn_repos_check_revision_access(svn_repo
   return SVN_NO_ERROR;
 }
 
+/* Return TRUE, if CHANGE deleted the node previously found at its target
+   path. */
+static svn_boolean_t
+is_deletion(svn_log_changed_path2_t *change)
+{
+  /* We need a 'N' action here ... */
+  return change->action == 'E'
+      || change->action == 'R'
+      || change->action == 'D';
+}
+
+/* Change all moves in CHANGES to ADD.  Use POOL for temporary allocations.
+ */
+static void
+turn_moves_into_copies(apr_hash_t *changes,
+                       apr_pool_t *pool)
+{
+  apr_hash_index_t *hi;
+  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
+    {
+      const char *key;
+      apr_ssize_t klen;
+      svn_log_changed_path2_t *change;
+      apr_hash_this(hi, (const void **)&key, &klen, (void**)&change);
+
+      switch (change->action)
+        {
+          case 'V':
+            change->action = 'A';
+            break;
+
+          case 'E':
+            change->action = 'R';
+            break;
+
+          default:
+            break;
+        }
+    }
+}
+
+/* Replace ADDs with MOVes, if they are unique, have a matching deletion
+ * and if the copy-from revision is REVISION-1.  Use POOL for temporary
+ * allocations.
+ */
+static void
+turn_unique_copies_into_moves(apr_hash_t *changes,
+                              svn_revnum_t revision,
+                              apr_pool_t *pool)
+{
+  apr_hash_index_t *hi;
+  apr_hash_t *unique_copy_sources;
+  const char **sources;
+  int i;
+
+  /* find all copy-from paths (ADD and MOV alike) */
+
+  svn_boolean_t any_deletion = FALSE;
+  apr_array_header_t *copy_sources
+    = apr_array_make(pool, apr_hash_count(changes), sizeof(const char*));
+
+  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
+    {
+      svn_log_changed_path2_t *change;
+      apr_hash_this(hi, NULL, NULL, (void**)&change);
+
+      if (change->copyfrom_path && change->copyfrom_rev == revision-1)
+        APR_ARRAY_PUSH(copy_sources, const char *)
+          = change->copyfrom_path;
+
+      any_deletion |= is_deletion(change);
+    }
+
+  /* no suitable copy-from or no deletion -> no moves */
+
+  if (!copy_sources->nelts || !any_deletion)
+    return;
+
+  /* identify copy-from paths that have been mentioned exactly once */
+
+  sources = (const char **)copy_sources->elts;
+  qsort(sources, copy_sources->nelts, copy_sources->elt_size,
+        (int (*)(const void *, const void *))svn_sort_compare_paths);
+
+  unique_copy_sources = apr_hash_make(pool);
+  for (i = 0; i < copy_sources->nelts; ++i)
+    if (   (i == 0 || strcmp(sources[i-1], sources[i]))
+        && (i == copy_sources->nelts-1 || strcmp(sources[i+1], sources[i])))
+      {
+        apr_hash_set(unique_copy_sources, sources[i],
+                     APR_HASH_KEY_STRING, sources[i]);
+      }
+
+  /* no unique copy-from paths -> no moves */
+
+  if (!apr_hash_count(unique_copy_sources))
+    return;
+
+  /* Replace all additions, replacements with a unique copy-from path,
+     the correct copy-from rev and a matching deletion in this revision,
+     with moves and move-replacements, respectively. */
+
+  for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
+    {
+      const char *key;
+      apr_ssize_t klen;
+      svn_log_changed_path2_t *change, *copy_from_change;
+
+      apr_hash_this(hi, (const void **)&key, &klen, (void**)&change);
+      if (   change->copyfrom_rev != revision-1
+          || !change->copyfrom_path
+          || !apr_hash_get(unique_copy_sources, change->copyfrom_path,
+                           APR_HASH_KEY_STRING))
+        continue;
+
+      copy_from_change = apr_hash_get(changes, change->copyfrom_path,
+                                      APR_HASH_KEY_STRING);
+      if (!copy_from_change || !is_deletion(copy_from_change))
+        continue;
+
+      /* There is a deletion of the ADD's copy-from path in *REVISION*.
+         This can either be the same as in REVISION-1 (o.k.) or must have
+         been replaced by some other node.  However, that would imply that
+         it still got deleted as part of the replacement, i.e. both cases
+         are o.k. */
+
+      switch (change->action)
+        {
+          case 'A':
+            change->action = 'V';
+            break;
+
+          case 'R':
+            change->action = 'E';
+            break;
+
+          default:
+            break;
+        }
+    }
+}
 
 /* Store as keys in CHANGED the paths of all node in ROOT that show a
  * significant change.  "Significant" means that the text or
@@ -163,7 +305,8 @@ svn_repos_check_revision_access(svn_repo
  *
  * To prevent changes from being processed over and over again, the
  * changed paths for ROOT may be passed in PREFETCHED_CHANGES.  If the
- * latter is NULL, we will request the list inside this function.
+ * latter is NULL, we will request the list inside this function using
+ * the specified MOVE_BEHAVIOR.
  *
  * If optional AUTHZ_READ_FUNC is non-NULL, then use it (with
  * AUTHZ_READ_BATON and FS) to check whether each changed-path (and
@@ -184,6 +327,7 @@ detect_changed(apr_hash_t **changed,
                svn_fs_root_t *root,
                svn_fs_t *fs,
                apr_hash_t *prefetched_changes,
+               svn_move_behavior_t move_behavior,
                svn_repos_authz_func_t authz_read_func,
                void *authz_read_baton,
                apr_pool_t *pool)
@@ -255,6 +399,14 @@ detect_changed(apr_hash_t **changed,
           action = 'D';
           break;
 
+        case svn_fs_path_change_move:
+          action = 'V';
+          break;
+
+        case svn_fs_path_change_movereplace:
+          action = 'E';
+          break;
+
         case svn_fs_path_change_modify:
         default:
           action = 'M';
@@ -306,7 +458,8 @@ detect_changed(apr_hash_t **changed,
         }
 
 
-      if ((action == 'A') || (action == 'R'))
+      if (   (action == 'A') || (action == 'R')
+          || (action == 'V') || (action == 'E'))
         {
           const char *copyfrom_path = change->copyfrom_path;
           svn_revnum_t copyfrom_rev = change->copyfrom_rev;
@@ -354,6 +507,23 @@ detect_changed(apr_hash_t **changed,
     return svn_error_create(SVN_ERR_AUTHZ_UNREADABLE,
                             NULL, NULL);
 
+  /* at least some paths are readable.  Post-process them. */
+  switch(move_behavior)
+    {
+      case svn_move_behavior_no_moves:
+        turn_moves_into_copies(*changed, pool);
+        break;
+
+      case svn_move_behavior_auto_moves:
+        turn_unique_copies_into_moves(*changed,
+                                      svn_fs_revision_root_revision(root),
+                                      pool);
+        break;
+
+      default:
+        break;
+    }
+
   if (found_unreadable)
     /* At least one changed-path was unreadable. */
     return svn_error_create(SVN_ERR_AUTHZ_PARTIALLY_READABLE,
@@ -638,6 +808,8 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
            was.  If not, there's no previous location to examine.  */
         case svn_fs_path_change_add:
         case svn_fs_path_change_replace:
+        case svn_fs_path_change_move:
+        case svn_fs_path_change_movereplace:
           {
             const char *copyfrom_path;
             svn_revnum_t copyfrom_rev;
@@ -782,8 +954,7 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
    *ADDED_MERGEINFO and deleted mergeinfo in *DELETED_MERGEINFO.
    If *PREFETCHED_CAHNGES already contains the changed paths for
    REV, use that.  Otherwise, request that data and return it in
-   *PREFETCHED_CHANGES.
-   Use POOL for all allocations. */
+   *PREFETCHED_CHANGES.  Use POOL for all allocations. */
 static svn_error_t *
 get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo,
                                svn_mergeinfo_t *deleted_mergeinfo,
@@ -820,7 +991,8 @@ get_combined_mergeinfo_changes(svn_merge
   err = fs_mergeinfo_changed(&deleted_mergeinfo_catalog,
                              &added_mergeinfo_catalog,
                              prefetched_changes,
-                             fs, rev, scratch_pool, scratch_pool);
+                             fs, rev,
+                             scratch_pool, scratch_pool);
   if (err)
     {
       if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
@@ -1025,6 +1197,7 @@ fill_log_entry(svn_log_entry_t *log_entr
                svn_fs_t *fs,
                apr_hash_t *prefetched_changes,
                svn_boolean_t discover_changed_paths,
+               svn_move_behavior_t move_behavior,
                const apr_array_header_t *revprops,
                svn_repos_authz_func_t authz_read_func,
                void *authz_read_baton,
@@ -1043,7 +1216,7 @@ fill_log_entry(svn_log_entry_t *log_entr
 
       SVN_ERR(svn_fs_revision_root(&newroot, fs, rev, pool));
       patherr = detect_changed(&changed_paths,
-                               newroot, fs, prefetched_changes,
+                               newroot, fs, prefetched_changes, move_behavior,
                                authz_read_func, authz_read_baton,
                                pool);
 
@@ -1160,9 +1333,9 @@ fill_log_entry(svn_log_entry_t *log_entr
    only the revision properties named by the (const char *) array elements
    (i.e. retrieve none if the array is empty).
 
-   LOG_TARGET_HISTORY_AS_MERGEINFO, HANDLING_MERGED_REVISION, and
-   NESTED_MERGES are as per the arguments of the same name to DO_LOGS.  If
-   HANDLING_MERGED_REVISION is true and *all* changed paths within REV are
+   LOG_TARGET_HISTORY_AS_MERGEINFO, HANDLING_MERGED_REVISION, MOVE_BEHAVIOR,
+   and NESTED_MERGES are as per the arguments of the same name to DO_LOGS.
+   If HANDLING_MERGED_REVISION is true and *all* changed paths within REV are
    already represented in LOG_TARGET_HISTORY_AS_MERGEINFO, then don't send
    the log message for REV.  If SUBTRACTIVE_MERGE is true, then REV was
    reverse merged.
@@ -1180,6 +1353,7 @@ send_log(svn_revnum_t rev,
          svn_boolean_t discover_changed_paths,
          svn_boolean_t subtractive_merge,
          svn_boolean_t handling_merged_revision,
+         svn_move_behavior_t move_behavior,
          const apr_array_header_t *revprops,
          svn_boolean_t has_children,
          svn_log_entry_receiver_t receiver,
@@ -1195,8 +1369,8 @@ send_log(svn_revnum_t rev,
   log_entry = svn_log_entry_create(pool);
   SVN_ERR(fill_log_entry(log_entry, rev, fs, prefetched_changes,
                          discover_changed_paths || handling_merged_revision,
-                         revprops, authz_read_func, authz_read_baton,
-                         pool));
+                         move_behavior, revprops, 
+                         authz_read_func, authz_read_baton, pool));
   log_entry->has_children = has_children;
   log_entry->subtractive_merge = subtractive_merge;
 
@@ -1663,6 +1837,7 @@ do_logs(svn_fs_t *fs,
         svn_boolean_t handling_merged_revisions,
         svn_boolean_t subtractive_merge,
         svn_boolean_t ignore_missing_locations,
+        svn_move_behavior_t move_behavior,
         const apr_array_header_t *revprops,
         svn_boolean_t descending_order,
         svn_log_entry_receiver_t receiver,
@@ -1712,6 +1887,7 @@ handle_merged_revisions(svn_revnum_t rev
                         svn_mergeinfo_t deleted_mergeinfo,
                         svn_boolean_t discover_changed_paths,
                         svn_boolean_t strict_node_history,
+                        svn_move_behavior_t move_behavior,
                         const apr_array_header_t *revprops,
                         svn_log_entry_receiver_t receiver,
                         void *receiver_baton,
@@ -1754,7 +1930,7 @@ handle_merged_revisions(svn_revnum_t rev
                       pl_range->range.start, pl_range->range.end, 0,
                       discover_changed_paths, strict_node_history,
                       TRUE, pl_range->reverse_merge, TRUE, TRUE,
-                      revprops, TRUE, receiver, receiver_baton,
+                      move_behavior, revprops, TRUE, receiver, receiver_baton,
                       authz_read_func, authz_read_baton, iterpool));
     }
   svn_pool_destroy(iterpool);
@@ -1888,6 +2064,9 @@ store_search(svn_mergeinfo_t processed,
    If IGNORE_MISSING_LOCATIONS is set, don't treat requests for bogus
    repository locations as fatal -- just ignore them.
 
+   MOVE_BEHAVIOR is a simple pass-through parameter that tells the FS
+   layer which changes to report as moves instead of additions.
+
    If LOG_TARGET_HISTORY_AS_MERGEINFO is not NULL then it contains mergeinfo
    representing the history of PATHS between HIST_START and HIST_END.
 
@@ -1925,6 +2104,7 @@ do_logs(svn_fs_t *fs,
         svn_boolean_t subtractive_merge,
         svn_boolean_t handling_merged_revisions,
         svn_boolean_t ignore_missing_locations,
+        svn_move_behavior_t move_behavior,
         const apr_array_header_t *revprops,
         svn_boolean_t descending_order,
         svn_log_entry_receiver_t receiver,
@@ -2019,8 +2199,8 @@ do_logs(svn_fs_t *fs,
                                                      &deleted_mergeinfo,
                                                      &changes,
                                                      fs, cur_paths,
-                                                     current, iterpool,
-                                                     iterpool));
+                                                     current,
+                                                     iterpool, iterpool));
               has_children = (apr_hash_count(added_mergeinfo) > 0
                               || apr_hash_count(deleted_mergeinfo) > 0);
             }
@@ -2034,7 +2214,7 @@ do_logs(svn_fs_t *fs,
                                log_target_history_as_mergeinfo, nested_merges,
                                discover_changed_paths,
                                subtractive_merge, handling_merged_revisions,
-                               revprops, has_children,
+                               move_behavior, revprops, has_children,
                                receiver, receiver_baton,
                                authz_read_func, authz_read_baton, iterpool));
 
@@ -2057,6 +2237,7 @@ do_logs(svn_fs_t *fs,
                     added_mergeinfo, deleted_mergeinfo,
                     discover_changed_paths,
                     strict_node_history,
+                    move_behavior,
                     revprops,
                     receiver, receiver_baton,
                     authz_read_func,
@@ -2138,7 +2319,8 @@ do_logs(svn_fs_t *fs,
           SVN_ERR(send_log(current, fs, NULL,
                            log_target_history_as_mergeinfo, nested_merges,
                            discover_changed_paths, subtractive_merge,
-                           handling_merged_revisions, revprops, has_children,
+                           handling_merged_revisions, move_behavior,
+                           revprops, has_children,
                            receiver, receiver_baton, authz_read_func,
                            authz_read_baton, iterpool));
           if (has_children)
@@ -2156,7 +2338,8 @@ do_logs(svn_fs_t *fs,
                                               added_mergeinfo,
                                               deleted_mergeinfo,
                                               discover_changed_paths,
-                                              strict_node_history, revprops,
+                                              strict_node_history,
+                                              move_behavior, revprops,
                                               receiver, receiver_baton,
                                               authz_read_func,
                                               authz_read_baton,
@@ -2258,7 +2441,7 @@ get_paths_history_as_mergeinfo(svn_merge
 }
 
 svn_error_t *
-svn_repos_get_logs4(svn_repos_t *repos,
+svn_repos_get_logs5(svn_repos_t *repos,
                     const apr_array_header_t *paths,
                     svn_revnum_t start,
                     svn_revnum_t end,
@@ -2266,6 +2449,7 @@ svn_repos_get_logs4(svn_repos_t *repos,
                     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_repos_authz_func_t authz_read_func,
                     void *authz_read_baton,
@@ -2374,7 +2558,7 @@ svn_repos_get_logs4(svn_repos_t *repos,
             rev = start + i;
           SVN_ERR(send_log(rev, fs, NULL, NULL, NULL,
                            discover_changed_paths, FALSE,
-                           FALSE, revprops, FALSE, receiver,
+                           FALSE, move_behavior, revprops, FALSE, receiver,
                            receiver_baton, authz_read_func,
                            authz_read_baton, iterpool));
         }
@@ -2402,7 +2586,8 @@ svn_repos_get_logs4(svn_repos_t *repos,
 
   return do_logs(repos->fs, paths, paths_history_mergeinfo, NULL, NULL, start, end,
                  limit, discover_changed_paths, strict_node_history,
-                 include_merged_revisions, FALSE, FALSE, FALSE, revprops,
+                 include_merged_revisions, FALSE, FALSE, FALSE,
+                 move_behavior, revprops,
                  descending_order, receiver, receiver_baton,
                  authz_read_func, authz_read_baton, pool);
 }

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/replay.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/replay.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/replay.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/replay.c Wed Nov 27 11:52:35 2013
@@ -25,6 +25,7 @@
 
 #include <apr_hash.h>
 
+#include "svn_private_config.h"
 #include "svn_types.h"
 #include "svn_delta.h"
 #include "svn_hash.h"
@@ -35,7 +36,7 @@
 #include "svn_props.h"
 #include "svn_pools.h"
 #include "svn_path.h"
-#include "svn_private_config.h"
+
 #include "private/svn_fspath.h"
 #include "private/svn_repos_private.h"
 #include "private/svn_delta_private.h"

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/reporter.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/reporter.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/reporter.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/reporter.c Wed Nov 27 11:52:35 2013
@@ -21,6 +21,7 @@
  * ====================================================================
  */
 
+#include "svn_private_config.h"
 #include "svn_dirent_uri.h"
 #include "svn_hash.h"
 #include "svn_path.h"
@@ -32,7 +33,6 @@
 #include "svn_pools.h"
 #include "svn_props.h"
 #include "repos.h"
-#include "svn_private_config.h"
 
 #include "private/svn_dep_compat.h"
 #include "private/svn_fspath.h"
@@ -499,7 +499,7 @@ get_revision_info(report_baton_t *b,
       info->author = author ? svn_string_dup(author, b->pool) : NULL;
 
       /* Cache it */
-      apr_hash_set(b->revision_infos, &info->rev, sizeof(rev), info);
+      apr_hash_set(b->revision_infos, &info->rev, sizeof(info->rev), info);
     }
 
   *revision_info = info;
@@ -842,7 +842,7 @@ add_file_smartly(report_baton_t *b,
          starting with '/', so make sure o_path always starts with a '/'
          too. */
       if (*o_path != '/')
-        o_path = apr_pstrcat(pool, "/", o_path, (char *)NULL);
+        o_path = apr_pstrcat(pool, "/", o_path, SVN_VA_NULL);
 
       SVN_ERR(svn_fs_closest_copy(&closest_copy_root, &closest_copy_path,
                                   b->t_root, o_path, pool));
@@ -1143,6 +1143,8 @@ delta_dirs(report_baton_t *b, svn_revnum
   apr_hash_t *s_entries = NULL, *t_entries;
   apr_hash_index_t *hi;
   apr_pool_t *subpool;
+  apr_array_header_t *t_ordered_entries = NULL;
+  int i;
 
   /* Compare the property lists.  If we're starting empty, pass a NULL
      source path so that we add all the properties.
@@ -1275,9 +1277,12 @@ delta_dirs(report_baton_t *b, svn_revnum
         }
 
       /* Loop over the dirents in the target. */
-      for (hi = apr_hash_first(pool, t_entries); hi; hi = apr_hash_next(hi))
+      SVN_ERR(svn_fs_dir_optimal_order(&t_ordered_entries, b->t_root,
+                                       t_entries, pool));
+      for (i = 0; i < t_ordered_entries->nelts; ++i)
         {
-          const svn_fs_dirent_t *t_entry = svn__apr_hash_index_val(hi);
+          const svn_fs_dirent_t *t_entry
+             = APR_ARRAY_IDX(t_ordered_entries, i, svn_fs_dirent_t *);
           const svn_fs_dirent_t *s_entry;
           const char *s_fullpath, *t_fullpath, *e_fullpath;
 

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/repos.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/repos.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/repos.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/repos.c Wed Nov 27 11:52:35 2013
@@ -23,6 +23,7 @@
 #include <apr_pools.h>
 #include <apr_file_io.h>
 
+#include "svn_private_config.h"
 #include "svn_pools.h"
 #include "svn_error.h"
 #include "svn_dirent_uri.h"
@@ -38,7 +39,6 @@
 
 #include "private/svn_repos_private.h"
 #include "private/svn_subr_private.h"
-#include "svn_private_config.h" /* for SVN_TEMPLATE_ROOT_DIR */
 
 #include "repos.h"
 
@@ -1796,6 +1796,11 @@ svn_repos_fs(svn_repos_t *repos)
   return repos->fs;
 }
 
+const char *
+svn_repos_fs_type(svn_repos_t *repos, apr_pool_t *pool)
+{
+  return apr_pstrdup(pool, repos->fs_type);
+}
 
 /* For historical reasons, for the Berkeley DB backend, this code uses
  * repository locking, which is motivated by the need to support the

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/repos.h
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/repos.h?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/repos.h (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/repos.h Wed Nov 27 11:52:35 2013
@@ -382,6 +382,11 @@ svn_repos__authz_read(svn_authz_t **auth
                       svn_boolean_t accept_urls,
                       apr_pool_t *pool);
 
+/* Walk the configuration in AUTHZ looking for any errors. */
+svn_error_t *
+svn_repos__authz_validate(svn_authz_t *authz,
+                          apr_pool_t *pool);
+
 
 /*** Utility Functions ***/
 

Modified: subversion/branches/verify-keep-going/subversion/libsvn_repos/rev_hunt.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_repos/rev_hunt.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_repos/rev_hunt.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_repos/rev_hunt.c Wed Nov 27 11:52:35 2013
@@ -23,8 +23,9 @@
 
 
 #include <string.h>
-#include "svn_compat.h"
+
 #include "svn_private_config.h"
+#include "svn_compat.h"
 #include "svn_hash.h"
 #include "svn_pools.h"
 #include "svn_error.h"
@@ -660,7 +661,7 @@ svn_repos_trace_node_locations(svn_fs_t 
   /* Ensure that FS_PATH is absolute, because our path-math below will
      depend on that being the case.  */
   if (*fs_path != '/')
-    fs_path = apr_pstrcat(pool, "/", fs_path, (char *)NULL);
+    fs_path = apr_pstrcat(pool, "/", fs_path, SVN_VA_NULL);
 
   /* Another sanity check. */
   if (authz_read_func)
@@ -877,7 +878,7 @@ svn_repos_node_location_segments(svn_rep
   /* Ensure that PATH is absolute, because our path-math will depend
      on that being the case.  */
   if (*path != '/')
-    path = apr_pstrcat(pool, "/", path, (char *)NULL);
+    path = apr_pstrcat(pool, "/", path, SVN_VA_NULL);
 
   /* Auth check. */
   if (authz_read_func)
@@ -941,7 +942,7 @@ svn_repos_node_location_segments(svn_rep
 
           /* authz_read_func requires path to have a leading slash. */
           const char *abs_path = apr_pstrcat(subpool, "/", segment->path,
-                                             (char *)NULL);
+                                             SVN_VA_NULL);
 
           SVN_ERR(svn_fs_revision_root(&cur_rev_root, fs,
                                        segment->range_end, subpool));

Modified: subversion/branches/verify-keep-going/subversion/libsvn_subr/auth.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_subr/auth.c?rev=1546002&r1=1546001&r2=1546002&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_subr/auth.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_subr/auth.c Wed Nov 27 11:52:35 2013
@@ -26,15 +26,16 @@
 #include <apr_tables.h>
 #include <apr_strings.h>
 
+#include "svn_private_config.h"
 #include "svn_hash.h"
 #include "svn_types.h"
 #include "svn_string.h"
 #include "svn_error.h"
 #include "svn_auth.h"
 #include "svn_config.h"
-#include "svn_private_config.h"
 #include "svn_dso.h"
 #include "svn_version.h"
+#include "private/svn_auth_private.h"
 #include "private/svn_dep_compat.h"
 
 #include "auth.h"
@@ -176,14 +177,18 @@ svn_auth_set_parameter(svn_auth_baton_t 
                        const char *name,
                        const void *value)
 {
-  svn_hash_sets(auth_baton->parameters, name, value);
+  if (auth_baton)
+    svn_hash_sets(auth_baton->parameters, name, value);
 }
 
 const void *
 svn_auth_get_parameter(svn_auth_baton_t *auth_baton,
                        const char *name)
 {
-  return svn_hash_gets(auth_baton->parameters, name);
+  if (auth_baton)
+    return svn_hash_gets(auth_baton->parameters, name);
+  else
+    return NULL;
 }
 
 
@@ -194,7 +199,7 @@ make_cache_key(const char *cred_kind,
                const char *realmstring,
                apr_pool_t *pool)
 {
-  return apr_pstrcat(pool, cred_kind, ":", realmstring, (char *)NULL);
+  return apr_pstrcat(pool, cred_kind, ":", realmstring, SVN_VA_NULL);
 }
 
 svn_error_t *
@@ -214,6 +219,10 @@ svn_auth_first_credentials(void **creden
   svn_auth_iterstate_t *iterstate;
   const char *cache_key;
 
+  if (! auth_baton)
+    return svn_error_create(SVN_ERR_AUTHN_NO_PROVIDER, NULL,
+                            _("No authentication providers registered"));
+
   /* Get the appropriate table of providers for CRED_KIND. */
   table = svn_hash_gets(auth_baton->tables, cred_kind);
   if (! table)
@@ -481,7 +490,8 @@ svn_auth_get_platform_specific_provider(
               check_list[0].version_query = version_function;
               check_list[1].label = NULL;
               check_list[1].version_query = NULL;
-              SVN_ERR(svn_ver_check_list(svn_subr_version(), check_list));
+              SVN_ERR(svn_ver_check_list2(svn_subr_version(), check_list,
+                                          svn_ver_equal));
             }
           if (apr_dso_sym(&provider_function_symbol,
                           dso,
@@ -509,19 +519,19 @@ svn_auth_get_platform_specific_provider(
       if (strcmp(provider_name, "gpg_agent") == 0 &&
           strcmp(provider_type, "simple") == 0)
         {
-          svn_auth_get_gpg_agent_simple_provider(provider, pool);
+          svn_auth__get_gpg_agent_simple_provider(provider, pool);
         }
 #endif
 #ifdef SVN_HAVE_KEYCHAIN_SERVICES
       if (strcmp(provider_name, "keychain") == 0 &&
           strcmp(provider_type, "simple") == 0)
         {
-          svn_auth_get_keychain_simple_provider(provider, pool);
+          svn_auth__get_keychain_simple_provider(provider, pool);
         }
       else if (strcmp(provider_name, "keychain") == 0 &&
                strcmp(provider_type, "ssl_client_cert_pw") == 0)
         {
-          svn_auth_get_keychain_ssl_client_cert_pw_provider(provider, pool);
+          svn_auth__get_keychain_ssl_client_cert_pw_provider(provider, pool);
         }
 #endif
 
@@ -529,17 +539,22 @@ svn_auth_get_platform_specific_provider(
       if (strcmp(provider_name, "windows") == 0 &&
           strcmp(provider_type, "simple") == 0)
         {
-          svn_auth_get_windows_simple_provider(provider, pool);
+          svn_auth__get_windows_simple_provider(provider, pool);
         }
       else if (strcmp(provider_name, "windows") == 0 &&
                strcmp(provider_type, "ssl_client_cert_pw") == 0)
         {
-          svn_auth_get_windows_ssl_client_cert_pw_provider(provider, pool);
+          svn_auth__get_windows_ssl_client_cert_pw_provider(provider, pool);
         }
       else if (strcmp(provider_name, "windows") == 0 &&
                strcmp(provider_type, "ssl_server_trust") == 0)
         {
-          svn_auth_get_windows_ssl_server_trust_provider(provider, pool);
+          svn_auth__get_windows_ssl_server_trust_provider(provider, pool);
+        }
+      else if (strcmp(provider_name, "windows") == 0 &&
+               strcmp(provider_type, "ssl_server_authority") == 0)
+        {
+          svn_auth__get_windows_ssl_server_authority_provider(provider, pool);
         }
 #endif
     }
@@ -651,5 +666,22 @@ svn_auth_get_platform_specific_client_pr
         }
     }
 
+  /* Windows has two providers without a store to allow easy access to
+     SSL servers. We enable these unconditionally.
+     (This behavior was moved here from svn_cmdline_create_auth_baton()) */
+  SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
+                                                  "windows",
+                                                  "ssl_server_trust",
+                                                  pool));
+  SVN__MAYBE_ADD_PROVIDER(*providers, provider);
+
+  /* The windows ssl authority certificate CRYPTOAPI provider. */
+  SVN_ERR(svn_auth_get_platform_specific_provider(&provider,
+                                                  "windows",
+                                                  "ssl_server_authority",
+                                                  pool));
+
+  SVN__MAYBE_ADD_PROVIDER(*providers, provider);
+
   return SVN_NO_ERROR;
 }