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. INFO_BATON will be used to fetch
87 repository info using fetch_repos_info() function if needed.
90 upgrade_externals_from_properties(svn_client_ctx_t *ctx,
91 const char *local_abspath,
92 struct repos_info_baton *info_baton,
93 apr_pool_t *scratch_pool);
96 svn_client_upgrade(const char *path,
97 svn_client_ctx_t *ctx,
98 apr_pool_t *scratch_pool)
100 const char *local_abspath;
101 apr_hash_t *externals;
102 struct repos_info_baton info_baton;
104 info_baton.state_pool = scratch_pool;
105 info_baton.ctx = ctx;
106 info_baton.last_repos = NULL;
107 info_baton.last_uuid = NULL;
109 if (svn_path_is_url(path))
110 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
111 _("'%s' is not a local path"), path);
113 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
114 SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath,
115 fetch_repos_info, &info_baton,
116 ctx->cancel_func, ctx->cancel_baton,
117 ctx->notify_func2, ctx->notify_baton2,
120 SVN_ERR(svn_wc__externals_defined_below(&externals,
121 ctx->wc_ctx, local_abspath,
122 scratch_pool, scratch_pool));
124 if (apr_hash_count(externals) > 0)
126 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
127 apr_hash_index_t *hi;
129 /* We are upgrading from >= 1.7. No need to upgrade from
130 svn:externals properties. And by that avoiding the removal
131 of recorded externals information (issue #4519)
133 Only directory externals need an explicit upgrade */
134 for (hi = apr_hash_first(scratch_pool, externals);
136 hi = apr_hash_next(hi))
138 const char *ext_abspath;
139 svn_node_kind_t kind;
141 svn_pool_clear(iterpool);
143 ext_abspath = apr_hash_this_key(hi);
145 SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL,
146 ctx->wc_ctx, local_abspath,
148 iterpool, iterpool));
150 if (kind == svn_node_dir)
152 svn_error_t *err = svn_client_upgrade(ext_abspath, ctx, iterpool);
156 svn_wc_notify_t *notify =
157 svn_wc_create_notify(ext_abspath,
158 svn_wc_notify_failed_external,
161 ctx->notify_func2(ctx->notify_baton2,
163 svn_error_clear(err);
164 /* Next external node, please... */
169 svn_pool_destroy(iterpool);
173 /* Upgrading from <= 1.6, or no svn:properties defined.
174 (There is no way to detect the difference from libsvn_client :( ) */
176 SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath,
177 &info_baton, scratch_pool));
182 /* Helper for upgrade_externals_from_properties: upgrades one external ITEM
183 in EXTERNALS_PARENT. Uses SCRATCH_POOL for temporary allocations. */
185 upgrade_external_item(svn_client_ctx_t *ctx,
186 const char *externals_parent_abspath,
187 const char *externals_parent_url,
188 const char *externals_parent_repos_root_url,
189 svn_wc_external_item2_t *item,
190 struct repos_info_baton *info_baton,
191 apr_pool_t *scratch_pool)
193 const char *resolved_url;
194 const char *external_abspath;
195 const char *repos_relpath;
196 const char *repos_root_url;
197 const char *repos_uuid;
198 svn_node_kind_t external_kind;
199 svn_revnum_t peg_revision;
200 svn_revnum_t revision;
203 external_abspath = svn_dirent_join(externals_parent_abspath,
207 SVN_ERR(svn_wc__resolve_relative_external_url(
210 externals_parent_repos_root_url,
211 externals_parent_url,
212 scratch_pool, scratch_pool));
214 /* This is a hack. We only need to call svn_wc_upgrade() on external
215 * dirs, as file externals are upgraded along with their defining
216 * WC. Reading the kind will throw an exception on an external dir,
217 * saying that the wc must be upgraded. If it's a file, the lookup
218 * is done in an adm_dir belonging to the defining wc (which has
219 * already been upgraded) and no error is returned. If it doesn't
220 * exist (external that isn't checked out yet), we'll just get
222 err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx,
223 external_abspath, TRUE, FALSE, scratch_pool);
224 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
226 svn_error_clear(err);
228 SVN_ERR(svn_client_upgrade(external_abspath, ctx, scratch_pool));
231 return svn_error_trace(err);
233 /* The upgrade of any dir should be done now, get the now reliable
235 SVN_ERR(svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath,
236 TRUE, FALSE, scratch_pool));
238 /* Update the EXTERNALS table according to the root URL,
239 * relpath and uuid known in the upgraded external WC. */
241 /* We should probably have a function that provides all three
242 * of root URL, repos relpath and uuid at once, but here goes... */
244 /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND
245 * when the node is not present in the file system.
246 * svn_wc__node_get_repos_info() would try to derive the URL. */
247 SVN_ERR(svn_wc__node_get_repos_info(NULL,
253 scratch_pool, scratch_pool));
255 /* If we haven't got any information from the checked out external,
256 * or if the URL information mismatches the external's definition,
257 * ask fetch_repos_info() to find out the repos root. */
258 if (0 != strcmp(resolved_url,
259 svn_path_url_add_component2(repos_root_url,
263 SVN_ERR(fetch_repos_info(&repos_root_url, &repos_uuid, info_baton,
264 resolved_url, scratch_pool, scratch_pool));
266 repos_relpath = svn_uri_skip_ancestor(repos_root_url,
270 /* There's just the URL, no idea what kind the external is.
271 * That's fine, as the external isn't even checked out yet.
272 * The kind will be set during the next 'update'. */
273 external_kind = svn_node_unknown;
276 peg_revision = (item->peg_revision.kind == svn_opt_revision_number
277 ? item->peg_revision.value.number
278 : SVN_INVALID_REVNUM);
280 revision = (item->revision.kind == svn_opt_revision_number
281 ? item->revision.value.number
282 : SVN_INVALID_REVNUM);
284 SVN_ERR(svn_wc__upgrade_add_external_info(ctx->wc_ctx,
287 externals_parent_abspath,
299 upgrade_externals_from_properties(svn_client_ctx_t *ctx,
300 const char *local_abspath,
301 struct repos_info_baton *info_baton,
302 apr_pool_t *scratch_pool)
304 apr_hash_index_t *hi;
305 apr_pool_t *iterpool;
306 apr_pool_t *iterpool2;
307 apr_hash_t *externals;
308 svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}};
310 /* Now it's time to upgrade the externals too. We do it after the wc
311 upgrade to avoid that errors in the externals causes the wc upgrade to
312 fail. Thanks to caching the performance penalty of walking the wc a
313 second time shouldn't be too severe */
314 SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS,
315 local_abspath, &rev, &rev, NULL,
316 svn_depth_infinity, NULL, ctx,
317 scratch_pool, scratch_pool));
319 iterpool = svn_pool_create(scratch_pool);
320 iterpool2 = svn_pool_create(scratch_pool);
322 for (hi = apr_hash_first(scratch_pool, externals); hi;
323 hi = apr_hash_next(hi))
326 const char *externals_parent_url;
327 const char *externals_parent_repos_root_url;
328 const char *externals_parent_repos_relpath;
329 const char *externals_parent_abspath = apr_hash_this_key(hi);
330 svn_string_t *external_desc = apr_hash_this_val(hi);
331 apr_array_header_t *externals_p;
334 svn_pool_clear(iterpool);
336 /* svn_client_propget5() has API promise to return absolute paths. */
337 SVN_ERR_ASSERT(svn_dirent_is_absolute(externals_parent_abspath));
339 externals_p = apr_array_make(iterpool, 1,
340 sizeof(svn_wc_external_item2_t*));
342 /* In this loop, an error causes the respective externals definition, or
343 * the external (inner loop), to be skipped, so that upgrade carries on
344 * with the other externals. */
345 err = svn_wc__node_get_repos_info(NULL,
346 &externals_parent_repos_relpath,
347 &externals_parent_repos_root_url,
350 externals_parent_abspath,
354 externals_parent_url = svn_path_url_add_component2(
355 externals_parent_repos_root_url,
356 externals_parent_repos_relpath,
359 err = svn_wc_parse_externals_description3(
360 &externals_p, svn_dirent_dirname(local_abspath, iterpool),
361 external_desc->data, FALSE, iterpool);
364 svn_wc_notify_t *notify =
365 svn_wc_create_notify(externals_parent_abspath,
366 svn_wc_notify_failed_external,
370 ctx->notify_func2(ctx->notify_baton2,
371 notify, scratch_pool);
373 svn_error_clear(err);
375 /* Next externals definition, please... */
379 for (i = 0; i < externals_p->nelts; i++)
381 svn_wc_external_item2_t *item;
383 item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*);
385 svn_pool_clear(iterpool2);
386 err = upgrade_external_item(ctx, externals_parent_abspath,
387 externals_parent_url,
388 externals_parent_repos_root_url,
389 item, info_baton, iterpool2);
393 svn_wc_notify_t *notify =
394 svn_wc_create_notify(svn_dirent_join(externals_parent_abspath,
397 svn_wc_notify_failed_external,
400 ctx->notify_func2(ctx->notify_baton2,
401 notify, scratch_pool);
402 svn_error_clear(err);
403 /* Next external node, please... */
408 svn_pool_destroy(iterpool);
409 svn_pool_destroy(iterpool2);