Enable svndumpfilter to handle dump format v3.  Ensure that the output
format matches the input format, instead of just outputting the newest
supported format.

In the dump-parser, don't forget the format version number after
reading it.  Instead, send the format version number to a (new)
callback.  At long last, the callbacks give you the entire dump!

In svndumpfilter, the new callback prints the output format string.
In the other users, it doesn't do anything useful yet.

In svndumpfilter, treat a text-delta as text, without applying it.
AFAICT, this requires a special case in the dump-parser, so we have
another reason to rev that API.

### This patch doesn't handle property-deletions yet.

### This patch doesn't make dump-parsing any stricter.

* subversion/include/svn_repos.h
  (SVN_REPOS_DUMPFILE_FORMAT_VERSION_WITH_DELTAS) New #define.
  (svn_repos_parse_fns3_t,
   svn_repos_parse_dumpstream3): New API version supporting a
   version-number callback and optional delta pass-through.
  (svn_repos_get_fs_build_parser4): Use svn_repos_parse_fns3_t.
  (svn_repos_parse_fns2_t,
   svn_repos_parse_dumpstream2): Deprecate.
  (svn_repos_parser_fns2_t,
   svn_repos_get_fs_build_parser3,
   svn_repos_get_fs_build_parser3): Move these below the newly-
   deprecated struct declaration.

* subversion/libsvn_repos/load.c
  (parse_property_block): Use svn_repos_parse_fns3_t.
  (parse_text_block): Use svn_repos_parse_fns3_t.  Allow handling
   deltas as text.
  (parse_format_version): Move some extra format version-checking
   to here...
  (svn_repos_parse_dumpstream3): ...from here.  Call the new version
   callback.  Pass the new deltas_are_text flag to parse_text_block.

* subversion/svndumpfilter/main.c
  (magic_header_record,
   delete_node_property): New callbacks.

* subversion/tests/cmdline/svndumpfilter_tests.py
  (dumpfilter_with_deltas): New test.
  (test_list): Add new test.

* subversion/libsvn_repos/load-fs-vtable.c
  (magic_header_record): New callback.
  (svn_repos_get_fs_build_parser4,
   svn_repos_load_fs4): Use the new API.

* subversion/svnrdump/load_editor.c
  (parse_baton): New 'format_version' member.  No practical use.
  (magic_header_record): New callback.
  (svn_rdump__load_dumpstream): Use the new API.

* subversion/libsvn_repos/deprecated.c
  (fns3_from_fns2,
   svn_repos_parse_dumpstream2): Backward compatibility boilerplate.


Index: subversion/include/svn_repos.h
===================================================================
--- subversion/include/svn_repos.h	(revision 1222407)
+++ subversion/include/svn_repos.h	(working copy)
@@ -2384,7 +2384,8 @@ svn_repos_node_from_baton(void *edit_baton);
 
 /* The RFC822-style headers in our dumpfile format. */
 #define SVN_REPOS_DUMPFILE_MAGIC_HEADER            "SVN-fs-dump-format-version"
-#define SVN_REPOS_DUMPFILE_FORMAT_VERSION           3
+#define SVN_REPOS_DUMPFILE_FORMAT_VERSION             3
+#define SVN_REPOS_DUMPFILE_FORMAT_VERSION_WITH_DELTAS 3
 #define SVN_REPOS_DUMPFILE_UUID                      "UUID"
 #define SVN_REPOS_DUMPFILE_CONTENT_LENGTH            "Content-length"
 
@@ -2686,12 +2687,28 @@ svn_repos_load_fs(svn_repos_t *repos,
 
 
 /**
- * A vtable that is driven by svn_repos_parse_dumpstream2().
+ * A vtable that is driven by svn_repos_parse_dumpstream3().
  *
- * @since New in 1.1.
+ * @since New in 1.8.
  */
-typedef struct svn_repos_parse_fns2_t
+typedef struct svn_repos_parse_fns3_t
 {
+  /** The parser has discovered a new "magic header" record within the
+   * parsing session represented by @a parse_baton.  The dump-format
+   * version number is @a version.
+   */
+  svn_error_t *(*magic_header_record)(int version,
+                              void *parse_baton,
+                              apr_pool_t *pool);
+
+  /** The parser has discovered a new uuid record within the parsing
+   * session represented by @a parse_baton.  The uuid's value is
+   * @a uuid, and it is allocated in @a pool.
+   */
+  svn_error_t *(*uuid_record)(const char *uuid,
+                              void *parse_baton,
+                              apr_pool_t *pool);
+
   /** The parser has discovered a new revision record within the
    * parsing session represented by @a parse_baton.  All the headers are
    * placed in @a headers (allocated in @a pool), which maps <tt>const
@@ -2704,14 +2721,6 @@ svn_repos_load_fs(svn_repos_t *repos,
                                       void *parse_baton,
                                       apr_pool_t *pool);
 
-  /** The parser has discovered a new uuid record within the parsing
-   * session represented by @a parse_baton.  The uuid's value is
-   * @a uuid, and it is allocated in @a pool.
-   */
-  svn_error_t *(*uuid_record)(const char *uuid,
-                              void *parse_baton,
-                              apr_pool_t *pool);
-
   /** The parser has discovered a new node record within the current
    * revision represented by @a revision_baton.  All the headers are
    * placed in @a headers (as with @c new_revision_record), allocated in
@@ -2774,23 +2783,29 @@ svn_repos_load_fs(svn_repos_t *repos,
    */
   svn_error_t *(*close_revision)(void *revision_baton);
 
-} svn_repos_parse_fns2_t;
+} svn_repos_parse_fns3_t;
 
-/** @deprecated Provided for backward compatibility with the 1.2 API. */
-typedef svn_repos_parse_fns2_t svn_repos_parser_fns2_t;
 
-
 /**
  * Read and parse dumpfile-formatted @a stream, calling callbacks in
  * @a parse_fns/@a parse_baton, and using @a pool for allocations.
  *
+ * If @a deltas_are_text is @c TRUE, treat text-deltas as text without
+ * parsing them.  This is useful when filtering a dump stream without
+ * loading it.  Otherwise, text-deltas are parsed by the appropriate
+ * member of parse_fns.
+ * 
  * If @a cancel_func is not @c NULL, it is called periodically with
  * @a cancel_baton as argument to see if the client wishes to cancel
  * the dump.
  *
  * This parser has built-in knowledge of the dumpfile format, but only
- * in a general sense:
+ * in a limited sense:
  *
+ *    * it recognizes the "magic" format-version header.
+ *
+ *    * it recognizes the UUID header.
+ *
  *    * it recognizes revision and node records by looking for either
  *      a REVISION_NUMBER or NODE_PATH headers.
  *
@@ -2803,11 +2818,12 @@ svn_repos_load_fs(svn_repos_t *repos,
  * This is enough knowledge to make it easy on vtable implementors,
  * but still allow expansion of the format:  most headers are ignored.
  *
- * @since New in 1.1.
+ * @since New in 1.8.
  */
 svn_error_t *
-svn_repos_parse_dumpstream2(svn_stream_t *stream,
-                            const svn_repos_parse_fns2_t *parse_fns,
+svn_repos_parse_dumpstream3(svn_stream_t *stream,
+                            svn_boolean_t deltas_are_text,
+                            const svn_repos_parse_fns3_t *parse_fns,
                             void *parse_baton,
                             svn_cancel_func_t cancel_func,
                             void *cancel_baton,
@@ -2849,7 +2865,7 @@ svn_error_t *
  * @since New in 1.8.
  */
 svn_error_t *
-svn_repos_get_fs_build_parser4(const svn_repos_parse_fns2_t **parser,
+svn_repos_get_fs_build_parser4(const svn_repos_parse_fns3_t **parser,
                                void **parse_baton,
                                svn_repos_t *repos,
                                svn_revnum_t start_rev,
@@ -2862,45 +2878,60 @@ svn_error_t *
                                void *notify_baton,
                                apr_pool_t *pool);
 
+
 /**
- * Similar to svn_repos_get_fs_build_parser4(), but with @a start_rev
- * and @a end_rev always passed as #SVN_INVALID_REVNUM.
+ * A vtable that is driven by svn_repos_parse_dumpstream2().
+ * Similar to #svn_repos_parse_fns3_t except that it lacks
+ * the delete_node_property and apply_textdelta callbacks.
  *
  * @deprecated Provided for backward compatibility with the 1.7 API.
- * @since New in 1.7.
  */
-SVN_DEPRECATED
-svn_error_t *
-svn_repos_get_fs_build_parser3(const svn_repos_parse_fns2_t **parser,
-                               void **parse_baton,
-                               svn_repos_t *repos,
-                               svn_boolean_t use_history,
-                               svn_boolean_t validate_props,
-                               enum svn_repos_load_uuid uuid_action,
-                               const char *parent_dir,
-                               svn_repos_notify_func_t notify_func,
-                               void *notify_baton,
-                               apr_pool_t *pool);
+typedef struct svn_repos_parse_fns2_t
+{
+  /** Same as #svn_repos_parse_fns3_t.new_revision_record. */
+  svn_error_t *(*new_revision_record)(void **revision_baton,
+                                      apr_hash_t *headers,
+                                      void *parse_baton,
+                                      apr_pool_t *pool);
+  /** Same as #svn_repos_parse_fns3_t.uuid_record. */
+  svn_error_t *(*uuid_record)(const char *uuid,
+                              void *parse_baton,
+                              apr_pool_t *pool);
+  /** Same as #svn_repos_parse_fns3_t.new_node_record. */
+  svn_error_t *(*new_node_record)(void **node_baton,
+                                  apr_hash_t *headers,
+                                  void *revision_baton,
+                                  apr_pool_t *pool);
+  /** Same as #svn_repos_parse_fns3_t.set_revision_property. */
+  svn_error_t *(*set_revision_property)(void *revision_baton,
+                                        const char *name,
+                                        const svn_string_t *value);
+  /** Same as #svn_repos_parse_fns3_t.set_node_property. */
+  svn_error_t *(*set_node_property)(void *node_baton,
+                                    const char *name,
+                                    const svn_string_t *value);
+  /** Same as #svn_repos_parse_fns3_t.delete_node_property. */
+  svn_error_t *(*delete_node_property)(void *node_baton,
+                                       const char *name);
+  /** Same as #svn_repos_parse_fns3_t.remove_node_props. */
+  svn_error_t *(*remove_node_props)(void *node_baton);
+  /** Same as #svn_repos_parse_fns3_t.set_fulltext. */
+  svn_error_t *(*set_fulltext)(svn_stream_t **stream,
+                               void *node_baton);
+  /** Same as #svn_repos_parse_fns3_t.apply_textdelta. */
+  svn_error_t *(*apply_textdelta)(svn_txdelta_window_handler_t *handler,
+                                  void **handler_baton,
+                                  void *node_baton);
+  /** Same as #svn_repos_parse_fns3_t.close_node. */
+  svn_error_t *(*close_node)(void *node_baton);
+  /** Same as #svn_repos_parse_fns3_t.close_revision. */
+  svn_error_t *(*close_revision)(void *revision_baton);
+} svn_repos_parse_fns2_t;
 
-/**
- * Similar to svn_repos_get_fs_build_parser3(), but with @a outstream
- * in place if a #svn_repos_notify_func_t and baton and with
- * @a validate_props always FALSE.
- *
- * @since New in 1.1.
- * @deprecated Provided for backward compatibility with the 1.6 API.
- */
-SVN_DEPRECATED
-svn_error_t *
-svn_repos_get_fs_build_parser2(const svn_repos_parse_fns2_t **parser,
-                               void **parse_baton,
-                               svn_repos_t *repos,
-                               svn_boolean_t use_history,
-                               enum svn_repos_load_uuid uuid_action,
-                               svn_stream_t *outstream,
-                               const char *parent_dir,
-                               apr_pool_t *pool);
+/** @deprecated Provided for backward compatibility with the 1.2 API. */
+typedef svn_repos_parse_fns2_t svn_repos_parser_fns2_t;
 
+
 /**
  * A vtable that is driven by svn_repos_parse_dumpstream().
  * Similar to #svn_repos_parse_fns2_t except that it lacks
@@ -2945,6 +2976,22 @@ typedef struct svn_repos_parse_fns_t
 
 
 /**
+ * Similar to svn_repos_parse_dumpstream3(), but uses the more limited
+ * #svn_repos_parser_fns2_t vtable type, and @a deltas_are_text is
+ * always @c FALSE.
+ *
+ * @deprecated Provided for backward compatibility with the 1.7 API.
+ */
+SVN_DEPRECATED
+svn_error_t *
+svn_repos_parse_dumpstream2(svn_stream_t *stream,
+                            const svn_repos_parser_fns2_t *parse_fns,
+                            void *parse_baton,
+                            svn_cancel_func_t cancel_func,
+                            void *cancel_baton,
+                            apr_pool_t *pool);
+
+/**
  * Similar to svn_repos_parse_dumpstream2(), but uses the more limited
  * #svn_repos_parser_fns_t vtable type.
  *
@@ -2959,8 +3006,46 @@ svn_repos_parse_dumpstream(svn_stream_t *stream,
                            void *cancel_baton,
                            apr_pool_t *pool);
 
+/**
+ * Similar to svn_repos_get_fs_build_parser4(), but with @a start_rev
+ * and @a end_rev always passed as #SVN_INVALID_REVNUM.
+ *
+ * @deprecated Provided for backward compatibility with the 1.7 API.
+ * @since New in 1.7.
+ */
+SVN_DEPRECATED
+svn_error_t *
+svn_repos_get_fs_build_parser3(const svn_repos_parse_fns2_t **parser,
+                               void **parse_baton,
+                               svn_repos_t *repos,
+                               svn_boolean_t use_history,
+                               svn_boolean_t validate_props,
+                               enum svn_repos_load_uuid uuid_action,
+                               const char *parent_dir,
+                               svn_repos_notify_func_t notify_func,
+                               void *notify_baton,
+                               apr_pool_t *pool);
 
 /**
+ * Similar to svn_repos_get_fs_build_parser3(), but with @a outstream
+ * in place if a #svn_repos_notify_func_t and baton and with
+ * @a validate_props always FALSE.
+ *
+ * @since New in 1.1.
+ * @deprecated Provided for backward compatibility with the 1.6 API.
+ */
+SVN_DEPRECATED
+svn_error_t *
+svn_repos_get_fs_build_parser2(const svn_repos_parse_fns2_t **parser,
+                               void **parse_baton,
+                               svn_repos_t *repos,
+                               svn_boolean_t use_history,
+                               enum svn_repos_load_uuid uuid_action,
+                               svn_stream_t *outstream,
+                               const char *parent_dir,
+                               apr_pool_t *pool);
+
+/**
  * Similar to svn_repos_get_fs_build_parser2(), but yields the more
  * limited svn_repos_parser_fns_t vtable type.
  *
Index: subversion/libsvn_repos/load.c
===================================================================
--- subversion/libsvn_repos/load.c	(revision 1222407)
+++ subversion/libsvn_repos/load.c	(working copy)
@@ -181,7 +181,7 @@ read_key_or_val(char **pbuf,
 static svn_error_t *
 parse_property_block(svn_stream_t *stream,
                      svn_filesize_t content_length,
-                     const svn_repos_parse_fns2_t *parse_fns,
+                     const svn_repos_parse_fns3_t *parse_fns,
                      void *record_baton,
                      void *parse_baton,
                      svn_boolean_t is_node,
@@ -299,7 +299,8 @@ static svn_error_t *
 parse_text_block(svn_stream_t *stream,
                  svn_filesize_t content_length,
                  svn_boolean_t is_delta,
-                 const svn_repos_parse_fns2_t *parse_fns,
+                 svn_boolean_t deltas_are_text,
+                 const svn_repos_parse_fns3_t *parse_fns,
                  void *record_baton,
                  char *buffer,
                  apr_size_t buflen,
@@ -308,7 +309,7 @@ parse_text_block(svn_stream_t *stream,
   svn_stream_t *text_stream = NULL;
   apr_size_t num_to_read, rlen, wlen;
 
-  if (is_delta)
+  if (is_delta && !deltas_are_text)
     {
       svn_txdelta_window_handler_t wh;
       void *whb;
@@ -373,7 +374,9 @@ parse_text_block(svn_stream_t *stream,
 /* Parse VERSIONSTRING and verify that we support the dumpfile format
    version number, setting *VERSION appropriately. */
 static svn_error_t *
-parse_format_version(const char *versionstring, int *version)
+parse_format_version(int *version,
+                     const char *versionstring,
+                     svn_boolean_t has_delta_support)
 {
   static const int magic_len = sizeof(SVN_REPOS_DUMPFILE_MAGIC_HEADER) - 1;
   const char *p = strchr(versionstring, ':');
@@ -406,8 +417,9 @@ static svn_error_t *
 /** The public routines **/
 
 svn_error_t *
-svn_repos_parse_dumpstream2(svn_stream_t *stream,
-                            const svn_repos_parse_fns2_t *parse_fns,
+svn_repos_parse_dumpstream3(svn_stream_t *stream,
+                            svn_boolean_t deltas_are_text,
+                            const svn_repos_parse_fns3_t *parse_fns,
                             void *parse_baton,
                             svn_cancel_func_t cancel_func,
                             void *cancel_baton,
@@ -428,17 +440,14 @@ svn_error_t *
     return stream_ran_dry();
 
   /* The first two lines of the stream are the dumpfile-format version
-     number, and a blank line. */
-  SVN_ERR(parse_format_version(linebuf->data, &version));
+     number, and a blank line.  To preserve backward compatibility,
+     don't assume the existence of newer parser-vtable functions. */
+  SVN_ERR(parse_format_version(&version,
+                               linebuf->data,
+                               parse_fns->delete_node_property != NULL));
+  if (parse_fns->magic_header_record != NULL)
+    SVN_ERR(parse_fns->magic_header_record(version, parse_baton, pool));
 
-  /* If we were called from svn_repos_parse_dumpstream(), the
-     callbacks to handle delta contents will be NULL, so we have to
-     reject dumpfiles with the current version. */
-  if (version == SVN_REPOS_DUMPFILE_FORMAT_VERSION
-      && (!parse_fns->delete_node_property || !parse_fns->apply_textdelta))
-    return svn_error_createf(SVN_ERR_STREAM_MALFORMED_DATA, NULL,
-                             _("Unsupported dumpfile version: %d"), version);
-
   /* A dumpfile "record" is defined to be a header-block of
      rfc822-style headers, possibly followed by a content-block.
 
@@ -527,14 +536,6 @@ svn_error_t *
         {
           SVN_ERR(parse_fns->uuid_record(value, parse_baton, pool));
         }
-      /* Or perhaps a dumpfile format? */
-      else if ((value = apr_hash_get(headers,
-                                     SVN_REPOS_DUMPFILE_MAGIC_HEADER,
-                                     APR_HASH_KEY_STRING)))
-        {
-          /* ### someday, switch modes of operation here. */
-          SVN_ERR(svn_cstring_atoi(&version, value));
-        }
       /* Or is this bogosity?! */
       else
         {
@@ -596,6 +597,7 @@ svn_error_t *
           SVN_ERR(parse_text_block(stream,
                                    svn__atoui64(text_cl),
                                    is_delta,
+                                   deltas_are_text,
                                    parse_fns,
                                    found_node ? node_baton : rev_baton,
                                    buffer,
@@ -631,6 +633,7 @@ svn_error_t *
             SVN_ERR(parse_text_block(stream,
                                      cl_value,
                                      FALSE,
+                                     FALSE,
                                      parse_fns,
                                      found_node ? node_baton : rev_baton,
                                      buffer,
Index: subversion/svndumpfilter/main.c
===================================================================
--- subversion/svndumpfilter/main.c	(revision 1222407)
+++ subversion/svndumpfilter/main.c	(working copy)
@@ -252,6 +252,19 @@ struct node_baton_t
 
 /* Filtering vtable members */
 
+/* File-format stamp. */
+static svn_error_t *
+magic_header_record(int version, void *parse_baton, apr_pool_t *pool)
+{
+  struct parse_baton_t *pb = parse_baton;
+  SVN_ERR(svn_stream_printf(pb->out_stream, pool,
+                            SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n",
+                            version));
+
+  return SVN_NO_ERROR;
+}
+
+
 /* New revision: set up revision_baton, decide if we skip it. */
 static svn_error_t *
 new_revision_record(void **revision_baton,
@@ -807,6 +820,16 @@ set_node_property(void *node_baton,
 
 
 static svn_error_t *
+delete_node_property(void *node_baton, const char *name)
+{
+  /* ### TODO: Error if nb->pb->format_version predates deltas. */
+
+  return svn_error_create(SVN_ERR_STREAM_MALFORMED_DATA, NULL,
+                          "your prop delete is incomplete");
+}
+
+
+static svn_error_t *
 remove_node_props(void *node_baton)
 {
   struct node_baton_t *nb = node_baton;
@@ -874,17 +897,18 @@ close_revision(void *revision_baton)
 
 
 /* Filtering vtable */
-svn_repos_parse_fns2_t filtering_vtable =
+svn_repos_parse_fns3_t filtering_vtable =
   {
+    magic_header_record,
+    uuid_record,
     new_revision_record,
-    uuid_record,
     new_node_record,
     set_revision_property,
     set_node_property,
-    NULL,
+    delete_node_property,
     remove_node_props,
     set_fulltext,
-    NULL,
+    NULL, /* We'll just copy svndiff text without parsing it. */
     close_node,
     close_revision
   };
@@ -1028,17 +1052,6 @@ parse_baton_initialize(struct parse_baton_t **pb,
   baton->last_live_revision = SVN_INVALID_REVNUM;
   baton->oldest_original_rev = SVN_INVALID_REVNUM;
 
-  /* This is non-ideal: We should pass through the version of the
-   * input dumpstream.  However, our API currently doesn't allow that.
-   * Hardcoding version 2 is acceptable because:
-   *   - We currently do not accept version 3 or greater.
-   *   - Dumpstream version 1 is so ancient as to be ignorable
-   *     (0.17.x and earlier)
-   */
-  SVN_ERR(svn_stream_printf(baton->out_stream, pool,
-                            SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n",
-                            2));
-
   *pb = baton;
   return SVN_NO_ERROR;
 }
@@ -1140,8 +1153,8 @@ do_filter(apr_getopt_t *os,
     }
 
   SVN_ERR(parse_baton_initialize(&pb, opt_state, do_exclude, pool));
-  SVN_ERR(svn_repos_parse_dumpstream2(pb->in_stream, &filtering_vtable, pb,
-                                      NULL, NULL, pool));
+  SVN_ERR(svn_repos_parse_dumpstream3(pb->in_stream, TRUE, &filtering_vtable,
+                                      pb, NULL, NULL, pool));
 
   /* The rest of this is just reporting.  If we aren't reporting, get
      outta here. */
Index: subversion/tests/cmdline/svndumpfilter_tests.py
===================================================================
--- subversion/tests/cmdline/svndumpfilter_tests.py	(revision 1222407)
+++ subversion/tests/cmdline/svndumpfilter_tests.py	(working copy)
@@ -33,8 +33,9 @@ import tempfile
 import svntest
 from svntest.verify import SVNExpectedStdout, SVNExpectedStderr
 
-# Get some helper routines from svnadmin_tests
+# Get some helper routines
 from svnadmin_tests import load_and_verify_dumpstream, test_create
+from svntest.main import run_svn, run_svnadmin
 
 # (abbreviation)
 Skip = svntest.testcase.Skip_deco
@@ -596,6 +597,39 @@ def dropped_but_not_renumbered_empty_revs(sbox):
                                      'propget', 'svn:mergeinfo', '-R',
                                      sbox.repo_url)
 
+def dumpfilter_with_deltas(sbox):
+  "svndumpfilter with deltas option"
+  # Test support for dump format v3 (i.e., created with --deltas).
+  test_create(sbox)
+
+  # Load v2 format.
+  v2_file = os.path.join(os.path.dirname(sys.argv[0]),
+                         'svndumpfilter_tests_data',
+                         'greek_tree.dump')
+  with open(v2_file, 'rb') as f:
+    load_and_verify_dumpstream(sbox, None, None, None, f.readlines())
+
+  # Commit a prop delete (a special case for v3).
+
+  # Dump in v3 format.
+  exit_code, v3_dump, errput = run_svnadmin('dump', sbox.repo_dir,
+                                            '--quiet', '--deltas')
+
+  #with open('v3.dump', 'wb') as f:
+  #  f.writelines(v3_dump)
+
+  # Load filtered v3.
+  run_svn(None, 'rm', sbox.repo_url + '/A', '-m', 'clear A for loading')
+  v3_filtered, err = filter_and_return_output(v3_dump, 0, '--quiet',
+                                              'include', 'A')
+
+  #with open('v3-filtered.dump', 'wb') as f:
+  #  f.writelines(v3_filtered)
+
+  load_and_verify_dumpstream(sbox, None, None, None, v3_filtered)
+
+  
+
 ########################################################################
 # Run the tests
 
@@ -608,6 +642,7 @@ test_list = [ None,
               dumpfilter_with_patterns,
               filter_mergeinfo_revs_outside_of_dump_stream,
               dropped_but_not_renumbered_empty_revs,
+              dumpfilter_with_deltas,
               ]
 
 if __name__ == '__main__':
Index: subversion/libsvn_repos/load-fs-vtable.c
===================================================================
--- subversion/libsvn_repos/load-fs-vtable.c	(revision 1222407)
+++ subversion/libsvn_repos/load-fs-vtable.c	(working copy)
@@ -589,6 +589,13 @@ maybe_add_with_history(struct node_baton *nb,
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+magic_header_record(int version,
+                    void *parse_baton,
+                    apr_pool_t *pool)
+{
+  return SVN_NO_ERROR;
+}
 
 static svn_error_t *
 uuid_record(const char *uuid,
@@ -1007,7 +1014,7 @@ close_revision(void *baton)
 
 
 svn_error_t *
-svn_repos_get_fs_build_parser4(const svn_repos_parse_fns2_t **callbacks,
+svn_repos_get_fs_build_parser4(const svn_repos_parse_fns3_t **callbacks,
                                void **parse_baton,
                                svn_repos_t *repos,
                                svn_revnum_t start_rev,
@@ -1020,7 +1027,7 @@ svn_error_t *
                                void *notify_baton,
                                apr_pool_t *pool)
 {
-  svn_repos_parse_fns2_t *parser = apr_pcalloc(pool, sizeof(*parser));
+  svn_repos_parse_fns3_t *parser = apr_pcalloc(pool, sizeof(*parser));
   struct parse_baton *pb = apr_pcalloc(pool, sizeof(*pb));
 
   if (parent_dir)
@@ -1033,9 +1040,10 @@ svn_error_t *
   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->uuid_record = uuid_record;
   parser->set_revision_property = set_revision_property;
   parser->set_node_property = set_node_property;
   parser->remove_node_props = remove_node_props;
@@ -1084,7 +1092,7 @@ svn_repos_load_fs4(svn_repos_t *repos,
                    void *cancel_baton,
                    apr_pool_t *pool)
 {
-  const svn_repos_parse_fns2_t *parser;
+  const svn_repos_parse_fns3_t *parser;
   void *parse_baton;
   struct parse_baton *pb;
 
@@ -1107,6 +1115,6 @@ svn_repos_load_fs4(svn_repos_t *repos,
   pb->use_pre_commit_hook = use_pre_commit_hook;
   pb->use_post_commit_hook = use_post_commit_hook;
 
-  return svn_repos_parse_dumpstream2(dumpstream, parser, parse_baton,
+  return svn_repos_parse_dumpstream3(dumpstream, FALSE, parser, parse_baton,
                                      cancel_func, cancel_baton, pool);
 }
Index: subversion/svnrdump/load_editor.c
===================================================================
--- subversion/svnrdump/load_editor.c	(revision 1222407)
+++ subversion/svnrdump/load_editor.c	(working copy)
@@ -91,6 +91,9 @@ struct parse_baton
   /* The oldest revision loaded from the dump stream, or
      SVN_INVALID_REVNUM if none have been loaded. */
   svn_revnum_t oldest_dumpstream_rev;
+
+  /* Version of the input dump stream.  ### Does svnrdump need to care? */
+  int format_version;
 };
 
 /**
@@ -469,6 +472,17 @@ new_revision_record(void **revision_baton,
 }
 
 static svn_error_t *
+magic_header_record(int version,
+            void *parse_baton,
+            apr_pool_t *pool)
+{
+  struct parse_baton *pb;
+  pb = parse_baton;
+  pb->format_version = version;
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
 uuid_record(const char *uuid,
             void *parse_baton,
             apr_pool_t *pool)
@@ -1029,7 +1043,7 @@ svn_rdump__load_dumpstream(svn_stream_t *stream,
                            void *cancel_baton,
                            apr_pool_t *pool)
 {
-  svn_repos_parse_fns2_t *parser;
+  svn_repos_parse_fns3_t *parser;
   struct parse_baton *parse_baton;
   const svn_string_t *lock_string;
   svn_boolean_t be_atomic;
@@ -1046,8 +1060,9 @@ svn_rdump__load_dumpstream(svn_stream_t *stream,
                                            session_url, pool));
 
   parser = apr_pcalloc(pool, sizeof(*parser));
+  parser->magic_header_record = magic_header_record;
+  parser->uuid_record = uuid_record;
   parser->new_revision_record = new_revision_record;
-  parser->uuid_record = uuid_record;
   parser->new_node_record = new_node_record;
   parser->set_revision_property = set_revision_property;
   parser->set_node_property = set_node_property;
@@ -1068,7 +1083,7 @@ svn_rdump__load_dumpstream(svn_stream_t *stream,
   parse_baton->last_rev_mapped = SVN_INVALID_REVNUM;
   parse_baton->oldest_dumpstream_rev = SVN_INVALID_REVNUM;
 
-  err = svn_repos_parse_dumpstream2(stream, parser, parse_baton,
+  err = svn_repos_parse_dumpstream3(stream, FALSE, parser, parse_baton,
                                     cancel_func, cancel_baton, pool);
 
   /* If all goes well, or if we're cancelled cleanly, don't leave a
Index: subversion/libsvn_repos/deprecated.c
===================================================================
--- subversion/libsvn_repos/deprecated.c	(revision 1222407)
+++ subversion/libsvn_repos/deprecated.c	(working copy)
@@ -795,7 +795,43 @@ fns2_from_fns(const svn_repos_parser_fns_t *fns,
   return fns2;
 }
 
+static svn_repos_parse_fns3_t *
+fns3_from_fns2(const svn_repos_parser_fns2_t *fns,
+               apr_pool_t *pool)
+{
+  svn_repos_parse_fns3_t *fns3;
+
+  fns3 = apr_palloc(pool, sizeof(*fns3));
+  fns3->new_revision_record = fns->new_revision_record;
+  fns3->uuid_record = fns->uuid_record;
+  fns3->new_node_record = fns->new_node_record;
+  fns3->set_revision_property = fns->set_revision_property;
+  fns3->set_node_property = fns->set_node_property;
+  fns3->remove_node_props = fns->remove_node_props;
+  fns3->set_fulltext = fns->set_fulltext;
+  fns3->close_node = fns->close_node;
+  fns3->close_revision = fns->close_revision;
+  fns3->delete_node_property = NULL;
+  fns3->apply_textdelta = NULL;
+  fns3->magic_header_record = NULL;
+  return fns3;
+}
+
 svn_error_t *
+svn_repos_parse_dumpstream2(svn_stream_t *stream,
+                            const svn_repos_parser_fns2_t *parse_fns,
+                            void *parse_baton,
+                            svn_cancel_func_t cancel_func,
+                            void *cancel_baton,
+                            apr_pool_t *pool)
+{
+  svn_repos_parse_fns3_t *fns3 = fns3_from_fns2(parse_fns, pool);
+
+  return svn_repos_parse_dumpstream3(stream, FALSE, fns3, parse_baton,
+                                     cancel_func, cancel_baton, pool);
+}
+
+svn_error_t *
 svn_repos_parse_dumpstream(svn_stream_t *stream,
                            const svn_repos_parser_fns_t *parse_fns,
                            void *parse_baton,
