Here's a stab at implementing 'svnrdump dump --incremental'.

It does what 'svn checkout' does to get the first revision --- namely,
calls svn_ra_do_update2() with start_empty=TRUE.

TODO: it passes send_copyfrom_args=TRUE to svn_ra_do_update2(), so it
still has to go by itself and requests fulltexts (or, copyfrom fulltexts
and txdeltas against those) for files that have been copied.  Currently,
for a cp-with-changes, it dumps nothing (neither fulltext nor delta).

TODO: dump revprops for the first revision. (haven't tested this)

Daniel
Index: subversion/svnrdump/svnrdump.c
===================================================================
--- subversion/svnrdump/svnrdump.c	(revision 1001291)
+++ subversion/svnrdump/svnrdump.c	(working copy)
@@ -50,6 +50,7 @@ enum svn_svnrdump__longopt_t
     opt_auth_nocache,
     opt_version,
     opt_config_option,
+    opt_incremental,
   };
 
 static const svn_opt_subcommand_desc2_t svnrdump__cmd_table[] =
@@ -95,6 +96,7 @@ static const apr_getopt_option_t svnrdump__options
                        "For example:\n"
                        "                             "
                        "    servers:global:http-library=serf")},
+    {"incremental",   opt_incremental, 0, N_("dump incrementally")},
     {0,                  0,   0, 0}
   };
 
@@ -113,6 +115,7 @@ struct replay_baton {
 typedef struct opt_baton_t {
   svn_ra_session_t *session;
   const char *url;
+  svn_revnum_t incremental;
   svn_revnum_t start_revision;
   svn_revnum_t end_revision;
   svn_boolean_t quiet;
@@ -229,8 +232,10 @@ open_connection(svn_ra_session_t **session,
 static svn_error_t *
 replay_revisions(svn_ra_session_t *session, const char *url,
                  svn_revnum_t start_revision, svn_revnum_t end_revision,
+                 svn_boolean_t incremental,
                  svn_boolean_t quiet, apr_pool_t *pool)
 {
+  const char *session_root_repos_relpath = NULL;
   const svn_delta_editor_t *dump_editor;
   struct replay_baton *replay_baton;
   void *dump_baton;
@@ -239,8 +244,19 @@ replay_revisions(svn_ra_session_t *session, const
 
   SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool));
 
-  SVN_ERR(get_dump_editor(&dump_editor, &dump_baton, stdout_stream, pool));
+  /* Maybe get the session root. */
+  if (incremental)
+    {
+      const char *session_root_url;
+      SVN_ERR(svn_ra_get_session_url(session, &session_root_url, pool));
+      SVN_ERR(svn_ra_get_path_relative_to_root(session,
+                                               &session_root_repos_relpath,
+                                               session_root_url, pool));
+    }
 
+  SVN_ERR(get_dump_editor(&dump_editor, &dump_baton, stdout_stream,
+                          incremental, session_root_repos_relpath, pool));
+
   replay_baton = apr_pcalloc(pool, sizeof(*replay_baton));
   replay_baton->editor = dump_editor;
   replay_baton->edit_baton = dump_baton;
@@ -292,7 +308,25 @@ replay_revisions(svn_ra_session_t *session, const
 
       start_revision++;
     }
+  else if (incremental)
+    {
+      svn_revnum_t revision = start_revision;
+      const svn_ra_reporter3_t *reporter;
+      void *reporter_baton;
 
+      /* Simulate a checkout.  */
+      SVN_ERR(svn_ra_do_update2(session, &reporter, &reporter_baton, revision,
+                                "", svn_depth_infinity, TRUE /* XXX need to get the delta source ourselves */,
+                                dump_editor, dump_baton, pool));
+      SVN_ERR(reporter->set_path(reporter_baton, "", revision,
+                                 svn_depth_infinity, TRUE /* start_empty */,
+                                 NULL /* lock_token */, pool));
+      /* Calls the dump editor. */
+      SVN_ERR(reporter->finish_report(reporter_baton, pool));
+
+      start_revision++;
+    }
+
   SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,
                               0, TRUE, replay_revstart, replay_revend,
                               replay_baton, pool));
@@ -385,6 +419,7 @@ dump_cmd(apr_getopt_t *os, void *baton, apr_pool_t
   opt_baton_t *opt_baton = baton;
   SVN_ERR(replay_revisions(opt_baton->session, opt_baton->url,
                            opt_baton->start_revision, opt_baton->end_revision,
+                           opt_baton->incremental,
                            opt_baton->quiet, pool));
   return SVN_NO_ERROR;
 }
@@ -503,6 +538,9 @@ main(int argc, const char **argv)
         case opt_non_interactive:
           non_interactive = TRUE;
           break;
+        case opt_incremental:
+          opt_baton->incremental = TRUE;
+          break;
         case opt_config_option:
           if (!config_options)
               config_options =
Index: subversion/svnrdump/dump_editor.h
===================================================================
--- subversion/svnrdump/dump_editor.h	(revision 1001291)
+++ subversion/svnrdump/dump_editor.h	(working copy)
@@ -55,6 +55,9 @@ struct dir_baton
      they're full paths, because that's what the editor driver gives
      us, although they're all really within this directory. */
   apr_hash_t *deleted_entries;
+
+  /* Set if we're during an incremental (and, hence, full) dump. */
+  svn_boolean_t incremental;
 };
 
 /**
@@ -79,12 +82,15 @@ struct handler_baton
 
 /**
  * Get a dump editor @a editor along with a @a edit_baton allocated in
- * @a pool. The editor will write output to @a stream.
+ * @a pool. The editor will write output to @a stream.  If @a incremental,
+ * then the first revision will be dumped in full (not as deltas).
  */
 svn_error_t *
 get_dump_editor(const svn_delta_editor_t **editor,
                 void **edit_baton,
                 svn_stream_t *stream,
+                svn_boolean_t incremental,
+                const char *session_root_repos_relpath,
                 apr_pool_t *pool);
 
 /**
Index: subversion/svnrdump/dump_editor.c
===================================================================
--- subversion/svnrdump/dump_editor.c	(revision 1001291)
+++ subversion/svnrdump/dump_editor.c	(working copy)
@@ -45,6 +45,13 @@ struct dump_edit_baton {
   /* The output stream we write the dumpfile to */
   svn_stream_t *stream;
 
+  /* Dump the first revision in full, not as deltas. */
+  /* XXX propagate this to the callbacks */
+  svn_boolean_t incremental;
+
+  /* Where the RA session is rooted. */
+  const char *session_root_repos_relpath;
+
   /* Pool for per-edit-session allocations */
   apr_pool_t *pool;
 
@@ -149,6 +156,8 @@ make_dir_baton(const char *path,
   new_db->added = added;
   new_db->written_out = FALSE;
   new_db->deleted_entries = apr_hash_make(eb->pool);
+  if (pb)
+    new_db->incremental = pb->incremental;
 
   return new_db;
 }
@@ -224,6 +233,16 @@ dump_newlines(struct dump_edit_baton *eb,
   return SVN_NO_ERROR;
 }
 
+/* Maybe fix a session-relative path into a repository-root-relative path. */
+static const char *
+increment_path(struct dir_baton *db, const char *path, apr_pool_t *pool)
+{
+  if (db->incremental)
+    return svn_relpath_join(db->eb->session_root_repos_relpath, path, pool);
+  else
+    return path;
+}
+
 /*
  * Write out a node record for PATH of type KIND under EB->FS_ROOT.
  * ACTION describes what is happening to the node (see enum
@@ -367,6 +386,7 @@ open_root(void *edit_baton,
           void **root_baton)
 {
   struct dump_edit_baton *eb = edit_baton;
+  struct dir_baton *rb;
   /* Allocate a special pool for the edit_baton to avoid pool
      lifetime issues */
 
@@ -375,8 +395,11 @@ open_root(void *edit_baton,
   eb->deleted_props = apr_hash_make(eb->pool);
   eb->propstring = svn_stringbuf_create("", eb->pool);
 
-  *root_baton = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
+  rb = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
                                edit_baton, NULL, FALSE, pool);
+  rb->incremental = eb->incremental;
+  eb->incremental = FALSE;
+  *root_baton = rb;
   LDR_DBG(("open_root %p\n", *root_baton));
 
   return SVN_NO_ERROR;
@@ -435,7 +458,7 @@ add_directory(const char *path,
   is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
 
   /* Dump the node */
-  SVN_ERR(dump_node(pb->eb, path,
+  SVN_ERR(dump_node(pb->eb, increment_path(pb, path, pool),
                     svn_node_dir,
                     val ? svn_node_action_replace : svn_node_action_add,
                     is_copy,
@@ -518,7 +541,8 @@ close_directory(void *dir_baton,
       apr_hash_this(hi, &key, NULL, NULL);
       path = key;
 
-      SVN_ERR(dump_node(db->eb, path, svn_node_unknown, svn_node_action_delete,
+      SVN_ERR(dump_node(db->eb, increment_path(db, path, pool),
+                        svn_node_unknown, svn_node_action_delete,
                         FALSE, NULL, SVN_INVALID_REVNUM, pool));
     }
 
@@ -554,7 +578,7 @@ add_file(const char *path,
   is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
 
   /* Dump the node. */
-  SVN_ERR(dump_node(pb->eb, path,
+  SVN_ERR(dump_node(pb->eb, increment_path(pb, path, pool),
                     svn_node_file,
                     val ? svn_node_action_replace : svn_node_action_add,
                     is_copy,
@@ -601,7 +625,8 @@ open_file(const char *path,
       copyfrom_rev = pb->copyfrom_rev;
     }
 
-  SVN_ERR(dump_node(pb->eb, path, svn_node_file, svn_node_action_change,
+  SVN_ERR(dump_node(pb->eb, increment_path(pb, path, pool),
+                    svn_node_file, svn_node_action_change,
                     FALSE, copyfrom_path, copyfrom_rev, pool));
 
   /* Build a nice file baton to pass to change_file_prop and
@@ -639,9 +664,9 @@ change_dir_prop(void *parent_baton,
          props. If it not, dump the node itself before dumping the
          props. */
 
-      SVN_ERR(dump_node(db->eb, db->abspath, svn_node_dir,
-                        svn_node_action_change, FALSE, db->copyfrom_path,
-                        db->copyfrom_rev, pool));
+      SVN_ERR(dump_node(db->eb, increment_path(db, db->abspath, pool),
+                        svn_node_dir, svn_node_action_change, FALSE,
+                       db->copyfrom_path, db->copyfrom_rev, pool));
       db->written_out = TRUE;
     }
 
@@ -849,6 +874,8 @@ svn_error_t *
 get_dump_editor(const svn_delta_editor_t **editor,
                 void **edit_baton,
                 svn_stream_t *stream,
+                svn_boolean_t incremental,
+                const char *session_root_repos_relpath,
                 apr_pool_t *pool)
 {
   struct dump_edit_baton *eb;
@@ -856,6 +883,8 @@ get_dump_editor(const svn_delta_editor_t **editor,
 
   eb = apr_pcalloc(pool, sizeof(struct dump_edit_baton));
   eb->stream = stream;
+  eb->incremental = incremental;
+  eb->session_root_repos_relpath = session_root_repos_relpath;
 
   de = svn_delta_default_editor(pool);
   de->open_root = open_root;
Implement 'svnrdump dump --incremental'.

**** WORK IN PROGRESS --- DO NOT COMMIT ****

* subversion/svnrdump/svnrdump.c
  (opt_incremental, svnrdump__options, opt_baton_t, main):
    Grow --incremental option.
  (replay_revisions):
    Grow INCREMENTAL boolean parameter.
    Use svn_ra_do_update2() to dump the first revision when incremental.
    Update call to get_dump_editor().

* subversion/svnrdump/dump_editor.h
  (dir_baton):  New INCREMENTAL member.
  (get_dump_editor):  New INCREMENTAL and SESSION_ROOT_REPOS_RELPATH parameters.

* subversion/svnrdump/dump_editor.c
  (dump_edit_baton):  New INCREMENTAL and SESSION_ROOT_REPOS_RELPATH members.
  (make_dir_baton):  Inherit INCREMENTAL from parent (if any).
  (increment_path):  New helper.
  (open_root):  Populate INCREMENTAL member of baton.
  (add_directory, close_directory, add_file, open_file, change_file_prop):
    Increment paths when calling dump_node().
  (get_dump_editor):  New INCREMENTAL and SESSION_ROOT_REPOS_RELPATH parameters.

Reply via email to