diff options
Diffstat (limited to 'subversion/svnrdump/dump_editor.c')
-rw-r--r-- | subversion/svnrdump/dump_editor.c | 967 |
1 files changed, 9 insertions, 958 deletions
diff --git a/subversion/svnrdump/dump_editor.c b/subversion/svnrdump/dump_editor.c index f900e209e276..ef52b41b43dc 100644 --- a/subversion/svnrdump/dump_editor.c +++ b/subversion/svnrdump/dump_editor.c @@ -22,951 +22,28 @@ * ==================================================================== */ -#include "svn_hash.h" -#include "svn_pools.h" -#include "svn_repos.h" -#include "svn_path.h" +#include "svn_types.h" #include "svn_props.h" -#include "svn_subst.h" -#include "svn_dirent_uri.h" +#include "svn_ra.h" +#include "svn_io.h" +#include "svn_delta.h" #include "private/svn_repos_private.h" -#include "private/svn_subr_private.h" -#include "private/svn_dep_compat.h" #include "private/svn_editor.h" #include "svnrdump.h" #include <assert.h> -#define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r)) - - -/* A directory baton used by all directory-related callback functions - * in the dump editor. */ -struct dir_baton -{ - struct dump_edit_baton *eb; - - /* Pool for per-directory allocations */ - apr_pool_t *pool; - - /* the path to this directory */ - const char *repos_relpath; /* a relpath */ - - /* Copyfrom info for the node, if any. */ - const char *copyfrom_path; /* a relpath */ - svn_revnum_t copyfrom_rev; - - /* Headers accumulated so far for this directory */ - svn_repos__dumpfile_headers_t *headers; - - /* Properties which were modified during change_dir_prop. */ - apr_hash_t *props; - - /* Properties which were deleted during change_dir_prop. */ - apr_hash_t *deleted_props; - - /* Hash of paths that need to be deleted, though some -might- be - replaced. Maps const char * paths to this dir_baton. Note that - 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; - - /* Flag to trigger dumping props. */ - svn_boolean_t dump_props; -}; - -/* A file baton used by all file-related callback functions in the dump - * editor */ -struct file_baton -{ - struct dump_edit_baton *eb; - - /* Pool for per-file allocations */ - apr_pool_t *pool; - - /* the path to this file */ - const char *repos_relpath; /* a relpath */ - - /* Properties which were modified during change_file_prop. */ - apr_hash_t *props; - - /* Properties which were deleted during change_file_prop. */ - apr_hash_t *deleted_props; - - /* The checksum of the file the delta is being applied to */ - const char *base_checksum; - - /* Copy state and source information (if any). */ - svn_boolean_t is_copy; - const char *copyfrom_path; - svn_revnum_t copyfrom_rev; - - /* The action associate with this node. */ - enum svn_node_action action; - - /* Flags to trigger dumping props and text. */ - svn_boolean_t dump_text; - svn_boolean_t dump_props; -}; /* The baton used by the dump editor. */ struct dump_edit_baton { - /* The output stream we write the dumpfile to */ - svn_stream_t *stream; - /* A backdoor ra session to fetch additional information during the edit. */ svn_ra_session_t *ra_session; - /* The repository relpath of the anchor of the editor when driven - via the RA update mechanism; NULL otherwise. (When the editor is - driven via the RA "replay" mechanism instead, the editor is - always anchored at the repository, we don't need to prepend an - anchor path to the dumped node paths, and open_root() doesn't - need to manufacture directory additions.) */ - const char *update_anchor_relpath; - - /* Pool for per-revision allocations */ - apr_pool_t *pool; - - /* Temporary file used for textdelta application along with its - absolute path; these two variables should be allocated in the - per-edit-session pool */ - const char *delta_abspath; - apr_file_t *delta_file; - /* The revision we're currently dumping. */ svn_revnum_t current_revision; - - /* The baton of the directory node whose block of - dump stream data has not been fully completed; NULL if there's no - such item. */ - struct dir_baton *pending_db; }; -/* Make a directory baton to represent the directory at PATH (relative - * to the EDIT_BATON). - * - * COPYFROM_PATH/COPYFROM_REV are the path/revision against which this - * directory should be compared for changes. If the copyfrom - * information is valid, the directory will be compared against its - * copy source. - * - * PB is the directory baton of this directory's parent, or NULL if - * this is the top-level directory of the edit. - * - * Perform all allocations in POOL. */ -static struct dir_baton * -make_dir_baton(const char *path, - const char *copyfrom_path, - svn_revnum_t copyfrom_rev, - void *edit_baton, - struct dir_baton *pb, - apr_pool_t *pool) -{ - struct dump_edit_baton *eb = edit_baton; - struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db)); - const char *repos_relpath; - - /* Construct the full path of this node. */ - if (pb) - repos_relpath = svn_relpath_canonicalize(path, pool); - else - repos_relpath = ""; - - /* Strip leading slash from copyfrom_path so that the path is - canonical and svn_relpath_join can be used */ - if (copyfrom_path) - copyfrom_path = svn_relpath_canonicalize(copyfrom_path, pool); - - new_db->eb = eb; - new_db->pool = pool; - new_db->repos_relpath = repos_relpath; - new_db->copyfrom_path = copyfrom_path - ? svn_relpath_canonicalize(copyfrom_path, pool) - : NULL; - new_db->copyfrom_rev = copyfrom_rev; - new_db->headers = NULL; - new_db->props = apr_hash_make(pool); - new_db->deleted_props = apr_hash_make(pool); - new_db->deleted_entries = apr_hash_make(pool); - - return new_db; -} - -/* Make a file baton to represent the directory at PATH (relative to - * PB->eb). PB is the directory baton of this directory's parent, or - * NULL if this is the top-level directory of the edit. Perform all - * allocations in POOL. */ -static struct file_baton * -make_file_baton(const char *path, - struct dir_baton *pb, - apr_pool_t *pool) -{ - struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb)); - - new_fb->eb = pb->eb; - new_fb->pool = pool; - new_fb->repos_relpath = svn_relpath_canonicalize(path, pool); - new_fb->props = apr_hash_make(pool); - new_fb->deleted_props = apr_hash_make(pool); - new_fb->is_copy = FALSE; - new_fb->copyfrom_path = NULL; - new_fb->copyfrom_rev = SVN_INVALID_REVNUM; - new_fb->action = svn_node_action_change; - - return new_fb; -} - -/* Append to HEADERS the required headers, and set *CONTENT to the property - * content section, to represent the property delta of PROPS/DELETED_PROPS. - */ -static svn_error_t * -get_props_content(svn_repos__dumpfile_headers_t *headers, - svn_stringbuf_t **content, - apr_hash_t *props, - apr_hash_t *deleted_props, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - svn_stream_t *content_stream; - apr_hash_t *normal_props; - - *content = svn_stringbuf_create_empty(result_pool); - - content_stream = svn_stream_from_stringbuf(*content, scratch_pool); - - SVN_ERR(svn_rdump__normalize_props(&normal_props, props, scratch_pool)); - SVN_ERR(svn_hash_write_incremental(normal_props, deleted_props, - content_stream, "PROPS-END", - scratch_pool)); - SVN_ERR(svn_stream_close(content_stream)); - - /* Prop-delta: true */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_PROP_DELTA, "true"); - - return SVN_NO_ERROR; -} - -/* A special case of dump_node(), for a delete record. - * - * The only thing special about this version is it only writes one blank - * line, not two, after the headers. Why? Historical precedent for the - * case where a delete record is used as part of a (delete + add-with-history) - * in implementing a replacement. - */ -static svn_error_t * -dump_node_delete(svn_stream_t *stream, - const char *node_relpath, - apr_pool_t *pool) -{ - svn_repos__dumpfile_headers_t *headers - = svn_repos__dumpfile_headers_create(pool); - - assert(svn_relpath_is_canonical(node_relpath)); - - /* Node-path: ... */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_relpath); - - /* Node-action: delete */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete"); - - SVN_ERR(svn_repos__dump_node_record(stream, headers, - NULL, FALSE, 0, /* props & text */ - FALSE /*content_length_always*/, pool)); - return SVN_NO_ERROR; -} - -/* Set *HEADERS_P to contain some headers for the node at PATH of type KIND. - * - * ACTION describes what is happening to the node (see enum - * svn_node_action). - * - * If the node was itself copied, IS_COPY is TRUE and the - * path/revision of the copy source are in COPYFROM_PATH/COPYFROM_REV. - * If IS_COPY is FALSE, yet COPYFROM_PATH/COPYFROM_REV are valid, this - * node is part of a copied subtree. - * - * Iff ACTION is svn_node_action_replace and IS_COPY, then first write a - * complete deletion record to the dump stream. - * - * If ACTION is svn_node_action_delete, then the node record will be - * complete. (The caller may want to write two blank lines after the - * header block.) - */ -static svn_error_t * -dump_node(svn_repos__dumpfile_headers_t **headers_p, - struct dump_edit_baton *eb, - const char *repos_relpath, - struct dir_baton *db, - struct file_baton *fb, - enum svn_node_action action, - svn_boolean_t is_copy, - const char *copyfrom_path, - svn_revnum_t copyfrom_rev, - apr_pool_t *pool) -{ - const char *node_relpath = repos_relpath; - svn_repos__dumpfile_headers_t *headers - = svn_repos__dumpfile_headers_create(pool); - - assert(svn_relpath_is_canonical(repos_relpath)); - assert(!copyfrom_path || svn_relpath_is_canonical(copyfrom_path)); - assert(! (db && fb)); - - /* Add the edit root relpath prefix if necessary. */ - if (eb->update_anchor_relpath) - node_relpath = svn_relpath_join(eb->update_anchor_relpath, - node_relpath, pool); - - /* Node-path: ... */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_relpath); - - /* Node-kind: "file" | "dir" */ - if (fb) - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_KIND, "file"); - else if (db) - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_KIND, "dir"); - - - /* Write the appropriate Node-action header */ - switch (action) - { - case svn_node_action_change: - /* We are here after a change_file_prop or change_dir_prop. They - set up whatever dump_props they needed to- nothing to - do here but print node action information. - - Node-action: change. */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "change"); - break; - - case svn_node_action_delete: - /* Node-action: delete */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "delete"); - break; - - case svn_node_action_replace: - if (! is_copy) - { - /* Node-action: replace */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "replace"); - - /* Wait for a change_*_prop to be called before dumping - anything */ - if (fb) - fb->dump_props = TRUE; - else if (db) - db->dump_props = TRUE; - break; - } - else - { - /* More complex case: is_copy is true, and copyfrom_path/ - copyfrom_rev are present: delete the original, and then re-add - it */ - /* ### Why not write a 'replace' record? Don't know. */ - - /* ### Unusually, we end this 'delete' node record with only a single - blank line after the header block -- no extra blank line. */ - SVN_ERR(dump_node_delete(eb->stream, repos_relpath, pool)); - - /* The remaining action is a non-replacing add-with-history */ - /* action = svn_node_action_add; */ - } - /* FALL THROUGH to 'add' */ - - case svn_node_action_add: - /* Node-action: add */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "add"); - - if (is_copy) - { - /* Node-copyfrom-rev / Node-copyfrom-path */ - svn_repos__dumpfile_header_pushf( - headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV, "%ld", copyfrom_rev); - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH, copyfrom_path); - } - else - { - /* fb->dump_props (for files) is handled in close_file() - which is called immediately. - - However, directories are not closed until all the work - inside them has been done; db->dump_props (for directories) - is handled (via dump_pending()) in all the functions that - can possibly be called after add_directory(): - - - add_directory() - - open_directory() - - delete_entry() - - close_directory() - - add_file() - - open_file() - - change_dir_prop() is a special case. */ - if (fb) - fb->dump_props = TRUE; - else if (db) - db->dump_props = TRUE; - } - - break; - } - - /* Return the headers so far. We don't necessarily have all the headers - yet -- there may be property-related and content length headers to - come, if this was not a 'delete' record. */ - *headers_p = headers; - return SVN_NO_ERROR; -} - -static svn_error_t * -dump_mkdir(struct dump_edit_baton *eb, - const char *repos_relpath, - apr_pool_t *pool) -{ - svn_stringbuf_t *prop_content; - svn_repos__dumpfile_headers_t *headers - = svn_repos__dumpfile_headers_create(pool); - - /* Node-path: ... */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_PATH, repos_relpath); - - /* Node-kind: dir */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_KIND, "dir"); - - /* Node-action: add */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_NODE_ACTION, "add"); - - /* Dump the (empty) property block. */ - SVN_ERR(get_props_content(headers, &prop_content, - apr_hash_make(pool), apr_hash_make(pool), - pool, pool)); - SVN_ERR(svn_repos__dump_node_record(eb->stream, headers, prop_content, - FALSE, 0, FALSE /*content_length_always*/, - pool)); - - /* Newlines to tie it all off. */ - SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); - - return SVN_NO_ERROR; -} - -/* Dump pending headers and properties for the directory EB->pending_db (if - * not null), to allow starting the dump of a child node */ -static svn_error_t * -dump_pending_dir(struct dump_edit_baton *eb, - apr_pool_t *scratch_pool) -{ - struct dir_baton *db = eb->pending_db; - svn_stringbuf_t *prop_content = NULL; - - if (! db) - return SVN_NO_ERROR; - - /* Some pending properties to dump? */ - if (db->dump_props) - { - SVN_ERR(get_props_content(db->headers, &prop_content, - db->props, db->deleted_props, - scratch_pool, scratch_pool)); - } - SVN_ERR(svn_repos__dump_node_record(eb->stream, db->headers, prop_content, - FALSE, 0, FALSE /*content_length_always*/, - scratch_pool)); - - /* No text is going to be dumped. Write a couple of newlines and - wait for the next node/ revision. */ - SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); - - if (db->dump_props) - { - /* Cleanup so that data is never dumped twice. */ - apr_hash_clear(db->props); - apr_hash_clear(db->deleted_props); - db->dump_props = FALSE; - } - - /* Anything that was pending is pending no longer. */ - eb->pending_db = NULL; - - return SVN_NO_ERROR; -} - - - -/*** Editor Function Implementations ***/ - -static svn_error_t * -open_root(void *edit_baton, - svn_revnum_t base_revision, - apr_pool_t *pool, - void **root_baton) -{ - struct dump_edit_baton *eb = edit_baton; - struct dir_baton *new_db = NULL; - - /* Clear the per-revision pool after each revision */ - svn_pool_clear(eb->pool); - - if (eb->update_anchor_relpath) - { - int i; - const char *parent_path = eb->update_anchor_relpath; - apr_array_header_t *dirs_to_add = - apr_array_make(pool, 4, sizeof(const char *)); - apr_pool_t *iterpool = svn_pool_create(pool); - - while (! svn_path_is_empty(parent_path)) - { - APR_ARRAY_PUSH(dirs_to_add, const char *) = parent_path; - parent_path = svn_relpath_dirname(parent_path, pool); - } - - for (i = dirs_to_add->nelts; i; --i) - { - const char *dir_to_add = - APR_ARRAY_IDX(dirs_to_add, i - 1, const char *); - - svn_pool_clear(iterpool); - - /* For parents of the source directory, we just manufacture - the adds ourselves. */ - if (i > 1) - { - SVN_ERR(dump_mkdir(eb, dir_to_add, iterpool)); - } - else - { - /* ... but for the source directory itself, we'll defer - to letting the typical plumbing handle this task. */ - new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM, - edit_baton, NULL, pool); - SVN_ERR(dump_node(&new_db->headers, - eb, new_db->repos_relpath, new_db, - NULL, svn_node_action_add, FALSE, - NULL, SVN_INVALID_REVNUM, pool)); - - /* Remember that we've started but not yet finished - handling this directory. */ - eb->pending_db = new_db; - } - } - svn_pool_destroy(iterpool); - } - - if (! new_db) - { - new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM, - edit_baton, NULL, pool); - } - - *root_baton = new_db; - return SVN_NO_ERROR; -} - -static svn_error_t * -delete_entry(const char *path, - svn_revnum_t revision, - void *parent_baton, - apr_pool_t *pool) -{ - struct dir_baton *pb = parent_baton; - - SVN_ERR(dump_pending_dir(pb->eb, pool)); - - /* We don't dump this deletion immediate. Rather, we add this path - to the deleted_entries of the parent directory baton. That way, - we can tell (later) an addition from a replacement. All the real - deletions get handled in close_directory(). */ - svn_hash_sets(pb->deleted_entries, apr_pstrdup(pb->pool, path), pb); - - return SVN_NO_ERROR; -} - -static svn_error_t * -add_directory(const char *path, - void *parent_baton, - const char *copyfrom_path, - svn_revnum_t copyfrom_rev, - apr_pool_t *pool, - void **child_baton) -{ - struct dir_baton *pb = parent_baton; - void *was_deleted; - struct dir_baton *new_db; - svn_boolean_t is_copy; - - SVN_ERR(dump_pending_dir(pb->eb, pool)); - - new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, - pb, pb->pool); - - /* This might be a replacement -- is the path already deleted? */ - was_deleted = svn_hash_gets(pb->deleted_entries, path); - - /* Detect an add-with-history */ - is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev); - - /* Dump the node */ - SVN_ERR(dump_node(&new_db->headers, - pb->eb, new_db->repos_relpath, new_db, NULL, - was_deleted ? svn_node_action_replace : svn_node_action_add, - is_copy, - is_copy ? new_db->copyfrom_path : NULL, - is_copy ? copyfrom_rev : SVN_INVALID_REVNUM, - pool)); - - if (was_deleted) - /* Delete the path, it's now been dumped */ - svn_hash_sets(pb->deleted_entries, path, NULL); - - /* Remember that we've started, but not yet finished handling this - directory. */ - pb->eb->pending_db = new_db; - - *child_baton = new_db; - return SVN_NO_ERROR; -} - -static svn_error_t * -open_directory(const char *path, - void *parent_baton, - svn_revnum_t base_revision, - apr_pool_t *pool, - void **child_baton) -{ - struct dir_baton *pb = parent_baton; - struct dir_baton *new_db; - const char *copyfrom_path = NULL; - svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM; - - SVN_ERR(dump_pending_dir(pb->eb, pool)); - - /* If the parent directory has explicit comparison path and rev, - record the same for this one. */ - if (ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev)) - { - copyfrom_path = svn_relpath_join(pb->copyfrom_path, - svn_relpath_basename(path, NULL), - pb->pool); - copyfrom_rev = pb->copyfrom_rev; - } - - new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb, - pb->pool); - - *child_baton = new_db; - return SVN_NO_ERROR; -} - -static svn_error_t * -close_directory(void *dir_baton, - apr_pool_t *pool) -{ - struct dir_baton *db = dir_baton; - apr_hash_index_t *hi; - svn_boolean_t this_pending; - - /* Remember if this directory is the one currently pending. */ - this_pending = (db->eb->pending_db == db); - - SVN_ERR(dump_pending_dir(db->eb, pool)); - - /* If this directory was pending, then dump_pending() should have - taken care of all the props and such. Of course, the only way - that would be the case is if this directory was added/replaced. - - Otherwise, if stuff for this directory has already been written - out (at some point in the past, prior to our handling other - nodes), we might need to generate a second "change" record just - to carry the information we've since learned about the - directory. */ - if ((! this_pending) && (db->dump_props)) - { - SVN_ERR(dump_node(&db->headers, - db->eb, db->repos_relpath, db, NULL, - svn_node_action_change, FALSE, - NULL, SVN_INVALID_REVNUM, pool)); - db->eb->pending_db = db; - SVN_ERR(dump_pending_dir(db->eb, pool)); - } - - /* Dump the deleted directory entries */ - for (hi = apr_hash_first(pool, db->deleted_entries); hi; - hi = apr_hash_next(hi)) - { - const char *path = apr_hash_this_key(hi); - - SVN_ERR(dump_node_delete(db->eb->stream, path, pool)); - /* This deletion record is complete -- write an extra newline */ - SVN_ERR(svn_stream_puts(db->eb->stream, "\n")); - } - - /* ### should be unnecessary */ - apr_hash_clear(db->deleted_entries); - - return SVN_NO_ERROR; -} - -static svn_error_t * -add_file(const char *path, - void *parent_baton, - const char *copyfrom_path, - svn_revnum_t copyfrom_rev, - apr_pool_t *pool, - void **file_baton) -{ - struct dir_baton *pb = parent_baton; - struct file_baton *fb; - void *was_deleted; - - SVN_ERR(dump_pending_dir(pb->eb, pool)); - - /* Make the file baton. */ - fb = make_file_baton(path, pb, pool); - - /* This might be a replacement -- is the path already deleted? */ - was_deleted = svn_hash_gets(pb->deleted_entries, path); - - /* Detect add-with-history. */ - if (ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev)) - { - fb->copyfrom_path = svn_relpath_canonicalize(copyfrom_path, fb->pool); - fb->copyfrom_rev = copyfrom_rev; - fb->is_copy = TRUE; - } - fb->action = was_deleted ? svn_node_action_replace : svn_node_action_add; - - /* Delete the path, it's now been dumped. */ - if (was_deleted) - svn_hash_sets(pb->deleted_entries, path, NULL); - - *file_baton = fb; - return SVN_NO_ERROR; -} - -static svn_error_t * -open_file(const char *path, - void *parent_baton, - svn_revnum_t ancestor_revision, - apr_pool_t *pool, - void **file_baton) -{ - struct dir_baton *pb = parent_baton; - struct file_baton *fb; - - SVN_ERR(dump_pending_dir(pb->eb, pool)); - - /* Make the file baton. */ - fb = make_file_baton(path, pb, pool); - - /* If the parent directory has explicit copyfrom path and rev, - record the same for this one. */ - if (ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev)) - { - fb->copyfrom_path = svn_relpath_join(pb->copyfrom_path, - svn_relpath_basename(path, NULL), - pb->pool); - fb->copyfrom_rev = pb->copyfrom_rev; - } - - *file_baton = fb; - return SVN_NO_ERROR; -} - -static svn_error_t * -change_dir_prop(void *parent_baton, - const char *name, - const svn_string_t *value, - apr_pool_t *pool) -{ - struct dir_baton *db = parent_baton; - svn_boolean_t this_pending; - - /* This directory is not pending, but something else is, so handle - the "something else". */ - this_pending = (db->eb->pending_db == db); - if (! this_pending) - SVN_ERR(dump_pending_dir(db->eb, pool)); - - if (svn_property_kind2(name) != svn_prop_regular_kind) - return SVN_NO_ERROR; - - if (value) - svn_hash_sets(db->props, - apr_pstrdup(db->pool, name), - svn_string_dup(value, db->pool)); - else - svn_hash_sets(db->deleted_props, apr_pstrdup(db->pool, name), ""); - - /* Make sure we eventually output the props */ - db->dump_props = TRUE; - - return SVN_NO_ERROR; -} - -static svn_error_t * -change_file_prop(void *file_baton, - const char *name, - const svn_string_t *value, - apr_pool_t *pool) -{ - struct file_baton *fb = file_baton; - - if (svn_property_kind2(name) != svn_prop_regular_kind) - return SVN_NO_ERROR; - - if (value) - svn_hash_sets(fb->props, - apr_pstrdup(fb->pool, name), - svn_string_dup(value, fb->pool)); - else - svn_hash_sets(fb->deleted_props, apr_pstrdup(fb->pool, name), ""); - - /* Dump the property headers and wait; close_file might need - to write text headers too depending on whether - apply_textdelta is called */ - fb->dump_props = TRUE; - - return SVN_NO_ERROR; -} - -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 dump_edit_baton *eb = fb->eb; - svn_stream_t *delta_filestream; - - /* Use a temporary file to measure the Text-content-length */ - delta_filestream = svn_stream_from_aprfile2(eb->delta_file, TRUE, pool); - - /* Prepare to write the delta to the delta_filestream */ - svn_txdelta_to_svndiff3(handler, handler_baton, - delta_filestream, 0, - SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool); - - /* Record that there's text to be dumped, and its base checksum. */ - fb->dump_text = TRUE; - fb->base_checksum = apr_pstrdup(fb->pool, base_checksum); - - return SVN_NO_ERROR; -} - -static svn_error_t * -close_file(void *file_baton, - const char *text_checksum, - apr_pool_t *pool) -{ - struct file_baton *fb = file_baton; - struct dump_edit_baton *eb = fb->eb; - svn_filesize_t text_content_length = 0; - svn_stringbuf_t *propstring = NULL; - svn_repos__dumpfile_headers_t *headers; - - SVN_ERR(dump_pending_dir(eb, pool)); - - /* Start dumping this node, by collecting some basic headers for it. */ - SVN_ERR(dump_node(&headers, eb, fb->repos_relpath, NULL, fb, - fb->action, fb->is_copy, fb->copyfrom_path, - fb->copyfrom_rev, pool)); - - /* Some pending properties to dump? We'll dump just the headers for - now, then dump the actual propchange content only after dumping - the text headers too (if present). */ - if (fb->dump_props) - { - SVN_ERR(get_props_content(headers, &propstring, - fb->props, fb->deleted_props, - pool, pool)); - } - - /* Dump the text headers */ - if (fb->dump_text) - { - /* Text-delta: true */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_TEXT_DELTA, "true"); - - SVN_ERR(svn_io_file_size_get(&text_content_length, eb->delta_file, - pool)); - - if (fb->base_checksum) - /* Text-delta-base-md5: */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5, fb->base_checksum); - - /* Text-content-md5: 82705804337e04dcd0e586bfa2389a7f */ - svn_repos__dumpfile_header_push( - headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_MD5, text_checksum); - } - - /* Dump the headers and props now */ - SVN_ERR(svn_repos__dump_node_record(eb->stream, headers, propstring, - fb->dump_text, text_content_length, - FALSE /*content_length_always*/, - pool)); - - if (fb->dump_props) - { - /* Cleanup */ - fb->dump_props = FALSE; - apr_hash_clear(fb->props); - apr_hash_clear(fb->deleted_props); - } - - /* Dump the text */ - if (fb->dump_text) - { - /* Seek to the beginning of the delta file, map it to a stream, - and copy the stream to eb->stream. Then close the stream and - truncate the file so we can reuse it for the next textdelta - application. Note that the file isn't created, opened or - closed here */ - svn_stream_t *delta_filestream; - apr_off_t offset = 0; - - SVN_ERR(svn_io_file_seek(eb->delta_file, APR_SET, &offset, pool)); - delta_filestream = svn_stream_from_aprfile2(eb->delta_file, TRUE, pool); - SVN_ERR(svn_stream_copy3(delta_filestream, eb->stream, NULL, NULL, pool)); - - /* Cleanup */ - SVN_ERR(svn_stream_close(delta_filestream)); - SVN_ERR(svn_io_file_trunc(eb->delta_file, 0, pool)); - } - - /* Write a couple of blank lines for matching output with `svnadmin - dump` */ - SVN_ERR(svn_stream_puts(eb->stream, "\n\n")); - - return SVN_NO_ERROR; -} - -static svn_error_t * -close_edit(void *edit_baton, apr_pool_t *pool) -{ - return SVN_NO_ERROR; -} - static svn_error_t * fetch_base_func(const char **filename, void *baton, @@ -1085,47 +162,21 @@ svn_rdump__get_dump_editor(const svn_delta_editor_t **editor, apr_pool_t *pool) { struct dump_edit_baton *eb; - svn_delta_editor_t *de; svn_delta_shim_callbacks_t *shim_callbacks = svn_delta_shim_callbacks_default(pool); eb = apr_pcalloc(pool, sizeof(struct dump_edit_baton)); - eb->stream = stream; eb->ra_session = ra_session; - eb->update_anchor_relpath = update_anchor_relpath; eb->current_revision = revision; - eb->pending_db = NULL; - - /* Create a special per-revision pool */ - eb->pool = svn_pool_create(pool); - - /* Open a unique temporary file for all textdelta applications in - this edit session. The file is automatically closed and cleaned - up when the edit session is done. */ - SVN_ERR(svn_io_open_unique_file3(&(eb->delta_file), &(eb->delta_abspath), - NULL, svn_io_file_del_on_close, pool, pool)); - - de = svn_delta_default_editor(pool); - de->open_root = open_root; - de->delete_entry = delete_entry; - de->add_directory = add_directory; - de->open_directory = open_directory; - de->close_directory = close_directory; - de->change_dir_prop = change_dir_prop; - de->change_file_prop = change_file_prop; - de->apply_textdelta = apply_textdelta; - de->add_file = add_file; - de->open_file = open_file; - de->close_file = close_file; - de->close_edit = close_edit; - /* Set the edit_baton and editor. */ - *edit_baton = eb; - *editor = de; + SVN_ERR(svn_repos__get_dump_editor(editor, edit_baton, + stream, update_anchor_relpath, pool)); /* Wrap this editor in a cancellation editor. */ SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton, - de, eb, editor, edit_baton, pool)); + *editor, *edit_baton, + editor, edit_baton, + pool)); shim_callbacks->fetch_base_func = fetch_base_func; shim_callbacks->fetch_props_func = fetch_props_func; |