diff options
Diffstat (limited to 'subversion/libsvn_client/externals.c')
-rw-r--r-- | subversion/libsvn_client/externals.c | 1044 |
1 files changed, 428 insertions, 616 deletions
diff --git a/subversion/libsvn_client/externals.c b/subversion/libsvn_client/externals.c index 3c23fa3..52c236c 100644 --- a/subversion/libsvn_client/externals.c +++ b/subversion/libsvn_client/externals.c @@ -24,10 +24,11 @@ /* ==================================================================== */ - + /*** Includes. ***/ #include <apr_uri.h> +#include "svn_hash.h" #include "svn_wc.h" #include "svn_pools.h" #include "svn_client.h" @@ -41,18 +42,7 @@ #include "svn_private_config.h" #include "private/svn_wc_private.h" - -/* Closure for handle_external_item_change. */ -struct external_change_baton_t -{ - /* The URL for the repository root. */ - const char *repos_root_url; - /* Passed through to svn_client_* functions. */ - svn_client_ctx_t *ctx; - - svn_boolean_t *timestamp_sleep; -}; /* Remove the directory at LOCAL_ABSPATH from revision control, and do the * same to any revision controlled directories underneath LOCAL_ABSPATH @@ -70,11 +60,16 @@ relegate_dir_external(svn_wc_context_t *wc_ctx, const char *local_abspath, svn_cancel_func_t cancel_func, void *cancel_baton, + svn_wc_notify_func2_t notify_func, + void *notify_baton, apr_pool_t *scratch_pool) { svn_error_t *err = SVN_NO_ERROR; - err = svn_wc__external_remove(wc_ctx, wri_abspath, local_abspath, + SVN_ERR(svn_wc__acquire_write_lock(NULL, wc_ctx, local_abspath, + FALSE, scratch_pool, scratch_pool)); + + err = svn_wc__external_remove(wc_ctx, wri_abspath, local_abspath, FALSE, cancel_func, cancel_baton, scratch_pool); if (err && (err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)) { @@ -114,8 +109,33 @@ relegate_dir_external(svn_wc_context_t *wc_ctx, /* Do our best, but no biggy if it fails. The rename will fail. */ svn_error_clear(svn_io_remove_file2(new_path, TRUE, scratch_pool)); - /* Rename. */ - SVN_ERR(svn_io_file_rename(local_abspath, new_path, scratch_pool)); + /* Rename. If this is still a working copy we should use the working + copy rename function (to release open handles) */ + err = svn_wc__rename_wc(wc_ctx, local_abspath, new_path, + scratch_pool); + + if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS) + { + svn_error_clear(err); + + /* And if it is no longer a working copy, we should just rename + it */ + err = svn_io_file_rename(local_abspath, new_path, scratch_pool); + } + + /* ### TODO: We should notify the user about the rename */ + if (notify_func) + { + svn_wc_notify_t *notify; + + notify = svn_wc_create_notify(err ? local_abspath : new_path, + svn_wc_notify_left_local_modifications, + scratch_pool); + notify->kind = svn_node_dir; + notify->err = err; + + notify_func(notify_baton, notify, scratch_pool); + } } return svn_error_trace(err); @@ -126,6 +146,7 @@ relegate_dir_external(svn_wc_context_t *wc_ctx, static svn_error_t * switch_dir_external(const char *local_abspath, const char *url, + const char *url_from_externals_definition, const svn_opt_revision_t *peg_revision, const svn_opt_revision_t *revision, const char *defining_abspath, @@ -135,10 +156,60 @@ switch_dir_external(const char *local_abspath, { svn_node_kind_t kind; svn_error_t *err; + svn_revnum_t external_peg_rev = SVN_INVALID_REVNUM; + svn_revnum_t external_rev = SVN_INVALID_REVNUM; apr_pool_t *subpool = svn_pool_create(pool); + const char *repos_root_url; + const char *repos_uuid; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); + if (peg_revision->kind == svn_opt_revision_number) + external_peg_rev = peg_revision->value.number; + + if (revision->kind == svn_opt_revision_number) + external_rev = revision->value.number; + + /* + * The code below assumes existing versioned paths are *not* part of + * the external's defining working copy. + * The working copy library does not support registering externals + * on top of existing BASE nodes and will error out if we try. + * So if the external target is part of the defining working copy's + * BASE tree, don't attempt to create the external. Doing so would + * leave behind a switched path instead of an external (since the + * switch succeeds but registration of the external in the DB fails). + * The working copy then cannot be updated until the path is switched back. + * See issue #4085. + */ + SVN_ERR(svn_wc__node_get_base(&kind, NULL, NULL, + &repos_root_url, &repos_uuid, + NULL, ctx->wc_ctx, local_abspath, + TRUE, /* ignore_enoent */ + TRUE, /* show hidden */ + pool, pool)); + if (kind != svn_node_unknown) + { + const char *wcroot_abspath; + const char *defining_wcroot_abspath; + + SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, + local_abspath, pool, pool)); + SVN_ERR(svn_wc__get_wcroot(&defining_wcroot_abspath, ctx->wc_ctx, + defining_abspath, pool, pool)); + if (strcmp(wcroot_abspath, defining_wcroot_abspath) == 0) + return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, + _("The external '%s' defined in %s at '%s' " + "cannot be checked out because '%s' is " + "already a versioned path."), + url_from_externals_definition, + SVN_PROP_EXTERNALS, + svn_dirent_local_style(defining_abspath, + pool), + svn_dirent_local_style(local_abspath, + pool)); + } + /* If path is a directory, try to update/switch to the correct URL and revision. */ SVN_ERR(svn_io_check_path(local_abspath, &kind, pool)); @@ -160,9 +231,6 @@ switch_dir_external(const char *local_abspath, if (node_url) { - const char *repos_root_url; - const char *repos_uuid; - /* If we have what appears to be a version controlled subdir, and its top-level URL matches that of our externals definition, perform an update. */ @@ -174,10 +242,22 @@ switch_dir_external(const char *local_abspath, FALSE, TRUE, timestamp_sleep, ctx, subpool)); + + /* We just decided that this existing directory is an external, + so update the external registry with this information, like + when checking out an external */ + SVN_ERR(svn_wc__external_register(ctx->wc_ctx, + defining_abspath, + local_abspath, svn_node_dir, + repos_root_url, repos_uuid, + svn_uri_skip_ancestor(repos_root_url, + url, pool), + external_peg_rev, + external_rev, + pool)); + svn_pool_destroy(subpool); - /* Issue #4130: We don't need to keep the external's DB open. */ - SVN_ERR(svn_wc__close_db(local_abspath, ctx->wc_ctx, pool)); - return SVN_NO_ERROR; + goto cleanup; } /* We'd really prefer not to have to do a brute-force @@ -188,9 +268,21 @@ switch_dir_external(const char *local_abspath, To do so, we need to know the repository root URL of the external working copy as it currently sits. */ - SVN_ERR(svn_wc__node_get_repos_info(&repos_root_url, &repos_uuid, - ctx->wc_ctx, local_abspath, - pool, subpool)); + err = svn_wc__node_get_repos_info(NULL, NULL, + &repos_root_url, &repos_uuid, + ctx->wc_ctx, local_abspath, + pool, subpool); + if (err) + { + if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND + && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) + return svn_error_trace(err); + + svn_error_clear(err); + repos_root_url = NULL; + repos_uuid = NULL; + } + if (repos_root_url) { /* If the new external target URL is not obviously a @@ -199,17 +291,11 @@ switch_dir_external(const char *local_abspath, if (! svn_uri__is_ancestor(repos_root_url, url)) { const char *repos_root; - svn_ra_session_t *ra_session; /* ... then figure out precisely which repository root URL that target URL *is* a child of ... */ - SVN_ERR(svn_client__open_ra_session_internal(&ra_session, - NULL, url, NULL, - NULL, FALSE, - TRUE, ctx, - subpool)); - SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root, - subpool)); + SVN_ERR(svn_client_get_repos_root(&repos_root, NULL, url, + ctx, subpool, subpool)); /* ... and use that to try to relocate the external working copy to the target location. */ @@ -250,14 +336,12 @@ switch_dir_external(const char *local_abspath, svn_uri_skip_ancestor( repos_root_url, url, subpool), - SVN_INVALID_REVNUM, - SVN_INVALID_REVNUM, + external_peg_rev, + external_rev, subpool)); svn_pool_destroy(subpool); - /* Issue #4130: We don't need to keep the external's DB open. */ - SVN_ERR(svn_wc__close_db(local_abspath, ctx->wc_ctx, pool)); - return SVN_NO_ERROR; + goto cleanup; } } } @@ -273,12 +357,10 @@ switch_dir_external(const char *local_abspath, if (kind == svn_node_dir) { /* Buh-bye, old and busted ... */ - SVN_ERR(svn_wc__acquire_write_lock(NULL, ctx->wc_ctx, local_abspath, - FALSE, pool, pool)); - SVN_ERR(relegate_dir_external(ctx->wc_ctx, defining_abspath, local_abspath, ctx->cancel_func, ctx->cancel_baton, + ctx->notify_func2, ctx->notify_baton2, pool)); } else @@ -295,28 +377,26 @@ switch_dir_external(const char *local_abspath, FALSE, FALSE, timestamp_sleep, ctx, pool)); - { - const char *repos_root_url; - const char *repos_uuid; - - SVN_ERR(svn_wc__node_get_repos_info(&repos_root_url, - &repos_uuid, - ctx->wc_ctx, local_abspath, - pool, pool)); - - SVN_ERR(svn_wc__external_register(ctx->wc_ctx, - defining_abspath, - local_abspath, svn_node_dir, - repos_root_url, repos_uuid, - svn_uri_skip_ancestor(repos_root_url, - url, pool), - SVN_INVALID_REVNUM, - SVN_INVALID_REVNUM, - pool)); - /* Issue #4123: We don't need to keep the newly checked out external's - DB open. */ - SVN_ERR(svn_wc__close_db(local_abspath, ctx->wc_ctx, pool)); - } + SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL, + &repos_root_url, + &repos_uuid, + ctx->wc_ctx, local_abspath, + pool, pool)); + + SVN_ERR(svn_wc__external_register(ctx->wc_ctx, + defining_abspath, + local_abspath, svn_node_dir, + repos_root_url, repos_uuid, + svn_uri_skip_ancestor(repos_root_url, + url, pool), + external_peg_rev, + external_rev, + pool)); + + cleanup: + /* Issues #4123 and #4130: We don't need to keep the newly checked + out external's DB open. */ + SVN_ERR(svn_wc__close_db(local_abspath, ctx->wc_ctx, pool)); return SVN_NO_ERROR; } @@ -331,25 +411,16 @@ switch_file_external(const char *local_abspath, const svn_opt_revision_t *revision, const char *def_dir_abspath, svn_ra_session_t *ra_session, - const char *ra_session_url, - svn_revnum_t ra_revnum, - const char *repos_root_url, - svn_boolean_t *timestamp_sleep, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { - apr_pool_t *subpool = svn_pool_create(scratch_pool); - const char *dir_abspath; - const char *target; - svn_config_t *cfg = ctx->config ? apr_hash_get(ctx->config, - SVN_CONFIG_CATEGORY_CONFIG, - APR_HASH_KEY_STRING) : NULL; + svn_config_t *cfg = ctx->config + ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) + : NULL; svn_boolean_t use_commit_times; const char *diff3_cmd; const char *preserved_exts_str; const apr_array_header_t *preserved_exts; - svn_boolean_t locked_here; - svn_error_t *err = NULL; svn_node_kind_t kind, external_kind; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); @@ -364,32 +435,27 @@ switch_file_external(const char *local_abspath, SVN_CONFIG_OPTION_DIFF3_CMD, NULL); if (diff3_cmd != NULL) - SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, subpool)); + SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool)); /* See which files the user wants to preserve the extension of when conflict files are made. */ svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, ""); preserved_exts = *preserved_exts_str - ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, subpool) + ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, scratch_pool) : NULL; - svn_dirent_split(&dir_abspath, &target, local_abspath, subpool); + { + const char *wcroot_abspath; - /* Try to get a access baton for the anchor using the input access - baton. If this fails and returns SVN_ERR_WC_NOT_LOCKED, then try - to get a new access baton to support inserting a file external - into a directory external. */ - SVN_ERR(svn_wc_locked2(&locked_here, NULL, ctx->wc_ctx, dir_abspath, - subpool)); - if (!locked_here) - { - const char *wcroot_abspath; + SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, local_abspath, + scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__get_wc_root(&wcroot_abspath, ctx->wc_ctx, dir_abspath, - subpool, subpool)); + /* File externals can only be installed inside the current working copy. + So verify if the working copy that contains/will contain the target + is the defining abspath, or one of its ancestors */ - if (!svn_dirent_is_ancestor(wcroot_abspath, def_dir_abspath)) + if (!svn_dirent_is_ancestor(wcroot_abspath, def_dir_abspath)) return svn_error_createf( SVN_ERR_WC_BAD_PATH, NULL, _("Cannot insert a file external defined on '%s' " @@ -398,18 +464,14 @@ switch_file_external(const char *local_abspath, scratch_pool), svn_dirent_local_style(wcroot_abspath, scratch_pool)); - } - - err = svn_wc_read_kind(&kind, ctx->wc_ctx, local_abspath, FALSE, subpool); - if (err) - goto cleanup; + } - err = svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, NULL, - ctx->wc_ctx, local_abspath, local_abspath, - TRUE, scratch_pool, scratch_pool); + SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, local_abspath, + TRUE, FALSE, scratch_pool)); - if (err) - goto cleanup; + SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, NULL, + ctx->wc_ctx, local_abspath, local_abspath, + TRUE, scratch_pool, scratch_pool)); /* If there is a versioned item with this name, ensure it's a file external before working with it. If there is no entry in the @@ -419,35 +481,25 @@ switch_file_external(const char *local_abspath, { if (external_kind != svn_node_file) { - if (!locked_here) - SVN_ERR(svn_wc__release_write_lock(ctx->wc_ctx, dir_abspath, - subpool)); - return svn_error_createf( SVN_ERR_CLIENT_FILE_EXTERNAL_OVERWRITE_VERSIONED, 0, _("The file external from '%s' cannot overwrite the existing " "versioned item at '%s'"), - url, svn_dirent_local_style(local_abspath, subpool)); + url, svn_dirent_local_style(local_abspath, scratch_pool)); } } else { svn_node_kind_t disk_kind; - err = svn_io_check_path(local_abspath, &disk_kind, subpool); - - if (err) - goto cleanup; + SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, scratch_pool)); if (kind == svn_node_file || kind == svn_node_dir) - { - err = svn_error_createf(SVN_ERR_WC_PATH_FOUND, NULL, - _("The file external '%s' can not be " - "created because the node exists."), - svn_dirent_local_style(local_abspath, - subpool)); - goto cleanup; - } + return svn_error_createf(SVN_ERR_WC_PATH_FOUND, NULL, + _("The file external '%s' can not be " + "created because the node exists."), + svn_dirent_local_style(local_abspath, + scratch_pool)); } { @@ -455,35 +507,41 @@ switch_file_external(const char *local_abspath, void *report_baton; const svn_delta_editor_t *switch_editor; void *switch_baton; - const char *switch_rev_url; - const char *repos_uuid; + svn_client__pathrev_t *switch_loc; svn_revnum_t revnum; - /* ### TODO: Provide the real definition path (now available in - ### def_dir_abspath) after switching to the new externals store. - ### We can't enable this now, because that would move the external - ### information into the wrong working copy */ - const char *definition_abspath = svn_dirent_dirname(local_abspath,subpool); - - /* Open an RA session to 'source' URL */ - SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum, - &switch_rev_url, - url, dir_abspath, - peg_revision, revision, - ctx, subpool)); - - SVN_ERR(svn_ra_reparent(ra_session, url, subpool)); - SVN_ERR(svn_ra_get_uuid2(ra_session, &repos_uuid, subpool)); + apr_array_header_t *inherited_props; + const char *dir_abspath; + const char *target; + + svn_dirent_split(&dir_abspath, &target, local_abspath, scratch_pool); + + /* ### Why do we open a new session? RA_SESSION is a valid + ### session -- the caller used it to call svn_ra_check_path on + ### this very URL, the caller also did the resolving and + ### reparenting that is repeated here. */ + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &switch_loc, + url, dir_abspath, + peg_revision, revision, + ctx, scratch_pool)); + /* Get the external file's iprops. */ + SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "", + switch_loc->rev, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_ra_reparent(ra_session, svn_uri_dirname(url, scratch_pool), + scratch_pool)); SVN_ERR(svn_wc__get_file_external_editor(&switch_editor, &switch_baton, &revnum, ctx->wc_ctx, local_abspath, - definition_abspath /* wri */, - switch_rev_url, - repos_root_url, - repos_uuid, + def_dir_abspath, + switch_loc->url, + switch_loc->repos_root_url, + switch_loc->repos_uuid, + inherited_props, use_commit_times, diff3_cmd, preserved_exts, - definition_abspath /* def */, + def_dir_abspath, url, peg_revision, revision, ctx->conflict_func2, ctx->conflict_baton2, @@ -491,300 +549,139 @@ switch_file_external(const char *local_abspath, ctx->cancel_baton, ctx->notify_func2, ctx->notify_baton2, - subpool, subpool)); + scratch_pool, scratch_pool)); /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an invalid revnum, that means RA will use the latest revision. */ - SVN_ERR(svn_ra_do_switch2(ra_session, &reporter, &report_baton, revnum, - target, svn_depth_unknown, url, - switch_editor, switch_baton, subpool)); - - SVN_ERR(svn_wc__crawl_file_external(ctx->wc_ctx, local_abspath, - reporter, report_baton, - TRUE, use_commit_times, - ctx->cancel_func, ctx->cancel_baton, - ctx->notify_func2, ctx->notify_baton2, - subpool)); + SVN_ERR(svn_ra_do_switch3(ra_session, &reporter, &report_baton, + switch_loc->rev, + target, svn_depth_unknown, switch_loc->url, + FALSE /* send_copyfrom */, + TRUE /* ignore_ancestry */, + switch_editor, switch_baton, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc__crawl_file_external(ctx->wc_ctx, local_abspath, + reporter, report_baton, + TRUE, use_commit_times, + ctx->cancel_func, ctx->cancel_baton, + ctx->notify_func2, ctx->notify_baton2, + scratch_pool)); if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_completed, - subpool); + scratch_pool); notify->kind = svn_node_none; notify->content_state = notify->prop_state = svn_wc_notify_state_inapplicable; notify->lock_state = svn_wc_notify_lock_state_inapplicable; notify->revision = revnum; - (*ctx->notify_func2)(ctx->notify_baton2, notify, subpool); + (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool); } } -cleanup: - if (!locked_here) - err = svn_error_compose_create(err, - svn_wc__release_write_lock(ctx->wc_ctx, dir_abspath, subpool)); - - svn_pool_destroy(subpool); - return svn_error_trace(err); + return SVN_NO_ERROR; } -/* Return the scheme of @a uri in @a scheme allocated from @a pool. - If @a uri does not appear to be a valid URI, then @a scheme will - not be updated. */ +/* Wrappers around svn_wc__external_remove, obtaining and releasing a lock for + directory externals */ static svn_error_t * -uri_scheme(const char **scheme, const char *uri, apr_pool_t *pool) +remove_external2(svn_boolean_t *removed, + svn_wc_context_t *wc_ctx, + const char *wri_abspath, + const char *local_abspath, + svn_node_kind_t external_kind, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) { - apr_size_t i; - - for (i = 0; uri[i] && uri[i] != ':'; ++i) - if (uri[i] == '/') - goto error; - - if (i > 0 && uri[i] == ':' && uri[i+1] == '/' && uri[i+2] == '/') - { - *scheme = apr_pstrmemdup(pool, uri, i); - return SVN_NO_ERROR; - } + SVN_ERR(svn_wc__external_remove(wc_ctx, wri_abspath, + local_abspath, + (external_kind == svn_node_none), + cancel_func, cancel_baton, + scratch_pool)); -error: - return svn_error_createf(SVN_ERR_BAD_URL, 0, - _("URL '%s' does not begin with a scheme"), - uri); + *removed = TRUE; + return SVN_NO_ERROR; } -/* If the URL for @a item is relative, then using the repository root - URL @a repos_root_url and the parent directory URL @parent_dir_url, - resolve it into an absolute URL and save it in @a item. - - Regardless if the URL is absolute or not, if there are no errors, - the URL in @a item will be canonicalized. - - The following relative URL formats are supported: - - ../ relative to the parent directory of the external - ^/ relative to the repository root - // relative to the scheme - / relative to the server's hostname - The ../ and ^/ relative URLs may use .. to remove path elements up - to the server root. - - The external URL should not be canonicalized before calling this function, - as otherwise the scheme relative URL '//host/some/path' would have been - canonicalized to '/host/some/path' and we would not be able to match on - the leading '//'. */ static svn_error_t * -resolve_relative_external_url(const char **resolved_url, - const svn_wc_external_item2_t *item, - const char *repos_root_url, - const char *parent_dir_url, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) +remove_external(svn_boolean_t *removed, + svn_wc_context_t *wc_ctx, + const char *wri_abspath, + const char *local_abspath, + svn_node_kind_t external_kind, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) { - const char *url = item->url; - apr_uri_t parent_dir_uri; - apr_status_t status; - - *resolved_url = item->url; - - /* If the URL is already absolute, there is nothing to do. */ - if (svn_path_is_url(url)) - { - /* "http://server/path" */ - *resolved_url = svn_uri_canonicalize(url, result_pool); - return SVN_NO_ERROR; - } - - if (url[0] == '/') - { - /* "/path", "//path", and "///path" */ - int num_leading_slashes = 1; - if (url[1] == '/') - { - num_leading_slashes++; - if (url[2] == '/') - num_leading_slashes++; - } - - /* "//schema-relative" and in some cases "///schema-relative". - This last format is supported on file:// schema relative. */ - url = apr_pstrcat(scratch_pool, - apr_pstrndup(scratch_pool, url, num_leading_slashes), - svn_relpath_canonicalize(url + num_leading_slashes, - scratch_pool), - (char*)NULL); - } - else - { - /* "^/path" and "../path" */ - url = svn_relpath_canonicalize(url, scratch_pool); - } - - /* Parse the parent directory URL into its parts. */ - status = apr_uri_parse(scratch_pool, parent_dir_url, &parent_dir_uri); - if (status) - return svn_error_createf(SVN_ERR_BAD_URL, 0, - _("Illegal parent directory URL '%s'"), - parent_dir_url); - - /* If the parent directory URL is at the server root, then the URL - may have no / after the hostname so apr_uri_parse() will leave - the URL's path as NULL. */ - if (! parent_dir_uri.path) - parent_dir_uri.path = apr_pstrmemdup(scratch_pool, "/", 1); - parent_dir_uri.query = NULL; - parent_dir_uri.fragment = NULL; - - /* Handle URLs relative to the current directory or to the - repository root. The backpaths may only remove path elements, - not the hostname. This allows an external to refer to another - repository in the same server relative to the location of this - repository, say using SVNParentPath. */ - if ((0 == strncmp("../", url, 3)) || - (0 == strncmp("^/", url, 2))) - { - apr_array_header_t *base_components; - apr_array_header_t *relative_components; - int i; - - /* Decompose either the parent directory's URL path or the - repository root's URL path into components. */ - if (0 == strncmp("../", url, 3)) - { - base_components = svn_path_decompose(parent_dir_uri.path, - scratch_pool); - relative_components = svn_path_decompose(url, scratch_pool); - } - else - { - apr_uri_t repos_root_uri; - - status = apr_uri_parse(scratch_pool, repos_root_url, - &repos_root_uri); - if (status) - return svn_error_createf(SVN_ERR_BAD_URL, 0, - _("Illegal repository root URL '%s'"), - repos_root_url); - - /* If the repository root URL is at the server root, then - the URL may have no / after the hostname so - apr_uri_parse() will leave the URL's path as NULL. */ - if (! repos_root_uri.path) - repos_root_uri.path = apr_pstrmemdup(scratch_pool, "/", 1); - - base_components = svn_path_decompose(repos_root_uri.path, - scratch_pool); - relative_components = svn_path_decompose(url + 2, scratch_pool); - } - - for (i = 0; i < relative_components->nelts; ++i) - { - const char *component = APR_ARRAY_IDX(relative_components, - i, - const char *); - if (0 == strcmp("..", component)) - { - /* Constructing the final absolute URL together with - apr_uri_unparse() requires that the path be absolute, - so only pop a component if the component being popped - is not the component for the root directory. */ - if (base_components->nelts > 1) - apr_array_pop(base_components); - } - else - APR_ARRAY_PUSH(base_components, const char *) = component; - } - - parent_dir_uri.path = (char *)svn_path_compose(base_components, - scratch_pool); - *resolved_url = svn_uri_canonicalize(apr_uri_unparse(scratch_pool, - &parent_dir_uri, 0), - result_pool); - return SVN_NO_ERROR; - } - - /* The remaining URLs are relative to the either the scheme or - server root and can only refer to locations inside that scope, so - backpaths are not allowed. */ - if (svn_path_is_backpath_present(url + 2)) - return svn_error_createf(SVN_ERR_BAD_URL, 0, - _("The external relative URL '%s' cannot have " - "backpaths, i.e. '..'"), - item->url); - - /* Relative to the scheme: Build a new URL from the parts we know. */ - if (0 == strncmp("//", url, 2)) + *removed = FALSE; + switch (external_kind) { - const char *scheme; - - SVN_ERR(uri_scheme(&scheme, repos_root_url, scratch_pool)); - *resolved_url = svn_uri_canonicalize(apr_pstrcat(scratch_pool, scheme, - ":", url, (char *)NULL), - result_pool); - return SVN_NO_ERROR; - } - - /* Relative to the server root: Just replace the path portion of the - parent's URL. */ - if (url[0] == '/') - { - parent_dir_uri.path = (char *)url; - *resolved_url = svn_uri_canonicalize(apr_uri_unparse(scratch_pool, - &parent_dir_uri, 0), - result_pool); - return SVN_NO_ERROR; + case svn_node_dir: + SVN_WC__CALL_WITH_WRITE_LOCK( + remove_external2(removed, + wc_ctx, wri_abspath, + local_abspath, external_kind, + cancel_func, cancel_baton, + scratch_pool), + wc_ctx, local_abspath, FALSE, scratch_pool); + break; + case svn_node_file: + default: + SVN_ERR(remove_external2(removed, + wc_ctx, wri_abspath, + local_abspath, external_kind, + cancel_func, cancel_baton, + scratch_pool)); + break; } - return svn_error_createf(SVN_ERR_BAD_URL, 0, - _("Unrecognized format for the relative external " - "URL '%s'"), - item->url); + return SVN_NO_ERROR; } +/* Called when an external that is in the EXTERNALS table is no longer + referenced from an svn:externals property */ static svn_error_t * -handle_external_item_removal(const struct external_change_baton_t *eb, +handle_external_item_removal(const svn_client_ctx_t *ctx, const char *defining_abspath, const char *local_abspath, apr_pool_t *scratch_pool) { - /* This branch is only used when an external is deleted from the - repository and the working copy is updated or committed. */ - svn_error_t *err; - svn_boolean_t lock_existed; + svn_node_kind_t external_kind; svn_node_kind_t kind; - const char *lock_root_abspath = NULL; + svn_boolean_t removed = FALSE; /* local_abspath should be a wcroot or a file external */ - SVN_ERR(svn_wc_read_kind(&kind, eb->ctx->wc_ctx, local_abspath, FALSE, + SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, NULL, + ctx->wc_ctx, defining_abspath, + local_abspath, FALSE, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, local_abspath, TRUE, FALSE, scratch_pool)); - if (kind == svn_node_none) - return SVN_NO_ERROR; /* It's neither... Nothing to remove */ + if (external_kind != kind) + external_kind = svn_node_none; /* Only remove the registration */ - SVN_ERR(svn_wc_locked2(&lock_existed, NULL, eb->ctx->wc_ctx, - local_abspath, scratch_pool)); + err = remove_external(&removed, + ctx->wc_ctx, defining_abspath, local_abspath, + external_kind, + ctx->cancel_func, ctx->cancel_baton, + scratch_pool); - if (! lock_existed) + if (err && err->apr_err == SVN_ERR_WC_NOT_LOCKED && removed) { - SVN_ERR(svn_wc__acquire_write_lock(&lock_root_abspath, - eb->ctx->wc_ctx, local_abspath, - FALSE, - scratch_pool, - scratch_pool)); + svn_error_clear(err); + err = NULL; /* We removed the working copy, so we can't release the + lock that was stored inside */ } - /* We don't use relegate_dir_external() here, because we know that - nothing else in this externals description (at least) is - going to need this directory, and therefore it's better to - leave stuff where the user expects it. */ - err = svn_wc__external_remove(eb->ctx->wc_ctx, defining_abspath, - local_abspath, - eb->ctx->cancel_func, eb->ctx->cancel_baton, - scratch_pool); - - if (eb->ctx->notify_func2) + if (ctx->notify_func2) { svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath, @@ -794,8 +691,18 @@ handle_external_item_removal(const struct external_change_baton_t *eb, notify->kind = kind; notify->err = err; - (eb->ctx->notify_func2)(eb->ctx->notify_baton2, - notify, scratch_pool); + (ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool); + + if (err && err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD) + { + notify = svn_wc_create_notify(local_abspath, + svn_wc_notify_left_local_modifications, + scratch_pool); + notify->kind = svn_node_dir; + notify->err = err; + + (ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool); + } } if (err && err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD) @@ -804,47 +711,26 @@ handle_external_item_removal(const struct external_change_baton_t *eb, err = NULL; } - - /* Unlock if we acquired the lock */ - if (lock_root_abspath != NULL) - { - svn_error_t *err2 = svn_wc__release_write_lock(eb->ctx->wc_ctx, - lock_root_abspath, - scratch_pool); - - if (err2 && err2->apr_err == SVN_ERR_WC_NOT_LOCKED) - { - /* We removed the lock by removing the node, how nice! */ - svn_error_clear(err2); - } - else - err = svn_error_compose_create(err, err2); - } - return svn_error_trace(err); } static svn_error_t * -handle_external_item_change(const struct external_change_baton_t *eb, +handle_external_item_change(svn_client_ctx_t *ctx, + const char *repos_root_url, const char *parent_dir_abspath, const char *parent_dir_url, const char *local_abspath, const char *old_defining_abspath, const svn_wc_external_item2_t *new_item, + svn_boolean_t *timestamp_sleep, apr_pool_t *scratch_pool) { svn_ra_session_t *ra_session; - svn_revnum_t ra_revnum; - const char *ra_session_url; - const char *repos_root_url; - const char *repos_uuid; + svn_client__pathrev_t *new_loc; const char *new_url; svn_node_kind_t ext_kind; - svn_node_kind_t local_kind; - - local_kind = svn_node_unknown; - SVN_ERR_ASSERT(eb->repos_root_url && parent_dir_url); + SVN_ERR_ASSERT(repos_root_url && parent_dir_url); SVN_ERR_ASSERT(new_item != NULL); /* Don't bother to check status, since we'll get that for free by @@ -854,38 +740,32 @@ handle_external_item_change(const struct external_change_baton_t *eb, iterpool, since the hash table values outlive the iterpool and any pointers they have should also outlive the iterpool. */ - SVN_ERR(resolve_relative_external_url(&new_url, - new_item, eb->repos_root_url, - parent_dir_url, - scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__resolve_relative_external_url(&new_url, + new_item, repos_root_url, + parent_dir_url, + scratch_pool, scratch_pool)); /* Determine if the external is a file or directory. */ /* Get the RA connection. */ - SVN_ERR(svn_client__ra_session_from_path(&ra_session, - &ra_revnum, - &ra_session_url, - new_url, NULL, - &(new_item->peg_revision), - &(new_item->revision), eb->ctx, - scratch_pool)); - - SVN_ERR(svn_ra_get_uuid2(ra_session, &repos_uuid, scratch_pool)); - SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, scratch_pool)); - SVN_ERR(svn_ra_check_path(ra_session, "", ra_revnum, &ext_kind, + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc, + new_url, NULL, + &(new_item->peg_revision), + &(new_item->revision), ctx, + scratch_pool)); + + SVN_ERR(svn_ra_check_path(ra_session, "", new_loc->rev, &ext_kind, scratch_pool)); if (svn_node_none == ext_kind) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' at revision %ld doesn't exist"), - ra_session_url, ra_revnum); + new_loc->url, new_loc->rev); if (svn_node_dir != ext_kind && svn_node_file != ext_kind) return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, _("URL '%s' at revision %ld is not a file " "or a directory"), - ra_session_url, ra_revnum); - - local_kind = ext_kind; + new_loc->url, new_loc->rev); /* Not protecting against recursive externals. Detecting them in @@ -893,10 +773,10 @@ handle_external_item_change(const struct external_change_baton_t *eb, user when it happens. Worst case: your disk fills up :-). */ /* First notify that we're about to handle an external. */ - if (eb->ctx->notify_func2) + if (ctx->notify_func2) { - (*eb->ctx->notify_func2)( - eb->ctx->notify_baton2, + (*ctx->notify_func2)( + ctx->notify_baton2, svn_wc_create_notify(local_abspath, svn_wc_notify_update_external, scratch_pool), @@ -912,24 +792,26 @@ handle_external_item_change(const struct external_change_baton_t *eb, scratch_pool)); } - switch (local_kind) + switch (ext_kind) { case svn_node_dir: - SVN_ERR(switch_dir_external(local_abspath, new_url, + SVN_ERR(switch_dir_external(local_abspath, new_loc->url, + new_item->url, &(new_item->peg_revision), &(new_item->revision), parent_dir_abspath, - eb->timestamp_sleep, eb->ctx, + timestamp_sleep, ctx, scratch_pool)); break; case svn_node_file: - if (strcmp(eb->repos_root_url, repos_root_url)) + if (strcmp(repos_root_url, new_loc->repos_root_url)) { const char *local_repos_root_url; const char *local_repos_uuid; const char *ext_repos_relpath; - - /* + svn_error_t *err; + + /* * The working copy library currently requires that all files * in the working copy have the same repository root URL. * The URL from the file external's definition differs from the @@ -938,34 +820,41 @@ handle_external_item_change(const struct external_change_baton_t *eb, * sure both URLs point to the same repository. See issue #4087. */ - SVN_ERR(svn_wc__node_get_repos_info(&local_repos_root_url, - &local_repos_uuid, - eb->ctx->wc_ctx, - parent_dir_abspath, - scratch_pool, scratch_pool)); - ext_repos_relpath = svn_uri_skip_ancestor(repos_root_url, + err = svn_wc__node_get_repos_info(NULL, NULL, + &local_repos_root_url, + &local_repos_uuid, + ctx->wc_ctx, parent_dir_abspath, + scratch_pool, scratch_pool); + if (err) + { + if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND + && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) + return svn_error_trace(err); + + svn_error_clear(err); + local_repos_root_url = NULL; + local_repos_uuid = NULL; + } + + ext_repos_relpath = svn_uri_skip_ancestor(new_loc->repos_root_url, new_url, scratch_pool); if (local_repos_uuid == NULL || local_repos_root_url == NULL || ext_repos_relpath == NULL || - strcmp(local_repos_uuid, repos_uuid) != 0) + strcmp(local_repos_uuid, new_loc->repos_uuid) != 0) return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, - _("Unsupported external: url of file external '%s' " + _("Unsupported external: URL of file external '%s' " "is not in repository '%s'"), - new_url, eb->repos_root_url); + new_url, repos_root_url); new_url = svn_path_url_add_component2(local_repos_root_url, ext_repos_relpath, scratch_pool); - SVN_ERR(svn_client__ra_session_from_path(&ra_session, - &ra_revnum, - &ra_session_url, - new_url, - NULL, - &(new_item->peg_revision), - &(new_item->revision), - eb->ctx, scratch_pool)); - SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, - scratch_pool)); + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc, + new_url, + NULL, + &(new_item->peg_revision), + &(new_item->revision), + ctx, scratch_pool)); } SVN_ERR(switch_file_external(local_abspath, @@ -974,12 +863,10 @@ handle_external_item_change(const struct external_change_baton_t *eb, &new_item->revision, parent_dir_abspath, ra_session, - ra_session_url, - ra_revnum, - repos_root_url, - eb->timestamp_sleep, eb->ctx, + ctx, scratch_pool)); break; + default: SVN_ERR_MALFUNCTION(); break; @@ -989,22 +876,21 @@ handle_external_item_change(const struct external_change_baton_t *eb, } static svn_error_t * -wrap_external_error(const struct external_change_baton_t *eb, +wrap_external_error(const svn_client_ctx_t *ctx, const char *target_abspath, svn_error_t *err, apr_pool_t *scratch_pool) { if (err && err->apr_err != SVN_ERR_CANCELLED) { - if (eb->ctx->notify_func2) + if (ctx->notify_func2) { svn_wc_notify_t *notifier = svn_wc_create_notify( target_abspath, svn_wc_notify_failed_external, scratch_pool); notifier->err = err; - eb->ctx->notify_func2(eb->ctx->notify_baton2, notifier, - scratch_pool); + ctx->notify_func2(ctx->notify_baton2, notifier, scratch_pool); } svn_error_clear(err); return SVN_NO_ERROR; @@ -1013,12 +899,10 @@ wrap_external_error(const struct external_change_baton_t *eb, return err; } -/* This implements the 'svn_hash_diff_func_t' interface. - BATON is of type 'struct handle_externals_desc_change_baton *'. - KEY is a 'const char *'. -*/ static svn_error_t * -handle_externals_change(const struct external_change_baton_t *eb, +handle_externals_change(svn_client_ctx_t *ctx, + const char *repos_root_url, + svn_boolean_t *timestamp_sleep, const char *local_abspath, const char *new_desc_text, apr_hash_t *old_externals, @@ -1049,7 +933,7 @@ handle_externals_change(const struct external_change_baton_t *eb, else new_desc = NULL; - SVN_ERR(svn_wc__node_get_url(&url, eb->ctx->wc_ctx, local_abspath, + SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, local_abspath, scratch_pool, iterpool)); SVN_ERR_ASSERT(url); @@ -1059,29 +943,47 @@ handle_externals_change(const struct external_change_baton_t *eb, const char *old_defining_abspath; svn_wc_external_item2_t *new_item; const char *target_abspath; + svn_boolean_t under_root; new_item = APR_ARRAY_IDX(new_desc, i, svn_wc_external_item2_t *); svn_pool_clear(iterpool); - target_abspath = svn_dirent_join(local_abspath, new_item->target_dir, - iterpool); + if (ctx->cancel_func) + SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); + + SVN_ERR(svn_dirent_is_under_root(&under_root, &target_abspath, + local_abspath, new_item->target_dir, + iterpool)); - old_defining_abspath = apr_hash_get(old_externals, target_abspath, - APR_HASH_KEY_STRING); + if (! under_root) + { + return svn_error_createf( + SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, + _("Path '%s' is not in the working copy"), + svn_dirent_local_style( + svn_dirent_join(local_abspath, new_item->target_dir, + iterpool), + iterpool)); + } + + old_defining_abspath = svn_hash_gets(old_externals, target_abspath); SVN_ERR(wrap_external_error( - eb, target_abspath, - handle_external_item_change(eb, local_abspath, url, + ctx, target_abspath, + handle_external_item_change(ctx, + repos_root_url, + local_abspath, url, target_abspath, old_defining_abspath, new_item, + timestamp_sleep, iterpool), iterpool)); /* And remove already processed items from the to-remove hash */ if (old_defining_abspath) - apr_hash_set(old_externals, target_abspath, APR_HASH_KEY_STRING, NULL); + svn_hash_sets(old_externals, target_abspath, NULL); } svn_pool_destroy(iterpool); @@ -1103,19 +1005,11 @@ svn_client__handle_externals(apr_hash_t *externals_new, apr_hash_t *old_external_defs; apr_hash_index_t *hi; apr_pool_t *iterpool; - struct external_change_baton_t eb; SVN_ERR_ASSERT(repos_root_url); - eb.repos_root_url = repos_root_url; - eb.ctx = ctx; - eb.timestamp_sleep = timestamp_sleep; - - iterpool = svn_pool_create(scratch_pool); - /* Parse the old externals. This part will be replaced by reading EXTERNALS - from the DB. */ SVN_ERR(svn_wc__externals_defined_below(&old_external_defs, ctx->wc_ctx, target_abspath, scratch_pool, iterpool)); @@ -1150,7 +1044,8 @@ svn_client__handle_externals(apr_hash_t *externals_new, } } - SVN_ERR(handle_externals_change(&eb, local_abspath, + SVN_ERR(handle_externals_change(ctx, repos_root_url, timestamp_sleep, + local_abspath, desc_text, old_external_defs, ambient_depth, requested_depth, iterpool)); @@ -1168,8 +1063,8 @@ svn_client__handle_externals(apr_hash_t *externals_new, svn_pool_clear(iterpool); SVN_ERR(wrap_external_error( - &eb, item_abspath, - handle_external_item_removal(&eb, defining_abspath, + ctx, item_abspath, + handle_external_item_removal(ctx, defining_abspath, item_abspath, iterpool), iterpool)); @@ -1177,23 +1072,34 @@ svn_client__handle_externals(apr_hash_t *externals_new, * external and the DEFINING_ABSPATH which we can remove? */ parent_abspath = item_abspath; do { - svn_wc_status3_t *parent_status; + svn_node_kind_t kind; parent_abspath = svn_dirent_dirname(parent_abspath, iterpool); - SVN_ERR(svn_wc_status3(&parent_status, ctx->wc_ctx, parent_abspath, - iterpool, iterpool)); - if (parent_status->node_status == svn_wc_status_unversioned) + SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, parent_abspath, + FALSE /* show_deleted*/, + FALSE /* show_hidden */, + iterpool)); + if (kind == svn_node_none) { svn_error_t *err; err = svn_io_dir_remove_nonrecursive(parent_abspath, iterpool); - if (err && APR_STATUS_IS_ENOTEMPTY(err->apr_err)) + if (err) { - svn_error_clear(err); - break; + if (APR_STATUS_IS_ENOTEMPTY(err->apr_err)) + { + svn_error_clear(err); + break; /* No parents to delete */ + } + else if (APR_STATUS_IS_ENOENT(err->apr_err) + || APR_STATUS_IS_ENOTDIR(err->apr_err)) + { + svn_error_clear(err); + /* Fall through; parent dir might be unversioned */ + } + else + return svn_error_trace(err); } - else - SVN_ERR(err); } } while (strcmp(parent_abspath, defining_abspath) != 0); } @@ -1212,21 +1118,15 @@ svn_client__export_externals(apr_hash_t *externals, svn_depth_t requested_depth, const char *native_eol, svn_boolean_t ignore_keywords, - svn_boolean_t *timestamp_sleep, svn_client_ctx_t *ctx, apr_pool_t *scratch_pool) { - struct external_change_baton_t eb = { 0 }; apr_pool_t *iterpool = svn_pool_create(scratch_pool); apr_pool_t *sub_iterpool = svn_pool_create(scratch_pool); apr_hash_index_t *hi; SVN_ERR_ASSERT(svn_dirent_is_absolute(to_abspath)); - eb.repos_root_url = repos_root_url; - eb.ctx = ctx; - eb.timestamp_sleep = timestamp_sleep; - for (hi = apr_hash_first(scratch_pool, externals); hi; hi = apr_hash_next(hi)) @@ -1242,7 +1142,7 @@ svn_client__export_externals(apr_hash_t *externals, SVN_ERR(svn_wc_parse_externals_description3(&items, local_abspath, desc_text, FALSE, - scratch_pool)); + iterpool)); if (! items->nelts) continue; @@ -1256,17 +1156,31 @@ svn_client__export_externals(apr_hash_t *externals, { const char *item_abspath; const char *new_url; + svn_boolean_t under_root; svn_wc_external_item2_t *item = APR_ARRAY_IDX(items, i, svn_wc_external_item2_t *); svn_pool_clear(sub_iterpool); - item_abspath = svn_dirent_join(local_abspath, item->target_dir, - sub_iterpool); + SVN_ERR(svn_dirent_is_under_root(&under_root, &item_abspath, + local_abspath, item->target_dir, + sub_iterpool)); + + if (! under_root) + { + return svn_error_createf( + SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL, + _("Path '%s' is not in the working copy"), + svn_dirent_local_style( + svn_dirent_join(local_abspath, item->target_dir, + sub_iterpool), + sub_iterpool)); + } - SVN_ERR(resolve_relative_external_url(&new_url, item, - repos_root_url, dir_url, - sub_iterpool, sub_iterpool)); + SVN_ERR(svn_wc__resolve_relative_external_url(&new_url, item, + repos_root_url, + dir_url, sub_iterpool, + sub_iterpool)); /* The target dir might have multiple components. Guarantee the path leading down to the last component. */ @@ -1275,7 +1189,7 @@ svn_client__export_externals(apr_hash_t *externals, sub_iterpool)); SVN_ERR(wrap_external_error( - &eb, item_abspath, + ctx, item_abspath, svn_client_export5(NULL, new_url, item_abspath, &item->peg_revision, &item->revision, @@ -1293,105 +1207,3 @@ svn_client__export_externals(apr_hash_t *externals, return SVN_NO_ERROR; } - -svn_error_t * -svn_client__do_external_status(svn_client_ctx_t *ctx, - apr_hash_t *external_map, - svn_depth_t depth, - svn_boolean_t get_all, - svn_boolean_t update, - svn_boolean_t no_ignore, - svn_client_status_func_t status_func, - void *status_baton, - apr_pool_t *pool) -{ - apr_hash_index_t *hi; - apr_pool_t *iterpool = svn_pool_create(pool); - - /* Loop over the hash of new values (we don't care about the old - ones). This is a mapping of versioned directories to property - values. */ - for (hi = apr_hash_first(pool, external_map); - hi; - hi = apr_hash_next(hi)) - { - svn_node_kind_t external_kind; - const char *local_abspath = svn__apr_hash_index_key(hi); - const char *defining_abspath = svn__apr_hash_index_val(hi); - svn_node_kind_t kind; - svn_opt_revision_t opt_rev; - - svn_pool_clear(iterpool); - - /* Obtain information on the expected external. */ - SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL, - &opt_rev.value.number, - ctx->wc_ctx, defining_abspath, - local_abspath, FALSE, - iterpool, iterpool)); - - if (external_kind != svn_node_dir) - continue; - - SVN_ERR(svn_io_check_path(local_abspath, &kind, iterpool)); - if (kind != svn_node_dir) - continue; - - if (SVN_IS_VALID_REVNUM(opt_rev.value.number)) - opt_rev.kind = svn_opt_revision_number; - else - opt_rev.kind = svn_opt_revision_unspecified; - - /* Tell the client we're starting an external status set. */ - if (ctx->notify_func2) - ctx->notify_func2( - ctx->notify_baton2, - svn_wc_create_notify(local_abspath, - svn_wc_notify_status_external, - iterpool), iterpool); - - /* And then do the status. */ - SVN_ERR(svn_client_status5(NULL, ctx, local_abspath, &opt_rev, depth, - get_all, update, no_ignore, FALSE, FALSE, - NULL, status_func, status_baton, - iterpool)); - } - - /* Destroy SUBPOOL and (implicitly) ITERPOOL. */ - svn_pool_destroy(iterpool); - - return SVN_NO_ERROR; -} - - -/* Implements the `svn_wc_externals_update_t' interface. */ -svn_error_t * -svn_client__external_info_gatherer(void *baton, - const char *local_abspath, - const svn_string_t *old_value, - const svn_string_t *new_value, - svn_depth_t depth, - apr_pool_t *scratch_pool) -{ - svn_client__external_func_baton_t *efb = baton; - - local_abspath = apr_pstrdup(efb->result_pool, local_abspath); - - if (efb->externals_old != NULL && old_value != NULL) - apr_hash_set(efb->externals_old, local_abspath, APR_HASH_KEY_STRING, - apr_pstrndup(efb->result_pool, - old_value->data, old_value->len)); - - if (efb->externals_new != NULL && new_value != NULL) - apr_hash_set(efb->externals_new, local_abspath, APR_HASH_KEY_STRING, - apr_pstrndup(efb->result_pool, - new_value->data, new_value->len)); - - if (efb->ambient_depths != NULL) - apr_hash_set(efb->ambient_depths, local_abspath, APR_HASH_KEY_STRING, - svn_depth_to_word(depth)); - - return SVN_NO_ERROR; -} - - |