The dump and load code contains lots of duplication between svnadmin, 
svndumpfilter and svnrdump. We know that code duplication leads to 
inconsistency and bugs. See, for example, issue #4476 "Mergeinfo containing r0 
makes svnsync and dump and load fail". We should not have allowed this 
duplication to happen, but we did, and eventually if we are to avoid further 
decay we need to refactor it to remove the duplication.

Our dump/load modules are currently organized like this:

-----------------------------------------------------------------------|
svnadmin load
                [        svn_repos_load_fs5()         ]
  dumpstream -> [parser] => [ get_fs_build_parser5()  ] -> repos-API

svnrdump load
                [    svn_rdump__load_dumpstream()     ]
  dumpstream -> [parser] => [   rdump-loader-vtable   ] -> RA-API

svndumpfilter
                [             do_filter()             ]
  dumpstream -> [parser] => [    filtering_vtable     ] -> dumpstream

svnadmin dump
                [        svn_repos_dump_fs3()         ]
  repos-API  -> [svn_repos_replay2  ] => [dump_editor1]-> dumpstream

svnrdump dump
                [         replay_revisions()          ]
  RA-API     -> [svn_ra_replay_range] => [dump_editor2] -> dumpstream
-----------------------------------------------------------------------|

We already use three instances of the same "parser" 
(svn_repos_parse_dumpstream3) -- Hooray! -- but a lot of duplication still 
remains.

First, we need to separate the filtering logic from the input/output 
conversion. We can do it like this (the double arrow "=>" represents the 
dumpstream API "svn_repos_parse_fns3_t"):

-----------------------------------------------------------------------|

svnadmin load
  dumpstream -> [parser] => [filter1] => [repos-writer ] -> repos-API

svnrdump load
  dumpstream -> [parser] => [filter2] => [RA-writer    ] -> RA-API

svndumpfilter
  dumpstream -> [parser] => [filter3] => [stream-writer] -> dumpstream

svnadmin dump
  repos-API  -> [repos-] => [filter4] => [stream-writer] -> dumpstream
                [reader]

svnrdump dump
  RA-API     -> [RA-   ] => [filter5] => [stream-writer] -> dumpstream
                [reader]
-----------------------------------------------------------------------|


Before we can deduplicate the filtering, we need to analyze what operations 
each tool supports. Most or all of these are optional. Here is my initial tally.

svnadmin load:
  * rev range
  * renumber revs (and adjust copyfrom+mergeinfo)
  * parent dir (and adjust copyfrom+mergeinfo)
  * set/keep repository UUID
  * run/bypass pre- and post-commit hooks
  * run/bypass prop validation
  * keep/ignore original commit date stamps

svnrdump load:
  * renumber revs (and adjust copyfrom+mergeinfo)
  * parent dir (and adjust copyfrom+mergeinfo)
  * skip specified revprops

svndumpfilter:
  * include/exclude paths
  * drop empty revs
  * renumber revs
  * skip missing merge sources
  * rewrite/preserve revprops in empty revs

svnadmin dump:
  * rev range
  * incremental/full first revision
  * deltas

svnrdump dump:
  * rev range
  * incremental/full first revision


Then decide which operations are generic filtering and which are specific to an 
input or output module.

Generic filtering:
  * rev range (but more efficient if implemented in an input module)
  * renumber revs
  * parent dir
  * skip specified revprops
  * include/exclude paths
  * drop empty revs
  * skip missing merge sources?
  * rewrite/preserve revprops in empty revs

Input/output module functionality:
  * rev range
    - all readers
  * set/keep repository UUID
    - repos-writer
  * run/bypass hooks
    - repos-writer
  * run/bypass prop validation
    - repos-writer
  * keep/ignore date stamps
    - repos-writer
  * incremental/full first revision
    - repos/RA readers
  * deltas
    - repos-reader?

So far, I have a working patch (attached) for "svnadmin load", splitting the 
filtering from the repos-writer. I am not sure whether to commit it as is; it's 
not perfect in a couple of details but it's pretty close. I intend to continue.

The biggest difficulty will likely be in deduplicating things like renumbering 
revisions, where the details of the functionality are already slightly 
different among the three tools and not very well planned nor documented. Most 
of the other parts I expect to be relatively straightforward.

Any thoughts or encouragement?

- Julian
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,

Reply via email to