/* Helper for svn_repos_dump_fs. Write a revision record of REV in FS to writable STREAM, using POOL. */ static svn_error_t * write_revision_record(svn_stream_t *stream, svn_fs_t *fs, svn_revnum_t rev, apr_pool_t *pool) { apr_size_t len; apr_hash_t *props; svn_stringbuf_t *encoded_prophash; apr_time_t timetemp; svn_string_t *datevalue; svn_stream_t *propstream; /* Read the revision props even if we're aren't going to dump them for verification purposes */ SVN_ERR(svn_fs_revision_proplist(&props, fs, rev, pool)); /* Run revision date properties through the time conversion to canonicalize them. */ /* ### Remove this when it is no longer needed for sure. */ datevalue = apr_hash_get(props, SVN_PROP_REVISION_DATE, APR_HASH_KEY_STRING); if (datevalue) { SVN_ERR(svn_time_from_cstring(&timetemp, datevalue->data, pool)); datevalue = svn_string_create(svn_time_to_cstring(timetemp, pool), pool); apr_hash_set(props, SVN_PROP_REVISION_DATE, APR_HASH_KEY_STRING, datevalue); } encoded_prophash = svn_stringbuf_create_ensure(0, pool); propstream = svn_stream_from_stringbuf(encoded_prophash, pool); SVN_ERR(svn_hash_write2(props, propstream, "PROPS-END", pool)); SVN_ERR(svn_stream_close(propstream)); /* ### someday write a revision-content-checksum */ SVN_ERR(svn_stream_printf(stream, pool, SVN_REPOS_DUMPFILE_REVISION_NUMBER ": %ld\n", rev)); SVN_ERR(svn_stream_printf(stream, pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH ": %" APR_SIZE_T_FMT "\n", encoded_prophash->len)); /* Write out a regular Content-length header for the benefit of non-Subversion RFC-822 parsers. */ SVN_ERR(svn_stream_printf(stream, pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH ": %" APR_SIZE_T_FMT "\n\n", encoded_prophash->len)); len = encoded_prophash->len; SVN_ERR(svn_stream_write(stream, encoded_prophash->data, &len)); len = 1; return svn_stream_write(stream, "\n", &len); }
/* This implements the svn_client_blame_receiver3_t interface. */ static svn_error_t * blame_receiver(void *baton, svn_revnum_t start_revnum, svn_revnum_t end_revnum, apr_int64_t line_no, svn_revnum_t revision, apr_hash_t *rev_props, svn_revnum_t merged_revision, apr_hash_t *merged_rev_props, const char *merged_path, const char *line, svn_boolean_t local_change, apr_pool_t *pool) { svn_cl__opt_state_t *opt_state = ((blame_baton_t *) baton)->opt_state; svn_stream_t *out = ((blame_baton_t *)baton)->out; svn_boolean_t use_merged = FALSE; if (opt_state->use_merge_history) { /* Choose which revision to use. If they aren't equal, prefer the earliest revision. Since we do a forward blame, we want to the first revision which put the line in its current state, so we use the earliest revision. If we ever switch to a backward blame algorithm, we may need to adjust this. */ if (merged_revision < revision) { SVN_ERR(svn_stream_printf(out, pool, "G ")); use_merged = TRUE; } else SVN_ERR(svn_stream_printf(out, pool, " ")); } if (use_merged) SVN_ERR(print_line_info(out, merged_revision, svn_prop_get_value(merged_rev_props, SVN_PROP_REVISION_AUTHOR), svn_prop_get_value(merged_rev_props, SVN_PROP_REVISION_DATE), merged_path, opt_state->verbose, end_revnum, pool)); else SVN_ERR(print_line_info(out, revision, svn_prop_get_value(rev_props, SVN_PROP_REVISION_AUTHOR), svn_prop_get_value(rev_props, SVN_PROP_REVISION_DATE), NULL, opt_state->verbose, end_revnum, pool)); return svn_stream_printf(out, pool, "%s%s", line, APR_EOL_STR); }
/* Print dumpstream-formatted information about REVISION. * Implements the `svn_ra_replay_revstart_callback_t' interface. */ static svn_error_t * replay_revstart(svn_revnum_t revision, void *replay_baton, const svn_delta_editor_t **editor, void **edit_baton, apr_hash_t *rev_props, apr_pool_t *pool) { struct replay_baton *rb = replay_baton; apr_hash_t *normal_props; svn_stringbuf_t *propstring; svn_stream_t *stdout_stream; svn_stream_t *revprop_stream; SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); /* Revision-number: 19 */ SVN_ERR(svn_stream_printf(stdout_stream, pool, SVN_REPOS_DUMPFILE_REVISION_NUMBER ": %ld\n", revision)); SVN_ERR(svn_rdump__normalize_props(&normal_props, rev_props, pool)); propstring = svn_stringbuf_create_ensure(0, pool); revprop_stream = svn_stream_from_stringbuf(propstring, pool); SVN_ERR(svn_hash_write2(normal_props, revprop_stream, "PROPS-END", pool)); SVN_ERR(svn_stream_close(revprop_stream)); /* Prop-content-length: 13 */ SVN_ERR(svn_stream_printf(stdout_stream, pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH ": %" APR_SIZE_T_FMT "\n", propstring->len)); /* Content-length: 29 */ SVN_ERR(svn_stream_printf(stdout_stream, pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH ": %" APR_SIZE_T_FMT "\n\n", propstring->len)); /* Property data. */ SVN_ERR(svn_stream_write(stdout_stream, propstring->data, &(propstring->len))); SVN_ERR(svn_stream_printf(stdout_stream, pool, "\n")); SVN_ERR(svn_stream_close(stdout_stream)); SVN_ERR(svn_rdump__get_dump_editor(editor, edit_baton, revision, rb->stdout_stream, rb->extra_ra_session, check_cancel, NULL, pool)); return SVN_NO_ERROR; }
static svn_error_t * add_directory(const char *path, void *parent_baton, const char *copyfrom_path, svn_revnum_t copyfrom_revision, apr_pool_t *pool, void **child_baton) { struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; struct dir_baton *b = apr_palloc(pool, sizeof(*b)); SVN_ERR(write_indent(eb, pool)); SVN_ERR(svn_stream_printf(eb->out, pool, "add_directory : '%s' [from '%s':%ld]\n", path, copyfrom_path, copyfrom_revision)); eb->indent_level++; SVN_ERR(eb->wrapped_editor->add_directory(path, pb->wrapped_dir_baton, copyfrom_path, copyfrom_revision, pool, &b->wrapped_dir_baton)); b->edit_baton = eb; *child_baton = b; return SVN_NO_ERROR; }
static svn_error_t * open_root(void *edit_baton, svn_revnum_t base_revision, apr_pool_t *pool, void **root_baton) { struct edit_baton *eb = edit_baton; struct dir_baton *dir_baton = apr_palloc(pool, sizeof(*dir_baton)); SVN_ERR(write_indent(eb, pool)); SVN_ERR(svn_stream_printf(eb->out, pool, "open_root : %ld\n", base_revision)); eb->indent_level++; SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton, base_revision, pool, &dir_baton->wrapped_dir_baton)); dir_baton->edit_baton = edit_baton; *root_baton = dir_baton; return SVN_NO_ERROR; }
static svn_error_t * open_file(const char *path, void *parent_baton, svn_revnum_t base_revision, apr_pool_t *pool, void **file_baton) { struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; struct file_baton *fb = apr_palloc(pool, sizeof(*fb)); SVN_ERR(write_indent(eb, pool)); SVN_ERR(svn_stream_printf(eb->out, pool, "open_file : '%s':%ld\n", path, base_revision)); eb->indent_level++; SVN_ERR(eb->wrapped_editor->open_file(path, pb->wrapped_dir_baton, base_revision, pool, &fb->wrapped_file_baton)); fb->edit_baton = eb; *file_baton = fb; return SVN_NO_ERROR; }
static svn_error_t * write_indent(struct edit_baton *eb, apr_pool_t *pool) { int i; for (i = 0; i < eb->indent_level; ++i) SVN_ERR(svn_stream_printf(eb->out, pool, " ")); return SVN_NO_ERROR; }
static svn_error_t * delete_path(void *report_baton, const char *path, apr_pool_t *pool) { struct report_baton *rb = report_baton; SVN_ERR(svn_stream_printf(rb->out, pool, "delete_path(%s)\n", path)); SVN_ERR(rb->wrapped_reporter->delete_path(rb->wrapped_report_baton, path, pool)); return SVN_NO_ERROR; }
static void print_usage(svn_stream_t *ostream, const char *progname, apr_pool_t *pool) { svn_error_clear(svn_stream_printf(ostream, pool, "Usage: %s [OPTIONS] <file1> <file2>\n" "\n" "Display the differences between <file1> and <file2> in unified diff\n" "format. OPTIONS are diff extensions as described by 'svn help diff'.\n" "Use '--' alone to indicate that no more options follow.\n", progname)); }
static svn_error_t * close_edit(void *edit_baton, apr_pool_t *pool) { struct edit_baton *eb = edit_baton; SVN_ERR(write_indent(eb, pool)); SVN_ERR(svn_stream_printf(eb->out, pool, "close_edit\n")); SVN_ERR(eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool)); return SVN_NO_ERROR; }
/* Print a revision record header for REVISION to STDOUT_STREAM. Use * SESSION to contact the repository for revision properties and * such. */ static svn_error_t * dump_revision_header(svn_ra_session_t *session, svn_stream_t *stdout_stream, svn_revnum_t revision, apr_pool_t *pool) { apr_hash_t *prophash; svn_stringbuf_t *propstring; svn_stream_t *propstream; SVN_ERR(svn_stream_printf(stdout_stream, pool, SVN_REPOS_DUMPFILE_REVISION_NUMBER ": %ld\n", revision)); prophash = apr_hash_make(pool); propstring = svn_stringbuf_create_empty(pool); SVN_ERR(svn_ra_rev_proplist(session, revision, &prophash, pool)); propstream = svn_stream_from_stringbuf(propstring, pool); SVN_ERR(svn_hash_write2(prophash, propstream, "PROPS-END", pool)); SVN_ERR(svn_stream_close(propstream)); /* Property-content-length: 14; Content-length: 14 */ SVN_ERR(svn_stream_printf(stdout_stream, pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH ": %" APR_SIZE_T_FMT "\n", propstring->len)); SVN_ERR(svn_stream_printf(stdout_stream, pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH ": %" APR_SIZE_T_FMT "\n\n", propstring->len)); /* The properties */ SVN_ERR(svn_stream_write(stdout_stream, propstring->data, &(propstring->len))); SVN_ERR(svn_stream_printf(stdout_stream, pool, "\n")); return SVN_NO_ERROR; }
static svn_error_t * set_target_revision(void *edit_baton, svn_revnum_t target_revision, apr_pool_t *pool) { struct edit_baton *eb = edit_baton; SVN_ERR(write_indent(eb, pool)); SVN_ERR(svn_stream_printf(eb->out, pool, "set_target_revision : %ld\n", target_revision)); return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton, target_revision, pool); }
static svn_error_t * absent_file(const char *path, void *file_baton, apr_pool_t *pool) { struct file_baton *fb = file_baton; struct edit_baton *eb = fb->edit_baton; SVN_ERR(write_indent(eb, pool)); SVN_ERR(svn_stream_printf(eb->out, pool, "absent_file : %s\n", path)); SVN_ERR(eb->wrapped_editor->absent_file(path, fb->wrapped_file_baton, pool)); return SVN_NO_ERROR; }
static svn_error_t * close_directory(void *dir_baton, apr_pool_t *pool) { struct dir_baton *db = dir_baton; struct edit_baton *eb = db->edit_baton; eb->indent_level--; SVN_ERR(write_indent(eb, pool)); SVN_ERR(svn_stream_printf(eb->out, pool, "close_directory\n")); SVN_ERR(eb->wrapped_editor->close_directory(db->wrapped_dir_baton, pool)); return SVN_NO_ERROR; }
static svn_error_t * absent_directory(const char *path, void *dir_baton, apr_pool_t *pool) { struct dir_baton *db = dir_baton; struct edit_baton *eb = db->edit_baton; SVN_ERR(write_indent(eb, pool)); SVN_ERR(svn_stream_printf(eb->out, pool, "absent_directory : %s\n", path)); SVN_ERR(eb->wrapped_editor->absent_directory(path, db->wrapped_dir_baton, pool)); return SVN_NO_ERROR; }
static svn_error_t * set_path(void *report_baton, const char *path, svn_revnum_t revision, svn_depth_t depth, svn_boolean_t start_empty, const char *lock_token, apr_pool_t *pool) { struct report_baton *rb = report_baton; SVN_ERR(svn_stream_printf(rb->out, pool, "set_path(%s, %ld, %s, %s, %s)\n", path, revision, svn_depth_to_word(depth), BOOLEAN_TO_WORD(start_empty), lock_token)); SVN_ERR(rb->wrapped_reporter->set_path(rb->wrapped_report_baton, path, revision, depth, start_empty, lock_token, pool)); return SVN_NO_ERROR; }
static svn_error_t * delete_entry(const char *path, svn_revnum_t base_revision, void *parent_baton, apr_pool_t *pool) { struct dir_baton *pb = parent_baton; struct edit_baton *eb = pb->edit_baton; SVN_ERR(write_indent(eb, pool)); SVN_ERR(svn_stream_printf(eb->out, pool, "delete_entry : %s:%ld\n", path, base_revision)); return eb->wrapped_editor->delete_entry(path, base_revision, pb->wrapped_dir_baton, pool); }
static svn_error_t * close_file(void *file_baton, const char *text_checksum, apr_pool_t *pool) { struct file_baton *fb = file_baton; struct edit_baton *eb = fb->edit_baton; eb->indent_level--; SVN_ERR(write_indent(eb, pool)); SVN_ERR(svn_stream_printf(eb->out, pool, "close_file : %s\n", text_checksum)); SVN_ERR(eb->wrapped_editor->close_file(fb->wrapped_file_baton, text_checksum, pool)); return SVN_NO_ERROR; }
static svn_error_t * change_dir_prop(void *dir_baton, const char *name, const svn_string_t *value, apr_pool_t *pool) { struct dir_baton *db = dir_baton; struct edit_baton *eb = db->edit_baton; SVN_ERR(write_indent(eb, pool)); SVN_ERR(svn_stream_printf(eb->out, pool, "change_dir_prop : %s\n", name)); SVN_ERR(eb->wrapped_editor->change_dir_prop(db->wrapped_dir_baton, name, value, pool)); return SVN_NO_ERROR; }
int main(int argc, char *argv[]) { apr_pool_t *pool; svn_stream_t *ostream; int rc = 0; svn_error_t *svn_err; apr_initialize(); pool = svn_pool_create(NULL); svn_err = svn_stream_for_stdout(&ostream, pool); if (svn_err) { svn_handle_error2(svn_err, stdout, FALSE, "diff4-test: "); rc = 2; } else if (argc == 5) { svn_err = do_diff4(ostream, argv[2], argv[1], argv[3], argv[4], pool); if (svn_err != NULL) { svn_handle_error2(svn_err, stdout, FALSE, "diff4-test: "); rc = 2; } } else { svn_error_clear(svn_stream_printf (ostream, pool, "Usage: %s <mine> <older> <yours> <ancestor>\n", argv[0])); rc = 2; } apr_terminate(); return rc; }
static svn_error_t * apply_textdelta(void *file_baton, const char *base_checksum, apr_pool_t *pool, svn_txdelta_window_handler_t *handler, void **handler_baton) { struct file_baton *fb = file_baton; struct edit_baton *eb = fb->edit_baton; SVN_ERR(write_indent(eb, pool)); SVN_ERR(svn_stream_printf(eb->out, pool, "apply_textdelta : %s\n", base_checksum)); SVN_ERR(eb->wrapped_editor->apply_textdelta(fb->wrapped_file_baton, base_checksum, pool, handler, handler_baton)); return SVN_NO_ERROR; }
/* Implements svn_hash_write2 and svn_hash_write_incremental. */ static svn_error_t * hash_write(apr_hash_t *hash, apr_hash_t *oldhash, svn_stream_t *stream, const char *terminator, apr_pool_t *pool) { apr_pool_t *subpool; apr_size_t len; apr_array_header_t *list; int i; subpool = svn_pool_create(pool); list = svn_sort__hash(hash, svn_sort_compare_items_lexically, pool); for (i = 0; i < list->nelts; i++) { svn_sort__item_t *item = &APR_ARRAY_IDX(list, i, svn_sort__item_t); svn_string_t *valstr = item->value; svn_pool_clear(subpool); /* Don't output entries equal to the ones in oldhash, if present. */ if (oldhash) { svn_string_t *oldstr = apr_hash_get(oldhash, item->key, item->klen); if (oldstr && svn_string_compare(valstr, oldstr)) continue; } /* Write it out. */ SVN_ERR(svn_stream_printf(stream, subpool, "K %" APR_SSIZE_T_FMT "\n%s\n" "V %" APR_SIZE_T_FMT "\n", item->klen, (const char *) item->key, valstr->len)); len = valstr->len; SVN_ERR(svn_stream_write(stream, valstr->data, &len)); SVN_ERR(svn_stream_printf(stream, subpool, "\n")); } if (oldhash) { /* Output a deletion entry for each property in oldhash but not hash. */ list = svn_sort__hash(oldhash, svn_sort_compare_items_lexically, pool); for (i = 0; i < list->nelts; i++) { svn_sort__item_t *item = &APR_ARRAY_IDX(list, i, svn_sort__item_t); svn_pool_clear(subpool); /* If it's not present in the new hash, write out a D entry. */ if (! apr_hash_get(hash, item->key, item->klen)) SVN_ERR(svn_stream_printf(stream, subpool, "D %" APR_SSIZE_T_FMT "\n%s\n", item->klen, (const char *) item->key)); } } if (terminator) SVN_ERR(svn_stream_printf(stream, subpool, "%s\n", terminator)); svn_pool_destroy(subpool); return SVN_NO_ERROR; }
/* Replay revisions START_REVISION thru END_REVISION (inclusive) of * the repository located at URL, using callbacks which generate * Subversion repository dumpstreams describing the changes made in * those revisions. If QUIET is set, don't generate progress * messages. */ static svn_error_t * replay_revisions(svn_ra_session_t *session, svn_ra_session_t *extra_ra_session, const char *url, svn_revnum_t start_revision, svn_revnum_t end_revision, svn_boolean_t quiet, svn_boolean_t incremental, apr_pool_t *pool) { struct replay_baton *replay_baton; const char *uuid; svn_stream_t *stdout_stream; SVN_ERR(svn_stream_for_stdout(&stdout_stream, pool)); replay_baton = apr_pcalloc(pool, sizeof(*replay_baton)); replay_baton->stdout_stream = stdout_stream; replay_baton->extra_ra_session = extra_ra_session; replay_baton->quiet = quiet; /* Write the magic header and UUID */ SVN_ERR(svn_stream_printf(stdout_stream, pool, SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n", SVN_REPOS_DUMPFILE_FORMAT_VERSION)); SVN_ERR(svn_ra_get_uuid2(session, &uuid, pool)); SVN_ERR(svn_stream_printf(stdout_stream, pool, SVN_REPOS_DUMPFILE_UUID ": %s\n\n", uuid)); /* Fake revision 0 if necessary */ if (start_revision == 0) { SVN_ERR(dump_revision_header(session, stdout_stream, start_revision, pool)); /* Revision 0 has no tree changes, so we're done. */ if (! quiet) SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n", start_revision)); start_revision++; /* If our first revision is 0, we can treat this as an incremental dump. */ incremental = TRUE; } if (incremental) { SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision, 0, TRUE, replay_revstart, replay_revend, replay_baton, pool)); } else { const svn_ra_reporter3_t *reporter; void *report_baton; const svn_delta_editor_t *dump_editor; void *dump_baton; /* First, we need to dump the start_revision in full. We'll start with a revision record header. */ SVN_ERR(dump_revision_header(session, stdout_stream, start_revision, pool)); /* Then, we'll drive the dump editor with what would look like a full checkout of the repository as it looked in START_REVISION. We do this by manufacturing a basic 'report' to the update reporter, telling it that we have nothing to start with. The delta between nothing and everything-at-REV is, effectively, a full dump of REV. */ SVN_ERR(svn_rdump__get_dump_editor(&dump_editor, &dump_baton, start_revision, stdout_stream, extra_ra_session, check_cancel, NULL, pool)); SVN_ERR(svn_ra_do_update2(session, &reporter, &report_baton, start_revision, "", svn_depth_infinity, FALSE, dump_editor, dump_baton, pool)); SVN_ERR(reporter->set_path(report_baton, "", start_revision, svn_depth_infinity, TRUE, NULL, pool)); SVN_ERR(reporter->finish_report(report_baton, pool)); /* All finished with START_REVISION! */ if (! quiet) SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n", start_revision)); start_revision++; /* Now go pick up additional revisions in the range, if any. */ if (start_revision <= end_revision) SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision, 0, TRUE, replay_revstart, replay_revend, replay_baton, pool)); } SVN_ERR(svn_stream_close(stdout_stream)); return SVN_NO_ERROR; }
/* This helper is the main "meat" of the editor -- it does all the work of writing a node record. Write out a node record for PATH of type KIND under EB->FS_ROOT. ACTION describes what is happening to the node (see enum svn_node_action). Write record to writable EB->STREAM, using EB->BUFFER to write in chunks. If the node was itself copied, IS_COPY is TRUE and the path/revision of the copy source are in CMP_PATH/CMP_REV. If IS_COPY is FALSE, yet CMP_PATH/CMP_REV are valid, this node is part of a copied subtree. */ static svn_error_t * dump_node(struct edit_baton *eb, const char *path, svn_node_kind_t kind, enum svn_node_action action, svn_boolean_t is_copy, const char *cmp_path, svn_revnum_t cmp_rev, apr_pool_t *pool) { svn_stringbuf_t *propstring; svn_filesize_t content_length = 0; apr_size_t len; svn_boolean_t must_dump_text = FALSE, must_dump_props = FALSE; const char *compare_path = path; svn_revnum_t compare_rev = eb->current_rev - 1; svn_fs_root_t *compare_root = NULL; apr_file_t *delta_file = NULL; /* Maybe validate the path. */ if (eb->verify || eb->notify_func) { svn_error_t *err = svn_fs__path_valid(path, pool); if (err) { if (eb->notify_func) { char errbuf[512]; /* ### svn_strerror() magic number */ svn_repos_notify_t *notify; notify = svn_repos_notify_create(svn_repos_notify_warning, pool); notify->warning = svn_repos_notify_warning_invalid_fspath; notify->warning_str = apr_psprintf( pool, _("E%06d: While validating fspath '%s': %s"), err->apr_err, path, svn_err_best_message(err, errbuf, sizeof(errbuf))); eb->notify_func(eb->notify_baton, notify, pool); } /* Return the error in addition to notifying about it. */ if (eb->verify) return svn_error_trace(err); else svn_error_clear(err); } } /* Write out metadata headers for this file node. */ SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", path)); if (kind == svn_node_file) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_KIND ": file\n")); else if (kind == svn_node_dir) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n")); /* Remove leading slashes from copyfrom paths. */ if (cmp_path) cmp_path = svn_relpath_canonicalize(cmp_path, pool); /* Validate the comparison path/rev. */ if (ARE_VALID_COPY_ARGS(cmp_path, cmp_rev)) { compare_path = cmp_path; compare_rev = cmp_rev; } if (action == svn_node_action_change) { SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n")); /* either the text or props changed, or possibly both. */ SVN_ERR(svn_fs_revision_root(&compare_root, svn_fs_root_fs(eb->fs_root), compare_rev, pool)); SVN_ERR(svn_fs_props_changed(&must_dump_props, compare_root, compare_path, eb->fs_root, path, pool)); if (kind == svn_node_file) SVN_ERR(svn_fs_contents_changed(&must_dump_text, compare_root, compare_path, eb->fs_root, path, pool)); } else if (action == svn_node_action_replace) { if (! is_copy) { /* a simple delete+add, implied by a single 'replace' action. */ SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_ACTION ": replace\n")); /* definitely need to dump all content for a replace. */ if (kind == svn_node_file) must_dump_text = TRUE; must_dump_props = TRUE; } else { /* more complex: delete original, then add-with-history. */ /* the path & kind headers have already been printed; just add a delete action, and end the current record.*/ SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n\n")); /* recurse: print an additional add-with-history record. */ SVN_ERR(dump_node(eb, path, kind, svn_node_action_add, is_copy, compare_path, compare_rev, pool)); /* we can leave this routine quietly now, don't need to dump any content; that was already done in the second record. */ must_dump_text = FALSE; must_dump_props = FALSE; } } else if (action == svn_node_action_delete) { SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n")); /* we can leave this routine quietly now, don't need to dump any content. */ must_dump_text = FALSE; must_dump_props = FALSE; } else if (action == svn_node_action_add) { SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n")); if (! is_copy) { /* Dump all contents for a simple 'add'. */ if (kind == svn_node_file) must_dump_text = TRUE; must_dump_props = TRUE; } else { if (!eb->verify && cmp_rev < eb->oldest_dumped_rev && eb->notify_func) { svn_repos_notify_t *notify = svn_repos_notify_create(svn_repos_notify_warning, pool); notify->warning = svn_repos_notify_warning_found_old_reference; notify->warning_str = apr_psprintf( pool, _("Referencing data in revision %ld," " which is older than the oldest" " dumped revision (r%ld). Loading this dump" " into an empty repository" " will fail."), cmp_rev, eb->oldest_dumped_rev); eb->found_old_reference = TRUE; eb->notify_func(eb->notify_baton, notify, pool); } SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV ": %ld\n" SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH ": %s\n", cmp_rev, cmp_path)); SVN_ERR(svn_fs_revision_root(&compare_root, svn_fs_root_fs(eb->fs_root), compare_rev, pool)); /* Need to decide if the copied node had any extra textual or property mods as well. */ SVN_ERR(svn_fs_props_changed(&must_dump_props, compare_root, compare_path, eb->fs_root, path, pool)); if (kind == svn_node_file) { svn_checksum_t *checksum; const char *hex_digest; SVN_ERR(svn_fs_contents_changed(&must_dump_text, compare_root, compare_path, eb->fs_root, path, pool)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, compare_root, compare_path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_MD5 ": %s\n", hex_digest)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, compare_root, compare_path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_SHA1 ": %s\n", hex_digest)); } } } if ((! must_dump_text) && (! must_dump_props)) { /* If we're not supposed to dump text or props, so be it, we can just go home. However, if either one needs to be dumped, then our dumpstream format demands that at a *minimum*, we see a lone "PROPS-END" as a divider between text and props content within the content-block. */ len = 2; return svn_stream_write(eb->stream, "\n\n", &len); /* ### needed? */ } /*** Start prepping content to dump... ***/ /* If we are supposed to dump properties, write out a property length header and generate a stringbuf that contains those property values here. */ if (must_dump_props) { apr_hash_t *prophash, *oldhash = NULL; apr_size_t proplen; svn_stream_t *propstream; SVN_ERR(svn_fs_node_proplist(&prophash, eb->fs_root, path, pool)); /* If this is a partial dump, then issue a warning if we dump mergeinfo properties that refer to revisions older than the first revision dumped. */ if (!eb->verify && eb->notify_func && eb->oldest_dumped_rev > 1) { svn_string_t *mergeinfo_str = apr_hash_get(prophash, SVN_PROP_MERGEINFO, APR_HASH_KEY_STRING); if (mergeinfo_str) { svn_mergeinfo_t mergeinfo, old_mergeinfo; SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_str->data, pool)); SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( &old_mergeinfo, mergeinfo, eb->oldest_dumped_rev - 1, 0, TRUE, pool, pool)); if (apr_hash_count(old_mergeinfo)) { svn_repos_notify_t *notify = svn_repos_notify_create(svn_repos_notify_warning, pool); notify->warning = svn_repos_notify_warning_found_old_mergeinfo; notify->warning_str = apr_psprintf( pool, _("Mergeinfo referencing revision(s) prior " "to the oldest dumped revision (r%ld). " "Loading this dump may result in invalid " "mergeinfo."), eb->oldest_dumped_rev); eb->found_old_mergeinfo = TRUE; eb->notify_func(eb->notify_baton, notify, pool); } } } if (eb->use_deltas && compare_root) { /* Fetch the old property hash to diff against and output a header saying that our property contents are a delta. */ SVN_ERR(svn_fs_node_proplist(&oldhash, compare_root, compare_path, pool)); SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_PROP_DELTA ": true\n")); } else oldhash = apr_hash_make(pool); propstring = svn_stringbuf_create_ensure(0, pool); propstream = svn_stream_from_stringbuf(propstring, pool); SVN_ERR(svn_hash_write_incremental(prophash, oldhash, propstream, "PROPS-END", pool)); SVN_ERR(svn_stream_close(propstream)); proplen = propstring->len; content_length += proplen; SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH ": %" APR_SIZE_T_FMT "\n", proplen)); } /* If we are supposed to dump text, write out a text length header here, and an MD5 checksum (if available). */ if (must_dump_text && (kind == svn_node_file)) { svn_checksum_t *checksum; const char *hex_digest; svn_filesize_t textlen; if (eb->use_deltas) { /* Compute the text delta now and write it into a temporary file, so that we can find its length. Output a header saying our text contents are a delta. */ SVN_ERR(store_delta(&delta_file, &textlen, compare_root, compare_path, eb->fs_root, path, pool)); SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_DELTA ": true\n")); if (compare_root) { SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, compare_root, compare_path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5 ": %s\n", hex_digest)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, compare_root, compare_path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_SHA1 ": %s\n", hex_digest)); } } else { /* Just fetch the length of the file. */ SVN_ERR(svn_fs_file_length(&textlen, eb->fs_root, path, pool)); } content_length += textlen; SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH ": %" SVN_FILESIZE_T_FMT "\n", textlen)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, eb->fs_root, path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5 ": %s\n", hex_digest)); SVN_ERR(svn_fs_file_checksum(&checksum, svn_checksum_sha1, eb->fs_root, path, FALSE, pool)); hex_digest = svn_checksum_to_cstring(checksum, pool); if (hex_digest) SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_TEXT_CONTENT_SHA1 ": %s\n", hex_digest)); } /* 'Content-length:' is the last header before we dump the content, and is the sum of the text and prop contents lengths. We write this only for the benefit of non-Subversion RFC-822 parsers. */ SVN_ERR(svn_stream_printf(eb->stream, pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH ": %" SVN_FILESIZE_T_FMT "\n\n", content_length)); /* Dump property content if we're supposed to do so. */ if (must_dump_props) { len = propstring->len; SVN_ERR(svn_stream_write(eb->stream, propstring->data, &len)); } /* Dump text content */ if (must_dump_text && (kind == svn_node_file)) { svn_stream_t *contents; if (delta_file) { /* Make sure to close the underlying file when the stream is closed. */ contents = svn_stream_from_aprfile2(delta_file, FALSE, pool); } else SVN_ERR(svn_fs_file_contents(&contents, eb->fs_root, path, pool)); SVN_ERR(svn_stream_copy3(contents, svn_stream_disown(eb->stream, pool), NULL, NULL, pool)); } len = 2; return svn_stream_write(eb->stream, "\n\n", &len); /* ### needed? */ }
/* Writes out a git-like literal output of the compressed data in COMPRESSED_DATA to OUTPUT_STREAM, describing that its normal length is UNCOMPRESSED_SIZE. */ static svn_error_t * write_literal(svn_filesize_t uncompressed_size, svn_stream_t *compressed_data, svn_stream_t *output_stream, svn_cancel_func_t cancel_func, void *cancel_baton, apr_pool_t *scratch_pool) { apr_size_t rd; SVN_ERR(svn_stream_seek(compressed_data, NULL)); /* Seek to start */ SVN_ERR(svn_stream_printf(output_stream, scratch_pool, "literal %" SVN_FILESIZE_T_FMT APR_EOL_STR, uncompressed_size)); do { char chunk[GIT_BASE85_CHUNKSIZE]; const unsigned char *next; apr_size_t left; rd = sizeof(chunk); if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); SVN_ERR(svn_stream_read_full(compressed_data, chunk, &rd)); { apr_size_t one = 1; SVN_ERR(svn_stream_write(output_stream, &b85lenstr[rd-1], &one)); } left = rd; next = (void*)chunk; while (left) { char five[5]; unsigned info = 0; int n; apr_size_t five_sz; /* Push 4 bytes into the 32 bit info, when available */ for (n = 24; n >= 0 && left; n -= 8, next++, left--) { info |= (*next) << n; } /* Write out info as base85 */ for (n = 4; n >= 0; n--) { five[n] = b85str[info % 85]; info /= 85; } five_sz = 5; SVN_ERR(svn_stream_write(output_stream, five, &five_sz)); } SVN_ERR(svn_stream_puts(output_stream, APR_EOL_STR)); } while (rd == GIT_BASE85_CHUNKSIZE); return SVN_NO_ERROR; }
/* Implementation of svn_repos_notify_func_t to wrap the output to a response stream for svn_repos_dump_fs2() and svn_repos_verify_fs() */ static void repos_notify_handler(void *baton, const svn_repos_notify_t *notify, apr_pool_t *scratch_pool) { svn_stream_t *feedback_stream = baton; apr_size_t len; switch (notify->action) { case svn_repos_notify_warning: len = strlen(notify->warning_str); svn_error_clear(svn_stream_write(feedback_stream, notify->warning_str, &len)); return; case svn_repos_notify_dump_rev_end: svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, _("* Dumped revision %ld.\n"), notify->revision)); return; case svn_repos_notify_verify_rev_end: svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, _("* Verified revision %ld.\n"), notify->revision)); return; case svn_repos_notify_load_txn_committed: if (notify->old_revision == SVN_INVALID_REVNUM) { svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, _("\n------- Committed revision %ld >>>\n\n"), notify->new_revision)); } else { svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, _("\n------- Committed new rev %ld" " (loaded from original rev %ld" ") >>>\n\n"), notify->new_revision, notify->old_revision)); } return; case svn_repos_notify_load_node_start: { switch (notify->node_action) { case svn_node_action_change: svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, _(" * editing path : %s ..."), notify->path)); break; case svn_node_action_delete: svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, _(" * deleting path : %s ..."), notify->path)); break; case svn_node_action_add: svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, _(" * adding path : %s ..."), notify->path)); break; case svn_node_action_replace: svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, _(" * replacing path : %s ..."), notify->path)); break; } } return; case svn_repos_notify_load_node_done: len = 7; svn_error_clear(svn_stream_write(feedback_stream, _(" done.\n"), &len)); return; case svn_repos_notify_load_copied_node: len = 9; svn_error_clear(svn_stream_write(feedback_stream, "COPIED...", &len)); return; case svn_repos_notify_load_txn_start: svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, _("<<< Started new transaction, based on " "original revision %ld\n"), notify->old_revision)); return; case svn_repos_notify_load_normalized_mergeinfo: svn_error_clear(svn_stream_printf(feedback_stream, scratch_pool, _(" removing '\\r' from %s ..."), SVN_PROP_MERGEINFO)); return; default: return; } }
static svn_error_t * print_line_info(svn_stream_t *out, svn_revnum_t revision, const char *author, const char *date, const char *path, svn_boolean_t verbose, svn_revnum_t end_revnum, apr_pool_t *pool) { const char *time_utf8; const char *time_stdout; const char *rev_str; int rev_maxlength; /* The standard column width for the revision number is 6 characters. If the revision number can potentially be larger (i.e. if the end_revnum is larger than 1000000), we increase the column width as needed. */ rev_maxlength = 6; while (end_revnum >= 1000000) { rev_maxlength++; end_revnum = end_revnum / 10; } rev_str = SVN_IS_VALID_REVNUM(revision) ? apr_psprintf(pool, "%*ld", rev_maxlength, revision) : apr_psprintf(pool, "%*s", rev_maxlength, "-"); if (verbose) { if (date) { SVN_ERR(svn_cl__time_cstring_to_human_cstring(&time_utf8, date, pool)); SVN_ERR(svn_cmdline_cstring_from_utf8(&time_stdout, time_utf8, pool)); } else { /* ### This is a 44 characters long string. It assumes the current format of svn_time_to_human_cstring and also 3 letter abbreviations for the month and weekday names. Else, the line contents will be misaligned. */ time_stdout = " -"; } SVN_ERR(svn_stream_printf(out, pool, "%s %10s %s ", rev_str, author ? author : " -", time_stdout)); if (path) SVN_ERR(svn_stream_printf(out, pool, "%-14s ", path)); } else { return svn_stream_printf(out, pool, "%s %10.10s ", rev_str, author ? author : " -"); } return SVN_NO_ERROR; }
svn_error_t * svn_fs_fs__write_noderev(svn_stream_t *outfile, node_revision_t *noderev, int format, svn_boolean_t include_mergeinfo, apr_pool_t *scratch_pool) { SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_ID ": %s\n", svn_fs_fs__id_unparse(noderev->id, scratch_pool)->data)); SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TYPE ": %s\n", (noderev->kind == svn_node_file) ? SVN_FS_FS__KIND_FILE : SVN_FS_FS__KIND_DIR)); if (noderev->predecessor_id) SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PRED ": %s\n", svn_fs_fs__id_unparse(noderev->predecessor_id, scratch_pool)->data)); SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COUNT ": %d\n", noderev->predecessor_count)); if (noderev->data_rep) SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TEXT ": %s\n", svn_fs_fs__unparse_representation (noderev->data_rep, format, noderev->kind == svn_node_dir, scratch_pool, scratch_pool)->data)); if (noderev->prop_rep) SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PROPS ": %s\n", svn_fs_fs__unparse_representation (noderev->prop_rep, format, TRUE, scratch_pool, scratch_pool)->data)); SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_CPATH ": %s\n", noderev->created_path)); if (noderev->copyfrom_path) SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPYFROM ": %ld" " %s\n", noderev->copyfrom_rev, noderev->copyfrom_path)); if ((noderev->copyroot_rev != svn_fs_fs__id_rev(noderev->id)) || (strcmp(noderev->copyroot_path, noderev->created_path) != 0)) SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPYROOT ": %ld" " %s\n", noderev->copyroot_rev, noderev->copyroot_path)); if (noderev->is_fresh_txn_root) SVN_ERR(svn_stream_puts(outfile, HEADER_FRESHTXNRT ": y\n")); if (include_mergeinfo) { if (noderev->mergeinfo_count > 0) SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_MINFO_CNT ": %" APR_INT64_T_FMT "\n", noderev->mergeinfo_count)); if (noderev->has_mergeinfo) SVN_ERR(svn_stream_puts(outfile, HEADER_MINFO_HERE ": y\n")); } return svn_stream_puts(outfile, "\n"); }
svn_error_t * svn_fs_x__write_noderev(svn_stream_t *outfile, svn_fs_x__noderev_t *noderev, apr_pool_t *scratch_pool) { svn_string_t *str_id; str_id = svn_fs_x__id_unparse(&noderev->noderev_id, scratch_pool); SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_ID ": %s\n", str_id->data)); str_id = svn_fs_x__id_unparse(&noderev->node_id, scratch_pool); SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_NODE ": %s\n", str_id->data)); str_id = svn_fs_x__id_unparse(&noderev->copy_id, scratch_pool); SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPY ": %s\n", str_id->data)); SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TYPE ": %s\n", (noderev->kind == svn_node_file) ? SVN_FS_X__KIND_FILE : SVN_FS_X__KIND_DIR)); if (svn_fs_x__id_used(&noderev->predecessor_id)) SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PRED ": %s\n", svn_fs_x__id_unparse(&noderev->predecessor_id, scratch_pool)->data)); SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COUNT ": %d\n", noderev->predecessor_count)); if (noderev->data_rep) SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_TEXT ": %s\n", svn_fs_x__unparse_representation (noderev->data_rep, noderev->kind == svn_node_dir, scratch_pool, scratch_pool)->data)); if (noderev->prop_rep) SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_PROPS ": %s\n", svn_fs_x__unparse_representation (noderev->prop_rep, TRUE, scratch_pool, scratch_pool)->data)); SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_CPATH ": %s\n", auto_escape_path(noderev->created_path, scratch_pool))); if (noderev->copyfrom_path) SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPYFROM ": %ld" " %s\n", noderev->copyfrom_rev, auto_escape_path(noderev->copyfrom_path, scratch_pool))); if ( ( noderev->copyroot_rev != svn_fs_x__get_revnum(noderev->noderev_id.change_set)) || (strcmp(noderev->copyroot_path, noderev->created_path) != 0)) SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_COPYROOT ": %ld" " %s\n", noderev->copyroot_rev, auto_escape_path(noderev->copyroot_path, scratch_pool))); if (noderev->mergeinfo_count > 0) SVN_ERR(svn_stream_printf(outfile, scratch_pool, HEADER_MINFO_CNT ": %" APR_INT64_T_FMT "\n", noderev->mergeinfo_count)); if (noderev->has_mergeinfo) SVN_ERR(svn_stream_puts(outfile, HEADER_MINFO_HERE ": y\n")); return svn_stream_puts(outfile, "\n"); }
/* The main dumper. */ svn_error_t * svn_repos_dump_fs3(svn_repos_t *repos, svn_stream_t *stream, svn_revnum_t start_rev, svn_revnum_t end_rev, svn_boolean_t incremental, svn_boolean_t use_deltas, 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_delta_editor_t *dump_editor; void *dump_edit_baton = NULL; svn_revnum_t i; svn_fs_t *fs = svn_repos_fs(repos); apr_pool_t *subpool = svn_pool_create(pool); svn_revnum_t youngest; const char *uuid; int version; svn_boolean_t found_old_reference = FALSE; svn_boolean_t found_old_mergeinfo = FALSE; svn_repos_notify_t *notify; /* Determine the current youngest revision of the filesystem. */ SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool)); /* Use default vals if necessary. */ if (! SVN_IS_VALID_REVNUM(start_rev)) start_rev = 0; if (! SVN_IS_VALID_REVNUM(end_rev)) end_rev = youngest; if (! stream) stream = svn_stream_empty(pool); /* Validate the revisions. */ if (start_rev > end_rev) return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL, _("Start revision %ld" " is greater than end revision %ld"), start_rev, end_rev); if (end_rev > youngest) return svn_error_createf(SVN_ERR_REPOS_BAD_ARGS, NULL, _("End revision %ld is invalid " "(youngest revision is %ld)"), end_rev, youngest); if ((start_rev == 0) && incremental) incremental = FALSE; /* revision 0 looks the same regardless of whether or not this is an incremental dump, so just simplify things. */ /* Write out the UUID. */ SVN_ERR(svn_fs_get_uuid(fs, &uuid, pool)); /* If we're not using deltas, use the previous version, for compatibility with svn 1.0.x. */ version = SVN_REPOS_DUMPFILE_FORMAT_VERSION; if (!use_deltas) version--; /* Write out "general" metadata for the dumpfile. In this case, a magic header followed by a dumpfile format version. */ SVN_ERR(svn_stream_printf(stream, pool, SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n", version)); SVN_ERR(svn_stream_printf(stream, pool, SVN_REPOS_DUMPFILE_UUID ": %s\n\n", uuid)); /* Create a notify object that we can reuse in the loop. */ if (notify_func) notify = svn_repos_notify_create(svn_repos_notify_dump_rev_end, pool); /* Main loop: we're going to dump revision i. */ for (i = start_rev; i <= end_rev; i++) { svn_revnum_t from_rev, to_rev; svn_fs_root_t *to_root; svn_boolean_t use_deltas_for_rev; svn_pool_clear(subpool); /* Check for cancellation. */ if (cancel_func) SVN_ERR(cancel_func(cancel_baton)); /* Special-case the initial revision dump: it needs to contain *all* nodes, because it's the foundation of all future revisions in the dumpfile. */ if ((i == start_rev) && (! incremental)) { /* Special-special-case a dump of revision 0. */ if (i == 0) { /* Just write out the one revision 0 record and move on. The parser might want to use its properties. */ SVN_ERR(write_revision_record(stream, fs, 0, subpool)); to_rev = 0; goto loop_end; } /* Compare START_REV to revision 0, so that everything appears to be added. */ from_rev = 0; to_rev = i; } else { /* In the normal case, we want to compare consecutive revs. */ from_rev = i - 1; to_rev = i; } /* Write the revision record. */ SVN_ERR(write_revision_record(stream, fs, to_rev, subpool)); /* Fetch the editor which dumps nodes to a file. Regardless of what we've been told, don't use deltas for the first rev of a non-incremental dump. */ use_deltas_for_rev = use_deltas && (incremental || i != start_rev); SVN_ERR(get_dump_editor(&dump_editor, &dump_edit_baton, fs, to_rev, "", stream, notify_func, notify_baton, start_rev, use_deltas_for_rev, FALSE, subpool)); /* Drive the editor in one way or another. */ SVN_ERR(svn_fs_revision_root(&to_root, fs, to_rev, subpool)); /* If this is the first revision of a non-incremental dump, we're in for a full tree dump. Otherwise, we want to simply replay the revision. */ if ((i == start_rev) && (! incremental)) { svn_fs_root_t *from_root; SVN_ERR(svn_fs_revision_root(&from_root, fs, from_rev, subpool)); SVN_ERR(svn_repos_dir_delta2(from_root, "", "", to_root, "", dump_editor, dump_edit_baton, NULL, NULL, FALSE, /* don't send text-deltas */ svn_depth_infinity, FALSE, /* don't send entry props */ FALSE, /* don't ignore ancestry */ subpool)); } else { SVN_ERR(svn_repos_replay2(to_root, "", SVN_INVALID_REVNUM, FALSE, dump_editor, dump_edit_baton, NULL, NULL, subpool)); } loop_end: if (notify_func) { notify->revision = to_rev; notify_func(notify_baton, notify, subpool); } if (dump_edit_baton) /* We never get an edit baton for r0. */ { if (((struct edit_baton *)dump_edit_baton)->found_old_reference) found_old_reference = TRUE; if (((struct edit_baton *)dump_edit_baton)->found_old_mergeinfo) found_old_mergeinfo = TRUE; } } if (notify_func) { /* Did we issue any warnings about references to revisions older than the oldest dumped revision? If so, then issue a final generic warning, since the inline warnings already issued might easily be missed. */ notify = svn_repos_notify_create(svn_repos_notify_dump_end, subpool); notify_func(notify_baton, notify, subpool); if (found_old_reference) { notify = svn_repos_notify_create(svn_repos_notify_warning, subpool); notify->warning = svn_repos_notify_warning_found_old_reference; notify->warning_str = _("The range of revisions dumped " "contained references to " "copy sources outside that " "range."); notify_func(notify_baton, notify, subpool); } /* Ditto if we issued any warnings about old revisions referenced in dumped mergeinfo. */ if (found_old_mergeinfo) { notify = svn_repos_notify_create(svn_repos_notify_warning, subpool); notify->warning = svn_repos_notify_warning_found_old_mergeinfo; notify->warning_str = _("The range of revisions dumped " "contained mergeinfo " "which reference revisions outside " "that range."); notify_func(notify_baton, notify, subpool); } } svn_pool_destroy(subpool); return SVN_NO_ERROR; }