Split the dumpfile loader functionality used by 'svnadmin load' into two
parts:
  1. filtering (includes renumbering revs; moving to a parent dir)
  2. committing (deals with UUID, hooks, dates, and validating props)

Part of de-duplicating the dumpfile dump, load and filtering implementations.

No functional change.

* subversion/include/svn_repos.h
  (svn_repos_get_dumpfile_load_filter,
   svn_repos_get_dumpfile_committer): New functions.

* subversion/libsvn_repos/load-filter.c
  New file, implementing svn_repos_get_dumpfile_load_filter().

* subversion/libsvn_repos/load-fs-vtable.c
  Strip out the filtering part of this implementation and rename
  svn_repos_get_fs_build_parser5() to svn_repos_get_dumpfile_committer().
  Re-implement svn_repos_get_fs_build_parser5() as a wrapper around the two
  new functions.
--This line, and those below, will be ignored--

Index: subversion/include/svn_repos.h
===================================================================
--- subversion/include/svn_repos.h	(revision 1649661)
+++ subversion/include/svn_repos.h	(working copy)
@@ -3324,12 +3324,127 @@ svn_repos_parse_dumpstream3(svn_stream_t
                             svn_cancel_func_t cancel_func,
                             void *cancel_baton,
                             apr_pool_t *pool);
 
 
 /**
+ * Get a dumpfile filter that supports:
+ *    - skipping revs outside a range
+ *    - renumbering revs (also adjusting copyfrom and mergeinfo)
+ *    - parent dir (also adjusting copyfrom and mergeinfo)
+ *
+ * Set @a *filter_p and @a *filter_baton_p to a filter that forwards to
+ * @a committer / @a committer_baton.
+ *
+ * Use @a repos for predicting the revision numbers that will be committed.
+ * ### Unnecessarily tight coupling. Use a callback instead?
+ *
+ * @a start_rev and @a end_rev act as filters, the lower and upper
+ * (inclusive) range values of revisions which will
+ * be loaded.  Either both of these values are #SVN_INVALID_REVNUM (in
+ * which case no revision-based filtering occurs at all), or both are
+ * valid revisions (where @a start_rev is older than or equivalent to
+ * @a end_rev).  They refer to dump stream revision numbers rather than
+ * committed revision numbers.
+ *
+ * @a accept_invalid_mergeinfo: Whether to pass through invalid mergeinfo
+ * unchanged or to throw an error, when normalizing and adjusting for
+ * parent dir and/or revision number changes.
+ *
+ * If @a parent_dir is not NULL, then the parser will reparent all the
+ * loaded nodes, from root to @a parent_dir.  The directory @a parent_dir
+ * must be an existing directory in the repository.
+ *
+ * If @a notify_func is non-null, call it with @a notify_baton to issue
+ * the following notifications:
+ *          svn_repos_notify_load_skipped_rev
+ *          svn_repos_notify_load_normalized_mergeinfo
+ *          svn_repos_notify_warning:_invalid_mergeinfo
+ *
+ * Allocate the result in @a pool. Use a subpool of @a pool for temporary
+ * allocations.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_repos_get_dumpfile_load_filter(const svn_repos_parse_fns3_t **filter_p,
+                                   void **filter_baton_p,
+                                   const svn_repos_parse_fns3_t *committer,
+                                   void *committer_baton,
+                                   svn_repos_t *repos,
+                                   svn_revnum_t start_rev,
+                                   svn_revnum_t end_rev,
+                                   svn_boolean_t accept_invalid_mergeinfo,
+                                   const char *parent_dir,
+                                   svn_repos_notify_func_t notify_func,
+                                   void *notify_baton,
+                                   apr_pool_t *pool);
+
+/**
+ * Get a dumpfile committer that supports:
+ *    - setting the repository UUID
+ *    - running pre- and post-commit hooks
+ *          ### the start-commit hook is not supported
+ *    - 'validating' svn:* props (not specifically mergeinfo)
+ *    - keeping or ignoring the original commit date stamps
+ *
+ * Set @a *committer and @a *committer_baton to a vtable that commits new
+ * revisions to the fs in @a repos.
+ *
+ * If @a use_history is set, then the parser will require relative
+ * 'copyfrom' history to exist in the repository when it encounters
+ * nodes that are added-with-history.
+ * ### What does 'require relative copyfrom history to exist' mean?
+ * ### What happens if @a use_history is false?
+ * ### The 'use_history=FALSE' case is unused and untested in Subversion.
+ *
+ * If @a validate_props is set, then validate Subversion revision and
+ * node properties (those in the svn: namespace) against established
+ * rules for those things.
+ *
+ * Treat UUID records in a manner consistent with @a uuid_action.
+ *
+ * If @a use_pre_commit_hook is set, call the repository's pre-commit
+ * hook before committing each loaded revision.
+ *
+ * If @a use_post_commit_hook is set, call the repository's
+ * post-commit hook after committing each loaded revision.
+ *
+ * If @a ignore_dates is set, ignore any revision datestamps found in
+ * @a dumpstream, allowing the revisions created by the load process
+ * to be stamped as if they were newly created via the normal commit
+ * process.
+ *
+ * If @a notify_func is non-null, call it with @a notify_baton to issue
+ * the following notifications:
+ *          svn_repos_notify_load_txn_start
+ *          svn_repos_notify_load_copied_node
+ *          svn_repos_notify_load_node_start
+ *          svn_repos_notify_load_node_done
+ *          svn_repos_notify_load_txn_committed
+ *
+ * Allocate the result in @a pool. Use @a pool also for temporary
+ * allocations.
+ *
+ * @since New in 1.9.
+ */
+svn_error_t *
+svn_repos_get_dumpfile_committer(const svn_repos_parse_fns3_t **committer,
+                                 void **committer_baton,
+                                 svn_repos_t *repos,
+                                 svn_boolean_t use_history,
+                                 svn_boolean_t validate_props,
+                                 enum svn_repos_load_uuid uuid_action,
+                                 svn_boolean_t use_pre_commit_hook,
+                                 svn_boolean_t use_post_commit_hook,
+                                 svn_boolean_t ignore_dates,
+                                 svn_repos_notify_func_t notify_func,
+                                 void *notify_baton,
+                                 apr_pool_t *pool);
+
+/**
  * Set @a *parser and @a *parse_baton to a vtable parser which commits new
  * revisions to the fs in @a repos.  The constructed parser will treat
  * UUID records in a manner consistent with @a uuid_action.  Use @a pool
  * to operate on the fs.
  *
  * @a start_rev and @a end_rev act as filters, the lower and upper
Index: subversion/libsvn_repos/load-filter.c
===================================================================
--- subversion/libsvn_repos/load-filter.c	(revision 1647592)
+++ subversion/libsvn_repos/load-filter.c	(working copy)
@@ -1,8 +1,7 @@
-/* load-fs-vtable.c --- dumpstream loader vtable for committing into a
- *                      Subversion filesystem.
+/* load-filter.c --- dumpstream filter for adjusting revnums and paths.
  *
  * ====================================================================
  *    Licensed to the Apache Software Foundation (ASF) under one
  *    or more contributor license agreements.  See the NOTICE file
  *    distributed with this work for additional information
  *    regarding copyright ownership.  The ASF licenses this file
@@ -27,18 +26,14 @@
 #include "svn_pools.h"
 #include "svn_error.h"
 #include "svn_fs.h"
 #include "svn_repos.h"
 #include "svn_string.h"
 #include "svn_props.h"
-#include "repos.h"
-#include "svn_private_config.h"
 #include "svn_mergeinfo.h"
-#include "svn_checksum.h"
 #include "svn_subst.h"
-#include "svn_ctype.h"
 #include "svn_dirent_uri.h"
 
 #include <apr_lib.h>
 
 #include "private/svn_fspath.h"
 #include "private/svn_dep_compat.h"
@@ -47,33 +42,34 @@
 /*----------------------------------------------------------------------*/
 
 /** Batons used herein **/
 
 struct parse_baton
 {
-  svn_repos_t *repos;
-  svn_fs_t *fs;
+  const svn_repos_parse_fns3_t *inner_vtable;
+  void *inner_baton;
 
-  svn_boolean_t use_history;
-  svn_boolean_t validate_props;
-  svn_boolean_t ignore_dates;
-  svn_boolean_t use_pre_commit_hook;
-  svn_boolean_t use_post_commit_hook;
-  enum svn_repos_load_uuid uuid_action;
-  const char *parent_dir; /* repository relpath, or NULL */
-  svn_repos_notify_func_t notify_func;
-  void *notify_baton;
-  apr_pool_t *notify_pool; /* scratch pool for notifications */
-  apr_pool_t *pool;
+  svn_fs_t *fs;
 
   /* Start and end (inclusive) of revision range we'll pay attention
      to, or a pair of SVN_INVALID_REVNUMs if we're not filtering by
      revisions. */
   svn_revnum_t start_rev;
   svn_revnum_t end_rev;
 
+  /* Whether to pass through invalid mergeinfo unchanged or to throw an
+     error, when normalizing and adjusting for parent dir and/or revision
+     number changes. */
+  svn_boolean_t reject_invalid_mergeinfo;
+
+  const char *parent_dir; /* repository relpath, or NULL */
+
+  svn_repos_notify_func_t notify_func;
+  void *notify_baton;
+  apr_pool_t *notify_pool; /* scratch pool for notifications */
+
   /* A hash mapping copy-from revisions and mergeinfo range revisions
      (svn_revnum_t *) in the dump stream to their corresponding revisions
      (svn_revnum_t *) in the loaded repository.  The hash and its
      contents are allocated in POOL. */
   /* ### See http://subversion.tigris.org/issues/show_bug.cgi?id=3903
      ### for discussion about improving the memory costs of this mapping. */
@@ -88,38 +84,30 @@ struct parse_baton
      have been loaded yet, this is set to SVN_INVALID_REVNUM. */
   svn_revnum_t oldest_dumpstream_rev;
 };
 
 struct revision_baton
 {
+  void *inner_rev_baton;
+
   /* rev num from dump file */
   svn_revnum_t rev;
-  svn_fs_txn_t *txn;
-  svn_fs_root_t *txn_root;
-
-  const svn_string_t *datestamp;
 
   /* (rev num from dump file) minus (rev num to be committed) */
   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;
 };
 
 struct node_baton
 {
+  void *inner_node_baton;
+
   const char *path;
-  svn_node_kind_t kind;
-  enum svn_node_action action;
-  svn_checksum_t *base_checksum;        /* null, if not available */
-  svn_checksum_t *result_checksum;      /* null, if not available */
-  svn_checksum_t *copy_source_checksum; /* null, if not available */
 
   svn_revnum_t copyfrom_rev;
   const char *copyfrom_path;
 
   struct revision_baton *rb;
   apr_pool_t *pool;
@@ -152,49 +140,12 @@ get_revision_mapping(apr_hash_t *rev_map
   svn_revnum_t *to_rev = apr_hash_get(rev_map, &from_rev,
                                       sizeof(from_rev));
   return to_rev ? *to_rev : SVN_INVALID_REVNUM;
 }
 
 
-/* Change revision property NAME to VALUE for REVISION in REPOS.  If
-   VALIDATE_PROPS is set, use functions which perform validation of
-   the property value.  Otherwise, bypass those checks. */
-static svn_error_t *
-change_rev_prop(svn_repos_t *repos,
-                svn_revnum_t revision,
-                const char *name,
-                const svn_string_t *value,
-                svn_boolean_t validate_props,
-                apr_pool_t *pool)
-{
-  if (validate_props)
-    return svn_repos_fs_change_rev_prop4(repos, revision, NULL, name,
-                                         NULL, value, FALSE, FALSE,
-                                         NULL, NULL, pool);
-  else
-    return svn_fs_change_rev_prop2(svn_repos_fs(repos), revision, name,
-                                   NULL, value, pool);
-}
-
-/* Change property NAME to VALUE for PATH in TXN_ROOT.  If
-   VALIDATE_PROPS is set, use functions which perform validation of
-   the property value.  Otherwise, bypass those checks. */
-static svn_error_t *
-change_node_prop(svn_fs_root_t *txn_root,
-                 const char *path,
-                 const char *name,
-                 const svn_string_t *value,
-                 svn_boolean_t validate_props,
-                 apr_pool_t *pool)
-{
-  if (validate_props)
-    return svn_repos_fs_change_node_prop(txn_root, path, name, value, pool);
-  else
-    return svn_fs_change_node_prop(txn_root, path, name, value, pool);
-}
-
 /* Prepend the mergeinfo source paths in MERGEINFO_ORIG with PARENT_DIR, and
    return it in *MERGEINFO_VAL. */
 /* ### FIXME:  Consider somehow sharing code with
    ### svnrdump/load_editor.c:prefix_mergeinfo_paths() */
 static svn_error_t *
 prefix_mergeinfo_paths(svn_string_t **mergeinfo_val,
@@ -337,60 +288,44 @@ renumber_mergeinfo_revs(svn_string_t **f
 
   return SVN_NO_ERROR;
 }
 
 /*----------------------------------------------------------------------*/
 
-/** vtable for doing commits to a fs **/
+/** vtable for filtering a dumpfile **/
 
 
+/* Make a node baton, parsing the relevant HEADERS.
+ *
+ * If RB->pb->parent_dir:
+ *   prefix it to NB->path
+ *   prefix it to NB->copyfrom_path (if present)
+ */
 static svn_error_t *
 make_node_baton(struct node_baton **node_baton_p,
                 apr_hash_t *headers,
                 struct revision_baton *rb,
                 apr_pool_t *pool)
 {
   struct node_baton *nb = apr_pcalloc(pool, sizeof(*nb));
   const char *val;
 
   /* Start with sensible defaults. */
   nb->rb = rb;
   nb->pool = pool;
-  nb->kind = svn_node_unknown;
 
   /* Then add info from the headers.  */
   if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_PATH)))
   {
     val = svn_relpath_canonicalize(val, pool);
     if (rb->pb->parent_dir)
       nb->path = svn_relpath_join(rb->pb->parent_dir, val, pool);
     else
       nb->path = val;
   }
 
-  if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_KIND)))
-    {
-      if (! strcmp(val, "file"))
-        nb->kind = svn_node_file;
-      else if (! strcmp(val, "dir"))
-        nb->kind = svn_node_dir;
-    }
-
-  nb->action = (enum svn_node_action)(-1);  /* an invalid action code */
-  if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_ACTION)))
-    {
-      if (! strcmp(val, "change"))
-        nb->action = svn_node_action_change;
-      else if (! strcmp(val, "add"))
-        nb->action = svn_node_action_add;
-      else if (! strcmp(val, "delete"))
-        nb->action = svn_node_action_delete;
-      else if (! strcmp(val, "replace"))
-        nb->action = svn_node_action_replace;
-    }
-
   nb->copyfrom_rev = SVN_INVALID_REVNUM;
   if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV)))
     {
       nb->copyfrom_rev = SVN_STR_TO_REV(val);
     }
   if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH)))
@@ -399,51 +334,34 @@ make_node_baton(struct node_baton **node
       if (rb->pb->parent_dir)
         nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir, val, pool);
       else
         nb->copyfrom_path = val;
     }
 
-  if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_CHECKSUM)))
-    {
-      SVN_ERR(svn_checksum_parse_hex(&nb->result_checksum, svn_checksum_md5,
-                                     val, pool));
-    }
-
-  if ((val = svn_hash_gets(headers,
-                           SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_CHECKSUM)))
-    {
-      SVN_ERR(svn_checksum_parse_hex(&nb->base_checksum, svn_checksum_md5, val,
-                                     pool));
-    }
-
-  if ((val = svn_hash_gets(headers,
-                           SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_CHECKSUM)))
-    {
-      SVN_ERR(svn_checksum_parse_hex(&nb->copy_source_checksum,
-                                     svn_checksum_md5, val, pool));
-    }
-
   /* What's cool about this dump format is that the parser just
      ignores any unrecognized headers.  :-)  */
 
   *node_baton_p = nb;
   return SVN_NO_ERROR;
 }
 
+/* Make a revision baton, parsing the relevant HEADERS.
+ *
+ * Set RB->skipped iff the revision number is outside the requested range.
+ */
 static struct revision_baton *
 make_revision_baton(apr_hash_t *headers,
                     struct parse_baton *pb,
                     apr_pool_t *pool)
 {
   struct revision_baton *rb = apr_pcalloc(pool, sizeof(*rb));
   const char *val;
 
   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)))
     {
       rb->rev = SVN_STR_TO_REV(val);
 
       /* If we're filtering revisions, is this one we'll skip? */
@@ -486,30 +404,13 @@ new_revision_record(void **revision_bato
      Calculate the revision 'offset' for finding copyfrom sources.
      It might be positive or negative. */
   rb->rev_offset = (apr_int32_t) ((rb->rev) - (head_rev + 1));
 
   if ((rb->rev > 0) && (! rb->skipped))
     {
-      /* Create a new fs txn. */
-      SVN_ERR(svn_fs_begin_txn2(&(rb->txn), pb->fs, head_rev,
-                                SVN_FS_TXN_CLIENT_DATE, pool));
-      SVN_ERR(svn_fs_txn_root(&(rb->txn_root), rb->txn, pool));
-
-      if (pb->notify_func)
-        {
-          /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
-          svn_repos_notify_t *notify = svn_repos_notify_create(
-                                            svn_repos_notify_load_txn_start,
-                                            pb->notify_pool);
-
-          notify->old_revision = rb->rev;
-          pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
-          svn_pool_clear(pb->notify_pool);
-        }
-
-      /* Stash the oldest "old" revision committed from the load stream. */
+      /* Stash the oldest revision found in the dump stream. */
       if (!SVN_IS_VALID_REVNUM(pb->oldest_dumpstream_rev))
         pb->oldest_dumpstream_rev = rb->rev;
     }
 
   /* If we're skipping this revision, try to notify someone. */
   if (rb->skipped && pb->notify_func)
@@ -521,44 +422,44 @@ new_revision_record(void **revision_bato
 
       notify->old_revision = rb->rev;
       pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
       svn_pool_clear(pb->notify_pool);
     }
 
+  /* If we're skipping this revision, we're done here. */
+  if (rb->skipped)
+    {
+      *revision_baton = rb;
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(pb->inner_vtable->new_revision_record(&rb->inner_rev_baton, headers,
+                                                pb->inner_baton, pool));
+
   /* If we're parsing revision 0, only the revision props are (possibly)
      interesting to us: when loading the stream into an empty
      filesystem, then we want new filesystem's revision 0 to have the
      same props.  Otherwise, we just ignore revision 0 in the stream. */
 
   *revision_baton = rb;
   return SVN_NO_ERROR;
 }
 
 
 
-/* Factorized helper func for new_node_record() */
+/*
+  ... maybe adjust NB->copyfrom_rev for revnum changes
+ */
 static svn_error_t *
 maybe_add_with_history(struct node_baton *nb,
                        struct revision_baton *rb,
                        apr_pool_t *pool)
 {
-  struct parse_baton *pb = rb->pb;
-
-  if ((nb->copyfrom_path == NULL) || (! pb->use_history))
-    {
-      /* Add empty file or dir, without history. */
-      if (nb->kind == svn_node_file)
-        SVN_ERR(svn_fs_make_file(rb->txn_root, nb->path, pool));
-
-      else if (nb->kind == svn_node_dir)
-        SVN_ERR(svn_fs_make_dir(rb->txn_root, nb->path, pool));
-    }
-  else
+  if (nb->copyfrom_path)
     {
       /* Hunt down the source revision in this fs. */
-      svn_fs_root_t *copy_root;
       svn_revnum_t copyfrom_rev;
 
       /* Try to find the copyfrom revision in the revision map;
          failing that, fall back to the revision offset approach. */
       copyfrom_rev = get_revision_mapping(rb->pb->rev_map, nb->copyfrom_rev);
       if (! SVN_IS_VALID_REVNUM(copyfrom_rev))
@@ -567,72 +468,39 @@ maybe_add_with_history(struct node_baton
       if (! SVN_IS_VALID_REVNUM(copyfrom_rev))
         return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
                                  _("Relative source revision %ld is not"
                                    " available in current repository"),
                                  copyfrom_rev);
 
-      SVN_ERR(svn_fs_revision_root(&copy_root, pb->fs, copyfrom_rev, pool));
-
-      if (nb->copy_source_checksum)
-        {
-          svn_checksum_t *checksum;
-          SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, copy_root,
-                                       nb->copyfrom_path, TRUE, pool));
-          if (!svn_checksum_match(nb->copy_source_checksum, checksum))
-            return svn_checksum_mismatch_err(nb->copy_source_checksum,
-                      checksum, pool,
-                      _("Copy source checksum mismatch on copy from '%s'@%ld\n"
-                        "to '%s' in rev based on r%ld"),
-                      nb->copyfrom_path, copyfrom_rev, nb->path, rb->rev);
-        }
-
-      SVN_ERR(svn_fs_copy(copy_root, nb->copyfrom_path,
-                          rb->txn_root, nb->path, pool));
-
-      if (pb->notify_func)
-        {
-          /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
-          svn_repos_notify_t *notify = svn_repos_notify_create(
-                                            svn_repos_notify_load_copied_node,
-                                            pb->notify_pool);
-
-          pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
-          svn_pool_clear(pb->notify_pool);
-        }
+      nb->copyfrom_rev = copyfrom_rev;
     }
 
   return SVN_NO_ERROR;
 }
 
 static svn_error_t *
 magic_header_record(int version,
                     void *parse_baton,
                     apr_pool_t *pool)
 {
+  struct parse_baton *pb = parse_baton;
+
+  SVN_ERR(pb->inner_vtable->magic_header_record(version, pb->inner_baton,
+                                                pool));
   return SVN_NO_ERROR;
 }
 
 static svn_error_t *
 uuid_record(const char *uuid,
             void *parse_baton,
             apr_pool_t *pool)
 {
   struct parse_baton *pb = parse_baton;
-  svn_revnum_t youngest_rev;
-
-  if (pb->uuid_action == svn_repos_load_uuid_ignore)
-    return SVN_NO_ERROR;
-
-  if (pb->uuid_action != svn_repos_load_uuid_force)
-    {
-      SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, pool));
-      if (youngest_rev != 0)
-        return SVN_NO_ERROR;
-    }
 
-  return svn_fs_set_uuid(pb->fs, uuid, pool);
+  SVN_ERR(pb->inner_vtable->uuid_record(uuid, pb->inner_baton, pool));
+  return SVN_NO_ERROR;
 }
 
 static svn_error_t *
 new_node_record(void **node_baton,
                 apr_hash_t *headers,
                 void *revision_baton,
@@ -653,98 +521,49 @@ new_node_record(void **node_baton,
   if (rb->skipped)
     {
       *node_baton = nb;
       return SVN_NO_ERROR;
     }
 
-  /* Make sure we have an action we recognize. */
-  if (nb->action < svn_node_action_change
-        || nb->action > svn_node_action_replace)
-      return svn_error_createf(SVN_ERR_STREAM_UNRECOGNIZED_DATA, NULL,
-                               _("Unrecognized node-action on node '%s'"),
-                               nb->path);
+  SVN_ERR(maybe_add_with_history(nb, rb, pool));
 
-  if (pb->notify_func)
+  /* Update headers to reflect rev and/or parent-dir changes */
+  svn_hash_sets(headers, SVN_REPOS_DUMPFILE_NODE_PATH,
+                apr_psprintf(pool, "%s", nb->path));
+  if (nb->copyfrom_path)
     {
-      /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
-      svn_repos_notify_t *notify = svn_repos_notify_create(
-                                        svn_repos_notify_load_node_start,
-                                        pb->notify_pool);
-
-      notify->path = nb->path;
-      pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
-      svn_pool_clear(pb->notify_pool);
+      svn_hash_sets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH,
+                    apr_psprintf(pool, "%s", nb->copyfrom_path));
     }
-
-  switch (nb->action)
+  if (SVN_IS_VALID_REVNUM(nb->copyfrom_rev))
     {
-    case svn_node_action_change:
-      break;
-
-    case svn_node_action_delete:
-      SVN_ERR(svn_fs_delete(rb->txn_root, nb->path, pool));
-      break;
-
-    case svn_node_action_add:
-      SVN_ERR(maybe_add_with_history(nb, rb, pool));
-      break;
-
-    case svn_node_action_replace:
-      SVN_ERR(svn_fs_delete(rb->txn_root, nb->path, pool));
-      SVN_ERR(maybe_add_with_history(nb, rb, pool));
-      break;
+      svn_hash_sets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV,
+                    apr_psprintf(pool, "%ld", nb->copyfrom_rev));
     }
 
+  SVN_ERR(pb->inner_vtable->new_node_record(&nb->inner_node_baton, headers,
+                                            rb->inner_rev_baton, pool));
+
   *node_baton = nb;
   return SVN_NO_ERROR;
 }
 
 static svn_error_t *
 set_revision_property(void *baton,
                       const char *name,
                       const svn_string_t *value)
 {
   struct revision_baton *rb = baton;
   struct parse_baton *pb = rb->pb;
-  svn_boolean_t is_date = strcmp(name, SVN_PROP_REVISION_DATE) == 0;
 
   /* If we're skipping this revision, we're done here. */
   if (rb->skipped)
     return SVN_NO_ERROR;
 
-  /* If we're ignoring dates, and this is one, we're done here. */
-  if (is_date && pb->ignore_dates)
-    return SVN_NO_ERROR;
-
-  if (rb->rev > 0)
-    {
-      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.) */
-      if (is_date)
-        rb->datestamp = svn_string_dup(value, rb->pool);
-    }
-  else if (rb->rev == 0)
-    {
-      /* Special case: set revision 0 properties when loading into an
-         'empty' filesystem. */
-      svn_revnum_t youngest_rev;
-
-      SVN_ERR(svn_fs_youngest_rev(&youngest_rev, pb->fs, rb->pool));
-
-      if (youngest_rev == 0)
-        SVN_ERR(change_rev_prop(pb->repos, 0, name, value,
-                                pb->validate_props, rb->pool));
-    }
-
+  SVN_ERR(pb->inner_vtable->set_revision_property(rb->inner_rev_baton,
+                                                  name, value));
   return SVN_NO_ERROR;
 }
 
 
 /* Adjust mergeinfo:
  *   - normalize line endings (if all CRLF, change to LF; but error if mixed);
@@ -830,13 +649,13 @@ set_node_property(void *baton,
       svn_string_t *new_value;
       svn_error_t *err;
 
       err = adjust_mergeinfo_property(rb, &new_value, value, nb->pool);
       if (err)
         {
-          if (pb->validate_props)
+          if (pb->reject_invalid_mergeinfo)
             {
               return svn_error_quick_wrap(
                        err,
                        _("Invalid svn:mergeinfo value"));
             }
           if (pb->notify_func)
@@ -856,14 +675,15 @@ set_node_property(void *baton,
       else
         {
           value = new_value;
         }
     }
 
-  return change_node_prop(rb->txn_root, nb->path, name, value,
-                          pb->validate_props, nb->pool);
+  SVN_ERR(rb->pb->inner_vtable->set_node_property(nb->inner_node_baton,
+                                                  name, value));
+  return SVN_NO_ERROR;
 }
 
 
 static svn_error_t *
 delete_node_property(void *baton,
                      const char *name)
@@ -872,40 +692,29 @@ delete_node_property(void *baton,
   struct revision_baton *rb = nb->rb;
 
   /* If we're skipping this revision, we're done here. */
   if (rb->skipped)
     return SVN_NO_ERROR;
 
-  return change_node_prop(rb->txn_root, nb->path, name, NULL,
-                          rb->pb->validate_props, nb->pool);
+  SVN_ERR(rb->pb->inner_vtable->delete_node_property(nb->inner_node_baton,
+                                                     name));
+  return SVN_NO_ERROR;
 }
 
 
 static svn_error_t *
 remove_node_props(void *baton)
 {
   struct node_baton *nb = baton;
   struct revision_baton *rb = nb->rb;
-  apr_hash_t *proplist;
-  apr_hash_index_t *hi;
 
   /* If we're skipping this revision, we're done here. */
   if (rb->skipped)
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_fs_node_proplist(&proplist,
-                               rb->txn_root, nb->path, nb->pool));
-
-  for (hi = apr_hash_first(nb->pool, proplist); hi; hi = apr_hash_next(hi))
-    {
-      const char *key = apr_hash_this_key(hi);
-
-      SVN_ERR(change_node_prop(rb->txn_root, nb->path, key, NULL,
-                               rb->pb->validate_props, nb->pool));
-    }
-
+  SVN_ERR(rb->pb->inner_vtable->remove_node_props(nb->inner_node_baton));
   return SVN_NO_ERROR;
 }
 
 
 static svn_error_t *
 apply_textdelta(svn_txdelta_window_handler_t *handler,
@@ -919,19 +728,15 @@ apply_textdelta(svn_txdelta_window_handl
   if (rb->skipped)
     {
       *handler = NULL;
       return SVN_NO_ERROR;
     }
 
-  return svn_fs_apply_textdelta(handler, handler_baton,
-                                rb->txn_root, nb->path,
-                                svn_checksum_to_cstring(nb->base_checksum,
-                                                        nb->pool),
-                                svn_checksum_to_cstring(nb->result_checksum,
-                                                        nb->pool),
-                                nb->pool);
+  SVN_ERR(rb->pb->inner_vtable->apply_textdelta(handler, handler_baton,
+                                                nb->inner_node_baton));
+  return SVN_NO_ERROR;
 }
 
 
 static svn_error_t *
 set_fulltext(svn_stream_t **stream,
              void *node_baton)
@@ -943,17 +748,14 @@ set_fulltext(svn_stream_t **stream,
   if (rb->skipped)
     {
       *stream = NULL;
       return SVN_NO_ERROR;
     }
 
-  return svn_fs_apply_text(stream,
-                           rb->txn_root, nb->path,
-                           svn_checksum_to_cstring(nb->result_checksum,
-                                                   nb->pool),
-                           nb->pool);
+  SVN_ERR(rb->pb->inner_vtable->set_fulltext(stream, nb->inner_node_baton));
+  return SVN_NO_ERROR;
 }
 
 
 static svn_error_t *
 close_node(void *baton)
 {
@@ -962,117 +764,35 @@ close_node(void *baton)
   struct parse_baton *pb = rb->pb;
 
   /* If we're skipping this revision, we're done here. */
   if (rb->skipped)
     return SVN_NO_ERROR;
 
-  if (pb->notify_func)
-    {
-      /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
-      svn_repos_notify_t *notify = svn_repos_notify_create(
-                                            svn_repos_notify_load_node_done,
-                                            pb->notify_pool);
-
-      pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
-      svn_pool_clear(pb->notify_pool);
-    }
-
+  SVN_ERR(pb->inner_vtable->close_node(nb->inner_node_baton));
   return SVN_NO_ERROR;
 }
 
 
 static svn_error_t *
 close_revision(void *baton)
 {
   struct revision_baton *rb = baton;
   struct parse_baton *pb = rb->pb;
-  const char *conflict_msg = NULL;
   svn_revnum_t committed_rev;
-  svn_error_t *err;
-  const char *txn_name = NULL;
-  apr_hash_t *hooks_env;
 
   /* If we're skipping this revision or it has an invalid revision
      number, we're done here. */
   if (rb->skipped || (rb->rev <= 0))
     return SVN_NO_ERROR;
 
-  /* If the dumpstream doesn't have an 'svn:date' property and we
-     aren't ignoring the dates in the dumpstream altogether, remove
-     any 'svn:date' revision property that was set by FS layer when
-     the TXN was created.  */
-  if (! (pb->ignore_dates || rb->datestamp))
-    {
-      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)
-    {
-      SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, pb->repos->hooks_env_path,
-                                         rb->pool, rb->pool));
-
-      err = svn_fs_txn_name(&txn_name, rb->txn, rb->pool);
-      if (err)
-        {
-          svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool));
-          return svn_error_trace(err);
-        }
-    }
-
-  /* Run the pre-commit hook, if so commanded. */
-  if (pb->use_pre_commit_hook)
-    {
-      err = svn_repos__hooks_pre_commit(pb->repos, hooks_env,
-                                        txn_name, rb->pool);
-      if (err)
-        {
-          svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool));
-          return svn_error_trace(err);
-        }
-    }
-
-  /* Commit. */
-  err = svn_fs_commit_txn(&conflict_msg, &committed_rev, rb->txn, rb->pool);
-  if (SVN_IS_VALID_REVNUM(committed_rev))
-    {
-      if (err)
-        {
-          /* ### Log any error, but better yet is to rev
-             ### close_revision()'s API to allow both committed_rev and err
-             ### to be returned, see #3768. */
-          svn_error_clear(err);
-        }
-    }
-  else
-    {
-      svn_error_clear(svn_fs_abort_txn(rb->txn, rb->pool));
-      if (conflict_msg)
-        return svn_error_quick_wrap(err, conflict_msg);
-      else
-        return svn_error_trace(err);
-    }
-
-  /* Run post-commit hook, if so commanded.  */
-  if (pb->use_post_commit_hook)
-    {
-      if ((err = svn_repos__hooks_post_commit(pb->repos, hooks_env,
-                                              committed_rev, txn_name,
-                                              rb->pool)))
-        return svn_error_create
-          (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err,
-           _("Commit succeeded, but post-commit hook failed"));
-    }
+  SVN_ERR(pb->inner_vtable->close_revision(rb->inner_rev_baton));
+  /* Get the committed rev. */
+  /* ### We could/should check here whether this "expected" committed rev
+         matches the actual committed rev. If not, then any copyfrom and
+         mergeinfo adjustments based on this value are wrong. */
+  committed_rev = rb->rev - rb->rev_offset;
 
   /* After a successful commit, must record the dump-rev -> in-repos-rev
      mapping, so that copyfrom instructions in the dump file can look up the
      correct repository revision to copy from. */
   set_revision_mapping(pb->rev_map, rb->rev, committed_rev);
 
@@ -1092,140 +812,74 @@ close_revision(void *baton)
         }
     }
 
   /* Update our "last revision mapped". */
   pb->last_rev_mapped = rb->rev;
 
-  /* Deltify the predecessors of paths changed in this revision. */
-  SVN_ERR(svn_fs_deltify_revision(pb->fs, committed_rev, rb->pool));
-
-  if (pb->notify_func)
-    {
-      /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
-      svn_repos_notify_t *notify = svn_repos_notify_create(
-                                        svn_repos_notify_load_txn_committed,
-                                        pb->notify_pool);
-
-      notify->new_revision = committed_rev;
-      notify->old_revision = ((committed_rev == rb->rev)
-                                    ? SVN_INVALID_REVNUM
-                                    : rb->rev);
-      pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
-      svn_pool_clear(pb->notify_pool);
-    }
-
   return SVN_NO_ERROR;
 }
 
 
 /*----------------------------------------------------------------------*/
 
 /** The public routines **/
 
 
 svn_error_t *
-svn_repos_get_fs_build_parser5(const svn_repos_parse_fns3_t **callbacks,
-                               void **parse_baton,
-                               svn_repos_t *repos,
-                               svn_revnum_t start_rev,
-                               svn_revnum_t end_rev,
-                               svn_boolean_t use_history,
-                               svn_boolean_t validate_props,
-                               enum svn_repos_load_uuid uuid_action,
-                               const char *parent_dir,
-                               svn_boolean_t use_pre_commit_hook,
-                               svn_boolean_t use_post_commit_hook,
-                               svn_boolean_t ignore_dates,
-                               svn_repos_notify_func_t notify_func,
-                               void *notify_baton,
-                               apr_pool_t *pool)
+svn_repos_get_dumpfile_load_filter(const svn_repos_parse_fns3_t **filter_p,
+                                   void **filter_baton_p,
+                                   const svn_repos_parse_fns3_t *committer,
+                                   void *committer_baton,
+                                   svn_repos_t *repos,
+                                   svn_revnum_t start_rev,
+                                   svn_revnum_t end_rev,
+                                   svn_boolean_t reject_invalid_mergeinfo,
+                                   const char *parent_dir,
+                                   svn_repos_notify_func_t notify_func,
+                                   void *notify_baton,
+                                   apr_pool_t *pool)
 {
-  svn_repos_parse_fns3_t *parser = apr_pcalloc(pool, sizeof(*parser));
-  struct parse_baton *pb = apr_pcalloc(pool, sizeof(*pb));
+  svn_repos_parse_fns3_t *filter = apr_pcalloc(pool, sizeof(*filter));
+  struct parse_baton *fb = apr_pcalloc(pool, sizeof(*fb));
 
   if (parent_dir)
     parent_dir = svn_relpath_canonicalize(parent_dir, pool);
 
   SVN_ERR_ASSERT((SVN_IS_VALID_REVNUM(start_rev) &&
                   SVN_IS_VALID_REVNUM(end_rev))
                  || ((! SVN_IS_VALID_REVNUM(start_rev)) &&
                      (! SVN_IS_VALID_REVNUM(end_rev))));
   if (SVN_IS_VALID_REVNUM(start_rev))
     SVN_ERR_ASSERT(start_rev <= end_rev);
 
-  parser->magic_header_record = magic_header_record;
-  parser->uuid_record = uuid_record;
-  parser->new_revision_record = new_revision_record;
-  parser->new_node_record = new_node_record;
-  parser->set_revision_property = set_revision_property;
-  parser->set_node_property = set_node_property;
-  parser->remove_node_props = remove_node_props;
-  parser->set_fulltext = set_fulltext;
-  parser->close_node = close_node;
-  parser->close_revision = close_revision;
-  parser->delete_node_property = delete_node_property;
-  parser->apply_textdelta = apply_textdelta;
-
-  pb->repos = repos;
-  pb->fs = svn_repos_fs(repos);
-  pb->use_history = use_history;
-  pb->validate_props = validate_props;
-  pb->notify_func = notify_func;
-  pb->notify_baton = notify_baton;
-  pb->uuid_action = uuid_action;
-  pb->parent_dir = parent_dir;
-  pb->pool = pool;
-  pb->notify_pool = svn_pool_create(pool);
-  pb->rev_map = apr_hash_make(pool);
-  pb->oldest_dumpstream_rev = SVN_INVALID_REVNUM;
-  pb->last_rev_mapped = SVN_INVALID_REVNUM;
-  pb->start_rev = start_rev;
-  pb->end_rev = end_rev;
-  pb->use_pre_commit_hook = use_pre_commit_hook;
-  pb->use_post_commit_hook = use_post_commit_hook;
-  pb->ignore_dates = ignore_dates;
+  filter->magic_header_record = magic_header_record;
+  filter->uuid_record = uuid_record;
+  filter->new_revision_record = new_revision_record;
+  filter->new_node_record = new_node_record;
+  filter->set_revision_property = set_revision_property;
+  filter->set_node_property = set_node_property;
+  filter->remove_node_props = remove_node_props;
+  filter->set_fulltext = set_fulltext;
+  filter->close_node = close_node;
+  filter->close_revision = close_revision;
+  filter->delete_node_property = delete_node_property;
+  filter->apply_textdelta = apply_textdelta;
+
+  fb->inner_vtable = committer;
+  fb->inner_baton = committer_baton;
+  fb->fs = svn_repos_fs(repos);
+  fb->start_rev = start_rev;
+  fb->end_rev = end_rev;
+  fb->reject_invalid_mergeinfo = reject_invalid_mergeinfo;
+  fb->parent_dir = parent_dir;
+  fb->notify_func = notify_func;
+  fb->notify_baton = notify_baton;
+  fb->notify_pool = svn_pool_create(pool);
+
+  fb->rev_map = apr_hash_make(pool);
+  fb->last_rev_mapped = SVN_INVALID_REVNUM;
+  fb->oldest_dumpstream_rev = SVN_INVALID_REVNUM;
 
-  *callbacks = parser;
-  *parse_baton = pb;
+  *filter_p = filter;
+  *filter_baton_p = fb;
   return SVN_NO_ERROR;
 }
-
-
-svn_error_t *
-svn_repos_load_fs5(svn_repos_t *repos,
-                   svn_stream_t *dumpstream,
-                   svn_revnum_t start_rev,
-                   svn_revnum_t end_rev,
-                   enum svn_repos_load_uuid uuid_action,
-                   const char *parent_dir,
-                   svn_boolean_t use_pre_commit_hook,
-                   svn_boolean_t use_post_commit_hook,
-                   svn_boolean_t validate_props,
-                   svn_boolean_t ignore_dates,
-                   svn_repos_notify_func_t notify_func,
-                   void *notify_baton,
-                   svn_cancel_func_t cancel_func,
-                   void *cancel_baton,
-                   apr_pool_t *pool)
-{
-  const svn_repos_parse_fns3_t *parser;
-  void *parse_baton;
-
-  /* This is really simple. */
-
-  SVN_ERR(svn_repos_get_fs_build_parser5(&parser, &parse_baton,
-                                         repos,
-                                         start_rev, end_rev,
-                                         TRUE, /* look for copyfrom revs */
-                                         validate_props,
-                                         uuid_action,
-                                         parent_dir,
-                                         use_pre_commit_hook,
-                                         use_post_commit_hook,
-                                         ignore_dates,
-                                         notify_func,
-                                         notify_baton,
-                                         pool));
-
-  return svn_repos_parse_dumpstream3(dumpstream, parser, parse_baton, FALSE,
-                                     cancel_func, cancel_baton, pool);
-}
Index: subversion/libsvn_repos/load-fs-vtable.c
===================================================================
--- subversion/libsvn_repos/load-fs-vtable.c	(revision 1649661)
+++ subversion/libsvn_repos/load-fs-vtable.c	(working copy)
@@ -27,23 +27,19 @@
 #include "svn_pools.h"
 #include "svn_error.h"
 #include "svn_fs.h"
 #include "svn_repos.h"
 #include "svn_string.h"
 #include "svn_props.h"
-#include "repos.h"
-#include "svn_mergeinfo.h"
 #include "svn_checksum.h"
-#include "svn_subst.h"
 #include "svn_dirent_uri.h"
+#include "repos.h"
 
 #include <apr_lib.h>
 
-#include "private/svn_fspath.h"
 #include "private/svn_dep_compat.h"
-#include "private/svn_mergeinfo_private.h"
 
 /*----------------------------------------------------------------------*/
 
 /** Batons used herein **/
 
 struct parse_baton
@@ -54,55 +50,27 @@ struct parse_baton
   svn_boolean_t use_history;
   svn_boolean_t validate_props;
   svn_boolean_t ignore_dates;
   svn_boolean_t use_pre_commit_hook;
   svn_boolean_t use_post_commit_hook;
   enum svn_repos_load_uuid uuid_action;
-  const char *parent_dir; /* repository relpath, or NULL */
+
   svn_repos_notify_func_t notify_func;
   void *notify_baton;
   apr_pool_t *notify_pool; /* scratch pool for notifications */
-  apr_pool_t *pool;
-
-  /* Start and end (inclusive) of revision range we'll pay attention
-     to, or a pair of SVN_INVALID_REVNUMs if we're not filtering by
-     revisions. */
-  svn_revnum_t start_rev;
-  svn_revnum_t end_rev;
-
-  /* A hash mapping copy-from revisions and mergeinfo range revisions
-     (svn_revnum_t *) in the dump stream to their corresponding revisions
-     (svn_revnum_t *) in the loaded repository.  The hash and its
-     contents are allocated in POOL. */
-  /* ### See http://subversion.tigris.org/issues/show_bug.cgi?id=3903
-     ### for discussion about improving the memory costs of this mapping. */
-  apr_hash_t *rev_map;
-
-  /* The most recent (youngest) revision from the dump stream mapped in
-     REV_MAP.  If no revisions have been mapped yet, this is set to
-     SVN_INVALID_REVNUM. */
-  svn_revnum_t last_rev_mapped;
-
-  /* The oldest revision loaded from the dump stream.  If no revisions
-     have been loaded yet, this is set to SVN_INVALID_REVNUM. */
-  svn_revnum_t oldest_dumpstream_rev;
 };
 
 struct revision_baton
 {
   /* rev num from dump file */
   svn_revnum_t rev;
   svn_fs_txn_t *txn;
   svn_fs_root_t *txn_root;
 
   const svn_string_t *datestamp;
 
-  /* (rev num from dump file) minus (rev num to be committed) */
-  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;
 };
@@ -123,39 +91,12 @@ struct node_baton
   apr_pool_t *pool;
 };
 
 
 /*----------------------------------------------------------------------*/
 
-/* Record the mapping of FROM_REV to TO_REV in REV_MAP, ensuring that
-   anything added to the hash is allocated in the hash's pool. */
-static void
-set_revision_mapping(apr_hash_t *rev_map,
-                     svn_revnum_t from_rev,
-                     svn_revnum_t to_rev)
-{
-  svn_revnum_t *mapped_revs = apr_palloc(apr_hash_pool_get(rev_map),
-                                         sizeof(svn_revnum_t) * 2);
-  mapped_revs[0] = from_rev;
-  mapped_revs[1] = to_rev;
-  apr_hash_set(rev_map, mapped_revs,
-               sizeof(svn_revnum_t), mapped_revs + 1);
-}
-
-/* Return the revision to which FROM_REV maps in REV_MAP, or
-   SVN_INVALID_REVNUM if no such mapping exists. */
-static svn_revnum_t
-get_revision_mapping(apr_hash_t *rev_map,
-                     svn_revnum_t from_rev)
-{
-  svn_revnum_t *to_rev = apr_hash_get(rev_map, &from_rev,
-                                      sizeof(from_rev));
-  return to_rev ? *to_rev : SVN_INVALID_REVNUM;
-}
-
-
 /* Change revision property NAME to VALUE for REVISION in REPOS.  If
    VALIDATE_PROPS is set, use functions which perform validation of
    the property value.  Otherwise, bypass those checks. */
 static svn_error_t *
 change_rev_prop(svn_repos_t *repos,
                 svn_revnum_t revision,
@@ -187,168 +128,18 @@ change_node_prop(svn_fs_root_t *txn_root
   if (validate_props)
     return svn_repos_fs_change_node_prop(txn_root, path, name, value, pool);
   else
     return svn_fs_change_node_prop(txn_root, path, name, value, pool);
 }
 
-/* Prepend the mergeinfo source paths in MERGEINFO_ORIG with PARENT_DIR, and
-   return it in *MERGEINFO_VAL. */
-/* ### FIXME:  Consider somehow sharing code with
-   ### svnrdump/load_editor.c:prefix_mergeinfo_paths() */
-static svn_error_t *
-prefix_mergeinfo_paths(svn_string_t **mergeinfo_val,
-                       const svn_string_t *mergeinfo_orig,
-                       const char *parent_dir,
-                       apr_pool_t *pool)
-{
-  apr_hash_t *prefixed_mergeinfo, *mergeinfo;
-  apr_hash_index_t *hi;
-
-  SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_orig->data, pool));
-  prefixed_mergeinfo = apr_hash_make(pool);
-  for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
-    {
-      const char *merge_source = apr_hash_this_key(hi);
-      svn_rangelist_t *rangelist = apr_hash_this_val(hi);
-      const char *path;
-
-      merge_source = svn_relpath_canonicalize(merge_source, pool);
-
-      /* The svn:mergeinfo property syntax demands a repos abspath */
-      path = svn_fspath__canonicalize(svn_relpath_join(parent_dir,
-                                                       merge_source, pool),
-                                      pool);
-      svn_hash_sets(prefixed_mergeinfo, path, rangelist);
-    }
-  return svn_mergeinfo_to_string(mergeinfo_val, prefixed_mergeinfo, pool);
-}
-
-
-/* Examine the mergeinfo in INITIAL_VAL, renumber revisions in rangelists
-   as appropriate, and return the (possibly new) mergeinfo in *FINAL_VAL
-   (allocated from POOL). */
-/* ### FIXME:  Consider somehow sharing code with
-   ### svnrdump/load_editor.c:renumber_mergeinfo_revs() */
-static svn_error_t *
-renumber_mergeinfo_revs(svn_string_t **final_val,
-                        const svn_string_t *initial_val,
-                        struct revision_baton *rb,
-                        apr_pool_t *pool)
-{
-  apr_pool_t *subpool = svn_pool_create(pool);
-  svn_mergeinfo_t mergeinfo, predates_stream_mergeinfo;
-  svn_mergeinfo_t final_mergeinfo = apr_hash_make(subpool);
-  apr_hash_index_t *hi;
-
-  SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool));
-
-  /* Issue #3020
-     http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16
-     Remove mergeinfo older than the oldest revision in the dump stream
-     and adjust its revisions by the difference between the head rev of
-     the target repository and the current dump stream rev. */
-  if (rb->pb->oldest_dumpstream_rev > 1)
-    {
-      SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
-        &predates_stream_mergeinfo, mergeinfo,
-        rb->pb->oldest_dumpstream_rev - 1, 0,
-        TRUE, subpool, subpool));
-      SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
-        &mergeinfo, mergeinfo,
-        rb->pb->oldest_dumpstream_rev - 1, 0,
-        FALSE, subpool, subpool));
-      SVN_ERR(svn_mergeinfo__adjust_mergeinfo_rangelists(
-        &predates_stream_mergeinfo, predates_stream_mergeinfo,
-        -rb->rev_offset, subpool, subpool));
-    }
-  else
-    {
-      predates_stream_mergeinfo = NULL;
-    }
-
-  for (hi = apr_hash_first(subpool, mergeinfo); hi; hi = apr_hash_next(hi))
-    {
-      const char *merge_source = apr_hash_this_key(hi);
-      svn_rangelist_t *rangelist = apr_hash_this_val(hi);
-      struct parse_baton *pb = rb->pb;
-      int i;
-
-      /* Possibly renumber revisions in merge source's rangelist. */
-      for (i = 0; i < rangelist->nelts; i++)
-        {
-          svn_revnum_t rev_from_map;
-          svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
-                                                   svn_merge_range_t *);
-          rev_from_map = get_revision_mapping(pb->rev_map, range->start);
-          if (SVN_IS_VALID_REVNUM(rev_from_map))
-            {
-              range->start = rev_from_map;
-            }
-          else if (range->start == pb->oldest_dumpstream_rev - 1)
-            {
-              /* Since the start revision of svn_merge_range_t are not
-                 inclusive there is one possible valid start revision that
-                 won't be found in the PB->REV_MAP mapping of load stream
-                 revsions to loaded revisions: The revision immediately
-                 preceding the oldest revision from the load stream.
-                 This is a valid revision for mergeinfo, but not a valid
-                 copy from revision (which PB->REV_MAP also maps for) so it
-                 will never be in the mapping.
-
-                 If that is what we have here, then find the mapping for the
-                 oldest rev from the load stream and subtract 1 to get the
-                 renumbered, non-inclusive, start revision. */
-              rev_from_map = get_revision_mapping(pb->rev_map,
-                                                  pb->oldest_dumpstream_rev);
-              if (SVN_IS_VALID_REVNUM(rev_from_map))
-                range->start = rev_from_map - 1;
-            }
-          else
-            {
-              /* If we can't remap the start revision then don't even bother
-                 trying to remap the end revision.  It's possible we might
-                 actually succeed at the latter, which can result in invalid
-                 mergeinfo with a start rev > end rev.  If that gets into the
-                 repository then a world of bustage breaks loose anytime that
-                 bogus mergeinfo is parsed.  See
-                 http://subversion.tigris.org/issues/show_bug.cgi?id=3020#desc16.
-                 */
-              continue;
-            }
-
-          rev_from_map = get_revision_mapping(pb->rev_map, range->end);
-          if (SVN_IS_VALID_REVNUM(rev_from_map))
-            range->end = rev_from_map;
-        }
-      svn_hash_sets(final_mergeinfo, merge_source, rangelist);
-    }
-
-  if (predates_stream_mergeinfo)
-    {
-      SVN_ERR(svn_mergeinfo_merge2(final_mergeinfo, predates_stream_mergeinfo,
-                                   subpool, subpool));
-    }
-
-  SVN_ERR(svn_mergeinfo__canonicalize_ranges(final_mergeinfo, subpool));
-
-  SVN_ERR(svn_mergeinfo_to_string(final_val, final_mergeinfo, pool));
-  svn_pool_destroy(subpool);
-
-  return SVN_NO_ERROR;
-}
-
 /*----------------------------------------------------------------------*/
 
 /** vtable for doing commits to a fs **/
 
 
 /* Make a node baton, parsing the relevant HEADERS.
- *
- * If RB->pb->parent_dir:
- *   prefix it to NB->path
- *   prefix it to NB->copyfrom_path (if present)
  */
 static svn_error_t *
 make_node_baton(struct node_baton **node_baton_p,
                 apr_hash_t *headers,
                 struct revision_baton *rb,
                 apr_pool_t *pool)
@@ -361,17 +152,13 @@ make_node_baton(struct node_baton **node
   nb->pool = pool;
   nb->kind = svn_node_unknown;
 
   /* Then add info from the headers.  */
   if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_PATH)))
   {
-    val = svn_relpath_canonicalize(val, pool);
-    if (rb->pb->parent_dir)
-      nb->path = svn_relpath_join(rb->pb->parent_dir, val, pool);
-    else
-      nb->path = val;
+    nb->path = svn_relpath_canonicalize(val, pool);
   }
 
   if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_KIND)))
     {
       if (! strcmp(val, "file"))
         nb->kind = svn_node_file;
@@ -396,17 +183,13 @@ make_node_baton(struct node_baton **node
   if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV)))
     {
       nb->copyfrom_rev = SVN_STR_TO_REV(val);
     }
   if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH)))
     {
-      val = svn_relpath_canonicalize(val, pool);
-      if (rb->pb->parent_dir)
-        nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir, val, pool);
-      else
-        nb->copyfrom_path = val;
+      nb->copyfrom_path = svn_relpath_canonicalize(val, pool);
     }
 
   if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_CHECKSUM)))
     {
       SVN_ERR(svn_checksum_parse_hex(&nb->result_checksum, svn_checksum_md5,
                                      val, pool));
@@ -431,14 +214,12 @@ make_node_baton(struct node_baton **node
 
   *node_baton_p = nb;
   return SVN_NO_ERROR;
 }
 
 /* Make a revision baton, parsing the relevant HEADERS.
- *
- * Set RB->skipped iff the revision number is outside the range given in PB.
  */
 static struct revision_baton *
 make_revision_baton(apr_hash_t *headers,
                     struct parse_baton *pb,
                     apr_pool_t *pool)
 {
@@ -450,17 +231,12 @@ make_revision_baton(apr_hash_t *headers,
   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)))
     {
       rb->rev = SVN_STR_TO_REV(val);
-
-      /* If we're filtering revisions, is this one we'll skip? */
-      rb->skipped = (SVN_IS_VALID_REVNUM(pb->start_rev)
-                     && ((rb->rev < pb->start_rev) ||
-                         (rb->rev > pb->end_rev)));
     }
 
   return rb;
 }
 
 
@@ -473,32 +249,15 @@ new_revision_record(void **revision_bato
   struct parse_baton *pb = parse_baton;
   struct revision_baton *rb;
   svn_revnum_t head_rev;
 
   rb = make_revision_baton(headers, pb, pool);
 
-  /* ### If we're filtering revisions, and this is one we've skipped,
-     ### and we've skipped it because it has a revision number younger
-     ### than the youngest in our acceptable range, then should we
-     ### just bail out here? */
-  /*
-  if (rb->skipped && (rb->rev > pb->end_rev))
-    return svn_error_createf(SVN_ERR_CEASE_INVOCATION, 0,
-                             _("Finished processing acceptable load "
-                               "revision range"));
-  */
-
   SVN_ERR(svn_fs_youngest_rev(&head_rev, pb->fs, pool));
 
-  /* FIXME: This is a lame fallback loading multiple segments of dump in
-     several separate operations. It is highly susceptible to race conditions.
-     Calculate the revision 'offset' for finding copyfrom sources.
-     It might be positive or negative. */
-  rb->rev_offset = (apr_int32_t) ((rb->rev) - (head_rev + 1));
-
-  if ((rb->rev > 0) && (! rb->skipped))
+  if (rb->rev > 0)
     {
       /* Create a new fs txn. */
       SVN_ERR(svn_fs_begin_txn2(&(rb->txn), pb->fs, head_rev,
                                 SVN_FS_TXN_CLIENT_DATE, pool));
       SVN_ERR(svn_fs_txn_root(&(rb->txn_root), rb->txn, pool));
 
@@ -510,29 +269,12 @@ new_revision_record(void **revision_bato
                                             pb->notify_pool);
 
           notify->old_revision = rb->rev;
           pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
           svn_pool_clear(pb->notify_pool);
         }
-
-      /* Stash the oldest "old" revision committed from the load stream. */
-      if (!SVN_IS_VALID_REVNUM(pb->oldest_dumpstream_rev))
-        pb->oldest_dumpstream_rev = rb->rev;
-    }
-
-  /* If we're skipping this revision, try to notify someone. */
-  if (rb->skipped && pb->notify_func)
-    {
-      /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
-      svn_repos_notify_t *notify = svn_repos_notify_create(
-                                        svn_repos_notify_load_skipped_rev,
-                                        pb->notify_pool);
-
-      notify->old_revision = rb->rev;
-      pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
-      svn_pool_clear(pb->notify_pool);
     }
 
   /* If we're parsing revision 0, only the revision props are (possibly)
      interesting to us: when loading the stream into an empty
      filesystem, then we want new filesystem's revision 0 to have the
      same props.  Otherwise, we just ignore revision 0 in the stream. */
@@ -542,13 +284,13 @@ new_revision_record(void **revision_bato
 }
 
 
 
 /* Perform a copy or a plain add.
  *
- * For a copy, also adjust the copy-from rev, check any copy-source checksum,
+ * For a copy, also check any copy-source checksum,
  * and send a notification.
  */
 static svn_error_t *
 maybe_add_with_history(struct node_baton *nb,
                        struct revision_baton *rb,
                        apr_pool_t *pool)
@@ -563,41 +305,28 @@ maybe_add_with_history(struct node_baton
 
       else if (nb->kind == svn_node_dir)
         SVN_ERR(svn_fs_make_dir(rb->txn_root, nb->path, pool));
     }
   else
     {
-      /* Hunt down the source revision in this fs. */
+      /* Copy from the source revision in this fs. */
       svn_fs_root_t *copy_root;
-      svn_revnum_t copyfrom_rev;
-
-      /* Try to find the copyfrom revision in the revision map;
-         failing that, fall back to the revision offset approach. */
-      copyfrom_rev = get_revision_mapping(rb->pb->rev_map, nb->copyfrom_rev);
-      if (! SVN_IS_VALID_REVNUM(copyfrom_rev))
-        copyfrom_rev = nb->copyfrom_rev - rb->rev_offset;
-
-      if (! SVN_IS_VALID_REVNUM(copyfrom_rev))
-        return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
-                                 _("Relative source revision %ld is not"
-                                   " available in current repository"),
-                                 copyfrom_rev);
 
-      SVN_ERR(svn_fs_revision_root(&copy_root, pb->fs, copyfrom_rev, pool));
+      SVN_ERR(svn_fs_revision_root(&copy_root, pb->fs, nb->copyfrom_rev, pool));
 
       if (nb->copy_source_checksum)
         {
           svn_checksum_t *checksum;
           SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, copy_root,
                                        nb->copyfrom_path, TRUE, pool));
           if (!svn_checksum_match(nb->copy_source_checksum, checksum))
             return svn_checksum_mismatch_err(nb->copy_source_checksum,
                       checksum, pool,
                       _("Copy source checksum mismatch on copy from '%s'@%ld\n"
                         "to '%s' in rev based on r%ld"),
-                      nb->copyfrom_path, copyfrom_rev, nb->path, rb->rev);
+                      nb->copyfrom_path, nb->copyfrom_rev, nb->path, rb->rev);
         }
 
       SVN_ERR(svn_fs_copy(copy_root, nb->copyfrom_path,
                           rb->txn_root, nb->path, pool));
 
       if (pb->notify_func)
@@ -658,19 +387,12 @@ new_node_record(void **node_baton,
     return svn_error_create(SVN_ERR_STREAM_MALFORMED_DATA, NULL,
                             _("Malformed dumpstream: "
                               "Revision 0 must not contain node records"));
 
   SVN_ERR(make_node_baton(&nb, headers, rb, pool));
 
-  /* If we're skipping this revision, we're done here. */
-  if (rb->skipped)
-    {
-      *node_baton = nb;
-      return SVN_NO_ERROR;
-    }
-
   /* Make sure we have an action we recognize. */
   if (nb->action < svn_node_action_change
         || nb->action > svn_node_action_replace)
       return svn_error_createf(SVN_ERR_STREAM_UNRECOGNIZED_DATA, NULL,
                                _("Unrecognized node-action on node '%s'"),
                                nb->path);
@@ -716,16 +438,12 @@ set_revision_property(void *baton,
                       const svn_string_t *value)
 {
   struct revision_baton *rb = baton;
   struct parse_baton *pb = rb->pb;
   svn_boolean_t is_date = strcmp(name, SVN_PROP_REVISION_DATE) == 0;
 
-  /* If we're skipping this revision, we're done here. */
-  if (rb->skipped)
-    return SVN_NO_ERROR;
-
   /* If we're ignoring dates, and this is one, we're done here. */
   if (is_date && pb->ignore_dates)
     return SVN_NO_ERROR;
 
   if (rb->rev > 0)
     {
@@ -755,141 +473,33 @@ set_revision_property(void *baton,
     }
 
   return SVN_NO_ERROR;
 }
 
 
-/* Adjust mergeinfo:
- *   - normalize line endings (if all CRLF, change to LF; but error if mixed);
- *   - adjust revision numbers (see renumber_mergeinfo_revs());
- *   - adjust paths (see prefix_mergeinfo_paths()).
- */
-static svn_error_t *
-adjust_mergeinfo_property(struct revision_baton *rb,
-                          svn_string_t **new_value_p,
-                          const svn_string_t *old_value,
-                          apr_pool_t *result_pool)
-{
-  struct parse_baton *pb = rb->pb;
-  svn_string_t prop_val = *old_value;
-
-  /* Tolerate mergeinfo with "\r\n" line endings because some
-     dumpstream sources might contain as much.  If so normalize
-     the line endings to '\n' and notify that we have made this
-     correction. */
-  if (strstr(prop_val.data, "\r"))
-    {
-      const char *prop_eol_normalized;
-
-      SVN_ERR(svn_subst_translate_cstring2(prop_val.data,
-                                           &prop_eol_normalized,
-                                           "\n",  /* translate to LF */
-                                           FALSE, /* no repair */
-                                           NULL,  /* no keywords */
-                                           FALSE, /* no expansion */
-                                           result_pool));
-      prop_val.data = prop_eol_normalized;
-      prop_val.len = strlen(prop_eol_normalized);
-
-      if (pb->notify_func)
-        {
-          /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
-          svn_repos_notify_t *notify
-                  = svn_repos_notify_create(
-                                svn_repos_notify_load_normalized_mergeinfo,
-                                pb->notify_pool);
-
-          pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
-          svn_pool_clear(pb->notify_pool);
-        }
-    }
-
-  /* Renumber mergeinfo as appropriate. */
-  SVN_ERR(renumber_mergeinfo_revs(new_value_p, &prop_val, rb,
-                                  result_pool));
-
-  if (pb->parent_dir)
-    {
-      /* Prefix the merge source paths with PB->parent_dir. */
-      /* ASSUMPTION: All source paths are included in the dump stream. */
-      SVN_ERR(prefix_mergeinfo_paths(new_value_p, *new_value_p,
-                                     pb->parent_dir, result_pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-
 static svn_error_t *
 set_node_property(void *baton,
                   const char *name,
                   const svn_string_t *value)
 {
   struct node_baton *nb = baton;
   struct revision_baton *rb = nb->rb;
   struct parse_baton *pb = rb->pb;
 
-  /* If we're skipping this revision, we're done here. */
-  if (rb->skipped)
-    return SVN_NO_ERROR;
-
-  /* Adjust mergeinfo. If this fails, presumably because the mergeinfo
-     property has an ill-formed value, then we must not fail to load
-     the repository (at least if it's a simple load with no revision
-     offset adjustments, path changes, etc.) so just warn and leave it
-     as it is. */
-  if (strcmp(name, SVN_PROP_MERGEINFO) == 0)
-    {
-      svn_string_t *new_value;
-      svn_error_t *err;
-
-      err = adjust_mergeinfo_property(rb, &new_value, value, nb->pool);
-      if (err)
-        {
-          if (pb->validate_props)
-            {
-              return svn_error_quick_wrap(
-                       err,
-                       _("Invalid svn:mergeinfo value"));
-            }
-          if (pb->notify_func)
-            {
-              svn_repos_notify_t *notify
-                = svn_repos_notify_create(svn_repos_notify_warning,
-                                          pb->notify_pool);
-
-              notify->warning = svn_repos_notify_warning_invalid_mergeinfo;
-              notify->warning_str = _("Invalid svn:mergeinfo value; "
-                                      "leaving unchanged");
-              pb->notify_func(pb->notify_baton, notify, pb->notify_pool);
-              svn_pool_clear(pb->notify_pool);
-            }
-          svn_error_clear(err);
-        }
-      else
-        {
-          value = new_value;
-        }
-    }
-
   return change_node_prop(rb->txn_root, nb->path, name, value,
                           pb->validate_props, nb->pool);
 }
 
 
 static svn_error_t *
 delete_node_property(void *baton,
                      const char *name)
 {
   struct node_baton *nb = baton;
   struct revision_baton *rb = nb->rb;
 
-  /* If we're skipping this revision, we're done here. */
-  if (rb->skipped)
-    return SVN_NO_ERROR;
-
   return change_node_prop(rb->txn_root, nb->path, name, NULL,
                           rb->pb->validate_props, nb->pool);
 }
 
 
 static svn_error_t *
@@ -897,16 +507,12 @@ remove_node_props(void *baton)
 {
   struct node_baton *nb = baton;
   struct revision_baton *rb = nb->rb;
   apr_hash_t *proplist;
   apr_hash_index_t *hi;
 
-  /* If we're skipping this revision, we're done here. */
-  if (rb->skipped)
-    return SVN_NO_ERROR;
-
   SVN_ERR(svn_fs_node_proplist(&proplist,
                                rb->txn_root, nb->path, nb->pool));
 
   for (hi = apr_hash_first(nb->pool, proplist); hi; hi = apr_hash_next(hi))
     {
       const char *key = apr_hash_this_key(hi);
@@ -924,19 +530,12 @@ apply_textdelta(svn_txdelta_window_handl
                 void **handler_baton,
                 void *node_baton)
 {
   struct node_baton *nb = node_baton;
   struct revision_baton *rb = nb->rb;
 
-  /* If we're skipping this revision, we're done here. */
-  if (rb->skipped)
-    {
-      *handler = NULL;
-      return SVN_NO_ERROR;
-    }
-
   return svn_fs_apply_textdelta(handler, handler_baton,
                                 rb->txn_root, nb->path,
                                 svn_checksum_to_cstring(nb->base_checksum,
                                                         nb->pool),
                                 svn_checksum_to_cstring(nb->result_checksum,
                                                         nb->pool),
@@ -948,19 +547,12 @@ static svn_error_t *
 set_fulltext(svn_stream_t **stream,
              void *node_baton)
 {
   struct node_baton *nb = node_baton;
   struct revision_baton *rb = nb->rb;
 
-  /* If we're skipping this revision, we're done here. */
-  if (rb->skipped)
-    {
-      *stream = NULL;
-      return SVN_NO_ERROR;
-    }
-
   return svn_fs_apply_text(stream,
                            rb->txn_root, nb->path,
                            svn_checksum_to_cstring(nb->result_checksum,
                                                    nb->pool),
                            nb->pool);
 }
@@ -970,16 +562,12 @@ static svn_error_t *
 close_node(void *baton)
 {
   struct node_baton *nb = baton;
   struct revision_baton *rb = nb->rb;
   struct parse_baton *pb = rb->pb;
 
-  /* If we're skipping this revision, we're done here. */
-  if (rb->skipped)
-    return SVN_NO_ERROR;
-
   if (pb->notify_func)
     {
       /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
       svn_repos_notify_t *notify = svn_repos_notify_create(
                                             svn_repos_notify_load_node_done,
                                             pb->notify_pool);
@@ -1000,15 +588,14 @@ close_revision(void *baton)
   const char *conflict_msg = NULL;
   svn_revnum_t committed_rev;
   svn_error_t *err;
   const char *txn_name = NULL;
   apr_hash_t *hooks_env;
 
-  /* If we're skipping this revision or it has an invalid revision
-     number, we're done here. */
-  if (rb->skipped || (rb->rev <= 0))
+  /* If this revision has an invalid revision number, we're done here. */
+  if (rb->rev <= 0)
     return SVN_NO_ERROR;
 
   /* If the dumpstream doesn't have an 'svn:date' property and we
      aren't ignoring the dates in the dumpstream altogether, remove
      any 'svn:date' revision property that was set by FS layer when
      the TXN was created.  */
@@ -1080,36 +667,12 @@ close_revision(void *baton)
                                               rb->pool)))
         return svn_error_create
           (SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err,
            _("Commit succeeded, but post-commit hook failed"));
     }
 
-  /* After a successful commit, must record the dump-rev -> in-repos-rev
-     mapping, so that copyfrom instructions in the dump file can look up the
-     correct repository revision to copy from. */
-  set_revision_mapping(pb->rev_map, rb->rev, committed_rev);
-
-  /* If the incoming dump stream has non-contiguous revisions (e.g. from
-     using svndumpfilter --drop-empty-revs without --renumber-revs) then
-     we must account for the missing gaps in PB->REV_MAP.  Otherwise we
-     might not be able to map all mergeinfo source revisions to the correct
-     revisions in the target repos. */
-  if ((pb->last_rev_mapped != SVN_INVALID_REVNUM)
-      && (rb->rev != pb->last_rev_mapped + 1))
-    {
-      svn_revnum_t i;
-
-      for (i = pb->last_rev_mapped + 1; i < rb->rev; i++)
-        {
-          set_revision_mapping(pb->rev_map, i, pb->last_rev_mapped);
-        }
-    }
-
-  /* Update our "last revision mapped". */
-  pb->last_rev_mapped = rb->rev;
-
   /* Deltify the predecessors of paths changed in this revision. */
   SVN_ERR(svn_fs_deltify_revision(pb->fs, committed_rev, rb->pool));
 
   if (pb->notify_func)
     {
       /* ### TODO: Use proper scratch pool instead of pb->notify_pool */
@@ -1132,41 +695,28 @@ close_revision(void *baton)
 /*----------------------------------------------------------------------*/
 
 /** The public routines **/
 
 
 svn_error_t *
-svn_repos_get_fs_build_parser5(const svn_repos_parse_fns3_t **callbacks,
-                               void **parse_baton,
-                               svn_repos_t *repos,
-                               svn_revnum_t start_rev,
-                               svn_revnum_t end_rev,
-                               svn_boolean_t use_history,
-                               svn_boolean_t validate_props,
-                               enum svn_repos_load_uuid uuid_action,
-                               const char *parent_dir,
-                               svn_boolean_t use_pre_commit_hook,
-                               svn_boolean_t use_post_commit_hook,
-                               svn_boolean_t ignore_dates,
-                               svn_repos_notify_func_t notify_func,
-                               void *notify_baton,
-                               apr_pool_t *pool)
+svn_repos_get_dumpfile_committer(const svn_repos_parse_fns3_t **committer,
+                                 void **committer_baton,
+                                 svn_repos_t *repos,
+                                 svn_boolean_t use_history,
+                                 svn_boolean_t validate_props,
+                                 enum svn_repos_load_uuid uuid_action,
+                                 svn_boolean_t use_pre_commit_hook,
+                                 svn_boolean_t use_post_commit_hook,
+                                 svn_boolean_t ignore_dates,
+                                 svn_repos_notify_func_t notify_func,
+                                 void *notify_baton,
+                                 apr_pool_t *pool)
 {
   svn_repos_parse_fns3_t *parser = apr_pcalloc(pool, sizeof(*parser));
   struct parse_baton *pb = apr_pcalloc(pool, sizeof(*pb));
 
-  if (parent_dir)
-    parent_dir = svn_relpath_canonicalize(parent_dir, pool);
-
-  SVN_ERR_ASSERT((SVN_IS_VALID_REVNUM(start_rev) &&
-                  SVN_IS_VALID_REVNUM(end_rev))
-                 || ((! SVN_IS_VALID_REVNUM(start_rev)) &&
-                     (! SVN_IS_VALID_REVNUM(end_rev))));
-  if (SVN_IS_VALID_REVNUM(start_rev))
-    SVN_ERR_ASSERT(start_rev <= end_rev);
-
   parser->magic_header_record = magic_header_record;
   parser->uuid_record = uuid_record;
   parser->new_revision_record = new_revision_record;
   parser->new_node_record = new_node_record;
   parser->set_revision_property = set_revision_property;
   parser->set_node_property = set_node_property;
@@ -1181,29 +731,66 @@ svn_repos_get_fs_build_parser5(const svn
   pb->fs = svn_repos_fs(repos);
   pb->use_history = use_history;
   pb->validate_props = validate_props;
   pb->notify_func = notify_func;
   pb->notify_baton = notify_baton;
   pb->uuid_action = uuid_action;
-  pb->parent_dir = parent_dir;
-  pb->pool = pool;
   pb->notify_pool = svn_pool_create(pool);
-  pb->rev_map = apr_hash_make(pool);
-  pb->oldest_dumpstream_rev = SVN_INVALID_REVNUM;
-  pb->last_rev_mapped = SVN_INVALID_REVNUM;
-  pb->start_rev = start_rev;
-  pb->end_rev = end_rev;
   pb->use_pre_commit_hook = use_pre_commit_hook;
   pb->use_post_commit_hook = use_post_commit_hook;
   pb->ignore_dates = ignore_dates;
 
-  *callbacks = parser;
-  *parse_baton = pb;
+  *committer = parser;
+  *committer_baton = pb;
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_repos_get_fs_build_parser5(const svn_repos_parse_fns3_t **parser,
+                               void **parse_baton,
+                               svn_repos_t *repos,
+                               svn_revnum_t start_rev,
+                               svn_revnum_t end_rev,
+                               svn_boolean_t use_history,
+                               svn_boolean_t validate_props,
+                               enum svn_repos_load_uuid uuid_action,
+                               const char *parent_dir,
+                               svn_boolean_t use_pre_commit_hook,
+                               svn_boolean_t use_post_commit_hook,
+                               svn_boolean_t ignore_dates,
+                               svn_repos_notify_func_t notify_func,
+                               void *notify_baton,
+                               apr_pool_t *pool)
+{
+  const svn_repos_parse_fns3_t *committer;
+  void *committer_baton;
+  svn_boolean_t reject_invalid_mergeinfo = validate_props;
+
+  SVN_ERR(svn_repos_get_dumpfile_committer(&committer, &committer_baton,
+                                           repos,
+                                           use_history,
+                                           validate_props,
+                                           uuid_action,
+                                           use_pre_commit_hook,
+                                           use_post_commit_hook,
+                                           ignore_dates,
+                                           notify_func,
+                                           notify_baton,
+                                           pool));
+
+  SVN_ERR(svn_repos_get_dumpfile_load_filter(parser, parse_baton,
+                                             committer, committer_baton,
+                                             repos,
+                                             start_rev, end_rev,
+                                             reject_invalid_mergeinfo,
+                                             parent_dir,
+                                             notify_func,
+                                             notify_baton,
+                                             pool));
+  return SVN_NO_ERROR;
+}
 
 svn_error_t *
 svn_repos_load_fs5(svn_repos_t *repos,
                    svn_stream_t *dumpstream,
                    svn_revnum_t start_rev,
                    svn_revnum_t end_rev,
