summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/externals.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/externals.c')
-rw-r--r--subversion/libsvn_client/externals.c1044
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;
-}
-
-