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 /* Examing the array of svn_wc_external_item2_t's EXT_DESC (parsed
132 from the svn:externals property set on LOCAL_ABSPATH) and determine
133 if the external working copies described by such should be
134 relocated as a side-effect of the relocation of their parent
135 working copy (from OLD_PARENT_REPOS_ROOT_URL to
136 NEW_PARENT_REPOS_ROOT_URL). If so, attempt said relocation. */
138 relocate_externals(const char *local_abspath,
139 apr_array_header_t *ext_desc,
140 const char *old_parent_repos_root_url,
141 const char *new_parent_repos_root_url,
142 svn_client_ctx_t *ctx,
143 apr_pool_t *scratch_pool)
145 apr_pool_t *iterpool;
148 /* Parse an externals definition into an array of external items. */
150 iterpool = svn_pool_create(scratch_pool);
152 for (i = 0; i < ext_desc->nelts; i++)
154 svn_wc_external_item2_t *ext_item =
155 APR_ARRAY_IDX(ext_desc, i, svn_wc_external_item2_t *);
156 const char *target_repos_root_url;
157 const char *target_abspath;
160 svn_pool_clear(iterpool);
162 /* If this external isn't pulled in via a relative URL, ignore
163 it. There's no sense in relocating a working copy only to
164 have the next 'svn update' try to point it back to another
166 if (! ((strncmp("../", ext_item->url, 3) == 0) ||
167 (strncmp("^/", ext_item->url, 2) == 0)))
170 /* If the external working copy's not-yet-relocated repos root
171 URL matches the primary working copy's pre-relocated
172 repository root URL, try to relocate that external, too.
173 You might wonder why this check is needed, given that we're
174 already limiting ourselves to externals pulled via URLs
175 relative to their primary working copy. Well, it's because
176 you can use "../" to "crawl up" above one repository's URL
177 space and down into another one. */
178 SVN_ERR(svn_dirent_get_absolute(&target_abspath,
179 svn_dirent_join(local_abspath,
180 ext_item->target_dir,
183 err = svn_client_get_repos_root(&target_repos_root_url, NULL /* uuid */,
184 target_abspath, ctx, iterpool, iterpool);
186 /* Ignore externals that aren't present in the working copy.
187 * This can happen if an external is deleted from disk accidentally,
188 * or if an external is configured on a locally added directory. */
189 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
191 svn_error_clear(err);
197 if (strcmp(target_repos_root_url, old_parent_repos_root_url) == 0)
198 SVN_ERR(svn_client_relocate2(target_abspath,
199 old_parent_repos_root_url,
200 new_parent_repos_root_url,
201 FALSE, ctx, iterpool));
204 svn_pool_destroy(iterpool);
210 svn_client_relocate2(const char *wcroot_dir,
211 const char *from_prefix,
212 const char *to_prefix,
213 svn_boolean_t ignore_externals,
214 svn_client_ctx_t *ctx,
217 struct validator_baton_t vb;
218 const char *local_abspath;
219 apr_hash_t *externals_hash = NULL;
220 apr_hash_index_t *hi;
221 apr_pool_t *iterpool = NULL;
222 const char *old_repos_root_url, *new_repos_root_url;
224 /* Populate our validator callback baton, and call the relocate code. */
226 vb.path = wcroot_dir;
227 vb.url_uuids = apr_array_make(pool, 1, sizeof(struct url_uuid_t));
230 if (svn_path_is_url(wcroot_dir))
231 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
232 _("'%s' is not a local path"),
235 SVN_ERR(svn_dirent_get_absolute(&local_abspath, wcroot_dir, pool));
237 /* If we're ignoring externals, just relocate and get outta here. */
238 if (ignore_externals)
240 return svn_error_trace(svn_wc_relocate4(ctx->wc_ctx, local_abspath,
241 from_prefix, to_prefix,
242 validator_func, &vb, pool));
245 /* Fetch our current root URL. */
246 SVN_ERR(svn_client_get_repos_root(&old_repos_root_url, NULL /* uuid */,
247 local_abspath, ctx, pool, pool));
249 /* Perform the relocation. */
250 SVN_ERR(svn_wc_relocate4(ctx->wc_ctx, local_abspath, from_prefix, to_prefix,
251 validator_func, &vb, pool));
253 /* Now fetch new current root URL. */
254 SVN_ERR(svn_client_get_repos_root(&new_repos_root_url, NULL /* uuid */,
255 local_abspath, ctx, pool, pool));
258 /* Relocate externals, too (if any). */
259 SVN_ERR(svn_wc__externals_gather_definitions(&externals_hash, NULL,
260 ctx->wc_ctx, local_abspath,
263 if (! apr_hash_count(externals_hash))
266 iterpool = svn_pool_create(pool);
268 for (hi = apr_hash_first(pool, externals_hash);
270 hi = apr_hash_next(hi))
272 const char *this_abspath = svn__apr_hash_index_key(hi);
273 const char *value = svn__apr_hash_index_val(hi);
274 apr_array_header_t *ext_desc;
276 svn_pool_clear(iterpool);
278 SVN_ERR(svn_wc_parse_externals_description3(&ext_desc, this_abspath,
282 SVN_ERR(relocate_externals(this_abspath, ext_desc, old_repos_root_url,
283 new_repos_root_url, ctx, iterpool));
286 svn_pool_destroy(iterpool);