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. 199299742Sdim 200299742Sdim Use RA_SESSION_P to run the update if it is not NULL. If it is then 201299742Sdim open a new ra session and place it in RA_SESSION_P. This allows 202299742Sdim repeated calls to update_internal to reuse the same session. 203251881Speter*/ 204251881Speterstatic svn_error_t * 205251881Speterupdate_internal(svn_revnum_t *result_rev, 206299742Sdim svn_boolean_t *timestamp_sleep, 207251881Speter apr_hash_t *conflicted_paths, 208299742Sdim svn_ra_session_t **ra_session_p, 209251881Speter const char *local_abspath, 210251881Speter const char *anchor_abspath, 211251881Speter const svn_opt_revision_t *revision, 212251881Speter svn_depth_t depth, 213251881Speter svn_boolean_t depth_is_sticky, 214251881Speter svn_boolean_t ignore_externals, 215251881Speter svn_boolean_t allow_unver_obstructions, 216251881Speter svn_boolean_t adds_as_modification, 217251881Speter svn_boolean_t notify_summary, 218251881Speter svn_client_ctx_t *ctx, 219299742Sdim apr_pool_t *result_pool, 220299742Sdim apr_pool_t *scratch_pool) 221251881Speter{ 222251881Speter const svn_delta_editor_t *update_editor; 223251881Speter void *update_edit_baton; 224251881Speter const svn_ra_reporter3_t *reporter; 225251881Speter void *report_baton; 226251881Speter const char *corrected_url; 227251881Speter const char *target; 228251881Speter const char *repos_root_url; 229251881Speter const char *repos_relpath; 230251881Speter const char *repos_uuid; 231251881Speter const char *anchor_url; 232251881Speter svn_revnum_t revnum; 233251881Speter svn_boolean_t use_commit_times; 234251881Speter svn_boolean_t clean_checkout = FALSE; 235251881Speter const char *diff3_cmd; 236251881Speter apr_hash_t *wcroot_iprops; 237251881Speter svn_opt_revision_t opt_rev; 238299742Sdim svn_ra_session_t *ra_session = *ra_session_p; 239251881Speter const char *preserved_exts_str; 240251881Speter apr_array_header_t *preserved_exts; 241251881Speter struct svn_client__dirent_fetcher_baton_t dfb; 242251881Speter svn_boolean_t server_supports_depth; 243251881Speter svn_boolean_t cropping_target; 244251881Speter svn_boolean_t target_conflicted = FALSE; 245251881Speter svn_config_t *cfg = ctx->config 246251881Speter ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) 247251881Speter : NULL; 248251881Speter 249251881Speter if (result_rev) 250251881Speter *result_rev = SVN_INVALID_REVNUM; 251251881Speter 252251881Speter /* An unknown depth can't be sticky. */ 253251881Speter if (depth == svn_depth_unknown) 254251881Speter depth_is_sticky = FALSE; 255251881Speter 256251881Speter if (strcmp(local_abspath, anchor_abspath)) 257299742Sdim target = svn_dirent_basename(local_abspath, scratch_pool); 258251881Speter else 259251881Speter target = ""; 260251881Speter 261251881Speter /* Check if our anchor exists in BASE. If it doesn't we can't update. */ 262251881Speter SVN_ERR(svn_wc__node_get_base(NULL, NULL, &repos_relpath, &repos_root_url, 263251881Speter &repos_uuid, NULL, 264251881Speter ctx->wc_ctx, anchor_abspath, 265299742Sdim TRUE /* ignore_enoent */, 266299742Sdim scratch_pool, scratch_pool)); 267251881Speter 268251881Speter /* It does not make sense to update conflict victims. */ 269251881Speter if (repos_relpath) 270251881Speter { 271251881Speter svn_error_t *err; 272251881Speter svn_boolean_t text_conflicted, prop_conflicted; 273251881Speter 274251881Speter anchor_url = svn_path_url_add_component2(repos_root_url, repos_relpath, 275299742Sdim scratch_pool); 276251881Speter 277251881Speter err = svn_wc_conflicted_p3(&text_conflicted, &prop_conflicted, 278251881Speter NULL, 279299742Sdim ctx->wc_ctx, local_abspath, scratch_pool); 280251881Speter 281251881Speter if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 282251881Speter return svn_error_trace(err); 283251881Speter svn_error_clear(err); 284251881Speter 285251881Speter /* tree-conflicts are handled by the update editor */ 286251881Speter if (!err && (text_conflicted || prop_conflicted)) 287251881Speter target_conflicted = TRUE; 288251881Speter } 289251881Speter else 290251881Speter anchor_url = NULL; 291251881Speter 292251881Speter if (! anchor_url || target_conflicted) 293251881Speter { 294251881Speter if (ctx->notify_func2) 295251881Speter { 296251881Speter svn_wc_notify_t *nt; 297251881Speter 298251881Speter nt = svn_wc_create_notify(local_abspath, 299251881Speter target_conflicted 300251881Speter ? svn_wc_notify_skip_conflicted 301251881Speter : svn_wc_notify_update_skip_working_only, 302299742Sdim scratch_pool); 303251881Speter 304299742Sdim ctx->notify_func2(ctx->notify_baton2, nt, scratch_pool); 305251881Speter } 306251881Speter return SVN_NO_ERROR; 307251881Speter } 308251881Speter 309251881Speter /* We may need to crop the tree if the depth is sticky */ 310251881Speter cropping_target = (depth_is_sticky && depth < svn_depth_infinity); 311251881Speter if (cropping_target) 312251881Speter { 313251881Speter svn_node_kind_t target_kind; 314251881Speter 315251881Speter if (depth == svn_depth_exclude) 316251881Speter { 317251881Speter SVN_ERR(svn_wc_exclude(ctx->wc_ctx, 318251881Speter local_abspath, 319251881Speter ctx->cancel_func, ctx->cancel_baton, 320251881Speter ctx->notify_func2, ctx->notify_baton2, 321299742Sdim scratch_pool)); 322251881Speter 323251881Speter /* Target excluded, we are done now */ 324251881Speter return SVN_NO_ERROR; 325251881Speter } 326251881Speter 327251881Speter SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, local_abspath, 328299742Sdim TRUE, TRUE, scratch_pool)); 329251881Speter if (target_kind == svn_node_dir) 330251881Speter { 331251881Speter SVN_ERR(svn_wc_crop_tree2(ctx->wc_ctx, local_abspath, depth, 332251881Speter ctx->cancel_func, ctx->cancel_baton, 333251881Speter ctx->notify_func2, ctx->notify_baton2, 334299742Sdim scratch_pool)); 335251881Speter } 336251881Speter } 337251881Speter 338251881Speter /* check whether the "clean c/o" optimization is applicable */ 339299742Sdim SVN_ERR(is_empty_wc(&clean_checkout, local_abspath, anchor_abspath, 340299742Sdim scratch_pool)); 341251881Speter 342251881Speter /* Get the external diff3, if any. */ 343251881Speter svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, 344251881Speter SVN_CONFIG_OPTION_DIFF3_CMD, NULL); 345251881Speter 346251881Speter if (diff3_cmd != NULL) 347299742Sdim SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool)); 348251881Speter 349251881Speter /* See if the user wants last-commit timestamps instead of current ones. */ 350251881Speter SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, 351251881Speter SVN_CONFIG_SECTION_MISCELLANY, 352251881Speter SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); 353251881Speter 354251881Speter /* See which files the user wants to preserve the extension of when 355251881Speter conflict files are made. */ 356251881Speter svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, 357251881Speter SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, ""); 358251881Speter preserved_exts = *preserved_exts_str 359299742Sdim ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, scratch_pool) 360251881Speter : NULL; 361251881Speter 362251881Speter /* Let everyone know we're starting a real update (unless we're 363251881Speter asked not to). */ 364251881Speter if (ctx->notify_func2 && notify_summary) 365251881Speter { 366251881Speter svn_wc_notify_t *notify 367251881Speter = svn_wc_create_notify(local_abspath, svn_wc_notify_update_started, 368299742Sdim scratch_pool); 369251881Speter notify->kind = svn_node_none; 370251881Speter notify->content_state = notify->prop_state 371251881Speter = svn_wc_notify_state_inapplicable; 372251881Speter notify->lock_state = svn_wc_notify_lock_state_inapplicable; 373299742Sdim ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); 374251881Speter } 375251881Speter 376299742Sdim /* Try to reuse the RA session by reparenting it to the anchor_url. 377299742Sdim * This code is probably overly cautious since we only use this 378299742Sdim * currently when parents are missing and so all the anchor_urls 379299742Sdim * have to be in the same repo. */ 380299742Sdim if (ra_session) 381299742Sdim { 382299742Sdim svn_error_t *err = svn_ra_reparent(ra_session, anchor_url, scratch_pool); 383299742Sdim if (err) 384299742Sdim { 385299742Sdim if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL) 386299742Sdim { 387299742Sdim /* session changed repos, can't reuse it */ 388299742Sdim svn_error_clear(err); 389299742Sdim ra_session = NULL; 390299742Sdim } 391299742Sdim else 392299742Sdim { 393299742Sdim return svn_error_trace(err); 394299742Sdim } 395299742Sdim } 396299742Sdim else 397299742Sdim { 398299742Sdim corrected_url = NULL; 399299742Sdim } 400299742Sdim } 401251881Speter 402299742Sdim /* Open an RA session for the URL if one isn't already available */ 403299742Sdim if (!ra_session) 404299742Sdim { 405299742Sdim SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url, 406299742Sdim anchor_url, 407299742Sdim anchor_abspath, NULL, 408299742Sdim TRUE /* write_dav_props */, 409299742Sdim TRUE /* read_dav_props */, 410299742Sdim ctx, 411299742Sdim result_pool, scratch_pool)); 412299742Sdim *ra_session_p = ra_session; 413299742Sdim } 414299742Sdim 415251881Speter /* If we got a corrected URL from the RA subsystem, we'll need to 416251881Speter relocate our working copy first. */ 417251881Speter if (corrected_url) 418251881Speter { 419251881Speter const char *new_repos_root_url; 420251881Speter 421251881Speter /* To relocate everything inside our repository we need the old and new 422251881Speter repos root. */ 423299742Sdim SVN_ERR(svn_ra_get_repos_root2(ra_session, &new_repos_root_url, 424299742Sdim scratch_pool)); 425251881Speter 426251881Speter /* svn_client_relocate2() will check the uuid */ 427262253Speter SVN_ERR(svn_client_relocate2(anchor_abspath, repos_root_url, 428251881Speter new_repos_root_url, ignore_externals, 429299742Sdim ctx, scratch_pool)); 430251881Speter 431251881Speter /* Store updated repository root for externals */ 432251881Speter repos_root_url = new_repos_root_url; 433251881Speter /* ### We should update anchor_loc->repos_uuid too, although currently 434251881Speter * we don't use it. */ 435251881Speter anchor_url = corrected_url; 436251881Speter } 437251881Speter 438251881Speter /* Resolve unspecified REVISION now, because we need to retrieve the 439251881Speter correct inherited props prior to the editor drive and we need to 440251881Speter use the same value of HEAD for both. */ 441251881Speter opt_rev.kind = revision->kind; 442251881Speter opt_rev.value = revision->value; 443251881Speter if (opt_rev.kind == svn_opt_revision_unspecified) 444251881Speter opt_rev.kind = svn_opt_revision_head; 445251881Speter 446251881Speter /* ### todo: shouldn't svn_client__get_revision_number be able 447251881Speter to take a URL as easily as a local path? */ 448251881Speter SVN_ERR(svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx, 449251881Speter local_abspath, ra_session, &opt_rev, 450299742Sdim scratch_pool)); 451251881Speter 452251881Speter SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, 453299742Sdim SVN_RA_CAPABILITY_DEPTH, scratch_pool)); 454251881Speter 455251881Speter dfb.ra_session = ra_session; 456251881Speter dfb.target_revision = revnum; 457251881Speter dfb.anchor_url = anchor_url; 458251881Speter 459251881Speter SVN_ERR(svn_client__get_inheritable_props(&wcroot_iprops, local_abspath, 460251881Speter revnum, depth, ra_session, 461299742Sdim ctx, scratch_pool, scratch_pool)); 462251881Speter 463251881Speter /* Fetch the update editor. If REVISION is invalid, that's okay; 464251881Speter the RA driver will call editor->set_target_revision later on. */ 465251881Speter SVN_ERR(svn_wc__get_update_editor(&update_editor, &update_edit_baton, 466251881Speter &revnum, ctx->wc_ctx, anchor_abspath, 467251881Speter target, wcroot_iprops, use_commit_times, 468251881Speter depth, depth_is_sticky, 469251881Speter allow_unver_obstructions, 470251881Speter adds_as_modification, 471251881Speter server_supports_depth, 472251881Speter clean_checkout, 473251881Speter diff3_cmd, preserved_exts, 474251881Speter svn_client__dirent_fetcher, &dfb, 475251881Speter conflicted_paths ? record_conflict : NULL, 476251881Speter conflicted_paths, 477251881Speter NULL, NULL, 478251881Speter ctx->cancel_func, ctx->cancel_baton, 479251881Speter ctx->notify_func2, ctx->notify_baton2, 480299742Sdim scratch_pool, scratch_pool)); 481251881Speter 482251881Speter /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an 483251881Speter invalid revnum, that means RA will use the latest revision. */ 484251881Speter SVN_ERR(svn_ra_do_update3(ra_session, &reporter, &report_baton, 485251881Speter revnum, target, 486251881Speter (!server_supports_depth || depth_is_sticky 487251881Speter ? depth 488251881Speter : svn_depth_unknown), 489251881Speter FALSE /* send_copyfrom_args */, 490251881Speter FALSE /* ignore_ancestry */, 491299742Sdim update_editor, update_edit_baton, 492299742Sdim scratch_pool, scratch_pool)); 493251881Speter 494251881Speter /* Past this point, we assume the WC is going to be modified so we will 495251881Speter * need to sleep for timestamps. */ 496251881Speter *timestamp_sleep = TRUE; 497251881Speter 498251881Speter /* Drive the reporter structure, describing the revisions within 499299742Sdim LOCAL_ABSPATH. When this calls reporter->finish_report, the 500299742Sdim reporter will drive the update_editor. */ 501251881Speter SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter, 502251881Speter report_baton, TRUE, 503251881Speter depth, (! depth_is_sticky), 504251881Speter (! server_supports_depth), 505251881Speter use_commit_times, 506251881Speter ctx->cancel_func, ctx->cancel_baton, 507251881Speter ctx->notify_func2, ctx->notify_baton2, 508299742Sdim scratch_pool)); 509251881Speter 510251881Speter /* We handle externals after the update is complete, so that 511251881Speter handling external items (and any errors therefrom) doesn't delay 512251881Speter the primary operation. */ 513251881Speter if ((SVN_DEPTH_IS_RECURSIVE(depth) || cropping_target) 514251881Speter && (! ignore_externals)) 515251881Speter { 516251881Speter apr_hash_t *new_externals; 517251881Speter apr_hash_t *new_depths; 518251881Speter SVN_ERR(svn_wc__externals_gather_definitions(&new_externals, 519251881Speter &new_depths, 520251881Speter ctx->wc_ctx, local_abspath, 521299742Sdim depth, 522299742Sdim scratch_pool, scratch_pool)); 523251881Speter 524251881Speter SVN_ERR(svn_client__handle_externals(new_externals, 525251881Speter new_depths, 526251881Speter repos_root_url, local_abspath, 527299742Sdim depth, timestamp_sleep, ra_session, 528299742Sdim ctx, scratch_pool)); 529251881Speter } 530251881Speter 531251881Speter /* Let everyone know we're finished here (unless we're asked not to). */ 532251881Speter if (ctx->notify_func2 && notify_summary) 533251881Speter { 534251881Speter svn_wc_notify_t *notify 535251881Speter = svn_wc_create_notify(local_abspath, svn_wc_notify_update_completed, 536299742Sdim scratch_pool); 537251881Speter notify->kind = svn_node_none; 538251881Speter notify->content_state = notify->prop_state 539251881Speter = svn_wc_notify_state_inapplicable; 540251881Speter notify->lock_state = svn_wc_notify_lock_state_inapplicable; 541251881Speter notify->revision = revnum; 542299742Sdim ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); 543251881Speter } 544251881Speter 545251881Speter /* If the caller wants the result revision, give it to them. */ 546251881Speter if (result_rev) 547251881Speter *result_rev = revnum; 548251881Speter 549251881Speter return SVN_NO_ERROR; 550251881Speter} 551251881Speter 552251881Spetersvn_error_t * 553251881Spetersvn_client__update_internal(svn_revnum_t *result_rev, 554299742Sdim svn_boolean_t *timestamp_sleep, 555251881Speter const char *local_abspath, 556251881Speter const svn_opt_revision_t *revision, 557251881Speter svn_depth_t depth, 558251881Speter svn_boolean_t depth_is_sticky, 559251881Speter svn_boolean_t ignore_externals, 560251881Speter svn_boolean_t allow_unver_obstructions, 561251881Speter svn_boolean_t adds_as_modification, 562251881Speter svn_boolean_t make_parents, 563251881Speter svn_boolean_t innerupdate, 564299742Sdim svn_ra_session_t *ra_session, 565251881Speter svn_client_ctx_t *ctx, 566251881Speter apr_pool_t *pool) 567251881Speter{ 568251881Speter const char *anchor_abspath, *lockroot_abspath; 569251881Speter svn_error_t *err; 570251881Speter svn_opt_revision_t peg_revision = *revision; 571251881Speter apr_hash_t *conflicted_paths 572251881Speter = ctx->conflict_func2 ? apr_hash_make(pool) : NULL; 573251881Speter 574251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 575251881Speter SVN_ERR_ASSERT(! (innerupdate && make_parents)); 576251881Speter 577251881Speter if (make_parents) 578251881Speter { 579251881Speter int i; 580251881Speter const char *parent_abspath = local_abspath; 581251881Speter apr_array_header_t *missing_parents = 582251881Speter apr_array_make(pool, 4, sizeof(const char *)); 583299742Sdim apr_pool_t *iterpool; 584251881Speter 585299742Sdim iterpool = svn_pool_create(pool); 586299742Sdim 587251881Speter while (1) 588251881Speter { 589299742Sdim svn_pool_clear(iterpool); 590299742Sdim 591251881Speter /* Try to lock. If we can't lock because our target (or its 592251881Speter parent) isn't a working copy, we'll try to walk up the 593251881Speter tree to find a working copy, remembering this path's 594251881Speter parent as one we need to flesh out. */ 595251881Speter err = svn_wc__acquire_write_lock(&lockroot_abspath, ctx->wc_ctx, 596251881Speter parent_abspath, !innerupdate, 597299742Sdim pool, iterpool); 598251881Speter if (!err) 599251881Speter break; 600251881Speter if ((err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) 601251881Speter || svn_dirent_is_root(parent_abspath, strlen(parent_abspath))) 602251881Speter return err; 603251881Speter svn_error_clear(err); 604251881Speter 605251881Speter /* Remember the parent of our update target as a missing 606251881Speter parent. */ 607251881Speter parent_abspath = svn_dirent_dirname(parent_abspath, pool); 608251881Speter APR_ARRAY_PUSH(missing_parents, const char *) = parent_abspath; 609251881Speter } 610251881Speter 611251881Speter /* Run 'svn up --depth=empty' (effectively) on the missing 612251881Speter parents, if any. */ 613251881Speter anchor_abspath = lockroot_abspath; 614251881Speter for (i = missing_parents->nelts - 1; i >= 0; i--) 615251881Speter { 616251881Speter const char *missing_parent = 617251881Speter APR_ARRAY_IDX(missing_parents, i, const char *); 618251881Speter 619299742Sdim svn_pool_clear(iterpool); 620299742Sdim 621299742Sdim err = update_internal(result_rev, timestamp_sleep, conflicted_paths, 622299742Sdim &ra_session, missing_parent, 623299742Sdim anchor_abspath, &peg_revision, svn_depth_empty, 624299742Sdim FALSE, ignore_externals, 625299742Sdim allow_unver_obstructions, adds_as_modification, 626299742Sdim FALSE, ctx, pool, iterpool); 627251881Speter if (err) 628251881Speter goto cleanup; 629251881Speter anchor_abspath = missing_parent; 630251881Speter 631251881Speter /* If we successfully updated a missing parent, let's re-use 632251881Speter the returned revision number for future updates for the 633251881Speter sake of consistency. */ 634251881Speter peg_revision.kind = svn_opt_revision_number; 635251881Speter peg_revision.value.number = *result_rev; 636251881Speter } 637299742Sdim 638299742Sdim svn_pool_destroy(iterpool); 639251881Speter } 640251881Speter else 641251881Speter { 642251881Speter SVN_ERR(svn_wc__acquire_write_lock(&lockroot_abspath, ctx->wc_ctx, 643251881Speter local_abspath, !innerupdate, 644251881Speter pool, pool)); 645251881Speter anchor_abspath = lockroot_abspath; 646251881Speter } 647251881Speter 648299742Sdim err = update_internal(result_rev, timestamp_sleep, conflicted_paths, 649299742Sdim &ra_session, 650251881Speter local_abspath, anchor_abspath, 651251881Speter &peg_revision, depth, depth_is_sticky, 652251881Speter ignore_externals, allow_unver_obstructions, 653299742Sdim adds_as_modification, 654299742Sdim TRUE, ctx, pool, pool); 655251881Speter 656251881Speter /* Give the conflict resolver callback the opportunity to 657251881Speter * resolve any conflicts that were raised. */ 658299742Sdim if (! err && ctx->conflict_func2 && apr_hash_count(conflicted_paths)) 659251881Speter { 660251881Speter err = svn_client__resolve_conflicts(NULL, conflicted_paths, ctx, pool); 661251881Speter } 662251881Speter 663251881Speter cleanup: 664251881Speter err = svn_error_compose_create( 665251881Speter err, 666251881Speter svn_wc__release_write_lock(ctx->wc_ctx, lockroot_abspath, pool)); 667251881Speter 668251881Speter return svn_error_trace(err); 669251881Speter} 670251881Speter 671251881Speter 672251881Spetersvn_error_t * 673251881Spetersvn_client_update4(apr_array_header_t **result_revs, 674251881Speter const apr_array_header_t *paths, 675251881Speter const svn_opt_revision_t *revision, 676251881Speter svn_depth_t depth, 677251881Speter svn_boolean_t depth_is_sticky, 678251881Speter svn_boolean_t ignore_externals, 679251881Speter svn_boolean_t allow_unver_obstructions, 680251881Speter svn_boolean_t adds_as_modification, 681251881Speter svn_boolean_t make_parents, 682251881Speter svn_client_ctx_t *ctx, 683251881Speter apr_pool_t *pool) 684251881Speter{ 685251881Speter int i; 686251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 687251881Speter const char *path = NULL; 688251881Speter svn_boolean_t sleep = FALSE; 689251881Speter svn_error_t *err = SVN_NO_ERROR; 690299742Sdim svn_boolean_t found_valid_target = FALSE; 691251881Speter 692251881Speter if (result_revs) 693251881Speter *result_revs = apr_array_make(pool, paths->nelts, sizeof(svn_revnum_t)); 694251881Speter 695251881Speter for (i = 0; i < paths->nelts; ++i) 696251881Speter { 697251881Speter path = APR_ARRAY_IDX(paths, i, const char *); 698251881Speter 699251881Speter if (svn_path_is_url(path)) 700251881Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 701251881Speter _("'%s' is not a local path"), path); 702251881Speter } 703251881Speter 704251881Speter for (i = 0; i < paths->nelts; ++i) 705251881Speter { 706251881Speter svn_revnum_t result_rev; 707251881Speter const char *local_abspath; 708251881Speter path = APR_ARRAY_IDX(paths, i, const char *); 709251881Speter 710251881Speter svn_pool_clear(iterpool); 711251881Speter 712251881Speter if (ctx->cancel_func) 713251881Speter { 714251881Speter err = ctx->cancel_func(ctx->cancel_baton); 715251881Speter if (err) 716251881Speter goto cleanup; 717251881Speter } 718251881Speter 719251881Speter err = svn_dirent_get_absolute(&local_abspath, path, iterpool); 720251881Speter if (err) 721251881Speter goto cleanup; 722299742Sdim err = svn_client__update_internal(&result_rev, &sleep, local_abspath, 723251881Speter revision, depth, depth_is_sticky, 724251881Speter ignore_externals, 725251881Speter allow_unver_obstructions, 726251881Speter adds_as_modification, 727251881Speter make_parents, 728299742Sdim FALSE, NULL, ctx, 729251881Speter iterpool); 730251881Speter 731251881Speter if (err) 732251881Speter { 733251881Speter if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) 734251881Speter goto cleanup; 735251881Speter 736251881Speter svn_error_clear(err); 737251881Speter err = SVN_NO_ERROR; 738251881Speter 739251881Speter /* SVN_ERR_WC_NOT_WORKING_COPY: it's not versioned */ 740251881Speter 741251881Speter result_rev = SVN_INVALID_REVNUM; 742251881Speter if (ctx->notify_func2) 743251881Speter { 744251881Speter svn_wc_notify_t *notify; 745251881Speter notify = svn_wc_create_notify(path, 746251881Speter svn_wc_notify_skip, 747251881Speter iterpool); 748299742Sdim ctx->notify_func2(ctx->notify_baton2, notify, iterpool); 749251881Speter } 750251881Speter } 751299742Sdim else 752299742Sdim found_valid_target = TRUE; 753299742Sdim 754251881Speter if (result_revs) 755251881Speter APR_ARRAY_PUSH(*result_revs, svn_revnum_t) = result_rev; 756251881Speter } 757251881Speter svn_pool_destroy(iterpool); 758251881Speter 759251881Speter cleanup: 760299742Sdim if (!err && !found_valid_target) 761299742Sdim return svn_error_create(SVN_ERR_WC_NOT_WORKING_COPY, NULL, 762299742Sdim _("None of the targets are working copies")); 763251881Speter if (sleep) 764262253Speter { 765262253Speter const char *wcroot_abspath; 766251881Speter 767262253Speter if (paths->nelts == 1) 768262253Speter { 769262253Speter const char *abspath; 770262253Speter 771262253Speter /* PATH iteslf may have been removed by the update. */ 772262253Speter SVN_ERR(svn_dirent_get_absolute(&abspath, path, pool)); 773262253Speter SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, abspath, 774262253Speter pool, pool)); 775262253Speter } 776262253Speter else 777262253Speter wcroot_abspath = NULL; 778262253Speter 779262253Speter svn_io_sleep_for_timestamps(wcroot_abspath, pool); 780262253Speter } 781262253Speter 782251881Speter return svn_error_trace(err); 783251881Speter} 784