1251881Speter/* 2251881Speter * ra.c : routines for interacting with the RA layer 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#include <apr_pools.h> 27251881Speter 28251881Speter#include "svn_error.h" 29251881Speter#include "svn_hash.h" 30251881Speter#include "svn_pools.h" 31251881Speter#include "svn_string.h" 32251881Speter#include "svn_sorts.h" 33251881Speter#include "svn_ra.h" 34251881Speter#include "svn_client.h" 35251881Speter#include "svn_dirent_uri.h" 36251881Speter#include "svn_path.h" 37251881Speter#include "svn_props.h" 38251881Speter#include "svn_mergeinfo.h" 39251881Speter#include "client.h" 40251881Speter#include "mergeinfo.h" 41251881Speter 42251881Speter#include "svn_private_config.h" 43251881Speter#include "private/svn_wc_private.h" 44251881Speter#include "private/svn_client_private.h" 45299742Sdim#include "private/svn_sorts_private.h" 46251881Speter 47251881Speter 48251881Speter/* This is the baton that we pass svn_ra_open3(), and is associated with 49251881Speter the callback table we provide to RA. */ 50251881Spetertypedef struct callback_baton_t 51251881Speter{ 52251881Speter /* Holds the directory that corresponds to the REPOS_URL at svn_ra_open3() 53251881Speter time. When callbacks specify a relative path, they are joined with 54251881Speter this base directory. */ 55251881Speter const char *base_dir_abspath; 56251881Speter 57251881Speter /* TEMPORARY: Is 'base_dir_abspath' a versioned path? cmpilato 58251881Speter suspects that the commit-to-multiple-disjoint-working-copies 59251881Speter code is getting this all wrong, sometimes passing an unversioned 60251881Speter (or versioned in a foreign wc) path here which sorta kinda 61251881Speter happens to work most of the time but is ultimately incorrect. */ 62251881Speter svn_boolean_t base_dir_isversioned; 63251881Speter 64251881Speter /* Used as wri_abspath for obtaining access to the pristine store */ 65251881Speter const char *wcroot_abspath; 66251881Speter 67251881Speter /* An array of svn_client_commit_item3_t * structures, present only 68251881Speter during working copy commits. */ 69251881Speter const apr_array_header_t *commit_items; 70251881Speter 71251881Speter /* A client context. */ 72251881Speter svn_client_ctx_t *ctx; 73251881Speter 74299742Sdim /* Last progress reported by progress callback. */ 75299742Sdim apr_off_t last_progress; 76251881Speter} callback_baton_t; 77251881Speter 78251881Speter 79251881Speter 80251881Speterstatic svn_error_t * 81251881Speteropen_tmp_file(apr_file_t **fp, 82251881Speter void *callback_baton, 83251881Speter apr_pool_t *pool) 84251881Speter{ 85251881Speter return svn_error_trace(svn_io_open_unique_file3(fp, NULL, NULL, 86251881Speter svn_io_file_del_on_pool_cleanup, 87251881Speter pool, pool)); 88251881Speter} 89251881Speter 90251881Speter 91251881Speter/* This implements the 'svn_ra_get_wc_prop_func_t' interface. */ 92251881Speterstatic svn_error_t * 93251881Speterget_wc_prop(void *baton, 94251881Speter const char *relpath, 95251881Speter const char *name, 96251881Speter const svn_string_t **value, 97251881Speter apr_pool_t *pool) 98251881Speter{ 99251881Speter callback_baton_t *cb = baton; 100251881Speter const char *local_abspath = NULL; 101251881Speter svn_error_t *err; 102251881Speter 103251881Speter *value = NULL; 104251881Speter 105251881Speter /* If we have a list of commit_items, search through that for a 106251881Speter match for this relative URL. */ 107251881Speter if (cb->commit_items) 108251881Speter { 109251881Speter int i; 110251881Speter for (i = 0; i < cb->commit_items->nelts; i++) 111251881Speter { 112251881Speter svn_client_commit_item3_t *item 113251881Speter = APR_ARRAY_IDX(cb->commit_items, i, svn_client_commit_item3_t *); 114251881Speter 115251881Speter if (! strcmp(relpath, item->session_relpath)) 116251881Speter { 117251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(item->path)); 118251881Speter local_abspath = item->path; 119251881Speter break; 120251881Speter } 121251881Speter } 122251881Speter 123251881Speter /* Commits can only query relpaths in the commit_items list 124251881Speter since the commit driver traverses paths as they are, or will 125251881Speter be, in the repository. Non-commits query relpaths in the 126251881Speter working copy. */ 127251881Speter if (! local_abspath) 128251881Speter return SVN_NO_ERROR; 129251881Speter } 130251881Speter 131251881Speter /* If we don't have a base directory, then there are no properties. */ 132251881Speter else if (cb->base_dir_abspath == NULL) 133251881Speter return SVN_NO_ERROR; 134251881Speter 135251881Speter else 136251881Speter local_abspath = svn_dirent_join(cb->base_dir_abspath, relpath, pool); 137251881Speter 138251881Speter err = svn_wc_prop_get2(value, cb->ctx->wc_ctx, local_abspath, name, 139251881Speter pool, pool); 140251881Speter if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 141251881Speter { 142251881Speter svn_error_clear(err); 143251881Speter err = NULL; 144251881Speter } 145251881Speter return svn_error_trace(err); 146251881Speter} 147251881Speter 148251881Speter/* This implements the 'svn_ra_push_wc_prop_func_t' interface. */ 149251881Speterstatic svn_error_t * 150251881Speterpush_wc_prop(void *baton, 151251881Speter const char *relpath, 152251881Speter const char *name, 153251881Speter const svn_string_t *value, 154251881Speter apr_pool_t *pool) 155251881Speter{ 156251881Speter callback_baton_t *cb = baton; 157251881Speter int i; 158251881Speter 159251881Speter /* If we're committing, search through the commit_items list for a 160251881Speter match for this relative URL. */ 161251881Speter if (! cb->commit_items) 162251881Speter return svn_error_createf 163251881Speter (SVN_ERR_UNSUPPORTED_FEATURE, NULL, 164251881Speter _("Attempt to set wcprop '%s' on '%s' in a non-commit operation"), 165251881Speter name, svn_dirent_local_style(relpath, pool)); 166251881Speter 167251881Speter for (i = 0; i < cb->commit_items->nelts; i++) 168251881Speter { 169251881Speter svn_client_commit_item3_t *item 170251881Speter = APR_ARRAY_IDX(cb->commit_items, i, svn_client_commit_item3_t *); 171251881Speter 172251881Speter if (strcmp(relpath, item->session_relpath) == 0) 173251881Speter { 174251881Speter apr_pool_t *changes_pool = item->incoming_prop_changes->pool; 175251881Speter svn_prop_t *prop = apr_palloc(changes_pool, sizeof(*prop)); 176251881Speter 177251881Speter prop->name = apr_pstrdup(changes_pool, name); 178251881Speter if (value) 179251881Speter prop->value = svn_string_dup(value, changes_pool); 180251881Speter else 181251881Speter prop->value = NULL; 182251881Speter 183251881Speter /* Buffer the propchange to take effect during the 184251881Speter post-commit process. */ 185251881Speter APR_ARRAY_PUSH(item->incoming_prop_changes, svn_prop_t *) = prop; 186251881Speter return SVN_NO_ERROR; 187251881Speter } 188251881Speter } 189251881Speter 190251881Speter return SVN_NO_ERROR; 191251881Speter} 192251881Speter 193251881Speter 194251881Speter/* This implements the 'svn_ra_set_wc_prop_func_t' interface. */ 195251881Speterstatic svn_error_t * 196251881Speterset_wc_prop(void *baton, 197251881Speter const char *path, 198251881Speter const char *name, 199251881Speter const svn_string_t *value, 200251881Speter apr_pool_t *pool) 201251881Speter{ 202251881Speter callback_baton_t *cb = baton; 203251881Speter const char *local_abspath; 204251881Speter 205251881Speter local_abspath = svn_dirent_join(cb->base_dir_abspath, path, pool); 206251881Speter 207251881Speter /* We pass 1 for the 'force' parameter here. Since the property is 208251881Speter coming from the repository, we definitely want to accept it. 209251881Speter Ideally, we'd raise a conflict if, say, the received property is 210251881Speter svn:eol-style yet the file has a locally added svn:mime-type 211251881Speter claiming that it's binary. Probably the repository is still 212251881Speter right, but the conflict would remind the user to make sure. 213251881Speter Unfortunately, we don't have a clean mechanism for doing that 214251881Speter here, so we just set the property and hope for the best. */ 215251881Speter return svn_error_trace(svn_wc_prop_set4(cb->ctx->wc_ctx, local_abspath, 216251881Speter name, 217251881Speter value, svn_depth_empty, 218251881Speter TRUE /* skip_checks */, 219251881Speter NULL /* changelist_filter */, 220251881Speter NULL, NULL /* cancellation */, 221251881Speter NULL, NULL /* notification */, 222251881Speter pool)); 223251881Speter} 224251881Speter 225251881Speter 226251881Speter/* This implements the `svn_ra_invalidate_wc_props_func_t' interface. */ 227251881Speterstatic svn_error_t * 228251881Speterinvalidate_wc_props(void *baton, 229251881Speter const char *path, 230251881Speter const char *prop_name, 231251881Speter apr_pool_t *pool) 232251881Speter{ 233251881Speter callback_baton_t *cb = baton; 234251881Speter const char *local_abspath; 235251881Speter 236251881Speter local_abspath = svn_dirent_join(cb->base_dir_abspath, path, pool); 237251881Speter 238251881Speter /* It's easier just to clear the whole dav_cache than to remove 239251881Speter individual items from it recursively like this. And since we 240251881Speter know that the RA providers that ship with Subversion only 241251881Speter invalidate the one property they use the most from this cache, 242251881Speter and that we're intentionally trying to get away from the use of 243251881Speter the cache altogether anyway, there's little to lose in wiping the 244251881Speter whole cache. Is it the most well-behaved approach to take? Not 245251881Speter so much. We choose not to care. */ 246251881Speter return svn_error_trace(svn_wc__node_clear_dav_cache_recursive( 247251881Speter cb->ctx->wc_ctx, local_abspath, pool)); 248251881Speter} 249251881Speter 250251881Speter 251251881Speter/* This implements the `svn_ra_get_wc_contents_func_t' interface. */ 252251881Speterstatic svn_error_t * 253251881Speterget_wc_contents(void *baton, 254251881Speter svn_stream_t **contents, 255251881Speter const svn_checksum_t *checksum, 256251881Speter apr_pool_t *pool) 257251881Speter{ 258251881Speter callback_baton_t *cb = baton; 259251881Speter 260251881Speter if (! cb->wcroot_abspath) 261251881Speter { 262251881Speter *contents = NULL; 263251881Speter return SVN_NO_ERROR; 264251881Speter } 265251881Speter 266251881Speter return svn_error_trace( 267251881Speter svn_wc__get_pristine_contents_by_checksum(contents, 268251881Speter cb->ctx->wc_ctx, 269251881Speter cb->wcroot_abspath, 270251881Speter checksum, 271251881Speter pool, pool)); 272251881Speter} 273251881Speter 274251881Speter 275251881Speterstatic svn_error_t * 276251881Spetercancel_callback(void *baton) 277251881Speter{ 278251881Speter callback_baton_t *b = baton; 279251881Speter return svn_error_trace((b->ctx->cancel_func)(b->ctx->cancel_baton)); 280251881Speter} 281251881Speter 282251881Speter 283251881Speterstatic svn_error_t * 284251881Speterget_client_string(void *baton, 285251881Speter const char **name, 286251881Speter apr_pool_t *pool) 287251881Speter{ 288251881Speter callback_baton_t *b = baton; 289251881Speter *name = apr_pstrdup(pool, b->ctx->client_name); 290251881Speter return SVN_NO_ERROR; 291251881Speter} 292251881Speter 293299742Sdim/* Implements svn_ra_progress_notify_func_t. Accumulates progress information 294299742Sdim * for different RA sessions and reports total progress to caller. */ 295299742Sdimstatic void 296299742Sdimprogress_func(apr_off_t progress, 297299742Sdim apr_off_t total, 298299742Sdim void *baton, 299299742Sdim apr_pool_t *pool) 300299742Sdim{ 301299742Sdim callback_baton_t *b = baton; 302299742Sdim svn_client_ctx_t *public_ctx = b->ctx; 303299742Sdim svn_client__private_ctx_t *private_ctx = 304299742Sdim svn_client__get_private_ctx(public_ctx); 305251881Speter 306299742Sdim private_ctx->total_progress += (progress - b->last_progress); 307299742Sdim b->last_progress = progress; 308299742Sdim 309299742Sdim if (public_ctx->progress_func) 310299742Sdim { 311299742Sdim /* All RA implementations currently provide -1 for total. So it doesn't 312299742Sdim make sense to develop some complex logic to combine total across all 313299742Sdim RA sessions. */ 314299742Sdim public_ctx->progress_func(private_ctx->total_progress, -1, 315299742Sdim public_ctx->progress_baton, pool); 316299742Sdim } 317299742Sdim} 318299742Sdim 319251881Speter#define SVN_CLIENT__MAX_REDIRECT_ATTEMPTS 3 /* ### TODO: Make configurable. */ 320251881Speter 321251881Spetersvn_error_t * 322251881Spetersvn_client__open_ra_session_internal(svn_ra_session_t **ra_session, 323251881Speter const char **corrected_url, 324251881Speter const char *base_url, 325251881Speter const char *base_dir_abspath, 326251881Speter const apr_array_header_t *commit_items, 327251881Speter svn_boolean_t write_dav_props, 328251881Speter svn_boolean_t read_dav_props, 329251881Speter svn_client_ctx_t *ctx, 330251881Speter apr_pool_t *result_pool, 331251881Speter apr_pool_t *scratch_pool) 332251881Speter{ 333251881Speter svn_ra_callbacks2_t *cbtable; 334251881Speter callback_baton_t *cb = apr_pcalloc(result_pool, sizeof(*cb)); 335251881Speter const char *uuid = NULL; 336251881Speter 337251881Speter SVN_ERR_ASSERT(!write_dav_props || read_dav_props); 338251881Speter SVN_ERR_ASSERT(!read_dav_props || base_dir_abspath != NULL); 339251881Speter SVN_ERR_ASSERT(base_dir_abspath == NULL 340251881Speter || svn_dirent_is_absolute(base_dir_abspath)); 341251881Speter 342251881Speter SVN_ERR(svn_ra_create_callbacks(&cbtable, result_pool)); 343251881Speter cbtable->open_tmp_file = open_tmp_file; 344251881Speter cbtable->get_wc_prop = read_dav_props ? get_wc_prop : NULL; 345251881Speter cbtable->set_wc_prop = (write_dav_props && read_dav_props) 346251881Speter ? set_wc_prop : NULL; 347251881Speter cbtable->push_wc_prop = commit_items ? push_wc_prop : NULL; 348251881Speter cbtable->invalidate_wc_props = (write_dav_props && read_dav_props) 349251881Speter ? invalidate_wc_props : NULL; 350251881Speter cbtable->auth_baton = ctx->auth_baton; /* new-style */ 351299742Sdim cbtable->progress_func = progress_func; 352299742Sdim cbtable->progress_baton = cb; 353251881Speter cbtable->cancel_func = ctx->cancel_func ? cancel_callback : NULL; 354251881Speter cbtable->get_client_string = get_client_string; 355251881Speter if (base_dir_abspath) 356251881Speter cbtable->get_wc_contents = get_wc_contents; 357299742Sdim cbtable->check_tunnel_func = ctx->check_tunnel_func; 358299742Sdim cbtable->open_tunnel_func = ctx->open_tunnel_func; 359299742Sdim cbtable->tunnel_baton = ctx->tunnel_baton; 360251881Speter 361251881Speter cb->commit_items = commit_items; 362251881Speter cb->ctx = ctx; 363251881Speter 364251881Speter if (base_dir_abspath && (read_dav_props || write_dav_props)) 365251881Speter { 366251881Speter svn_error_t *err = svn_wc__node_get_repos_info(NULL, NULL, NULL, &uuid, 367251881Speter ctx->wc_ctx, 368251881Speter base_dir_abspath, 369251881Speter result_pool, 370251881Speter scratch_pool); 371251881Speter 372251881Speter if (err && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY 373251881Speter || err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND 374251881Speter || err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)) 375251881Speter { 376251881Speter svn_error_clear(err); 377251881Speter uuid = NULL; 378251881Speter } 379251881Speter else 380251881Speter { 381251881Speter SVN_ERR(err); 382251881Speter cb->base_dir_isversioned = TRUE; 383251881Speter } 384251881Speter cb->base_dir_abspath = apr_pstrdup(result_pool, base_dir_abspath); 385251881Speter } 386251881Speter 387251881Speter if (base_dir_abspath) 388251881Speter { 389251881Speter svn_error_t *err = svn_wc__get_wcroot(&cb->wcroot_abspath, 390251881Speter ctx->wc_ctx, base_dir_abspath, 391251881Speter result_pool, scratch_pool); 392251881Speter 393251881Speter if (err) 394251881Speter { 395251881Speter if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY 396251881Speter && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND 397251881Speter && err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED) 398251881Speter return svn_error_trace(err); 399251881Speter 400251881Speter svn_error_clear(err); 401251881Speter cb->wcroot_abspath = NULL; 402251881Speter } 403251881Speter } 404251881Speter 405251881Speter /* If the caller allows for auto-following redirections, and the 406251881Speter RA->open() call above reveals a CORRECTED_URL, try the new URL. 407251881Speter We'll do this in a loop up to some maximum number follow-and-retry 408251881Speter attempts. */ 409251881Speter if (corrected_url) 410251881Speter { 411251881Speter apr_hash_t *attempted = apr_hash_make(scratch_pool); 412251881Speter int attempts_left = SVN_CLIENT__MAX_REDIRECT_ATTEMPTS; 413251881Speter 414251881Speter *corrected_url = NULL; 415251881Speter while (attempts_left--) 416251881Speter { 417251881Speter const char *corrected = NULL; 418251881Speter 419251881Speter /* Try to open the RA session. If this is our last attempt, 420251881Speter don't accept corrected URLs from the RA provider. */ 421251881Speter SVN_ERR(svn_ra_open4(ra_session, 422251881Speter attempts_left == 0 ? NULL : &corrected, 423251881Speter base_url, uuid, cbtable, cb, ctx->config, 424251881Speter result_pool)); 425251881Speter 426251881Speter /* No error and no corrected URL? We're done here. */ 427251881Speter if (! corrected) 428251881Speter break; 429251881Speter 430251881Speter /* Notify the user that a redirect is being followed. */ 431251881Speter if (ctx->notify_func2 != NULL) 432251881Speter { 433251881Speter svn_wc_notify_t *notify = 434251881Speter svn_wc_create_notify_url(corrected, 435251881Speter svn_wc_notify_url_redirect, 436251881Speter scratch_pool); 437299742Sdim ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool); 438251881Speter } 439251881Speter 440251881Speter /* Our caller will want to know what our final corrected URL was. */ 441251881Speter *corrected_url = corrected; 442251881Speter 443251881Speter /* Make sure we've not attempted this URL before. */ 444251881Speter if (svn_hash_gets(attempted, corrected)) 445251881Speter return svn_error_createf(SVN_ERR_CLIENT_CYCLE_DETECTED, NULL, 446251881Speter _("Redirect cycle detected for URL '%s'"), 447251881Speter corrected); 448251881Speter 449251881Speter /* Remember this CORRECTED_URL so we don't wind up in a loop. */ 450251881Speter svn_hash_sets(attempted, corrected, (void *)1); 451251881Speter base_url = corrected; 452251881Speter } 453251881Speter } 454251881Speter else 455251881Speter { 456251881Speter SVN_ERR(svn_ra_open4(ra_session, NULL, base_url, 457251881Speter uuid, cbtable, cb, ctx->config, result_pool)); 458251881Speter } 459251881Speter 460251881Speter return SVN_NO_ERROR; 461251881Speter} 462251881Speter#undef SVN_CLIENT__MAX_REDIRECT_ATTEMPTS 463251881Speter 464251881Speter 465251881Spetersvn_error_t * 466251881Spetersvn_client_open_ra_session2(svn_ra_session_t **session, 467251881Speter const char *url, 468251881Speter const char *wri_abspath, 469251881Speter svn_client_ctx_t *ctx, 470251881Speter apr_pool_t *result_pool, 471251881Speter apr_pool_t *scratch_pool) 472251881Speter{ 473251881Speter return svn_error_trace( 474251881Speter svn_client__open_ra_session_internal(session, NULL, url, 475251881Speter wri_abspath, NULL, 476251881Speter FALSE, FALSE, 477251881Speter ctx, result_pool, 478251881Speter scratch_pool)); 479251881Speter} 480251881Speter 481251881Spetersvn_error_t * 482251881Spetersvn_client__resolve_rev_and_url(svn_client__pathrev_t **resolved_loc_p, 483251881Speter svn_ra_session_t *ra_session, 484251881Speter const char *path_or_url, 485251881Speter const svn_opt_revision_t *peg_revision, 486251881Speter const svn_opt_revision_t *revision, 487251881Speter svn_client_ctx_t *ctx, 488251881Speter apr_pool_t *pool) 489251881Speter{ 490251881Speter svn_opt_revision_t peg_rev = *peg_revision; 491251881Speter svn_opt_revision_t start_rev = *revision; 492251881Speter const char *url; 493251881Speter svn_revnum_t rev; 494251881Speter 495251881Speter /* Default revisions: peg -> working or head; operative -> peg. */ 496251881Speter SVN_ERR(svn_opt_resolve_revisions(&peg_rev, &start_rev, 497251881Speter svn_path_is_url(path_or_url), 498251881Speter TRUE /* notice_local_mods */, 499251881Speter pool)); 500251881Speter 501251881Speter /* Run the history function to get the object's (possibly 502251881Speter different) url in REVISION. */ 503251881Speter SVN_ERR(svn_client__repos_locations(&url, &rev, NULL, NULL, 504251881Speter ra_session, path_or_url, &peg_rev, 505251881Speter &start_rev, NULL, ctx, pool)); 506251881Speter 507251881Speter SVN_ERR(svn_client__pathrev_create_with_session(resolved_loc_p, 508251881Speter ra_session, rev, url, pool)); 509251881Speter return SVN_NO_ERROR; 510251881Speter} 511251881Speter 512251881Spetersvn_error_t * 513251881Spetersvn_client__ra_session_from_path2(svn_ra_session_t **ra_session_p, 514251881Speter svn_client__pathrev_t **resolved_loc_p, 515251881Speter const char *path_or_url, 516251881Speter const char *base_dir_abspath, 517251881Speter const svn_opt_revision_t *peg_revision, 518251881Speter const svn_opt_revision_t *revision, 519251881Speter svn_client_ctx_t *ctx, 520251881Speter apr_pool_t *pool) 521251881Speter{ 522251881Speter svn_ra_session_t *ra_session; 523251881Speter const char *initial_url; 524251881Speter const char *corrected_url; 525251881Speter svn_client__pathrev_t *resolved_loc; 526251881Speter const char *wri_abspath; 527251881Speter 528251881Speter SVN_ERR(svn_client_url_from_path2(&initial_url, path_or_url, ctx, pool, 529251881Speter pool)); 530251881Speter if (! initial_url) 531251881Speter return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, 532251881Speter _("'%s' has no URL"), path_or_url); 533251881Speter 534251881Speter if (base_dir_abspath) 535251881Speter wri_abspath = base_dir_abspath; 536251881Speter else if (!svn_path_is_url(path_or_url)) 537251881Speter SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url, pool)); 538251881Speter else 539251881Speter wri_abspath = NULL; 540251881Speter 541251881Speter SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url, 542251881Speter initial_url, 543251881Speter wri_abspath, 544251881Speter NULL /* commit_items */, 545251881Speter base_dir_abspath != NULL, 546251881Speter base_dir_abspath != NULL, 547251881Speter ctx, pool, pool)); 548251881Speter 549251881Speter /* If we got a CORRECTED_URL, we'll want to refer to that as the 550251881Speter URL-ized form of PATH_OR_URL from now on. */ 551251881Speter if (corrected_url && svn_path_is_url(path_or_url)) 552251881Speter path_or_url = corrected_url; 553251881Speter 554251881Speter SVN_ERR(svn_client__resolve_rev_and_url(&resolved_loc, ra_session, 555251881Speter path_or_url, peg_revision, revision, 556251881Speter ctx, pool)); 557251881Speter 558251881Speter /* Make the session point to the real URL. */ 559251881Speter SVN_ERR(svn_ra_reparent(ra_session, resolved_loc->url, pool)); 560251881Speter 561251881Speter *ra_session_p = ra_session; 562251881Speter if (resolved_loc_p) 563251881Speter *resolved_loc_p = resolved_loc; 564251881Speter 565251881Speter return SVN_NO_ERROR; 566251881Speter} 567251881Speter 568251881Speter 569251881Spetersvn_error_t * 570251881Spetersvn_client__ensure_ra_session_url(const char **old_session_url, 571251881Speter svn_ra_session_t *ra_session, 572251881Speter const char *session_url, 573251881Speter apr_pool_t *pool) 574251881Speter{ 575251881Speter SVN_ERR(svn_ra_get_session_url(ra_session, old_session_url, pool)); 576251881Speter if (! session_url) 577251881Speter SVN_ERR(svn_ra_get_repos_root2(ra_session, &session_url, pool)); 578251881Speter if (strcmp(*old_session_url, session_url) != 0) 579251881Speter SVN_ERR(svn_ra_reparent(ra_session, session_url, pool)); 580251881Speter return SVN_NO_ERROR; 581251881Speter} 582251881Speter 583251881Speter 584251881Speter 585251881Speter/*** Repository Locations ***/ 586251881Speter 587251881Speterstruct gls_receiver_baton_t 588251881Speter{ 589251881Speter apr_array_header_t *segments; 590251881Speter svn_client_ctx_t *ctx; 591251881Speter apr_pool_t *pool; 592251881Speter}; 593251881Speter 594251881Speterstatic svn_error_t * 595251881Spetergls_receiver(svn_location_segment_t *segment, 596251881Speter void *baton, 597251881Speter apr_pool_t *pool) 598251881Speter{ 599251881Speter struct gls_receiver_baton_t *b = baton; 600251881Speter APR_ARRAY_PUSH(b->segments, svn_location_segment_t *) = 601251881Speter svn_location_segment_dup(segment, b->pool); 602251881Speter if (b->ctx->cancel_func) 603251881Speter SVN_ERR((b->ctx->cancel_func)(b->ctx->cancel_baton)); 604251881Speter return SVN_NO_ERROR; 605251881Speter} 606251881Speter 607251881Speter/* A qsort-compatible function which sorts svn_location_segment_t's 608251881Speter based on their revision range covering, resulting in ascending 609251881Speter (oldest-to-youngest) ordering. */ 610251881Speterstatic int 611251881Spetercompare_segments(const void *a, const void *b) 612251881Speter{ 613251881Speter const svn_location_segment_t *a_seg 614251881Speter = *((const svn_location_segment_t * const *) a); 615251881Speter const svn_location_segment_t *b_seg 616251881Speter = *((const svn_location_segment_t * const *) b); 617251881Speter if (a_seg->range_start == b_seg->range_start) 618251881Speter return 0; 619251881Speter return (a_seg->range_start < b_seg->range_start) ? -1 : 1; 620251881Speter} 621251881Speter 622251881Spetersvn_error_t * 623251881Spetersvn_client__repos_location_segments(apr_array_header_t **segments, 624251881Speter svn_ra_session_t *ra_session, 625251881Speter const char *url, 626251881Speter svn_revnum_t peg_revision, 627251881Speter svn_revnum_t start_revision, 628251881Speter svn_revnum_t end_revision, 629251881Speter svn_client_ctx_t *ctx, 630251881Speter apr_pool_t *pool) 631251881Speter{ 632251881Speter struct gls_receiver_baton_t gls_receiver_baton; 633251881Speter const char *old_session_url; 634251881Speter svn_error_t *err; 635251881Speter 636251881Speter *segments = apr_array_make(pool, 8, sizeof(svn_location_segment_t *)); 637251881Speter gls_receiver_baton.segments = *segments; 638251881Speter gls_receiver_baton.ctx = ctx; 639251881Speter gls_receiver_baton.pool = pool; 640251881Speter SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session, 641251881Speter url, pool)); 642251881Speter err = svn_ra_get_location_segments(ra_session, "", peg_revision, 643251881Speter start_revision, end_revision, 644251881Speter gls_receiver, &gls_receiver_baton, 645251881Speter pool); 646251881Speter SVN_ERR(svn_error_compose_create( 647251881Speter err, svn_ra_reparent(ra_session, old_session_url, pool))); 648299742Sdim svn_sort__array(*segments, compare_segments); 649251881Speter return SVN_NO_ERROR; 650251881Speter} 651251881Speter 652251881Speter/* Set *START_URL and *END_URL to the URLs that the object URL@PEG_REVNUM 653251881Speter * had in revisions START_REVNUM and END_REVNUM. Return an error if the 654251881Speter * node cannot be traced back to one of the requested revisions. 655251881Speter * 656251881Speter * START_URL and/or END_URL may be NULL if not wanted. START_REVNUM and 657251881Speter * END_REVNUM must be valid revision numbers except that END_REVNUM may 658251881Speter * be SVN_INVALID_REVNUM if END_URL is NULL. 659251881Speter * 660299742Sdim * YOUNGEST_REV is the already retrieved youngest revision of the ra session, 661299742Sdim * but can be SVN_INVALID_REVNUM if the value is not already retrieved. 662299742Sdim * 663251881Speter * RA_SESSION is an open RA session parented at URL. 664251881Speter */ 665251881Speterstatic svn_error_t * 666251881Speterrepos_locations(const char **start_url, 667251881Speter const char **end_url, 668251881Speter svn_ra_session_t *ra_session, 669251881Speter const char *url, 670251881Speter svn_revnum_t peg_revnum, 671251881Speter svn_revnum_t start_revnum, 672251881Speter svn_revnum_t end_revnum, 673299742Sdim svn_revnum_t youngest_rev, 674251881Speter apr_pool_t *result_pool, 675251881Speter apr_pool_t *scratch_pool) 676251881Speter{ 677251881Speter const char *repos_url, *start_path, *end_path; 678251881Speter apr_array_header_t *revs; 679251881Speter apr_hash_t *rev_locs; 680251881Speter 681299742Sdim SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(peg_revnum)); 682299742Sdim SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start_revnum)); 683299742Sdim SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(end_revnum) || end_url == NULL); 684251881Speter 685251881Speter /* Avoid a network request in the common easy case. */ 686251881Speter if (start_revnum == peg_revnum 687251881Speter && (end_revnum == peg_revnum || end_revnum == SVN_INVALID_REVNUM)) 688251881Speter { 689251881Speter if (start_url) 690251881Speter *start_url = apr_pstrdup(result_pool, url); 691251881Speter if (end_url) 692251881Speter *end_url = apr_pstrdup(result_pool, url); 693251881Speter return SVN_NO_ERROR; 694251881Speter } 695251881Speter 696251881Speter SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, scratch_pool)); 697251881Speter 698299742Sdim /* Handle another common case: The repository root can't move */ 699299742Sdim if (! strcmp(repos_url, url)) 700299742Sdim { 701299742Sdim if (! SVN_IS_VALID_REVNUM(youngest_rev)) 702299742Sdim SVN_ERR(svn_ra_get_latest_revnum(ra_session, &youngest_rev, 703299742Sdim scratch_pool)); 704299742Sdim 705299742Sdim if (start_revnum > youngest_rev 706299742Sdim || (SVN_IS_VALID_REVNUM(end_revnum) && (end_revnum > youngest_rev))) 707299742Sdim return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL, 708299742Sdim _("No such revision %ld"), 709299742Sdim (start_revnum > youngest_rev) 710299742Sdim ? start_revnum : end_revnum); 711299742Sdim 712299742Sdim if (start_url) 713299742Sdim *start_url = apr_pstrdup(result_pool, repos_url); 714299742Sdim if (end_url) 715299742Sdim *end_url = apr_pstrdup(result_pool, repos_url); 716299742Sdim return SVN_NO_ERROR; 717299742Sdim } 718299742Sdim 719251881Speter revs = apr_array_make(scratch_pool, 2, sizeof(svn_revnum_t)); 720251881Speter APR_ARRAY_PUSH(revs, svn_revnum_t) = start_revnum; 721251881Speter if (end_revnum != start_revnum && end_revnum != SVN_INVALID_REVNUM) 722251881Speter APR_ARRAY_PUSH(revs, svn_revnum_t) = end_revnum; 723251881Speter 724251881Speter SVN_ERR(svn_ra_get_locations(ra_session, &rev_locs, "", peg_revnum, 725251881Speter revs, scratch_pool)); 726251881Speter 727251881Speter /* We'd better have all the paths we were looking for! */ 728251881Speter if (start_url) 729251881Speter { 730251881Speter start_path = apr_hash_get(rev_locs, &start_revnum, sizeof(svn_revnum_t)); 731251881Speter if (! start_path) 732251881Speter return svn_error_createf 733251881Speter (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, 734251881Speter _("Unable to find repository location for '%s' in revision %ld"), 735251881Speter url, start_revnum); 736251881Speter *start_url = svn_path_url_add_component2(repos_url, start_path + 1, 737251881Speter result_pool); 738251881Speter } 739251881Speter 740251881Speter if (end_url) 741251881Speter { 742251881Speter end_path = apr_hash_get(rev_locs, &end_revnum, sizeof(svn_revnum_t)); 743251881Speter if (! end_path) 744251881Speter return svn_error_createf 745251881Speter (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, 746251881Speter _("The location for '%s' for revision %ld does not exist in the " 747251881Speter "repository or refers to an unrelated object"), 748251881Speter url, end_revnum); 749251881Speter 750251881Speter *end_url = svn_path_url_add_component2(repos_url, end_path + 1, 751251881Speter result_pool); 752251881Speter } 753251881Speter 754251881Speter return SVN_NO_ERROR; 755251881Speter} 756251881Speter 757251881Spetersvn_error_t * 758251881Spetersvn_client__repos_location(svn_client__pathrev_t **op_loc_p, 759251881Speter svn_ra_session_t *ra_session, 760251881Speter const svn_client__pathrev_t *peg_loc, 761251881Speter svn_revnum_t op_revnum, 762251881Speter svn_client_ctx_t *ctx, 763251881Speter apr_pool_t *result_pool, 764251881Speter apr_pool_t *scratch_pool) 765251881Speter{ 766251881Speter const char *old_session_url; 767251881Speter const char *op_url; 768251881Speter svn_error_t *err; 769251881Speter 770251881Speter SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session, 771251881Speter peg_loc->url, scratch_pool)); 772251881Speter err = repos_locations(&op_url, NULL, ra_session, 773251881Speter peg_loc->url, peg_loc->rev, 774299742Sdim op_revnum, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, 775251881Speter result_pool, scratch_pool); 776251881Speter SVN_ERR(svn_error_compose_create( 777251881Speter err, svn_ra_reparent(ra_session, old_session_url, scratch_pool))); 778251881Speter 779251881Speter *op_loc_p = svn_client__pathrev_create(peg_loc->repos_root_url, 780251881Speter peg_loc->repos_uuid, 781251881Speter op_revnum, op_url, result_pool); 782251881Speter return SVN_NO_ERROR; 783251881Speter} 784251881Speter 785251881Spetersvn_error_t * 786251881Spetersvn_client__repos_locations(const char **start_url, 787251881Speter svn_revnum_t *start_revision, 788251881Speter const char **end_url, 789251881Speter svn_revnum_t *end_revision, 790251881Speter svn_ra_session_t *ra_session, 791251881Speter const char *path, 792251881Speter const svn_opt_revision_t *revision, 793251881Speter const svn_opt_revision_t *start, 794251881Speter const svn_opt_revision_t *end, 795251881Speter svn_client_ctx_t *ctx, 796251881Speter apr_pool_t *pool) 797251881Speter{ 798251881Speter const char *url; 799251881Speter const char *local_abspath_or_url; 800251881Speter svn_revnum_t peg_revnum = SVN_INVALID_REVNUM; 801251881Speter svn_revnum_t start_revnum, end_revnum; 802251881Speter svn_revnum_t youngest_rev = SVN_INVALID_REVNUM; 803251881Speter apr_pool_t *subpool = svn_pool_create(pool); 804251881Speter 805251881Speter /* Ensure that we are given some real revision data to work with. 806251881Speter (It's okay if the END is unspecified -- in that case, we'll just 807251881Speter set it to the same thing as START.) */ 808251881Speter if (revision->kind == svn_opt_revision_unspecified 809251881Speter || start->kind == svn_opt_revision_unspecified) 810251881Speter return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL); 811251881Speter 812251881Speter if (end == NULL) 813251881Speter { 814251881Speter static const svn_opt_revision_t unspecified_rev 815251881Speter = { svn_opt_revision_unspecified, { 0 } }; 816251881Speter 817251881Speter end = &unspecified_rev; 818251881Speter } 819251881Speter 820251881Speter /* Determine LOCAL_ABSPATH_OR_URL, URL, and possibly PEG_REVNUM. 821251881Speter If we are looking at the working version of a WC path that is scheduled 822251881Speter as a copy, then we need to use the copy-from URL and peg revision. */ 823251881Speter if (! svn_path_is_url(path)) 824251881Speter { 825251881Speter SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path, subpool)); 826251881Speter 827251881Speter if (revision->kind == svn_opt_revision_working) 828251881Speter { 829251881Speter const char *repos_root_url; 830251881Speter const char *repos_relpath; 831251881Speter svn_boolean_t is_copy; 832251881Speter 833251881Speter SVN_ERR(svn_wc__node_get_origin(&is_copy, &peg_revnum, &repos_relpath, 834299742Sdim &repos_root_url, NULL, NULL, NULL, 835251881Speter ctx->wc_ctx, local_abspath_or_url, 836251881Speter FALSE, subpool, subpool)); 837251881Speter 838251881Speter if (repos_relpath) 839251881Speter url = svn_path_url_add_component2(repos_root_url, repos_relpath, 840251881Speter pool); 841251881Speter else 842251881Speter url = NULL; 843251881Speter 844251881Speter if (url && is_copy && ra_session) 845251881Speter { 846251881Speter const char *session_url; 847251881Speter SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, 848251881Speter subpool)); 849251881Speter 850251881Speter if (strcmp(session_url, url) != 0) 851251881Speter { 852251881Speter /* We can't use the caller provided RA session now :( */ 853251881Speter ra_session = NULL; 854251881Speter } 855251881Speter } 856251881Speter } 857251881Speter else 858251881Speter url = NULL; 859251881Speter 860251881Speter if (! url) 861251881Speter SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, 862251881Speter local_abspath_or_url, pool, subpool)); 863251881Speter 864251881Speter if (!url) 865251881Speter { 866251881Speter return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, 867251881Speter _("'%s' has no URL"), 868251881Speter svn_dirent_local_style(path, pool)); 869251881Speter } 870251881Speter } 871251881Speter else 872251881Speter { 873251881Speter local_abspath_or_url = path; 874251881Speter url = path; 875251881Speter } 876251881Speter 877251881Speter /* ### We should be smarter here. If the callers just asks for BASE and 878251881Speter WORKING revisions, we should already have the correct URLs, so we 879251881Speter don't need to do anything more here in that case. */ 880251881Speter 881251881Speter /* Open a RA session to this URL if we don't have one already. */ 882251881Speter if (! ra_session) 883251881Speter SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL, 884251881Speter ctx, subpool, subpool)); 885251881Speter 886251881Speter /* Resolve the opt_revision_ts. */ 887251881Speter if (peg_revnum == SVN_INVALID_REVNUM) 888251881Speter SVN_ERR(svn_client__get_revision_number(&peg_revnum, &youngest_rev, 889251881Speter ctx->wc_ctx, local_abspath_or_url, 890251881Speter ra_session, revision, pool)); 891251881Speter 892251881Speter SVN_ERR(svn_client__get_revision_number(&start_revnum, &youngest_rev, 893251881Speter ctx->wc_ctx, local_abspath_or_url, 894251881Speter ra_session, start, pool)); 895251881Speter if (end->kind == svn_opt_revision_unspecified) 896251881Speter end_revnum = start_revnum; 897251881Speter else 898251881Speter SVN_ERR(svn_client__get_revision_number(&end_revnum, &youngest_rev, 899251881Speter ctx->wc_ctx, local_abspath_or_url, 900251881Speter ra_session, end, pool)); 901251881Speter 902251881Speter /* Set the output revision variables. */ 903251881Speter if (start_revision) 904251881Speter { 905251881Speter *start_revision = start_revnum; 906251881Speter } 907251881Speter if (end_revision && end->kind != svn_opt_revision_unspecified) 908251881Speter { 909251881Speter *end_revision = end_revnum; 910251881Speter } 911251881Speter 912251881Speter SVN_ERR(repos_locations(start_url, end_url, 913251881Speter ra_session, url, peg_revnum, 914299742Sdim start_revnum, end_revnum, youngest_rev, 915251881Speter pool, subpool)); 916251881Speter svn_pool_destroy(subpool); 917251881Speter return SVN_NO_ERROR; 918251881Speter} 919251881Speter 920251881Spetersvn_error_t * 921253734Spetersvn_client__calc_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p, 922253734Speter const svn_client__pathrev_t *loc1, 923253734Speter apr_hash_t *history1, 924253734Speter svn_boolean_t has_rev_zero_history1, 925253734Speter const svn_client__pathrev_t *loc2, 926253734Speter apr_hash_t *history2, 927253734Speter svn_boolean_t has_rev_zero_history2, 928253734Speter apr_pool_t *result_pool, 929253734Speter apr_pool_t *scratch_pool) 930251881Speter{ 931251881Speter apr_hash_index_t *hi; 932251881Speter svn_revnum_t yc_revision = SVN_INVALID_REVNUM; 933251881Speter const char *yc_relpath = NULL; 934251881Speter 935251881Speter if (strcmp(loc1->repos_root_url, loc2->repos_root_url) != 0) 936251881Speter { 937251881Speter *ancestor_p = NULL; 938251881Speter return SVN_NO_ERROR; 939251881Speter } 940251881Speter 941251881Speter /* Loop through the first location's history, check for overlapping 942251881Speter paths and ranges in the second location's history, and 943251881Speter remembering the youngest matching location. */ 944251881Speter for (hi = apr_hash_first(scratch_pool, history1); hi; hi = apr_hash_next(hi)) 945251881Speter { 946299742Sdim const char *path = apr_hash_this_key(hi); 947299742Sdim apr_ssize_t path_len = apr_hash_this_key_len(hi); 948299742Sdim svn_rangelist_t *ranges1 = apr_hash_this_val(hi); 949251881Speter svn_rangelist_t *ranges2, *common; 950251881Speter 951251881Speter ranges2 = apr_hash_get(history2, path, path_len); 952251881Speter if (ranges2) 953251881Speter { 954251881Speter /* We have a path match. Now, did our two histories share 955251881Speter any revisions at that path? */ 956251881Speter SVN_ERR(svn_rangelist_intersect(&common, ranges1, ranges2, 957251881Speter TRUE, scratch_pool)); 958251881Speter if (common->nelts) 959251881Speter { 960251881Speter svn_merge_range_t *yc_range = 961251881Speter APR_ARRAY_IDX(common, common->nelts - 1, svn_merge_range_t *); 962251881Speter if ((! SVN_IS_VALID_REVNUM(yc_revision)) 963251881Speter || (yc_range->end > yc_revision)) 964251881Speter { 965251881Speter yc_revision = yc_range->end; 966251881Speter yc_relpath = path + 1; 967251881Speter } 968251881Speter } 969251881Speter } 970251881Speter } 971251881Speter 972251881Speter /* It's possible that PATH_OR_URL1 and PATH_OR_URL2's only common 973251881Speter history is revision 0. */ 974251881Speter if (!yc_relpath && has_rev_zero_history1 && has_rev_zero_history2) 975251881Speter { 976251881Speter yc_relpath = ""; 977251881Speter yc_revision = 0; 978251881Speter } 979251881Speter 980251881Speter if (yc_relpath) 981251881Speter { 982251881Speter *ancestor_p = svn_client__pathrev_create_with_relpath( 983251881Speter loc1->repos_root_url, loc1->repos_uuid, 984251881Speter yc_revision, yc_relpath, result_pool); 985251881Speter } 986251881Speter else 987251881Speter { 988251881Speter *ancestor_p = NULL; 989251881Speter } 990251881Speter return SVN_NO_ERROR; 991251881Speter} 992251881Speter 993251881Spetersvn_error_t * 994253734Spetersvn_client__get_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p, 995253734Speter const svn_client__pathrev_t *loc1, 996253734Speter const svn_client__pathrev_t *loc2, 997253734Speter svn_ra_session_t *session, 998253734Speter svn_client_ctx_t *ctx, 999253734Speter apr_pool_t *result_pool, 1000253734Speter apr_pool_t *scratch_pool) 1001253734Speter{ 1002253734Speter apr_pool_t *sesspool = NULL; 1003253734Speter apr_hash_t *history1, *history2; 1004253734Speter svn_boolean_t has_rev_zero_history1; 1005253734Speter svn_boolean_t has_rev_zero_history2; 1006253734Speter 1007253734Speter if (strcmp(loc1->repos_root_url, loc2->repos_root_url) != 0) 1008253734Speter { 1009253734Speter *ancestor_p = NULL; 1010253734Speter return SVN_NO_ERROR; 1011253734Speter } 1012253734Speter 1013253734Speter /* Open an RA session for the two locations. */ 1014253734Speter if (session == NULL) 1015253734Speter { 1016253734Speter sesspool = svn_pool_create(scratch_pool); 1017253734Speter SVN_ERR(svn_client_open_ra_session2(&session, loc1->url, NULL, ctx, 1018253734Speter sesspool, sesspool)); 1019253734Speter } 1020253734Speter 1021253734Speter /* We're going to cheat and use history-as-mergeinfo because it 1022253734Speter saves us a bunch of annoying custom data comparisons and such. */ 1023253734Speter SVN_ERR(svn_client__get_history_as_mergeinfo(&history1, 1024253734Speter &has_rev_zero_history1, 1025253734Speter loc1, 1026253734Speter SVN_INVALID_REVNUM, 1027253734Speter SVN_INVALID_REVNUM, 1028253734Speter session, ctx, scratch_pool)); 1029253734Speter SVN_ERR(svn_client__get_history_as_mergeinfo(&history2, 1030253734Speter &has_rev_zero_history2, 1031253734Speter loc2, 1032253734Speter SVN_INVALID_REVNUM, 1033253734Speter SVN_INVALID_REVNUM, 1034253734Speter session, ctx, scratch_pool)); 1035253734Speter /* Close the ra session if we opened one. */ 1036253734Speter if (sesspool) 1037253734Speter svn_pool_destroy(sesspool); 1038253734Speter 1039253734Speter SVN_ERR(svn_client__calc_youngest_common_ancestor(ancestor_p, 1040253734Speter loc1, history1, 1041253734Speter has_rev_zero_history1, 1042253734Speter loc2, history2, 1043253734Speter has_rev_zero_history2, 1044253734Speter result_pool, 1045253734Speter scratch_pool)); 1046253734Speter 1047253734Speter return SVN_NO_ERROR; 1048253734Speter} 1049253734Speter 1050251881Speterstruct ra_ev2_baton { 1051251881Speter /* The working copy context, from the client context. */ 1052251881Speter svn_wc_context_t *wc_ctx; 1053251881Speter 1054251881Speter /* For a given REPOS_RELPATH, provide a LOCAL_ABSPATH that represents 1055251881Speter that repository node. */ 1056251881Speter apr_hash_t *relpath_map; 1057251881Speter}; 1058251881Speter 1059251881Speter 1060251881Spetersvn_error_t * 1061251881Spetersvn_client__ra_provide_base(svn_stream_t **contents, 1062251881Speter svn_revnum_t *revision, 1063251881Speter void *baton, 1064251881Speter const char *repos_relpath, 1065251881Speter apr_pool_t *result_pool, 1066251881Speter apr_pool_t *scratch_pool) 1067251881Speter{ 1068251881Speter struct ra_ev2_baton *reb = baton; 1069251881Speter const char *local_abspath; 1070251881Speter svn_error_t *err; 1071251881Speter 1072251881Speter local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath); 1073251881Speter if (!local_abspath) 1074251881Speter { 1075251881Speter *contents = NULL; 1076251881Speter return SVN_NO_ERROR; 1077251881Speter } 1078251881Speter 1079251881Speter err = svn_wc_get_pristine_contents2(contents, reb->wc_ctx, local_abspath, 1080251881Speter result_pool, scratch_pool); 1081251881Speter if (err) 1082251881Speter { 1083251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1084251881Speter return svn_error_trace(err); 1085251881Speter 1086251881Speter svn_error_clear(err); 1087251881Speter *contents = NULL; 1088251881Speter return SVN_NO_ERROR; 1089251881Speter } 1090251881Speter 1091251881Speter if (*contents != NULL) 1092251881Speter { 1093251881Speter /* The pristine contents refer to the BASE, or to the pristine of 1094251881Speter a copy/move to this location. Fetch the correct revision. */ 1095251881Speter SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL, 1096299742Sdim NULL, 1097251881Speter reb->wc_ctx, local_abspath, FALSE, 1098251881Speter scratch_pool, scratch_pool)); 1099251881Speter } 1100251881Speter 1101251881Speter return SVN_NO_ERROR; 1102251881Speter} 1103251881Speter 1104251881Speter 1105251881Spetersvn_error_t * 1106251881Spetersvn_client__ra_provide_props(apr_hash_t **props, 1107251881Speter svn_revnum_t *revision, 1108251881Speter void *baton, 1109251881Speter const char *repos_relpath, 1110251881Speter apr_pool_t *result_pool, 1111251881Speter apr_pool_t *scratch_pool) 1112251881Speter{ 1113251881Speter struct ra_ev2_baton *reb = baton; 1114251881Speter const char *local_abspath; 1115251881Speter svn_error_t *err; 1116251881Speter 1117251881Speter local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath); 1118251881Speter if (!local_abspath) 1119251881Speter { 1120251881Speter *props = NULL; 1121251881Speter return SVN_NO_ERROR; 1122251881Speter } 1123251881Speter 1124251881Speter err = svn_wc_get_pristine_props(props, reb->wc_ctx, local_abspath, 1125251881Speter result_pool, scratch_pool); 1126251881Speter if (err) 1127251881Speter { 1128251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1129251881Speter return svn_error_trace(err); 1130251881Speter 1131251881Speter svn_error_clear(err); 1132251881Speter *props = NULL; 1133251881Speter return SVN_NO_ERROR; 1134251881Speter } 1135251881Speter 1136251881Speter if (*props != NULL) 1137251881Speter { 1138251881Speter /* The pristine props refer to the BASE, or to the pristine props of 1139251881Speter a copy/move to this location. Fetch the correct revision. */ 1140251881Speter SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL, 1141299742Sdim NULL, 1142251881Speter reb->wc_ctx, local_abspath, FALSE, 1143251881Speter scratch_pool, scratch_pool)); 1144251881Speter } 1145251881Speter 1146251881Speter return SVN_NO_ERROR; 1147251881Speter} 1148251881Speter 1149251881Speter 1150251881Spetersvn_error_t * 1151251881Spetersvn_client__ra_get_copysrc_kind(svn_node_kind_t *kind, 1152251881Speter void *baton, 1153251881Speter const char *repos_relpath, 1154251881Speter svn_revnum_t src_revision, 1155251881Speter apr_pool_t *scratch_pool) 1156251881Speter{ 1157251881Speter struct ra_ev2_baton *reb = baton; 1158251881Speter const char *local_abspath; 1159251881Speter 1160251881Speter local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath); 1161251881Speter if (!local_abspath) 1162251881Speter { 1163251881Speter *kind = svn_node_unknown; 1164251881Speter return SVN_NO_ERROR; 1165251881Speter } 1166251881Speter 1167251881Speter /* ### what to do with SRC_REVISION? */ 1168251881Speter 1169251881Speter SVN_ERR(svn_wc_read_kind2(kind, reb->wc_ctx, local_abspath, 1170251881Speter FALSE, FALSE, scratch_pool)); 1171251881Speter 1172251881Speter return SVN_NO_ERROR; 1173251881Speter} 1174251881Speter 1175251881Speter 1176251881Spetervoid * 1177251881Spetersvn_client__ra_make_cb_baton(svn_wc_context_t *wc_ctx, 1178251881Speter apr_hash_t *relpath_map, 1179251881Speter apr_pool_t *result_pool) 1180251881Speter{ 1181251881Speter struct ra_ev2_baton *reb = apr_palloc(result_pool, sizeof(*reb)); 1182251881Speter 1183251881Speter SVN_ERR_ASSERT_NO_RETURN(wc_ctx != NULL); 1184251881Speter SVN_ERR_ASSERT_NO_RETURN(relpath_map != NULL); 1185251881Speter 1186251881Speter reb->wc_ctx = wc_ctx; 1187251881Speter reb->relpath_map = relpath_map; 1188251881Speter 1189251881Speter return reb; 1190251881Speter} 1191