1251881Speter/* 2251881Speter * upgrade.c: wrapper around wc upgrade functionality. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* ==================================================================== */ 25251881Speter 26251881Speter 27251881Speter 28251881Speter/*** Includes. ***/ 29251881Speter 30251881Speter#include "svn_time.h" 31251881Speter#include "svn_wc.h" 32251881Speter#include "svn_client.h" 33251881Speter#include "svn_config.h" 34251881Speter#include "svn_dirent_uri.h" 35251881Speter#include "svn_path.h" 36251881Speter#include "svn_pools.h" 37251881Speter#include "client.h" 38251881Speter#include "svn_props.h" 39251881Speter 40251881Speter#include "svn_private_config.h" 41251881Speter#include "private/svn_wc_private.h" 42251881Speter 43251881Speter 44251881Speter/*** Code. ***/ 45251881Speter 46251881Speter/* callback baton for fetch_repos_info */ 47251881Speterstruct repos_info_baton 48251881Speter{ 49251881Speter apr_pool_t *state_pool; 50251881Speter svn_client_ctx_t *ctx; 51251881Speter const char *last_repos; 52251881Speter const char *last_uuid; 53251881Speter}; 54251881Speter 55251881Speter/* svn_wc_upgrade_get_repos_info_t implementation for calling 56251881Speter svn_wc_upgrade() from svn_client_upgrade() */ 57251881Speterstatic svn_error_t * 58251881Speterfetch_repos_info(const char **repos_root, 59251881Speter const char **repos_uuid, 60251881Speter void *baton, 61251881Speter const char *url, 62251881Speter apr_pool_t *result_pool, 63251881Speter apr_pool_t *scratch_pool) 64251881Speter{ 65251881Speter struct repos_info_baton *ri = baton; 66251881Speter 67251881Speter /* The same info is likely to retrieved multiple times (e.g. externals) */ 68251881Speter if (ri->last_repos && svn_uri__is_ancestor(ri->last_repos, url)) 69251881Speter { 70251881Speter *repos_root = apr_pstrdup(result_pool, ri->last_repos); 71251881Speter *repos_uuid = apr_pstrdup(result_pool, ri->last_uuid); 72251881Speter return SVN_NO_ERROR; 73251881Speter } 74251881Speter 75251881Speter SVN_ERR(svn_client_get_repos_root(repos_root, repos_uuid, url, ri->ctx, 76251881Speter result_pool, scratch_pool)); 77251881Speter 78251881Speter /* Store data for further calls */ 79251881Speter ri->last_repos = apr_pstrdup(ri->state_pool, *repos_root); 80251881Speter ri->last_uuid = apr_pstrdup(ri->state_pool, *repos_uuid); 81251881Speter 82251881Speter return SVN_NO_ERROR; 83251881Speter} 84251881Speter 85251881Spetersvn_error_t * 86251881Spetersvn_client_upgrade(const char *path, 87251881Speter svn_client_ctx_t *ctx, 88251881Speter apr_pool_t *scratch_pool) 89251881Speter{ 90251881Speter const char *local_abspath; 91251881Speter apr_hash_t *externals; 92251881Speter apr_hash_index_t *hi; 93251881Speter apr_pool_t *iterpool; 94251881Speter apr_pool_t *iterpool2; 95251881Speter svn_opt_revision_t rev = {svn_opt_revision_unspecified, {0}}; 96251881Speter struct repos_info_baton info_baton; 97251881Speter 98251881Speter info_baton.state_pool = scratch_pool; 99251881Speter info_baton.ctx = ctx; 100251881Speter info_baton.last_repos = NULL; 101251881Speter info_baton.last_uuid = NULL; 102251881Speter 103251881Speter if (svn_path_is_url(path)) 104251881Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 105251881Speter _("'%s' is not a local path"), path); 106251881Speter 107251881Speter SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); 108251881Speter SVN_ERR(svn_wc_upgrade(ctx->wc_ctx, local_abspath, 109251881Speter fetch_repos_info, &info_baton, 110251881Speter ctx->cancel_func, ctx->cancel_baton, 111251881Speter ctx->notify_func2, ctx->notify_baton2, 112251881Speter scratch_pool)); 113251881Speter 114251881Speter /* Now it's time to upgrade the externals too. We do it after the wc 115251881Speter upgrade to avoid that errors in the externals causes the wc upgrade to 116251881Speter fail. Thanks to caching the performance penalty of walking the wc a 117251881Speter second time shouldn't be too severe */ 118251881Speter SVN_ERR(svn_client_propget5(&externals, NULL, SVN_PROP_EXTERNALS, 119251881Speter local_abspath, &rev, &rev, NULL, 120251881Speter svn_depth_infinity, NULL, ctx, 121251881Speter scratch_pool, scratch_pool)); 122251881Speter 123251881Speter iterpool = svn_pool_create(scratch_pool); 124251881Speter iterpool2 = svn_pool_create(scratch_pool); 125251881Speter 126251881Speter for (hi = apr_hash_first(scratch_pool, externals); hi; 127251881Speter hi = apr_hash_next(hi)) 128251881Speter { 129251881Speter int i; 130251881Speter const char *externals_parent_abspath; 131251881Speter const char *externals_parent_url; 132251881Speter const char *externals_parent_repos_root_url; 133251881Speter const char *externals_parent_repos_relpath; 134251881Speter const char *externals_parent = svn__apr_hash_index_key(hi); 135251881Speter svn_string_t *external_desc = svn__apr_hash_index_val(hi); 136251881Speter apr_array_header_t *externals_p; 137251881Speter svn_error_t *err; 138251881Speter 139251881Speter svn_pool_clear(iterpool); 140251881Speter externals_p = apr_array_make(iterpool, 1, 141251881Speter sizeof(svn_wc_external_item2_t*)); 142251881Speter 143251881Speter /* In this loop, an error causes the respective externals definition, or 144251881Speter * the external (inner loop), to be skipped, so that upgrade carries on 145251881Speter * with the other externals. */ 146251881Speter 147251881Speter err = svn_dirent_get_absolute(&externals_parent_abspath, 148251881Speter externals_parent, iterpool); 149251881Speter 150251881Speter if (!err) 151251881Speter err = svn_wc__node_get_repos_info(NULL, 152251881Speter &externals_parent_repos_relpath, 153251881Speter &externals_parent_repos_root_url, 154251881Speter NULL, 155251881Speter ctx->wc_ctx, 156251881Speter externals_parent_abspath, 157251881Speter iterpool, iterpool); 158251881Speter 159251881Speter if (!err) 160251881Speter externals_parent_url = svn_path_url_add_component2( 161251881Speter externals_parent_repos_root_url, 162251881Speter externals_parent_repos_relpath, 163251881Speter iterpool); 164251881Speter if (!err) 165251881Speter err = svn_wc_parse_externals_description3( 166251881Speter &externals_p, svn_dirent_dirname(path, iterpool), 167251881Speter external_desc->data, FALSE, iterpool); 168251881Speter if (err) 169251881Speter { 170251881Speter svn_wc_notify_t *notify = 171251881Speter svn_wc_create_notify(externals_parent, 172251881Speter svn_wc_notify_failed_external, 173251881Speter scratch_pool); 174251881Speter notify->err = err; 175251881Speter 176251881Speter ctx->notify_func2(ctx->notify_baton2, 177251881Speter notify, scratch_pool); 178251881Speter 179251881Speter svn_error_clear(err); 180251881Speter 181251881Speter /* Next externals definition, please... */ 182251881Speter continue; 183251881Speter } 184251881Speter 185251881Speter for (i = 0; i < externals_p->nelts; i++) 186251881Speter { 187251881Speter svn_wc_external_item2_t *item; 188251881Speter const char *resolved_url; 189251881Speter const char *external_abspath; 190251881Speter const char *repos_relpath; 191251881Speter const char *repos_root_url; 192251881Speter const char *repos_uuid; 193251881Speter svn_node_kind_t external_kind; 194251881Speter svn_revnum_t peg_revision; 195251881Speter svn_revnum_t revision; 196251881Speter 197251881Speter item = APR_ARRAY_IDX(externals_p, i, svn_wc_external_item2_t*); 198251881Speter 199251881Speter svn_pool_clear(iterpool2); 200251881Speter external_abspath = svn_dirent_join(externals_parent_abspath, 201251881Speter item->target_dir, 202251881Speter iterpool2); 203251881Speter 204251881Speter err = svn_wc__resolve_relative_external_url( 205251881Speter &resolved_url, 206251881Speter item, 207251881Speter externals_parent_repos_root_url, 208251881Speter externals_parent_url, 209251881Speter scratch_pool, scratch_pool); 210251881Speter if (err) 211251881Speter goto handle_error; 212251881Speter 213251881Speter /* This is a hack. We only need to call svn_wc_upgrade() on external 214251881Speter * dirs, as file externals are upgraded along with their defining 215251881Speter * WC. Reading the kind will throw an exception on an external dir, 216251881Speter * saying that the wc must be upgraded. If it's a file, the lookup 217251881Speter * is done in an adm_dir belonging to the defining wc (which has 218251881Speter * already been upgraded) and no error is returned. If it doesn't 219251881Speter * exist (external that isn't checked out yet), we'll just get 220251881Speter * svn_node_none. */ 221251881Speter err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, 222251881Speter external_abspath, TRUE, FALSE, iterpool2); 223251881Speter if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) 224251881Speter { 225251881Speter svn_error_clear(err); 226251881Speter 227251881Speter err = svn_client_upgrade(external_abspath, ctx, iterpool2); 228251881Speter if (err) 229251881Speter goto handle_error; 230251881Speter } 231251881Speter else if (err) 232251881Speter goto handle_error; 233251881Speter 234251881Speter /* The upgrade of any dir should be done now, get the now reliable 235251881Speter * kind. */ 236251881Speter err = svn_wc_read_kind2(&external_kind, ctx->wc_ctx, external_abspath, 237251881Speter TRUE, FALSE, iterpool2); 238251881Speter if (err) 239251881Speter goto handle_error; 240251881Speter 241251881Speter /* Update the EXTERNALS table according to the root URL, 242251881Speter * relpath and uuid known in the upgraded external WC. */ 243251881Speter 244251881Speter /* We should probably have a function that provides all three 245251881Speter * of root URL, repos relpath and uuid at once, but here goes... */ 246251881Speter 247251881Speter /* First get the relpath, as that returns SVN_ERR_WC_PATH_NOT_FOUND 248251881Speter * when the node is not present in the file system. 249251881Speter * svn_wc__node_get_repos_info() would try to derive the URL. */ 250251881Speter err = svn_wc__node_get_repos_info(NULL, 251251881Speter &repos_relpath, 252251881Speter &repos_root_url, 253251881Speter &repos_uuid, 254251881Speter ctx->wc_ctx, 255251881Speter external_abspath, 256251881Speter iterpool2, iterpool2); 257251881Speter if (err) 258251881Speter goto handle_error; 259251881Speter 260251881Speter /* If we haven't got any information from the checked out external, 261251881Speter * or if the URL information mismatches the external's definition, 262251881Speter * ask fetch_repos_info() to find out the repos root. */ 263251881Speter if (0 != strcmp(resolved_url, 264251881Speter svn_path_url_add_component2(repos_root_url, 265251881Speter repos_relpath, 266251881Speter scratch_pool))) 267251881Speter { 268251881Speter err = fetch_repos_info(&repos_root_url, 269251881Speter &repos_uuid, 270251881Speter &info_baton, 271251881Speter resolved_url, 272251881Speter scratch_pool, scratch_pool); 273251881Speter if (err) 274251881Speter goto handle_error; 275251881Speter 276251881Speter repos_relpath = svn_uri_skip_ancestor(repos_root_url, 277251881Speter resolved_url, 278251881Speter iterpool2); 279251881Speter 280251881Speter /* There's just the URL, no idea what kind the external is. 281251881Speter * That's fine, as the external isn't even checked out yet. 282251881Speter * The kind will be set during the next 'update'. */ 283251881Speter external_kind = svn_node_unknown; 284251881Speter } 285251881Speter 286251881Speter if (err) 287251881Speter goto handle_error; 288251881Speter 289251881Speter peg_revision = (item->peg_revision.kind == svn_opt_revision_number 290251881Speter ? item->peg_revision.value.number 291251881Speter : SVN_INVALID_REVNUM); 292251881Speter 293251881Speter revision = (item->revision.kind == svn_opt_revision_number 294251881Speter ? item->revision.value.number 295251881Speter : SVN_INVALID_REVNUM); 296251881Speter 297251881Speter err = svn_wc__upgrade_add_external_info(ctx->wc_ctx, 298251881Speter external_abspath, 299251881Speter external_kind, 300251881Speter externals_parent, 301251881Speter repos_relpath, 302251881Speter repos_root_url, 303251881Speter repos_uuid, 304251881Speter peg_revision, 305251881Speter revision, 306251881Speter iterpool2); 307251881Speterhandle_error: 308251881Speter if (err) 309251881Speter { 310251881Speter svn_wc_notify_t *notify = 311251881Speter svn_wc_create_notify(external_abspath, 312251881Speter svn_wc_notify_failed_external, 313251881Speter scratch_pool); 314251881Speter notify->err = err; 315251881Speter ctx->notify_func2(ctx->notify_baton2, 316251881Speter notify, scratch_pool); 317251881Speter svn_error_clear(err); 318251881Speter /* Next external node, please... */ 319251881Speter } 320251881Speter } 321251881Speter } 322251881Speter 323251881Speter svn_pool_destroy(iterpool); 324251881Speter svn_pool_destroy(iterpool2); 325251881Speter 326251881Speter return SVN_NO_ERROR; 327251881Speter} 328