1251881Speter/* 2251881Speter * update.c: wrappers around wc update 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_hash.h" 31251881Speter#include "svn_wc.h" 32251881Speter#include "svn_client.h" 33251881Speter#include "svn_error.h" 34251881Speter#include "svn_config.h" 35251881Speter#include "svn_time.h" 36251881Speter#include "svn_dirent_uri.h" 37251881Speter#include "svn_path.h" 38251881Speter#include "svn_pools.h" 39251881Speter#include "svn_io.h" 40251881Speter#include "client.h" 41251881Speter 42251881Speter#include "svn_private_config.h" 43251881Speter#include "private/svn_wc_private.h" 44251881Speter 45251881Speter/* Implements svn_wc_dirents_func_t for update and switch handling. Assumes 46251881Speter a struct svn_client__dirent_fetcher_baton_t * baton */ 47251881Spetersvn_error_t * 48251881Spetersvn_client__dirent_fetcher(void *baton, 49251881Speter apr_hash_t **dirents, 50251881Speter const char *repos_root_url, 51251881Speter const char *repos_relpath, 52251881Speter apr_pool_t *result_pool, 53251881Speter apr_pool_t *scratch_pool) 54251881Speter{ 55251881Speter struct svn_client__dirent_fetcher_baton_t *dfb = baton; 56251881Speter const char *old_url = NULL; 57251881Speter const char *session_relpath; 58251881Speter svn_node_kind_t kind; 59251881Speter const char *url; 60251881Speter 61251881Speter url = svn_path_url_add_component2(repos_root_url, repos_relpath, 62251881Speter scratch_pool); 63251881Speter 64251881Speter if (!svn_uri__is_ancestor(dfb->anchor_url, url)) 65251881Speter { 66251881Speter SVN_ERR(svn_client__ensure_ra_session_url(&old_url, dfb->ra_session, 67251881Speter url, scratch_pool)); 68251881Speter session_relpath = ""; 69251881Speter } 70251881Speter else 71251881Speter SVN_ERR(svn_ra_get_path_relative_to_session(dfb->ra_session, 72251881Speter &session_relpath, url, 73251881Speter scratch_pool)); 74251881Speter 75251881Speter /* Is session_relpath still a directory? */ 76251881Speter SVN_ERR(svn_ra_check_path(dfb->ra_session, session_relpath, 77251881Speter dfb->target_revision, &kind, scratch_pool)); 78251881Speter 79251881Speter if (kind == svn_node_dir) 80251881Speter SVN_ERR(svn_ra_get_dir2(dfb->ra_session, dirents, NULL, NULL, 81251881Speter session_relpath, dfb->target_revision, 82251881Speter SVN_DIRENT_KIND, result_pool)); 83251881Speter else 84251881Speter *dirents = NULL; 85251881Speter 86251881Speter if (old_url) 87251881Speter SVN_ERR(svn_ra_reparent(dfb->ra_session, old_url, scratch_pool)); 88251881Speter 89251881Speter return SVN_NO_ERROR; 90251881Speter} 91251881Speter 92251881Speter 93251881Speter/*** Code. ***/ 94251881Speter 95251881Speter/* Set *CLEAN_CHECKOUT to FALSE only if LOCAL_ABSPATH is a non-empty 96251881Speter folder. ANCHOR_ABSPATH is the w/c root and LOCAL_ABSPATH will still 97251881Speter be considered empty, if it is equal to ANCHOR_ABSPATH and only 98251881Speter contains the admin sub-folder. 99251881Speter If the w/c folder already exists but cannot be openend, we return 100251881Speter "unclean" - just in case. Most likely, the caller will have to bail 101251881Speter out later due to the same error we got here. 102251881Speter */ 103251881Speterstatic svn_error_t * 104251881Speteris_empty_wc(svn_boolean_t *clean_checkout, 105251881Speter const char *local_abspath, 106251881Speter const char *anchor_abspath, 107251881Speter apr_pool_t *pool) 108251881Speter{ 109251881Speter apr_dir_t *dir; 110251881Speter apr_finfo_t finfo; 111251881Speter svn_error_t *err; 112251881Speter 113251881Speter /* "clean" until found dirty */ 114251881Speter *clean_checkout = TRUE; 115251881Speter 116251881Speter /* open directory. If it does not exist, yet, a clean one will 117251881Speter be created by the caller. */ 118251881Speter err = svn_io_dir_open(&dir, local_abspath, pool); 119251881Speter if (err) 120251881Speter { 121251881Speter if (! APR_STATUS_IS_ENOENT(err->apr_err)) 122251881Speter *clean_checkout = FALSE; 123251881Speter 124251881Speter svn_error_clear(err); 125251881Speter return SVN_NO_ERROR; 126251881Speter } 127251881Speter 128251881Speter for (err = svn_io_dir_read(&finfo, APR_FINFO_NAME, dir, pool); 129251881Speter err == SVN_NO_ERROR; 130251881Speter err = svn_io_dir_read(&finfo, APR_FINFO_NAME, dir, pool)) 131251881Speter { 132251881Speter /* Ignore entries for this dir and its parent, robustly. 133251881Speter (APR promises that they'll come first, so technically 134251881Speter this guard could be moved outside the loop. But Ryan Bloom 135251881Speter says he doesn't believe it, and I believe him. */ 136251881Speter if (! (finfo.name[0] == '.' 137251881Speter && (finfo.name[1] == '\0' 138251881Speter || (finfo.name[1] == '.' && finfo.name[2] == '\0')))) 139251881Speter { 140251881Speter if ( ! svn_wc_is_adm_dir(finfo.name, pool) 141251881Speter || strcmp(local_abspath, anchor_abspath) != 0) 142251881Speter { 143251881Speter *clean_checkout = FALSE; 144251881Speter break; 145251881Speter } 146251881Speter } 147251881Speter } 148251881Speter 149251881Speter if (err) 150251881Speter { 151251881Speter if (! APR_STATUS_IS_ENOENT(err->apr_err)) 152251881Speter { 153251881Speter /* There was some issue reading the folder content. 154251881Speter * We better disable optimizations in that case. */ 155251881Speter *clean_checkout = FALSE; 156251881Speter } 157251881Speter 158251881Speter svn_error_clear(err); 159251881Speter } 160251881Speter 161251881Speter return svn_io_dir_close(dir); 162251881Speter} 163251881Speter 164251881Speter/* A conflict callback that simply records the conflicted path in BATON. 165251881Speter 166251881Speter Implements svn_wc_conflict_resolver_func2_t. 167251881Speter*/ 168251881Speterstatic svn_error_t * 169251881Speterrecord_conflict(svn_wc_conflict_result_t **result, 170251881Speter const svn_wc_conflict_description2_t *description, 171251881Speter void *baton, 172251881Speter apr_pool_t *result_pool, 173251881Speter apr_pool_t *scratch_pool) 174251881Speter{ 175251881Speter apr_hash_t *conflicted_paths = baton; 176251881Speter 177251881Speter svn_hash_sets(conflicted_paths, 178251881Speter apr_pstrdup(apr_hash_pool_get(conflicted_paths), 179251881Speter description->local_abspath), ""); 180251881Speter *result = svn_wc_create_conflict_result(svn_wc_conflict_choose_postpone, 181251881Speter NULL, result_pool); 182251881Speter return SVN_NO_ERROR; 183251881Speter} 184251881Speter 185251881Speter/* This is a helper for svn_client__update_internal(), which see for 186251881Speter an explanation of most of these parameters. Some stuff that's 187251881Speter unique is as follows: 188251881Speter 189251881Speter ANCHOR_ABSPATH is the local absolute path of the update anchor. 190251881Speter This is typically either the same as LOCAL_ABSPATH, or the 191251881Speter immediate parent of LOCAL_ABSPATH. 192251881Speter 193251881Speter If NOTIFY_SUMMARY is set (and there's a notification handler in 194251881Speter CTX), transmit the final update summary upon successful 195251881Speter completion of the update. 196251881Speter 197251881Speter Add the paths of any conflict victims to CONFLICTED_PATHS, if that 198251881Speter is not null. 199251881Speter*/ 200251881Speterstatic svn_error_t * 201251881Speterupdate_internal(svn_revnum_t *result_rev, 202251881Speter apr_hash_t *conflicted_paths, 203251881Speter const char *local_abspath, 204251881Speter const char *anchor_abspath, 205251881Speter const svn_opt_revision_t *revision, 206251881Speter svn_depth_t depth, 207251881Speter svn_boolean_t depth_is_sticky, 208251881Speter svn_boolean_t ignore_externals, 209251881Speter svn_boolean_t allow_unver_obstructions, 210251881Speter svn_boolean_t adds_as_modification, 211251881Speter svn_boolean_t *timestamp_sleep, 212251881Speter svn_boolean_t notify_summary, 213251881Speter svn_client_ctx_t *ctx, 214251881Speter apr_pool_t *pool) 215251881Speter{ 216251881Speter const svn_delta_editor_t *update_editor; 217251881Speter void *update_edit_baton; 218251881Speter const svn_ra_reporter3_t *reporter; 219251881Speter void *report_baton; 220251881Speter const char *corrected_url; 221251881Speter const char *target; 222251881Speter const char *repos_root_url; 223251881Speter const char *repos_relpath; 224251881Speter const char *repos_uuid; 225251881Speter const char *anchor_url; 226251881Speter svn_revnum_t revnum; 227251881Speter svn_boolean_t use_commit_times; 228251881Speter svn_boolean_t clean_checkout = FALSE; 229251881Speter const char *diff3_cmd; 230251881Speter apr_hash_t *wcroot_iprops; 231251881Speter svn_opt_revision_t opt_rev; 232251881Speter svn_ra_session_t *ra_session; 233251881Speter const char *preserved_exts_str; 234251881Speter apr_array_header_t *preserved_exts; 235251881Speter struct svn_client__dirent_fetcher_baton_t dfb; 236251881Speter svn_boolean_t server_supports_depth; 237251881Speter svn_boolean_t cropping_target; 238251881Speter svn_boolean_t target_conflicted = FALSE; 239251881Speter svn_config_t *cfg = ctx->config 240251881Speter ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) 241251881Speter : NULL; 242251881Speter 243251881Speter if (result_rev) 244251881Speter *result_rev = SVN_INVALID_REVNUM; 245251881Speter 246251881Speter /* An unknown depth can't be sticky. */ 247251881Speter if (depth == svn_depth_unknown) 248251881Speter depth_is_sticky = FALSE; 249251881Speter 250251881Speter if (strcmp(local_abspath, anchor_abspath)) 251251881Speter target = svn_dirent_basename(local_abspath, pool); 252251881Speter else 253251881Speter target = ""; 254251881Speter 255251881Speter /* Check if our anchor exists in BASE. If it doesn't we can't update. */ 256251881Speter SVN_ERR(svn_wc__node_get_base(NULL, NULL, &repos_relpath, &repos_root_url, 257251881Speter &repos_uuid, NULL, 258251881Speter ctx->wc_ctx, anchor_abspath, 259251881Speter TRUE, FALSE, 260251881Speter pool, pool)); 261251881Speter 262251881Speter /* It does not make sense to update conflict victims. */ 263251881Speter if (repos_relpath) 264251881Speter { 265251881Speter svn_error_t *err; 266251881Speter svn_boolean_t text_conflicted, prop_conflicted; 267251881Speter 268251881Speter anchor_url = svn_path_url_add_component2(repos_root_url, repos_relpath, 269251881Speter pool); 270251881Speter 271251881Speter err = svn_wc_conflicted_p3(&text_conflicted, &prop_conflicted, 272251881Speter NULL, 273251881Speter ctx->wc_ctx, local_abspath, pool); 274251881Speter 275251881Speter if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 276251881Speter return svn_error_trace(err); 277251881Speter svn_error_clear(err); 278251881Speter 279251881Speter /* tree-conflicts are handled by the update editor */ 280251881Speter if (!err && (text_conflicted || prop_conflicted)) 281251881Speter target_conflicted = TRUE; 282251881Speter } 283251881Speter else 284251881Speter anchor_url = NULL; 285251881Speter 286251881Speter if (! anchor_url || target_conflicted) 287251881Speter { 288251881Speter if (ctx->notify_func2) 289251881Speter { 290251881Speter svn_wc_notify_t *nt; 291251881Speter 292251881Speter nt = svn_wc_create_notify(local_abspath, 293251881Speter target_conflicted 294251881Speter ? svn_wc_notify_skip_conflicted 295251881Speter : svn_wc_notify_update_skip_working_only, 296251881Speter pool); 297251881Speter 298251881Speter ctx->notify_func2(ctx->notify_baton2, nt, pool); 299251881Speter } 300251881Speter return SVN_NO_ERROR; 301251881Speter } 302251881Speter 303251881Speter /* We may need to crop the tree if the depth is sticky */ 304251881Speter cropping_target = (depth_is_sticky && depth < svn_depth_infinity); 305251881Speter if (cropping_target) 306251881Speter { 307251881Speter svn_node_kind_t target_kind; 308251881Speter 309251881Speter if (depth == svn_depth_exclude) 310251881Speter { 311251881Speter SVN_ERR(svn_wc_exclude(ctx->wc_ctx, 312251881Speter local_abspath, 313251881Speter ctx->cancel_func, ctx->cancel_baton, 314251881Speter ctx->notify_func2, ctx->notify_baton2, 315251881Speter pool)); 316251881Speter 317251881Speter /* Target excluded, we are done now */ 318251881Speter return SVN_NO_ERROR; 319251881Speter } 320251881Speter 321251881Speter SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, local_abspath, 322251881Speter TRUE, TRUE, pool)); 323251881Speter if (target_kind == svn_node_dir) 324251881Speter { 325251881Speter SVN_ERR(svn_wc_crop_tree2(ctx->wc_ctx, local_abspath, depth, 326251881Speter ctx->cancel_func, ctx->cancel_baton, 327251881Speter ctx->notify_func2, ctx->notify_baton2, 328251881Speter pool)); 329251881Speter } 330251881Speter } 331251881Speter 332251881Speter /* check whether the "clean c/o" optimization is applicable */ 333251881Speter SVN_ERR(is_empty_wc(&clean_checkout, local_abspath, anchor_abspath, pool)); 334251881Speter 335251881Speter /* Get the external diff3, if any. */ 336251881Speter svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, 337251881Speter SVN_CONFIG_OPTION_DIFF3_CMD, NULL); 338251881Speter 339251881Speter if (diff3_cmd != NULL) 340251881Speter SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, pool)); 341251881Speter 342251881Speter /* See if the user wants last-commit timestamps instead of current ones. */ 343251881Speter SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, 344251881Speter SVN_CONFIG_SECTION_MISCELLANY, 345251881Speter SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); 346251881Speter 347251881Speter /* See which files the user wants to preserve the extension of when 348251881Speter conflict files are made. */ 349251881Speter svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, 350251881Speter SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, ""); 351251881Speter preserved_exts = *preserved_exts_str 352251881Speter ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, pool) 353251881Speter : NULL; 354251881Speter 355251881Speter /* Let everyone know we're starting a real update (unless we're 356251881Speter asked not to). */ 357251881Speter if (ctx->notify_func2 && notify_summary) 358251881Speter { 359251881Speter svn_wc_notify_t *notify 360251881Speter = svn_wc_create_notify(local_abspath, svn_wc_notify_update_started, 361251881Speter pool); 362251881Speter notify->kind = svn_node_none; 363251881Speter notify->content_state = notify->prop_state 364251881Speter = svn_wc_notify_state_inapplicable; 365251881Speter notify->lock_state = svn_wc_notify_lock_state_inapplicable; 366251881Speter (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); 367251881Speter } 368251881Speter 369251881Speter /* Open an RA session for the URL */ 370251881Speter SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url, 371251881Speter anchor_url, 372251881Speter anchor_abspath, NULL, TRUE, 373251881Speter TRUE, ctx, pool, pool)); 374251881Speter 375251881Speter /* If we got a corrected URL from the RA subsystem, we'll need to 376251881Speter relocate our working copy first. */ 377251881Speter if (corrected_url) 378251881Speter { 379251881Speter const char *new_repos_root_url; 380251881Speter 381251881Speter /* To relocate everything inside our repository we need the old and new 382251881Speter repos root. */ 383251881Speter SVN_ERR(svn_ra_get_repos_root2(ra_session, &new_repos_root_url, pool)); 384251881Speter 385251881Speter /* svn_client_relocate2() will check the uuid */ 386262253Speter SVN_ERR(svn_client_relocate2(anchor_abspath, repos_root_url, 387251881Speter new_repos_root_url, ignore_externals, 388251881Speter ctx, pool)); 389251881Speter 390251881Speter /* Store updated repository root for externals */ 391251881Speter repos_root_url = new_repos_root_url; 392251881Speter /* ### We should update anchor_loc->repos_uuid too, although currently 393251881Speter * we don't use it. */ 394251881Speter anchor_url = corrected_url; 395251881Speter } 396251881Speter 397251881Speter /* Resolve unspecified REVISION now, because we need to retrieve the 398251881Speter correct inherited props prior to the editor drive and we need to 399251881Speter use the same value of HEAD for both. */ 400251881Speter opt_rev.kind = revision->kind; 401251881Speter opt_rev.value = revision->value; 402251881Speter if (opt_rev.kind == svn_opt_revision_unspecified) 403251881Speter opt_rev.kind = svn_opt_revision_head; 404251881Speter 405251881Speter /* ### todo: shouldn't svn_client__get_revision_number be able 406251881Speter to take a URL as easily as a local path? */ 407251881Speter SVN_ERR(svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx, 408251881Speter local_abspath, ra_session, &opt_rev, 409251881Speter pool)); 410251881Speter 411251881Speter SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, 412251881Speter SVN_RA_CAPABILITY_DEPTH, pool)); 413251881Speter 414251881Speter dfb.ra_session = ra_session; 415251881Speter dfb.target_revision = revnum; 416251881Speter dfb.anchor_url = anchor_url; 417251881Speter 418251881Speter SVN_ERR(svn_client__get_inheritable_props(&wcroot_iprops, local_abspath, 419251881Speter revnum, depth, ra_session, 420251881Speter ctx, pool, pool)); 421251881Speter 422251881Speter /* Fetch the update editor. If REVISION is invalid, that's okay; 423251881Speter the RA driver will call editor->set_target_revision later on. */ 424251881Speter SVN_ERR(svn_wc__get_update_editor(&update_editor, &update_edit_baton, 425251881Speter &revnum, ctx->wc_ctx, anchor_abspath, 426251881Speter target, wcroot_iprops, use_commit_times, 427251881Speter depth, depth_is_sticky, 428251881Speter allow_unver_obstructions, 429251881Speter adds_as_modification, 430251881Speter server_supports_depth, 431251881Speter clean_checkout, 432251881Speter diff3_cmd, preserved_exts, 433251881Speter svn_client__dirent_fetcher, &dfb, 434251881Speter conflicted_paths ? record_conflict : NULL, 435251881Speter conflicted_paths, 436251881Speter NULL, NULL, 437251881Speter ctx->cancel_func, ctx->cancel_baton, 438251881Speter ctx->notify_func2, ctx->notify_baton2, 439251881Speter pool, pool)); 440251881Speter 441251881Speter /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an 442251881Speter invalid revnum, that means RA will use the latest revision. */ 443251881Speter SVN_ERR(svn_ra_do_update3(ra_session, &reporter, &report_baton, 444251881Speter revnum, target, 445251881Speter (!server_supports_depth || depth_is_sticky 446251881Speter ? depth 447251881Speter : svn_depth_unknown), 448251881Speter FALSE /* send_copyfrom_args */, 449251881Speter FALSE /* ignore_ancestry */, 450251881Speter update_editor, update_edit_baton, pool, pool)); 451251881Speter 452251881Speter /* Past this point, we assume the WC is going to be modified so we will 453251881Speter * need to sleep for timestamps. */ 454251881Speter *timestamp_sleep = TRUE; 455251881Speter 456251881Speter /* Drive the reporter structure, describing the revisions within 457251881Speter PATH. When we call reporter->finish_report, the 458251881Speter update_editor will be driven by svn_repos_dir_delta2. */ 459251881Speter SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter, 460251881Speter report_baton, TRUE, 461251881Speter depth, (! depth_is_sticky), 462251881Speter (! server_supports_depth), 463251881Speter use_commit_times, 464251881Speter ctx->cancel_func, ctx->cancel_baton, 465251881Speter ctx->notify_func2, ctx->notify_baton2, 466251881Speter pool)); 467251881Speter 468251881Speter /* We handle externals after the update is complete, so that 469251881Speter handling external items (and any errors therefrom) doesn't delay 470251881Speter the primary operation. */ 471251881Speter if ((SVN_DEPTH_IS_RECURSIVE(depth) || cropping_target) 472251881Speter && (! ignore_externals)) 473251881Speter { 474251881Speter apr_hash_t *new_externals; 475251881Speter apr_hash_t *new_depths; 476251881Speter SVN_ERR(svn_wc__externals_gather_definitions(&new_externals, 477251881Speter &new_depths, 478251881Speter ctx->wc_ctx, local_abspath, 479251881Speter depth, pool, pool)); 480251881Speter 481251881Speter SVN_ERR(svn_client__handle_externals(new_externals, 482251881Speter new_depths, 483251881Speter repos_root_url, local_abspath, 484251881Speter depth, timestamp_sleep, 485251881Speter ctx, pool)); 486251881Speter } 487251881Speter 488251881Speter /* Let everyone know we're finished here (unless we're asked not to). */ 489251881Speter if (ctx->notify_func2 && notify_summary) 490251881Speter { 491251881Speter svn_wc_notify_t *notify 492251881Speter = svn_wc_create_notify(local_abspath, svn_wc_notify_update_completed, 493251881Speter pool); 494251881Speter notify->kind = svn_node_none; 495251881Speter notify->content_state = notify->prop_state 496251881Speter = svn_wc_notify_state_inapplicable; 497251881Speter notify->lock_state = svn_wc_notify_lock_state_inapplicable; 498251881Speter notify->revision = revnum; 499251881Speter (*ctx->notify_func2)(ctx->notify_baton2, notify, pool); 500251881Speter } 501251881Speter 502251881Speter /* If the caller wants the result revision, give it to them. */ 503251881Speter if (result_rev) 504251881Speter *result_rev = revnum; 505251881Speter 506251881Speter return SVN_NO_ERROR; 507251881Speter} 508251881Speter 509251881Spetersvn_error_t * 510251881Spetersvn_client__update_internal(svn_revnum_t *result_rev, 511251881Speter const char *local_abspath, 512251881Speter const svn_opt_revision_t *revision, 513251881Speter svn_depth_t depth, 514251881Speter svn_boolean_t depth_is_sticky, 515251881Speter svn_boolean_t ignore_externals, 516251881Speter svn_boolean_t allow_unver_obstructions, 517251881Speter svn_boolean_t adds_as_modification, 518251881Speter svn_boolean_t make_parents, 519251881Speter svn_boolean_t innerupdate, 520251881Speter svn_boolean_t *timestamp_sleep, 521251881Speter svn_client_ctx_t *ctx, 522251881Speter apr_pool_t *pool) 523251881Speter{ 524251881Speter const char *anchor_abspath, *lockroot_abspath; 525251881Speter svn_error_t *err; 526251881Speter svn_opt_revision_t peg_revision = *revision; 527251881Speter apr_hash_t *conflicted_paths 528251881Speter = ctx->conflict_func2 ? apr_hash_make(pool) : NULL; 529251881Speter 530251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 531251881Speter SVN_ERR_ASSERT(! (innerupdate && make_parents)); 532251881Speter 533251881Speter if (make_parents) 534251881Speter { 535251881Speter int i; 536251881Speter const char *parent_abspath = local_abspath; 537251881Speter apr_array_header_t *missing_parents = 538251881Speter apr_array_make(pool, 4, sizeof(const char *)); 539251881Speter 540251881Speter while (1) 541251881Speter { 542251881Speter /* Try to lock. If we can't lock because our target (or its 543251881Speter parent) isn't a working copy, we'll try to walk up the 544251881Speter tree to find a working copy, remembering this path's 545251881Speter parent as one we need to flesh out. */ 546251881Speter err = svn_wc__acquire_write_lock(&lockroot_abspath, ctx->wc_ctx, 547251881Speter parent_abspath, !innerupdate, 548251881Speter pool, pool); 549251881Speter if (!err) 550251881Speter break; 551251881Speter if ((err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) 552251881Speter || svn_dirent_is_root(parent_abspath, strlen(parent_abspath))) 553251881Speter return err; 554251881Speter svn_error_clear(err); 555251881Speter 556251881Speter /* Remember the parent of our update target as a missing 557251881Speter parent. */ 558251881Speter parent_abspath = svn_dirent_dirname(parent_abspath, pool); 559251881Speter APR_ARRAY_PUSH(missing_parents, const char *) = parent_abspath; 560251881Speter } 561251881Speter 562251881Speter /* Run 'svn up --depth=empty' (effectively) on the missing 563251881Speter parents, if any. */ 564251881Speter anchor_abspath = lockroot_abspath; 565251881Speter for (i = missing_parents->nelts - 1; i >= 0; i--) 566251881Speter { 567251881Speter const char *missing_parent = 568251881Speter APR_ARRAY_IDX(missing_parents, i, const char *); 569251881Speter 570251881Speter err = update_internal(result_rev, conflicted_paths, 571251881Speter missing_parent, anchor_abspath, 572251881Speter &peg_revision, svn_depth_empty, FALSE, 573251881Speter ignore_externals, allow_unver_obstructions, 574251881Speter adds_as_modification, timestamp_sleep, 575251881Speter FALSE, ctx, pool); 576251881Speter if (err) 577251881Speter goto cleanup; 578251881Speter anchor_abspath = missing_parent; 579251881Speter 580251881Speter /* If we successfully updated a missing parent, let's re-use 581251881Speter the returned revision number for future updates for the 582251881Speter sake of consistency. */ 583251881Speter peg_revision.kind = svn_opt_revision_number; 584251881Speter peg_revision.value.number = *result_rev; 585251881Speter } 586251881Speter } 587251881Speter else 588251881Speter { 589251881Speter SVN_ERR(svn_wc__acquire_write_lock(&lockroot_abspath, ctx->wc_ctx, 590251881Speter local_abspath, !innerupdate, 591251881Speter pool, pool)); 592251881Speter anchor_abspath = lockroot_abspath; 593251881Speter } 594251881Speter 595251881Speter err = update_internal(result_rev, conflicted_paths, 596251881Speter local_abspath, anchor_abspath, 597251881Speter &peg_revision, depth, depth_is_sticky, 598251881Speter ignore_externals, allow_unver_obstructions, 599251881Speter adds_as_modification, timestamp_sleep, 600251881Speter TRUE, ctx, pool); 601251881Speter 602251881Speter /* Give the conflict resolver callback the opportunity to 603251881Speter * resolve any conflicts that were raised. */ 604251881Speter if (! err && ctx->conflict_func2) 605251881Speter { 606251881Speter err = svn_client__resolve_conflicts(NULL, conflicted_paths, ctx, pool); 607251881Speter } 608251881Speter 609251881Speter cleanup: 610251881Speter err = svn_error_compose_create( 611251881Speter err, 612251881Speter svn_wc__release_write_lock(ctx->wc_ctx, lockroot_abspath, pool)); 613251881Speter 614251881Speter return svn_error_trace(err); 615251881Speter} 616251881Speter 617251881Speter 618251881Spetersvn_error_t * 619251881Spetersvn_client_update4(apr_array_header_t **result_revs, 620251881Speter const apr_array_header_t *paths, 621251881Speter const svn_opt_revision_t *revision, 622251881Speter svn_depth_t depth, 623251881Speter svn_boolean_t depth_is_sticky, 624251881Speter svn_boolean_t ignore_externals, 625251881Speter svn_boolean_t allow_unver_obstructions, 626251881Speter svn_boolean_t adds_as_modification, 627251881Speter svn_boolean_t make_parents, 628251881Speter svn_client_ctx_t *ctx, 629251881Speter apr_pool_t *pool) 630251881Speter{ 631251881Speter int i; 632251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 633251881Speter const char *path = NULL; 634251881Speter svn_boolean_t sleep = FALSE; 635251881Speter svn_error_t *err = SVN_NO_ERROR; 636251881Speter 637251881Speter if (result_revs) 638251881Speter *result_revs = apr_array_make(pool, paths->nelts, sizeof(svn_revnum_t)); 639251881Speter 640251881Speter for (i = 0; i < paths->nelts; ++i) 641251881Speter { 642251881Speter path = APR_ARRAY_IDX(paths, i, const char *); 643251881Speter 644251881Speter if (svn_path_is_url(path)) 645251881Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 646251881Speter _("'%s' is not a local path"), path); 647251881Speter } 648251881Speter 649251881Speter for (i = 0; i < paths->nelts; ++i) 650251881Speter { 651251881Speter svn_revnum_t result_rev; 652251881Speter const char *local_abspath; 653251881Speter path = APR_ARRAY_IDX(paths, i, const char *); 654251881Speter 655251881Speter svn_pool_clear(iterpool); 656251881Speter 657251881Speter if (ctx->cancel_func) 658251881Speter { 659251881Speter err = ctx->cancel_func(ctx->cancel_baton); 660251881Speter if (err) 661251881Speter goto cleanup; 662251881Speter } 663251881Speter 664251881Speter err = svn_dirent_get_absolute(&local_abspath, path, iterpool); 665251881Speter if (err) 666251881Speter goto cleanup; 667251881Speter err = svn_client__update_internal(&result_rev, local_abspath, 668251881Speter revision, depth, depth_is_sticky, 669251881Speter ignore_externals, 670251881Speter allow_unver_obstructions, 671251881Speter adds_as_modification, 672251881Speter make_parents, 673251881Speter FALSE, &sleep, 674251881Speter ctx, 675251881Speter iterpool); 676251881Speter 677251881Speter if (err) 678251881Speter { 679251881Speter if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) 680251881Speter goto cleanup; 681251881Speter 682251881Speter svn_error_clear(err); 683251881Speter err = SVN_NO_ERROR; 684251881Speter 685251881Speter /* SVN_ERR_WC_NOT_WORKING_COPY: it's not versioned */ 686251881Speter 687251881Speter result_rev = SVN_INVALID_REVNUM; 688251881Speter if (ctx->notify_func2) 689251881Speter { 690251881Speter svn_wc_notify_t *notify; 691251881Speter notify = svn_wc_create_notify(path, 692251881Speter svn_wc_notify_skip, 693251881Speter iterpool); 694251881Speter (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool); 695251881Speter } 696251881Speter } 697251881Speter if (result_revs) 698251881Speter APR_ARRAY_PUSH(*result_revs, svn_revnum_t) = result_rev; 699251881Speter } 700251881Speter svn_pool_destroy(iterpool); 701251881Speter 702251881Speter cleanup: 703251881Speter if (sleep) 704262253Speter { 705262253Speter const char *wcroot_abspath; 706251881Speter 707262253Speter if (paths->nelts == 1) 708262253Speter { 709262253Speter const char *abspath; 710262253Speter 711262253Speter /* PATH iteslf may have been removed by the update. */ 712262253Speter SVN_ERR(svn_dirent_get_absolute(&abspath, path, pool)); 713262253Speter SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, abspath, 714262253Speter pool, pool)); 715262253Speter } 716262253Speter else 717262253Speter wcroot_abspath = NULL; 718262253Speter 719262253Speter svn_io_sleep_for_timestamps(wcroot_abspath, pool); 720262253Speter } 721262253Speter 722251881Speter return svn_error_trace(err); 723251881Speter} 724