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.