2 * relocate.c: wrapper around wc relocation functionality.
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 /* ==================================================================== */
31 #include "svn_client.h"
32 #include "svn_pools.h"
33 #include "svn_error.h"
34 #include "svn_dirent_uri.h"
38 #include "private/svn_wc_private.h"
40 #include "svn_private_config.h"
45 /* Repository root and UUID for a repository. */
52 struct validator_baton_t
54 svn_client_ctx_t *ctx;
56 apr_array_header_t *url_uuids;
63 validator_func(void *baton,
69 struct validator_baton_t *b = baton;
70 struct url_uuid_t *url_uuid = NULL;
71 const char *disable_checks;
73 apr_array_header_t *uuids = b->url_uuids;
76 for (i = 0; i < uuids->nelts; ++i)
78 struct url_uuid_t *uu = &APR_ARRAY_IDX(uuids, i,
80 if (svn_uri__is_ancestor(uu->root, url))
87 disable_checks = getenv("SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_RELOCATE_VALIDATION");
88 if (disable_checks && (strcmp(disable_checks, "yes") == 0))
90 /* Lie about URL_UUID's components, claiming they match the
91 expectations of the validation code below. */
92 url_uuid = apr_pcalloc(pool, sizeof(*url_uuid));
93 url_uuid->root = apr_pstrdup(pool, root_url);
94 url_uuid->uuid = apr_pstrdup(pool, uuid);
97 /* We use an RA session in a subpool to get the UUID of the
98 repository at the new URL so we can force the RA session to close
99 by destroying the subpool. */
102 apr_pool_t *sesspool = svn_pool_create(pool);
104 url_uuid = &APR_ARRAY_PUSH(uuids, struct url_uuid_t);
105 SVN_ERR(svn_client_get_repos_root(&url_uuid->root,
110 svn_pool_destroy(sesspool);
113 /* Make sure the url is a repository root if desired. */
115 && strcmp(root_url, url_uuid->root) != 0)
116 return svn_error_createf(SVN_ERR_CLIENT_INVALID_RELOCATION, NULL,
117 _("'%s' is not the root of the repository"),
120 /* Make sure the UUIDs match. */
121 if (uuid && strcmp(uuid, url_uuid->uuid) != 0)
122 return svn_error_createf
123 (SVN_ERR_CLIENT_INVALID_RELOCATION, NULL,
124 _("The repository at '%s' has uuid '%s', but the WC has '%s'"),
125 url, url_uuid->uuid, uuid);
131 svn_client_relocate2(const char *wcroot_dir,
132 const char *from_prefix,
133 const char *to_prefix,
134 svn_boolean_t ignore_externals,
135 svn_client_ctx_t *ctx,
138 struct validator_baton_t vb;
139 const char *local_abspath;
140 apr_hash_t *externals_hash = NULL;
141 apr_hash_index_t *hi;
142 apr_pool_t *iterpool = NULL;
143 const char *old_repos_root_url, *new_repos_root_url;
144 char *sig_from_prefix, *sig_to_prefix;
145 apr_size_t index_from, index_to;
147 /* Populate our validator callback baton, and call the relocate code. */
149 vb.path = wcroot_dir;
150 vb.url_uuids = apr_array_make(pool, 1, sizeof(struct url_uuid_t));
153 if (svn_path_is_url(wcroot_dir))
154 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
155 _("'%s' is not a local path"),
158 SVN_ERR(svn_dirent_get_absolute(&local_abspath, wcroot_dir, pool));
160 /* If we're ignoring externals, just relocate and get outta here. */
161 if (ignore_externals)
163 return svn_error_trace(svn_wc_relocate4(ctx->wc_ctx, local_abspath,
164 from_prefix, to_prefix,
165 validator_func, &vb, pool));
168 /* Fetch our current root URL. */
169 SVN_ERR(svn_client_get_repos_root(&old_repos_root_url, NULL /* uuid */,
170 local_abspath, ctx, pool, pool));
172 /* Perform the relocation. */
173 SVN_ERR(svn_wc_relocate4(ctx->wc_ctx, local_abspath, from_prefix, to_prefix,
174 validator_func, &vb, pool));
176 /* Now fetch new current root URL. */
177 SVN_ERR(svn_client_get_repos_root(&new_repos_root_url, NULL /* uuid */,
178 local_abspath, ctx, pool, pool));
181 /* Relocate externals, too (if any). */
182 SVN_ERR(svn_wc__externals_defined_below(&externals_hash,
183 ctx->wc_ctx, local_abspath,
185 if (! apr_hash_count(externals_hash))
188 /* A valid prefix for the main working copy may be too long to be
189 valid for an external. Trim any common trailing characters to
190 leave the significant part that changes. */
191 sig_from_prefix = apr_pstrdup(pool, from_prefix);
192 sig_to_prefix = apr_pstrdup(pool, to_prefix);
193 index_from = strlen(sig_from_prefix);
194 index_to = strlen(sig_to_prefix);
195 while (index_from && index_to
196 && sig_from_prefix[index_from] == sig_to_prefix[index_to])
198 sig_from_prefix[index_from] = sig_to_prefix[index_to] = '\0';
203 iterpool = svn_pool_create(pool);
205 for (hi = apr_hash_first(pool, externals_hash);
207 hi = apr_hash_next(hi))
209 svn_node_kind_t kind;
210 const char *this_abspath = apr_hash_this_key(hi);
212 svn_pool_clear(iterpool);
214 SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL,
216 local_abspath, this_abspath,
217 FALSE, iterpool, iterpool));
219 if (kind == svn_node_dir)
221 const char *this_repos_root_url;
224 err = svn_client_get_repos_root(&this_repos_root_url, NULL /* uuid */,
225 this_abspath, ctx, iterpool, iterpool);
227 /* Ignore externals that aren't present in the working copy.
228 * This can happen if an external is deleted from disk accidentally,
229 * or if an external is configured on a locally added directory. */
230 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
232 svn_error_clear(err);
237 if (strcmp(old_repos_root_url, this_repos_root_url) == 0)
238 SVN_ERR(svn_client_relocate2(this_abspath,
239 sig_from_prefix, sig_to_prefix,
240 FALSE /* ignore_externals */,
245 svn_pool_destroy(iterpool);