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);
86 svn_client_upgrade(const char *path,
87 svn_client_ctx_t *ctx,
88 apr_pool_t *scratch_pool)
90 const char *local_abspath;
91 apr_hash_t *externals;
94 apr_pool_t *iterpool2;
95 svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}};
96 struct repos_info_baton info_baton;
98 info_baton.state_pool = scratch_pool;
100 info_baton.last_repos = NULL;
101 info_baton.last_uuid = NULL;
103 if (svn_path_is_url(path))
104 return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
105 _("'%s' is not a local path"), path);
107 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
108 SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath,
109 fetch_repos_info, &info_baton,
110 ctx->cancel_func, ctx->cancel_baton,
111 ctx->notify_func2, ctx->notify_baton2,
114 /* Now it's time to upgrade the externals too. We do it after the wc
115 upgrade to avoid that errors in the externals causes the wc upgrade to
116 fail. Thanks to caching the performance penalty of walking the wc a
117 second time shouldn't be too severe */
118 SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS,
119 local_abspath, &rev, &rev, NULL,
120 svn_depth_infinity, NULL, ctx,
121 scratch_pool, scratch_pool));
123 iterpool = svn_pool_create(scratch_pool);
124 iterpool2 = svn_pool_create(scratch_pool);
126 for (hi = apr_hash_first(scratch_pool, externals); hi;
127 hi = apr_hash_next(hi))
130 const char *externals_parent_abspath;
131 const char *externals_parent_url;
132 const char *externals_parent_repos_root_url;
133 const char *externals_parent_repos_relpath;
134 const char *externals_parent = svn__apr_hash_index_key(hi);
135 svn_string_t *external_desc = svn__apr_hash_index_val(hi);
136 apr_array_header_t *externals_p;
139 svn_pool_clear(iterpool);
140 externals_p = apr_array_make(iterpool, 1,
141 sizeof(svn_wc_external_item2_t*));
143 /* In this loop, an error causes the respective externals definition, or
144 * the external (inner loop), to be skipped, so that upgrade carries on
145 * with the other externals. */
147 err = svn_dirent_get_absolute(&externals_parent_abspath,
148 externals_parent, iterpool);
151 err = svn_wc__node_get_repos_info(NULL,
152 &externals_parent_repos_relpath,
153 &externals_parent_repos_root_url,
156 externals_parent_abspath,
160 externals_parent_url = svn_path_url_add_component2(
161 externals_parent_repos_root_url,
162 externals_parent_repos_relpath,
165 err = svn_wc_parse_externals_description3(
166 &externals_p, svn_dirent_dirname(path, iterpool),
167 external_desc->data, FALSE, iterpool);
170 svn_wc_notify_t *notify =
171 svn_wc_create_notify(externals_parent,
172 svn_wc_notify_failed_external,
176 ctx->notify_func2(ctx->notify_baton2,
177 notify, scratch_pool);
179 svn_error_clear(err);
181 /* Next externals definition, please... */
185 for (i = 0; i < externals_p->nelts; i++)
187 svn_wc_external_item2_t *item;
188 const char *resolved_url;
189 const char *external_abspath;
190 const char *repos_relpath;
191 const char *repos_root_url;
192 const char *repos_uuid;
193 svn_node_kind_t external_kind;
194 svn_revnum_t peg_revision;
195 svn_revnum_t revision;
197 item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*);
199 svn_pool_clear(iterpool2);
200 external_abspath = svn_dirent_join(externals_parent_abspath,
204 err = svn_wc__resolve_relative_external_url(
207 externals_parent_repos_root_url,
208 externals_parent_url,
209 scratch_pool, scratch_pool);
213 /* This is a hack. We only need to call svn_wc_upgrade() on external
214 * dirs, as file externals are upgraded along with their defining
215 * WC. Reading the kind will throw an exception on an external dir,
216 * saying that the wc must be upgraded. If it's a file, the lookup
217 * is done in an adm_dir belonging to the defining wc (which has
218 * already been upgraded) and no error is returned. If it doesn't
219 * exist (external that isn't checked out yet), we'll just get
221 err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx,
222 external_abspath, TRUE, FALSE, iterpool2);
223 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
225 svn_error_clear(err);
227 err = svn_client_upgrade(external_abspath, ctx, iterpool2);
234 /* The upgrade of any dir should be done now, get the now reliable
236 err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath,
237 TRUE, FALSE, iterpool2);
241 /* Update the EXTERNALS table according to the root URL,
242 * relpath and uuid known in the upgraded external WC. */
244 /* We should probably have a function that provides all three
245 * of root URL, repos relpath and uuid at once, but here goes... */
247 /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND
248 * when the node is not present in the file system.
249 * svn_wc__node_get_repos_info() would try to derive the URL. */
250 err = svn_wc__node_get_repos_info(NULL,
256 iterpool2, iterpool2);
260 /* If we haven't got any information from the checked out external,
261 * or if the URL information mismatches the external's definition,
262 * ask fetch_repos_info() to find out the repos root. */
263 if (0 != strcmp(resolved_url,
264 svn_path_url_add_component2(repos_root_url,
268 err = fetch_repos_info(&repos_root_url,
272 scratch_pool, scratch_pool);
276 repos_relpath = svn_uri_skip_ancestor(repos_root_url,
280 /* There's just the URL, no idea what kind the external is.
281 * That's fine, as the external isn't even checked out yet.
282 * The kind will be set during the next 'update'. */
283 external_kind = svn_node_unknown;
289 peg_revision = (item->peg_revision.kind == svn_opt_revision_number
290 ? item->peg_revision.value.number
291 : SVN_INVALID_REVNUM);
293 revision = (item->revision.kind == svn_opt_revision_number
294 ? item->revision.value.number
295 : SVN_INVALID_REVNUM);
297 err = svn_wc__upgrade_add_external_info(ctx->wc_ctx,
310 svn_wc_notify_t *notify =
311 svn_wc_create_notify(external_abspath,
312 svn_wc_notify_failed_external,
315 ctx->notify_func2(ctx->notify_baton2,
316 notify, scratch_pool);
317 svn_error_clear(err);
318 /* Next external node, please... */
323 svn_pool_destroy(iterpool);
324 svn_pool_destroy(iterpool2);