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 185362181Sdim/* Perform post-update processing of externals defined below LOCAL_ABSPATH. */ 186362181Sdimstatic svn_error_t * 187362181Sdimhandle_externals(svn_boolean_t *timestamp_sleep, 188362181Sdim const char *local_abspath, 189362181Sdim svn_depth_t depth, 190362181Sdim const char *repos_root_url, 191362181Sdim svn_ra_session_t *ra_session, 192362181Sdim svn_client_ctx_t *ctx, 193362181Sdim apr_pool_t *scratch_pool) 194362181Sdim{ 195362181Sdim apr_hash_t *new_externals; 196362181Sdim apr_hash_t *new_depths; 197362181Sdim 198362181Sdim SVN_ERR(svn_wc__externals_gather_definitions(&new_externals, 199362181Sdim &new_depths, 200362181Sdim ctx->wc_ctx, local_abspath, 201362181Sdim depth, 202362181Sdim scratch_pool, scratch_pool)); 203362181Sdim 204362181Sdim SVN_ERR(svn_client__handle_externals(new_externals, 205362181Sdim new_depths, 206362181Sdim repos_root_url, local_abspath, 207362181Sdim depth, timestamp_sleep, ra_session, 208362181Sdim ctx, scratch_pool)); 209362181Sdim return SVN_NO_ERROR; 210362181Sdim} 211362181Sdim 212362181Sdim/* Try to reuse the RA session by reparenting it to the anchor_url. 213362181Sdim * This code is probably overly cautious since we only use this 214362181Sdim * currently when parents are missing and so all the anchor_urls 215362181Sdim * have to be in the same repo. 216362181Sdim * Note that ra_session_p is an (optional) input parameter as well 217362181Sdim * as an output parameter. */ 218362181Sdimstatic svn_error_t * 219362181Sdimreuse_ra_session(svn_ra_session_t **ra_session_p, 220362181Sdim const char **corrected_url, 221362181Sdim const char *anchor_url, 222362181Sdim const char *anchor_abspath, 223362181Sdim svn_client_ctx_t *ctx, 224362181Sdim apr_pool_t *result_pool, 225362181Sdim apr_pool_t *scratch_pool) 226362181Sdim{ 227362181Sdim svn_ra_session_t *ra_session = *ra_session_p; 228362181Sdim 229362181Sdim if (ra_session) 230362181Sdim { 231362181Sdim svn_error_t *err = svn_ra_reparent(ra_session, anchor_url, scratch_pool); 232362181Sdim if (err) 233362181Sdim { 234362181Sdim if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL) 235362181Sdim { 236362181Sdim /* session changed repos, can't reuse it */ 237362181Sdim svn_error_clear(err); 238362181Sdim ra_session = NULL; 239362181Sdim } 240362181Sdim else 241362181Sdim { 242362181Sdim return svn_error_trace(err); 243362181Sdim } 244362181Sdim } 245362181Sdim else 246362181Sdim { 247362181Sdim *corrected_url = NULL; 248362181Sdim } 249362181Sdim } 250362181Sdim 251362181Sdim /* Open an RA session for the URL if one isn't already available */ 252362181Sdim if (!ra_session) 253362181Sdim { 254362181Sdim SVN_ERR(svn_client__open_ra_session_internal(&ra_session, corrected_url, 255362181Sdim anchor_url, 256362181Sdim anchor_abspath, NULL, 257362181Sdim TRUE /* write_dav_props */, 258362181Sdim TRUE /* read_dav_props */, 259362181Sdim ctx, 260362181Sdim result_pool, scratch_pool)); 261362181Sdim *ra_session_p = ra_session; 262362181Sdim } 263362181Sdim 264362181Sdim return SVN_NO_ERROR; 265362181Sdim} 266362181Sdim 267251881Speter/* This is a helper for svn_client__update_internal(), which see for 268251881Speter an explanation of most of these parameters. Some stuff that's 269251881Speter unique is as follows: 270251881Speter 271251881Speter ANCHOR_ABSPATH is the local absolute path of the update anchor. 272251881Speter This is typically either the same as LOCAL_ABSPATH, or the 273251881Speter immediate parent of LOCAL_ABSPATH. 274251881Speter 275251881Speter If NOTIFY_SUMMARY is set (and there's a notification handler in 276251881Speter CTX), transmit the final update summary upon successful 277251881Speter completion of the update. 278251881Speter 279251881Speter Add the paths of any conflict victims to CONFLICTED_PATHS, if that 280251881Speter is not null. 281289180Speter 282289180Speter Use RA_SESSION_P to run the update if it is not NULL. If it is then 283289180Speter open a new ra session and place it in RA_SESSION_P. This allows 284289180Speter repeated calls to update_internal to reuse the same session. 285251881Speter*/ 286251881Speterstatic svn_error_t * 287251881Speterupdate_internal(svn_revnum_t *result_rev, 288289180Speter svn_boolean_t *timestamp_sleep, 289251881Speter apr_hash_t *conflicted_paths, 290289180Speter svn_ra_session_t **ra_session_p, 291251881Speter const char *local_abspath, 292251881Speter const char *anchor_abspath, 293251881Speter const svn_opt_revision_t *revision, 294251881Speter svn_depth_t depth, 295251881Speter svn_boolean_t depth_is_sticky, 296251881Speter svn_boolean_t ignore_externals, 297251881Speter svn_boolean_t allow_unver_obstructions, 298251881Speter svn_boolean_t adds_as_modification, 299251881Speter svn_boolean_t notify_summary, 300251881Speter svn_client_ctx_t *ctx, 301289180Speter apr_pool_t *result_pool, 302289180Speter apr_pool_t *scratch_pool) 303251881Speter{ 304251881Speter const svn_delta_editor_t *update_editor; 305251881Speter void *update_edit_baton; 306251881Speter const svn_ra_reporter3_t *reporter; 307251881Speter void *report_baton; 308251881Speter const char *corrected_url; 309251881Speter const char *target; 310251881Speter const char *repos_root_url; 311251881Speter const char *repos_relpath; 312251881Speter const char *repos_uuid; 313251881Speter const char *anchor_url; 314251881Speter svn_revnum_t revnum; 315251881Speter svn_boolean_t use_commit_times; 316251881Speter svn_boolean_t clean_checkout = FALSE; 317251881Speter const char *diff3_cmd; 318251881Speter apr_hash_t *wcroot_iprops; 319251881Speter svn_opt_revision_t opt_rev; 320289180Speter svn_ra_session_t *ra_session = *ra_session_p; 321251881Speter const char *preserved_exts_str; 322251881Speter apr_array_header_t *preserved_exts; 323251881Speter struct svn_client__dirent_fetcher_baton_t dfb; 324251881Speter svn_boolean_t server_supports_depth; 325251881Speter svn_boolean_t cropping_target; 326251881Speter svn_boolean_t target_conflicted = FALSE; 327251881Speter svn_config_t *cfg = ctx->config 328251881Speter ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) 329251881Speter : NULL; 330251881Speter 331251881Speter if (result_rev) 332251881Speter *result_rev = SVN_INVALID_REVNUM; 333251881Speter 334251881Speter /* An unknown depth can't be sticky. */ 335251881Speter if (depth == svn_depth_unknown) 336251881Speter depth_is_sticky = FALSE; 337251881Speter 338251881Speter if (strcmp(local_abspath, anchor_abspath)) 339289180Speter target = svn_dirent_basename(local_abspath, scratch_pool); 340251881Speter else 341251881Speter target = ""; 342251881Speter 343251881Speter /* Check if our anchor exists in BASE. If it doesn't we can't update. */ 344251881Speter SVN_ERR(svn_wc__node_get_base(NULL, NULL, &repos_relpath, &repos_root_url, 345251881Speter &repos_uuid, NULL, 346251881Speter ctx->wc_ctx, anchor_abspath, 347289180Speter TRUE /* ignore_enoent */, 348289180Speter scratch_pool, scratch_pool)); 349251881Speter 350251881Speter /* It does not make sense to update conflict victims. */ 351251881Speter if (repos_relpath) 352251881Speter { 353251881Speter svn_error_t *err; 354251881Speter svn_boolean_t text_conflicted, prop_conflicted; 355251881Speter 356251881Speter anchor_url = svn_path_url_add_component2(repos_root_url, repos_relpath, 357289180Speter scratch_pool); 358251881Speter 359251881Speter err = svn_wc_conflicted_p3(&text_conflicted, &prop_conflicted, 360251881Speter NULL, 361289180Speter ctx->wc_ctx, local_abspath, scratch_pool); 362251881Speter 363251881Speter if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 364251881Speter return svn_error_trace(err); 365251881Speter svn_error_clear(err); 366251881Speter 367251881Speter /* tree-conflicts are handled by the update editor */ 368251881Speter if (!err && (text_conflicted || prop_conflicted)) 369251881Speter target_conflicted = TRUE; 370251881Speter } 371251881Speter else 372251881Speter anchor_url = NULL; 373251881Speter 374251881Speter if (! anchor_url || target_conflicted) 375251881Speter { 376251881Speter if (ctx->notify_func2) 377251881Speter { 378251881Speter svn_wc_notify_t *nt; 379251881Speter 380251881Speter nt = svn_wc_create_notify(local_abspath, 381251881Speter target_conflicted 382251881Speter ? svn_wc_notify_skip_conflicted 383251881Speter : svn_wc_notify_update_skip_working_only, 384289180Speter scratch_pool); 385251881Speter 386289180Speter ctx->notify_func2(ctx->notify_baton2, nt, scratch_pool); 387251881Speter } 388251881Speter return SVN_NO_ERROR; 389251881Speter } 390251881Speter 391251881Speter /* We may need to crop the tree if the depth is sticky */ 392251881Speter cropping_target = (depth_is_sticky && depth < svn_depth_infinity); 393251881Speter if (cropping_target) 394251881Speter { 395251881Speter svn_node_kind_t target_kind; 396251881Speter 397251881Speter if (depth == svn_depth_exclude) 398251881Speter { 399251881Speter SVN_ERR(svn_wc_exclude(ctx->wc_ctx, 400251881Speter local_abspath, 401251881Speter ctx->cancel_func, ctx->cancel_baton, 402251881Speter ctx->notify_func2, ctx->notify_baton2, 403289180Speter scratch_pool)); 404251881Speter 405362181Sdim if (!ignore_externals) 406362181Sdim { 407362181Sdim /* We may now be able to remove externals below LOCAL_ABSPATH. */ 408362181Sdim SVN_ERR(reuse_ra_session(ra_session_p, &corrected_url, 409362181Sdim anchor_url, anchor_abspath, 410362181Sdim ctx, result_pool, scratch_pool)); 411362181Sdim ra_session = *ra_session_p; 412362181Sdim SVN_ERR(handle_externals(timestamp_sleep, local_abspath, depth, 413362181Sdim repos_root_url, ra_session, ctx, 414362181Sdim scratch_pool)); 415362181Sdim } 416362181Sdim 417251881Speter /* Target excluded, we are done now */ 418251881Speter return SVN_NO_ERROR; 419251881Speter } 420251881Speter 421251881Speter SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, local_abspath, 422289180Speter TRUE, TRUE, scratch_pool)); 423251881Speter if (target_kind == svn_node_dir) 424251881Speter { 425251881Speter SVN_ERR(svn_wc_crop_tree2(ctx->wc_ctx, local_abspath, depth, 426251881Speter ctx->cancel_func, ctx->cancel_baton, 427251881Speter ctx->notify_func2, ctx->notify_baton2, 428289180Speter scratch_pool)); 429251881Speter } 430251881Speter } 431251881Speter 432251881Speter /* check whether the "clean c/o" optimization is applicable */ 433289180Speter SVN_ERR(is_empty_wc(&clean_checkout, local_abspath, anchor_abspath, 434289180Speter scratch_pool)); 435251881Speter 436251881Speter /* Get the external diff3, if any. */ 437251881Speter svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, 438251881Speter SVN_CONFIG_OPTION_DIFF3_CMD, NULL); 439251881Speter 440251881Speter if (diff3_cmd != NULL) 441289180Speter SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool)); 442251881Speter 443251881Speter /* See if the user wants last-commit timestamps instead of current ones. */ 444251881Speter SVN_ERR(svn_config_get_bool(cfg, &use_commit_times, 445251881Speter SVN_CONFIG_SECTION_MISCELLANY, 446251881Speter SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); 447251881Speter 448251881Speter /* See which files the user wants to preserve the extension of when 449251881Speter conflict files are made. */ 450251881Speter svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY, 451251881Speter SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, ""); 452251881Speter preserved_exts = *preserved_exts_str 453289180Speter ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ", FALSE, scratch_pool) 454251881Speter : NULL; 455251881Speter 456251881Speter /* Let everyone know we're starting a real update (unless we're 457251881Speter asked not to). */ 458251881Speter if (ctx->notify_func2 && notify_summary) 459251881Speter { 460251881Speter svn_wc_notify_t *notify 461251881Speter = svn_wc_create_notify(local_abspath, svn_wc_notify_update_started, 462289180Speter scratch_pool); 463251881Speter notify->kind = svn_node_none; 464251881Speter notify->content_state = notify->prop_state 465251881Speter = svn_wc_notify_state_inapplicable; 466251881Speter notify->lock_state = svn_wc_notify_lock_state_inapplicable; 467289180Speter ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); 468251881Speter } 469251881Speter 470362181Sdim SVN_ERR(reuse_ra_session(ra_session_p, &corrected_url, anchor_url, 471362181Sdim anchor_abspath, ctx, result_pool, scratch_pool)); 472362181Sdim ra_session = *ra_session_p; 473251881Speter 474251881Speter /* If we got a corrected URL from the RA subsystem, we'll need to 475251881Speter relocate our working copy first. */ 476251881Speter if (corrected_url) 477251881Speter { 478251881Speter const char *new_repos_root_url; 479251881Speter 480251881Speter /* To relocate everything inside our repository we need the old and new 481251881Speter repos root. */ 482289180Speter SVN_ERR(svn_ra_get_repos_root2(ra_session, &new_repos_root_url, 483289180Speter scratch_pool)); 484251881Speter 485251881Speter /* svn_client_relocate2() will check the uuid */ 486262250Speter SVN_ERR(svn_client_relocate2(anchor_abspath, repos_root_url, 487251881Speter new_repos_root_url, ignore_externals, 488289180Speter ctx, scratch_pool)); 489251881Speter 490251881Speter /* Store updated repository root for externals */ 491251881Speter repos_root_url = new_repos_root_url; 492251881Speter /* ### We should update anchor_loc->repos_uuid too, although currently 493251881Speter * we don't use it. */ 494251881Speter anchor_url = corrected_url; 495251881Speter } 496251881Speter 497251881Speter /* Resolve unspecified REVISION now, because we need to retrieve the 498251881Speter correct inherited props prior to the editor drive and we need to 499251881Speter use the same value of HEAD for both. */ 500251881Speter opt_rev.kind = revision->kind; 501251881Speter opt_rev.value = revision->value; 502251881Speter if (opt_rev.kind == svn_opt_revision_unspecified) 503251881Speter opt_rev.kind = svn_opt_revision_head; 504251881Speter 505251881Speter /* ### todo: shouldn't svn_client__get_revision_number be able 506251881Speter to take a URL as easily as a local path? */ 507251881Speter SVN_ERR(svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx, 508251881Speter local_abspath, ra_session, &opt_rev, 509289180Speter scratch_pool)); 510251881Speter 511251881Speter SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, 512289180Speter SVN_RA_CAPABILITY_DEPTH, scratch_pool)); 513251881Speter 514251881Speter dfb.ra_session = ra_session; 515251881Speter dfb.target_revision = revnum; 516251881Speter dfb.anchor_url = anchor_url; 517251881Speter 518251881Speter SVN_ERR(svn_client__get_inheritable_props(&wcroot_iprops, local_abspath, 519251881Speter revnum, depth, ra_session, 520289180Speter ctx, scratch_pool, scratch_pool)); 521251881Speter 522251881Speter /* Fetch the update editor. If REVISION is invalid, that's okay; 523251881Speter the RA driver will call editor->set_target_revision later on. */ 524251881Speter SVN_ERR(svn_wc__get_update_editor(&update_editor, &update_edit_baton, 525251881Speter &revnum, ctx->wc_ctx, anchor_abspath, 526251881Speter target, wcroot_iprops, use_commit_times, 527251881Speter depth, depth_is_sticky, 528251881Speter allow_unver_obstructions, 529251881Speter adds_as_modification, 530251881Speter server_supports_depth, 531251881Speter clean_checkout, 532251881Speter diff3_cmd, preserved_exts, 533251881Speter svn_client__dirent_fetcher, &dfb, 534251881Speter conflicted_paths ? record_conflict : NULL, 535251881Speter conflicted_paths, 536251881Speter NULL, NULL, 537251881Speter ctx->cancel_func, ctx->cancel_baton, 538251881Speter ctx->notify_func2, ctx->notify_baton2, 539289180Speter scratch_pool, scratch_pool)); 540251881Speter 541251881Speter /* Tell RA to do an update of URL+TARGET to REVISION; if we pass an 542251881Speter invalid revnum, that means RA will use the latest revision. */ 543251881Speter SVN_ERR(svn_ra_do_update3(ra_session, &reporter, &report_baton, 544251881Speter revnum, target, 545251881Speter (!server_supports_depth || depth_is_sticky 546251881Speter ? depth 547251881Speter : svn_depth_unknown), 548251881Speter FALSE /* send_copyfrom_args */, 549251881Speter FALSE /* ignore_ancestry */, 550289180Speter update_editor, update_edit_baton, 551289180Speter scratch_pool, scratch_pool)); 552251881Speter 553251881Speter /* Past this point, we assume the WC is going to be modified so we will 554251881Speter * need to sleep for timestamps. */ 555251881Speter *timestamp_sleep = TRUE; 556251881Speter 557251881Speter /* Drive the reporter structure, describing the revisions within 558289180Speter LOCAL_ABSPATH. When this calls reporter->finish_report, the 559289180Speter reporter will drive the update_editor. */ 560251881Speter SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter, 561251881Speter report_baton, TRUE, 562251881Speter depth, (! depth_is_sticky), 563251881Speter (! server_supports_depth), 564251881Speter use_commit_times, 565251881Speter ctx->cancel_func, ctx->cancel_baton, 566251881Speter ctx->notify_func2, ctx->notify_baton2, 567289180Speter scratch_pool)); 568251881Speter 569251881Speter /* We handle externals after the update is complete, so that 570251881Speter handling external items (and any errors therefrom) doesn't delay 571251881Speter the primary operation. */ 572251881Speter if ((SVN_DEPTH_IS_RECURSIVE(depth) || cropping_target) 573251881Speter && (! ignore_externals)) 574251881Speter { 575362181Sdim SVN_ERR(handle_externals(timestamp_sleep, local_abspath, depth, 576362181Sdim repos_root_url, ra_session, ctx, scratch_pool)); 577251881Speter } 578251881Speter 579251881Speter /* Let everyone know we're finished here (unless we're asked not to). */ 580251881Speter if (ctx->notify_func2 && notify_summary) 581251881Speter { 582251881Speter svn_wc_notify_t *notify 583251881Speter = svn_wc_create_notify(local_abspath, svn_wc_notify_update_completed, 584289180Speter scratch_pool); 585251881Speter notify->kind = svn_node_none; 586251881Speter notify->content_state = notify->prop_state 587251881Speter = svn_wc_notify_state_inapplicable; 588251881Speter notify->lock_state = svn_wc_notify_lock_state_inapplicable; 589251881Speter notify->revision = revnum; 590289180Speter ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); 591251881Speter } 592251881Speter 593251881Speter /* If the caller wants the result revision, give it to them. */ 594251881Speter if (result_rev) 595251881Speter *result_rev = revnum; 596251881Speter 597251881Speter return SVN_NO_ERROR; 598251881Speter} 599251881Speter 600251881Spetersvn_error_t * 601251881Spetersvn_client__update_internal(svn_revnum_t *result_rev, 602289180Speter svn_boolean_t *timestamp_sleep, 603251881Speter const char *local_abspath, 604251881Speter const svn_opt_revision_t *revision, 605251881Speter svn_depth_t depth, 606251881Speter svn_boolean_t depth_is_sticky, 607251881Speter svn_boolean_t ignore_externals, 608251881Speter svn_boolean_t allow_unver_obstructions, 609251881Speter svn_boolean_t adds_as_modification, 610251881Speter svn_boolean_t make_parents, 611251881Speter svn_boolean_t innerupdate, 612289180Speter svn_ra_session_t *ra_session, 613251881Speter svn_client_ctx_t *ctx, 614251881Speter apr_pool_t *pool) 615251881Speter{ 616251881Speter const char *anchor_abspath, *lockroot_abspath; 617251881Speter svn_error_t *err; 618362181Sdim svn_opt_revision_t opt_rev = *revision; /* operative revision */ 619251881Speter apr_hash_t *conflicted_paths 620251881Speter = ctx->conflict_func2 ? apr_hash_make(pool) : NULL; 621251881Speter 622251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 623251881Speter SVN_ERR_ASSERT(! (innerupdate && make_parents)); 624251881Speter 625251881Speter if (make_parents) 626251881Speter { 627251881Speter int i; 628251881Speter const char *parent_abspath = local_abspath; 629251881Speter apr_array_header_t *missing_parents = 630251881Speter apr_array_make(pool, 4, sizeof(const char *)); 631289180Speter apr_pool_t *iterpool; 632251881Speter 633289180Speter iterpool = svn_pool_create(pool); 634289180Speter 635251881Speter while (1) 636251881Speter { 637289180Speter svn_pool_clear(iterpool); 638289180Speter 639251881Speter /* Try to lock. If we can't lock because our target (or its 640251881Speter parent) isn't a working copy, we'll try to walk up the 641251881Speter tree to find a working copy, remembering this path's 642251881Speter parent as one we need to flesh out. */ 643251881Speter err = svn_wc__acquire_write_lock(&lockroot_abspath, ctx->wc_ctx, 644251881Speter parent_abspath, !innerupdate, 645289180Speter pool, iterpool); 646251881Speter if (!err) 647251881Speter break; 648251881Speter if ((err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) 649251881Speter || svn_dirent_is_root(parent_abspath, strlen(parent_abspath))) 650251881Speter return err; 651251881Speter svn_error_clear(err); 652251881Speter 653251881Speter /* Remember the parent of our update target as a missing 654251881Speter parent. */ 655251881Speter parent_abspath = svn_dirent_dirname(parent_abspath, pool); 656251881Speter APR_ARRAY_PUSH(missing_parents, const char *) = parent_abspath; 657251881Speter } 658251881Speter 659251881Speter /* Run 'svn up --depth=empty' (effectively) on the missing 660251881Speter parents, if any. */ 661251881Speter anchor_abspath = lockroot_abspath; 662251881Speter for (i = missing_parents->nelts - 1; i >= 0; i--) 663251881Speter { 664251881Speter const char *missing_parent = 665251881Speter APR_ARRAY_IDX(missing_parents, i, const char *); 666251881Speter 667289180Speter svn_pool_clear(iterpool); 668289180Speter 669289180Speter err = update_internal(result_rev, timestamp_sleep, conflicted_paths, 670289180Speter &ra_session, missing_parent, 671362181Sdim anchor_abspath, &opt_rev, svn_depth_empty, 672289180Speter FALSE, ignore_externals, 673289180Speter allow_unver_obstructions, adds_as_modification, 674289180Speter FALSE, ctx, pool, iterpool); 675251881Speter if (err) 676251881Speter goto cleanup; 677251881Speter anchor_abspath = missing_parent; 678251881Speter 679251881Speter /* If we successfully updated a missing parent, let's re-use 680251881Speter the returned revision number for future updates for the 681251881Speter sake of consistency. */ 682362181Sdim opt_rev.kind = svn_opt_revision_number; 683362181Sdim opt_rev.value.number = *result_rev; 684251881Speter } 685289180Speter 686289180Speter svn_pool_destroy(iterpool); 687251881Speter } 688251881Speter else 689251881Speter { 690251881Speter SVN_ERR(svn_wc__acquire_write_lock(&lockroot_abspath, ctx->wc_ctx, 691251881Speter local_abspath, !innerupdate, 692251881Speter pool, pool)); 693251881Speter anchor_abspath = lockroot_abspath; 694251881Speter } 695251881Speter 696289180Speter err = update_internal(result_rev, timestamp_sleep, conflicted_paths, 697289180Speter &ra_session, 698251881Speter local_abspath, anchor_abspath, 699362181Sdim &opt_rev, depth, depth_is_sticky, 700251881Speter ignore_externals, allow_unver_obstructions, 701289180Speter adds_as_modification, 702289180Speter TRUE, ctx, pool, pool); 703251881Speter 704251881Speter /* Give the conflict resolver callback the opportunity to 705251881Speter * resolve any conflicts that were raised. */ 706289180Speter if (! err && ctx->conflict_func2 && apr_hash_count(conflicted_paths)) 707251881Speter { 708251881Speter err = svn_client__resolve_conflicts(NULL, conflicted_paths, ctx, pool); 709251881Speter } 710251881Speter 711251881Speter cleanup: 712251881Speter err = svn_error_compose_create( 713251881Speter err, 714251881Speter svn_wc__release_write_lock(ctx->wc_ctx, lockroot_abspath, pool)); 715251881Speter 716251881Speter return svn_error_trace(err); 717251881Speter} 718251881Speter 719251881Speter 720251881Spetersvn_error_t * 721251881Spetersvn_client_update4(apr_array_header_t **result_revs, 722251881Speter const apr_array_header_t *paths, 723251881Speter const svn_opt_revision_t *revision, 724251881Speter svn_depth_t depth, 725251881Speter svn_boolean_t depth_is_sticky, 726251881Speter svn_boolean_t ignore_externals, 727251881Speter svn_boolean_t allow_unver_obstructions, 728251881Speter svn_boolean_t adds_as_modification, 729251881Speter svn_boolean_t make_parents, 730251881Speter svn_client_ctx_t *ctx, 731251881Speter apr_pool_t *pool) 732251881Speter{ 733251881Speter int i; 734251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 735251881Speter const char *path = NULL; 736251881Speter svn_boolean_t sleep = FALSE; 737251881Speter svn_error_t *err = SVN_NO_ERROR; 738289180Speter svn_boolean_t found_valid_target = FALSE; 739251881Speter 740251881Speter if (result_revs) 741251881Speter *result_revs = apr_array_make(pool, paths->nelts, sizeof(svn_revnum_t)); 742251881Speter 743251881Speter for (i = 0; i < paths->nelts; ++i) 744251881Speter { 745251881Speter path = APR_ARRAY_IDX(paths, i, const char *); 746251881Speter 747251881Speter if (svn_path_is_url(path)) 748251881Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 749251881Speter _("'%s' is not a local path"), path); 750251881Speter } 751251881Speter 752251881Speter for (i = 0; i < paths->nelts; ++i) 753251881Speter { 754251881Speter svn_revnum_t result_rev; 755251881Speter const char *local_abspath; 756251881Speter path = APR_ARRAY_IDX(paths, i, const char *); 757251881Speter 758251881Speter svn_pool_clear(iterpool); 759251881Speter 760251881Speter if (ctx->cancel_func) 761251881Speter { 762251881Speter err = ctx->cancel_func(ctx->cancel_baton); 763251881Speter if (err) 764251881Speter goto cleanup; 765251881Speter } 766251881Speter 767251881Speter err = svn_dirent_get_absolute(&local_abspath, path, iterpool); 768251881Speter if (err) 769251881Speter goto cleanup; 770289180Speter err = svn_client__update_internal(&result_rev, &sleep, local_abspath, 771251881Speter revision, depth, depth_is_sticky, 772251881Speter ignore_externals, 773251881Speter allow_unver_obstructions, 774251881Speter adds_as_modification, 775251881Speter make_parents, 776289180Speter FALSE, NULL, ctx, 777251881Speter iterpool); 778251881Speter 779251881Speter if (err) 780251881Speter { 781251881Speter if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) 782251881Speter goto cleanup; 783251881Speter 784251881Speter svn_error_clear(err); 785251881Speter err = SVN_NO_ERROR; 786251881Speter 787251881Speter /* SVN_ERR_WC_NOT_WORKING_COPY: it's not versioned */ 788251881Speter 789251881Speter result_rev = SVN_INVALID_REVNUM; 790251881Speter if (ctx->notify_func2) 791251881Speter { 792251881Speter svn_wc_notify_t *notify; 793251881Speter notify = svn_wc_create_notify(path, 794251881Speter svn_wc_notify_skip, 795251881Speter iterpool); 796289180Speter ctx->notify_func2(ctx->notify_baton2, notify, iterpool); 797251881Speter } 798251881Speter } 799289180Speter else 800289180Speter found_valid_target = TRUE; 801289180Speter 802251881Speter if (result_revs) 803251881Speter APR_ARRAY_PUSH(*result_revs, svn_revnum_t) = result_rev; 804251881Speter } 805251881Speter svn_pool_destroy(iterpool); 806251881Speter 807251881Speter cleanup: 808289180Speter if (!err && !found_valid_target) 809289180Speter return svn_error_create(SVN_ERR_WC_NOT_WORKING_COPY, NULL, 810289180Speter _("None of the targets are working copies")); 811251881Speter if (sleep) 812257936Speter { 813257936Speter const char *wcroot_abspath; 814251881Speter 815257936Speter if (paths->nelts == 1) 816257936Speter { 817257936Speter const char *abspath; 818257936Speter 819257936Speter /* PATH iteslf may have been removed by the update. */ 820257936Speter SVN_ERR(svn_dirent_get_absolute(&abspath, path, pool)); 821257936Speter SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx, abspath, 822257936Speter pool, pool)); 823257936Speter } 824257936Speter else 825257936Speter wcroot_abspath = NULL; 826257936Speter 827257936Speter svn_io_sleep_for_timestamps(wcroot_abspath, pool); 828257936Speter } 829257936Speter 830251881Speter return svn_error_trace(err); 831251881Speter} 832