upgrade.c revision 289166
1/* 2 * upgrade.c: wrapper around wc upgrade 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 28/*** Includes. ***/ 29 30#include "svn_time.h" 31#include "svn_wc.h" 32#include "svn_client.h" 33#include "svn_config.h" 34#include "svn_dirent_uri.h" 35#include "svn_path.h" 36#include "svn_pools.h" 37#include "client.h" 38#include "svn_props.h" 39 40#include "svn_private_config.h" 41#include "private/svn_wc_private.h" 42 43 44/*** Code. ***/ 45 46/* callback baton for fetch_repos_info */ 47struct repos_info_baton 48{ 49 apr_pool_t *state_pool; 50 svn_client_ctx_t *ctx; 51 const char *last_repos; 52 const char *last_uuid; 53}; 54 55/* svn_wc_upgrade_get_repos_info_t implementation for calling 56 svn_wc_upgrade() from svn_client_upgrade() */ 57static svn_error_t * 58fetch_repos_info(const char **repos_root, 59 const char **repos_uuid, 60 void *baton, 61 const char *url, 62 apr_pool_t *result_pool, 63 apr_pool_t *scratch_pool) 64{ 65 struct repos_info_baton *ri = baton; 66 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)) 69 { 70 *repos_root = apr_pstrdup(result_pool, ri->last_repos); 71 *repos_uuid = apr_pstrdup(result_pool, ri->last_uuid); 72 return SVN_NO_ERROR; 73 } 74 75 SVN_ERR(svn_client_get_repos_root(repos_root, repos_uuid, url, ri->ctx, 76 result_pool, scratch_pool)); 77 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); 81 82 return SVN_NO_ERROR; 83} 84 85/* Forward definition. Upgrades svn:externals properties in the working copy 86 LOCAL_ABSPATH to the WC-NG storage. 87 */ 88static svn_error_t * 89upgrade_externals_from_properties(svn_client_ctx_t *ctx, 90 const char *local_abspath, 91 apr_pool_t *scratch_pool); 92 93svn_error_t * 94svn_client_upgrade(const char *path, 95 svn_client_ctx_t *ctx, 96 apr_pool_t *scratch_pool) 97{ 98 const char *local_abspath; 99 apr_hash_t *externals; 100 struct repos_info_baton info_baton; 101 102 info_baton.state_pool = scratch_pool; 103 info_baton.ctx = ctx; 104 info_baton.last_repos = NULL; 105 info_baton.last_uuid = NULL; 106 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); 110 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, 116 scratch_pool)); 117 118 SVN_ERR(svn_wc__externals_defined_below(&externals, 119 ctx->wc_ctx, local_abspath, 120 scratch_pool, scratch_pool)); 121 122 if (apr_hash_count(externals) > 0) 123 { 124 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 125 apr_hash_index_t *hi; 126 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) 130 131 Only directory externals need an explicit upgrade */ 132 for (hi = apr_hash_first(scratch_pool, externals); 133 hi; 134 hi = apr_hash_next(hi)) 135 { 136 const char *ext_abspath; 137 svn_node_kind_t kind; 138 139 svn_pool_clear(iterpool); 140 141 ext_abspath = svn__apr_hash_index_key(hi); 142 143 SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL, 144 ctx->wc_ctx, local_abspath, 145 ext_abspath, FALSE, 146 iterpool, iterpool)); 147 148 if (kind == svn_node_dir) 149 { 150 svn_error_t *err = svn_client_upgrade(ext_abspath, ctx, iterpool); 151 152 if (err) 153 { 154 svn_wc_notify_t *notify = 155 svn_wc_create_notify(ext_abspath, 156 svn_wc_notify_failed_external, 157 iterpool); 158 notify->err = err; 159 ctx->notify_func2(ctx->notify_baton2, 160 notify, iterpool); 161 svn_error_clear(err); 162 /* Next external node, please... */ 163 } 164 } 165 } 166 167 svn_pool_destroy(iterpool); 168 } 169 else 170 { 171 /* Upgrading from <= 1.6, or no svn:properties defined. 172 (There is no way to detect the difference from libsvn_client :( ) */ 173 174 SVN_ERR(upgrade_externals_from_properties(ctx, local_abspath, 175 scratch_pool)); 176 } 177 return SVN_NO_ERROR; 178} 179 180static svn_error_t * 181upgrade_externals_from_properties(svn_client_ctx_t *ctx, 182 const char *local_abspath, 183 apr_pool_t *scratch_pool) 184{ 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; 191 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)); 200 201 iterpool = svn_pool_create(scratch_pool); 202 iterpool2 = svn_pool_create(scratch_pool); 203 204 for (hi = apr_hash_first(scratch_pool, externals); hi; 205 hi = apr_hash_next(hi)) 206 { 207 int i; 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; 215 svn_error_t *err; 216 217 svn_pool_clear(iterpool); 218 externals_p = apr_array_make(iterpool, 1, 219 sizeof(svn_wc_external_item2_t*)); 220 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. */ 224 225 err = svn_dirent_get_absolute(&externals_parent_abspath, 226 externals_parent, iterpool); 227 228 if (!err) 229 err = svn_wc__node_get_repos_info(NULL, 230 &externals_parent_repos_relpath, 231 &externals_parent_repos_root_url, 232 NULL, 233 ctx->wc_ctx, 234 externals_parent_abspath, 235 iterpool, iterpool); 236 237 if (!err) 238 externals_parent_url = svn_path_url_add_component2( 239 externals_parent_repos_root_url, 240 externals_parent_repos_relpath, 241 iterpool); 242 if (!err) 243 err = svn_wc_parse_externals_description3( 244 &externals_p, svn_dirent_dirname(local_abspath, iterpool), 245 external_desc->data, FALSE, iterpool); 246 if (err) 247 { 248 svn_wc_notify_t *notify = 249 svn_wc_create_notify(externals_parent, 250 svn_wc_notify_failed_external, 251 scratch_pool); 252 notify->err = err; 253 254 ctx->notify_func2(ctx->notify_baton2, 255 notify, scratch_pool); 256 257 svn_error_clear(err); 258 259 /* Next externals definition, please... */ 260 continue; 261 } 262 263 for (i = 0; i < externals_p->nelts; i++) 264 { 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; 274 275 item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*); 276 277 svn_pool_clear(iterpool2); 278 external_abspath = svn_dirent_join(externals_parent_abspath, 279 item->target_dir, 280 iterpool2); 281 282 err = svn_wc__resolve_relative_external_url( 283 &resolved_url, 284 item, 285 externals_parent_repos_root_url, 286 externals_parent_url, 287 scratch_pool, scratch_pool); 288 if (err) 289 goto handle_error; 290 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 298 * svn_node_none. */ 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) 302 { 303 svn_error_clear(err); 304 305 err = svn_client_upgrade(external_abspath, ctx, iterpool2); 306 if (err) 307 goto handle_error; 308 } 309 else if (err) 310 goto handle_error; 311 312 /* The upgrade of any dir should be done now, get the now reliable 313 * kind. */ 314 err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath, 315 TRUE, FALSE, iterpool2); 316 if (err) 317 goto handle_error; 318 319 /* Update the EXTERNALS table according to the root URL, 320 * relpath and uuid known in the upgraded external WC. */ 321 322 /* We should probably have a function that provides all three 323 * of root URL, repos relpath and uuid at once, but here goes... */ 324 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, 329 &repos_relpath, 330 &repos_root_url, 331 &repos_uuid, 332 ctx->wc_ctx, 333 external_abspath, 334 iterpool2, iterpool2); 335 if (err) 336 goto handle_error; 337 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, 343 repos_relpath, 344 scratch_pool))) 345 { 346 err = fetch_repos_info(&repos_root_url, 347 &repos_uuid, 348 &info_baton, 349 resolved_url, 350 scratch_pool, scratch_pool); 351 if (err) 352 goto handle_error; 353 354 repos_relpath = svn_uri_skip_ancestor(repos_root_url, 355 resolved_url, 356 iterpool2); 357 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; 362 } 363 364 if (err) 365 goto handle_error; 366 367 peg_revision = (item->peg_revision.kind == svn_opt_revision_number 368 ? item->peg_revision.value.number 369 : SVN_INVALID_REVNUM); 370 371 revision = (item->revision.kind == svn_opt_revision_number 372 ? item->revision.value.number 373 : SVN_INVALID_REVNUM); 374 375 err = svn_wc__upgrade_add_external_info(ctx->wc_ctx, 376 external_abspath, 377 external_kind, 378 externals_parent, 379 repos_relpath, 380 repos_root_url, 381 repos_uuid, 382 peg_revision, 383 revision, 384 iterpool2); 385handle_error: 386 if (err) 387 { 388 svn_wc_notify_t *notify = 389 svn_wc_create_notify(external_abspath, 390 svn_wc_notify_failed_external, 391 scratch_pool); 392 notify->err = err; 393 ctx->notify_func2(ctx->notify_baton2, 394 notify, scratch_pool); 395 svn_error_clear(err); 396 /* Next external node, please... */ 397 } 398 } 399 } 400 401 svn_pool_destroy(iterpool); 402 svn_pool_destroy(iterpool2); 403 404 return SVN_NO_ERROR; 405} 406