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.