2 * upgrade.c: wrapper around wc upgrade 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 /* ==================================================================== */
32 #include "svn_client.h"
33 #include "svn_config.h"
34 #include "svn_dirent_uri.h"
36 #include "svn_pools.h"
38 #include "svn_props.h"
40 #include "svn_private_config.h"
41 #include "private/svn_wc_private.h"
46 /* callback baton for fetch_repos_info */
47 struct repos_info_baton
49 apr_pool_t *state_pool;
50 svn_client_ctx_t *ctx;
51 const char *last_repos;
52 const char *last_uuid;
55 /* svn_wc_upgrade_get_repos_info_t implementation for calling
56 svn_wc_upgrade() from svn_client_upgrade() */
58 fetch_repos_info(const char **repos_root,
59 const char **repos_uuid,
62 apr_pool_t *result_pool,
63 apr_pool_t *scratch_pool)
65 struct repos_info_baton *ri = baton;
67 /* The same info is likely to retrieved multiple times (e.g. externals) */
68 if (ri->last_repos && svn_uri__is_ancestor(ri->last_repos, url))
70 *repos_root = apr_pstrdup(result_pool, ri->last_repos);
71 *repos_uuid = apr_pstrdup(result_pool, ri->last_uuid);
75 SVN_ERR(svn_client_get_repos_root(repos_root, repos_uuid, url, ri->ctx,
76 result_pool, scratch_pool));
78 /* Store data for further calls */
79 ri->last_repos = apr_pstrdup(ri->state_pool, *repos_root);
80 ri->last_uuid = apr_pstrdup(ri->state_pool, *repos_uuid);
85 /* Forward definition. Upgrades svn:externals properties in the working copy
86 LOCAL_ABSPATH to the WC-NG storage.
89 upgrade_externals_from_properties(svn_client_ctx_t *ctx,
90 const char *local_abspath,
91 apr_pool_t *scratch_pool);
94 svn_client_upgrade(const char *path,
95 svn_client_ctx_t *ctx,
96 apr_pool_t *scratch_pool)
98 const char *local_abspath;
99 apr_hash_t *externals;
100 struct repos_info_baton info_baton;
102 info_baton.state_pool = scratch_pool;
103 info_baton.ctx = ctx;
104 info_baton.last_repos = NULL;
105 info_baton.last_uuid = NULL;
107 if (svn_path_is_url(path))
108 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
109 _("'%s' is not a local path"), path);
111 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
112 SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath,
113 fetch_repos_info, &info_baton,
114 ctx->cancel_func, ctx->cancel_baton,
115 ctx->notify_func2, ctx->notify_baton2,
118 SVN_ERR(svn_wc__externals_defined_below(&externals,
119 ctx->wc_ctx, local_abspath,
120 scratch_pool, scratch_pool));
122 if (apr_hash_count(externals) > 0)
124 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
125 apr_hash_index_t *hi;
127 /* We are upgrading from >= 1.7. No need to upgrade from
128 svn:externals properties. And by that avoiding the removal
129 of recorded externals information (issue #4519)
131 Only directory externals need an explicit upgrade */
132 for (hi = apr_hash_first(scratch_pool, externals);
134 hi = apr_hash_next(hi))
136 const char *ext_abspath;
137 svn_node_kind_t kind;
139 svn_pool_clear(iterpool);
141 ext_abspath = svn__apr_hash_index_key(hi);
143 SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL,
144 ctx->wc_ctx, local_abspath,
146 iterpool, iterpool));
148 if (kind == svn_node_dir)
150 svn_error_t *err = svn_client_upgrade(ext_abspath, ctx, iterpool);
154 svn_wc_notify_t *notify =
155 svn_wc_create_notify(ext_abspath,
156 svn_wc_notify_failed_external,
159 ctx->notify_func2(ctx->notify_baton2,
161 svn_error_clear(err);
162 /* Next external node, please... */
167 svn_pool_destroy(iterpool);
171 /* Upgrading from <= 1.6, or no svn:properties defined.
172 (There is no way to detect the difference from libsvn_client :( ) */
174 SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath,
181 upgrade_externals_from_properties(svn_client_ctx_t *ctx,
182 const char *local_abspath,
183 apr_pool_t *scratch_pool)
185 apr_hash_index_t *hi;
186 apr_pool_t *iterpool;
187 apr_pool_t *iterpool2;
188 apr_hash_t *externals;
189 svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}};
190 struct repos_info_baton info_baton;
192 /* Now it's time to upgrade the externals too. We do it after the wc
193 upgrade to avoid that errors in the externals causes the wc upgrade to
194 fail. Thanks to caching the performance penalty of walking the wc a
195 second time shouldn't be too severe */
196 SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS,
197 local_abspath, &rev, &rev, NULL,
198 svn_depth_infinity, NULL, ctx,
199 scratch_pool, scratch_pool));
201 iterpool = svn_pool_create(scratch_pool);
202 iterpool2 = svn_pool_create(scratch_pool);
204 for (hi = apr_hash_first(scratch_pool, externals); hi;
205 hi = apr_hash_next(hi))
208 const char *externals_parent_abspath;
209 const char *externals_parent_url;
210 const char *externals_parent_repos_root_url;
211 const char *externals_parent_repos_relpath;
212 const char *externals_parent = svn__apr_hash_index_key(hi);
213 svn_string_t *external_desc = svn__apr_hash_index_val(hi);
214 apr_array_header_t *externals_p;
217 svn_pool_clear(iterpool);
218 externals_p = apr_array_make(iterpool, 1,
219 sizeof(svn_wc_external_item2_t*));
221 /* In this loop, an error causes the respective externals definition, or
222 * the external (inner loop), to be skipped, so that upgrade carries on
223 * with the other externals. */
225 err = svn_dirent_get_absolute(&externals_parent_abspath,
226 externals_parent, iterpool);
229 err = svn_wc__node_get_repos_info(NULL,
230 &externals_parent_repos_relpath,
231 &externals_parent_repos_root_url,
234 externals_parent_abspath,
238 externals_parent_url = svn_path_url_add_component2(
239 externals_parent_repos_root_url,
240 externals_parent_repos_relpath,
243 err = svn_wc_parse_externals_description3(
244 &externals_p, svn_dirent_dirname(local_abspath, iterpool),
245 external_desc->data, FALSE, iterpool);
248 svn_wc_notify_t *notify =
249 svn_wc_create_notify(externals_parent,
250 svn_wc_notify_failed_external,
254 ctx->notify_func2(ctx->notify_baton2,
255 notify, scratch_pool);
257 svn_error_clear(err);
259 /* Next externals definition, please... */
263 for (i = 0; i < externals_p->nelts; i++)
265 svn_wc_external_item2_t *item;
266 const char *resolved_url;
267 const char *external_abspath;
268 const char *repos_relpath;
269 const char *repos_root_url;
270 const char *repos_uuid;
271 svn_node_kind_t external_kind;
272 svn_revnum_t peg_revision;
273 svn_revnum_t revision;
275 item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*);
277 svn_pool_clear(iterpool2);
278 external_abspath = svn_dirent_join(externals_parent_abspath,
282 err = svn_wc__resolve_relative_external_url(
285 externals_parent_repos_root_url,
286 externals_parent_url,
287 scratch_pool, scratch_pool);
291 /* This is a hack. We only need to call svn_wc_upgrade() on external
292 * dirs, as file externals are upgraded along with their defining
293 * WC. Reading the kind will throw an exception on an external dir,
294 * saying that the wc must be upgraded. If it's a file, the lookup
295 * is done in an adm_dir belonging to the defining wc (which has
296 * already been upgraded) and no error is returned. If it doesn't
297 * exist (external that isn't checked out yet), we'll just get
299 err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx,
300 external_abspath, TRUE, FALSE, iterpool2);
301 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
303 svn_error_clear(err);
305 err = svn_client_upgrade(external_abspath, ctx, iterpool2);
312 /* The upgrade of any dir should be done now, get the now reliable
314 err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath,
315 TRUE, FALSE, iterpool2);
319 /* Update the EXTERNALS table according to the root URL,
320 * relpath and uuid known in the upgraded external WC. */
322 /* We should probably have a function that provides all three
323 * of root URL, repos relpath and uuid at once, but here goes... */
325 /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND
326 * when the node is not present in the file system.
327 * svn_wc__node_get_repos_info() would try to derive the URL. */
328 err = svn_wc__node_get_repos_info(NULL,
334 iterpool2, iterpool2);
338 /* If we haven't got any information from the checked out external,
339 * or if the URL information mismatches the external's definition,
340 * ask fetch_repos_info() to find out the repos root. */
341 if (0 != strcmp(resolved_url,
342 svn_path_url_add_component2(repos_root_url,
346 err = fetch_repos_info(&repos_root_url,
350 scratch_pool, scratch_pool);
354 repos_relpath = svn_uri_skip_ancestor(repos_root_url,
358 /* There's just the URL, no idea what kind the external is.
359 * That's fine, as the external isn't even checked out yet.
360 * The kind will be set during the next 'update'. */
361 external_kind = svn_node_unknown;
367 peg_revision = (item->peg_revision.kind == svn_opt_revision_number
368 ? item->peg_revision.value.number
369 : SVN_INVALID_REVNUM);
371 revision = (item->revision.kind == svn_opt_revision_number
372 ? item->revision.value.number
373 : SVN_INVALID_REVNUM);
375 err = svn_wc__upgrade_add_external_info(ctx->wc_ctx,
388 svn_wc_notify_t *notify =
389 svn_wc_create_notify(external_abspath,
390 svn_wc_notify_failed_external,
393 ctx->notify_func2(ctx->notify_baton2,
394 notify, scratch_pool);
395 svn_error_clear(err);
396 /* Next external node, please... */
401 svn_pool_destroy(iterpool);
402 svn_pool_destroy(iterpool2);