blob: 2e5c1170f1aeeb7e785de6e9d10bf2509aacaeca [file] [log] [blame]
/*
* 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;
}
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;
char *sig_from_prefix, *sig_to_prefix;
apr_size_t index_from, index_to;
/* 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_defined_below(&externals_hash,
ctx->wc_ctx, local_abspath,
pool, pool));
if (! apr_hash_count(externals_hash))
return SVN_NO_ERROR;
/* A valid prefix for the main working copy may be too long to be
valid for an external. Trim any common trailing characters to
leave the significant part that changes. */
sig_from_prefix = apr_pstrdup(pool, from_prefix);
sig_to_prefix = apr_pstrdup(pool, to_prefix);
index_from = strlen(sig_from_prefix);
index_to = strlen(sig_to_prefix);
while (index_from && index_to
&& sig_from_prefix[index_from] == sig_to_prefix[index_to])
{
sig_from_prefix[index_from] = sig_to_prefix[index_to] = '\0';
--index_from;
--index_to;
}
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, externals_hash);
hi != NULL;
hi = apr_hash_next(hi))
{
svn_node_kind_t kind;
const char *this_abspath = apr_hash_this_key(hi);
svn_pool_clear(iterpool);
SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL,
ctx->wc_ctx,
local_abspath, this_abspath,
FALSE, iterpool, iterpool));
if (kind == svn_node_dir)
{
const char *this_repos_root_url;
svn_error_t *err;
err = svn_client_get_repos_root(&this_repos_root_url, NULL /* uuid */,
this_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;
}
SVN_ERR(err);
if (strcmp(old_repos_root_url, this_repos_root_url) == 0)
SVN_ERR(svn_client_relocate2(this_abspath,
sig_from_prefix, sig_to_prefix,
FALSE /* ignore_externals */,
ctx, iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}