summaryrefslogtreecommitdiff
path: root/subversion/svnrdump/dump_editor.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/svnrdump/dump_editor.c')
-rw-r--r--subversion/svnrdump/dump_editor.c943
1 files changed, 665 insertions, 278 deletions
diff --git a/subversion/svnrdump/dump_editor.c b/subversion/svnrdump/dump_editor.c
index c51becd..faf029f 100644
--- a/subversion/svnrdump/dump_editor.c
+++ b/subversion/svnrdump/dump_editor.c
@@ -30,9 +30,12 @@
#include "svn_subst.h"
#include "svn_dirent_uri.h"
-#include "private/svn_fspath.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))
@@ -49,24 +52,72 @@ struct dir_baton
struct dump_edit_baton *eb;
struct dir_baton *parent_dir_baton;
+ /* Pool for per-directory allocations */
+ apr_pool_t *pool;
+
/* is this directory a new addition to this revision? */
svn_boolean_t added;
/* has this directory been written to the output stream? */
svn_boolean_t written_out;
- /* the absolute path to this directory */
- const char *abspath; /* an fspath */
+ /* 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;
+ /* 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;
+
+ /* Flags to trigger dumping props and record termination newlines. */
+ svn_boolean_t dump_props;
+ svn_boolean_t dump_newlines;
+};
+
+/* A file baton used by all file-related callback functions in the dump
+ * editor */
+struct file_baton
+{
+ struct dump_edit_baton *eb;
+ struct dir_baton *parent_dir_baton;
+
+ /* 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;
};
/* A handler baton to be used in window_handler(). */
@@ -81,20 +132,19 @@ struct dump_edit_baton {
/* The output stream we write the dumpfile to */
svn_stream_t *stream;
- /* Pool for per-revision allocations */
- apr_pool_t *pool;
+ /* A backdoor ra session to fetch additional information during the edit. */
+ svn_ra_session_t *ra_session;
- /* Properties which were modified during change_file_prop
- * or change_dir_prop. */
- apr_hash_t *props;
-
- /* Properties which were deleted during change_file_prop
- * or change_dir_prop. */
- apr_hash_t *deleted_props;
+ /* 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;
- /* Temporary buffer to write property hashes to in human-readable
- * form. ### Is this really needed? */
- svn_stringbuf_t *propstring;
+ /* 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
@@ -102,13 +152,14 @@ struct dump_edit_baton {
const char *delta_abspath;
apr_file_t *delta_file;
- /* The checksum of the file the delta is being applied to */
- const char *base_checksum;
+ /* The revision we're currently dumping. */
+ svn_revnum_t current_revision;
- /* Flags to trigger dumping props and text */
- svn_boolean_t dump_text;
- svn_boolean_t dump_props;
- svn_boolean_t dump_newlines;
+ /* The kind (file or directory) and baton of the item whose block of
+ dump stream data has not been fully completed; NULL if there's no
+ such item. */
+ svn_node_kind_t pending_kind;
+ void *pending_baton;
};
/* Make a directory baton to represent the directory at PATH (relative
@@ -119,32 +170,28 @@ struct dump_edit_baton {
* information is valid, the directory will be compared against its
* copy source.
*
- * PARENT_DIR_BATON is the directory baton of this directory's parent,
- * or NULL if this is the top-level directory of the edit. ADDED
- * indicates if this directory is newly added in this revision.
- * Perform all allocations in POOL. */
+ * PB is the directory baton of this directory's parent, or NULL if
+ * this is the top-level directory of the edit. ADDED indicates if
+ * this directory is newly added in this revision. 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,
- void *parent_dir_baton,
+ struct dir_baton *pb,
svn_boolean_t added,
apr_pool_t *pool)
{
struct dump_edit_baton *eb = edit_baton;
- struct dir_baton *pb = parent_dir_baton;
struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
- const char *abspath;
+ const char *repos_relpath;
/* Construct the full path of this node. */
- /* ### FIXME: Not sure why we use an abspath here. If I understand
- ### correctly, the only place we used this path is in dump_node(),
- ### which immediately converts it into a relpath. -- cmpilato. */
if (pb)
- abspath = svn_fspath__canonicalize(path, pool);
+ repos_relpath = svn_relpath_canonicalize(path, pool);
else
- abspath = "/";
+ repos_relpath = "";
/* Strip leading slash from copyfrom_path so that the path is
canonical and svn_relpath_join can be used */
@@ -153,68 +200,133 @@ make_dir_baton(const char *path,
new_db->eb = eb;
new_db->parent_dir_baton = pb;
- new_db->abspath = abspath;
- new_db->copyfrom_path = copyfrom_path;
+ 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->added = added;
new_db->written_out = FALSE;
+ 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;
}
-/* Extract and dump properties stored in edit baton EB, using POOL for
- * any temporary allocations. If TRIGGER_VAR is not NULL, it is set to FALSE.
- * Unless DUMP_DATA_TOO is set, only property headers are dumped.
- */
+/* 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->parent_dir_baton = pb;
+ 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;
+}
+
+/* Return in *HEADER and *CONTENT the headers and content for PROPS. */
static svn_error_t *
-do_dump_props(struct dump_edit_baton *eb,
- svn_boolean_t *trigger_var,
- svn_boolean_t dump_data_too,
- apr_pool_t *pool)
+get_props_content(svn_stringbuf_t **header,
+ 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 *propstream;
+ svn_stream_t *content_stream;
apr_hash_t *normal_props;
+ const char *buf;
- if (trigger_var && !*trigger_var)
- return SVN_NO_ERROR;
+ *content = svn_stringbuf_create_empty(result_pool);
+ *header = svn_stringbuf_create_empty(result_pool);
- SVN_ERR(svn_rdump__normalize_props(&normal_props, eb->props, eb->pool));
- svn_stringbuf_setempty(eb->propstring);
- propstream = svn_stream_from_stringbuf(eb->propstring, eb->pool);
- SVN_ERR(svn_hash_write_incremental(normal_props, eb->deleted_props,
- propstream, "PROPS-END", pool));
- SVN_ERR(svn_stream_close(propstream));
+ 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_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_PROP_DELTA
- ": true\n"));
+ *header = svn_stringbuf_createf(result_pool, SVN_REPOS_DUMPFILE_PROP_DELTA
+ ": true\n");
/* Prop-content-length: 193 */
- SVN_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
- ": %" APR_SIZE_T_FMT "\n", eb->propstring->len));
+ buf = apr_psprintf(scratch_pool, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH
+ ": %" APR_SIZE_T_FMT "\n", (*content)->len);
+ svn_stringbuf_appendcstr(*header, buf);
+
+ return SVN_NO_ERROR;
+}
+
+/* Extract and dump properties stored in PROPS and property deletions
+ * stored in DELETED_PROPS. If TRIGGER_VAR is not NULL, it is set to
+ * FALSE.
+ *
+ * If PROPSTRING is non-NULL, set *PROPSTRING to a string containing
+ * the content block of the property changes; otherwise, dump that to
+ * the stream, too.
+ */
+static svn_error_t *
+do_dump_props(svn_stringbuf_t **propstring,
+ svn_stream_t *stream,
+ apr_hash_t *props,
+ apr_hash_t *deleted_props,
+ svn_boolean_t *trigger_var,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_stringbuf_t *header;
+ svn_stringbuf_t *content;
+ apr_size_t len;
- if (dump_data_too)
+ if (trigger_var && !*trigger_var)
+ return SVN_NO_ERROR;
+
+ SVN_ERR(get_props_content(&header, &content, props, deleted_props,
+ result_pool, scratch_pool));
+ len = header->len;
+ SVN_ERR(svn_stream_write(stream, header->data, &len));
+
+ if (propstring)
+ {
+ *propstring = content;
+ }
+ else
{
/* Content-length: 14 */
- SVN_ERR(svn_stream_printf(eb->stream, pool,
+ SVN_ERR(svn_stream_printf(stream, scratch_pool,
SVN_REPOS_DUMPFILE_CONTENT_LENGTH
": %" APR_SIZE_T_FMT "\n\n",
- eb->propstring->len));
+ content->len));
- /* The properties. */
- SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
- &(eb->propstring->len)));
+ len = content->len;
+ SVN_ERR(svn_stream_write(stream, content->data, &len));
/* No text is going to be dumped. Write a couple of newlines and
wait for the next node/ revision. */
- SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
+ SVN_ERR(svn_stream_puts(stream, "\n\n"));
/* Cleanup so that data is never dumped twice. */
- SVN_ERR(svn_hash__clear(eb->props, eb->pool));
- SVN_ERR(svn_hash__clear(eb->deleted_props, eb->pool));
+ apr_hash_clear(props);
+ apr_hash_clear(deleted_props);
if (trigger_var)
*trigger_var = FALSE;
}
@@ -229,7 +341,7 @@ do_dump_newlines(struct dump_edit_baton *eb,
{
if (trigger_var && *trigger_var)
{
- SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
+ SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
*trigger_var = FALSE;
}
return SVN_NO_ERROR;
@@ -248,30 +360,36 @@ do_dump_newlines(struct dump_edit_baton *eb,
*/
static svn_error_t *
dump_node(struct dump_edit_baton *eb,
- const char *path, /* an absolute path. */
- svn_node_kind_t kind,
+ 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)
{
- /* Remove leading slashes from path and copyfrom_path */
- if (path)
- path = svn_relpath_canonicalize(path, pool);
+ const char *node_relpath = repos_relpath;
- if (copyfrom_path)
- copyfrom_path = svn_relpath_canonicalize(copyfrom_path, pool);
+ assert(svn_relpath_is_canonical(repos_relpath));
+ assert(!copyfrom_path || svn_relpath_is_canonical(copyfrom_path));
+ assert(! (db && fb));
- /* Node-path: commons/STATUS */
+ /* 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_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", path));
+ SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
+ node_relpath));
- /* Node-kind: file */
- if (kind == svn_node_file)
+ /* Node-kind: "file" | "dir" */
+ if (fb)
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
- else if (kind == svn_node_dir)
+ else if (db)
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
@@ -282,88 +400,99 @@ dump_node(struct dump_edit_baton *eb,
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 */
- SVN_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION
- ": change\n"));
+ do here but print node action information.
+
+ Node-action: change. */
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n"));
break;
case svn_node_action_replace:
- if (!is_copy)
+ if (is_copy)
+ {
+ /* Delete the original, and then re-add the replacement as a
+ copy using recursive calls into this function. */
+ SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_delete,
+ FALSE, NULL, SVN_INVALID_REVNUM, pool));
+ SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_add,
+ is_copy, copyfrom_path, copyfrom_rev, pool));
+ }
+ else
{
/* Node-action: replace */
- SVN_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION
- ": replace\n"));
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_NODE_ACTION
+ ": replace\n"));
/* Wait for a change_*_prop to be called before dumping
anything */
- eb->dump_props = TRUE;
- break;
+ if (fb)
+ fb->dump_props = TRUE;
+ else if (db)
+ db->dump_props = TRUE;
}
- /* More complex case: is_copy is true, and copyfrom_path/
- copyfrom_rev are present: delete the original, and then re-add
- it */
-
- 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, copyfrom_path, copyfrom_rev, pool));
-
- /* We can leave this routine quietly now, don't need to dump any
- content; that was already done in the second record. */
break;
case svn_node_action_delete:
- SVN_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION
- ": delete\n"));
+ /* Node-action: delete */
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n"));
/* We can leave this routine quietly now. Nothing more to do-
print a couple of newlines because we're not dumping props or
text. */
- SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
+ SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
+
break;
case svn_node_action_add:
- SVN_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
+ /* Node-action: add */
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
- if (!is_copy)
+ if (is_copy)
{
- /* eb->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;
- eb->dump_props for directories is handled 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. */
-
- /* Wait for a change_*_prop to be called before dumping
- anything */
- eb->dump_props = TRUE;
- break;
+ /* Node-copyfrom-rev / Node-copyfrom-path */
+ SVN_ERR(svn_stream_printf(eb->stream, pool,
+ SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
+ ": %ld\n"
+ SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
+ ": %s\n",
+ copyfrom_rev, copyfrom_path));
+
+ /* Ugly hack: If a directory was copied from a previous
+ revision, nothing like close_file() will be called to write two
+ blank lines. If change_dir_prop() is called, props are dumped
+ (along with the necessary PROPS-END\n\n and we're good. So
+ set DUMP_NEWLINES here to print the newlines unless
+ change_dir_prop() is called next otherwise the `svnadmin load`
+ parser will fail. */
+ if (db)
+ db->dump_newlines = TRUE;
+ }
+ 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;
}
-
- SVN_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
- ": %ld\n"
- SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
- ": %s\n",
- copyfrom_rev, copyfrom_path));
-
- /* Ugly hack: If a directory was copied from a previous
- revision, nothing like close_file() will be called to write two
- blank lines. If change_dir_prop() is called, props are dumped
- (along with the necessary PROPS-END\n\n and we're good. So
- set DUMP_NEWLINES here to print the newlines unless
- change_dir_prop() is called next otherwise the `svnadmin load`
- parser will fail. */
- if (kind == svn_node_dir)
- eb->dump_newlines = TRUE;
break;
}
@@ -371,24 +500,156 @@ dump_node(struct dump_edit_baton *eb,
}
static svn_error_t *
+dump_mkdir(struct dump_edit_baton *eb,
+ const char *repos_relpath,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *prop_header, *prop_content;
+ apr_size_t len;
+ const char *buf;
+
+ /* Node-path: ... */
+ SVN_ERR(svn_stream_printf(eb->stream, pool,
+ SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
+ repos_relpath));
+
+ /* Node-kind: dir */
+ SVN_ERR(svn_stream_printf(eb->stream, pool,
+ SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
+
+ /* Node-action: add */
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
+
+ /* Dump the (empty) property block. */
+ SVN_ERR(get_props_content(&prop_header, &prop_content,
+ apr_hash_make(pool), apr_hash_make(pool),
+ pool, pool));
+ len = prop_header->len;
+ SVN_ERR(svn_stream_write(eb->stream, prop_header->data, &len));
+ len = prop_content->len;
+ buf = apr_psprintf(pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+ ": %" APR_SIZE_T_FMT "\n", len);
+ SVN_ERR(svn_stream_puts(eb->stream, buf));
+ SVN_ERR(svn_stream_puts(eb->stream, "\n"));
+ SVN_ERR(svn_stream_write(eb->stream, prop_content->data, &len));
+
+ /* Newlines to tie it all off. */
+ SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
+
+ return SVN_NO_ERROR;
+}
+
+/* Dump pending items from the specified node, to allow starting the dump
+ of a child node */
+static svn_error_t *
+dump_pending(struct dump_edit_baton *eb,
+ apr_pool_t *scratch_pool)
+{
+ if (! eb->pending_baton)
+ return SVN_NO_ERROR;
+
+ if (eb->pending_kind == svn_node_dir)
+ {
+ struct dir_baton *db = eb->pending_baton;
+
+ /* Some pending properties to dump? */
+ SVN_ERR(do_dump_props(NULL, eb->stream, db->props, db->deleted_props,
+ &(db->dump_props), db->pool, scratch_pool));
+
+ /* Some pending newlines to dump? */
+ SVN_ERR(do_dump_newlines(eb, &(db->dump_newlines), scratch_pool));
+ }
+ else if (eb->pending_kind == svn_node_file)
+ {
+ struct file_baton *fb = eb->pending_baton;
+
+ /* Some pending properties to dump? */
+ SVN_ERR(do_dump_props(NULL, eb->stream, fb->props, fb->deleted_props,
+ &(fb->dump_props), fb->pool, scratch_pool));
+ }
+ else
+ abort();
+
+ /* Anything that was pending is pending no longer. */
+ eb->pending_baton = NULL;
+ eb->pending_kind = svn_node_none;
+
+ 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);
- eb->props = apr_hash_make(eb->pool);
- 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,
- edit_baton, NULL, FALSE, eb->pool);
LDR_DBG(("open_root %p\n", *root_baton));
+ 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, TRUE, pool);
+ SVN_ERR(dump_node(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. */
+ new_db->written_out = TRUE;
+ eb->pending_baton = new_db;
+ eb->pending_kind = svn_node_dir;
+ }
+ }
+ svn_pool_destroy(iterpool);
+ }
+
+ if (! new_db)
+ {
+ new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
+ edit_baton, NULL, FALSE, pool);
+ }
+
+ *root_baton = new_db;
return SVN_NO_ERROR;
}
@@ -402,16 +663,13 @@ delete_entry(const char *path,
LDR_DBG(("delete_entry %s\n", path));
- /* Some pending properties to dump? */
- SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
-
- /* Some pending newlines to dump? */
- SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
+ SVN_ERR(dump_pending(pb->eb, pool));
- /* Add this path to the deleted_entries of the parent directory
- baton. */
- apr_hash_set(pb->deleted_entries, apr_pstrdup(pb->eb->pool, path),
- APR_HASH_KEY_STRING, pb);
+ /* 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->eb->pool, path), pb);
return SVN_NO_ERROR;
}
@@ -431,35 +689,34 @@ add_directory(const char *path,
LDR_DBG(("add_directory %s\n", path));
+ SVN_ERR(dump_pending(pb->eb, pool));
+
new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb,
pb, TRUE, pb->eb->pool);
- /* Some pending properties to dump? */
- SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
-
- /* Some pending newlines to dump? */
- SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
-
/* This might be a replacement -- is the path already deleted? */
- val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
+ val = 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(pb->eb, path,
- svn_node_dir,
+ SVN_ERR(dump_node(pb->eb, new_db->repos_relpath, new_db, NULL,
val ? svn_node_action_replace : svn_node_action_add,
is_copy,
- is_copy ? copyfrom_path : NULL,
+ is_copy ? new_db->copyfrom_path : NULL,
is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
pool));
if (val)
/* Delete the path, it's now been dumped */
- apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
+ svn_hash_sets(pb->deleted_entries, path, NULL);
+ /* Remember that we've started, but not yet finished handling this
+ directory. */
new_db->written_out = TRUE;
+ pb->eb->pending_baton = new_db;
+ pb->eb->pending_kind = svn_node_dir;
*child_baton = new_db;
return SVN_NO_ERROR;
@@ -479,11 +736,7 @@ open_directory(const char *path,
LDR_DBG(("open_directory %s\n", path));
- /* Some pending properties to dump? */
- SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
-
- /* Some pending newlines to dump? */
- SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
+ SVN_ERR(dump_pending(pb->eb, pool));
/* If the parent directory has explicit comparison path and rev,
record the same for this one. */
@@ -497,6 +750,7 @@ open_directory(const char *path,
new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb,
FALSE, pb->eb->pool);
+
*child_baton = new_db;
return SVN_NO_ERROR;
}
@@ -506,16 +760,34 @@ close_directory(void *dir_baton,
apr_pool_t *pool)
{
struct dir_baton *db = dir_baton;
- struct dump_edit_baton *eb = db->eb;
apr_hash_index_t *hi;
+ svn_boolean_t this_pending;
LDR_DBG(("close_directory %p\n", dir_baton));
- /* Some pending properties to dump? */
- SVN_ERR(do_dump_props(eb, &(eb->dump_props), TRUE, pool));
+ /* Remember if this directory is the one currently pending. */
+ this_pending = (db->eb->pending_baton == db);
- /* Some pending newlines to dump? */
- SVN_ERR(do_dump_newlines(eb, &(eb->dump_newlines), pool));
+ SVN_ERR(dump_pending(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->eb, db->repos_relpath, db, NULL,
+ svn_node_action_change, FALSE,
+ NULL, SVN_INVALID_REVNUM, pool));
+ db->eb->pending_baton = db;
+ db->eb->pending_kind = svn_node_dir;
+ SVN_ERR(dump_pending(db->eb, pool));
+ }
/* Dump the deleted directory entries */
for (hi = apr_hash_first(pool, db->deleted_entries); hi;
@@ -523,11 +795,13 @@ close_directory(void *dir_baton,
{
const char *path = svn__apr_hash_index_key(hi);
- SVN_ERR(dump_node(db->eb, path, svn_node_unknown, svn_node_action_delete,
+ SVN_ERR(dump_node(db->eb, path, NULL, NULL, svn_node_action_delete,
FALSE, NULL, SVN_INVALID_REVNUM, pool));
}
- SVN_ERR(svn_hash__clear(db->deleted_entries, pool));
+ /* ### should be unnecessary */
+ apr_hash_clear(db->deleted_entries);
+
return SVN_NO_ERROR;
}
@@ -540,40 +814,33 @@ add_file(const char *path,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
+ struct file_baton *fb;
void *val;
- svn_boolean_t is_copy;
LDR_DBG(("add_file %s\n", path));
- /* Some pending properties to dump? */
- SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
+ SVN_ERR(dump_pending(pb->eb, pool));
- /* Some pending newlines to dump? */
- SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), pool));
+ /* Make the file baton. */
+ fb = make_file_baton(path, pb, pool);
/* This might be a replacement -- is the path already deleted? */
- val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
+ val = svn_hash_gets(pb->deleted_entries, path);
/* Detect add-with-history. */
- is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
-
- /* Dump the node. */
- SVN_ERR(dump_node(pb->eb, path,
- svn_node_file,
- val ? svn_node_action_replace : svn_node_action_add,
- is_copy,
- is_copy ? copyfrom_path : NULL,
- is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
- pool));
+ 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 = val ? svn_node_action_replace : svn_node_action_add;
+ /* Delete the path, it's now been dumped. */
if (val)
- /* delete the path, it's now been dumped. */
- apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
-
- /* Build a nice file baton to pass to change_file_prop and
- apply_textdelta */
- *file_baton = pb->eb;
+ svn_hash_sets(pb->deleted_entries, path, NULL);
+ *file_baton = fb;
return SVN_NO_ERROR;
}
@@ -585,34 +852,26 @@ open_file(const char *path,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
- const char *copyfrom_path = NULL;
- svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
+ struct file_baton *fb;
LDR_DBG(("open_file %s\n", path));
- /* Some pending properties to dump? */
- SVN_ERR(do_dump_props(pb->eb, &(pb->eb->dump_props), TRUE, pool));
+ SVN_ERR(dump_pending(pb->eb, pool));
- /* Some pending newlines to dump? */
- SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), 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))
{
- copyfrom_path = svn_relpath_join(pb->copyfrom_path,
- svn_relpath_basename(path, NULL),
- pb->eb->pool);
- copyfrom_rev = pb->copyfrom_rev;
+ fb->copyfrom_path = svn_relpath_join(pb->copyfrom_path,
+ svn_relpath_basename(path, NULL),
+ pb->eb->pool);
+ fb->copyfrom_rev = pb->copyfrom_rev;
}
- SVN_ERR(dump_node(pb->eb, path, 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
- apply_textdelta */
- *file_baton = pb->eb;
-
+ *file_baton = fb;
return SVN_NO_ERROR;
}
@@ -623,37 +882,30 @@ change_dir_prop(void *parent_baton,
apr_pool_t *pool)
{
struct dir_baton *db = parent_baton;
+ svn_boolean_t this_pending;
LDR_DBG(("change_dir_prop %p\n", parent_baton));
- if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
+ /* This directory is not pending, but something else is, so handle
+ the "something else". */
+ this_pending = (db->eb->pending_baton == db);
+ if (! this_pending)
+ SVN_ERR(dump_pending(db->eb, pool));
+
+ if (svn_property_kind2(name) != svn_prop_regular_kind)
return SVN_NO_ERROR;
if (value)
- apr_hash_set(db->eb->props, apr_pstrdup(db->eb->pool, name),
- APR_HASH_KEY_STRING, svn_string_dup(value, db->eb->pool));
+ svn_hash_sets(db->props,
+ apr_pstrdup(db->pool, name),
+ svn_string_dup(value, db->pool));
else
- apr_hash_set(db->eb->deleted_props, apr_pstrdup(db->eb->pool, name),
- APR_HASH_KEY_STRING, "");
-
- if (! db->written_out)
- {
- /* If db->written_out is set, it means that the node information
- corresponding to this directory has already been written: don't
- do anything; do_dump_props() will take care of dumping the
- 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));
- db->written_out = TRUE;
- }
+ svn_hash_sets(db->deleted_props, apr_pstrdup(db->pool, name), "");
/* Make sure we eventually output the props, and disable printing
a couple of extra newlines */
- db->eb->dump_newlines = FALSE;
- db->eb->dump_props = TRUE;
+ db->dump_newlines = FALSE;
+ db->dump_props = TRUE;
return SVN_NO_ERROR;
}
@@ -664,24 +916,24 @@ change_file_prop(void *file_baton,
const svn_string_t *value,
apr_pool_t *pool)
{
- struct dump_edit_baton *eb = file_baton;
+ struct file_baton *fb = file_baton;
LDR_DBG(("change_file_prop %p\n", file_baton));
- if (svn_property_kind(NULL, name) != svn_prop_regular_kind)
+ if (svn_property_kind2(name) != svn_prop_regular_kind)
return SVN_NO_ERROR;
if (value)
- apr_hash_set(eb->props, apr_pstrdup(eb->pool, name),
- APR_HASH_KEY_STRING, svn_string_dup(value, eb->pool));
+ svn_hash_sets(fb->props,
+ apr_pstrdup(fb->pool, name),
+ svn_string_dup(value, fb->pool));
else
- apr_hash_set(eb->deleted_props, apr_pstrdup(eb->pool, name),
- APR_HASH_KEY_STRING, "");
+ 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 */
- eb->dump_props = TRUE;
+ fb->dump_props = TRUE;
return SVN_NO_ERROR;
}
@@ -708,17 +960,17 @@ apply_textdelta(void *file_baton, const char *base_checksum,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
- struct dump_edit_baton *eb = file_baton;
-
- /* Custom handler_baton allocated in a separate pool */
+ struct file_baton *fb = file_baton;
+ struct dump_edit_baton *eb = fb->eb;
struct handler_baton *hb;
svn_stream_t *delta_filestream;
- hb = apr_pcalloc(eb->pool, sizeof(*hb));
-
LDR_DBG(("apply_textdelta %p\n", file_baton));
- /* Use a temporary file to measure the text-content-length */
+ /* This is custom handler_baton, allocated from a separate pool. */
+ hb = apr_pcalloc(eb->pool, sizeof(*hb));
+
+ /* 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 */
@@ -726,9 +978,9 @@ apply_textdelta(void *file_baton, const char *base_checksum,
delta_filestream, 0,
SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
- eb->dump_text = TRUE;
- eb->base_checksum = apr_pstrdup(eb->pool, base_checksum);
- SVN_ERR(svn_stream_close(delta_filestream));
+ /* Record that there's text to be dumped, and its base checksum. */
+ fb->dump_text = TRUE;
+ fb->base_checksum = apr_pstrdup(eb->pool, base_checksum);
/* The actual writing takes place when this function has
finished. Set handler and handler_baton now so for
@@ -744,35 +996,46 @@ close_file(void *file_baton,
const char *text_checksum,
apr_pool_t *pool)
{
- struct dump_edit_baton *eb = file_baton;
+ struct file_baton *fb = file_baton;
+ struct dump_edit_baton *eb = fb->eb;
apr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t));
+ svn_stringbuf_t *propstring;
LDR_DBG(("close_file %p\n", file_baton));
- /* Some pending properties to dump? Dump just the headers- dump the
- props only after dumping the text headers too (if present) */
- SVN_ERR(do_dump_props(eb, &(eb->dump_props), FALSE, pool));
+ SVN_ERR(dump_pending(eb, pool));
+
+ /* Dump the node. */
+ SVN_ERR(dump_node(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). */
+ SVN_ERR(do_dump_props(&propstring, eb->stream, fb->props, fb->deleted_props,
+ &(fb->dump_props), pool, pool));
/* Dump the text headers */
- if (eb->dump_text)
+ if (fb->dump_text)
{
apr_status_t err;
/* Text-delta: true */
- SVN_ERR(svn_stream_printf(eb->stream, pool,
- SVN_REPOS_DUMPFILE_TEXT_DELTA
- ": true\n"));
+ SVN_ERR(svn_stream_puts(eb->stream,
+ SVN_REPOS_DUMPFILE_TEXT_DELTA
+ ": true\n"));
err = apr_file_info_get(info, APR_FINFO_SIZE, eb->delta_file);
if (err)
SVN_ERR(svn_error_wrap_apr(err, NULL));
- if (eb->base_checksum)
+ if (fb->base_checksum)
/* Text-delta-base-md5: */
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_MD5
": %s\n",
- eb->base_checksum));
+ fb->base_checksum));
/* Text-content-length: 39 */
SVN_ERR(svn_stream_printf(eb->stream, pool,
@@ -789,31 +1052,31 @@ close_file(void *file_baton,
/* Content-length: 1549 */
/* If both text and props are absent, skip this header */
- if (eb->dump_props)
+ if (fb->dump_props)
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_CONTENT_LENGTH
": %ld\n\n",
- (unsigned long)info->size + eb->propstring->len));
- else if (eb->dump_text)
+ (unsigned long)info->size + propstring->len));
+ else if (fb->dump_text)
SVN_ERR(svn_stream_printf(eb->stream, pool,
SVN_REPOS_DUMPFILE_CONTENT_LENGTH
": %ld\n\n",
(unsigned long)info->size));
/* Dump the props now */
- if (eb->dump_props)
+ if (fb->dump_props)
{
- SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
- &(eb->propstring->len)));
+ SVN_ERR(svn_stream_write(eb->stream, propstring->data,
+ &(propstring->len)));
/* Cleanup */
- eb->dump_props = FALSE;
- SVN_ERR(svn_hash__clear(eb->props, eb->pool));
- SVN_ERR(svn_hash__clear(eb->deleted_props, eb->pool));
+ fb->dump_props = FALSE;
+ apr_hash_clear(fb->props);
+ apr_hash_clear(fb->deleted_props);
}
/* Dump the text */
- if (eb->dump_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
@@ -830,12 +1093,11 @@ close_file(void *file_baton,
/* Cleanup */
SVN_ERR(svn_stream_close(delta_filestream));
SVN_ERR(svn_io_file_trunc(eb->delta_file, 0, pool));
- eb->dump_text = FALSE;
}
/* Write a couple of blank lines for matching output with `svnadmin
dump` */
- SVN_ERR(svn_stream_printf(eb->stream, pool, "\n\n"));
+ SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
return SVN_NO_ERROR;
}
@@ -846,19 +1108,134 @@ 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,
+ const char *path,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct dump_edit_baton *eb = baton;
+ svn_stream_t *fstream;
+ svn_error_t *err;
+
+ if (path[0] == '/')
+ path += 1;
+
+ if (! SVN_IS_VALID_REVNUM(base_revision))
+ base_revision = eb->current_revision - 1;
+
+ SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ result_pool, scratch_pool));
+
+ err = svn_ra_get_file(eb->ra_session, path, base_revision,
+ fstream, NULL, NULL, scratch_pool);
+ if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ SVN_ERR(svn_stream_close(fstream));
+
+ *filename = NULL;
+ return SVN_NO_ERROR;
+ }
+ else if (err)
+ return svn_error_trace(err);
+
+ SVN_ERR(svn_stream_close(fstream));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_props_func(apr_hash_t **props,
+ void *baton,
+ const char *path,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct dump_edit_baton *eb = baton;
+ svn_node_kind_t node_kind;
+
+ if (path[0] == '/')
+ path += 1;
+
+ if (! SVN_IS_VALID_REVNUM(base_revision))
+ base_revision = eb->current_revision - 1;
+
+ SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, &node_kind,
+ scratch_pool));
+
+ if (node_kind == svn_node_file)
+ {
+ SVN_ERR(svn_ra_get_file(eb->ra_session, path, base_revision,
+ NULL, NULL, props, result_pool));
+ }
+ else if (node_kind == svn_node_dir)
+ {
+ apr_array_header_t *tmp_props;
+
+ SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, props, path,
+ base_revision, 0 /* Dirent fields */,
+ result_pool));
+ tmp_props = svn_prop_hash_to_array(*props, result_pool);
+ SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
+ result_pool));
+ *props = svn_prop_array_to_hash(tmp_props, result_pool);
+ }
+ else
+ {
+ *props = apr_hash_make(result_pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_kind_func(svn_node_kind_t *kind,
+ void *baton,
+ const char *path,
+ svn_revnum_t base_revision,
+ apr_pool_t *scratch_pool)
+{
+ struct dump_edit_baton *eb = baton;
+
+ if (path[0] == '/')
+ path += 1;
+
+ if (! SVN_IS_VALID_REVNUM(base_revision))
+ base_revision = eb->current_revision - 1;
+
+ SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, kind,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_rdump__get_dump_editor(const svn_delta_editor_t **editor,
void **edit_baton,
+ svn_revnum_t revision,
svn_stream_t *stream,
+ svn_ra_session_t *ra_session,
+ const char *update_anchor_relpath,
svn_cancel_func_t cancel_func,
void *cancel_baton,
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_kind = svn_node_none;
/* Create a special per-revision pool */
eb->pool = svn_pool_create(pool);
@@ -888,6 +1265,16 @@ svn_rdump__get_dump_editor(const svn_delta_editor_t **editor,
*editor = de;
/* Wrap this editor in a cancellation editor. */
- return svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
- de, eb, editor, edit_baton, pool);
+ SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
+ de, eb, editor, edit_baton, pool));
+
+ shim_callbacks->fetch_base_func = fetch_base_func;
+ shim_callbacks->fetch_props_func = fetch_props_func;
+ shim_callbacks->fetch_kind_func = fetch_kind_func;
+ shim_callbacks->fetch_baton = eb;
+
+ SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
+ NULL, NULL, shim_callbacks, pool, pool));
+
+ return SVN_NO_ERROR;
}