ra.c revision 253734
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" 45251881Speter 46251881Speter 47251881Speter/* This is the baton that we pass svn_ra_open3(), and is associated with 48251881Speter the callback table we provide to RA. */ 49251881Spetertypedef struct callback_baton_t 50251881Speter{ 51251881Speter /* Holds the directory that corresponds to the REPOS_URL at svn_ra_open3() 52251881Speter time. When callbacks specify a relative path, they are joined with 53251881Speter this base directory. */ 54251881Speter const char *base_dir_abspath; 55251881Speter 56251881Speter /* TEMPORARY: Is 'base_dir_abspath' a versioned path? cmpilato 57251881Speter suspects that the commit-to-multiple-disjoint-working-copies 58251881Speter code is getting this all wrong, sometimes passing an unversioned 59251881Speter (or versioned in a foreign wc) path here which sorta kinda 60251881Speter happens to work most of the time but is ultimately incorrect. */ 61251881Speter svn_boolean_t base_dir_isversioned; 62251881Speter 63251881Speter /* Used as wri_abspath for obtaining access to the pristine store */ 64251881Speter const char *wcroot_abspath; 65251881Speter 66251881Speter /* An array of svn_client_commit_item3_t * structures, present only 67251881Speter during working copy commits. */ 68251881Speter const apr_array_header_t *commit_items; 69251881Speter 70251881Speter /* A client context. */ 71251881Speter svn_client_ctx_t *ctx; 72251881Speter 73251881Speter} callback_baton_t; 74251881Speter 75251881Speter 76251881Speter 77251881Speterstatic svn_error_t * 78251881Speteropen_tmp_file(apr_file_t **fp, 79251881Speter void *callback_baton, 80251881Speter apr_pool_t *pool) 81251881Speter{ 82251881Speter return svn_error_trace(svn_io_open_unique_file3(fp, NULL, NULL, 83251881Speter svn_io_file_del_on_pool_cleanup, 84251881Speter pool, pool)); 85251881Speter} 86251881Speter 87251881Speter 88251881Speter/* This implements the 'svn_ra_get_wc_prop_func_t' interface. */ 89251881Speterstatic svn_error_t * 90251881Speterget_wc_prop(void *baton, 91251881Speter const char *relpath, 92251881Speter const char *name, 93251881Speter const svn_string_t **value, 94251881Speter apr_pool_t *pool) 95251881Speter{ 96251881Speter callback_baton_t *cb = baton; 97251881Speter const char *local_abspath = NULL; 98251881Speter svn_error_t *err; 99251881Speter 100251881Speter *value = NULL; 101251881Speter 102251881Speter /* If we have a list of commit_items, search through that for a 103251881Speter match for this relative URL. */ 104251881Speter if (cb->commit_items) 105251881Speter { 106251881Speter int i; 107251881Speter for (i = 0; i < cb->commit_items->nelts; i++) 108251881Speter { 109251881Speter svn_client_commit_item3_t *item 110251881Speter = APR_ARRAY_IDX(cb->commit_items, i, svn_client_commit_item3_t *); 111251881Speter 112251881Speter if (! strcmp(relpath, item->session_relpath)) 113251881Speter { 114251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(item->path)); 115251881Speter local_abspath = item->path; 116251881Speter break; 117251881Speter } 118251881Speter } 119251881Speter 120251881Speter /* Commits can only query relpaths in the commit_items list 121251881Speter since the commit driver traverses paths as they are, or will 122251881Speter be, in the repository. Non-commits query relpaths in the 123251881Speter working copy. */ 124251881Speter if (! local_abspath) 125251881Speter return SVN_NO_ERROR; 126251881Speter } 127251881Speter 128251881Speter /* If we don't have a base directory, then there are no properties. */ 129251881Speter else if (cb->base_dir_abspath == NULL) 130251881Speter return SVN_NO_ERROR; 131251881Speter 132251881Speter else 133251881Speter local_abspath = svn_dirent_join(cb->base_dir_abspath, relpath, pool); 134251881Speter 135251881Speter err = svn_wc_prop_get2(value, cb->ctx->wc_ctx, local_abspath, name, 136251881Speter pool, pool); 137251881Speter if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 138251881Speter { 139251881Speter svn_error_clear(err); 140251881Speter err = NULL; 141251881Speter } 142251881Speter return svn_error_trace(err); 143251881Speter} 144251881Speter 145251881Speter/* This implements the 'svn_ra_push_wc_prop_func_t' interface. */ 146251881Speterstatic svn_error_t * 147251881Speterpush_wc_prop(void *baton, 148251881Speter const char *relpath, 149251881Speter const char *name, 150251881Speter const svn_string_t *value, 151251881Speter apr_pool_t *pool) 152251881Speter{ 153251881Speter callback_baton_t *cb = baton; 154251881Speter int i; 155251881Speter 156251881Speter /* If we're committing, search through the commit_items list for a 157251881Speter match for this relative URL. */ 158251881Speter if (! cb->commit_items) 159251881Speter return svn_error_createf 160251881Speter (SVN_ERR_UNSUPPORTED_FEATURE, NULL, 161251881Speter _("Attempt to set wcprop '%s' on '%s' in a non-commit operation"), 162251881Speter name, svn_dirent_local_style(relpath, pool)); 163251881Speter 164251881Speter for (i = 0; i < cb->commit_items->nelts; i++) 165251881Speter { 166251881Speter svn_client_commit_item3_t *item 167251881Speter = APR_ARRAY_IDX(cb->commit_items, i, svn_client_commit_item3_t *); 168251881Speter 169251881Speter if (strcmp(relpath, item->session_relpath) == 0) 170251881Speter { 171251881Speter apr_pool_t *changes_pool = item->incoming_prop_changes->pool; 172251881Speter svn_prop_t *prop = apr_palloc(changes_pool, sizeof(*prop)); 173251881Speter 174251881Speter prop->name = apr_pstrdup(changes_pool, name); 175251881Speter if (value) 176251881Speter prop->value = svn_string_dup(value, changes_pool); 177251881Speter else 178251881Speter prop->value = NULL; 179251881Speter 180251881Speter /* Buffer the propchange to take effect during the 181251881Speter post-commit process. */ 182251881Speter APR_ARRAY_PUSH(item->incoming_prop_changes, svn_prop_t *) = prop; 183251881Speter return SVN_NO_ERROR; 184251881Speter } 185251881Speter } 186251881Speter 187251881Speter return SVN_NO_ERROR; 188251881Speter} 189251881Speter 190251881Speter 191251881Speter/* This implements the 'svn_ra_set_wc_prop_func_t' interface. */ 192251881Speterstatic svn_error_t * 193251881Speterset_wc_prop(void *baton, 194251881Speter const char *path, 195251881Speter const char *name, 196251881Speter const svn_string_t *value, 197251881Speter apr_pool_t *pool) 198251881Speter{ 199251881Speter callback_baton_t *cb = baton; 200251881Speter const char *local_abspath; 201251881Speter 202251881Speter local_abspath = svn_dirent_join(cb->base_dir_abspath, path, pool); 203251881Speter 204251881Speter /* We pass 1 for the 'force' parameter here. Since the property is 205251881Speter coming from the repository, we definitely want to accept it. 206251881Speter Ideally, we'd raise a conflict if, say, the received property is 207251881Speter svn:eol-style yet the file has a locally added svn:mime-type 208251881Speter claiming that it's binary. Probably the repository is still 209251881Speter right, but the conflict would remind the user to make sure. 210251881Speter Unfortunately, we don't have a clean mechanism for doing that 211251881Speter here, so we just set the property and hope for the best. */ 212251881Speter return svn_error_trace(svn_wc_prop_set4(cb->ctx->wc_ctx, local_abspath, 213251881Speter name, 214251881Speter value, svn_depth_empty, 215251881Speter TRUE /* skip_checks */, 216251881Speter NULL /* changelist_filter */, 217251881Speter NULL, NULL /* cancellation */, 218251881Speter NULL, NULL /* notification */, 219251881Speter pool)); 220251881Speter} 221251881Speter 222251881Speter 223251881Speter/* This implements the `svn_ra_invalidate_wc_props_func_t' interface. */ 224251881Speterstatic svn_error_t * 225251881Speterinvalidate_wc_props(void *baton, 226251881Speter const char *path, 227251881Speter const char *prop_name, 228251881Speter apr_pool_t *pool) 229251881Speter{ 230251881Speter callback_baton_t *cb = baton; 231251881Speter const char *local_abspath; 232251881Speter 233251881Speter local_abspath = svn_dirent_join(cb->base_dir_abspath, path, pool); 234251881Speter 235251881Speter /* It's easier just to clear the whole dav_cache than to remove 236251881Speter individual items from it recursively like this. And since we 237251881Speter know that the RA providers that ship with Subversion only 238251881Speter invalidate the one property they use the most from this cache, 239251881Speter and that we're intentionally trying to get away from the use of 240251881Speter the cache altogether anyway, there's little to lose in wiping the 241251881Speter whole cache. Is it the most well-behaved approach to take? Not 242251881Speter so much. We choose not to care. */ 243251881Speter return svn_error_trace(svn_wc__node_clear_dav_cache_recursive( 244251881Speter cb->ctx->wc_ctx, local_abspath, pool)); 245251881Speter} 246251881Speter 247251881Speter 248251881Speter/* This implements the `svn_ra_get_wc_contents_func_t' interface. */ 249251881Speterstatic svn_error_t * 250251881Speterget_wc_contents(void *baton, 251251881Speter svn_stream_t **contents, 252251881Speter const svn_checksum_t *checksum, 253251881Speter apr_pool_t *pool) 254251881Speter{ 255251881Speter callback_baton_t *cb = baton; 256251881Speter 257251881Speter if (! cb->wcroot_abspath) 258251881Speter { 259251881Speter *contents = NULL; 260251881Speter return SVN_NO_ERROR; 261251881Speter } 262251881Speter 263251881Speter return svn_error_trace( 264251881Speter svn_wc__get_pristine_contents_by_checksum(contents, 265251881Speter cb->ctx->wc_ctx, 266251881Speter cb->wcroot_abspath, 267251881Speter checksum, 268251881Speter pool, pool)); 269251881Speter} 270251881Speter 271251881Speter 272251881Speterstatic svn_error_t * 273251881Spetercancel_callback(void *baton) 274251881Speter{ 275251881Speter callback_baton_t *b = baton; 276251881Speter return svn_error_trace((b->ctx->cancel_func)(b->ctx->cancel_baton)); 277251881Speter} 278251881Speter 279251881Speter 280251881Speterstatic svn_error_t * 281251881Speterget_client_string(void *baton, 282251881Speter const char **name, 283251881Speter apr_pool_t *pool) 284251881Speter{ 285251881Speter callback_baton_t *b = baton; 286251881Speter *name = apr_pstrdup(pool, b->ctx->client_name); 287251881Speter return SVN_NO_ERROR; 288251881Speter} 289251881Speter 290251881Speter 291251881Speter#define SVN_CLIENT__MAX_REDIRECT_ATTEMPTS 3 /* ### TODO: Make configurable. */ 292251881Speter 293251881Spetersvn_error_t * 294251881Spetersvn_client__open_ra_session_internal(svn_ra_session_t **ra_session, 295251881Speter const char **corrected_url, 296251881Speter const char *base_url, 297251881Speter const char *base_dir_abspath, 298251881Speter const apr_array_header_t *commit_items, 299251881Speter svn_boolean_t write_dav_props, 300251881Speter svn_boolean_t read_dav_props, 301251881Speter svn_client_ctx_t *ctx, 302251881Speter apr_pool_t *result_pool, 303251881Speter apr_pool_t *scratch_pool) 304251881Speter{ 305251881Speter svn_ra_callbacks2_t *cbtable; 306251881Speter callback_baton_t *cb = apr_pcalloc(result_pool, sizeof(*cb)); 307251881Speter const char *uuid = NULL; 308251881Speter 309251881Speter SVN_ERR_ASSERT(!write_dav_props || read_dav_props); 310251881Speter SVN_ERR_ASSERT(!read_dav_props || base_dir_abspath != NULL); 311251881Speter SVN_ERR_ASSERT(base_dir_abspath == NULL 312251881Speter || svn_dirent_is_absolute(base_dir_abspath)); 313251881Speter 314251881Speter SVN_ERR(svn_ra_create_callbacks(&cbtable, result_pool)); 315251881Speter cbtable->open_tmp_file = open_tmp_file; 316251881Speter cbtable->get_wc_prop = read_dav_props ? get_wc_prop : NULL; 317251881Speter cbtable->set_wc_prop = (write_dav_props && read_dav_props) 318251881Speter ? set_wc_prop : NULL; 319251881Speter cbtable->push_wc_prop = commit_items ? push_wc_prop : NULL; 320251881Speter cbtable->invalidate_wc_props = (write_dav_props && read_dav_props) 321251881Speter ? invalidate_wc_props : NULL; 322251881Speter cbtable->auth_baton = ctx->auth_baton; /* new-style */ 323251881Speter cbtable->progress_func = ctx->progress_func; 324251881Speter cbtable->progress_baton = ctx->progress_baton; 325251881Speter cbtable->cancel_func = ctx->cancel_func ? cancel_callback : NULL; 326251881Speter cbtable->get_client_string = get_client_string; 327251881Speter if (base_dir_abspath) 328251881Speter cbtable->get_wc_contents = get_wc_contents; 329251881Speter 330251881Speter cb->commit_items = commit_items; 331251881Speter cb->ctx = ctx; 332251881Speter 333251881Speter if (base_dir_abspath && (read_dav_props || write_dav_props)) 334251881Speter { 335251881Speter svn_error_t *err = svn_wc__node_get_repos_info(NULL, NULL, NULL, &uuid, 336251881Speter ctx->wc_ctx, 337251881Speter base_dir_abspath, 338251881Speter result_pool, 339251881Speter scratch_pool); 340251881Speter 341251881Speter if (err && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY 342251881Speter || err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND 343251881Speter || err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)) 344251881Speter { 345251881Speter svn_error_clear(err); 346251881Speter uuid = NULL; 347251881Speter } 348251881Speter else 349251881Speter { 350251881Speter SVN_ERR(err); 351251881Speter cb->base_dir_isversioned = TRUE; 352251881Speter } 353251881Speter cb->base_dir_abspath = apr_pstrdup(result_pool, base_dir_abspath); 354251881Speter } 355251881Speter 356251881Speter if (base_dir_abspath) 357251881Speter { 358251881Speter svn_error_t *err = svn_wc__get_wcroot(&cb->wcroot_abspath, 359251881Speter ctx->wc_ctx, base_dir_abspath, 360251881Speter result_pool, scratch_pool); 361251881Speter 362251881Speter if (err) 363251881Speter { 364251881Speter if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY 365251881Speter && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND 366251881Speter && err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED) 367251881Speter return svn_error_trace(err); 368251881Speter 369251881Speter svn_error_clear(err); 370251881Speter cb->wcroot_abspath = NULL; 371251881Speter } 372251881Speter } 373251881Speter 374251881Speter /* If the caller allows for auto-following redirections, and the 375251881Speter RA->open() call above reveals a CORRECTED_URL, try the new URL. 376251881Speter We'll do this in a loop up to some maximum number follow-and-retry 377251881Speter attempts. */ 378251881Speter if (corrected_url) 379251881Speter { 380251881Speter apr_hash_t *attempted = apr_hash_make(scratch_pool); 381251881Speter int attempts_left = SVN_CLIENT__MAX_REDIRECT_ATTEMPTS; 382251881Speter 383251881Speter *corrected_url = NULL; 384251881Speter while (attempts_left--) 385251881Speter { 386251881Speter const char *corrected = NULL; 387251881Speter 388251881Speter /* Try to open the RA session. If this is our last attempt, 389251881Speter don't accept corrected URLs from the RA provider. */ 390251881Speter SVN_ERR(svn_ra_open4(ra_session, 391251881Speter attempts_left == 0 ? NULL : &corrected, 392251881Speter base_url, uuid, cbtable, cb, ctx->config, 393251881Speter result_pool)); 394251881Speter 395251881Speter /* No error and no corrected URL? We're done here. */ 396251881Speter if (! corrected) 397251881Speter break; 398251881Speter 399251881Speter /* Notify the user that a redirect is being followed. */ 400251881Speter if (ctx->notify_func2 != NULL) 401251881Speter { 402251881Speter svn_wc_notify_t *notify = 403251881Speter svn_wc_create_notify_url(corrected, 404251881Speter svn_wc_notify_url_redirect, 405251881Speter scratch_pool); 406251881Speter (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool); 407251881Speter } 408251881Speter 409251881Speter /* Our caller will want to know what our final corrected URL was. */ 410251881Speter *corrected_url = corrected; 411251881Speter 412251881Speter /* Make sure we've not attempted this URL before. */ 413251881Speter if (svn_hash_gets(attempted, corrected)) 414251881Speter return svn_error_createf(SVN_ERR_CLIENT_CYCLE_DETECTED, NULL, 415251881Speter _("Redirect cycle detected for URL '%s'"), 416251881Speter corrected); 417251881Speter 418251881Speter /* Remember this CORRECTED_URL so we don't wind up in a loop. */ 419251881Speter svn_hash_sets(attempted, corrected, (void *)1); 420251881Speter base_url = corrected; 421251881Speter } 422251881Speter } 423251881Speter else 424251881Speter { 425251881Speter SVN_ERR(svn_ra_open4(ra_session, NULL, base_url, 426251881Speter uuid, cbtable, cb, ctx->config, result_pool)); 427251881Speter } 428251881Speter 429251881Speter return SVN_NO_ERROR; 430251881Speter} 431251881Speter#undef SVN_CLIENT__MAX_REDIRECT_ATTEMPTS 432251881Speter 433251881Speter 434251881Spetersvn_error_t * 435251881Spetersvn_client_open_ra_session2(svn_ra_session_t **session, 436251881Speter const char *url, 437251881Speter const char *wri_abspath, 438251881Speter svn_client_ctx_t *ctx, 439251881Speter apr_pool_t *result_pool, 440251881Speter apr_pool_t *scratch_pool) 441251881Speter{ 442251881Speter return svn_error_trace( 443251881Speter svn_client__open_ra_session_internal(session, NULL, url, 444251881Speter wri_abspath, NULL, 445251881Speter FALSE, FALSE, 446251881Speter ctx, result_pool, 447251881Speter scratch_pool)); 448251881Speter} 449251881Speter 450251881Spetersvn_error_t * 451251881Spetersvn_client__resolve_rev_and_url(svn_client__pathrev_t **resolved_loc_p, 452251881Speter svn_ra_session_t *ra_session, 453251881Speter const char *path_or_url, 454251881Speter const svn_opt_revision_t *peg_revision, 455251881Speter const svn_opt_revision_t *revision, 456251881Speter svn_client_ctx_t *ctx, 457251881Speter apr_pool_t *pool) 458251881Speter{ 459251881Speter svn_opt_revision_t peg_rev = *peg_revision; 460251881Speter svn_opt_revision_t start_rev = *revision; 461251881Speter const char *url; 462251881Speter svn_revnum_t rev; 463251881Speter 464251881Speter /* Default revisions: peg -> working or head; operative -> peg. */ 465251881Speter SVN_ERR(svn_opt_resolve_revisions(&peg_rev, &start_rev, 466251881Speter svn_path_is_url(path_or_url), 467251881Speter TRUE /* notice_local_mods */, 468251881Speter pool)); 469251881Speter 470251881Speter /* Run the history function to get the object's (possibly 471251881Speter different) url in REVISION. */ 472251881Speter SVN_ERR(svn_client__repos_locations(&url, &rev, NULL, NULL, 473251881Speter ra_session, path_or_url, &peg_rev, 474251881Speter &start_rev, NULL, ctx, pool)); 475251881Speter 476251881Speter SVN_ERR(svn_client__pathrev_create_with_session(resolved_loc_p, 477251881Speter ra_session, rev, url, pool)); 478251881Speter return SVN_NO_ERROR; 479251881Speter} 480251881Speter 481251881Spetersvn_error_t * 482251881Spetersvn_client__ra_session_from_path2(svn_ra_session_t **ra_session_p, 483251881Speter svn_client__pathrev_t **resolved_loc_p, 484251881Speter const char *path_or_url, 485251881Speter const char *base_dir_abspath, 486251881Speter const svn_opt_revision_t *peg_revision, 487251881Speter const svn_opt_revision_t *revision, 488251881Speter svn_client_ctx_t *ctx, 489251881Speter apr_pool_t *pool) 490251881Speter{ 491251881Speter svn_ra_session_t *ra_session; 492251881Speter const char *initial_url; 493251881Speter const char *corrected_url; 494251881Speter svn_client__pathrev_t *resolved_loc; 495251881Speter const char *wri_abspath; 496251881Speter 497251881Speter SVN_ERR(svn_client_url_from_path2(&initial_url, path_or_url, ctx, pool, 498251881Speter pool)); 499251881Speter if (! initial_url) 500251881Speter return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, 501251881Speter _("'%s' has no URL"), path_or_url); 502251881Speter 503251881Speter if (base_dir_abspath) 504251881Speter wri_abspath = base_dir_abspath; 505251881Speter else if (!svn_path_is_url(path_or_url)) 506251881Speter SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url, pool)); 507251881Speter else 508251881Speter wri_abspath = NULL; 509251881Speter 510251881Speter SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url, 511251881Speter initial_url, 512251881Speter wri_abspath, 513251881Speter NULL /* commit_items */, 514251881Speter base_dir_abspath != NULL, 515251881Speter base_dir_abspath != NULL, 516251881Speter ctx, pool, pool)); 517251881Speter 518251881Speter /* If we got a CORRECTED_URL, we'll want to refer to that as the 519251881Speter URL-ized form of PATH_OR_URL from now on. */ 520251881Speter if (corrected_url && svn_path_is_url(path_or_url)) 521251881Speter path_or_url = corrected_url; 522251881Speter 523251881Speter SVN_ERR(svn_client__resolve_rev_and_url(&resolved_loc, ra_session, 524251881Speter path_or_url, peg_revision, revision, 525251881Speter ctx, pool)); 526251881Speter 527251881Speter /* Make the session point to the real URL. */ 528251881Speter SVN_ERR(svn_ra_reparent(ra_session, resolved_loc->url, pool)); 529251881Speter 530251881Speter *ra_session_p = ra_session; 531251881Speter if (resolved_loc_p) 532251881Speter *resolved_loc_p = resolved_loc; 533251881Speter 534251881Speter return SVN_NO_ERROR; 535251881Speter} 536251881Speter 537251881Speter 538251881Spetersvn_error_t * 539251881Spetersvn_client__ensure_ra_session_url(const char **old_session_url, 540251881Speter svn_ra_session_t *ra_session, 541251881Speter const char *session_url, 542251881Speter apr_pool_t *pool) 543251881Speter{ 544251881Speter SVN_ERR(svn_ra_get_session_url(ra_session, old_session_url, pool)); 545251881Speter if (! session_url) 546251881Speter SVN_ERR(svn_ra_get_repos_root2(ra_session, &session_url, pool)); 547251881Speter if (strcmp(*old_session_url, session_url) != 0) 548251881Speter SVN_ERR(svn_ra_reparent(ra_session, session_url, pool)); 549251881Speter return SVN_NO_ERROR; 550251881Speter} 551251881Speter 552251881Speter 553251881Speter 554251881Speter/*** Repository Locations ***/ 555251881Speter 556251881Speterstruct gls_receiver_baton_t 557251881Speter{ 558251881Speter apr_array_header_t *segments; 559251881Speter svn_client_ctx_t *ctx; 560251881Speter apr_pool_t *pool; 561251881Speter}; 562251881Speter 563251881Speterstatic svn_error_t * 564251881Spetergls_receiver(svn_location_segment_t *segment, 565251881Speter void *baton, 566251881Speter apr_pool_t *pool) 567251881Speter{ 568251881Speter struct gls_receiver_baton_t *b = baton; 569251881Speter APR_ARRAY_PUSH(b->segments, svn_location_segment_t *) = 570251881Speter svn_location_segment_dup(segment, b->pool); 571251881Speter if (b->ctx->cancel_func) 572251881Speter SVN_ERR((b->ctx->cancel_func)(b->ctx->cancel_baton)); 573251881Speter return SVN_NO_ERROR; 574251881Speter} 575251881Speter 576251881Speter/* A qsort-compatible function which sorts svn_location_segment_t's 577251881Speter based on their revision range covering, resulting in ascending 578251881Speter (oldest-to-youngest) ordering. */ 579251881Speterstatic int 580251881Spetercompare_segments(const void *a, const void *b) 581251881Speter{ 582251881Speter const svn_location_segment_t *a_seg 583251881Speter = *((const svn_location_segment_t * const *) a); 584251881Speter const svn_location_segment_t *b_seg 585251881Speter = *((const svn_location_segment_t * const *) b); 586251881Speter if (a_seg->range_start == b_seg->range_start) 587251881Speter return 0; 588251881Speter return (a_seg->range_start < b_seg->range_start) ? -1 : 1; 589251881Speter} 590251881Speter 591251881Spetersvn_error_t * 592251881Spetersvn_client__repos_location_segments(apr_array_header_t **segments, 593251881Speter svn_ra_session_t *ra_session, 594251881Speter const char *url, 595251881Speter svn_revnum_t peg_revision, 596251881Speter svn_revnum_t start_revision, 597251881Speter svn_revnum_t end_revision, 598251881Speter svn_client_ctx_t *ctx, 599251881Speter apr_pool_t *pool) 600251881Speter{ 601251881Speter struct gls_receiver_baton_t gls_receiver_baton; 602251881Speter const char *old_session_url; 603251881Speter svn_error_t *err; 604251881Speter 605251881Speter *segments = apr_array_make(pool, 8, sizeof(svn_location_segment_t *)); 606251881Speter gls_receiver_baton.segments = *segments; 607251881Speter gls_receiver_baton.ctx = ctx; 608251881Speter gls_receiver_baton.pool = pool; 609251881Speter SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session, 610251881Speter url, pool)); 611251881Speter err = svn_ra_get_location_segments(ra_session, "", peg_revision, 612251881Speter start_revision, end_revision, 613251881Speter gls_receiver, &gls_receiver_baton, 614251881Speter pool); 615251881Speter SVN_ERR(svn_error_compose_create( 616251881Speter err, svn_ra_reparent(ra_session, old_session_url, pool))); 617251881Speter qsort((*segments)->elts, (*segments)->nelts, 618251881Speter (*segments)->elt_size, compare_segments); 619251881Speter return SVN_NO_ERROR; 620251881Speter} 621251881Speter 622251881Speter/* Set *START_URL and *END_URL to the URLs that the object URL@PEG_REVNUM 623251881Speter * had in revisions START_REVNUM and END_REVNUM. Return an error if the 624251881Speter * node cannot be traced back to one of the requested revisions. 625251881Speter * 626251881Speter * START_URL and/or END_URL may be NULL if not wanted. START_REVNUM and 627251881Speter * END_REVNUM must be valid revision numbers except that END_REVNUM may 628251881Speter * be SVN_INVALID_REVNUM if END_URL is NULL. 629251881Speter * 630251881Speter * RA_SESSION is an open RA session parented at URL. 631251881Speter */ 632251881Speterstatic svn_error_t * 633251881Speterrepos_locations(const char **start_url, 634251881Speter const char **end_url, 635251881Speter svn_ra_session_t *ra_session, 636251881Speter const char *url, 637251881Speter svn_revnum_t peg_revnum, 638251881Speter svn_revnum_t start_revnum, 639251881Speter svn_revnum_t end_revnum, 640251881Speter apr_pool_t *result_pool, 641251881Speter apr_pool_t *scratch_pool) 642251881Speter{ 643251881Speter const char *repos_url, *start_path, *end_path; 644251881Speter apr_array_header_t *revs; 645251881Speter apr_hash_t *rev_locs; 646251881Speter 647251881Speter SVN_ERR_ASSERT(peg_revnum != SVN_INVALID_REVNUM); 648251881Speter SVN_ERR_ASSERT(start_revnum != SVN_INVALID_REVNUM); 649251881Speter SVN_ERR_ASSERT(end_revnum != SVN_INVALID_REVNUM || end_url == NULL); 650251881Speter 651251881Speter /* Avoid a network request in the common easy case. */ 652251881Speter if (start_revnum == peg_revnum 653251881Speter && (end_revnum == peg_revnum || end_revnum == SVN_INVALID_REVNUM)) 654251881Speter { 655251881Speter if (start_url) 656251881Speter *start_url = apr_pstrdup(result_pool, url); 657251881Speter if (end_url) 658251881Speter *end_url = apr_pstrdup(result_pool, url); 659251881Speter return SVN_NO_ERROR; 660251881Speter } 661251881Speter 662251881Speter SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, scratch_pool)); 663251881Speter 664251881Speter revs = apr_array_make(scratch_pool, 2, sizeof(svn_revnum_t)); 665251881Speter APR_ARRAY_PUSH(revs, svn_revnum_t) = start_revnum; 666251881Speter if (end_revnum != start_revnum && end_revnum != SVN_INVALID_REVNUM) 667251881Speter APR_ARRAY_PUSH(revs, svn_revnum_t) = end_revnum; 668251881Speter 669251881Speter SVN_ERR(svn_ra_get_locations(ra_session, &rev_locs, "", peg_revnum, 670251881Speter revs, scratch_pool)); 671251881Speter 672251881Speter /* We'd better have all the paths we were looking for! */ 673251881Speter if (start_url) 674251881Speter { 675251881Speter start_path = apr_hash_get(rev_locs, &start_revnum, sizeof(svn_revnum_t)); 676251881Speter if (! start_path) 677251881Speter return svn_error_createf 678251881Speter (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, 679251881Speter _("Unable to find repository location for '%s' in revision %ld"), 680251881Speter url, start_revnum); 681251881Speter *start_url = svn_path_url_add_component2(repos_url, start_path + 1, 682251881Speter result_pool); 683251881Speter } 684251881Speter 685251881Speter if (end_url) 686251881Speter { 687251881Speter end_path = apr_hash_get(rev_locs, &end_revnum, sizeof(svn_revnum_t)); 688251881Speter if (! end_path) 689251881Speter return svn_error_createf 690251881Speter (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, 691251881Speter _("The location for '%s' for revision %ld does not exist in the " 692251881Speter "repository or refers to an unrelated object"), 693251881Speter url, end_revnum); 694251881Speter 695251881Speter *end_url = svn_path_url_add_component2(repos_url, end_path + 1, 696251881Speter result_pool); 697251881Speter } 698251881Speter 699251881Speter return SVN_NO_ERROR; 700251881Speter} 701251881Speter 702251881Spetersvn_error_t * 703251881Spetersvn_client__repos_location(svn_client__pathrev_t **op_loc_p, 704251881Speter svn_ra_session_t *ra_session, 705251881Speter const svn_client__pathrev_t *peg_loc, 706251881Speter svn_revnum_t op_revnum, 707251881Speter svn_client_ctx_t *ctx, 708251881Speter apr_pool_t *result_pool, 709251881Speter apr_pool_t *scratch_pool) 710251881Speter{ 711251881Speter const char *old_session_url; 712251881Speter const char *op_url; 713251881Speter svn_error_t *err; 714251881Speter 715251881Speter SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session, 716251881Speter peg_loc->url, scratch_pool)); 717251881Speter err = repos_locations(&op_url, NULL, ra_session, 718251881Speter peg_loc->url, peg_loc->rev, 719251881Speter op_revnum, SVN_INVALID_REVNUM, 720251881Speter result_pool, scratch_pool); 721251881Speter SVN_ERR(svn_error_compose_create( 722251881Speter err, svn_ra_reparent(ra_session, old_session_url, scratch_pool))); 723251881Speter 724251881Speter *op_loc_p = svn_client__pathrev_create(peg_loc->repos_root_url, 725251881Speter peg_loc->repos_uuid, 726251881Speter op_revnum, op_url, result_pool); 727251881Speter return SVN_NO_ERROR; 728251881Speter} 729251881Speter 730251881Spetersvn_error_t * 731251881Spetersvn_client__repos_locations(const char **start_url, 732251881Speter svn_revnum_t *start_revision, 733251881Speter const char **end_url, 734251881Speter svn_revnum_t *end_revision, 735251881Speter svn_ra_session_t *ra_session, 736251881Speter const char *path, 737251881Speter const svn_opt_revision_t *revision, 738251881Speter const svn_opt_revision_t *start, 739251881Speter const svn_opt_revision_t *end, 740251881Speter svn_client_ctx_t *ctx, 741251881Speter apr_pool_t *pool) 742251881Speter{ 743251881Speter const char *url; 744251881Speter const char *local_abspath_or_url; 745251881Speter svn_revnum_t peg_revnum = SVN_INVALID_REVNUM; 746251881Speter svn_revnum_t start_revnum, end_revnum; 747251881Speter svn_revnum_t youngest_rev = SVN_INVALID_REVNUM; 748251881Speter apr_pool_t *subpool = svn_pool_create(pool); 749251881Speter 750251881Speter /* Ensure that we are given some real revision data to work with. 751251881Speter (It's okay if the END is unspecified -- in that case, we'll just 752251881Speter set it to the same thing as START.) */ 753251881Speter if (revision->kind == svn_opt_revision_unspecified 754251881Speter || start->kind == svn_opt_revision_unspecified) 755251881Speter return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL); 756251881Speter 757251881Speter if (end == NULL) 758251881Speter { 759251881Speter static const svn_opt_revision_t unspecified_rev 760251881Speter = { svn_opt_revision_unspecified, { 0 } }; 761251881Speter 762251881Speter end = &unspecified_rev; 763251881Speter } 764251881Speter 765251881Speter /* Determine LOCAL_ABSPATH_OR_URL, URL, and possibly PEG_REVNUM. 766251881Speter If we are looking at the working version of a WC path that is scheduled 767251881Speter as a copy, then we need to use the copy-from URL and peg revision. */ 768251881Speter if (! svn_path_is_url(path)) 769251881Speter { 770251881Speter SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path, subpool)); 771251881Speter 772251881Speter if (revision->kind == svn_opt_revision_working) 773251881Speter { 774251881Speter const char *repos_root_url; 775251881Speter const char *repos_relpath; 776251881Speter svn_boolean_t is_copy; 777251881Speter 778251881Speter SVN_ERR(svn_wc__node_get_origin(&is_copy, &peg_revnum, &repos_relpath, 779251881Speter &repos_root_url, NULL, NULL, 780251881Speter ctx->wc_ctx, local_abspath_or_url, 781251881Speter FALSE, subpool, subpool)); 782251881Speter 783251881Speter if (repos_relpath) 784251881Speter url = svn_path_url_add_component2(repos_root_url, repos_relpath, 785251881Speter pool); 786251881Speter else 787251881Speter url = NULL; 788251881Speter 789251881Speter if (url && is_copy && ra_session) 790251881Speter { 791251881Speter const char *session_url; 792251881Speter SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, 793251881Speter subpool)); 794251881Speter 795251881Speter if (strcmp(session_url, url) != 0) 796251881Speter { 797251881Speter /* We can't use the caller provided RA session now :( */ 798251881Speter ra_session = NULL; 799251881Speter } 800251881Speter } 801251881Speter } 802251881Speter else 803251881Speter url = NULL; 804251881Speter 805251881Speter if (! url) 806251881Speter SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, 807251881Speter local_abspath_or_url, pool, subpool)); 808251881Speter 809251881Speter if (!url) 810251881Speter { 811251881Speter return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, 812251881Speter _("'%s' has no URL"), 813251881Speter svn_dirent_local_style(path, pool)); 814251881Speter } 815251881Speter } 816251881Speter else 817251881Speter { 818251881Speter local_abspath_or_url = path; 819251881Speter url = path; 820251881Speter } 821251881Speter 822251881Speter /* ### We should be smarter here. If the callers just asks for BASE and 823251881Speter WORKING revisions, we should already have the correct URLs, so we 824251881Speter don't need to do anything more here in that case. */ 825251881Speter 826251881Speter /* Open a RA session to this URL if we don't have one already. */ 827251881Speter if (! ra_session) 828251881Speter SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL, 829251881Speter ctx, subpool, subpool)); 830251881Speter 831251881Speter /* Resolve the opt_revision_ts. */ 832251881Speter if (peg_revnum == SVN_INVALID_REVNUM) 833251881Speter SVN_ERR(svn_client__get_revision_number(&peg_revnum, &youngest_rev, 834251881Speter ctx->wc_ctx, local_abspath_or_url, 835251881Speter ra_session, revision, pool)); 836251881Speter 837251881Speter SVN_ERR(svn_client__get_revision_number(&start_revnum, &youngest_rev, 838251881Speter ctx->wc_ctx, local_abspath_or_url, 839251881Speter ra_session, start, pool)); 840251881Speter if (end->kind == svn_opt_revision_unspecified) 841251881Speter end_revnum = start_revnum; 842251881Speter else 843251881Speter SVN_ERR(svn_client__get_revision_number(&end_revnum, &youngest_rev, 844251881Speter ctx->wc_ctx, local_abspath_or_url, 845251881Speter ra_session, end, pool)); 846251881Speter 847251881Speter /* Set the output revision variables. */ 848251881Speter if (start_revision) 849251881Speter { 850251881Speter *start_revision = start_revnum; 851251881Speter } 852251881Speter if (end_revision && end->kind != svn_opt_revision_unspecified) 853251881Speter { 854251881Speter *end_revision = end_revnum; 855251881Speter } 856251881Speter 857251881Speter SVN_ERR(repos_locations(start_url, end_url, 858251881Speter ra_session, url, peg_revnum, 859251881Speter start_revnum, end_revnum, 860251881Speter pool, subpool)); 861251881Speter svn_pool_destroy(subpool); 862251881Speter return SVN_NO_ERROR; 863251881Speter} 864251881Speter 865251881Spetersvn_error_t * 866253734Spetersvn_client__calc_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p, 867253734Speter const svn_client__pathrev_t *loc1, 868253734Speter apr_hash_t *history1, 869253734Speter svn_boolean_t has_rev_zero_history1, 870253734Speter const svn_client__pathrev_t *loc2, 871253734Speter apr_hash_t *history2, 872253734Speter svn_boolean_t has_rev_zero_history2, 873253734Speter apr_pool_t *result_pool, 874253734Speter apr_pool_t *scratch_pool) 875251881Speter{ 876251881Speter apr_hash_index_t *hi; 877251881Speter svn_revnum_t yc_revision = SVN_INVALID_REVNUM; 878251881Speter const char *yc_relpath = NULL; 879251881Speter 880251881Speter if (strcmp(loc1->repos_root_url, loc2->repos_root_url) != 0) 881251881Speter { 882251881Speter *ancestor_p = NULL; 883251881Speter return SVN_NO_ERROR; 884251881Speter } 885251881Speter 886251881Speter /* Loop through the first location's history, check for overlapping 887251881Speter paths and ranges in the second location's history, and 888251881Speter remembering the youngest matching location. */ 889251881Speter for (hi = apr_hash_first(scratch_pool, history1); hi; hi = apr_hash_next(hi)) 890251881Speter { 891251881Speter const char *path = svn__apr_hash_index_key(hi); 892251881Speter apr_ssize_t path_len = svn__apr_hash_index_klen(hi); 893251881Speter svn_rangelist_t *ranges1 = svn__apr_hash_index_val(hi); 894251881Speter svn_rangelist_t *ranges2, *common; 895251881Speter 896251881Speter ranges2 = apr_hash_get(history2, path, path_len); 897251881Speter if (ranges2) 898251881Speter { 899251881Speter /* We have a path match. Now, did our two histories share 900251881Speter any revisions at that path? */ 901251881Speter SVN_ERR(svn_rangelist_intersect(&common, ranges1, ranges2, 902251881Speter TRUE, scratch_pool)); 903251881Speter if (common->nelts) 904251881Speter { 905251881Speter svn_merge_range_t *yc_range = 906251881Speter APR_ARRAY_IDX(common, common->nelts - 1, svn_merge_range_t *); 907251881Speter if ((! SVN_IS_VALID_REVNUM(yc_revision)) 908251881Speter || (yc_range->end > yc_revision)) 909251881Speter { 910251881Speter yc_revision = yc_range->end; 911251881Speter yc_relpath = path + 1; 912251881Speter } 913251881Speter } 914251881Speter } 915251881Speter } 916251881Speter 917251881Speter /* It's possible that PATH_OR_URL1 and PATH_OR_URL2's only common 918251881Speter history is revision 0. */ 919251881Speter if (!yc_relpath && has_rev_zero_history1 && has_rev_zero_history2) 920251881Speter { 921251881Speter yc_relpath = ""; 922251881Speter yc_revision = 0; 923251881Speter } 924251881Speter 925251881Speter if (yc_relpath) 926251881Speter { 927251881Speter *ancestor_p = svn_client__pathrev_create_with_relpath( 928251881Speter loc1->repos_root_url, loc1->repos_uuid, 929251881Speter yc_revision, yc_relpath, result_pool); 930251881Speter } 931251881Speter else 932251881Speter { 933251881Speter *ancestor_p = NULL; 934251881Speter } 935251881Speter return SVN_NO_ERROR; 936251881Speter} 937251881Speter 938251881Spetersvn_error_t * 939253734Spetersvn_client__get_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p, 940253734Speter const svn_client__pathrev_t *loc1, 941253734Speter const svn_client__pathrev_t *loc2, 942253734Speter svn_ra_session_t *session, 943253734Speter svn_client_ctx_t *ctx, 944253734Speter apr_pool_t *result_pool, 945253734Speter apr_pool_t *scratch_pool) 946253734Speter{ 947253734Speter apr_pool_t *sesspool = NULL; 948253734Speter apr_hash_t *history1, *history2; 949253734Speter svn_boolean_t has_rev_zero_history1; 950253734Speter svn_boolean_t has_rev_zero_history2; 951253734Speter 952253734Speter if (strcmp(loc1->repos_root_url, loc2->repos_root_url) != 0) 953253734Speter { 954253734Speter *ancestor_p = NULL; 955253734Speter return SVN_NO_ERROR; 956253734Speter } 957253734Speter 958253734Speter /* Open an RA session for the two locations. */ 959253734Speter if (session == NULL) 960253734Speter { 961253734Speter sesspool = svn_pool_create(scratch_pool); 962253734Speter SVN_ERR(svn_client_open_ra_session2(&session, loc1->url, NULL, ctx, 963253734Speter sesspool, sesspool)); 964253734Speter } 965253734Speter 966253734Speter /* We're going to cheat and use history-as-mergeinfo because it 967253734Speter saves us a bunch of annoying custom data comparisons and such. */ 968253734Speter SVN_ERR(svn_client__get_history_as_mergeinfo(&history1, 969253734Speter &has_rev_zero_history1, 970253734Speter loc1, 971253734Speter SVN_INVALID_REVNUM, 972253734Speter SVN_INVALID_REVNUM, 973253734Speter session, ctx, scratch_pool)); 974253734Speter SVN_ERR(svn_client__get_history_as_mergeinfo(&history2, 975253734Speter &has_rev_zero_history2, 976253734Speter loc2, 977253734Speter SVN_INVALID_REVNUM, 978253734Speter SVN_INVALID_REVNUM, 979253734Speter session, ctx, scratch_pool)); 980253734Speter /* Close the ra session if we opened one. */ 981253734Speter if (sesspool) 982253734Speter svn_pool_destroy(sesspool); 983253734Speter 984253734Speter SVN_ERR(svn_client__calc_youngest_common_ancestor(ancestor_p, 985253734Speter loc1, history1, 986253734Speter has_rev_zero_history1, 987253734Speter loc2, history2, 988253734Speter has_rev_zero_history2, 989253734Speter result_pool, 990253734Speter scratch_pool)); 991253734Speter 992253734Speter return SVN_NO_ERROR; 993253734Speter} 994253734Speter 995253734Spetersvn_error_t * 996251881Spetersvn_client__youngest_common_ancestor(const char **ancestor_url, 997251881Speter svn_revnum_t *ancestor_rev, 998251881Speter const char *path_or_url1, 999251881Speter const svn_opt_revision_t *revision1, 1000251881Speter const char *path_or_url2, 1001251881Speter const svn_opt_revision_t *revision2, 1002251881Speter svn_client_ctx_t *ctx, 1003251881Speter apr_pool_t *result_pool, 1004251881Speter apr_pool_t *scratch_pool) 1005251881Speter{ 1006251881Speter apr_pool_t *sesspool = svn_pool_create(scratch_pool); 1007251881Speter svn_ra_session_t *session; 1008251881Speter svn_client__pathrev_t *loc1, *loc2, *ancestor; 1009251881Speter 1010251881Speter /* Resolve the two locations */ 1011251881Speter SVN_ERR(svn_client__ra_session_from_path2(&session, &loc1, 1012251881Speter path_or_url1, NULL, 1013251881Speter revision1, revision1, 1014251881Speter ctx, sesspool)); 1015251881Speter SVN_ERR(svn_client__resolve_rev_and_url(&loc2, session, 1016251881Speter path_or_url2, revision2, revision2, 1017251881Speter ctx, scratch_pool)); 1018251881Speter 1019251881Speter SVN_ERR(svn_client__get_youngest_common_ancestor( 1020251881Speter &ancestor, loc1, loc2, session, ctx, result_pool, scratch_pool)); 1021251881Speter 1022251881Speter if (ancestor) 1023251881Speter { 1024251881Speter *ancestor_url = ancestor->url; 1025251881Speter *ancestor_rev = ancestor->rev; 1026251881Speter } 1027251881Speter else 1028251881Speter { 1029251881Speter *ancestor_url = NULL; 1030251881Speter *ancestor_rev = SVN_INVALID_REVNUM; 1031251881Speter } 1032251881Speter svn_pool_destroy(sesspool); 1033251881Speter return SVN_NO_ERROR; 1034251881Speter} 1035251881Speter 1036251881Speter 1037251881Speterstruct ra_ev2_baton { 1038251881Speter /* The working copy context, from the client context. */ 1039251881Speter svn_wc_context_t *wc_ctx; 1040251881Speter 1041251881Speter /* For a given REPOS_RELPATH, provide a LOCAL_ABSPATH that represents 1042251881Speter that repository node. */ 1043251881Speter apr_hash_t *relpath_map; 1044251881Speter}; 1045251881Speter 1046251881Speter 1047251881Spetersvn_error_t * 1048251881Spetersvn_client__ra_provide_base(svn_stream_t **contents, 1049251881Speter svn_revnum_t *revision, 1050251881Speter void *baton, 1051251881Speter const char *repos_relpath, 1052251881Speter apr_pool_t *result_pool, 1053251881Speter apr_pool_t *scratch_pool) 1054251881Speter{ 1055251881Speter struct ra_ev2_baton *reb = baton; 1056251881Speter const char *local_abspath; 1057251881Speter svn_error_t *err; 1058251881Speter 1059251881Speter local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath); 1060251881Speter if (!local_abspath) 1061251881Speter { 1062251881Speter *contents = NULL; 1063251881Speter return SVN_NO_ERROR; 1064251881Speter } 1065251881Speter 1066251881Speter err = svn_wc_get_pristine_contents2(contents, reb->wc_ctx, local_abspath, 1067251881Speter result_pool, scratch_pool); 1068251881Speter if (err) 1069251881Speter { 1070251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1071251881Speter return svn_error_trace(err); 1072251881Speter 1073251881Speter svn_error_clear(err); 1074251881Speter *contents = NULL; 1075251881Speter return SVN_NO_ERROR; 1076251881Speter } 1077251881Speter 1078251881Speter if (*contents != NULL) 1079251881Speter { 1080251881Speter /* The pristine contents refer to the BASE, or to the pristine of 1081251881Speter a copy/move to this location. Fetch the correct revision. */ 1082251881Speter SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL, 1083251881Speter reb->wc_ctx, local_abspath, FALSE, 1084251881Speter scratch_pool, scratch_pool)); 1085251881Speter } 1086251881Speter 1087251881Speter return SVN_NO_ERROR; 1088251881Speter} 1089251881Speter 1090251881Speter 1091251881Spetersvn_error_t * 1092251881Spetersvn_client__ra_provide_props(apr_hash_t **props, 1093251881Speter svn_revnum_t *revision, 1094251881Speter void *baton, 1095251881Speter const char *repos_relpath, 1096251881Speter apr_pool_t *result_pool, 1097251881Speter apr_pool_t *scratch_pool) 1098251881Speter{ 1099251881Speter struct ra_ev2_baton *reb = baton; 1100251881Speter const char *local_abspath; 1101251881Speter svn_error_t *err; 1102251881Speter 1103251881Speter local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath); 1104251881Speter if (!local_abspath) 1105251881Speter { 1106251881Speter *props = NULL; 1107251881Speter return SVN_NO_ERROR; 1108251881Speter } 1109251881Speter 1110251881Speter err = svn_wc_get_pristine_props(props, reb->wc_ctx, local_abspath, 1111251881Speter result_pool, scratch_pool); 1112251881Speter if (err) 1113251881Speter { 1114251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1115251881Speter return svn_error_trace(err); 1116251881Speter 1117251881Speter svn_error_clear(err); 1118251881Speter *props = NULL; 1119251881Speter return SVN_NO_ERROR; 1120251881Speter } 1121251881Speter 1122251881Speter if (*props != NULL) 1123251881Speter { 1124251881Speter /* The pristine props refer to the BASE, or to the pristine props of 1125251881Speter a copy/move to this location. Fetch the correct revision. */ 1126251881Speter SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL, 1127251881Speter reb->wc_ctx, local_abspath, FALSE, 1128251881Speter scratch_pool, scratch_pool)); 1129251881Speter } 1130251881Speter 1131251881Speter return SVN_NO_ERROR; 1132251881Speter} 1133251881Speter 1134251881Speter 1135251881Spetersvn_error_t * 1136251881Spetersvn_client__ra_get_copysrc_kind(svn_node_kind_t *kind, 1137251881Speter void *baton, 1138251881Speter const char *repos_relpath, 1139251881Speter svn_revnum_t src_revision, 1140251881Speter apr_pool_t *scratch_pool) 1141251881Speter{ 1142251881Speter struct ra_ev2_baton *reb = baton; 1143251881Speter const char *local_abspath; 1144251881Speter 1145251881Speter local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath); 1146251881Speter if (!local_abspath) 1147251881Speter { 1148251881Speter *kind = svn_node_unknown; 1149251881Speter return SVN_NO_ERROR; 1150251881Speter } 1151251881Speter 1152251881Speter /* ### what to do with SRC_REVISION? */ 1153251881Speter 1154251881Speter SVN_ERR(svn_wc_read_kind2(kind, reb->wc_ctx, local_abspath, 1155251881Speter FALSE, FALSE, scratch_pool)); 1156251881Speter 1157251881Speter return SVN_NO_ERROR; 1158251881Speter} 1159251881Speter 1160251881Speter 1161251881Spetervoid * 1162251881Spetersvn_client__ra_make_cb_baton(svn_wc_context_t *wc_ctx, 1163251881Speter apr_hash_t *relpath_map, 1164251881Speter apr_pool_t *result_pool) 1165251881Speter{ 1166251881Speter struct ra_ev2_baton *reb = apr_palloc(result_pool, sizeof(*reb)); 1167251881Speter 1168251881Speter SVN_ERR_ASSERT_NO_RETURN(wc_ctx != NULL); 1169251881Speter SVN_ERR_ASSERT_NO_RETURN(relpath_map != NULL); 1170251881Speter 1171251881Speter reb->wc_ctx = wc_ctx; 1172251881Speter reb->relpath_map = relpath_map; 1173251881Speter 1174251881Speter return reb; 1175251881Speter} 1176