]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_client/relocate.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_client / relocate.c
1 /*
2  * relocate.c:  wrapper around wc relocation functionality.
3  *
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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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
20  *    under the License.
21  * ====================================================================
22  */
23
24 /* ==================================================================== */
25
26
27 \f
28 /*** Includes. ***/
29
30 #include "svn_wc.h"
31 #include "svn_client.h"
32 #include "svn_pools.h"
33 #include "svn_error.h"
34 #include "svn_dirent_uri.h"
35 #include "svn_path.h"
36 #include "client.h"
37
38 #include "private/svn_wc_private.h"
39
40 #include "svn_private_config.h"
41
42 \f
43 /*** Code. ***/
44
45 /* Repository root and UUID for a repository. */
46 struct url_uuid_t
47 {
48   const char *root;
49   const char *uuid;
50 };
51
52 struct validator_baton_t
53 {
54   svn_client_ctx_t *ctx;
55   const char *path;
56   apr_array_header_t *url_uuids;
57   apr_pool_t *pool;
58
59 };
60
61
62 static svn_error_t *
63 validator_func(void *baton,
64                const char *uuid,
65                const char *url,
66                const char *root_url,
67                apr_pool_t *pool)
68 {
69   struct validator_baton_t *b = baton;
70   struct url_uuid_t *url_uuid = NULL;
71   const char *disable_checks;
72
73   apr_array_header_t *uuids = b->url_uuids;
74   int i;
75
76   for (i = 0; i < uuids->nelts; ++i)
77     {
78       struct url_uuid_t *uu = &APR_ARRAY_IDX(uuids, i,
79                                              struct url_uuid_t);
80       if (svn_uri__is_ancestor(uu->root, url))
81         {
82           url_uuid = uu;
83           break;
84         }
85     }
86
87   disable_checks = getenv("SVN_I_LOVE_CORRUPTED_WORKING_COPIES_SO_DISABLE_RELOCATE_VALIDATION");
88   if (disable_checks && (strcmp(disable_checks, "yes") == 0))
89     {
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);
95     }
96
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. */
100   if (! url_uuid)
101     {
102       apr_pool_t *sesspool = svn_pool_create(pool);
103
104       url_uuid = &APR_ARRAY_PUSH(uuids, struct url_uuid_t);
105       SVN_ERR(svn_client_get_repos_root(&url_uuid->root,
106                                         &url_uuid->uuid,
107                                         url, b->ctx,
108                                         pool, sesspool));
109
110       svn_pool_destroy(sesspool);
111     }
112
113   /* Make sure the url is a repository root if desired. */
114   if (root_url
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"),
118                              url);
119
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);
126
127   return SVN_NO_ERROR;
128 }
129
130
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.  */
137 static svn_error_t *
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)
144 {
145   apr_pool_t *iterpool;
146   int i;
147
148   /* Parse an externals definition into an array of external items. */
149
150   iterpool = svn_pool_create(scratch_pool);
151
152   for (i = 0; i < ext_desc->nelts; i++)
153     {
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;
158       svn_error_t *err;
159
160       svn_pool_clear(iterpool);
161
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
165          location. */
166       if (! ((strncmp("../", ext_item->url, 3) == 0) ||
167              (strncmp("^/", ext_item->url, 2) == 0)))
168         continue;
169
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,
181                                                       iterpool),
182                                       iterpool));
183       err = svn_client_get_repos_root(&target_repos_root_url, NULL /* uuid */,
184                                       target_abspath, ctx, iterpool, iterpool);
185
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)
190         {
191           svn_error_clear(err);
192           continue;
193         }
194       else
195         SVN_ERR(err);
196
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));
202     }
203
204   svn_pool_destroy(iterpool);
205
206   return SVN_NO_ERROR;
207 }
208
209 svn_error_t *
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,
215                      apr_pool_t *pool)
216 {
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;
223
224   /* Populate our validator callback baton, and call the relocate code. */
225   vb.ctx = ctx;
226   vb.path = wcroot_dir;
227   vb.url_uuids = apr_array_make(pool, 1, sizeof(struct url_uuid_t));
228   vb.pool = pool;
229
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"),
233                              wcroot_dir);
234
235   SVN_ERR(svn_dirent_get_absolute(&local_abspath, wcroot_dir, pool));
236
237   /* If we're ignoring externals, just relocate and get outta here. */
238   if (ignore_externals)
239     {
240       return svn_error_trace(svn_wc_relocate4(ctx->wc_ctx, local_abspath,
241                                               from_prefix, to_prefix,
242                                               validator_func, &vb, pool));
243     }
244
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));
248
249   /* Perform the relocation. */
250   SVN_ERR(svn_wc_relocate4(ctx->wc_ctx, local_abspath, from_prefix, to_prefix,
251                            validator_func, &vb, pool));
252
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));
256
257
258   /* Relocate externals, too (if any). */
259   SVN_ERR(svn_wc__externals_gather_definitions(&externals_hash, NULL,
260                                                ctx->wc_ctx, local_abspath,
261                                                svn_depth_infinity,
262                                                pool, pool));
263   if (! apr_hash_count(externals_hash))
264     return SVN_NO_ERROR;
265
266   iterpool = svn_pool_create(pool);
267
268   for (hi = apr_hash_first(pool, externals_hash);
269        hi != NULL;
270        hi = apr_hash_next(hi))
271     {
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;
275
276       svn_pool_clear(iterpool);
277
278       SVN_ERR(svn_wc_parse_externals_description3(&ext_desc, this_abspath,
279                                                   value, FALSE,
280                                                   iterpool));
281       if (ext_desc->nelts)
282         SVN_ERR(relocate_externals(this_abspath, ext_desc, old_repos_root_url,
283                                    new_repos_root_url, ctx, iterpool));
284     }
285
286   svn_pool_destroy(iterpool);
287
288   return SVN_NO_ERROR;
289 }