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));
183 upgrade_externals_from_properties(svn_client_ctx_t *ctx,
184 const char *local_abspath,
185 struct repos_info_baton *info_baton,
186 apr_pool_t *scratch_pool)
188 apr_hash_index_t *hi;
189 apr_pool_t *iterpool;
190 apr_pool_t *iterpool2;
191 apr_hash_t *externals;
192 svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}};
194 /* Now it's time to upgrade the externals too. We do it after the wc
195 upgrade to avoid that errors in the externals causes the wc upgrade to
196 fail. Thanks to caching the performance penalty of walking the wc a
197 second time shouldn't be too severe */
198 SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS,
199 local_abspath, &rev, &rev, NULL,
200 svn_depth_infinity, NULL, ctx,
201 scratch_pool, scratch_pool));
203 iterpool = svn_pool_create(scratch_pool);
204 iterpool2 = svn_pool_create(scratch_pool);
206 for (hi = apr_hash_first(scratch_pool, externals); hi;
207 hi = apr_hash_next(hi))
210 const char *externals_parent_abspath;
211 const char *externals_parent_url;
212 const char *externals_parent_repos_root_url;
213 const char *externals_parent_repos_relpath;
214 const char *externals_parent = apr_hash_this_key(hi);
215 svn_string_t *external_desc = apr_hash_this_val(hi);
216 apr_array_header_t *externals_p;
219 svn_pool_clear(iterpool);
220 externals_p = apr_array_make(iterpool, 1,
221 sizeof(svn_wc_external_item2_t*));
223 /* In this loop, an error causes the respective externals definition, or
224 * the external (inner loop), to be skipped, so that upgrade carries on
225 * with the other externals. */
227 err = svn_dirent_get_absolute(&externals_parent_abspath,
228 externals_parent, iterpool);
231 err = svn_wc__node_get_repos_info(NULL,
232 &externals_parent_repos_relpath,
233 &externals_parent_repos_root_url,
236 externals_parent_abspath,
240 externals_parent_url = svn_path_url_add_component2(
241 externals_parent_repos_root_url,
242 externals_parent_repos_relpath,
245 err = svn_wc_parse_externals_description3(
246 &externals_p, svn_dirent_dirname(local_abspath, iterpool),
247 external_desc->data, FALSE, iterpool);
250 svn_wc_notify_t *notify =
251 svn_wc_create_notify(externals_parent,
252 svn_wc_notify_failed_external,
256 ctx->notify_func2(ctx->notify_baton2,
257 notify, scratch_pool);
259 svn_error_clear(err);
261 /* Next externals definition, please... */
265 for (i = 0; i < externals_p->nelts; i++)
267 svn_wc_external_item2_t *item;
268 const char *resolved_url;
269 const char *external_abspath;
270 const char *repos_relpath;
271 const char *repos_root_url;
272 const char *repos_uuid;
273 svn_node_kind_t external_kind;
274 svn_revnum_t peg_revision;
275 svn_revnum_t revision;
277 item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*);
279 svn_pool_clear(iterpool2);
280 external_abspath = svn_dirent_join(externals_parent_abspath,
284 err = svn_wc__resolve_relative_external_url(
287 externals_parent_repos_root_url,
288 externals_parent_url,
289 scratch_pool, scratch_pool);
293 /* This is a hack. We only need to call svn_wc_upgrade() on external
294 * dirs, as file externals are upgraded along with their defining
295 * WC. Reading the kind will throw an exception on an external dir,
296 * saying that the wc must be upgraded. If it's a file, the lookup
297 * is done in an adm_dir belonging to the defining wc (which has
298 * already been upgraded) and no error is returned. If it doesn't
299 * exist (external that isn't checked out yet), we'll just get
301 err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx,
302 external_abspath, TRUE, FALSE, iterpool2);
303 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
305 svn_error_clear(err);
307 err = svn_client_upgrade(external_abspath, ctx, iterpool2);
314 /* The upgrade of any dir should be done now, get the now reliable
316 err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath,
317 TRUE, FALSE, iterpool2);
321 /* Update the EXTERNALS table according to the root URL,
322 * relpath and uuid known in the upgraded external WC. */
324 /* We should probably have a function that provides all three
325 * of root URL, repos relpath and uuid at once, but here goes... */
327 /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND
328 * when the node is not present in the file system.
329 * svn_wc__node_get_repos_info() would try to derive the URL. */
330 err = svn_wc__node_get_repos_info(NULL,
336 iterpool2, iterpool2);
340 /* If we haven't got any information from the checked out external,
341 * or if the URL information mismatches the external's definition,
342 * ask fetch_repos_info() to find out the repos root. */
343 if (0 != strcmp(resolved_url,
344 svn_path_url_add_component2(repos_root_url,
348 err = fetch_repos_info(&repos_root_url,
352 scratch_pool, scratch_pool);
356 repos_relpath = svn_uri_skip_ancestor(repos_root_url,
360 /* There's just the URL, no idea what kind the external is.
361 * That's fine, as the external isn't even checked out yet.
362 * The kind will be set during the next 'update'. */
363 external_kind = svn_node_unknown;
369 peg_revision = (item->peg_revision.kind == svn_opt_revision_number
370 ? item->peg_revision.value.number
371 : SVN_INVALID_REVNUM);
373 revision = (item->revision.kind == svn_opt_revision_number
374 ? item->revision.value.number
375 : SVN_INVALID_REVNUM);
377 err = svn_wc__upgrade_add_external_info(ctx->wc_ctx,
390 svn_wc_notify_t *notify =
391 svn_wc_create_notify(external_abspath,
392 svn_wc_notify_failed_external,
395 ctx->notify_func2(ctx->notify_baton2,
396 notify, scratch_pool);
397 svn_error_clear(err);
398 /* Next external node, please... */
403 svn_pool_destroy(iterpool);
404 svn_pool_destroy(iterpool2);