diff options
Diffstat (limited to 'subversion/libsvn_client/export.c')
-rw-r--r-- | subversion/libsvn_client/export.c | 1225 |
1 files changed, 753 insertions, 472 deletions
diff --git a/subversion/libsvn_client/export.c b/subversion/libsvn_client/export.c index 5c684d5..c14a5f0 100644 --- a/subversion/libsvn_client/export.c +++ b/subversion/libsvn_client/export.c @@ -34,6 +34,7 @@ #include "svn_string.h" #include "svn_error.h" #include "svn_dirent_uri.h" +#include "svn_hash.h" #include "svn_path.h" #include "svn_pools.h" #include "svn_subst.h" @@ -42,8 +43,14 @@ #include "client.h" #include "svn_private_config.h" +#include "private/svn_subr_private.h" +#include "private/svn_delta_private.h" #include "private/svn_wc_private.h" +#ifndef ENABLE_EV2_IMPL +#define ENABLE_EV2_IMPL 0 +#endif + /*** Code. ***/ @@ -62,9 +69,9 @@ add_externals(apr_hash_t *externals, SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); - apr_hash_set(externals, local_abspath, APR_HASH_KEY_STRING, - apr_pstrmemdup(pool, externals_prop_val->data, - externals_prop_val->len)); + svn_hash_sets(externals, local_abspath, + apr_pstrmemdup(pool, externals_prop_val->data, + externals_prop_val->len)); return SVN_NO_ERROR; } @@ -150,15 +157,31 @@ append_basename_if_dir(const char **appendable_dirent_p, * Set the destination file's 'executable' flag according to the source * file's 'svn:executable' property. */ + +/* baton for export_node */ +struct export_info_baton +{ + const char *to_path; + const svn_opt_revision_t *revision; + svn_boolean_t ignore_keywords; + svn_boolean_t overwrite; + svn_wc_context_t *wc_ctx; + const char *native_eol; + svn_wc_notify_func2_t notify_func; + void *notify_baton; + const char *origin_abspath; + svn_boolean_t exported; +}; + +/* Export a file or directory. Implements svn_wc_status_func4_t */ static svn_error_t * -copy_one_versioned_file(const char *from_abspath, - const char *to_abspath, - svn_client_ctx_t *ctx, - const svn_opt_revision_t *revision, - const char *native_eol, - svn_boolean_t ignore_keywords, - apr_pool_t *scratch_pool) +export_node(void *baton, + const char *local_abspath, + const svn_wc_status3_t *status, + apr_pool_t *scratch_pool) { + struct export_info_baton *eib = baton; + svn_wc_context_t *wc_ctx = eib->wc_ctx; apr_hash_t *kw = NULL; svn_subst_eol_style_t style; apr_hash_t *props; @@ -170,19 +193,108 @@ copy_one_versioned_file(const char *from_abspath, svn_stream_t *dst_stream; const char *dst_tmp; svn_error_t *err; - svn_boolean_t is_deleted; - svn_wc_context_t *wc_ctx = ctx->wc_ctx; - SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, wc_ctx, from_abspath, - scratch_pool)); + const char *to_abspath = svn_dirent_join( + eib->to_path, + svn_dirent_skip_ancestor(eib->origin_abspath, + local_abspath), + scratch_pool); + + eib->exported = TRUE; /* Don't export 'deleted' files and directories unless it's a revision other than WORKING. These files and directories don't really exist in WORKING. */ - if (revision->kind == svn_opt_revision_working && is_deleted) + if (eib->revision->kind == svn_opt_revision_working + && status->node_status == svn_wc_status_deleted) return SVN_NO_ERROR; - if (revision->kind != svn_opt_revision_working) + if (status->kind == svn_node_dir) + { + apr_fileperms_t perm = APR_OS_DEFAULT; + + /* Try to make the new directory. If this fails because the + directory already exists, check our FORCE flag to see if we + care. */ + + /* Keep the source directory's permissions if applicable. + Skip retrieving the umask on windows. Apr does not implement setting + filesystem privileges on Windows. + Retrieving the file permissions with APR_FINFO_PROT | APR_FINFO_OWNER + is documented to be 'incredibly expensive' */ +#ifndef WIN32 + if (eib->revision->kind == svn_opt_revision_working) + { + apr_finfo_t finfo; + SVN_ERR(svn_io_stat(&finfo, local_abspath, APR_FINFO_PROT, + scratch_pool)); + perm = finfo.protection; + } +#endif + err = svn_io_dir_make(to_abspath, perm, scratch_pool); + if (err) + { + if (! APR_STATUS_IS_EEXIST(err->apr_err)) + return svn_error_trace(err); + if (! eib->overwrite) + SVN_ERR_W(err, _("Destination directory exists, and will not be " + "overwritten unless forced")); + else + svn_error_clear(err); + } + + if (eib->notify_func + && (strcmp(eib->origin_abspath, local_abspath) != 0)) + { + svn_wc_notify_t *notify = + svn_wc_create_notify(to_abspath, + svn_wc_notify_update_add, scratch_pool); + + notify->kind = svn_node_dir; + (eib->notify_func)(eib->notify_baton, notify, scratch_pool); + } + + return SVN_NO_ERROR; + } + else if (status->kind != svn_node_file) + { + if (strcmp(eib->origin_abspath, local_abspath) != 0) + return SVN_NO_ERROR; + + return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, + _("The node '%s' was not found."), + svn_dirent_local_style(local_abspath, + scratch_pool)); + } + + /* Skip file externals if they are a descendant of the export, + BUT NOT if we are explictly exporting the file external. */ + if (status->file_external && strcmp(eib->origin_abspath, local_abspath) != 0) + return SVN_NO_ERROR; + + /* Produce overwrite errors for the export root */ + if (strcmp(local_abspath, eib->origin_abspath) == 0) + { + svn_node_kind_t to_kind; + + SVN_ERR(svn_io_check_path(to_abspath, &to_kind, scratch_pool)); + + if ((to_kind == svn_node_file || to_kind == svn_node_unknown) + && !eib->overwrite) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Destination file '%s' exists, and " + "will not be overwritten unless forced"), + svn_dirent_local_style(to_abspath, + scratch_pool)); + else if (to_kind == svn_node_dir) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Destination '%s' exists. Cannot " + "overwrite directory with non-directory"), + svn_dirent_local_style(to_abspath, + scratch_pool)); + } + + if (eib->revision->kind != svn_opt_revision_working) { /* Only export 'added' files when the revision is WORKING. This is not WORKING, so skip the 'added' files, since they didn't exist @@ -207,38 +319,34 @@ copy_one_versioned_file(const char *from_abspath, We get all this for free from evaluating SOURCE == NULL: */ - SVN_ERR(svn_wc_get_pristine_contents2(&source, wc_ctx, from_abspath, + SVN_ERR(svn_wc_get_pristine_contents2(&source, wc_ctx, local_abspath, scratch_pool, scratch_pool)); if (source == NULL) return SVN_NO_ERROR; - SVN_ERR(svn_wc_get_pristine_props(&props, wc_ctx, from_abspath, + SVN_ERR(svn_wc_get_pristine_props(&props, wc_ctx, local_abspath, scratch_pool, scratch_pool)); } else { - svn_wc_status3_t *status; - /* ### hmm. this isn't always a specialfile. this will simply open ### the file readonly if it is a regular file. */ - SVN_ERR(svn_subst_read_specialfile(&source, from_abspath, scratch_pool, + SVN_ERR(svn_subst_read_specialfile(&source, local_abspath, scratch_pool, scratch_pool)); - SVN_ERR(svn_wc_prop_list2(&props, wc_ctx, from_abspath, scratch_pool, + SVN_ERR(svn_wc_prop_list2(&props, wc_ctx, local_abspath, scratch_pool, scratch_pool)); - SVN_ERR(svn_wc_status3(&status, wc_ctx, from_abspath, scratch_pool, - scratch_pool)); - if (status->text_status != svn_wc_status_normal) + if (status->node_status != svn_wc_status_normal) local_mod = TRUE; } /* We can early-exit if we're creating a special file. */ - special = apr_hash_get(props, SVN_PROP_SPECIAL, - APR_HASH_KEY_STRING); + special = svn_hash_gets(props, SVN_PROP_SPECIAL); if (special != NULL) { /* Create the destination as a special file, and copy the source details into the destination stream. */ + /* ### And forget the notification */ SVN_ERR(svn_subst_create_specialfile(&dst_stream, to_abspath, scratch_pool, scratch_pool)); return svn_error_trace( @@ -246,40 +354,32 @@ copy_one_versioned_file(const char *from_abspath, } - eol_style = apr_hash_get(props, SVN_PROP_EOL_STYLE, - APR_HASH_KEY_STRING); - keywords = apr_hash_get(props, SVN_PROP_KEYWORDS, - APR_HASH_KEY_STRING); - executable = apr_hash_get(props, SVN_PROP_EXECUTABLE, - APR_HASH_KEY_STRING); + eol_style = svn_hash_gets(props, SVN_PROP_EOL_STYLE); + keywords = svn_hash_gets(props, SVN_PROP_KEYWORDS); + executable = svn_hash_gets(props, SVN_PROP_EXECUTABLE); if (eol_style) - SVN_ERR(get_eol_style(&style, &eol, eol_style->data, native_eol)); + SVN_ERR(get_eol_style(&style, &eol, eol_style->data, eib->native_eol)); if (local_mod) { /* Use the modified time from the working copy of the file */ - SVN_ERR(svn_io_file_affected_time(&tm, from_abspath, scratch_pool)); + SVN_ERR(svn_io_file_affected_time(&tm, local_abspath, scratch_pool)); } else { - SVN_ERR(svn_wc__node_get_changed_info(NULL, &tm, NULL, wc_ctx, - from_abspath, scratch_pool, - scratch_pool)); + tm = status->changed_date; } if (keywords) { - svn_revnum_t changed_rev; + svn_revnum_t changed_rev = status->changed_rev; const char *suffix; - const char *url; - const char *author; - - SVN_ERR(svn_wc__node_get_changed_info(&changed_rev, NULL, &author, - wc_ctx, from_abspath, scratch_pool, - scratch_pool)); - + const char *url = svn_path_url_add_component2(status->repos_root_url, + status->repos_relpath, + scratch_pool); + const char *author = status->changed_author; if (local_mod) { /* For locally modified files, we'll append an 'M' @@ -294,13 +394,11 @@ copy_one_versioned_file(const char *from_abspath, suffix = ""; } - SVN_ERR(svn_wc__node_get_url(&url, wc_ctx, from_abspath, - scratch_pool, scratch_pool)); - - SVN_ERR(svn_subst_build_keywords2 - (&kw, keywords->data, - apr_psprintf(scratch_pool, "%ld%s", changed_rev, suffix), - url, tm, author, scratch_pool)); + SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data, + apr_psprintf(scratch_pool, "%ld%s", + changed_rev, suffix), + url, status->repos_root_url, tm, + author, scratch_pool)); } /* For atomicity, we translate to a tmp file and then rename the tmp file @@ -317,7 +415,7 @@ copy_one_versioned_file(const char *from_abspath, eol, FALSE /* repair */, kw, - ! ignore_keywords /* expand */, + ! eib->ignore_keywords /* expand */, scratch_pool); /* ###: use cancel func/baton in place of NULL/NULL below. */ @@ -336,273 +434,17 @@ copy_one_versioned_file(const char *from_abspath, /* Now that dst_tmp contains the translated data, do the atomic rename. */ SVN_ERR(svn_io_file_rename(dst_tmp, to_abspath, scratch_pool)); - if (ctx->notify_func2) + if (eib->notify_func) { svn_wc_notify_t *notify = svn_wc_create_notify(to_abspath, svn_wc_notify_update_add, scratch_pool); notify->kind = svn_node_file; - (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool); - } - - return SVN_NO_ERROR; -} - -/* Make an unversioned copy of the versioned file or directory tree at the - * source path FROM_ABSPATH. Copy it to the destination path TO_ABSPATH. - * - * If REVISION is svn_opt_revision_working, copy the working version, - * otherwise copy the base version. - * - * See copy_one_versioned_file() for details of file copying behaviour, - * including IGNORE_KEYWORDS and NATIVE_EOL. - * - * Include externals unless IGNORE_EXTERNALS is true. - * - * Recurse according to DEPTH. - * - - */ -static svn_error_t * -copy_versioned_files(const char *from_abspath, - const char *to_abspath, - const svn_opt_revision_t *revision, - svn_boolean_t force, - svn_boolean_t ignore_externals, - svn_boolean_t ignore_keywords, - svn_depth_t depth, - const char *native_eol, - svn_client_ctx_t *ctx, - apr_pool_t *pool) -{ - svn_error_t *err; - apr_pool_t *iterpool; - const apr_array_header_t *children; - svn_node_kind_t from_kind; - svn_depth_t node_depth; - - SVN_ERR_ASSERT(svn_dirent_is_absolute(from_abspath)); - SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath)); - - /* Only export 'added' and 'replaced' files when the revision is WORKING; - when the revision is BASE (i.e. != WORKING), only export 'added' and - 'replaced' files when they are part of a copy-/move-here. Otherwise, skip - them, since they don't have an associated text-base. This condition for - added/replaced simply is an optimization. Added and replaced files would - be handled similarly by svn_wc_get_pristine_contents2(), which would - return NULL if they have no base associated. - TODO: We may prefer not to duplicate this condition and rather use - svn_wc_get_pristine_contents2() or a dedicated new function instead. - - Don't export 'deleted' files and directories unless it's a - revision other than WORKING. These files and directories - don't really exist in WORKING. */ - if (revision->kind != svn_opt_revision_working) - { - svn_boolean_t is_added; - const char *repos_relpath; - - SVN_ERR(svn_wc__node_get_origin(&is_added, NULL, &repos_relpath, - NULL, NULL, NULL, - ctx->wc_ctx, from_abspath, FALSE, - pool, pool)); - - if (is_added && !repos_relpath) - return SVN_NO_ERROR; /* Local addition */ - } - else - { - svn_boolean_t is_deleted; - - SVN_ERR(svn_wc__node_is_status_deleted(&is_deleted, ctx->wc_ctx, - from_abspath, pool)); - if (is_deleted) - return SVN_NO_ERROR; - } - - SVN_ERR(svn_wc_read_kind(&from_kind, ctx->wc_ctx, from_abspath, FALSE, - pool)); - - if (from_kind == svn_node_dir) - { - apr_fileperms_t perm = APR_OS_DEFAULT; - int j; - - /* Try to make the new directory. If this fails because the - directory already exists, check our FORCE flag to see if we - care. */ - - /* Keep the source directory's permissions if applicable. - Skip retrieving the umask on windows. Apr does not implement setting - filesystem privileges on Windows. - Retrieving the file permissions with APR_FINFO_PROT | APR_FINFO_OWNER - is documented to be 'incredibly expensive' */ -#ifndef WIN32 - if (revision->kind == svn_opt_revision_working) - { - apr_finfo_t finfo; - SVN_ERR(svn_io_stat(&finfo, from_abspath, APR_FINFO_PROT, pool)); - perm = finfo.protection; - } -#endif - err = svn_io_dir_make(to_abspath, perm, pool); - if (err) - { - if (! APR_STATUS_IS_EEXIST(err->apr_err)) - return svn_error_trace(err); - if (! force) - SVN_ERR_W(err, _("Destination directory exists, and will not be " - "overwritten unless forced")); - else - svn_error_clear(err); - } - - SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx, from_abspath, - FALSE, pool, pool)); - - iterpool = svn_pool_create(pool); - for (j = 0; j < children->nelts; j++) - { - const char *child_abspath = APR_ARRAY_IDX(children, j, const char *); - const char *child_name = svn_dirent_basename(child_abspath, NULL); - const char *target_abspath; - svn_node_kind_t child_kind; - - svn_pool_clear(iterpool); - - if (ctx->cancel_func) - SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); - - target_abspath = svn_dirent_join(to_abspath, child_name, iterpool); - - SVN_ERR(svn_wc_read_kind(&child_kind, ctx->wc_ctx, child_abspath, - FALSE, iterpool)); - - if (child_kind == svn_node_dir) - { - if (depth == svn_depth_infinity - || depth == svn_depth_immediates) - { - if (ctx->notify_func2) - { - svn_wc_notify_t *notify = - svn_wc_create_notify(target_abspath, - svn_wc_notify_update_add, pool); - notify->kind = svn_node_dir; - (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); - } - - if (depth == svn_depth_infinity) - SVN_ERR(copy_versioned_files(child_abspath, target_abspath, - revision, force, - ignore_externals, - ignore_keywords, depth, - native_eol, ctx, iterpool)); - else - SVN_ERR(svn_io_make_dir_recursively(target_abspath, - iterpool)); - } - } - else if (child_kind == svn_node_file - && depth >= svn_depth_files) - { - svn_node_kind_t external_kind; - - SVN_ERR(svn_wc__read_external_info(&external_kind, - NULL, NULL, NULL, - NULL, ctx->wc_ctx, - child_abspath, - child_abspath, TRUE, - pool, pool)); - - if (external_kind != svn_node_file) - SVN_ERR(copy_one_versioned_file(child_abspath, target_abspath, - ctx, revision, - native_eol, ignore_keywords, - iterpool)); - } - } - - SVN_ERR(svn_wc__node_get_depth(&node_depth, ctx->wc_ctx, - from_abspath, pool)); - - /* Handle externals. */ - if (! ignore_externals && depth == svn_depth_infinity - && node_depth == svn_depth_infinity) - { - apr_array_header_t *ext_items; - const svn_string_t *prop_val; - - SVN_ERR(svn_wc_prop_get2(&prop_val, ctx->wc_ctx, from_abspath, - SVN_PROP_EXTERNALS, pool, pool)); - if (prop_val != NULL) - { - int i; - - SVN_ERR(svn_wc_parse_externals_description3(&ext_items, - from_abspath, - prop_val->data, - FALSE, pool)); - for (i = 0; i < ext_items->nelts; ++i) - { - svn_wc_external_item2_t *ext_item; - const char *new_from, *new_to; - - svn_pool_clear(iterpool); - - ext_item = APR_ARRAY_IDX(ext_items, i, - svn_wc_external_item2_t *); - new_from = svn_dirent_join(from_abspath, - ext_item->target_dir, - iterpool); - new_to = svn_dirent_join(to_abspath, ext_item->target_dir, - iterpool); - - /* The target dir might have parents that don't exist. - Guarantee the path upto the last component. */ - if (!svn_dirent_is_root(ext_item->target_dir, - strlen(ext_item->target_dir))) - { - const char *parent = svn_dirent_dirname(new_to, iterpool); - SVN_ERR(svn_io_make_dir_recursively(parent, iterpool)); - } - - SVN_ERR(copy_versioned_files(new_from, new_to, - revision, force, FALSE, - ignore_keywords, - svn_depth_infinity, native_eol, - ctx, iterpool)); - } - } - } - - svn_pool_destroy(iterpool); - } - else if (from_kind == svn_node_file) - { - svn_node_kind_t to_kind; - - SVN_ERR(svn_io_check_path(to_abspath, &to_kind, pool)); - - if ((to_kind == svn_node_file || to_kind == svn_node_unknown) && ! force) - return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, - _("Destination file '%s' exists, and " - "will not be overwritten unless forced"), - svn_dirent_local_style(to_abspath, pool)); - else if (to_kind == svn_node_dir) - return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, - _("Destination '%s' exists. Cannot " - "overwrite directory with non-directory"), - svn_dirent_local_style(to_abspath, pool)); - - SVN_ERR(copy_one_versioned_file(from_abspath, to_abspath, ctx, - revision, native_eol, ignore_keywords, - pool)); + (eib->notify_func)(eib->notify_baton, notify, scratch_pool); } return SVN_NO_ERROR; } - /* Abstraction of open_root. * * Create PATH if it does not exist and is not obstructed, and invoke @@ -656,6 +498,7 @@ open_root_internal(const char *path, struct edit_baton { + const char *repos_root_url; const char *root_path; const char *root_url; svn_boolean_t force; @@ -702,6 +545,7 @@ struct file_baton /* Any keyword vals to be substituted */ const char *revision; const char *url; + const char *repos_root_url; const char *author; apr_time_t date; @@ -823,6 +667,7 @@ add_file(const char *path, fb->edit_baton = eb; fb->path = full_path; fb->url = full_url; + fb->repos_root_url = eb->repos_root_url; fb->pool = pool; *baton = fb; @@ -840,7 +685,9 @@ window_handler(svn_txdelta_window_t *window, void *baton) if (err) { /* We failed to apply the patch; clean up the temporary file. */ - svn_error_clear(svn_io_remove_file2(hb->tmppath, TRUE, hb->pool)); + err = svn_error_compose_create( + err, + svn_io_remove_file2(hb->tmppath, TRUE, hb->pool)); } return svn_error_trace(err); @@ -957,8 +804,7 @@ close_file(void *file_baton, SVN_ERR(svn_checksum_parse_hex(&text_checksum, svn_checksum_md5, text_digest, pool)); - actual_checksum = svn_checksum__from_digest(fb->text_digest, - svn_checksum_md5, pool); + actual_checksum = svn_checksum__from_digest_md5(fb->text_digest, pool); /* Note that text_digest can be NULL when talking to certain repositories. In that case text_checksum will be NULL and the following match code @@ -987,8 +833,9 @@ close_file(void *file_baton, } if (fb->keywords_val) - SVN_ERR(svn_subst_build_keywords2(&final_kw, fb->keywords_val->data, - fb->revision, fb->url, fb->date, + SVN_ERR(svn_subst_build_keywords3(&final_kw, fb->keywords_val->data, + fb->revision, fb->url, + fb->repos_root_url, fb->date, fb->author, pool)); SVN_ERR(svn_subst_copy_and_translate4(fb->tmppath, fb->path, @@ -1020,6 +867,504 @@ close_file(void *file_baton, 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) +{ + /* Always use empty props, since the node won't have pre-existing props + (This is an export, remember?) */ + *props = apr_hash_make(result_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) +{ + /* An export always gets text against the empty stream (i.e, full texts). */ + *filename = NULL; + + return SVN_NO_ERROR; +} + +static svn_error_t * +get_editor_ev1(const svn_delta_editor_t **export_editor, + void **edit_baton, + struct edit_baton *eb, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_delta_editor_t *editor = svn_delta_default_editor(result_pool); + + editor->set_target_revision = set_target_revision; + editor->open_root = open_root; + editor->add_directory = add_directory; + editor->add_file = add_file; + editor->apply_textdelta = apply_textdelta; + editor->close_file = close_file; + editor->change_file_prop = change_file_prop; + editor->change_dir_prop = change_dir_prop; + + SVN_ERR(svn_delta_get_cancellation_editor(ctx->cancel_func, + ctx->cancel_baton, + editor, + eb, + export_editor, + edit_baton, + result_pool)); + + return SVN_NO_ERROR; +} + + +/*** The Ev2 Implementation ***/ + +static svn_error_t * +add_file_ev2(void *baton, + const char *relpath, + const svn_checksum_t *checksum, + svn_stream_t *contents, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + const char *full_path = svn_dirent_join(eb->root_path, relpath, + scratch_pool); + /* RELPATH is not canonicalized, i.e. it may still contain spaces etc. + * but EB->root_url is. */ + const char *full_url = svn_path_url_add_component2(eb->root_url, + relpath, + scratch_pool); + const svn_string_t *val; + /* The four svn: properties we might actually care about. */ + const svn_string_t *eol_style_val = NULL; + const svn_string_t *keywords_val = NULL; + const svn_string_t *executable_val = NULL; + svn_boolean_t special = FALSE; + /* Any keyword vals to be substituted */ + const char *revision = NULL; + const char *author = NULL; + apr_time_t date = 0; + + /* Look at any properties for additional information. */ + if ( (val = svn_hash_gets(props, SVN_PROP_EOL_STYLE)) ) + eol_style_val = val; + + if ( !eb->ignore_keywords && (val = svn_hash_gets(props, SVN_PROP_KEYWORDS)) ) + keywords_val = val; + + if ( (val = svn_hash_gets(props, SVN_PROP_EXECUTABLE)) ) + executable_val = val; + + /* Try to fill out the baton's keywords-structure too. */ + if ( (val = svn_hash_gets(props, SVN_PROP_ENTRY_COMMITTED_REV)) ) + revision = val->data; + + if ( (val = svn_hash_gets(props, SVN_PROP_ENTRY_COMMITTED_DATE)) ) + SVN_ERR(svn_time_from_cstring(&date, val->data, scratch_pool)); + + if ( (val = svn_hash_gets(props, SVN_PROP_ENTRY_LAST_AUTHOR)) ) + author = val->data; + + if ( (val = svn_hash_gets(props, SVN_PROP_SPECIAL)) ) + special = TRUE; + + if (special) + { + svn_stream_t *tmp_stream; + + SVN_ERR(svn_subst_create_specialfile(&tmp_stream, full_path, + scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_copy3(contents, tmp_stream, eb->cancel_func, + eb->cancel_baton, scratch_pool)); + } + else + { + svn_stream_t *tmp_stream; + const char *tmppath; + + /* Create a temporary file in the same directory as the file. We're going + to rename the thing into place when we're done. */ + SVN_ERR(svn_stream_open_unique(&tmp_stream, &tmppath, + svn_dirent_dirname(full_path, + scratch_pool), + svn_io_file_del_none, + scratch_pool, scratch_pool)); + + /* Possibly wrap the stream to be translated, as dictated by + the props. */ + if (eol_style_val || keywords_val) + { + svn_subst_eol_style_t style; + const char *eol = NULL; + svn_boolean_t repair = FALSE; + apr_hash_t *final_kw = NULL; + + if (eol_style_val) + { + SVN_ERR(get_eol_style(&style, &eol, eol_style_val->data, + eb->native_eol)); + repair = TRUE; + } + + if (keywords_val) + SVN_ERR(svn_subst_build_keywords3(&final_kw, keywords_val->data, + revision, full_url, + eb->repos_root_url, + date, author, scratch_pool)); + + /* Writing through a translated stream is more efficient than + reading through one, so we wrap TMP_STREAM and not CONTENTS. */ + tmp_stream = svn_subst_stream_translated(tmp_stream, eol, repair, + final_kw, TRUE, /* expand */ + scratch_pool); + } + + SVN_ERR(svn_stream_copy3(contents, tmp_stream, eb->cancel_func, + eb->cancel_baton, scratch_pool)); + + /* Move the file into place. */ + SVN_ERR(svn_io_file_rename(tmppath, full_path, scratch_pool)); + } + + if (executable_val) + SVN_ERR(svn_io_set_file_executable(full_path, TRUE, FALSE, scratch_pool)); + + if (date && (! special)) + SVN_ERR(svn_io_set_file_affected_time(date, full_path, scratch_pool)); + + if (eb->notify_func) + { + svn_wc_notify_t *notify = svn_wc_create_notify(full_path, + svn_wc_notify_update_add, + scratch_pool); + notify->kind = svn_node_file; + (*eb->notify_func)(eb->notify_baton, notify, scratch_pool); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +add_directory_ev2(void *baton, + const char *relpath, + const apr_array_header_t *children, + apr_hash_t *props, + svn_revnum_t replaces_rev, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + svn_node_kind_t kind; + const char *full_path = svn_dirent_join(eb->root_path, relpath, + scratch_pool); + svn_string_t *val; + + SVN_ERR(svn_io_check_path(full_path, &kind, scratch_pool)); + if (kind == svn_node_none) + SVN_ERR(svn_io_dir_make(full_path, APR_OS_DEFAULT, scratch_pool)); + else if (kind == svn_node_file) + return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL, + _("'%s' exists and is not a directory"), + svn_dirent_local_style(full_path, scratch_pool)); + else if (! (kind == svn_node_dir && eb->force)) + return svn_error_createf(SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, + _("'%s' already exists"), + svn_dirent_local_style(full_path, scratch_pool)); + + if ( (val = svn_hash_gets(props, SVN_PROP_EXTERNALS)) ) + SVN_ERR(add_externals(eb->externals, full_path, val)); + + if (eb->notify_func) + { + svn_wc_notify_t *notify = svn_wc_create_notify(full_path, + svn_wc_notify_update_add, + scratch_pool); + notify->kind = svn_node_dir; + (*eb->notify_func)(eb->notify_baton, notify, scratch_pool); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +target_revision_func(void *baton, + svn_revnum_t target_revision, + apr_pool_t *scratch_pool) +{ + struct edit_baton *eb = baton; + + *eb->target_revision = target_revision; + + return SVN_NO_ERROR; +} + +static svn_error_t * +get_editor_ev2(const svn_delta_editor_t **export_editor, + void **edit_baton, + struct edit_baton *eb, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_editor_t *editor; + struct svn_delta__extra_baton *exb = apr_pcalloc(result_pool, sizeof(*exb)); + svn_boolean_t *found_abs_paths = apr_palloc(result_pool, + sizeof(*found_abs_paths)); + + exb->baton = eb; + exb->target_revision = target_revision_func; + + SVN_ERR(svn_editor_create(&editor, eb, ctx->cancel_func, ctx->cancel_baton, + result_pool, scratch_pool)); + SVN_ERR(svn_editor_setcb_add_directory(editor, add_directory_ev2, + scratch_pool)); + SVN_ERR(svn_editor_setcb_add_file(editor, add_file_ev2, scratch_pool)); + + *found_abs_paths = TRUE; + + SVN_ERR(svn_delta__delta_from_editor(export_editor, edit_baton, + editor, NULL, NULL, found_abs_paths, + NULL, NULL, + fetch_props_func, eb, + fetch_base_func, eb, + exb, result_pool)); + + /* Create the root of the export. */ + SVN_ERR(open_root_internal(eb->root_path, eb->force, eb->notify_func, + eb->notify_baton, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +export_file_ev2(const char *from_path_or_url, + const char *to_path, + struct edit_baton *eb, + svn_client__pathrev_t *loc, + svn_ra_session_t *ra_session, + svn_boolean_t overwrite, + apr_pool_t *scratch_pool) +{ + svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url); + apr_hash_t *props; + svn_stream_t *tmp_stream; + svn_node_kind_t to_kind; + + if (svn_path_is_empty(to_path)) + { + if (from_is_url) + to_path = svn_uri_basename(from_path_or_url, scratch_pool); + else + to_path = svn_dirent_basename(from_path_or_url, NULL); + eb->root_path = to_path; + } + else + { + SVN_ERR(append_basename_if_dir(&to_path, from_path_or_url, + from_is_url, scratch_pool)); + eb->root_path = to_path; + } + + SVN_ERR(svn_io_check_path(to_path, &to_kind, scratch_pool)); + + if ((to_kind == svn_node_file || to_kind == svn_node_unknown) && + ! overwrite) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Destination file '%s' exists, and " + "will not be overwritten unless forced"), + svn_dirent_local_style(to_path, scratch_pool)); + else if (to_kind == svn_node_dir) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Destination '%s' exists. Cannot " + "overwrite directory with non-directory"), + svn_dirent_local_style(to_path, scratch_pool)); + + tmp_stream = svn_stream_buffered(scratch_pool); + + SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, + tmp_stream, NULL, &props, scratch_pool)); + + /* Since you cannot actually root an editor at a file, we manually drive + * a function of our editor. */ + SVN_ERR(add_file_ev2(eb, "", NULL, tmp_stream, props, SVN_INVALID_REVNUM, + scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +export_file(const char *from_path_or_url, + const char *to_path, + struct edit_baton *eb, + svn_client__pathrev_t *loc, + svn_ra_session_t *ra_session, + svn_boolean_t overwrite, + apr_pool_t *scratch_pool) +{ + apr_hash_t *props; + apr_hash_index_t *hi; + struct file_baton *fb = apr_pcalloc(scratch_pool, sizeof(*fb)); + svn_node_kind_t to_kind; + svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url); + + if (svn_path_is_empty(to_path)) + { + if (from_is_url) + to_path = svn_uri_basename(from_path_or_url, scratch_pool); + else + to_path = svn_dirent_basename(from_path_or_url, NULL); + eb->root_path = to_path; + } + else + { + SVN_ERR(append_basename_if_dir(&to_path, from_path_or_url, + from_is_url, scratch_pool)); + eb->root_path = to_path; + } + + SVN_ERR(svn_io_check_path(to_path, &to_kind, scratch_pool)); + + if ((to_kind == svn_node_file || to_kind == svn_node_unknown) && + ! overwrite) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Destination file '%s' exists, and " + "will not be overwritten unless forced"), + svn_dirent_local_style(to_path, scratch_pool)); + else if (to_kind == svn_node_dir) + return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, + _("Destination '%s' exists. Cannot " + "overwrite directory with non-directory"), + svn_dirent_local_style(to_path, scratch_pool)); + + /* Since you cannot actually root an editor at a file, we + * manually drive a few functions of our editor. */ + + /* This is the equivalent of a parentless add_file(). */ + fb->edit_baton = eb; + fb->path = eb->root_path; + fb->url = eb->root_url; + fb->pool = scratch_pool; + fb->repos_root_url = eb->repos_root_url; + + /* Copied from apply_textdelta(). */ + SVN_ERR(svn_stream_open_unique(&fb->tmp_stream, &fb->tmppath, + svn_dirent_dirname(fb->path, scratch_pool), + svn_io_file_del_none, + fb->pool, fb->pool)); + + /* Step outside the editor-likeness for a moment, to actually talk + * to the repository. */ + /* ### note: the stream will not be closed */ + SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, + fb->tmp_stream, + NULL, &props, scratch_pool)); + + /* Push the props into change_file_prop(), to update the file_baton + * with information. */ + for (hi = apr_hash_first(scratch_pool, props); hi; hi = apr_hash_next(hi)) + { + const char *propname = svn__apr_hash_index_key(hi); + const svn_string_t *propval = svn__apr_hash_index_val(hi); + + SVN_ERR(change_file_prop(fb, propname, propval, scratch_pool)); + } + + /* And now just use close_file() to do all the keyword and EOL + * work, and put the file into place. */ + SVN_ERR(close_file(fb, NULL, scratch_pool)); + + return SVN_NO_ERROR; +} + +static svn_error_t * +export_directory(const char *from_path_or_url, + const char *to_path, + struct edit_baton *eb, + svn_client__pathrev_t *loc, + svn_ra_session_t *ra_session, + svn_boolean_t overwrite, + svn_boolean_t ignore_externals, + svn_boolean_t ignore_keywords, + svn_depth_t depth, + const char *native_eol, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + void *edit_baton; + const svn_delta_editor_t *export_editor; + const svn_ra_reporter3_t *reporter; + void *report_baton; + svn_node_kind_t kind; + + if (!ENABLE_EV2_IMPL) + SVN_ERR(get_editor_ev1(&export_editor, &edit_baton, eb, ctx, + scratch_pool, scratch_pool)); + else + SVN_ERR(get_editor_ev2(&export_editor, &edit_baton, eb, ctx, + scratch_pool, scratch_pool)); + + /* Manufacture a basic 'report' to the update reporter. */ + SVN_ERR(svn_ra_do_update3(ra_session, + &reporter, &report_baton, + loc->rev, + "", /* no sub-target */ + depth, + FALSE, /* don't want copyfrom-args */ + FALSE, /* don't want ignore_ancestry */ + export_editor, edit_baton, + scratch_pool, scratch_pool)); + + SVN_ERR(reporter->set_path(report_baton, "", loc->rev, + /* Depth is irrelevant, as we're + passing start_empty=TRUE anyway. */ + svn_depth_infinity, + TRUE, /* "help, my dir is empty!" */ + NULL, scratch_pool)); + + SVN_ERR(reporter->finish_report(report_baton, scratch_pool)); + + /* Special case: Due to our sly export/checkout method of updating an + * empty directory, no target will have been created if the exported + * item is itself an empty directory (export_editor->open_root never + * gets called, because there are no "changes" to make to the empty + * dir we reported to the repository). + * + * So we just create the empty dir manually; but we do it via + * open_root_internal(), in order to get proper notification. + */ + SVN_ERR(svn_io_check_path(to_path, &kind, scratch_pool)); + if (kind == svn_node_none) + SVN_ERR(open_root_internal + (to_path, overwrite, ctx->notify_func2, + ctx->notify_baton2, scratch_pool)); + + if (! ignore_externals && depth == svn_depth_infinity) + { + const char *to_abspath; + + SVN_ERR(svn_dirent_get_absolute(&to_abspath, to_path, scratch_pool)); + SVN_ERR(svn_client__export_externals(eb->externals, + from_path_or_url, + to_abspath, eb->repos_root_url, + depth, native_eol, + ignore_keywords, + ctx, scratch_pool)); + } + + return SVN_NO_ERROR; +} + /*** Public Interfaces ***/ @@ -1039,7 +1384,6 @@ svn_client_export5(svn_revnum_t *result_rev, apr_pool_t *pool) { svn_revnum_t edit_revision = SVN_INVALID_REVNUM; - const char *url; svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url); SVN_ERR_ASSERT(peg_revision != NULL); @@ -1055,23 +1399,20 @@ svn_client_export5(svn_revnum_t *result_rev, if (from_is_url || ! SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind)) { - svn_revnum_t revnum; + svn_client__pathrev_t *loc; svn_ra_session_t *ra_session; svn_node_kind_t kind; struct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb)); - const char *repos_root_url; /* Get the RA connection. */ - SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum, - &url, from_path_or_url, NULL, - peg_revision, - revision, ctx, pool)); - - /* Get the repository root. */ - SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool)); + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc, + from_path_or_url, NULL, + peg_revision, + revision, ctx, pool)); + SVN_ERR(svn_ra_get_repos_root2(ra_session, &eb->repos_root_url, pool)); eb->root_path = to_path; - eb->root_url = url; + eb->root_url = loc->url; eb->force = overwrite; eb->target_revision = &edit_revision; eb->externals = apr_hash_make(pool); @@ -1082,153 +1423,23 @@ svn_client_export5(svn_revnum_t *result_rev, eb->notify_func = ctx->notify_func2; eb->notify_baton = ctx->notify_baton2; - SVN_ERR(svn_ra_check_path(ra_session, "", revnum, &kind, pool)); + SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, pool)); if (kind == svn_node_file) { - apr_hash_t *props; - apr_hash_index_t *hi; - struct file_baton *fb = apr_pcalloc(pool, sizeof(*fb)); - svn_node_kind_t to_kind; - - if (svn_path_is_empty(to_path)) - { - if (from_is_url) - to_path = svn_uri_basename(from_path_or_url, pool); - else - to_path = svn_dirent_basename(from_path_or_url, NULL); - eb->root_path = to_path; - } + if (!ENABLE_EV2_IMPL) + SVN_ERR(export_file(from_path_or_url, to_path, eb, loc, ra_session, + overwrite, pool)); else - { - SVN_ERR(append_basename_if_dir(&to_path, from_path_or_url, - from_is_url, pool)); - eb->root_path = to_path; - } - - SVN_ERR(svn_io_check_path(to_path, &to_kind, pool)); - - if ((to_kind == svn_node_file || to_kind == svn_node_unknown) && - ! overwrite) - return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, - _("Destination file '%s' exists, and " - "will not be overwritten unless forced"), - svn_dirent_local_style(to_path, pool)); - else if (to_kind == svn_node_dir) - return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, - _("Destination '%s' exists. Cannot " - "overwrite directory with non-directory"), - svn_dirent_local_style(to_path, pool)); - - /* Since you cannot actually root an editor at a file, we - * manually drive a few functions of our editor. */ - - /* This is the equivalent of a parentless add_file(). */ - fb->edit_baton = eb; - fb->path = eb->root_path; - fb->url = eb->root_url; - fb->pool = pool; - - /* Copied from apply_textdelta(). */ - SVN_ERR(svn_stream_open_unique(&fb->tmp_stream, &fb->tmppath, - svn_dirent_dirname(fb->path, pool), - svn_io_file_del_none, - fb->pool, fb->pool)); - - /* Step outside the editor-likeness for a moment, to actually talk - * to the repository. */ - /* ### note: the stream will not be closed */ - SVN_ERR(svn_ra_get_file(ra_session, "", revnum, - fb->tmp_stream, - NULL, &props, pool)); - - /* Push the props into change_file_prop(), to update the file_baton - * with information. */ - for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) - { - const char *propname = svn__apr_hash_index_key(hi); - const svn_string_t *propval = svn__apr_hash_index_val(hi); - - SVN_ERR(change_file_prop(fb, propname, propval, pool)); - } - - /* And now just use close_file() to do all the keyword and EOL - * work, and put the file into place. */ - SVN_ERR(close_file(fb, NULL, pool)); + SVN_ERR(export_file_ev2(from_path_or_url, to_path, eb, loc, + ra_session, overwrite, pool)); } else if (kind == svn_node_dir) { - void *edit_baton; - const svn_delta_editor_t *export_editor; - const svn_ra_reporter3_t *reporter; - void *report_baton; - svn_delta_editor_t *editor = svn_delta_default_editor(pool); - svn_boolean_t use_sleep = FALSE; - - editor->set_target_revision = set_target_revision; - editor->open_root = open_root; - editor->add_directory = add_directory; - editor->add_file = add_file; - editor->apply_textdelta = apply_textdelta; - editor->close_file = close_file; - editor->change_file_prop = change_file_prop; - editor->change_dir_prop = change_dir_prop; - - SVN_ERR(svn_delta_get_cancellation_editor(ctx->cancel_func, - ctx->cancel_baton, - editor, - eb, - &export_editor, - &edit_baton, - pool)); - - - /* Manufacture a basic 'report' to the update reporter. */ - SVN_ERR(svn_ra_do_update2(ra_session, - &reporter, &report_baton, - revnum, - "", /* no sub-target */ - depth, - FALSE, /* don't want copyfrom-args */ - export_editor, edit_baton, pool)); - - SVN_ERR(reporter->set_path(report_baton, "", revnum, - /* Depth is irrelevant, as we're - passing start_empty=TRUE anyway. */ - svn_depth_infinity, - TRUE, /* "help, my dir is empty!" */ - NULL, pool)); - - SVN_ERR(reporter->finish_report(report_baton, pool)); - - /* Special case: Due to our sly export/checkout method of - * updating an empty directory, no target will have been created - * if the exported item is itself an empty directory - * (export_editor->open_root never gets called, because there - * are no "changes" to make to the empty dir we reported to the - * repository). - * - * So we just create the empty dir manually; but we do it via - * open_root_internal(), in order to get proper notification. - */ - SVN_ERR(svn_io_check_path(to_path, &kind, pool)); - if (kind == svn_node_none) - SVN_ERR(open_root_internal - (to_path, overwrite, ctx->notify_func2, - ctx->notify_baton2, pool)); - - if (! ignore_externals && depth == svn_depth_infinity) - { - const char *to_abspath; - - SVN_ERR(svn_dirent_get_absolute(&to_abspath, to_path, pool)); - SVN_ERR(svn_client__export_externals(eb->externals, - from_path_or_url, - to_abspath, repos_root_url, - depth, native_eol, - ignore_keywords, &use_sleep, - ctx, pool)); - } + SVN_ERR(export_directory(from_path_or_url, to_path, + eb, loc, ra_session, overwrite, + ignore_externals, ignore_keywords, depth, + native_eol, ctx, pool)); } else if (kind == svn_node_none) { @@ -1240,7 +1451,10 @@ svn_client_export5(svn_revnum_t *result_rev, } else { + struct export_info_baton eib; svn_node_kind_t kind; + apr_hash_t *externals = NULL; + /* This is a working copy export. */ /* just copy the contents of the working copy into the target path. */ SVN_ERR(svn_dirent_get_absolute(&from_path_or_url, from_path_or_url, @@ -1284,14 +1498,80 @@ svn_client_export5(svn_revnum_t *result_rev, * For a start, to detect the source kind, it looks at what is on disk * rather than the versioned working or base node. */ - if (kind == svn_node_file) SVN_ERR(append_basename_if_dir(&to_path, from_path_or_url, FALSE, pool)); - SVN_ERR(copy_versioned_files(from_path_or_url, to_path, revision, - overwrite, ignore_externals, ignore_keywords, - depth, native_eol, ctx, pool)); + eib.to_path = to_path; + eib.revision = revision; + eib.overwrite = overwrite; + eib.ignore_keywords = ignore_keywords; + eib.wc_ctx = ctx->wc_ctx; + eib.native_eol = native_eol; + eib.notify_func = ctx->notify_func2;; + eib.notify_baton = ctx->notify_baton2; + eib.origin_abspath = from_path_or_url; + eib.exported = FALSE; + + SVN_ERR(svn_wc_walk_status(ctx->wc_ctx, from_path_or_url, depth, + TRUE /* get_all */, + TRUE /* no_ignore */, + FALSE /* ignore_text_mods */, + NULL, + export_node, &eib, + ctx->cancel_func, ctx->cancel_baton, + pool)); + + if (!eib.exported) + return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, + _("The node '%s' was not found."), + svn_dirent_local_style(from_path_or_url, + pool)); + + if (!ignore_externals) + SVN_ERR(svn_wc__externals_defined_below(&externals, ctx->wc_ctx, + from_path_or_url, + pool, pool)); + + if (externals && apr_hash_count(externals)) + { + apr_pool_t *iterpool = svn_pool_create(pool); + apr_hash_index_t *hi; + + for (hi = apr_hash_first(pool, externals); + hi; + hi = apr_hash_next(hi)) + { + const char *external_abspath = svn__apr_hash_index_key(hi); + const char *relpath; + const char *target_abspath; + + svn_pool_clear(iterpool); + + relpath = svn_dirent_skip_ancestor(from_path_or_url, + external_abspath); + + target_abspath = svn_dirent_join(to_path, relpath, + iterpool); + + /* Ensure that the parent directory exists */ + SVN_ERR(svn_io_make_dir_recursively( + svn_dirent_dirname(target_abspath, iterpool), + iterpool)); + + SVN_ERR(svn_client_export5(NULL, + svn_dirent_join(from_path_or_url, + relpath, + iterpool), + target_abspath, + peg_revision, revision, + TRUE, ignore_externals, + ignore_keywords, depth, native_eol, + ctx, iterpool)); + } + + svn_pool_destroy(iterpool); + } } @@ -1309,3 +1589,4 @@ svn_client_export5(svn_revnum_t *result_rev, return SVN_NO_ERROR; } + |