summaryrefslogtreecommitdiffstats
path: root/subversion/libsvn_client/relocate.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/relocate.c')
-rw-r--r--subversion/libsvn_client/relocate.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/subversion/libsvn_client/relocate.c b/subversion/libsvn_client/relocate.c
new file mode 100644
index 0000000..ed8d09c
--- /dev/null
+++ b/subversion/libsvn_client/relocate.c
@@ -0,0 +1,289 @@
+/*
+ * relocate.c: wrapper around wc relocation functionality.
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+/* ==================================================================== */
+
+
+
+/*** Includes. ***/
+
+#include "svn_wc.h"
+#include "svn_client.h"
+#include "svn_pools.h"
+#include "svn_error.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "client.h"
+
+#include "private/svn_wc_private.h"
+
+#include "svn_private_config.h"
+
+
+/*** Code. ***/
+
+/* Repository root and UUID for a repository. */
+struct url_uuid_t
+{
+ const char *root;
+ const char *uuid;
+};
+
+struct validator_baton_t
+{
+ svn_client_ctx_t *ctx;
+ const char *path;
+ apr_array_header_t *url_uuids;
+ apr_pool_t *pool;
+
+};
+
+
+static svn_error_t *
+validator_func(void *baton,
+ const char *uuid,
+ const char *url,
+ const char *root_url,
+ apr_pool_t *pool)
+{
+ struct validator_baton_t *b = baton;
+ struct url_uuid_t *url_uuid = NULL;
+ const char *disable_checks;
+
+ apr_array_header_t *uuids = b->url_uuids;
+ int i;
+
+ for (i = 0; i < uuids->nelts; ++i)
+ {
+ struct url_uuid_t *uu = &APR_ARRAY_IDX(uuids, i,
+ struct url_uuid_t);
+ if (svn_uri__is_ancestor(uu->root, url))
+ {
+ url_uuid = uu;
+ break;
+ }
+ }
+
+ disable_checks = getenv("SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_RELOCATE_VALIDATION");
+ if (disable_checks && (strcmp(disable_checks, "yes") == 0))
+ {
+ /* Lie about URL_UUID's components, claiming they match the
+ expectations of the validation code below. */
+ url_uuid = apr_pcalloc(pool, sizeof(*url_uuid));
+ url_uuid->root = apr_pstrdup(pool, root_url);
+ url_uuid->uuid = apr_pstrdup(pool, uuid);
+ }
+
+ /* We use an RA session in a subpool to get the UUID of the
+ repository at the new URL so we can force the RA session to close
+ by destroying the subpool. */
+ if (! url_uuid)
+ {
+ apr_pool_t *sesspool = svn_pool_create(pool);
+
+ url_uuid = &APR_ARRAY_PUSH(uuids, struct url_uuid_t);
+ SVN_ERR(svn_client_get_repos_root(&url_uuid->root,
+ &url_uuid->uuid,
+ url, b->ctx,
+ pool, sesspool));
+
+ svn_pool_destroy(sesspool);
+ }
+
+ /* Make sure the url is a repository root if desired. */
+ if (root_url
+ && strcmp(root_url, url_uuid->root) != 0)
+ return svn_error_createf(SVN_ERR_CLIENT_INVALID_RELOCATION, NULL,
+ _("'%s' is not the root of the repository"),
+ url);
+
+ /* Make sure the UUIDs match. */
+ if (uuid && strcmp(uuid, url_uuid->uuid) != 0)
+ return svn_error_createf
+ (SVN_ERR_CLIENT_INVALID_RELOCATION, NULL,
+ _("The repository at '%s' has uuid '%s', but the WC has '%s'"),
+ url, url_uuid->uuid, uuid);
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Examing the array of svn_wc_external_item2_t's EXT_DESC (parsed
+ from the svn:externals property set on LOCAL_ABSPATH) and determine
+ if the external working copies described by such should be
+ relocated as a side-effect of the relocation of their parent
+ working copy (from OLD_PARENT_REPOS_ROOT_URL to
+ NEW_PARENT_REPOS_ROOT_URL). If so, attempt said relocation. */
+static svn_error_t *
+relocate_externals(const char *local_abspath,
+ apr_array_header_t *ext_desc,
+ const char *old_parent_repos_root_url,
+ const char *new_parent_repos_root_url,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool;
+ int i;
+
+ /* Parse an externals definition into an array of external items. */
+
+ iterpool = svn_pool_create(scratch_pool);
+
+ for (i = 0; i < ext_desc->nelts; i++)
+ {
+ svn_wc_external_item2_t *ext_item =
+ APR_ARRAY_IDX(ext_desc, i, svn_wc_external_item2_t *);
+ const char *target_repos_root_url;
+ const char *target_abspath;
+ svn_error_t *err;
+
+ svn_pool_clear(iterpool);
+
+ /* If this external isn't pulled in via a relative URL, ignore
+ it. There's no sense in relocating a working copy only to
+ have the next 'svn update' try to point it back to another
+ location. */
+ if (! ((strncmp("../", ext_item->url, 3) == 0) ||
+ (strncmp("^/", ext_item->url, 2) == 0)))
+ continue;
+
+ /* If the external working copy's not-yet-relocated repos root
+ URL matches the primary working copy's pre-relocated
+ repository root URL, try to relocate that external, too.
+ You might wonder why this check is needed, given that we're
+ already limiting ourselves to externals pulled via URLs
+ relative to their primary working copy. Well, it's because
+ you can use "../" to "crawl up" above one repository's URL
+ space and down into another one. */
+ SVN_ERR(svn_dirent_get_absolute(&target_abspath,
+ svn_dirent_join(local_abspath,
+ ext_item->target_dir,
+ iterpool),
+ iterpool));
+ err = svn_client_get_repos_root(&target_repos_root_url, NULL /* uuid */,
+ target_abspath, ctx, iterpool, iterpool);
+
+ /* Ignore externals that aren't present in the working copy.
+ * This can happen if an external is deleted from disk accidentally,
+ * or if an external is configured on a locally added directory. */
+ if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ continue;
+ }
+ else
+ SVN_ERR(err);
+
+ if (strcmp(target_repos_root_url, old_parent_repos_root_url) == 0)
+ SVN_ERR(svn_client_relocate2(target_abspath,
+ old_parent_repos_root_url,
+ new_parent_repos_root_url,
+ FALSE, ctx, iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_relocate2(const char *wcroot_dir,
+ const char *from_prefix,
+ const char *to_prefix,
+ svn_boolean_t ignore_externals,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ struct validator_baton_t vb;
+ const char *local_abspath;
+ apr_hash_t *externals_hash = NULL;
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool = NULL;
+ const char *old_repos_root_url, *new_repos_root_url;
+
+ /* Populate our validator callback baton, and call the relocate code. */
+ vb.ctx = ctx;
+ vb.path = wcroot_dir;
+ vb.url_uuids = apr_array_make(pool, 1, sizeof(struct url_uuid_t));
+ vb.pool = pool;
+
+ if (svn_path_is_url(wcroot_dir))
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' is not a local path"),
+ wcroot_dir);
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, wcroot_dir, pool));
+
+ /* If we're ignoring externals, just relocate and get outta here. */
+ if (ignore_externals)
+ {
+ return svn_error_trace(svn_wc_relocate4(ctx->wc_ctx, local_abspath,
+ from_prefix, to_prefix,
+ validator_func, &vb, pool));
+ }
+
+ /* Fetch our current root URL. */
+ SVN_ERR(svn_client_get_repos_root(&old_repos_root_url, NULL /* uuid */,
+ local_abspath, ctx, pool, pool));
+
+ /* Perform the relocation. */
+ SVN_ERR(svn_wc_relocate4(ctx->wc_ctx, local_abspath, from_prefix, to_prefix,
+ validator_func, &vb, pool));
+
+ /* Now fetch new current root URL. */
+ SVN_ERR(svn_client_get_repos_root(&new_repos_root_url, NULL /* uuid */,
+ local_abspath, ctx, pool, pool));
+
+
+ /* Relocate externals, too (if any). */
+ SVN_ERR(svn_wc__externals_gather_definitions(&externals_hash, NULL,
+ ctx->wc_ctx, local_abspath,
+ svn_depth_infinity,
+ pool, pool));
+ if (! apr_hash_count(externals_hash))
+ return SVN_NO_ERROR;
+
+ iterpool = svn_pool_create(pool);
+
+ for (hi = apr_hash_first(pool, externals_hash);
+ hi != NULL;
+ hi = apr_hash_next(hi))
+ {
+ const char *this_abspath = svn__apr_hash_index_key(hi);
+ const char *value = svn__apr_hash_index_val(hi);
+ apr_array_header_t *ext_desc;
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_wc_parse_externals_description3(&ext_desc, this_abspath,
+ value, FALSE,
+ iterpool));
+ if (ext_desc->nelts)
+ SVN_ERR(relocate_externals(this_abspath, ext_desc, old_repos_root_url,
+ new_repos_root_url, ctx, iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
OpenPOWER on IntegriCloud