1251881Speter/* 2251881Speter * merge.c: merging 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* ==================================================================== */ 25251881Speter 26251881Speter 27251881Speter 28251881Speter/*** Includes ***/ 29251881Speter 30251881Speter#include <assert.h> 31251881Speter#include <apr_strings.h> 32251881Speter#include <apr_tables.h> 33251881Speter#include <apr_hash.h> 34251881Speter#include "svn_types.h" 35251881Speter#include "svn_hash.h" 36251881Speter#include "svn_wc.h" 37251881Speter#include "svn_delta.h" 38251881Speter#include "svn_diff.h" 39251881Speter#include "svn_mergeinfo.h" 40251881Speter#include "svn_client.h" 41251881Speter#include "svn_string.h" 42251881Speter#include "svn_error.h" 43251881Speter#include "svn_dirent_uri.h" 44251881Speter#include "svn_path.h" 45251881Speter#include "svn_io.h" 46251881Speter#include "svn_utf.h" 47251881Speter#include "svn_pools.h" 48251881Speter#include "svn_config.h" 49251881Speter#include "svn_props.h" 50251881Speter#include "svn_time.h" 51251881Speter#include "svn_sorts.h" 52251881Speter#include "svn_subst.h" 53251881Speter#include "svn_ra.h" 54251881Speter#include "client.h" 55251881Speter#include "mergeinfo.h" 56251881Speter 57251881Speter#include "private/svn_opt_private.h" 58251881Speter#include "private/svn_wc_private.h" 59251881Speter#include "private/svn_mergeinfo_private.h" 60251881Speter#include "private/svn_fspath.h" 61251881Speter#include "private/svn_ra_private.h" 62251881Speter#include "private/svn_client_private.h" 63251881Speter#include "private/svn_subr_private.h" 64251881Speter 65251881Speter#include "svn_private_config.h" 66251881Speter 67251881Speter 68251881Speter/*-----------------------------------------------------------------------*/ 69251881Speter 70251881Speter/* MERGEINFO MERGE SOURCE NORMALIZATION 71251881Speter * 72251881Speter * Nearly any helper function herein that accepts two URL/revision 73251881Speter * pairs (or equivalent struct merge_source_t) expects one of two things 74251881Speter * to be true: 75251881Speter * 76251881Speter * 1. that mergeinfo is not being recorded at all for this 77251881Speter * operation, or 78251881Speter * 79251881Speter * 2. that the pairs represent two locations along a single line 80251881Speter * of version history such that there are no copies in the 81251881Speter * history of the object between the locations when treating 82251881Speter * the oldest of the two locations as non-inclusive. In other 83251881Speter * words, if there is a copy at all between them, there is only 84251881Speter * one copy and its source was the oldest of the two locations. 85251881Speter * 86251881Speter * We use svn_ra_get_location_segments() to split a given range of 87251881Speter * revisions across an object's history into several which obey these 88251881Speter * rules. For example, an extract from the log of Subversion's own 89251881Speter * /subversion/tags/1.4.5 directory shows the following copies between 90251881Speter * r859500 and r866500 (omitting the '/subversion' prefix for clarity): 91251881Speter * 92251881Speter * r859598: 93251881Speter * A /branches/1.4.x (from /trunk:859597) 94251881Speter * 95251881Speter * r865417: 96251881Speter * A /tags/1.4.4 (from /branches/1.4.x:865262) 97251881Speter * # Notice that this copy leaves a gap between 865262 and 865417. 98251881Speter * 99251881Speter * r866420: 100251881Speter * A /branches/1.4.5 (from /tags/1.4.4:866419) 101251881Speter * 102251881Speter * r866425: 103251881Speter * D /branches/1.4.5 104251881Speter * A /tags/1.4.5 (from /branches/1.4.5:866424) 105251881Speter * 106251881Speter * In graphical form: 107251881Speter * 108251881Speter * 859500 859597 865262 866419 866424 866500 109251881Speter * . . . . . . 110251881Speter * trunk ------------------------------------------------ 111251881Speter * \ . . . 112251881Speter * branches/1.4.x A------------------------------------- 113251881Speter * . \______ . . 114251881Speter * . \ . . 115251881Speter * tags/1.4.4 . A----------------------- 116251881Speter * . . \ . 117251881Speter * branches/1.4.5 . . A------D 118251881Speter * . . . \. 119251881Speter * tags/1.4.5 . . . A--------- 120251881Speter * . . . . 121251881Speter * 859598 865417 866420 866425 122251881Speter * 123251881Speter * A merge of the difference between r859500 and r866500 of this directory 124251881Speter * gets split into sequential merges of the following location pairs. 125251881Speter * 126251881Speter * 859500 859597 865262 865416 866419 866424 866500 127251881Speter * . . . . . . . 128251881Speter * trunk (======] . . . . . 129251881Speter * . . . . . 130251881Speter * trunk ( . . . . . 131251881Speter * branches/1.4.x ======] . . . . 132251881Speter * . . . . 133251881Speter * branches/1.4.x ( . . . . 134251881Speter * tags/1.4.4 =============] . . 135251881Speter * implicit_src_gap (======] . . . 136251881Speter * . . . 137251881Speter * tags/1.4.4 ( . . 138251881Speter * branches/1.4.5 ======] . 139251881Speter * . . 140251881Speter * branches/1.4.5 ( . 141251881Speter * tags/1.4.5 ======] 142251881Speter * 143251881Speter * which are represented in merge_source_t as: 144251881Speter * 145251881Speter * [/trunk:859500, /trunk:859597] 146251881Speter * (recorded in svn:mergeinfo as /trunk:859501-859597) 147251881Speter * 148251881Speter * [/trunk:859597, /branches/1.4.x:865262] 149251881Speter * (recorded in svn:mergeinfo as /branches/1.4.x:859598-865262) 150251881Speter * 151251881Speter * [/branches/1.4.x:865262, /tags/1.4.4@866419] 152251881Speter * (recorded in svn:mergeinfo as /tags/1.4.4:865263-866419) 153251881Speter * (and there is a gap, the revision range [865262, 865416]) 154251881Speter * 155251881Speter * [/tags/1.4.4@866419, /branches/1.4.5@866424] 156251881Speter * (recorded in svn:mergeinfo as /branches/1.4.5:866420-866424) 157251881Speter * 158251881Speter * [/branches/1.4.5@866424, /tags/1.4.5@866500] 159251881Speter * (recorded in svn:mergeinfo as /tags/1.4.5:866425-866500) 160251881Speter * 161251881Speter * Our helper functions would then operate on one of these location 162251881Speter * pairs at a time. 163251881Speter */ 164251881Speter 165251881Speter/* WHICH SVN_CLIENT_MERGE* API DO I WANT? 166251881Speter * 167251881Speter * libsvn_client has three public merge APIs; they are all wrappers 168251881Speter * around the do_merge engine. Which one to use depends on the number 169251881Speter * of URLs passed as arguments and whether or not specific merge 170251881Speter * ranges (-c/-r) are specified. 171251881Speter * 172251881Speter * 1 URL 2 URLs 173251881Speter * +----+--------------------------------+---------------------+ 174251881Speter * | -c | mergeinfo-driven | | 175251881Speter * | or | cherrypicking | | 176251881Speter * | -r | (svn_client_merge_peg) | | 177251881Speter * |----+--------------------------------+ | 178251881Speter * | | mergeinfo-driven | unsupported | 179251881Speter * | | 'cherry harvest', i.e. merge | | 180251881Speter * | | all revisions from URL that | | 181251881Speter * | no | have not already been merged | | 182251881Speter * | -c | (svn_client_merge_peg) | | 183251881Speter * | or +--------------------------------+---------------------+ 184251881Speter * | -r | mergeinfo-driven | mergeinfo-writing | 185251881Speter * | | whole-branch | diff-and-apply | 186251881Speter * | | heuristic merge | (svn_client_merge) | 187251881Speter * | | (svn_client_merge_reintegrate) | | 188251881Speter * +----+--------------------------------+---------------------+ 189251881Speter * 190251881Speter * 191251881Speter */ 192251881Speter 193251881Speter/* THE CHILDREN_WITH_MERGEINFO ARRAY 194251881Speter * 195251881Speter * Many of the helper functions in this file pass around an 196251881Speter * apr_array_header_t *CHILDREN_WITH_MERGEINFO. This is a depth first 197251881Speter * sorted array filled with svn_client__merge_path_t * describing the 198251881Speter * merge target and any of its subtrees which have explicit mergeinfo 199251881Speter * or otherwise need special attention during a merge. 200251881Speter * 201251881Speter * During mergeinfo unaware merges, CHILDREN_WITH_MERGEINFO contains 202251881Speter * contains only one element (added by do_mergeinfo_unaware_dir_merge) 203251881Speter * describing a contiguous range to be merged to the WC merge target. 204251881Speter * 205251881Speter * During mergeinfo aware merges CHILDREN_WITH_MERGEINFO is created 206251881Speter * by get_mergeinfo_paths() and outside of that function and its helpers 207251881Speter * should always meet the criteria dictated in get_mergeinfo_paths()'s doc 208251881Speter * string. The elements of CHILDREN_WITH_MERGEINFO should never be NULL. 209251881Speter * 210251881Speter * For clarification on mergeinfo aware vs. mergeinfo unaware merges, see 211251881Speter * the doc string for HONOR_MERGEINFO(). 212251881Speter */ 213251881Speter 214251881Speter 215251881Speter/*-----------------------------------------------------------------------*/ 216251881Speter 217251881Speter/*** Repos-Diff Editor Callbacks ***/ 218251881Speter 219251881Speter/* */ 220251881Spetertypedef struct merge_source_t 221251881Speter{ 222251881Speter /* "left" side URL and revision (inclusive iff youngest) */ 223251881Speter const svn_client__pathrev_t *loc1; 224251881Speter 225251881Speter /* "right" side URL and revision (inclusive iff youngest) */ 226251881Speter const svn_client__pathrev_t *loc2; 227251881Speter 228251881Speter /* True iff LOC1 is an ancestor of LOC2 or vice-versa (history-wise). */ 229251881Speter svn_boolean_t ancestral; 230251881Speter} merge_source_t; 231251881Speter 232251881Speter/* Description of the merge target root node (a WC working node) */ 233251881Spetertypedef struct merge_target_t 234251881Speter{ 235251881Speter /* Absolute path to the WC node */ 236251881Speter const char *abspath; 237251881Speter 238251881Speter /* The repository location of the base node of the target WC. If the node 239251881Speter * is locally added, then URL & REV are NULL & SVN_INVALID_REVNUM. 240251881Speter * REPOS_ROOT_URL and REPOS_UUID are always valid. */ 241251881Speter svn_client__pathrev_t loc; 242251881Speter 243251881Speter} merge_target_t; 244251881Speter 245251881Spetertypedef struct merge_cmd_baton_t { 246251881Speter svn_boolean_t force_delete; /* Delete a file/dir even if modified */ 247251881Speter svn_boolean_t dry_run; 248251881Speter svn_boolean_t record_only; /* Whether to merge only mergeinfo 249251881Speter differences. */ 250251881Speter svn_boolean_t same_repos; /* Whether the merge source repository 251251881Speter is the same repository as the 252251881Speter target. Defaults to FALSE if DRY_RUN 253251881Speter is TRUE.*/ 254251881Speter svn_boolean_t mergeinfo_capable; /* Whether the merge source server 255251881Speter is capable of Merge Tracking. */ 256251881Speter svn_boolean_t ignore_mergeinfo; /* Don't honor mergeinfo; see 257251881Speter doc string of do_merge(). FALSE if 258251881Speter MERGE_SOURCE->ancestral is FALSE. */ 259251881Speter svn_boolean_t diff_ignore_ancestry; /* Diff unrelated nodes as if related; see 260251881Speter doc string of do_merge(). FALSE if 261251881Speter MERGE_SOURCE->ancestral is FALSE. */ 262251881Speter svn_boolean_t reintegrate_merge; /* Whether this is a --reintegrate 263251881Speter merge or not. */ 264251881Speter const merge_target_t *target; /* Description of merge target node */ 265251881Speter 266251881Speter /* The left and right URLs and revs. The value of this field changes to 267251881Speter reflect the merge_source_t *currently* being merged by do_merge(). */ 268251881Speter merge_source_t merge_source; 269251881Speter 270251881Speter /* Rangelist containing single range which describes the gap, if any, 271251881Speter in the natural history of the merge source currently being processed. 272251881Speter See http://subversion.tigris.org/issues/show_bug.cgi?id=3432. 273251881Speter Updated during each call to do_directory_merge(). May be NULL if there 274251881Speter is no gap. */ 275251881Speter svn_rangelist_t *implicit_src_gap; 276251881Speter 277251881Speter svn_client_ctx_t *ctx; /* Client context for callbacks, etc. */ 278251881Speter 279251881Speter /* The list of any paths which remained in conflict after a 280251881Speter resolution attempt was made. We track this in-memory, rather 281251881Speter than just using WC entry state, since the latter doesn't help us 282251881Speter when in dry_run mode. 283251881Speter ### And because we only want to resolve conflicts that were 284251881Speter generated by this merge, not pre-existing ones? */ 285251881Speter apr_hash_t *conflicted_paths; 286251881Speter 287251881Speter /* A list of absolute paths which had no explicit mergeinfo prior to the 288251881Speter merge but got explicit mergeinfo added by the merge. This is populated 289251881Speter by merge_change_props() and is allocated in POOL so it is subject to the 290251881Speter lifetime limitations of POOL. Is NULL if no paths are found which 291251881Speter meet the criteria or DRY_RUN is true. */ 292251881Speter apr_hash_t *paths_with_new_mergeinfo; 293251881Speter 294251881Speter /* A list of absolute paths whose mergeinfo doesn't need updating after 295251881Speter the merge. This can be caused by the removal of mergeinfo by the merge 296251881Speter or by deleting the node itself. This is populated by merge_change_props() 297251881Speter and the delete callbacks and is allocated in POOL so it is subject to the 298251881Speter lifetime limitations of POOL. Is NULL if no paths are found which 299251881Speter meet the criteria or DRY_RUN is true. */ 300251881Speter apr_hash_t *paths_with_deleted_mergeinfo; 301251881Speter 302251881Speter /* The list of absolute skipped paths, which should be examined and 303251881Speter cleared after each invocation of the callback. The paths 304251881Speter are absolute. Is NULL if MERGE_B->MERGE_SOURCE->ancestral and 305251881Speter MERGE_B->REINTEGRATE_MERGE are both false. */ 306251881Speter apr_hash_t *skipped_abspaths; 307251881Speter 308251881Speter /* The list of absolute merged paths. Unused if MERGE_B->MERGE_SOURCE->ancestral 309251881Speter and MERGE_B->REINTEGRATE_MERGE are both false. */ 310251881Speter apr_hash_t *merged_abspaths; 311251881Speter 312251881Speter /* A hash of (const char *) absolute WC paths mapped to the same which 313251881Speter represent the roots of subtrees added by the merge. */ 314251881Speter apr_hash_t *added_abspaths; 315251881Speter 316251881Speter /* A list of tree conflict victim absolute paths which may be NULL. */ 317251881Speter apr_hash_t *tree_conflicted_abspaths; 318251881Speter 319251881Speter /* The diff3_cmd in ctx->config, if any, else null. We could just 320251881Speter extract this as needed, but since more than one caller uses it, 321251881Speter we just set it up when this baton is created. */ 322251881Speter const char *diff3_cmd; 323251881Speter const apr_array_header_t *merge_options; 324251881Speter 325251881Speter /* RA sessions used throughout a merge operation. Opened/re-parented 326251881Speter as needed. 327251881Speter 328251881Speter NOTE: During the actual merge editor drive, RA_SESSION1 is used 329251881Speter for the primary editing and RA_SESSION2 for fetching additional 330251881Speter information -- as necessary -- from the repository. So during 331251881Speter this phase of the merge, you *must not* reparent RA_SESSION1; use 332251881Speter (temporarily reparenting if you must) RA_SESSION2 instead. */ 333251881Speter svn_ra_session_t *ra_session1; 334251881Speter svn_ra_session_t *ra_session2; 335251881Speter 336251881Speter /* During the merge, *USE_SLEEP is set to TRUE if a sleep will be required 337251881Speter afterwards to ensure timestamp integrity, or unchanged if not. */ 338251881Speter svn_boolean_t *use_sleep; 339251881Speter 340251881Speter /* Pool which has a lifetime limited to one iteration over a given 341251881Speter merge source, i.e. it is cleared on every call to do_directory_merge() 342251881Speter or do_file_merge() in do_merge(). */ 343251881Speter apr_pool_t *pool; 344251881Speter 345251881Speter 346251881Speter /* State for notify_merge_begin() */ 347251881Speter struct notify_begin_state_t 348251881Speter { 349251881Speter /* Cache of which abspath was last notified. */ 350251881Speter const char *last_abspath; 351251881Speter 352251881Speter /* Reference to the one-and-only CHILDREN_WITH_MERGEINFO (see global 353251881Speter comment) or a similar list for single-file-merges */ 354251881Speter const apr_array_header_t *nodes_with_mergeinfo; 355251881Speter } notify_begin; 356251881Speter 357251881Speter} merge_cmd_baton_t; 358251881Speter 359251881Speter 360251881Speter/* Return TRUE iff we should be taking account of mergeinfo in deciding what 361251881Speter changes to merge, for the merge described by MERGE_B. Specifically, that 362251881Speter is if the merge source server is capable of merge tracking, the left-side 363251881Speter merge source is an ancestor of the right-side (or vice-versa), the merge 364251881Speter source is in the same repository as the merge target, and we are not 365251881Speter ignoring mergeinfo. */ 366251881Speter#define HONOR_MERGEINFO(merge_b) ((merge_b)->mergeinfo_capable \ 367251881Speter && (merge_b)->merge_source.ancestral \ 368251881Speter && (merge_b)->same_repos \ 369251881Speter && (! (merge_b)->ignore_mergeinfo)) 370251881Speter 371251881Speter 372251881Speter/* Return TRUE iff we should be recording mergeinfo for the merge described 373251881Speter by MERGE_B. Specifically, that is if we are honoring mergeinfo and the 374251881Speter merge is not a dry run. */ 375251881Speter#define RECORD_MERGEINFO(merge_b) (HONOR_MERGEINFO(merge_b) \ 376251881Speter && !(merge_b)->dry_run) 377251881Speter 378251881Speter 379251881Speter/*-----------------------------------------------------------------------*/ 380251881Speter 381251881Speter/*** Utilities ***/ 382251881Speter 383251881Speter/* Return TRUE iff the session URL of RA_SESSION is equal to URL. Useful in 384251881Speter * asserting preconditions. */ 385251881Speterstatic svn_boolean_t 386251881Spetersession_url_is(svn_ra_session_t *ra_session, 387251881Speter const char *url, 388251881Speter apr_pool_t *scratch_pool) 389251881Speter{ 390251881Speter const char *session_url; 391251881Speter svn_error_t *err 392251881Speter = svn_ra_get_session_url(ra_session, &session_url, scratch_pool); 393251881Speter 394251881Speter SVN_ERR_ASSERT_NO_RETURN(! err); 395251881Speter return strcmp(url, session_url) == 0; 396251881Speter} 397251881Speter 398251881Speter/* Return a new merge_source_t structure, allocated in RESULT_POOL, 399251881Speter * initialized with deep copies of LOC1 and LOC2 and ANCESTRAL. */ 400251881Speterstatic merge_source_t * 401251881Spetermerge_source_create(const svn_client__pathrev_t *loc1, 402251881Speter const svn_client__pathrev_t *loc2, 403251881Speter svn_boolean_t ancestral, 404251881Speter apr_pool_t *result_pool) 405251881Speter{ 406251881Speter merge_source_t *s 407251881Speter = apr_palloc(result_pool, sizeof(*s)); 408251881Speter 409251881Speter s->loc1 = svn_client__pathrev_dup(loc1, result_pool); 410251881Speter s->loc2 = svn_client__pathrev_dup(loc2, result_pool); 411251881Speter s->ancestral = ancestral; 412251881Speter return s; 413251881Speter} 414251881Speter 415251881Speter/* Return a deep copy of SOURCE, allocated in RESULT_POOL. */ 416251881Speterstatic merge_source_t * 417251881Spetermerge_source_dup(const merge_source_t *source, 418251881Speter apr_pool_t *result_pool) 419251881Speter{ 420251881Speter merge_source_t *s = apr_palloc(result_pool, sizeof(*s)); 421251881Speter 422251881Speter s->loc1 = svn_client__pathrev_dup(source->loc1, result_pool); 423251881Speter s->loc2 = svn_client__pathrev_dup(source->loc2, result_pool); 424251881Speter s->ancestral = source->ancestral; 425251881Speter return s; 426251881Speter} 427251881Speter 428251881Speter/* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository 429251881Speter of LOCAL_ABSPATH. Use SCRATCH_POOL for temporary allocations. */ 430251881Speterstatic svn_error_t * 431251881Spetercheck_repos_match(const merge_target_t *target, 432251881Speter const char *local_abspath, 433251881Speter const char *url, 434251881Speter apr_pool_t *scratch_pool) 435251881Speter{ 436251881Speter if (!svn_uri__is_ancestor(target->loc.repos_root_url, url)) 437251881Speter return svn_error_createf( 438251881Speter SVN_ERR_UNSUPPORTED_FEATURE, NULL, 439251881Speter _("URL '%s' of '%s' is not in repository '%s'"), 440251881Speter url, svn_dirent_local_style(local_abspath, scratch_pool), 441251881Speter target->loc.repos_root_url); 442251881Speter 443251881Speter return SVN_NO_ERROR; 444251881Speter} 445251881Speter 446251881Speter/* Return TRUE iff the repository of LOCATION1 is the same as 447251881Speter * that of LOCATION2. If STRICT_URLS is true, the URLs must 448251881Speter * match (and the UUIDs, just to be sure), otherwise just the UUIDs must 449251881Speter * match and the URLs can differ (a common case is http versus https). */ 450251881Speterstatic svn_boolean_t 451251881Speteris_same_repos(const svn_client__pathrev_t *location1, 452251881Speter const svn_client__pathrev_t *location2, 453251881Speter svn_boolean_t strict_urls) 454251881Speter{ 455251881Speter if (strict_urls) 456251881Speter return (strcmp(location1->repos_root_url, location2->repos_root_url) == 0 457251881Speter && strcmp(location1->repos_uuid, location2->repos_uuid) == 0); 458251881Speter else 459251881Speter return (strcmp(location1->repos_uuid, location2->repos_uuid) == 0); 460251881Speter} 461251881Speter 462251881Speter/* If the repository identified of LOCATION1 is not the same as that 463251881Speter * of LOCATION2, throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES 464251881Speter * error mentioning PATH1 and PATH2. For STRICT_URLS, see is_same_repos(). 465251881Speter */ 466251881Speterstatic svn_error_t * 467251881Spetercheck_same_repos(const svn_client__pathrev_t *location1, 468251881Speter const char *path1, 469251881Speter const svn_client__pathrev_t *location2, 470251881Speter const char *path2, 471251881Speter svn_boolean_t strict_urls, 472251881Speter apr_pool_t *scratch_pool) 473251881Speter{ 474251881Speter if (! is_same_repos(location1, location2, strict_urls)) 475251881Speter return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, 476251881Speter _("'%s' must be from the same repository as " 477251881Speter "'%s'"), path1, path2); 478251881Speter return SVN_NO_ERROR; 479251881Speter} 480251881Speter 481251881Speter/* Store LOCAL_ABSPATH in PATH_HASH after duplicating it into the pool 482251881Speter containing PATH_HASH. */ 483251881Speterstatic APR_INLINE void 484251881Speterstore_path(apr_hash_t *path_hash, const char *local_abspath) 485251881Speter{ 486251881Speter const char *dup_path = apr_pstrdup(apr_hash_pool_get(path_hash), 487251881Speter local_abspath); 488251881Speter 489251881Speter svn_hash_sets(path_hash, dup_path, dup_path); 490251881Speter} 491251881Speter 492251881Speter/* Store LOCAL_ABSPATH in *PATH_HASH_P after duplicating it into the pool 493251881Speter containing *PATH_HASH_P. If *PATH_HASH_P is NULL, then first set 494251881Speter *PATH_HASH_P to a new hash allocated from POOL. */ 495251881Speterstatic APR_INLINE void 496251881Speteralloc_and_store_path(apr_hash_t **path_hash_p, 497251881Speter const char *local_abspath, 498251881Speter apr_pool_t *pool) 499251881Speter{ 500251881Speter if (! *path_hash_p) 501251881Speter *path_hash_p = apr_hash_make(pool); 502251881Speter store_path(*path_hash_p, local_abspath); 503251881Speter} 504251881Speter 505251881Speter/* Return whether any WC path was put in conflict by the merge 506251881Speter operation corresponding to MERGE_B. */ 507251881Speterstatic APR_INLINE svn_boolean_t 508251881Speteris_path_conflicted_by_merge(merge_cmd_baton_t *merge_b) 509251881Speter{ 510251881Speter return (merge_b->conflicted_paths && 511251881Speter apr_hash_count(merge_b->conflicted_paths) > 0); 512251881Speter} 513251881Speter 514251881Speter/* Return a state indicating whether the WC metadata matches the 515251881Speter * node kind on disk of the local path LOCAL_ABSPATH. 516251881Speter * Use MERGE_B to determine the dry-run details; particularly, if a dry run 517251881Speter * noted that it deleted this path, assume matching node kinds (as if both 518251881Speter * kinds were svn_node_none). 519251881Speter * 520251881Speter * - Return svn_wc_notify_state_inapplicable if the node kind matches. 521251881Speter * - Return 'obstructed' if there is a node on disk where none or a 522251881Speter * different kind is expected, or if the disk node cannot be read. 523251881Speter * - Return 'missing' if there is no node on disk but one is expected. 524251881Speter * Also return 'missing' for server-excluded nodes (not here due to 525251881Speter * authz or other reasons determined by the server). 526251881Speter * 527251881Speter * Optionally return a bit more info for interested users. 528251881Speter **/ 529251881Speterstatic svn_error_t * 530251881Speterperform_obstruction_check(svn_wc_notify_state_t *obstruction_state, 531251881Speter svn_boolean_t *deleted, 532251881Speter svn_boolean_t *excluded, 533251881Speter svn_node_kind_t *kind, 534251881Speter svn_depth_t *parent_depth, 535251881Speter const merge_cmd_baton_t *merge_b, 536251881Speter const char *local_abspath, 537251881Speter apr_pool_t *scratch_pool) 538251881Speter{ 539251881Speter svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx; 540251881Speter svn_node_kind_t wc_kind; 541251881Speter svn_boolean_t check_root; 542251881Speter 543251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 544251881Speter 545251881Speter *obstruction_state = svn_wc_notify_state_inapplicable; 546251881Speter 547251881Speter if (deleted) 548251881Speter *deleted = FALSE; 549251881Speter if (kind) 550251881Speter *kind = svn_node_none; 551251881Speter 552251881Speter if (kind == NULL) 553251881Speter kind = &wc_kind; 554251881Speter 555251881Speter check_root = ! strcmp(local_abspath, merge_b->target->abspath); 556251881Speter 557251881Speter SVN_ERR(svn_wc__check_for_obstructions(obstruction_state, 558251881Speter kind, 559251881Speter deleted, 560251881Speter excluded, 561251881Speter parent_depth, 562251881Speter wc_ctx, local_abspath, 563251881Speter check_root, 564251881Speter scratch_pool)); 565251881Speter return SVN_NO_ERROR; 566251881Speter} 567251881Speter 568251881Speter/* Create *LEFT and *RIGHT conflict versions for conflict victim 569251881Speter * at VICTIM_ABSPATH, with kind NODE_KIND, using information obtained 570251881Speter * from MERGE_SOURCE and TARGET. 571251881Speter * Allocate returned conflict versions in RESULT_POOL. */ 572251881Speterstatic svn_error_t * 573251881Spetermake_conflict_versions(const svn_wc_conflict_version_t **left, 574251881Speter const svn_wc_conflict_version_t **right, 575251881Speter const char *victim_abspath, 576251881Speter svn_node_kind_t node_kind, 577251881Speter const merge_source_t *merge_source, 578251881Speter const merge_target_t *target, 579251881Speter apr_pool_t *result_pool, 580251881Speter apr_pool_t *scratch_pool) 581251881Speter{ 582251881Speter const char *child = svn_dirent_skip_ancestor(target->abspath, 583251881Speter victim_abspath); 584251881Speter const char *left_relpath, *right_relpath; 585251881Speter 586251881Speter SVN_ERR_ASSERT(child != NULL); 587251881Speter left_relpath = svn_client__pathrev_relpath(merge_source->loc1, 588251881Speter scratch_pool); 589251881Speter right_relpath = svn_client__pathrev_relpath(merge_source->loc2, 590251881Speter scratch_pool); 591251881Speter 592251881Speter *left = svn_wc_conflict_version_create2( 593251881Speter merge_source->loc1->repos_root_url, 594251881Speter merge_source->loc1->repos_uuid, 595251881Speter svn_relpath_join(left_relpath, child, scratch_pool), 596251881Speter merge_source->loc1->rev, node_kind, result_pool); 597251881Speter 598251881Speter *right = svn_wc_conflict_version_create2( 599251881Speter merge_source->loc2->repos_root_url, 600251881Speter merge_source->loc2->repos_uuid, 601251881Speter svn_relpath_join(right_relpath, child, scratch_pool), 602251881Speter merge_source->loc2->rev, node_kind, result_pool); 603251881Speter 604251881Speter return SVN_NO_ERROR; 605251881Speter} 606251881Speter 607251881Speter/* Helper for filter_self_referential_mergeinfo() 608251881Speter 609251881Speter *MERGEINFO is a non-empty, non-null collection of mergeinfo. 610251881Speter 611251881Speter Remove all mergeinfo from *MERGEINFO that describes revision ranges 612251881Speter greater than REVISION. Put a copy of any removed mergeinfo, allocated 613251881Speter in POOL, into *YOUNGER_MERGEINFO. 614251881Speter 615251881Speter If no mergeinfo is removed from *MERGEINFO then *YOUNGER_MERGEINFO is set 616251881Speter to NULL. If all mergeinfo is removed from *MERGEINFO then *MERGEINFO is 617251881Speter set to NULL. 618251881Speter */ 619251881Speterstatic svn_error_t* 620251881Spetersplit_mergeinfo_on_revision(svn_mergeinfo_t *younger_mergeinfo, 621251881Speter svn_mergeinfo_t *mergeinfo, 622251881Speter svn_revnum_t revision, 623251881Speter apr_pool_t *pool) 624251881Speter{ 625251881Speter apr_hash_index_t *hi; 626251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 627251881Speter 628251881Speter *younger_mergeinfo = NULL; 629251881Speter for (hi = apr_hash_first(pool, *mergeinfo); hi; hi = apr_hash_next(hi)) 630251881Speter { 631251881Speter int i; 632251881Speter const char *merge_source_path = svn__apr_hash_index_key(hi); 633251881Speter svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 634251881Speter 635251881Speter svn_pool_clear(iterpool); 636251881Speter 637251881Speter for (i = 0; i < rangelist->nelts; i++) 638251881Speter { 639251881Speter svn_merge_range_t *range = 640251881Speter APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 641251881Speter if (range->end <= revision) 642251881Speter { 643251881Speter /* This entirely of this range is as old or older than 644251881Speter REVISION, so leave it in *MERGEINFO. */ 645251881Speter continue; 646251881Speter } 647251881Speter else 648251881Speter { 649251881Speter /* Since the rangelists in svn_mergeinfo_t's are sorted in 650251881Speter increasing order we know that part or all of *this* range 651251881Speter and *all* of the remaining ranges in *RANGELIST are younger 652251881Speter than REVISION. Remove the younger rangelists from 653251881Speter *MERGEINFO and put them in *YOUNGER_MERGEINFO. */ 654251881Speter int j; 655251881Speter svn_rangelist_t *younger_rangelist = 656251881Speter apr_array_make(pool, 1, sizeof(svn_merge_range_t *)); 657251881Speter 658251881Speter for (j = i; j < rangelist->nelts; j++) 659251881Speter { 660251881Speter svn_merge_range_t *younger_range = svn_merge_range_dup( 661251881Speter APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *), pool); 662251881Speter 663251881Speter /* REVISION might intersect with the first range where 664251881Speter range->end > REVISION. If that is the case then split 665251881Speter the current range into two, putting the younger half 666251881Speter into *YOUNGER_MERGEINFO and leaving the older half in 667251881Speter *MERGEINFO. */ 668251881Speter if (j == i && range->start + 1 <= revision) 669251881Speter younger_range->start = range->end = revision; 670251881Speter 671251881Speter APR_ARRAY_PUSH(younger_rangelist, svn_merge_range_t *) = 672251881Speter younger_range; 673251881Speter } 674251881Speter 675251881Speter /* So far we've only been manipulating rangelists, now we 676251881Speter actually create *YOUNGER_MERGEINFO and then remove the older 677251881Speter ranges from *MERGEINFO */ 678251881Speter if (!(*younger_mergeinfo)) 679251881Speter *younger_mergeinfo = apr_hash_make(pool); 680251881Speter svn_hash_sets(*younger_mergeinfo, merge_source_path, 681251881Speter younger_rangelist); 682251881Speter SVN_ERR(svn_mergeinfo_remove2(mergeinfo, *younger_mergeinfo, 683251881Speter *mergeinfo, TRUE, pool, iterpool)); 684251881Speter break; /* ...out of for (i = 0; i < rangelist->nelts; i++) */ 685251881Speter } 686251881Speter } 687251881Speter } 688251881Speter 689251881Speter svn_pool_destroy(iterpool); 690251881Speter 691251881Speter return SVN_NO_ERROR; 692251881Speter} 693251881Speter 694251881Speter 695251881Speter/* Make a copy of PROPCHANGES (array of svn_prop_t) into *TRIMMED_PROPCHANGES, 696251881Speter omitting any svn:mergeinfo changes. */ 697251881Speterstatic svn_error_t * 698251881Speteromit_mergeinfo_changes(apr_array_header_t **trimmed_propchanges, 699251881Speter const apr_array_header_t *propchanges, 700251881Speter apr_pool_t *result_pool) 701251881Speter{ 702251881Speter int i; 703251881Speter 704251881Speter *trimmed_propchanges = apr_array_make(result_pool, 705251881Speter propchanges->nelts, 706251881Speter sizeof(svn_prop_t)); 707251881Speter 708251881Speter for (i = 0; i < propchanges->nelts; ++i) 709251881Speter { 710251881Speter const svn_prop_t *change = &APR_ARRAY_IDX(propchanges, i, svn_prop_t); 711251881Speter 712251881Speter /* If this property is not svn:mergeinfo, then copy it. */ 713251881Speter if (strcmp(change->name, SVN_PROP_MERGEINFO) != 0) 714251881Speter APR_ARRAY_PUSH(*trimmed_propchanges, svn_prop_t) = *change; 715251881Speter } 716251881Speter 717251881Speter return SVN_NO_ERROR; 718251881Speter} 719251881Speter 720251881Speter 721251881Speter/* Helper for merge_props_changed(). 722251881Speter 723251881Speter *PROPS is an array of svn_prop_t structures representing regular properties 724251881Speter to be added to the working copy TARGET_ABSPATH. 725251881Speter 726251881Speter The merge source and target are assumed to be in the same repository. 727251881Speter 728251881Speter Filter out mergeinfo property additions to TARGET_ABSPATH when 729251881Speter those additions refer to the same line of history as TARGET_ABSPATH as 730251881Speter described below. 731251881Speter 732251881Speter Examine the added mergeinfo, looking at each range (or single rev) 733251881Speter of each source path. If a source_path/range refers to the same line of 734251881Speter history as TARGET_ABSPATH (pegged at its base revision), then filter out 735251881Speter that range. If the entire rangelist for a given path is filtered then 736251881Speter filter out the path as well. 737251881Speter 738251881Speter RA_SESSION is an open RA session to the repository 739251881Speter in which both the source and target live, else RA_SESSION is not used. It 740251881Speter may be temporarily reparented as needed by this function. 741251881Speter 742251881Speter Use CTX for any further client operations. 743251881Speter 744251881Speter If any filtering occurs, set outgoing *PROPS to a shallow copy (allocated 745251881Speter in POOL) of incoming *PROPS minus the filtered mergeinfo. */ 746251881Speterstatic svn_error_t * 747251881Speterfilter_self_referential_mergeinfo(apr_array_header_t **props, 748251881Speter const char *target_abspath, 749251881Speter svn_ra_session_t *ra_session, 750251881Speter svn_client_ctx_t *ctx, 751251881Speter apr_pool_t *pool) 752251881Speter{ 753251881Speter apr_array_header_t *adjusted_props; 754251881Speter int i; 755251881Speter apr_pool_t *iterpool; 756251881Speter svn_boolean_t is_copy; 757251881Speter const char *repos_relpath; 758251881Speter svn_client__pathrev_t target_base; 759251881Speter 760251881Speter /* If PATH itself has been added there is no need to filter. */ 761251881Speter SVN_ERR(svn_wc__node_get_origin(&is_copy, &target_base.rev, &repos_relpath, 762251881Speter &target_base.repos_root_url, 763251881Speter &target_base.repos_uuid, NULL, 764251881Speter ctx->wc_ctx, target_abspath, FALSE, 765251881Speter pool, pool)); 766251881Speter 767251881Speter if (is_copy || !repos_relpath) 768251881Speter return SVN_NO_ERROR; /* A copy or a local addition */ 769251881Speter 770251881Speter target_base.url = svn_path_url_add_component2(target_base.repos_root_url, 771251881Speter repos_relpath, pool); 772251881Speter 773251881Speter adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t)); 774251881Speter iterpool = svn_pool_create(pool); 775251881Speter for (i = 0; i < (*props)->nelts; ++i) 776251881Speter { 777251881Speter svn_prop_t *prop = &APR_ARRAY_IDX((*props), i, svn_prop_t); 778251881Speter 779251881Speter svn_mergeinfo_t mergeinfo, younger_mergeinfo; 780251881Speter svn_mergeinfo_t filtered_mergeinfo = NULL; 781251881Speter svn_mergeinfo_t filtered_younger_mergeinfo = NULL; 782251881Speter svn_error_t *err; 783251881Speter 784251881Speter /* If this property isn't mergeinfo or is NULL valued (i.e. prop removal) 785251881Speter or empty mergeinfo it does not require any special handling. There 786251881Speter is nothing to filter out of empty mergeinfo and the concept of 787251881Speter filtering doesn't apply if we are trying to remove mergeinfo 788251881Speter entirely. */ 789251881Speter if ((strcmp(prop->name, SVN_PROP_MERGEINFO) != 0) 790251881Speter || (! prop->value) /* Removal of mergeinfo */ 791251881Speter || (! prop->value->len)) /* Empty mergeinfo */ 792251881Speter { 793251881Speter APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop; 794251881Speter continue; 795251881Speter } 796251881Speter 797251881Speter svn_pool_clear(iterpool); 798251881Speter 799251881Speter /* Non-empty mergeinfo; filter self-referential mergeinfo out. */ 800251881Speter 801251881Speter /* Parse the incoming mergeinfo to allow easier manipulation. */ 802251881Speter err = svn_mergeinfo_parse(&mergeinfo, prop->value->data, iterpool); 803251881Speter 804251881Speter if (err) 805251881Speter { 806251881Speter /* Issue #3896: If we can't parse it, we certainly can't 807251881Speter filter it. */ 808251881Speter if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) 809251881Speter { 810251881Speter svn_error_clear(err); 811251881Speter APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop; 812251881Speter continue; 813251881Speter } 814251881Speter else 815251881Speter { 816251881Speter return svn_error_trace(err); 817251881Speter } 818251881Speter } 819251881Speter 820251881Speter /* The working copy target PATH is at BASE_REVISION. Divide the 821251881Speter incoming mergeinfo into two groups. One where all revision ranges 822251881Speter are as old or older than BASE_REVISION and one where all revision 823251881Speter ranges are younger. 824251881Speter 825251881Speter Note: You may be wondering why we do this. 826251881Speter 827251881Speter For the incoming mergeinfo "older" than target's base revision we 828251881Speter can filter out self-referential mergeinfo efficiently using 829251881Speter svn_client__get_history_as_mergeinfo(). We simply look at PATH's 830251881Speter natural history as mergeinfo and remove that from any incoming 831251881Speter mergeinfo. 832251881Speter 833251881Speter For mergeinfo "younger" than the base revision we can't use 834251881Speter svn_ra_get_location_segments() to look into PATH's future 835251881Speter history. Instead we must use svn_client__repos_locations() and 836251881Speter look at each incoming source/range individually and see if PATH 837251881Speter at its base revision and PATH at the start of the incoming range 838251881Speter exist on the same line of history. If they do then we can filter 839251881Speter out the incoming range. But since we have to do this for each 840251881Speter range there is a substantial performance penalty to pay if the 841251881Speter incoming ranges are not contiguous, i.e. we call 842251881Speter svn_client__repos_locations for each discrete range and incur 843251881Speter the cost of a roundtrip communication with the repository. */ 844251881Speter SVN_ERR(split_mergeinfo_on_revision(&younger_mergeinfo, 845251881Speter &mergeinfo, 846251881Speter target_base.rev, 847251881Speter iterpool)); 848251881Speter 849251881Speter /* Filter self-referential mergeinfo from younger_mergeinfo. */ 850251881Speter if (younger_mergeinfo) 851251881Speter { 852251881Speter apr_hash_index_t *hi; 853251881Speter const char *merge_source_root_url; 854251881Speter 855251881Speter SVN_ERR(svn_ra_get_repos_root2(ra_session, 856251881Speter &merge_source_root_url, iterpool)); 857251881Speter 858251881Speter for (hi = apr_hash_first(iterpool, younger_mergeinfo); 859251881Speter hi; hi = apr_hash_next(hi)) 860251881Speter { 861251881Speter int j; 862251881Speter const char *source_path = svn__apr_hash_index_key(hi); 863251881Speter svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 864251881Speter const char *merge_source_url; 865251881Speter svn_rangelist_t *adjusted_rangelist = 866251881Speter apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *)); 867251881Speter 868251881Speter merge_source_url = 869251881Speter svn_path_url_add_component2(merge_source_root_url, 870251881Speter source_path + 1, iterpool); 871251881Speter 872251881Speter for (j = 0; j < rangelist->nelts; j++) 873251881Speter { 874251881Speter svn_error_t *err2; 875251881Speter svn_client__pathrev_t *start_loc; 876251881Speter svn_merge_range_t *range = 877251881Speter APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *); 878251881Speter 879251881Speter /* Because the merge source normalization code 880251881Speter ensures mergeinfo refers to real locations on 881251881Speter the same line of history, there's no need to 882251881Speter look at the whole range, just the start. */ 883251881Speter 884251881Speter /* Check if PATH@BASE_REVISION exists at 885251881Speter RANGE->START on the same line of history. 886251881Speter (start+1 because RANGE->start is not inclusive.) */ 887251881Speter err2 = svn_client__repos_location(&start_loc, ra_session, 888251881Speter &target_base, 889251881Speter range->start + 1, 890251881Speter ctx, iterpool, iterpool); 891251881Speter if (err2) 892251881Speter { 893251881Speter if (err2->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES 894251881Speter || err2->apr_err == SVN_ERR_FS_NOT_FOUND 895251881Speter || err2->apr_err == SVN_ERR_FS_NO_SUCH_REVISION) 896251881Speter { 897251881Speter /* PATH@BASE_REVISION didn't exist at 898251881Speter RANGE->START + 1 or is unrelated to the 899251881Speter resource PATH@RANGE->START. Some of the 900251881Speter requested revisions may not even exist in 901251881Speter the repository; a real possibility since 902251881Speter mergeinfo is hand editable. In all of these 903251881Speter cases clear and ignore the error and don't 904251881Speter do any filtering. 905251881Speter 906251881Speter Note: In this last case it is possible that 907251881Speter we will allow self-referential mergeinfo to 908251881Speter be applied, but fixing it here is potentially 909251881Speter very costly in terms of finding what part of 910251881Speter a range is actually valid. Simply allowing 911251881Speter the merge to proceed without filtering the 912251881Speter offending range seems the least worst 913251881Speter option. */ 914251881Speter svn_error_clear(err2); 915251881Speter err2 = NULL; 916251881Speter APR_ARRAY_PUSH(adjusted_rangelist, 917251881Speter svn_merge_range_t *) = range; 918251881Speter } 919251881Speter else 920251881Speter { 921251881Speter return svn_error_trace(err2); 922251881Speter } 923251881Speter } 924251881Speter else 925251881Speter { 926251881Speter /* PATH@BASE_REVISION exists on the same 927251881Speter line of history at RANGE->START and RANGE->END. 928251881Speter Now check that PATH@BASE_REVISION's path 929251881Speter names at RANGE->START and RANGE->END are the same. 930251881Speter If the names are not the same then the mergeinfo 931251881Speter describing PATH@RANGE->START through 932251881Speter PATH@RANGE->END actually belong to some other 933251881Speter line of history and we want to record this 934251881Speter mergeinfo, not filter it. */ 935251881Speter if (strcmp(start_loc->url, merge_source_url) != 0) 936251881Speter { 937251881Speter APR_ARRAY_PUSH(adjusted_rangelist, 938251881Speter svn_merge_range_t *) = range; 939251881Speter } 940251881Speter } 941251881Speter /* else no need to add, this mergeinfo is 942251881Speter all on the same line of history. */ 943251881Speter } /* for (j = 0; j < rangelist->nelts; j++) */ 944251881Speter 945251881Speter /* Add any rangelists for source_path that are not 946251881Speter self-referential. */ 947251881Speter if (adjusted_rangelist->nelts) 948251881Speter { 949251881Speter if (!filtered_younger_mergeinfo) 950251881Speter filtered_younger_mergeinfo = apr_hash_make(iterpool); 951251881Speter svn_hash_sets(filtered_younger_mergeinfo, source_path, 952251881Speter adjusted_rangelist); 953251881Speter } 954251881Speter 955251881Speter } /* Iteration over each merge source in younger_mergeinfo. */ 956251881Speter } /* if (younger_mergeinfo) */ 957251881Speter 958251881Speter /* Filter self-referential mergeinfo from "older" mergeinfo. */ 959251881Speter if (mergeinfo) 960251881Speter { 961251881Speter svn_mergeinfo_t implicit_mergeinfo; 962251881Speter 963251881Speter SVN_ERR(svn_client__get_history_as_mergeinfo( 964251881Speter &implicit_mergeinfo, NULL, 965251881Speter &target_base, target_base.rev, SVN_INVALID_REVNUM, 966251881Speter ra_session, ctx, iterpool)); 967251881Speter 968251881Speter /* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */ 969251881Speter SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo, 970251881Speter implicit_mergeinfo, 971251881Speter mergeinfo, TRUE, iterpool, iterpool)); 972251881Speter } 973251881Speter 974251881Speter /* Combine whatever older and younger filtered mergeinfo exists 975251881Speter into filtered_mergeinfo. */ 976251881Speter if (filtered_mergeinfo && filtered_younger_mergeinfo) 977251881Speter SVN_ERR(svn_mergeinfo_merge2(filtered_mergeinfo, 978251881Speter filtered_younger_mergeinfo, iterpool, 979251881Speter iterpool)); 980251881Speter else if (filtered_younger_mergeinfo) 981251881Speter filtered_mergeinfo = filtered_younger_mergeinfo; 982251881Speter 983251881Speter /* If there is any incoming mergeinfo remaining after filtering 984251881Speter then put it in adjusted_props. */ 985251881Speter if (filtered_mergeinfo && apr_hash_count(filtered_mergeinfo)) 986251881Speter { 987251881Speter /* Convert filtered_mergeinfo to a svn_prop_t and put it 988251881Speter back in the array. */ 989251881Speter svn_string_t *filtered_mergeinfo_str; 990251881Speter svn_prop_t *adjusted_prop = apr_pcalloc(pool, 991251881Speter sizeof(*adjusted_prop)); 992251881Speter SVN_ERR(svn_mergeinfo_to_string(&filtered_mergeinfo_str, 993251881Speter filtered_mergeinfo, 994251881Speter pool)); 995251881Speter adjusted_prop->name = SVN_PROP_MERGEINFO; 996251881Speter adjusted_prop->value = filtered_mergeinfo_str; 997251881Speter APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *adjusted_prop; 998251881Speter } 999251881Speter } 1000251881Speter svn_pool_destroy(iterpool); 1001251881Speter 1002251881Speter *props = adjusted_props; 1003251881Speter return SVN_NO_ERROR; 1004251881Speter} 1005251881Speter 1006251881Speter/* Prepare a set of property changes PROPCHANGES to be used for a merge 1007251881Speter operation on LOCAL_ABSPATH. 1008251881Speter 1009251881Speter Remove all non-regular prop-changes (entry-props and WC-props). 1010251881Speter Remove all non-mergeinfo prop-changes if it's a record-only merge. 1011251881Speter Remove self-referential mergeinfo (### in some cases...) 1012251881Speter Remove foreign-repository mergeinfo (### in some cases...) 1013251881Speter 1014251881Speter Store the resulting property changes in *PROP_UPDATES. 1015251881Speter Store information on where mergeinfo is updated in MERGE_B. 1016251881Speter 1017251881Speter Used for both file and directory property merges. */ 1018251881Speterstatic svn_error_t * 1019251881Speterprepare_merge_props_changed(const apr_array_header_t **prop_updates, 1020251881Speter const char *local_abspath, 1021251881Speter const apr_array_header_t *propchanges, 1022251881Speter merge_cmd_baton_t *merge_b, 1023251881Speter apr_pool_t *result_pool, 1024251881Speter apr_pool_t *scratch_pool) 1025251881Speter{ 1026251881Speter apr_array_header_t *props; 1027251881Speter 1028251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 1029251881Speter 1030251881Speter /* We only want to merge "regular" version properties: by 1031251881Speter definition, 'svn merge' shouldn't touch any data within .svn/ */ 1032251881Speter SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props, 1033251881Speter result_pool)); 1034251881Speter 1035251881Speter /* If we are only applying mergeinfo changes then we need to do 1036251881Speter additional filtering of PROPS so it contains only mergeinfo changes. */ 1037251881Speter if (merge_b->record_only && props->nelts) 1038251881Speter { 1039251881Speter apr_array_header_t *mergeinfo_props = 1040251881Speter apr_array_make(result_pool, 1, sizeof(svn_prop_t)); 1041251881Speter int i; 1042251881Speter 1043251881Speter for (i = 0; i < props->nelts; i++) 1044251881Speter { 1045251881Speter svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t); 1046251881Speter 1047251881Speter if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0) 1048251881Speter { 1049251881Speter APR_ARRAY_PUSH(mergeinfo_props, svn_prop_t) = *prop; 1050251881Speter break; 1051251881Speter } 1052251881Speter } 1053251881Speter props = mergeinfo_props; 1054251881Speter } 1055251881Speter 1056251881Speter if (props->nelts) 1057251881Speter { 1058251881Speter /* Issue #3383: We don't want mergeinfo from a foreign repos. 1059251881Speter 1060251881Speter If this is a merge from a foreign repository we must strip all 1061251881Speter incoming mergeinfo (including mergeinfo deletions). */ 1062251881Speter if (! merge_b->same_repos) 1063251881Speter SVN_ERR(omit_mergeinfo_changes(&props, props, result_pool)); 1064251881Speter 1065251881Speter /* If this is a forward merge then don't add new mergeinfo to 1066251881Speter PATH that is already part of PATH's own history, see 1067251881Speter http://svn.haxx.se/dev/archive-2008-09/0006.shtml. If the 1068251881Speter merge sources are not ancestral then there is no concept of a 1069251881Speter 'forward' or 'reverse' merge and we filter unconditionally. */ 1070251881Speter if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev 1071251881Speter || !merge_b->merge_source.ancestral) 1072251881Speter { 1073251881Speter if (HONOR_MERGEINFO(merge_b) || merge_b->reintegrate_merge) 1074251881Speter SVN_ERR(filter_self_referential_mergeinfo(&props, 1075251881Speter local_abspath, 1076251881Speter merge_b->ra_session2, 1077251881Speter merge_b->ctx, 1078251881Speter result_pool)); 1079251881Speter } 1080251881Speter } 1081251881Speter *prop_updates = props; 1082251881Speter 1083251881Speter /* Make a record in BATON if we find a PATH where mergeinfo is added 1084251881Speter where none existed previously or PATH is having its existing 1085251881Speter mergeinfo deleted. */ 1086251881Speter if (props->nelts) 1087251881Speter { 1088251881Speter int i; 1089251881Speter 1090251881Speter for (i = 0; i < props->nelts; ++i) 1091251881Speter { 1092251881Speter svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t); 1093251881Speter 1094251881Speter if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0) 1095251881Speter { 1096251881Speter /* Does LOCAL_ABSPATH have any pristine mergeinfo? */ 1097251881Speter svn_boolean_t has_pristine_mergeinfo = FALSE; 1098251881Speter apr_hash_t *pristine_props; 1099251881Speter 1100251881Speter SVN_ERR(svn_wc_get_pristine_props(&pristine_props, 1101251881Speter merge_b->ctx->wc_ctx, 1102251881Speter local_abspath, 1103251881Speter scratch_pool, 1104251881Speter scratch_pool)); 1105251881Speter 1106251881Speter if (pristine_props 1107251881Speter && svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO)) 1108251881Speter has_pristine_mergeinfo = TRUE; 1109251881Speter 1110251881Speter if (!has_pristine_mergeinfo && prop->value) 1111251881Speter { 1112251881Speter alloc_and_store_path(&merge_b->paths_with_new_mergeinfo, 1113251881Speter local_abspath, merge_b->pool); 1114251881Speter } 1115251881Speter else if (has_pristine_mergeinfo && !prop->value) 1116251881Speter { 1117251881Speter alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo, 1118251881Speter local_abspath, merge_b->pool); 1119251881Speter } 1120251881Speter } 1121251881Speter } 1122251881Speter } 1123251881Speter 1124251881Speter return SVN_NO_ERROR; 1125251881Speter} 1126251881Speter 1127251881Speter#define CONFLICT_REASON_NONE ((svn_wc_conflict_reason_t)-1) 1128251881Speter#define CONFLICT_REASON_SKIP ((svn_wc_conflict_reason_t)-2) 1129251881Speter#define CONFLICT_REASON_SKIP_WC ((svn_wc_conflict_reason_t)-3) 1130251881Speter 1131251881Speter/* Baton used for testing trees for being editted while performing tree 1132251881Speter conflict detection for incoming deletes */ 1133251881Speterstruct dir_delete_baton_t 1134251881Speter{ 1135251881Speter /* Reference to dir baton of directory that is the root of the deletion */ 1136251881Speter struct merge_dir_baton_t *del_root; 1137251881Speter 1138251881Speter /* Boolean indicating that some edit is found. Allows avoiding more work */ 1139251881Speter svn_boolean_t found_edit; 1140251881Speter 1141251881Speter /* A list of paths that are compared. Kept up to date until FOUND_EDIT is 1142251881Speter set to TRUE */ 1143251881Speter apr_hash_t *compared_abspaths; 1144251881Speter}; 1145251881Speter 1146251881Speter/* Baton for the merge_dir_*() functions. Initialized in merge_dir_opened() */ 1147251881Speterstruct merge_dir_baton_t 1148251881Speter{ 1149251881Speter /* Reference to the parent baton, unless the parent is the anchor, in which 1150251881Speter case PARENT_BATON is NULL */ 1151251881Speter struct merge_dir_baton_t *parent_baton; 1152251881Speter 1153251881Speter /* The pool containing this baton. Use for RESULT_POOL for storing in this 1154251881Speter baton */ 1155251881Speter apr_pool_t *pool; 1156251881Speter 1157251881Speter /* This directory doesn't have a representation in the working copy, so any 1158251881Speter operation on it will be skipped and possibly cause a tree conflict on the 1159251881Speter shadow root */ 1160251881Speter svn_boolean_t shadowed; 1161251881Speter 1162251881Speter /* This node or one of its descendants received operational changes from the 1163251881Speter merge. If this node is the shadow root its tree conflict status has been 1164251881Speter applied */ 1165251881Speter svn_boolean_t edited; 1166251881Speter 1167251881Speter /* If a tree conflict will be installed once edited, it's reason. If a skip 1168251881Speter should be produced its reason. Otherwise CONFLICT_REASON_NONE for no tree 1169251881Speter conflict. 1170251881Speter 1171251881Speter Special values: 1172251881Speter CONFLICT_REASON_SKIP: 1173251881Speter The node will be skipped with content and property state as stored in 1174251881Speter SKIP_REASON. 1175251881Speter 1176251881Speter CONFLICT_REASON_SKIP_WC: 1177251881Speter The node will be skipped as an obstructing working copy. 1178251881Speter */ 1179251881Speter svn_wc_conflict_reason_t tree_conflict_reason; 1180251881Speter svn_wc_conflict_action_t tree_conflict_action; 1181251881Speter 1182251881Speter /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to 1183251881Speter add to the notification */ 1184251881Speter svn_wc_notify_state_t skip_reason; 1185251881Speter 1186251881Speter /* TRUE if the node was added by this merge. Otherwise FALSE */ 1187251881Speter svn_boolean_t added; 1188251881Speter svn_boolean_t add_is_replace; /* Add is second part of replace */ 1189251881Speter 1190251881Speter /* TRUE if we are taking over an existing directory as addition, otherwise 1191251881Speter FALSE. */ 1192251881Speter svn_boolean_t add_existing; 1193251881Speter 1194251881Speter /* NULL, or an hashtable mapping const char * local_abspaths to 1195251881Speter const char *kind mapping, containing deleted nodes that still need a delete 1196251881Speter notification (which may be a replaced notification if the node is not just 1197251881Speter deleted) */ 1198251881Speter apr_hash_t *pending_deletes; 1199251881Speter 1200251881Speter /* NULL, or an hashtable mapping const char * LOCAL_ABSPATHs to 1201251881Speter a const svn_wc_conflict_description2_t * instance, describing the just 1202251881Speter installed conflict */ 1203251881Speter apr_hash_t *new_tree_conflicts; 1204251881Speter 1205251881Speter /* If not NULL, a reference to the information of the delete test that is 1206251881Speter currently in progress. Allocated in the root-directory baton, referenced 1207251881Speter from all descendants */ 1208251881Speter struct dir_delete_baton_t *delete_state; 1209251881Speter}; 1210251881Speter 1211251881Speter/* Baton for the merge_dir_*() functions. Initialized in merge_file_opened() */ 1212251881Speterstruct merge_file_baton_t 1213251881Speter{ 1214251881Speter /* Reference to the parent baton, unless the parent is the anchor, in which 1215251881Speter case PARENT_BATON is NULL */ 1216251881Speter struct merge_dir_baton_t *parent_baton; 1217251881Speter 1218251881Speter /* This file doesn't have a representation in the working copy, so any 1219251881Speter operation on it will be skipped and possibly cause a tree conflict 1220251881Speter on the shadow root */ 1221251881Speter svn_boolean_t shadowed; 1222251881Speter 1223251881Speter /* This node received operational changes from the merge. If this node 1224251881Speter is the shadow root its tree conflict status has been applied */ 1225251881Speter svn_boolean_t edited; 1226251881Speter 1227251881Speter /* If a tree conflict will be installed once edited, it's reason. If a skip 1228251881Speter should be produced its reason. Some special values are defined. See the 1229251881Speter merge_tree_baton_t for an explanation. */ 1230251881Speter svn_wc_conflict_reason_t tree_conflict_reason; 1231251881Speter svn_wc_conflict_action_t tree_conflict_action; 1232251881Speter 1233251881Speter /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to 1234251881Speter add to the notification */ 1235251881Speter svn_wc_notify_state_t skip_reason; 1236251881Speter 1237251881Speter /* TRUE if the node was added by this merge. Otherwise FALSE */ 1238251881Speter svn_boolean_t added; 1239251881Speter svn_boolean_t add_is_replace; /* Add is second part of replace */ 1240251881Speter}; 1241251881Speter 1242251881Speter/* Forward declaration */ 1243251881Speterstatic svn_error_t * 1244251881Speternotify_merge_begin(merge_cmd_baton_t *merge_b, 1245251881Speter const char *local_abspath, 1246251881Speter svn_boolean_t delete_action, 1247251881Speter apr_pool_t *scratch_pool); 1248251881Speter 1249251881Speter/* Record the skip for future processing and (later) produce the 1250251881Speter skip notification */ 1251251881Speterstatic svn_error_t * 1252251881Speterrecord_skip(merge_cmd_baton_t *merge_b, 1253251881Speter const char *local_abspath, 1254251881Speter svn_node_kind_t kind, 1255251881Speter svn_wc_notify_action_t action, 1256251881Speter svn_wc_notify_state_t state, 1257251881Speter apr_pool_t *scratch_pool) 1258251881Speter{ 1259251881Speter if (merge_b->record_only) 1260251881Speter return SVN_NO_ERROR; /* ### Why? - Legacy compatibility */ 1261251881Speter 1262251881Speter if (merge_b->merge_source.ancestral 1263251881Speter || merge_b->reintegrate_merge) 1264251881Speter { 1265251881Speter store_path(merge_b->skipped_abspaths, local_abspath); 1266251881Speter } 1267251881Speter 1268251881Speter if (merge_b->ctx->notify_func2) 1269251881Speter { 1270251881Speter svn_wc_notify_t *notify; 1271251881Speter 1272251881Speter SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool)); 1273251881Speter 1274251881Speter notify = svn_wc_create_notify(local_abspath, action, scratch_pool); 1275251881Speter notify->kind = kind; 1276251881Speter notify->content_state = notify->prop_state = state; 1277251881Speter 1278251881Speter (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify, 1279251881Speter scratch_pool); 1280251881Speter } 1281251881Speter return SVN_NO_ERROR; 1282251881Speter} 1283251881Speter 1284251881Speter/* Record a tree conflict in the WC, unless this is a dry run or a record- 1285251881Speter * only merge, or if a tree conflict is already flagged for the VICTIM_PATH. 1286251881Speter * (The latter can happen if a merge-tracking-aware merge is doing multiple 1287251881Speter * editor drives because of a gap in the range of eligible revisions.) 1288251881Speter * 1289251881Speter * The tree conflict, with its victim specified by VICTIM_PATH, is 1290251881Speter * assumed to have happened during a merge using merge baton MERGE_B. 1291251881Speter * 1292251881Speter * NODE_KIND must be the node kind of "old" and "theirs" and "mine"; 1293251881Speter * this function cannot cope with node kind clashes. 1294251881Speter * ACTION and REASON correspond to the fields 1295251881Speter * of the same names in svn_wc_tree_conflict_description_t. 1296251881Speter */ 1297251881Speterstatic svn_error_t * 1298251881Speterrecord_tree_conflict(merge_cmd_baton_t *merge_b, 1299251881Speter const char *local_abspath, 1300251881Speter struct merge_dir_baton_t *parent_baton, 1301251881Speter svn_node_kind_t node_kind, 1302251881Speter svn_wc_conflict_action_t action, 1303251881Speter svn_wc_conflict_reason_t reason, 1304251881Speter const svn_wc_conflict_description2_t *existing_conflict, 1305251881Speter svn_boolean_t notify_tc, 1306251881Speter apr_pool_t *scratch_pool) 1307251881Speter{ 1308251881Speter svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx; 1309251881Speter 1310253734Speter if (merge_b->record_only) 1311253734Speter return SVN_NO_ERROR; 1312253734Speter 1313251881Speter if (merge_b->merge_source.ancestral 1314251881Speter || merge_b->reintegrate_merge) 1315251881Speter { 1316251881Speter store_path(merge_b->tree_conflicted_abspaths, local_abspath); 1317251881Speter } 1318251881Speter 1319251881Speter alloc_and_store_path(&merge_b->conflicted_paths, local_abspath, 1320251881Speter merge_b->pool); 1321251881Speter 1322253734Speter if (!merge_b->dry_run) 1323251881Speter { 1324251881Speter svn_wc_conflict_description2_t *conflict; 1325251881Speter const svn_wc_conflict_version_t *left; 1326251881Speter const svn_wc_conflict_version_t *right; 1327251881Speter apr_pool_t *result_pool = parent_baton ? parent_baton->pool 1328251881Speter : scratch_pool; 1329251881Speter 1330251881Speter if (reason == svn_wc_conflict_reason_deleted) 1331251881Speter { 1332251881Speter const char *moved_to_abspath; 1333251881Speter 1334251881Speter SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL, 1335251881Speter wc_ctx, local_abspath, 1336251881Speter scratch_pool, scratch_pool)); 1337251881Speter 1338251881Speter if (moved_to_abspath) 1339251881Speter { 1340251881Speter /* Local abspath itself has been moved away. If only a 1341251881Speter descendant is moved away, we call the node itself deleted */ 1342251881Speter reason = svn_wc_conflict_reason_moved_away; 1343251881Speter } 1344251881Speter } 1345251881Speter else if (reason == svn_wc_conflict_reason_added) 1346251881Speter { 1347251881Speter const char *moved_from_abspath; 1348251881Speter SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL, 1349251881Speter wc_ctx, local_abspath, 1350251881Speter scratch_pool, scratch_pool)); 1351251881Speter if (moved_from_abspath) 1352251881Speter reason = svn_wc_conflict_reason_moved_here; 1353251881Speter } 1354251881Speter 1355251881Speter SVN_ERR(make_conflict_versions(&left, &right, local_abspath, node_kind, 1356251881Speter &merge_b->merge_source, merge_b->target, 1357251881Speter result_pool, scratch_pool)); 1358251881Speter 1359251881Speter /* Fix up delete of file, add of dir replacement (or other way around) */ 1360251881Speter if (existing_conflict != NULL && existing_conflict->src_left_version) 1361251881Speter left = existing_conflict->src_left_version; 1362251881Speter 1363251881Speter conflict = svn_wc_conflict_description_create_tree2( 1364251881Speter local_abspath, node_kind, svn_wc_operation_merge, 1365251881Speter left, right, result_pool); 1366251881Speter 1367251881Speter conflict->action = action; 1368251881Speter conflict->reason = reason; 1369251881Speter 1370251881Speter /* May return SVN_ERR_WC_PATH_UNEXPECTED_STATUS */ 1371251881Speter if (existing_conflict) 1372251881Speter SVN_ERR(svn_wc__del_tree_conflict(wc_ctx, local_abspath, 1373251881Speter scratch_pool)); 1374251881Speter 1375251881Speter SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict, 1376251881Speter scratch_pool)); 1377251881Speter 1378251881Speter if (parent_baton) 1379251881Speter { 1380251881Speter if (! parent_baton->new_tree_conflicts) 1381251881Speter parent_baton->new_tree_conflicts = apr_hash_make(result_pool); 1382251881Speter 1383251881Speter svn_hash_sets(parent_baton->new_tree_conflicts, 1384251881Speter apr_pstrdup(result_pool, local_abspath), 1385251881Speter conflict); 1386251881Speter } 1387251881Speter 1388251881Speter /* ### TODO: Store in parent baton */ 1389251881Speter } 1390251881Speter 1391251881Speter /* On a replacement we currently get two tree conflicts */ 1392251881Speter if (merge_b->ctx->notify_func2 && notify_tc) 1393251881Speter { 1394251881Speter svn_wc_notify_t *notify; 1395251881Speter 1396251881Speter SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool)); 1397251881Speter 1398251881Speter notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict, 1399251881Speter scratch_pool); 1400251881Speter notify->kind = node_kind; 1401251881Speter 1402251881Speter (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify, 1403251881Speter scratch_pool); 1404251881Speter } 1405251881Speter 1406251881Speter return SVN_NO_ERROR; 1407251881Speter} 1408251881Speter 1409251881Speter/* Record the add for future processing and produce the 1410251881Speter update_add notification 1411251881Speter */ 1412251881Speterstatic svn_error_t * 1413251881Speterrecord_update_add(merge_cmd_baton_t *merge_b, 1414251881Speter const char *local_abspath, 1415251881Speter svn_node_kind_t kind, 1416251881Speter svn_boolean_t notify_replaced, 1417251881Speter apr_pool_t *scratch_pool) 1418251881Speter{ 1419251881Speter if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge) 1420251881Speter { 1421251881Speter store_path(merge_b->merged_abspaths, local_abspath); 1422251881Speter } 1423251881Speter 1424251881Speter if (merge_b->ctx->notify_func2) 1425251881Speter { 1426251881Speter svn_wc_notify_t *notify; 1427251881Speter svn_wc_notify_action_t action = svn_wc_notify_update_add; 1428251881Speter 1429251881Speter SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool)); 1430251881Speter 1431251881Speter if (notify_replaced) 1432251881Speter action = svn_wc_notify_update_replace; 1433251881Speter 1434251881Speter notify = svn_wc_create_notify(local_abspath, action, scratch_pool); 1435251881Speter notify->kind = kind; 1436251881Speter 1437251881Speter (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify, 1438251881Speter scratch_pool); 1439251881Speter } 1440251881Speter 1441251881Speter return SVN_NO_ERROR; 1442251881Speter} 1443251881Speter 1444251881Speter/* Record the update for future processing and produce the 1445251881Speter update_update notification */ 1446251881Speterstatic svn_error_t * 1447251881Speterrecord_update_update(merge_cmd_baton_t *merge_b, 1448251881Speter const char *local_abspath, 1449251881Speter svn_node_kind_t kind, 1450251881Speter svn_wc_notify_state_t content_state, 1451251881Speter svn_wc_notify_state_t prop_state, 1452251881Speter apr_pool_t *scratch_pool) 1453251881Speter{ 1454251881Speter if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge) 1455251881Speter { 1456251881Speter store_path(merge_b->merged_abspaths, local_abspath); 1457251881Speter } 1458251881Speter 1459251881Speter if (merge_b->ctx->notify_func2) 1460251881Speter { 1461251881Speter svn_wc_notify_t *notify; 1462251881Speter 1463251881Speter SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool)); 1464251881Speter 1465251881Speter notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_update, 1466251881Speter scratch_pool); 1467251881Speter notify->kind = kind; 1468251881Speter notify->content_state = content_state; 1469251881Speter notify->prop_state = prop_state; 1470251881Speter 1471251881Speter (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify, 1472251881Speter scratch_pool); 1473251881Speter } 1474251881Speter 1475251881Speter return SVN_NO_ERROR; 1476251881Speter} 1477251881Speter 1478251881Speter/* Record the delete for future processing and for (later) producing the 1479251881Speter update_delete notification */ 1480251881Speterstatic svn_error_t * 1481251881Speterrecord_update_delete(merge_cmd_baton_t *merge_b, 1482251881Speter struct merge_dir_baton_t *parent_db, 1483251881Speter const char *local_abspath, 1484251881Speter svn_node_kind_t kind, 1485251881Speter apr_pool_t *scratch_pool) 1486251881Speter{ 1487251881Speter /* Update the lists of merged, skipped, tree-conflicted and added paths. */ 1488251881Speter if (merge_b->merge_source.ancestral 1489251881Speter || merge_b->reintegrate_merge) 1490251881Speter { 1491251881Speter /* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we 1492251881Speter are now deleting it, then remove it from the list of added 1493251881Speter paths. */ 1494251881Speter svn_hash_sets(merge_b->added_abspaths, local_abspath, NULL); 1495251881Speter store_path(merge_b->merged_abspaths, local_abspath); 1496251881Speter } 1497251881Speter 1498251881Speter SVN_ERR(notify_merge_begin(merge_b, local_abspath, TRUE, scratch_pool)); 1499251881Speter 1500251881Speter if (parent_db) 1501251881Speter { 1502251881Speter const char *dup_abspath = apr_pstrdup(parent_db->pool, local_abspath); 1503251881Speter 1504251881Speter if (!parent_db->pending_deletes) 1505251881Speter parent_db->pending_deletes = apr_hash_make(parent_db->pool); 1506251881Speter 1507251881Speter svn_hash_sets(parent_db->pending_deletes, dup_abspath, 1508251881Speter svn_node_kind_to_word(kind)); 1509251881Speter } 1510251881Speter 1511251881Speter return SVN_NO_ERROR; 1512251881Speter} 1513251881Speter 1514251881Speter/* Notify the pending 'D'eletes, that were waiting to see if a matching 'A'dd 1515251881Speter might make them a 'R'eplace. */ 1516251881Speterstatic svn_error_t * 1517251881Speterhandle_pending_notifications(merge_cmd_baton_t *merge_b, 1518251881Speter struct merge_dir_baton_t *db, 1519251881Speter apr_pool_t *scratch_pool) 1520251881Speter{ 1521251881Speter if (merge_b->ctx->notify_func2 && db->pending_deletes) 1522251881Speter { 1523251881Speter apr_hash_index_t *hi; 1524251881Speter 1525251881Speter for (hi = apr_hash_first(scratch_pool, db->pending_deletes); 1526251881Speter hi; 1527251881Speter hi = apr_hash_next(hi)) 1528251881Speter { 1529251881Speter const char *del_abspath = svn__apr_hash_index_key(hi); 1530251881Speter svn_wc_notify_t *notify; 1531251881Speter 1532251881Speter notify = svn_wc_create_notify(del_abspath, 1533251881Speter svn_wc_notify_update_delete, 1534251881Speter scratch_pool); 1535251881Speter notify->kind = svn_node_kind_from_word( 1536251881Speter svn__apr_hash_index_val(hi)); 1537251881Speter 1538251881Speter (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, 1539251881Speter notify, scratch_pool); 1540251881Speter } 1541251881Speter 1542251881Speter db->pending_deletes = NULL; 1543251881Speter } 1544251881Speter return SVN_NO_ERROR; 1545251881Speter} 1546251881Speter 1547251881Speter/* Helper function for the merge_dir_*() and merge_file_*() functions. 1548251881Speter 1549251881Speter Installs and notifies pre-recorded tree conflicts and skips for 1550251881Speter ancestors of operational merges 1551251881Speter */ 1552251881Speterstatic svn_error_t * 1553251881Spetermark_dir_edited(merge_cmd_baton_t *merge_b, 1554251881Speter struct merge_dir_baton_t *db, 1555251881Speter const char *local_abspath, 1556251881Speter apr_pool_t *scratch_pool) 1557251881Speter{ 1558251881Speter /* ### Too much common code with mark_file_edited */ 1559251881Speter if (db->edited) 1560251881Speter return SVN_NO_ERROR; 1561251881Speter 1562251881Speter if (db->parent_baton && !db->parent_baton->edited) 1563251881Speter { 1564251881Speter const char *dir_abspath = svn_dirent_dirname(local_abspath, 1565251881Speter scratch_pool); 1566251881Speter 1567251881Speter SVN_ERR(mark_dir_edited(merge_b, db->parent_baton, dir_abspath, 1568251881Speter scratch_pool)); 1569251881Speter } 1570251881Speter 1571251881Speter db->edited = TRUE; 1572251881Speter 1573251881Speter if (! db->shadowed) 1574251881Speter return SVN_NO_ERROR; /* Easy out */ 1575251881Speter 1576251881Speter if (db->parent_baton 1577251881Speter && db->parent_baton->delete_state 1578251881Speter && db->tree_conflict_reason != CONFLICT_REASON_NONE) 1579251881Speter { 1580251881Speter db->parent_baton->delete_state->found_edit = TRUE; 1581251881Speter } 1582251881Speter else if (db->tree_conflict_reason == CONFLICT_REASON_SKIP 1583251881Speter || db->tree_conflict_reason == CONFLICT_REASON_SKIP_WC) 1584251881Speter { 1585251881Speter /* open_directory() decided not to flag a tree conflict, but 1586251881Speter for clarity we produce a skip for this node that 1587251881Speter most likely isn't touched by the merge itself */ 1588251881Speter 1589251881Speter if (merge_b->ctx->notify_func2) 1590251881Speter { 1591251881Speter svn_wc_notify_t *notify; 1592251881Speter 1593251881Speter SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, 1594251881Speter scratch_pool)); 1595251881Speter 1596251881Speter notify = svn_wc_create_notify( 1597251881Speter local_abspath, 1598251881Speter (db->tree_conflict_reason == CONFLICT_REASON_SKIP) 1599251881Speter ? svn_wc_notify_skip 1600251881Speter : svn_wc_notify_update_skip_obstruction, 1601251881Speter scratch_pool); 1602251881Speter notify->kind = svn_node_dir; 1603251881Speter notify->content_state = notify->prop_state = db->skip_reason; 1604251881Speter 1605251881Speter (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, 1606251881Speter notify, 1607251881Speter scratch_pool); 1608251881Speter } 1609251881Speter 1610251881Speter if (merge_b->merge_source.ancestral 1611251881Speter || merge_b->reintegrate_merge) 1612251881Speter { 1613251881Speter store_path(merge_b->skipped_abspaths, local_abspath); 1614251881Speter } 1615251881Speter } 1616251881Speter else if (db->tree_conflict_reason != CONFLICT_REASON_NONE) 1617251881Speter { 1618251881Speter /* open_directory() decided that a tree conflict should be raised */ 1619251881Speter 1620251881Speter SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton, 1621251881Speter svn_node_dir, db->tree_conflict_action, 1622251881Speter db->tree_conflict_reason, 1623251881Speter NULL, TRUE, 1624251881Speter scratch_pool)); 1625251881Speter } 1626251881Speter 1627251881Speter return SVN_NO_ERROR; 1628251881Speter} 1629251881Speter 1630251881Speter/* Helper function for the merge_file_*() functions. 1631251881Speter 1632251881Speter Installs and notifies pre-recorded tree conflicts and skips for 1633251881Speter ancestors of operational merges 1634251881Speter */ 1635251881Speterstatic svn_error_t * 1636251881Spetermark_file_edited(merge_cmd_baton_t *merge_b, 1637251881Speter struct merge_file_baton_t *fb, 1638251881Speter const char *local_abspath, 1639251881Speter apr_pool_t *scratch_pool) 1640251881Speter{ 1641251881Speter /* ### Too much common code with mark_dir_edited */ 1642251881Speter if (fb->edited) 1643251881Speter return SVN_NO_ERROR; 1644251881Speter 1645251881Speter if (fb->parent_baton && !fb->parent_baton->edited) 1646251881Speter { 1647251881Speter const char *dir_abspath = svn_dirent_dirname(local_abspath, 1648251881Speter scratch_pool); 1649251881Speter 1650251881Speter SVN_ERR(mark_dir_edited(merge_b, fb->parent_baton, dir_abspath, 1651251881Speter scratch_pool)); 1652251881Speter } 1653251881Speter 1654251881Speter fb->edited = TRUE; 1655251881Speter 1656251881Speter if (! fb->shadowed) 1657251881Speter return SVN_NO_ERROR; /* Easy out */ 1658251881Speter 1659251881Speter if (fb->parent_baton 1660251881Speter && fb->parent_baton->delete_state 1661251881Speter && fb->tree_conflict_reason != CONFLICT_REASON_NONE) 1662251881Speter { 1663251881Speter fb->parent_baton->delete_state->found_edit = TRUE; 1664251881Speter } 1665251881Speter else if (fb->tree_conflict_reason == CONFLICT_REASON_SKIP 1666251881Speter || fb->tree_conflict_reason == CONFLICT_REASON_SKIP_WC) 1667251881Speter { 1668251881Speter /* open_directory() decided not to flag a tree conflict, but 1669251881Speter for clarity we produce a skip for this node that 1670251881Speter most likely isn't touched by the merge itself */ 1671251881Speter 1672251881Speter if (merge_b->ctx->notify_func2) 1673251881Speter { 1674251881Speter svn_wc_notify_t *notify; 1675251881Speter 1676251881Speter SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, 1677251881Speter scratch_pool)); 1678251881Speter 1679251881Speter notify = svn_wc_create_notify(local_abspath, svn_wc_notify_skip, 1680251881Speter scratch_pool); 1681251881Speter notify->kind = svn_node_file; 1682251881Speter notify->content_state = notify->prop_state = fb->skip_reason; 1683251881Speter 1684251881Speter (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, 1685251881Speter notify, 1686251881Speter scratch_pool); 1687251881Speter } 1688251881Speter 1689251881Speter if (merge_b->merge_source.ancestral 1690251881Speter || merge_b->reintegrate_merge) 1691251881Speter { 1692251881Speter store_path(merge_b->skipped_abspaths, local_abspath); 1693251881Speter } 1694251881Speter } 1695251881Speter else if (fb->tree_conflict_reason != CONFLICT_REASON_NONE) 1696251881Speter { 1697251881Speter /* open_file() decided that a tree conflict should be raised */ 1698251881Speter 1699251881Speter SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton, 1700251881Speter svn_node_file, fb->tree_conflict_action, 1701251881Speter fb->tree_conflict_reason, 1702251881Speter NULL, TRUE, 1703251881Speter scratch_pool)); 1704251881Speter } 1705251881Speter 1706251881Speter return SVN_NO_ERROR; 1707251881Speter} 1708251881Speter 1709251881Speter/* An svn_diff_tree_processor_t function. 1710251881Speter 1711251881Speter Called before either merge_file_changed(), merge_file_added(), 1712251881Speter merge_file_deleted() or merge_file_closed(), unless it sets *SKIP to TRUE. 1713251881Speter 1714251881Speter When *SKIP is TRUE, the diff driver avoids work on getting the details 1715251881Speter for the closing callbacks. 1716251881Speter */ 1717251881Speterstatic svn_error_t * 1718251881Spetermerge_file_opened(void **new_file_baton, 1719251881Speter svn_boolean_t *skip, 1720251881Speter const char *relpath, 1721251881Speter const svn_diff_source_t *left_source, 1722251881Speter const svn_diff_source_t *right_source, 1723251881Speter const svn_diff_source_t *copyfrom_source, 1724251881Speter void *dir_baton, 1725251881Speter const struct svn_diff_tree_processor_t *processor, 1726251881Speter apr_pool_t *result_pool, 1727251881Speter apr_pool_t *scratch_pool) 1728251881Speter{ 1729251881Speter merge_cmd_baton_t *merge_b = processor->baton; 1730251881Speter struct merge_dir_baton_t *pdb = dir_baton; 1731251881Speter struct merge_file_baton_t *fb; 1732251881Speter const char *local_abspath = svn_dirent_join(merge_b->target->abspath, 1733251881Speter relpath, scratch_pool); 1734251881Speter 1735251881Speter fb = apr_pcalloc(result_pool, sizeof(*fb)); 1736251881Speter fb->tree_conflict_reason = CONFLICT_REASON_NONE; 1737251881Speter fb->tree_conflict_action = svn_wc_conflict_action_edit; 1738251881Speter fb->skip_reason = svn_wc_notify_state_unknown; 1739251881Speter 1740251881Speter *new_file_baton = fb; 1741251881Speter 1742251881Speter if (pdb) 1743251881Speter { 1744251881Speter fb->parent_baton = pdb; 1745251881Speter fb->shadowed = pdb->shadowed; 1746251881Speter fb->skip_reason = pdb->skip_reason; 1747251881Speter } 1748251881Speter 1749251881Speter if (fb->shadowed) 1750251881Speter { 1751251881Speter /* An ancestor is tree conflicted. Nothing to do here. */ 1752251881Speter } 1753251881Speter else if (left_source != NULL) 1754251881Speter { 1755251881Speter /* Node is expected to be a file, which will be changed or deleted. */ 1756251881Speter svn_node_kind_t kind; 1757251881Speter svn_boolean_t is_deleted; 1758251881Speter svn_boolean_t excluded; 1759251881Speter svn_depth_t parent_depth; 1760251881Speter 1761251881Speter if (! right_source) 1762251881Speter fb->tree_conflict_action = svn_wc_conflict_action_delete; 1763251881Speter 1764251881Speter { 1765251881Speter svn_wc_notify_state_t obstr_state; 1766251881Speter 1767251881Speter SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded, 1768251881Speter &kind, &parent_depth, 1769251881Speter merge_b, local_abspath, 1770251881Speter scratch_pool)); 1771251881Speter 1772251881Speter if (obstr_state != svn_wc_notify_state_inapplicable) 1773251881Speter { 1774251881Speter fb->shadowed = TRUE; 1775251881Speter fb->tree_conflict_reason = CONFLICT_REASON_SKIP; 1776251881Speter fb->skip_reason = obstr_state; 1777251881Speter return SVN_NO_ERROR; 1778251881Speter } 1779251881Speter 1780251881Speter if (is_deleted) 1781251881Speter kind = svn_node_none; 1782251881Speter } 1783251881Speter 1784251881Speter if (kind == svn_node_none) 1785251881Speter { 1786251881Speter fb->shadowed = TRUE; 1787251881Speter 1788251881Speter /* If this is not the merge target and the parent is too shallow to 1789251881Speter contain this directory, and the directory is not present 1790251881Speter via exclusion or depth filtering, skip it instead of recording 1791251881Speter a tree conflict. 1792251881Speter 1793251881Speter Non-inheritable mergeinfo will be recorded, allowing 1794251881Speter future merges into non-shallow working copies to merge 1795251881Speter changes we missed this time around. */ 1796251881Speter if (pdb && (excluded 1797251881Speter || (parent_depth != svn_depth_unknown && 1798251881Speter parent_depth < svn_depth_files))) 1799251881Speter { 1800251881Speter fb->shadowed = TRUE; 1801251881Speter 1802251881Speter fb->tree_conflict_reason = CONFLICT_REASON_SKIP; 1803251881Speter fb->skip_reason = svn_wc_notify_state_missing; 1804251881Speter return SVN_NO_ERROR; 1805251881Speter } 1806251881Speter 1807251881Speter if (is_deleted) 1808251881Speter fb->tree_conflict_reason = svn_wc_conflict_reason_deleted; 1809251881Speter else 1810251881Speter fb->tree_conflict_reason = svn_wc_conflict_reason_missing; 1811251881Speter 1812251881Speter /* ### Similar to directory */ 1813251881Speter *skip = TRUE; 1814251881Speter SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool)); 1815251881Speter return SVN_NO_ERROR; 1816251881Speter /* ### /Similar */ 1817251881Speter } 1818251881Speter else if (kind != svn_node_file) 1819251881Speter { 1820251881Speter fb->shadowed = TRUE; 1821251881Speter 1822251881Speter fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed; 1823251881Speter 1824251881Speter /* ### Similar to directory */ 1825251881Speter *skip = TRUE; 1826251881Speter SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool)); 1827251881Speter return SVN_NO_ERROR; 1828251881Speter /* ### /Similar */ 1829251881Speter } 1830251881Speter 1831251881Speter if (! right_source) 1832251881Speter { 1833251881Speter /* We want to delete the directory */ 1834251881Speter fb->tree_conflict_action = svn_wc_conflict_action_delete; 1835251881Speter SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool)); 1836251881Speter 1837251881Speter if (fb->shadowed) 1838251881Speter { 1839251881Speter return SVN_NO_ERROR; /* Already set a tree conflict */ 1840251881Speter } 1841251881Speter 1842251881Speter /* Comparison mode to verify for delete tree conflicts? */ 1843251881Speter if (pdb && pdb->delete_state 1844251881Speter && pdb->delete_state->found_edit) 1845251881Speter { 1846251881Speter /* Earlier nodes found a conflict. Done. */ 1847251881Speter *skip = TRUE; 1848251881Speter } 1849251881Speter } 1850251881Speter } 1851251881Speter else 1852251881Speter { 1853251881Speter const svn_wc_conflict_description2_t *old_tc = NULL; 1854251881Speter 1855251881Speter /* The node doesn't exist pre-merge: We have an addition */ 1856251881Speter fb->added = TRUE; 1857251881Speter fb->tree_conflict_action = svn_wc_conflict_action_add; 1858251881Speter 1859251881Speter if (pdb && pdb->pending_deletes 1860251881Speter && svn_hash_gets(pdb->pending_deletes, local_abspath)) 1861251881Speter { 1862251881Speter fb->add_is_replace = TRUE; 1863251881Speter fb->tree_conflict_action = svn_wc_conflict_action_replace; 1864251881Speter 1865251881Speter svn_hash_sets(pdb->pending_deletes, local_abspath, NULL); 1866251881Speter } 1867251881Speter 1868251881Speter if (pdb 1869251881Speter && pdb->new_tree_conflicts 1870251881Speter && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath))) 1871251881Speter { 1872251881Speter fb->tree_conflict_action = svn_wc_conflict_action_replace; 1873251881Speter fb->tree_conflict_reason = old_tc->reason; 1874251881Speter 1875251881Speter /* Update the tree conflict to store that this is a replace */ 1876251881Speter SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb, 1877251881Speter svn_node_file, 1878251881Speter fb->tree_conflict_action, 1879251881Speter fb->tree_conflict_reason, 1880251881Speter old_tc, FALSE, 1881251881Speter scratch_pool)); 1882251881Speter 1883251881Speter if (old_tc->reason == svn_wc_conflict_reason_deleted 1884251881Speter || old_tc->reason == svn_wc_conflict_reason_moved_away) 1885251881Speter { 1886251881Speter /* Issue #3806: Incoming replacements on local deletes produce 1887251881Speter inconsistent result. 1888251881Speter 1889251881Speter In this specific case we can continue applying the add part 1890251881Speter of the replacement. */ 1891251881Speter } 1892251881Speter else 1893251881Speter { 1894251881Speter *skip = TRUE; 1895251881Speter 1896251881Speter return SVN_NO_ERROR; 1897251881Speter } 1898251881Speter } 1899251881Speter else if (! (merge_b->dry_run 1900251881Speter && ((pdb && pdb->added) || fb->add_is_replace))) 1901251881Speter { 1902251881Speter svn_wc_notify_state_t obstr_state; 1903251881Speter svn_node_kind_t kind; 1904251881Speter svn_boolean_t is_deleted; 1905251881Speter 1906251881Speter SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL, 1907251881Speter &kind, NULL, 1908251881Speter merge_b, local_abspath, 1909251881Speter scratch_pool)); 1910251881Speter 1911251881Speter if (obstr_state != svn_wc_notify_state_inapplicable) 1912251881Speter { 1913251881Speter /* Skip the obstruction */ 1914251881Speter fb->shadowed = TRUE; 1915251881Speter fb->tree_conflict_reason = CONFLICT_REASON_SKIP; 1916251881Speter fb->skip_reason = obstr_state; 1917251881Speter } 1918251881Speter else if (kind != svn_node_none && !is_deleted) 1919251881Speter { 1920251881Speter /* Set a tree conflict */ 1921251881Speter fb->shadowed = TRUE; 1922251881Speter fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed; 1923251881Speter } 1924251881Speter } 1925251881Speter 1926251881Speter /* Handle pending conflicts */ 1927251881Speter SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool)); 1928251881Speter } 1929251881Speter 1930251881Speter return SVN_NO_ERROR; 1931251881Speter} 1932251881Speter 1933251881Speter/* An svn_diff_tree_processor_t function. 1934251881Speter * 1935251881Speter * Called after merge_file_opened() when a node receives only text and/or 1936251881Speter * property changes between LEFT_SOURCE and RIGHT_SOURCE. 1937251881Speter * 1938251881Speter * left_file and right_file can be NULL when the file is not modified. 1939251881Speter * left_props and right_props are always available. 1940251881Speter */ 1941251881Speterstatic svn_error_t * 1942251881Spetermerge_file_changed(const char *relpath, 1943251881Speter const svn_diff_source_t *left_source, 1944251881Speter const svn_diff_source_t *right_source, 1945251881Speter const char *left_file, 1946251881Speter const char *right_file, 1947251881Speter /*const*/ apr_hash_t *left_props, 1948251881Speter /*const*/ apr_hash_t *right_props, 1949251881Speter svn_boolean_t file_modified, 1950251881Speter const apr_array_header_t *prop_changes, 1951251881Speter void *file_baton, 1952251881Speter const struct svn_diff_tree_processor_t *processor, 1953251881Speter apr_pool_t *scratch_pool) 1954251881Speter{ 1955251881Speter merge_cmd_baton_t *merge_b = processor->baton; 1956251881Speter struct merge_file_baton_t *fb = file_baton; 1957251881Speter svn_client_ctx_t *ctx = merge_b->ctx; 1958251881Speter const char *local_abspath = svn_dirent_join(merge_b->target->abspath, 1959251881Speter relpath, scratch_pool); 1960251881Speter const svn_wc_conflict_version_t *left; 1961251881Speter const svn_wc_conflict_version_t *right; 1962251881Speter svn_wc_notify_state_t text_state; 1963251881Speter svn_wc_notify_state_t property_state; 1964251881Speter 1965251881Speter SVN_ERR_ASSERT(local_abspath && svn_dirent_is_absolute(local_abspath)); 1966251881Speter SVN_ERR_ASSERT(!left_file || svn_dirent_is_absolute(left_file)); 1967251881Speter SVN_ERR_ASSERT(!right_file || svn_dirent_is_absolute(right_file)); 1968251881Speter 1969251881Speter SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool)); 1970251881Speter 1971251881Speter if (fb->shadowed) 1972251881Speter { 1973251881Speter if (fb->tree_conflict_reason == CONFLICT_REASON_NONE) 1974251881Speter { 1975251881Speter /* We haven't notified for this node yet: report a skip */ 1976251881Speter SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file, 1977251881Speter svn_wc_notify_update_shadowed_update, 1978251881Speter fb->skip_reason, scratch_pool)); 1979251881Speter } 1980251881Speter 1981251881Speter return SVN_NO_ERROR; 1982251881Speter } 1983251881Speter 1984251881Speter /* This callback is essentially no more than a wrapper around 1985251881Speter svn_wc_merge5(). Thank goodness that all the 1986251881Speter diff-editor-mechanisms are doing the hard work of getting the 1987251881Speter fulltexts! */ 1988251881Speter 1989251881Speter property_state = svn_wc_notify_state_unchanged; 1990251881Speter text_state = svn_wc_notify_state_unchanged; 1991251881Speter 1992251881Speter SVN_ERR(prepare_merge_props_changed(&prop_changes, local_abspath, 1993251881Speter prop_changes, merge_b, 1994251881Speter scratch_pool, scratch_pool)); 1995251881Speter 1996251881Speter SVN_ERR(make_conflict_versions(&left, &right, local_abspath, 1997251881Speter svn_node_file, &merge_b->merge_source, merge_b->target, 1998251881Speter scratch_pool, scratch_pool)); 1999251881Speter 2000251881Speter /* Do property merge now, if we are not going to perform a text merge */ 2001251881Speter if ((merge_b->record_only || !left_file) && prop_changes->nelts) 2002251881Speter { 2003251881Speter SVN_ERR(svn_wc_merge_props3(&property_state, ctx->wc_ctx, local_abspath, 2004251881Speter left, right, 2005251881Speter left_props, prop_changes, 2006251881Speter merge_b->dry_run, 2007251881Speter NULL, NULL, 2008251881Speter ctx->cancel_func, ctx->cancel_baton, 2009251881Speter scratch_pool)); 2010251881Speter if (property_state == svn_wc_notify_state_conflicted) 2011251881Speter { 2012251881Speter alloc_and_store_path(&merge_b->conflicted_paths, local_abspath, 2013251881Speter merge_b->pool); 2014251881Speter } 2015251881Speter } 2016251881Speter 2017251881Speter /* Easy out: We are only applying mergeinfo differences. */ 2018251881Speter if (merge_b->record_only) 2019251881Speter { 2020251881Speter /* NO-OP */ 2021251881Speter } 2022251881Speter else if (left_file) 2023251881Speter { 2024251881Speter svn_boolean_t has_local_mods; 2025251881Speter enum svn_wc_merge_outcome_t content_outcome; 2026251881Speter 2027251881Speter /* xgettext: the '.working', '.merge-left.r%ld' and 2028251881Speter '.merge-right.r%ld' strings are used to tag onto a file 2029251881Speter name in case of a merge conflict */ 2030251881Speter const char *target_label = _(".working"); 2031251881Speter const char *left_label = apr_psprintf(scratch_pool, 2032251881Speter _(".merge-left.r%ld"), 2033251881Speter left_source->revision); 2034251881Speter const char *right_label = apr_psprintf(scratch_pool, 2035251881Speter _(".merge-right.r%ld"), 2036251881Speter right_source->revision); 2037251881Speter 2038251881Speter SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, ctx->wc_ctx, 2039251881Speter local_abspath, FALSE, scratch_pool)); 2040251881Speter 2041251881Speter /* Do property merge and text merge in one step so that keyword expansion 2042251881Speter takes into account the new property values. */ 2043251881Speter SVN_ERR(svn_wc_merge5(&content_outcome, &property_state, ctx->wc_ctx, 2044251881Speter left_file, right_file, local_abspath, 2045251881Speter left_label, right_label, target_label, 2046251881Speter left, right, 2047251881Speter merge_b->dry_run, merge_b->diff3_cmd, 2048251881Speter merge_b->merge_options, 2049251881Speter left_props, prop_changes, 2050251881Speter NULL, NULL, 2051251881Speter ctx->cancel_func, 2052251881Speter ctx->cancel_baton, 2053251881Speter scratch_pool)); 2054251881Speter 2055251881Speter if (content_outcome == svn_wc_merge_conflict 2056251881Speter || property_state == svn_wc_notify_state_conflicted) 2057251881Speter { 2058251881Speter alloc_and_store_path(&merge_b->conflicted_paths, local_abspath, 2059251881Speter merge_b->pool); 2060251881Speter } 2061251881Speter 2062251881Speter if (content_outcome == svn_wc_merge_conflict) 2063251881Speter text_state = svn_wc_notify_state_conflicted; 2064251881Speter else if (has_local_mods 2065251881Speter && content_outcome != svn_wc_merge_unchanged) 2066251881Speter text_state = svn_wc_notify_state_merged; 2067251881Speter else if (content_outcome == svn_wc_merge_merged) 2068251881Speter text_state = svn_wc_notify_state_changed; 2069251881Speter else if (content_outcome == svn_wc_merge_no_merge) 2070251881Speter text_state = svn_wc_notify_state_missing; 2071251881Speter else /* merge_outcome == svn_wc_merge_unchanged */ 2072251881Speter text_state = svn_wc_notify_state_unchanged; 2073251881Speter } 2074251881Speter 2075251881Speter if (text_state == svn_wc_notify_state_conflicted 2076251881Speter || text_state == svn_wc_notify_state_merged 2077251881Speter || text_state == svn_wc_notify_state_changed 2078251881Speter || property_state == svn_wc_notify_state_conflicted 2079251881Speter || property_state == svn_wc_notify_state_merged 2080251881Speter || property_state == svn_wc_notify_state_changed) 2081251881Speter { 2082251881Speter SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file, 2083251881Speter text_state, property_state, 2084251881Speter scratch_pool)); 2085251881Speter } 2086251881Speter 2087251881Speter return SVN_NO_ERROR; 2088251881Speter} 2089251881Speter 2090251881Speter/* An svn_diff_tree_processor_t function. 2091251881Speter * 2092251881Speter * Called after merge_file_opened() when a node doesn't exist in LEFT_SOURCE, 2093251881Speter * but does in RIGHT_SOURCE. 2094251881Speter * 2095251881Speter * When a node is replaced instead of just added a separate opened+deleted will 2096251881Speter * be invoked before the current open+added. 2097251881Speter */ 2098251881Speterstatic svn_error_t * 2099251881Spetermerge_file_added(const char *relpath, 2100251881Speter const svn_diff_source_t *copyfrom_source, 2101251881Speter const svn_diff_source_t *right_source, 2102251881Speter const char *copyfrom_file, 2103251881Speter const char *right_file, 2104251881Speter /*const*/ apr_hash_t *copyfrom_props, 2105251881Speter /*const*/ apr_hash_t *right_props, 2106251881Speter void *file_baton, 2107251881Speter const struct svn_diff_tree_processor_t *processor, 2108251881Speter apr_pool_t *scratch_pool) 2109251881Speter{ 2110251881Speter merge_cmd_baton_t *merge_b = processor->baton; 2111251881Speter struct merge_file_baton_t *fb = file_baton; 2112251881Speter const char *local_abspath = svn_dirent_join(merge_b->target->abspath, 2113251881Speter relpath, scratch_pool); 2114251881Speter apr_hash_t *pristine_props; 2115251881Speter apr_hash_t *new_props; 2116251881Speter 2117251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 2118251881Speter 2119251881Speter SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool)); 2120251881Speter 2121251881Speter if (fb->shadowed) 2122251881Speter { 2123251881Speter if (fb->tree_conflict_reason == CONFLICT_REASON_NONE) 2124251881Speter { 2125251881Speter /* We haven't notified for this node yet: report a skip */ 2126251881Speter SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file, 2127251881Speter svn_wc_notify_update_shadowed_add, 2128251881Speter fb->skip_reason, scratch_pool)); 2129251881Speter } 2130251881Speter 2131251881Speter return SVN_NO_ERROR; 2132251881Speter } 2133251881Speter 2134251881Speter /* Easy out: We are only applying mergeinfo differences. */ 2135251881Speter if (merge_b->record_only) 2136251881Speter { 2137251881Speter return SVN_NO_ERROR; 2138251881Speter } 2139251881Speter 2140251881Speter if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge) 2141251881Speter && ( !fb->parent_baton || !fb->parent_baton->added)) 2142251881Speter { 2143251881Speter /* Store the roots of added subtrees */ 2144251881Speter store_path(merge_b->added_abspaths, local_abspath); 2145251881Speter } 2146251881Speter 2147251881Speter if (!merge_b->dry_run) 2148251881Speter { 2149251881Speter const char *copyfrom_url; 2150251881Speter svn_revnum_t copyfrom_rev; 2151251881Speter svn_stream_t *new_contents, *pristine_contents; 2152251881Speter 2153251881Speter /* If this is a merge from the same repository as our 2154251881Speter working copy, we handle adds as add-with-history. 2155251881Speter Otherwise, we'll use a pure add. */ 2156251881Speter if (merge_b->same_repos) 2157251881Speter { 2158251881Speter const char *child = 2159251881Speter svn_dirent_skip_ancestor(merge_b->target->abspath, 2160251881Speter local_abspath); 2161251881Speter SVN_ERR_ASSERT(child != NULL); 2162251881Speter copyfrom_url = svn_path_url_add_component2( 2163251881Speter merge_b->merge_source.loc2->url, 2164251881Speter child, scratch_pool); 2165251881Speter copyfrom_rev = right_source->revision; 2166251881Speter SVN_ERR(check_repos_match(merge_b->target, local_abspath, 2167251881Speter copyfrom_url, scratch_pool)); 2168251881Speter SVN_ERR(svn_stream_open_readonly(&pristine_contents, 2169251881Speter right_file, 2170251881Speter scratch_pool, 2171251881Speter scratch_pool)); 2172251881Speter new_contents = NULL; /* inherit from new_base_contents */ 2173251881Speter 2174251881Speter pristine_props = right_props; /* Includes last_* information */ 2175251881Speter new_props = NULL; /* No local changes */ 2176251881Speter 2177251881Speter if (svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO)) 2178251881Speter { 2179251881Speter alloc_and_store_path(&merge_b->paths_with_new_mergeinfo, 2180251881Speter local_abspath, merge_b->pool); 2181251881Speter } 2182251881Speter } 2183251881Speter else 2184251881Speter { 2185251881Speter apr_array_header_t *regular_props; 2186251881Speter 2187251881Speter copyfrom_url = NULL; 2188251881Speter copyfrom_rev = SVN_INVALID_REVNUM; 2189251881Speter 2190251881Speter pristine_contents = svn_stream_empty(scratch_pool); 2191251881Speter SVN_ERR(svn_stream_open_readonly(&new_contents, right_file, 2192251881Speter scratch_pool, scratch_pool)); 2193251881Speter 2194251881Speter pristine_props = apr_hash_make(scratch_pool); /* Local addition */ 2195251881Speter 2196251881Speter /* We don't want any foreign properties */ 2197251881Speter SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props, 2198251881Speter scratch_pool), 2199251881Speter NULL, NULL, ®ular_props, 2200251881Speter scratch_pool)); 2201251881Speter 2202251881Speter new_props = svn_prop_array_to_hash(regular_props, scratch_pool); 2203251881Speter 2204251881Speter /* Issue #3383: We don't want mergeinfo from a foreign repository. */ 2205251881Speter svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL); 2206251881Speter } 2207251881Speter 2208251881Speter /* Do everything like if we had called 'svn cp PATH1 PATH2'. */ 2209251881Speter SVN_ERR(svn_wc_add_repos_file4(merge_b->ctx->wc_ctx, 2210251881Speter local_abspath, 2211251881Speter pristine_contents, 2212251881Speter new_contents, 2213251881Speter pristine_props, new_props, 2214251881Speter copyfrom_url, copyfrom_rev, 2215251881Speter merge_b->ctx->cancel_func, 2216251881Speter merge_b->ctx->cancel_baton, 2217251881Speter scratch_pool)); 2218251881Speter 2219251881Speter /* Caller must call svn_sleep_for_timestamps() */ 2220251881Speter *merge_b->use_sleep = TRUE; 2221251881Speter } 2222251881Speter 2223251881Speter SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_file, 2224251881Speter fb->add_is_replace, scratch_pool)); 2225251881Speter 2226251881Speter return SVN_NO_ERROR; 2227251881Speter} 2228251881Speter 2229251881Speter/* Compare the two sets of properties PROPS1 and PROPS2, ignoring the 2230251881Speter * "svn:mergeinfo" property, and noticing only "normal" props. Set *SAME to 2231251881Speter * true if the rest of the properties are identical or false if they differ. 2232251881Speter */ 2233251881Speterstatic svn_error_t * 2234251881Speterproperties_same_p(svn_boolean_t *same, 2235251881Speter apr_hash_t *props1, 2236251881Speter apr_hash_t *props2, 2237251881Speter apr_pool_t *scratch_pool) 2238251881Speter{ 2239251881Speter apr_array_header_t *prop_changes; 2240251881Speter int i, diffs; 2241251881Speter 2242251881Speter /* Examine the properties that differ */ 2243251881Speter SVN_ERR(svn_prop_diffs(&prop_changes, props1, props2, scratch_pool)); 2244251881Speter diffs = 0; 2245251881Speter for (i = 0; i < prop_changes->nelts; i++) 2246251881Speter { 2247251881Speter const char *pname = APR_ARRAY_IDX(prop_changes, i, svn_prop_t).name; 2248251881Speter 2249251881Speter /* Count the properties we're interested in; ignore the rest */ 2250251881Speter if (svn_wc_is_normal_prop(pname) 2251251881Speter && strcmp(pname, SVN_PROP_MERGEINFO) != 0) 2252251881Speter diffs++; 2253251881Speter } 2254251881Speter *same = (diffs == 0); 2255251881Speter return SVN_NO_ERROR; 2256251881Speter} 2257251881Speter 2258251881Speter/* Compare the file OLDER_ABSPATH (together with its normal properties in 2259251881Speter * ORIGINAL_PROPS which may also contain WC props and entry props) with the 2260251881Speter * versioned file MINE_ABSPATH (together with its versioned properties). 2261251881Speter * Set *SAME to true if they are the same or false if they differ, ignoring 2262251881Speter * the "svn:mergeinfo" property, and ignoring differences in keyword 2263251881Speter * expansion and end-of-line style. */ 2264251881Speterstatic svn_error_t * 2265251881Speterfiles_same_p(svn_boolean_t *same, 2266251881Speter const char *older_abspath, 2267251881Speter apr_hash_t *original_props, 2268251881Speter const char *mine_abspath, 2269251881Speter svn_wc_context_t *wc_ctx, 2270251881Speter apr_pool_t *scratch_pool) 2271251881Speter{ 2272251881Speter apr_hash_t *working_props; 2273251881Speter 2274251881Speter SVN_ERR(svn_wc_prop_list2(&working_props, wc_ctx, mine_abspath, 2275251881Speter scratch_pool, scratch_pool)); 2276251881Speter 2277251881Speter /* Compare the properties */ 2278251881Speter SVN_ERR(properties_same_p(same, original_props, working_props, 2279251881Speter scratch_pool)); 2280251881Speter if (*same) 2281251881Speter { 2282251881Speter svn_stream_t *mine_stream; 2283251881Speter svn_stream_t *older_stream; 2284251881Speter svn_opt_revision_t working_rev = { svn_opt_revision_working, { 0 } }; 2285251881Speter 2286251881Speter /* Compare the file content, translating 'mine' to 'normal' form. */ 2287251881Speter if (svn_prop_get_value(working_props, SVN_PROP_SPECIAL) != NULL) 2288251881Speter SVN_ERR(svn_subst_read_specialfile(&mine_stream, mine_abspath, 2289251881Speter scratch_pool, scratch_pool)); 2290251881Speter else 2291251881Speter SVN_ERR(svn_client__get_normalized_stream(&mine_stream, wc_ctx, 2292251881Speter mine_abspath, &working_rev, 2293251881Speter FALSE, TRUE, NULL, NULL, 2294251881Speter scratch_pool, scratch_pool)); 2295251881Speter 2296251881Speter SVN_ERR(svn_stream_open_readonly(&older_stream, older_abspath, 2297251881Speter scratch_pool, scratch_pool)); 2298251881Speter 2299251881Speter SVN_ERR(svn_stream_contents_same2(same, mine_stream, older_stream, 2300251881Speter scratch_pool)); 2301251881Speter 2302251881Speter } 2303251881Speter 2304251881Speter return SVN_NO_ERROR; 2305251881Speter} 2306251881Speter 2307251881Speter/* An svn_diff_tree_processor_t function. 2308251881Speter * 2309251881Speter * Called after merge_file_opened() when a node does exist in LEFT_SOURCE, but 2310251881Speter * no longer exists (or is replaced) in RIGHT_SOURCE. 2311251881Speter * 2312251881Speter * When a node is replaced instead of just added a separate opened+added will 2313251881Speter * be invoked after the current open+deleted. 2314251881Speter */ 2315251881Speterstatic svn_error_t * 2316251881Spetermerge_file_deleted(const char *relpath, 2317251881Speter const svn_diff_source_t *left_source, 2318251881Speter const char *left_file, 2319251881Speter /*const*/ apr_hash_t *left_props, 2320251881Speter void *file_baton, 2321251881Speter const struct svn_diff_tree_processor_t *processor, 2322251881Speter apr_pool_t *scratch_pool) 2323251881Speter{ 2324251881Speter merge_cmd_baton_t *merge_b = processor->baton; 2325251881Speter struct merge_file_baton_t *fb = file_baton; 2326251881Speter const char *local_abspath = svn_dirent_join(merge_b->target->abspath, 2327251881Speter relpath, scratch_pool); 2328251881Speter svn_boolean_t same; 2329251881Speter 2330251881Speter SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool)); 2331251881Speter 2332251881Speter if (fb->shadowed) 2333251881Speter { 2334251881Speter if (fb->tree_conflict_reason == CONFLICT_REASON_NONE) 2335251881Speter { 2336251881Speter /* We haven't notified for this node yet: report a skip */ 2337251881Speter SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file, 2338251881Speter svn_wc_notify_update_shadowed_delete, 2339251881Speter fb->skip_reason, scratch_pool)); 2340251881Speter } 2341251881Speter 2342251881Speter return SVN_NO_ERROR; 2343251881Speter } 2344251881Speter 2345251881Speter /* Easy out: We are only applying mergeinfo differences. */ 2346251881Speter if (merge_b->record_only) 2347251881Speter { 2348251881Speter return SVN_NO_ERROR; 2349251881Speter } 2350251881Speter 2351251881Speter /* If the files are identical, attempt deletion */ 2352251881Speter if (merge_b->force_delete) 2353251881Speter same = TRUE; 2354251881Speter else 2355251881Speter SVN_ERR(files_same_p(&same, left_file, left_props, 2356251881Speter local_abspath, merge_b->ctx->wc_ctx, 2357251881Speter scratch_pool)); 2358251881Speter 2359251881Speter if (fb->parent_baton 2360251881Speter && fb->parent_baton->delete_state) 2361251881Speter { 2362251881Speter if (same) 2363251881Speter { 2364251881Speter /* Note that we checked this file */ 2365251881Speter store_path(fb->parent_baton->delete_state->compared_abspaths, 2366251881Speter local_abspath); 2367251881Speter } 2368251881Speter else 2369251881Speter { 2370251881Speter /* We found some modification. Parent should raise a tree conflict */ 2371251881Speter fb->parent_baton->delete_state->found_edit = TRUE; 2372251881Speter } 2373251881Speter 2374251881Speter return SVN_NO_ERROR; 2375251881Speter } 2376251881Speter else if (same) 2377251881Speter { 2378251881Speter if (!merge_b->dry_run) 2379251881Speter SVN_ERR(svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath, 2380251881Speter FALSE /* keep_local */, FALSE /* unversioned */, 2381251881Speter merge_b->ctx->cancel_func, 2382251881Speter merge_b->ctx->cancel_baton, 2383251881Speter NULL, NULL /* no notify */, 2384251881Speter scratch_pool)); 2385251881Speter 2386251881Speter /* Record that we might have deleted mergeinfo */ 2387251881Speter alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo, 2388251881Speter local_abspath, merge_b->pool); 2389251881Speter 2390251881Speter /* And notify the deletion */ 2391251881Speter SVN_ERR(record_update_delete(merge_b, fb->parent_baton, local_abspath, 2392251881Speter svn_node_file, scratch_pool)); 2393251881Speter } 2394251881Speter else 2395251881Speter { 2396251881Speter /* The files differ, so raise a conflict instead of deleting */ 2397251881Speter 2398251881Speter /* This is use case 5 described in the paper attached to issue 2399251881Speter * #2282. See also notes/tree-conflicts/detection.txt 2400251881Speter */ 2401251881Speter SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton, 2402251881Speter svn_node_file, 2403251881Speter svn_wc_conflict_action_delete, 2404251881Speter svn_wc_conflict_reason_edited, 2405251881Speter NULL, TRUE, 2406251881Speter scratch_pool)); 2407251881Speter } 2408251881Speter 2409251881Speter return SVN_NO_ERROR; 2410251881Speter} 2411251881Speter 2412251881Speter/* An svn_diff_tree_processor_t function. 2413251881Speter 2414251881Speter Called before either merge_dir_changed(), merge_dir_added(), 2415251881Speter merge_dir_deleted() or merge_dir_closed(), unless it sets *SKIP to TRUE. 2416251881Speter 2417251881Speter After this call and before the close call, all descendants will receive 2418251881Speter their changes, unless *SKIP_CHILDREN is set to TRUE. 2419251881Speter 2420251881Speter When *SKIP is TRUE, the diff driver avoids work on getting the details 2421251881Speter for the closing callbacks. 2422251881Speter 2423251881Speter The SKIP and SKIP_DESCENDANTS work independantly. 2424251881Speter */ 2425251881Speterstatic svn_error_t * 2426251881Spetermerge_dir_opened(void **new_dir_baton, 2427251881Speter svn_boolean_t *skip, 2428251881Speter svn_boolean_t *skip_children, 2429251881Speter const char *relpath, 2430251881Speter const svn_diff_source_t *left_source, 2431251881Speter const svn_diff_source_t *right_source, 2432251881Speter const svn_diff_source_t *copyfrom_source, 2433251881Speter void *parent_dir_baton, 2434251881Speter const struct svn_diff_tree_processor_t *processor, 2435251881Speter apr_pool_t *result_pool, 2436251881Speter apr_pool_t *scratch_pool) 2437251881Speter{ 2438251881Speter merge_cmd_baton_t *merge_b = processor->baton; 2439251881Speter struct merge_dir_baton_t *db; 2440251881Speter struct merge_dir_baton_t *pdb = parent_dir_baton; 2441251881Speter 2442251881Speter const char *local_abspath = svn_dirent_join(merge_b->target->abspath, 2443251881Speter relpath, scratch_pool); 2444251881Speter 2445251881Speter db = apr_pcalloc(result_pool, sizeof(*db)); 2446251881Speter db->pool = result_pool; 2447251881Speter db->tree_conflict_reason = CONFLICT_REASON_NONE; 2448251881Speter db->tree_conflict_action = svn_wc_conflict_action_edit; 2449251881Speter db->skip_reason = svn_wc_notify_state_unknown; 2450251881Speter 2451251881Speter *new_dir_baton = db; 2452251881Speter 2453251881Speter if (pdb) 2454251881Speter { 2455251881Speter db->parent_baton = pdb; 2456251881Speter db->shadowed = pdb->shadowed; 2457251881Speter db->skip_reason = pdb->skip_reason; 2458251881Speter } 2459251881Speter 2460251881Speter if (db->shadowed) 2461251881Speter { 2462251881Speter /* An ancestor is tree conflicted. Nothing to do here. */ 2463251881Speter if (! left_source) 2464251881Speter db->added = TRUE; 2465251881Speter } 2466251881Speter else if (left_source != NULL) 2467251881Speter { 2468251881Speter /* Node is expected to be a directory. */ 2469251881Speter svn_node_kind_t kind; 2470251881Speter svn_boolean_t is_deleted; 2471251881Speter svn_boolean_t excluded; 2472251881Speter svn_depth_t parent_depth; 2473251881Speter 2474251881Speter if (! right_source) 2475251881Speter db->tree_conflict_action = svn_wc_conflict_action_delete; 2476251881Speter 2477251881Speter /* Check for an obstructed or missing node on disk. */ 2478251881Speter { 2479251881Speter svn_wc_notify_state_t obstr_state; 2480251881Speter SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded, 2481251881Speter &kind, &parent_depth, 2482251881Speter merge_b, local_abspath, 2483251881Speter scratch_pool)); 2484251881Speter 2485251881Speter if (obstr_state != svn_wc_notify_state_inapplicable) 2486251881Speter { 2487251881Speter db->shadowed = TRUE; 2488251881Speter 2489251881Speter if (obstr_state == svn_wc_notify_state_obstructed) 2490251881Speter { 2491251881Speter svn_boolean_t is_wcroot; 2492251881Speter 2493251881Speter SVN_ERR(svn_wc_check_root(&is_wcroot, NULL, NULL, 2494251881Speter merge_b->ctx->wc_ctx, 2495251881Speter local_abspath, scratch_pool)); 2496251881Speter 2497251881Speter if (is_wcroot) 2498251881Speter { 2499251881Speter db->tree_conflict_reason = CONFLICT_REASON_SKIP_WC; 2500251881Speter return SVN_NO_ERROR; 2501251881Speter } 2502251881Speter } 2503251881Speter 2504251881Speter db->tree_conflict_reason = CONFLICT_REASON_SKIP; 2505251881Speter db->skip_reason = obstr_state; 2506251881Speter 2507251881Speter if (! right_source) 2508251881Speter { 2509251881Speter *skip = *skip_children = TRUE; 2510251881Speter SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, 2511251881Speter scratch_pool)); 2512251881Speter } 2513251881Speter 2514251881Speter return SVN_NO_ERROR; 2515251881Speter } 2516251881Speter 2517251881Speter if (is_deleted) 2518251881Speter kind = svn_node_none; 2519251881Speter } 2520251881Speter 2521251881Speter if (kind == svn_node_none) 2522251881Speter { 2523251881Speter db->shadowed = TRUE; 2524251881Speter 2525251881Speter /* If this is not the merge target and the parent is too shallow to 2526251881Speter contain this directory, and the directory is not presen 2527251881Speter via exclusion or depth filtering, skip it instead of recording 2528251881Speter a tree conflict. 2529251881Speter 2530251881Speter Non-inheritable mergeinfo will be recorded, allowing 2531251881Speter future merges into non-shallow working copies to merge 2532251881Speter changes we missed this time around. */ 2533251881Speter if (pdb && (excluded 2534251881Speter || (parent_depth != svn_depth_unknown && 2535251881Speter parent_depth < svn_depth_immediates))) 2536251881Speter { 2537251881Speter db->shadowed = TRUE; 2538251881Speter 2539251881Speter db->tree_conflict_reason = CONFLICT_REASON_SKIP; 2540251881Speter db->skip_reason = svn_wc_notify_state_missing; 2541251881Speter 2542251881Speter return SVN_NO_ERROR; 2543251881Speter } 2544251881Speter 2545251881Speter if (is_deleted) 2546251881Speter db->tree_conflict_reason = svn_wc_conflict_reason_deleted; 2547251881Speter else 2548251881Speter db->tree_conflict_reason = svn_wc_conflict_reason_missing; 2549251881Speter 2550251881Speter /* ### To avoid breaking tests */ 2551251881Speter *skip = TRUE; 2552251881Speter *skip_children = TRUE; 2553251881Speter SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool)); 2554251881Speter return SVN_NO_ERROR; 2555251881Speter /* ### /avoid breaking tests */ 2556251881Speter } 2557251881Speter else if (kind != svn_node_dir) 2558251881Speter { 2559251881Speter db->shadowed = TRUE; 2560251881Speter 2561251881Speter db->tree_conflict_reason = svn_wc_conflict_reason_obstructed; 2562251881Speter 2563251881Speter /* ### To avoid breaking tests */ 2564251881Speter *skip = TRUE; 2565251881Speter *skip_children = TRUE; 2566251881Speter SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool)); 2567251881Speter return SVN_NO_ERROR; 2568251881Speter /* ### /avoid breaking tests */ 2569251881Speter } 2570251881Speter 2571251881Speter if (! right_source) 2572251881Speter { 2573251881Speter /* We want to delete the directory */ 2574251881Speter /* Mark PB edited now? */ 2575251881Speter db->tree_conflict_action = svn_wc_conflict_action_delete; 2576251881Speter SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool)); 2577251881Speter 2578251881Speter if (db->shadowed) 2579251881Speter { 2580251881Speter *skip_children = TRUE; 2581251881Speter return SVN_NO_ERROR; /* Already set a tree conflict */ 2582251881Speter } 2583251881Speter 2584251881Speter db->delete_state = (pdb != NULL) ? pdb->delete_state : NULL; 2585251881Speter 2586251881Speter if (db->delete_state && db->delete_state->found_edit) 2587251881Speter { 2588251881Speter /* A sibling found a conflict. Done. */ 2589251881Speter *skip = TRUE; 2590251881Speter *skip_children = TRUE; 2591251881Speter } 2592251881Speter else if (merge_b->force_delete) 2593251881Speter { 2594251881Speter /* No comparison necessary */ 2595251881Speter *skip_children = TRUE; 2596251881Speter } 2597251881Speter else if (! db->delete_state) 2598251881Speter { 2599251881Speter /* Start descendant comparison */ 2600251881Speter db->delete_state = apr_pcalloc(db->pool, 2601251881Speter sizeof(*db->delete_state)); 2602251881Speter 2603251881Speter db->delete_state->del_root = db; 2604251881Speter db->delete_state->compared_abspaths = apr_hash_make(db->pool); 2605251881Speter } 2606251881Speter } 2607251881Speter } 2608251881Speter else 2609251881Speter { 2610251881Speter const svn_wc_conflict_description2_t *old_tc = NULL; 2611251881Speter 2612251881Speter /* The node doesn't exist pre-merge: We have an addition */ 2613251881Speter db->added = TRUE; 2614251881Speter db->tree_conflict_action = svn_wc_conflict_action_add; 2615251881Speter 2616251881Speter if (pdb && pdb->pending_deletes 2617251881Speter && svn_hash_gets(pdb->pending_deletes, local_abspath)) 2618251881Speter { 2619251881Speter db->add_is_replace = TRUE; 2620251881Speter db->tree_conflict_action = svn_wc_conflict_action_replace; 2621251881Speter 2622251881Speter svn_hash_sets(pdb->pending_deletes, local_abspath, NULL); 2623251881Speter } 2624251881Speter 2625251881Speter if (pdb 2626251881Speter && pdb->new_tree_conflicts 2627251881Speter && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath))) 2628251881Speter { 2629251881Speter db->tree_conflict_action = svn_wc_conflict_action_replace; 2630251881Speter db->tree_conflict_reason = old_tc->reason; 2631251881Speter 2632251881Speter if (old_tc->reason == svn_wc_conflict_reason_deleted 2633251881Speter || old_tc->reason == svn_wc_conflict_reason_moved_away) 2634251881Speter { 2635251881Speter /* Issue #3806: Incoming replacements on local deletes produce 2636251881Speter inconsistent result. 2637251881Speter 2638251881Speter In this specific case we can continue applying the add part 2639251881Speter of the replacement. */ 2640251881Speter } 2641251881Speter else 2642251881Speter { 2643251881Speter *skip = TRUE; 2644251881Speter *skip_children = TRUE; 2645251881Speter 2646251881Speter /* Update the tree conflict to store that this is a replace */ 2647251881Speter SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb, 2648251881Speter svn_node_dir, 2649251881Speter db->tree_conflict_action, 2650251881Speter db->tree_conflict_reason, 2651251881Speter old_tc, FALSE, 2652251881Speter scratch_pool)); 2653251881Speter 2654251881Speter return SVN_NO_ERROR; 2655251881Speter } 2656251881Speter } 2657251881Speter 2658251881Speter if (! (merge_b->dry_run 2659251881Speter && ((pdb && pdb->added) || db->add_is_replace))) 2660251881Speter { 2661251881Speter svn_wc_notify_state_t obstr_state; 2662251881Speter svn_node_kind_t kind; 2663251881Speter svn_boolean_t is_deleted; 2664251881Speter 2665251881Speter SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL, 2666251881Speter &kind, NULL, 2667251881Speter merge_b, local_abspath, 2668251881Speter scratch_pool)); 2669251881Speter 2670251881Speter /* In this case of adding a directory, we have an exception to the 2671251881Speter * usual "skip if it's inconsistent" rule. If the directory exists 2672251881Speter * on disk unexpectedly, we simply make it versioned, because we can 2673251881Speter * do so without risk of destroying data. Only skip if it is 2674251881Speter * versioned but unexpectedly missing from disk, or is unversioned 2675251881Speter * but obstructed by a node of the wrong kind. */ 2676251881Speter if (obstr_state == svn_wc_notify_state_obstructed 2677251881Speter && (is_deleted || kind == svn_node_none)) 2678251881Speter { 2679251881Speter svn_node_kind_t disk_kind; 2680251881Speter 2681251881Speter SVN_ERR(svn_io_check_path(local_abspath, &disk_kind, 2682251881Speter scratch_pool)); 2683251881Speter 2684251881Speter if (disk_kind == svn_node_dir) 2685251881Speter { 2686251881Speter obstr_state = svn_wc_notify_state_inapplicable; 2687251881Speter db->add_existing = TRUE; /* Take over existing directory */ 2688251881Speter } 2689251881Speter } 2690251881Speter 2691251881Speter if (obstr_state != svn_wc_notify_state_inapplicable) 2692251881Speter { 2693251881Speter /* Skip the obstruction */ 2694251881Speter db->shadowed = TRUE; 2695251881Speter db->tree_conflict_reason = CONFLICT_REASON_SKIP; 2696251881Speter db->skip_reason = obstr_state; 2697251881Speter } 2698251881Speter else if (kind != svn_node_none && !is_deleted) 2699251881Speter { 2700251881Speter /* Set a tree conflict */ 2701251881Speter db->shadowed = TRUE; 2702251881Speter db->tree_conflict_reason = svn_wc_conflict_reason_obstructed; 2703251881Speter } 2704251881Speter } 2705251881Speter 2706251881Speter /* Handle pending conflicts */ 2707251881Speter SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool)); 2708251881Speter 2709251881Speter if (db->shadowed) 2710251881Speter { 2711251881Speter /* Notified and done. Skip children? */ 2712251881Speter } 2713251881Speter else if (merge_b->record_only) 2714251881Speter { 2715251881Speter /* Ok, we are done for this node and its descendants */ 2716251881Speter *skip = TRUE; 2717251881Speter *skip_children = TRUE; 2718251881Speter } 2719251881Speter else if (! merge_b->dry_run) 2720251881Speter { 2721251881Speter /* Create the directory on disk, to allow descendants to be added */ 2722251881Speter if (! db->add_existing) 2723251881Speter SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, 2724251881Speter scratch_pool)); 2725251881Speter 2726251881Speter if (old_tc) 2727251881Speter { 2728251881Speter /* svn_wc_add4 and svn_wc_add_from_disk2 can't add a node 2729251881Speter over an existing tree conflict */ 2730251881Speter 2731251881Speter /* ### These functions should take some tree conflict argument 2732251881Speter and allow overwriting the tc when one is passed */ 2733251881Speter 2734251881Speter SVN_ERR(svn_wc__del_tree_conflict(merge_b->ctx->wc_ctx, 2735251881Speter local_abspath, 2736251881Speter scratch_pool)); 2737251881Speter } 2738251881Speter 2739251881Speter if (merge_b->same_repos) 2740251881Speter { 2741251881Speter const char *original_url; 2742251881Speter 2743251881Speter original_url = svn_path_url_add_component2( 2744251881Speter merge_b->merge_source.loc2->url, 2745251881Speter relpath, scratch_pool); 2746251881Speter 2747251881Speter /* Limitation (aka HACK): 2748251881Speter We create a newly added directory with an original URL and 2749251881Speter revision as that in the repository, but without its properties 2750251881Speter and children. 2751251881Speter 2752251881Speter When the merge is cancelled before the final dir_added(), the 2753251881Speter copy won't really represent the in-repository state of the node. 2754251881Speter */ 2755251881Speter SVN_ERR(svn_wc_add4(merge_b->ctx->wc_ctx, local_abspath, 2756251881Speter svn_depth_infinity, 2757251881Speter original_url, 2758251881Speter right_source->revision, 2759251881Speter merge_b->ctx->cancel_func, 2760251881Speter merge_b->ctx->cancel_baton, 2761251881Speter NULL, NULL /* no notify! */, 2762251881Speter scratch_pool)); 2763251881Speter } 2764251881Speter else 2765251881Speter { 2766251881Speter SVN_ERR(svn_wc_add_from_disk2(merge_b->ctx->wc_ctx, local_abspath, 2767251881Speter apr_hash_make(scratch_pool), 2768251881Speter NULL, NULL /* no notify! */, 2769251881Speter scratch_pool)); 2770251881Speter } 2771251881Speter 2772251881Speter if (old_tc != NULL) 2773251881Speter { 2774251881Speter /* ### Should be atomic with svn_wc_add(4|_from_disk2)() */ 2775251881Speter SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb, 2776251881Speter svn_node_dir, 2777251881Speter db->tree_conflict_action, 2778251881Speter db->tree_conflict_reason, 2779251881Speter old_tc, FALSE, 2780251881Speter scratch_pool)); 2781251881Speter } 2782251881Speter } 2783251881Speter 2784251881Speter if (! db->shadowed && !merge_b->record_only) 2785251881Speter SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_dir, 2786251881Speter db->add_is_replace, scratch_pool)); 2787251881Speter } 2788251881Speter return SVN_NO_ERROR; 2789251881Speter} 2790251881Speter 2791251881Speter/* An svn_diff_tree_processor_t function. 2792251881Speter * 2793251881Speter * Called after merge_dir_opened() when a node exists in both the left and 2794251881Speter * right source, but has its properties changed inbetween. 2795251881Speter * 2796251881Speter * After the merge_dir_opened() but before the call to this merge_dir_changed() 2797251881Speter * function all descendants will have been updated. 2798251881Speter */ 2799251881Speterstatic svn_error_t * 2800251881Spetermerge_dir_changed(const char *relpath, 2801251881Speter const svn_diff_source_t *left_source, 2802251881Speter const svn_diff_source_t *right_source, 2803251881Speter /*const*/ apr_hash_t *left_props, 2804251881Speter /*const*/ apr_hash_t *right_props, 2805251881Speter const apr_array_header_t *prop_changes, 2806251881Speter void *dir_baton, 2807251881Speter const struct svn_diff_tree_processor_t *processor, 2808251881Speter apr_pool_t *scratch_pool) 2809251881Speter{ 2810251881Speter merge_cmd_baton_t *merge_b = processor->baton; 2811251881Speter struct merge_dir_baton_t *db = dir_baton; 2812251881Speter const apr_array_header_t *props; 2813251881Speter const char *local_abspath = svn_dirent_join(merge_b->target->abspath, 2814251881Speter relpath, scratch_pool); 2815251881Speter 2816251881Speter SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool)); 2817251881Speter 2818251881Speter SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool)); 2819251881Speter 2820251881Speter if (db->shadowed) 2821251881Speter { 2822251881Speter if (db->tree_conflict_reason == CONFLICT_REASON_NONE) 2823251881Speter { 2824251881Speter /* We haven't notified for this node yet: report a skip */ 2825251881Speter SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir, 2826251881Speter svn_wc_notify_update_shadowed_update, 2827251881Speter db->skip_reason, scratch_pool)); 2828251881Speter } 2829251881Speter 2830251881Speter return SVN_NO_ERROR; 2831251881Speter } 2832251881Speter 2833251881Speter SVN_ERR(prepare_merge_props_changed(&props, local_abspath, prop_changes, 2834251881Speter merge_b, scratch_pool, scratch_pool)); 2835251881Speter 2836251881Speter if (props->nelts) 2837251881Speter { 2838251881Speter const svn_wc_conflict_version_t *left; 2839251881Speter const svn_wc_conflict_version_t *right; 2840251881Speter svn_client_ctx_t *ctx = merge_b->ctx; 2841251881Speter svn_wc_notify_state_t prop_state; 2842251881Speter 2843251881Speter SVN_ERR(make_conflict_versions(&left, &right, local_abspath, 2844251881Speter svn_node_dir, &merge_b->merge_source, 2845251881Speter merge_b->target, 2846251881Speter scratch_pool, scratch_pool)); 2847251881Speter 2848251881Speter SVN_ERR(svn_wc_merge_props3(&prop_state, ctx->wc_ctx, local_abspath, 2849251881Speter left, right, 2850251881Speter left_props, props, 2851251881Speter merge_b->dry_run, 2852251881Speter NULL, NULL, 2853251881Speter ctx->cancel_func, ctx->cancel_baton, 2854251881Speter scratch_pool)); 2855251881Speter 2856251881Speter if (prop_state == svn_wc_notify_state_conflicted) 2857251881Speter { 2858251881Speter alloc_and_store_path(&merge_b->conflicted_paths, local_abspath, 2859251881Speter merge_b->pool); 2860251881Speter } 2861251881Speter 2862251881Speter if (prop_state == svn_wc_notify_state_conflicted 2863251881Speter || prop_state == svn_wc_notify_state_merged 2864251881Speter || prop_state == svn_wc_notify_state_changed) 2865251881Speter { 2866251881Speter SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file, 2867251881Speter svn_wc_notify_state_inapplicable, 2868251881Speter prop_state, scratch_pool)); 2869251881Speter } 2870251881Speter } 2871251881Speter 2872251881Speter return SVN_NO_ERROR; 2873251881Speter} 2874251881Speter 2875251881Speter 2876251881Speter/* An svn_diff_tree_processor_t function. 2877251881Speter * 2878251881Speter * Called after merge_dir_opened() when a node doesn't exist in LEFT_SOURCE, 2879251881Speter * but does in RIGHT_SOURCE. After the merge_dir_opened() but before the call 2880251881Speter * to this merge_dir_added() function all descendants will have been added. 2881251881Speter * 2882251881Speter * When a node is replaced instead of just added a separate opened+deleted will 2883251881Speter * be invoked before the current open+added. 2884251881Speter */ 2885251881Speterstatic svn_error_t * 2886251881Spetermerge_dir_added(const char *relpath, 2887251881Speter const svn_diff_source_t *copyfrom_source, 2888251881Speter const svn_diff_source_t *right_source, 2889251881Speter /*const*/ apr_hash_t *copyfrom_props, 2890251881Speter /*const*/ apr_hash_t *right_props, 2891251881Speter void *dir_baton, 2892251881Speter const struct svn_diff_tree_processor_t *processor, 2893251881Speter apr_pool_t *scratch_pool) 2894251881Speter{ 2895251881Speter merge_cmd_baton_t *merge_b = processor->baton; 2896251881Speter struct merge_dir_baton_t *db = dir_baton; 2897251881Speter const char *local_abspath = svn_dirent_join(merge_b->target->abspath, 2898251881Speter relpath, scratch_pool); 2899251881Speter 2900251881Speter /* For consistency; usually a no-op from _dir_added() */ 2901251881Speter SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool)); 2902251881Speter SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool)); 2903251881Speter 2904251881Speter if (db->shadowed) 2905251881Speter { 2906251881Speter if (db->tree_conflict_reason == CONFLICT_REASON_NONE) 2907251881Speter { 2908251881Speter /* We haven't notified for this node yet: report a skip */ 2909251881Speter SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir, 2910251881Speter svn_wc_notify_update_shadowed_add, 2911251881Speter db->skip_reason, scratch_pool)); 2912251881Speter } 2913251881Speter 2914251881Speter return SVN_NO_ERROR; 2915251881Speter } 2916251881Speter 2917251881Speter SVN_ERR_ASSERT( 2918251881Speter db->edited /* Marked edited from merge_open_dir() */ 2919251881Speter && ! merge_b->record_only /* Skip details from merge_open_dir() */ 2920251881Speter ); 2921251881Speter 2922251881Speter if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge) 2923251881Speter && ( !db->parent_baton || !db->parent_baton->added)) 2924251881Speter { 2925251881Speter /* Store the roots of added subtrees */ 2926251881Speter store_path(merge_b->added_abspaths, local_abspath); 2927251881Speter } 2928251881Speter 2929251881Speter if (merge_b->same_repos) 2930251881Speter { 2931251881Speter /* When the directory was added in merge_dir_added() we didn't update its 2932251881Speter pristine properties. Instead we receive the property changes later and 2933251881Speter apply them in this function. 2934251881Speter 2935251881Speter If we would apply them as changes (such as before fixing issue #3405), 2936251881Speter we would see the unmodified properties as local changes, and the 2937251881Speter pristine properties would be out of sync with what the repository 2938251881Speter expects for this directory. 2939251881Speter 2940251881Speter Instead of doing that we now simply set the properties as the pristine 2941251881Speter properties via a private libsvn_wc api. 2942251881Speter */ 2943251881Speter 2944251881Speter const char *copyfrom_url; 2945251881Speter svn_revnum_t copyfrom_rev; 2946251881Speter const char *parent_abspath; 2947251881Speter const char *child; 2948251881Speter 2949251881Speter /* Creating a hash containing regular and entry props */ 2950251881Speter apr_hash_t *new_pristine_props = right_props; 2951251881Speter 2952251881Speter parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool); 2953251881Speter child = svn_dirent_is_child(merge_b->target->abspath, local_abspath, NULL); 2954251881Speter SVN_ERR_ASSERT(child != NULL); 2955251881Speter 2956251881Speter copyfrom_url = svn_path_url_add_component2(merge_b->merge_source.loc2->url, 2957251881Speter child, scratch_pool); 2958251881Speter copyfrom_rev = right_source->revision; 2959251881Speter 2960251881Speter SVN_ERR(check_repos_match(merge_b->target, parent_abspath, copyfrom_url, 2961251881Speter scratch_pool)); 2962251881Speter 2963251881Speter if (!merge_b->dry_run) 2964251881Speter { 2965251881Speter SVN_ERR(svn_wc__complete_directory_add(merge_b->ctx->wc_ctx, 2966251881Speter local_abspath, 2967251881Speter new_pristine_props, 2968251881Speter copyfrom_url, copyfrom_rev, 2969251881Speter scratch_pool)); 2970251881Speter } 2971251881Speter 2972251881Speter if (svn_hash_gets(new_pristine_props, SVN_PROP_MERGEINFO)) 2973251881Speter { 2974251881Speter alloc_and_store_path(&merge_b->paths_with_new_mergeinfo, 2975251881Speter local_abspath, merge_b->pool); 2976251881Speter } 2977251881Speter } 2978251881Speter else 2979251881Speter { 2980251881Speter apr_array_header_t *regular_props; 2981251881Speter apr_hash_t *new_props; 2982251881Speter svn_wc_notify_state_t prop_state; 2983251881Speter 2984251881Speter SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props, 2985251881Speter scratch_pool), 2986251881Speter NULL, NULL, ®ular_props, scratch_pool)); 2987251881Speter 2988251881Speter new_props = svn_prop_array_to_hash(regular_props, scratch_pool); 2989251881Speter 2990251881Speter svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL); 2991251881Speter 2992251881Speter /* ### What is the easiest way to set new_props on LOCAL_ABSPATH? 2993251881Speter 2994251881Speter ### This doesn't need a merge as we just added the node 2995251881Speter ### (or installed a tree conflict and skipped this node)*/ 2996251881Speter 2997251881Speter SVN_ERR(svn_wc_merge_props3(&prop_state, merge_b->ctx->wc_ctx, 2998251881Speter local_abspath, 2999251881Speter NULL, NULL, 3000251881Speter apr_hash_make(scratch_pool), 3001251881Speter svn_prop_hash_to_array(new_props, 3002251881Speter scratch_pool), 3003251881Speter merge_b->dry_run, 3004251881Speter NULL, NULL, 3005251881Speter merge_b->ctx->cancel_func, 3006251881Speter merge_b->ctx->cancel_baton, 3007251881Speter scratch_pool)); 3008251881Speter if (prop_state == svn_wc_notify_state_conflicted) 3009251881Speter { 3010251881Speter alloc_and_store_path(&merge_b->conflicted_paths, local_abspath, 3011251881Speter merge_b->pool); 3012251881Speter } 3013251881Speter } 3014251881Speter 3015251881Speter return SVN_NO_ERROR; 3016251881Speter} 3017251881Speter 3018251881Speter/* Helper for merge_dir_deleted. Implement svn_wc_status_func4_t */ 3019251881Speterstatic svn_error_t * 3020251881Speterverify_touched_by_del_check(void *baton, 3021251881Speter const char *local_abspath, 3022251881Speter const svn_wc_status3_t *status, 3023251881Speter apr_pool_t *scratch_pool) 3024251881Speter{ 3025251881Speter struct dir_delete_baton_t *delb = baton; 3026251881Speter 3027251881Speter if (svn_hash_gets(delb->compared_abspaths, local_abspath)) 3028251881Speter return SVN_NO_ERROR; 3029251881Speter 3030251881Speter switch (status->node_status) 3031251881Speter { 3032251881Speter case svn_wc_status_deleted: 3033251881Speter case svn_wc_status_ignored: 3034251881Speter case svn_wc_status_none: 3035251881Speter return SVN_NO_ERROR; 3036251881Speter 3037251881Speter default: 3038251881Speter delb->found_edit = TRUE; 3039251881Speter return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); 3040251881Speter } 3041251881Speter} 3042251881Speter 3043251881Speter/* An svn_diff_tree_processor_t function. 3044251881Speter * 3045251881Speter * Called after merge_dir_opened() when a node existed only in the left source. 3046251881Speter * 3047251881Speter * After the merge_dir_opened() but before the call to this merge_dir_deleted() 3048251881Speter * function all descendants that existed in left_source will have been deleted. 3049251881Speter * 3050251881Speter * If this node is replaced, an _opened() followed by a matching _add() will 3051251881Speter * be invoked after this function. 3052251881Speter */ 3053251881Speterstatic svn_error_t * 3054251881Spetermerge_dir_deleted(const char *relpath, 3055251881Speter const svn_diff_source_t *left_source, 3056251881Speter /*const*/ apr_hash_t *left_props, 3057251881Speter void *dir_baton, 3058251881Speter const struct svn_diff_tree_processor_t *processor, 3059251881Speter apr_pool_t *scratch_pool) 3060251881Speter{ 3061251881Speter merge_cmd_baton_t *merge_b = processor->baton; 3062251881Speter struct merge_dir_baton_t *db = dir_baton; 3063251881Speter const char *local_abspath = svn_dirent_join(merge_b->target->abspath, 3064251881Speter relpath, scratch_pool); 3065251881Speter struct dir_delete_baton_t *delb; 3066251881Speter svn_boolean_t same; 3067251881Speter apr_hash_t *working_props; 3068251881Speter 3069251881Speter SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool)); 3070251881Speter SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool)); 3071251881Speter 3072251881Speter if (db->shadowed) 3073251881Speter { 3074251881Speter if (db->tree_conflict_reason == CONFLICT_REASON_NONE) 3075251881Speter { 3076251881Speter /* We haven't notified for this node yet: report a skip */ 3077251881Speter SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir, 3078251881Speter svn_wc_notify_update_shadowed_delete, 3079251881Speter db->skip_reason, scratch_pool)); 3080251881Speter } 3081251881Speter 3082251881Speter return SVN_NO_ERROR; 3083251881Speter } 3084251881Speter 3085251881Speter /* Easy out: We are only applying mergeinfo differences. */ 3086251881Speter if (merge_b->record_only) 3087251881Speter { 3088251881Speter return SVN_NO_ERROR; 3089251881Speter } 3090251881Speter 3091251881Speter SVN_ERR(svn_wc_prop_list2(&working_props, 3092251881Speter merge_b->ctx->wc_ctx, local_abspath, 3093251881Speter scratch_pool, scratch_pool)); 3094251881Speter 3095251881Speter if (merge_b->force_delete) 3096251881Speter same = TRUE; 3097251881Speter else 3098251881Speter { 3099251881Speter /* Compare the properties */ 3100251881Speter SVN_ERR(properties_same_p(&same, left_props, working_props, 3101251881Speter scratch_pool)); 3102251881Speter } 3103251881Speter 3104251881Speter delb = db->delete_state; 3105251881Speter assert(delb != NULL); 3106251881Speter 3107251881Speter if (! same) 3108251881Speter { 3109251881Speter delb->found_edit = TRUE; 3110251881Speter } 3111251881Speter else 3112251881Speter { 3113251881Speter store_path(delb->compared_abspaths, local_abspath); 3114251881Speter } 3115251881Speter 3116251881Speter if (delb->del_root != db) 3117251881Speter return SVN_NO_ERROR; 3118251881Speter 3119251881Speter if (delb->found_edit) 3120251881Speter same = FALSE; 3121251881Speter else if (merge_b->force_delete) 3122251881Speter same = TRUE; 3123251881Speter else 3124251881Speter { 3125251881Speter apr_array_header_t *ignores; 3126251881Speter svn_error_t *err; 3127251881Speter same = TRUE; 3128251881Speter 3129251881Speter SVN_ERR(svn_wc_get_default_ignores(&ignores, merge_b->ctx->config, 3130251881Speter scratch_pool)); 3131251881Speter 3132251881Speter /* None of the descendants was modified, but maybe there are 3133251881Speter descendants we haven't walked? 3134251881Speter 3135251881Speter Note that we aren't interested in changes, as we already verified 3136251881Speter changes in the paths touched by the merge. And the existance of 3137251881Speter other paths is enough to mark the directory edited */ 3138251881Speter err = svn_wc_walk_status(merge_b->ctx->wc_ctx, local_abspath, 3139251881Speter svn_depth_infinity, TRUE /* get-all */, 3140251881Speter FALSE /* no-ignore */, 3141251881Speter TRUE /* ignore-text-mods */, ignores, 3142251881Speter verify_touched_by_del_check, delb, 3143251881Speter merge_b->ctx->cancel_func, 3144251881Speter merge_b->ctx->cancel_baton, 3145251881Speter scratch_pool); 3146251881Speter 3147251881Speter if (err) 3148251881Speter { 3149251881Speter if (err->apr_err != SVN_ERR_CEASE_INVOCATION) 3150251881Speter return svn_error_trace(err); 3151251881Speter 3152251881Speter svn_error_clear(err); 3153251881Speter } 3154251881Speter 3155251881Speter same = ! delb->found_edit; 3156251881Speter } 3157251881Speter 3158251881Speter if (same && !merge_b->dry_run) 3159251881Speter { 3160251881Speter svn_error_t *err; 3161251881Speter 3162251881Speter err = svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath, 3163251881Speter FALSE /* keep_local */, FALSE /* unversioned */, 3164251881Speter merge_b->ctx->cancel_func, 3165251881Speter merge_b->ctx->cancel_baton, 3166251881Speter NULL, NULL /* no notify */, 3167251881Speter scratch_pool); 3168251881Speter 3169251881Speter if (err) 3170251881Speter { 3171251881Speter if (err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD) 3172251881Speter return svn_error_trace(err); 3173251881Speter 3174251881Speter svn_error_clear(err); 3175251881Speter same = FALSE; 3176251881Speter } 3177251881Speter } 3178251881Speter 3179251881Speter if (! same) 3180251881Speter { 3181251881Speter /* If the attempt to delete an existing directory failed, 3182251881Speter * the directory has local modifications (e.g. locally added 3183251881Speter * files, or property changes). Flag a tree conflict. */ 3184251881Speter 3185251881Speter /* This handles use case 5 described in the paper attached to issue 3186251881Speter * #2282. See also notes/tree-conflicts/detection.txt 3187251881Speter */ 3188251881Speter SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton, 3189251881Speter svn_node_dir, 3190251881Speter svn_wc_conflict_action_delete, 3191251881Speter svn_wc_conflict_reason_edited, 3192251881Speter NULL, TRUE, 3193251881Speter scratch_pool)); 3194251881Speter } 3195251881Speter else 3196251881Speter { 3197251881Speter /* Record that we might have deleted mergeinfo */ 3198251881Speter if (working_props 3199251881Speter && svn_hash_gets(working_props, SVN_PROP_MERGEINFO)) 3200251881Speter { 3201251881Speter alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo, 3202251881Speter local_abspath, merge_b->pool); 3203251881Speter } 3204251881Speter 3205251881Speter SVN_ERR(record_update_delete(merge_b, db->parent_baton, local_abspath, 3206251881Speter svn_node_dir, scratch_pool)); 3207251881Speter } 3208251881Speter 3209251881Speter return SVN_NO_ERROR; 3210251881Speter} 3211251881Speter 3212251881Speter/* An svn_diff_tree_processor_t function. 3213251881Speter * 3214251881Speter * Called after merge_dir_opened() when a node itself didn't change between 3215251881Speter * the left and right source. 3216251881Speter * 3217251881Speter * After the merge_dir_opened() but before the call to this merge_dir_closed() 3218251881Speter * function all descendants will have been processed. 3219251881Speter */ 3220251881Speterstatic svn_error_t * 3221251881Spetermerge_dir_closed(const char *relpath, 3222251881Speter const svn_diff_source_t *left_source, 3223251881Speter const svn_diff_source_t *right_source, 3224251881Speter void *dir_baton, 3225251881Speter const struct svn_diff_tree_processor_t *processor, 3226251881Speter apr_pool_t *scratch_pool) 3227251881Speter{ 3228251881Speter merge_cmd_baton_t *merge_b = processor->baton; 3229251881Speter struct merge_dir_baton_t *db = dir_baton; 3230251881Speter 3231251881Speter SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool)); 3232251881Speter 3233251881Speter return SVN_NO_ERROR; 3234251881Speter} 3235251881Speter 3236251881Speter/* An svn_diff_tree_processor_t function. 3237251881Speter 3238251881Speter Called when the diff driver wants to report an absent path. 3239251881Speter 3240251881Speter In case of merges this happens when the diff encounters a server-excluded 3241251881Speter path. 3242251881Speter 3243251881Speter We register a skipped path, which will make parent mergeinfo non- 3244251881Speter inheritable. This ensures that a future merge might see these skipped 3245251881Speter changes as eligable for merging. 3246251881Speter 3247251881Speter For legacy reasons we also notify the path as skipped. 3248251881Speter */ 3249251881Speterstatic svn_error_t * 3250251881Spetermerge_node_absent(const char *relpath, 3251251881Speter void *dir_baton, 3252251881Speter const svn_diff_tree_processor_t *processor, 3253251881Speter apr_pool_t *scratch_pool) 3254251881Speter{ 3255251881Speter merge_cmd_baton_t *merge_b = processor->baton; 3256251881Speter 3257251881Speter const char *local_abspath = svn_dirent_join(merge_b->target->abspath, 3258251881Speter relpath, scratch_pool); 3259251881Speter 3260251881Speter SVN_ERR(record_skip(merge_b, local_abspath, svn_node_unknown, 3261251881Speter svn_wc_notify_skip, svn_wc_notify_state_missing, 3262251881Speter scratch_pool)); 3263251881Speter 3264251881Speter return SVN_NO_ERROR; 3265251881Speter} 3266251881Speter 3267251881Speter/*-----------------------------------------------------------------------*/ 3268251881Speter 3269251881Speter/*** Merge Notification ***/ 3270251881Speter 3271251881Speter 3272251881Speter/* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for LOCAL_ABSPATH. If 3273251881Speter PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO 3274251881Speter where child->abspath == PATH is considered PATH's ancestor. If FALSE, 3275251881Speter then child->abspath must be a proper ancestor of PATH. 3276251881Speter 3277251881Speter CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first 3278251881Speter order of path. */ 3279251881Speterstatic svn_client__merge_path_t * 3280251881Speterfind_nearest_ancestor(const apr_array_header_t *children_with_mergeinfo, 3281251881Speter svn_boolean_t path_is_own_ancestor, 3282251881Speter const char *local_abspath) 3283251881Speter{ 3284251881Speter int i; 3285251881Speter 3286251881Speter SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL); 3287251881Speter 3288251881Speter for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--) 3289251881Speter { 3290251881Speter svn_client__merge_path_t *child = 3291251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); 3292251881Speter 3293251881Speter if (svn_dirent_is_ancestor(child->abspath, local_abspath) 3294251881Speter && (path_is_own_ancestor 3295251881Speter || strcmp(child->abspath, local_abspath) != 0)) 3296251881Speter return child; 3297251881Speter } 3298251881Speter return NULL; 3299251881Speter} 3300251881Speter 3301251881Speter/* Find the highest level path in a merge target (possibly the merge target 3302251881Speter itself) to use in a merge notification header. 3303251881Speter 3304251881Speter Return the svn_client__merge_path_t * representing the most distant 3305251881Speter ancestor in CHILDREN_WITH_MERGEINFO of LOCAL_ABSPATH where said 3306251881Speter ancestor's first remaining ranges element (per the REMAINING_RANGES 3307251881Speter member of the ancestor) intersect with the first remaining ranges element 3308251881Speter for every intermediate ancestor svn_client__merge_path_t * of 3309251881Speter LOCAL_ABSPATH. If no such ancestor is found return NULL. 3310251881Speter 3311251881Speter If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO 3312251881Speter represent a forward merge, then set *START to the oldest revision found 3313251881Speter in any of the intersecting ancestors and *END to the youngest revision 3314251881Speter found. If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO 3315251881Speter represent a reverse merge, then set *START to the youngest revision 3316251881Speter found and *END to the oldest revision found. If no ancestors are found 3317251881Speter then set *START and *END to SVN_INVALID_REVNUM. 3318251881Speter 3319251881Speter If PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO 3320251881Speter where child->abspath == PATH is considered PATH's ancestor. If FALSE, 3321251881Speter then child->abspath must be a proper ancestor of PATH. 3322251881Speter 3323251881Speter See the CHILDREN_WITH_MERGEINFO ARRAY global comment for more 3324251881Speter information. */ 3325251881Speterstatic svn_client__merge_path_t * 3326251881Speterfind_nearest_ancestor_with_intersecting_ranges( 3327251881Speter svn_revnum_t *start, 3328251881Speter svn_revnum_t *end, 3329251881Speter const apr_array_header_t *children_with_mergeinfo, 3330251881Speter svn_boolean_t path_is_own_ancestor, 3331251881Speter const char *local_abspath) 3332251881Speter{ 3333251881Speter int i; 3334251881Speter svn_client__merge_path_t *nearest_ancestor = NULL; 3335251881Speter 3336251881Speter *start = SVN_INVALID_REVNUM; 3337251881Speter *end = SVN_INVALID_REVNUM; 3338251881Speter 3339251881Speter SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL); 3340251881Speter 3341251881Speter for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--) 3342251881Speter { 3343251881Speter svn_client__merge_path_t *child = 3344251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); 3345251881Speter 3346251881Speter if (svn_dirent_is_ancestor(child->abspath, local_abspath) 3347251881Speter && (path_is_own_ancestor 3348251881Speter || strcmp(child->abspath, local_abspath) != 0)) 3349251881Speter { 3350251881Speter if (nearest_ancestor == NULL) 3351251881Speter { 3352251881Speter /* Found an ancestor. */ 3353251881Speter nearest_ancestor = child; 3354251881Speter 3355251881Speter if (child->remaining_ranges) 3356251881Speter { 3357251881Speter svn_merge_range_t *r1 = APR_ARRAY_IDX( 3358251881Speter child->remaining_ranges, 0, svn_merge_range_t *); 3359251881Speter *start = r1->start; 3360251881Speter *end = r1->end; 3361251881Speter } 3362251881Speter else 3363251881Speter { 3364251881Speter /* If CHILD->REMAINING_RANGES is null then LOCAL_ABSPATH 3365251881Speter is inside an absent subtree in the merge target. */ 3366251881Speter *start = SVN_INVALID_REVNUM; 3367251881Speter *end = SVN_INVALID_REVNUM; 3368251881Speter break; 3369251881Speter } 3370251881Speter } 3371251881Speter else 3372251881Speter { 3373251881Speter /* We'e found another ancestor for LOCAL_ABSPATH. Do its 3374251881Speter first remaining range intersect with the previously 3375251881Speter found ancestor? */ 3376251881Speter svn_merge_range_t *r1 = 3377251881Speter APR_ARRAY_IDX(nearest_ancestor->remaining_ranges, 0, 3378251881Speter svn_merge_range_t *); 3379251881Speter svn_merge_range_t *r2 = 3380251881Speter APR_ARRAY_IDX(child->remaining_ranges, 0, 3381251881Speter svn_merge_range_t *); 3382251881Speter 3383251881Speter if (r1 && r2) 3384251881Speter { 3385251881Speter svn_merge_range_t range1; 3386251881Speter svn_merge_range_t range2; 3387251881Speter svn_boolean_t reverse_merge = r1->start > r2->end; 3388251881Speter 3389251881Speter /* Flip endpoints if this is a reverse merge. */ 3390251881Speter if (reverse_merge) 3391251881Speter { 3392251881Speter range1.start = r1->end; 3393251881Speter range1.end = r1->start; 3394251881Speter range2.start = r2->end; 3395251881Speter range2.end = r2->start; 3396251881Speter } 3397251881Speter else 3398251881Speter { 3399251881Speter range1.start = r1->start; 3400251881Speter range1.end = r1->end; 3401251881Speter range2.start = r2->start; 3402251881Speter range2.end = r2->end; 3403251881Speter } 3404251881Speter 3405251881Speter if (range1.start < range2.end && range2.start < range1.end) 3406251881Speter { 3407251881Speter *start = reverse_merge ? 3408251881Speter MAX(r1->start, r2->start) : MIN(r1->start, r2->start); 3409251881Speter *end = reverse_merge ? 3410251881Speter MIN(r1->end, r2->end) : MAX(r1->end, r2->end); 3411251881Speter nearest_ancestor = child; 3412251881Speter } 3413251881Speter } 3414251881Speter } 3415251881Speter } 3416251881Speter } 3417251881Speter return nearest_ancestor; 3418251881Speter} 3419251881Speter 3420251881Speter/* Notify that we're starting to record mergeinfo for the merge of the 3421251881Speter * revision range RANGE into TARGET_ABSPATH. RANGE should be null if the 3422251881Speter * merge sources are not from the same URL. 3423251881Speter * 3424251881Speter * This calls the client's notification receiver (as found in the client 3425251881Speter * context), with a WC abspath. 3426251881Speter */ 3427251881Speterstatic void 3428251881Speternotify_mergeinfo_recording(const char *target_abspath, 3429251881Speter const svn_merge_range_t *range, 3430251881Speter svn_client_ctx_t *ctx, 3431251881Speter apr_pool_t *pool) 3432251881Speter{ 3433251881Speter if (ctx->notify_func2) 3434251881Speter { 3435251881Speter svn_wc_notify_t *n = svn_wc_create_notify( 3436251881Speter target_abspath, svn_wc_notify_merge_record_info_begin, pool); 3437251881Speter 3438251881Speter n->merge_range = range ? svn_merge_range_dup(range, pool) : NULL; 3439251881Speter ctx->notify_func2(ctx->notify_baton2, n, pool); 3440251881Speter } 3441251881Speter} 3442251881Speter 3443251881Speter/* Notify that we're completing the merge into TARGET_ABSPATH. 3444251881Speter * 3445251881Speter * This calls the client's notification receiver (as found in the client 3446251881Speter * context), with a WC abspath. 3447251881Speter */ 3448251881Speterstatic void 3449251881Speternotify_merge_completed(const char *target_abspath, 3450251881Speter svn_client_ctx_t *ctx, 3451251881Speter apr_pool_t *pool) 3452251881Speter{ 3453251881Speter if (ctx->notify_func2) 3454251881Speter { 3455251881Speter svn_wc_notify_t *n 3456251881Speter = svn_wc_create_notify(target_abspath, svn_wc_notify_merge_completed, 3457251881Speter pool); 3458251881Speter ctx->notify_func2(ctx->notify_baton2, n, pool); 3459251881Speter } 3460251881Speter} 3461251881Speter 3462251881Speter/* Is the notification the result of a real operative merge? */ 3463251881Speter#define IS_OPERATIVE_NOTIFICATION(notify) \ 3464251881Speter (notify->content_state == svn_wc_notify_state_conflicted \ 3465251881Speter || notify->content_state == svn_wc_notify_state_merged \ 3466251881Speter || notify->content_state == svn_wc_notify_state_changed \ 3467251881Speter || notify->prop_state == svn_wc_notify_state_conflicted \ 3468251881Speter || notify->prop_state == svn_wc_notify_state_merged \ 3469251881Speter || notify->prop_state == svn_wc_notify_state_changed \ 3470251881Speter || notify->action == svn_wc_notify_update_add \ 3471251881Speter || notify->action == svn_wc_notify_tree_conflict) 3472251881Speter 3473251881Speter 3474251881Speter/* Remove merge source gaps from range used for merge notifications. 3475251881Speter See http://subversion.tigris.org/issues/show_bug.cgi?id=4138 3476251881Speter 3477251881Speter If IMPLICIT_SRC_GAP is not NULL then it is a rangelist containing a 3478251881Speter single range (see the implicit_src_gap member of merge_cmd_baton_t). 3479251881Speter RANGE describes a (possibly reverse) merge. 3480251881Speter 3481251881Speter If IMPLICIT_SRC_GAP is not NULL and it's sole range intersects with 3482251881Speter the older revision in *RANGE, then remove IMPLICIT_SRC_GAP's range 3483251881Speter from *RANGE. */ 3484251881Speterstatic void 3485251881Speterremove_source_gap(svn_merge_range_t *range, 3486251881Speter apr_array_header_t *implicit_src_gap) 3487251881Speter{ 3488251881Speter if (implicit_src_gap) 3489251881Speter { 3490251881Speter svn_merge_range_t *gap_range = 3491251881Speter APR_ARRAY_IDX(implicit_src_gap, 0, svn_merge_range_t *); 3492251881Speter if (range->start < range->end) 3493251881Speter { 3494251881Speter if (gap_range->start == range->start) 3495251881Speter range->start = gap_range->end; 3496251881Speter } 3497251881Speter else /* Reverse merge */ 3498251881Speter { 3499251881Speter if (gap_range->start == range->end) 3500251881Speter range->end = gap_range->end; 3501251881Speter } 3502251881Speter } 3503251881Speter} 3504251881Speter 3505251881Speter/* Notify that we're starting a merge 3506251881Speter * 3507251881Speter * This calls the client's notification receiver (as found in the client 3508251881Speter * context), with a WC abspath. 3509251881Speter */ 3510251881Speterstatic svn_error_t * 3511251881Speternotify_merge_begin(merge_cmd_baton_t *merge_b, 3512251881Speter const char *local_abspath, 3513251881Speter svn_boolean_t delete_action, 3514251881Speter apr_pool_t *scratch_pool) 3515251881Speter{ 3516251881Speter svn_wc_notify_t *notify; 3517251881Speter svn_merge_range_t n_range = 3518251881Speter {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE}; 3519251881Speter const char *notify_abspath; 3520251881Speter 3521251881Speter if (! merge_b->ctx->notify_func2) 3522251881Speter return SVN_NO_ERROR; 3523251881Speter 3524251881Speter /* If our merge sources are ancestors of one another... */ 3525251881Speter if (merge_b->merge_source.ancestral) 3526251881Speter { 3527251881Speter const svn_client__merge_path_t *child; 3528251881Speter /* Find NOTIFY->PATH's nearest ancestor in 3529251881Speter NOTIFY->CHILDREN_WITH_MERGEINFO. Normally we consider a child in 3530251881Speter NOTIFY->CHILDREN_WITH_MERGEINFO representing PATH to be an 3531251881Speter ancestor of PATH, but if this is a deletion of PATH then the 3532251881Speter notification must be for a proper ancestor of PATH. This ensures 3533251881Speter we don't get notifications like: 3534251881Speter 3535251881Speter --- Merging rX into 'PARENT/CHILD' 3536251881Speter D PARENT/CHILD 3537251881Speter 3538251881Speter But rather: 3539251881Speter 3540251881Speter --- Merging rX into 'PARENT' 3541251881Speter D PARENT/CHILD 3542251881Speter */ 3543251881Speter 3544251881Speter child = find_nearest_ancestor_with_intersecting_ranges( 3545251881Speter &(n_range.start), &(n_range.end), 3546251881Speter merge_b->notify_begin.nodes_with_mergeinfo, 3547251881Speter ! delete_action, local_abspath); 3548251881Speter 3549251881Speter if (!child && delete_action) 3550251881Speter { 3551251881Speter /* Triggered by file replace in single-file-merge */ 3552251881Speter child = find_nearest_ancestor(merge_b->notify_begin.nodes_with_mergeinfo, 3553251881Speter TRUE, local_abspath); 3554251881Speter } 3555251881Speter 3556251881Speter assert(child != NULL); /* Should always find the merge anchor */ 3557251881Speter 3558251881Speter if (! child) 3559251881Speter return SVN_NO_ERROR; 3560251881Speter 3561251881Speter if (merge_b->notify_begin.last_abspath != NULL 3562251881Speter && strcmp(child->abspath, merge_b->notify_begin.last_abspath) == 0) 3563251881Speter { 3564251881Speter /* Don't notify the same merge again */ 3565251881Speter return SVN_NO_ERROR; 3566251881Speter } 3567251881Speter 3568251881Speter merge_b->notify_begin.last_abspath = child->abspath; 3569251881Speter 3570251881Speter if (child->absent || child->remaining_ranges->nelts == 0 3571251881Speter || !SVN_IS_VALID_REVNUM(n_range.start)) 3572251881Speter { 3573251881Speter /* No valid information for an header */ 3574251881Speter return SVN_NO_ERROR; 3575251881Speter } 3576251881Speter 3577251881Speter notify_abspath = child->abspath; 3578251881Speter } 3579251881Speter else 3580251881Speter { 3581251881Speter if (merge_b->notify_begin.last_abspath) 3582251881Speter return SVN_NO_ERROR; /* already notified */ 3583251881Speter 3584251881Speter notify_abspath = merge_b->target->abspath; 3585251881Speter /* Store something in last_abspath. Any value would do */ 3586251881Speter merge_b->notify_begin.last_abspath = merge_b->target->abspath; 3587251881Speter } 3588251881Speter 3589251881Speter notify = svn_wc_create_notify(notify_abspath, 3590251881Speter merge_b->same_repos 3591251881Speter ? svn_wc_notify_merge_begin 3592251881Speter : svn_wc_notify_foreign_merge_begin, 3593251881Speter scratch_pool); 3594251881Speter 3595251881Speter if (SVN_IS_VALID_REVNUM(n_range.start)) 3596251881Speter { 3597251881Speter /* If the merge source has a gap, then don't mention 3598251881Speter those gap revisions in the notification. */ 3599251881Speter remove_source_gap(&n_range, merge_b->implicit_src_gap); 3600251881Speter notify->merge_range = &n_range; 3601251881Speter } 3602251881Speter else 3603251881Speter { 3604251881Speter notify->merge_range = NULL; 3605251881Speter } 3606251881Speter 3607251881Speter (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify, 3608251881Speter scratch_pool); 3609251881Speter 3610251881Speter return SVN_NO_ERROR; 3611251881Speter} 3612251881Speter 3613251881Speter/* Set *OUT_RANGELIST to the intersection of IN_RANGELIST with the simple 3614251881Speter * (inheritable) revision range REV1:REV2, according to CONSIDER_INHERITANCE. 3615251881Speter * If REV1 is equal to REV2, the result is an empty rangelist, otherwise 3616251881Speter * REV1 must be less than REV2. 3617251881Speter * 3618251881Speter * Note: If CONSIDER_INHERITANCE is FALSE, the effect is to treat any non- 3619251881Speter * inheritable input ranges as if they were inheritable. If it is TRUE, the 3620251881Speter * effect is to discard any non-inheritable input ranges. Therefore the 3621251881Speter * ranges in *OUT_RANGELIST will always be inheritable. */ 3622251881Speterstatic svn_error_t * 3623251881Speterrangelist_intersect_range(svn_rangelist_t **out_rangelist, 3624251881Speter const svn_rangelist_t *in_rangelist, 3625251881Speter svn_revnum_t rev1, 3626251881Speter svn_revnum_t rev2, 3627251881Speter svn_boolean_t consider_inheritance, 3628251881Speter apr_pool_t *result_pool, 3629251881Speter apr_pool_t *scratch_pool) 3630251881Speter{ 3631251881Speter SVN_ERR_ASSERT(rev1 <= rev2); 3632251881Speter 3633251881Speter if (rev1 < rev2) 3634251881Speter { 3635251881Speter svn_rangelist_t *simple_rangelist = 3636251881Speter svn_rangelist__initialize(rev1, rev2, TRUE, scratch_pool); 3637251881Speter 3638251881Speter SVN_ERR(svn_rangelist_intersect(out_rangelist, 3639251881Speter simple_rangelist, in_rangelist, 3640251881Speter consider_inheritance, result_pool)); 3641251881Speter } 3642251881Speter else 3643251881Speter { 3644251881Speter *out_rangelist = apr_array_make(result_pool, 0, 3645251881Speter sizeof(svn_merge_range_t *)); 3646251881Speter } 3647251881Speter return SVN_NO_ERROR; 3648251881Speter} 3649251881Speter 3650251881Speter/* Helper for fix_deleted_subtree_ranges(). Like fix_deleted_subtree_ranges() 3651251881Speter this function should only be called when honoring mergeinfo. 3652251881Speter 3653251881Speter CHILD, PARENT, REVISION1, REVISION2, and RA_SESSION are all cascaded from 3654251881Speter fix_deleted_subtree_ranges() -- see that function for more information on 3655251881Speter each. 3656251881Speter 3657251881Speter If PARENT is not the merge target then PARENT must have already have been 3658251881Speter processed by this function as a child. Specifically, this means that 3659251881Speter PARENT->REMAINING_RANGES must already be populated -- it can be an empty 3660251881Speter rangelist but cannot be NULL. 3661251881Speter 3662251881Speter PRIMARY_URL is the merge source url of CHILD at the younger of REVISION1 3663251881Speter and REVISION2. 3664251881Speter 3665251881Speter Since this function is only invoked for subtrees of the merge target, the 3666251881Speter guarantees afforded by normalize_merge_sources() don't apply - see the 3667251881Speter 'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file. 3668251881Speter Therefore it is possible that PRIMARY_URL@REVISION1 and 3669251881Speter PRIMARY_URL@REVISION2 don't describe the endpoints of an unbroken line of 3670251881Speter history. The purpose of this helper is to identify these cases of broken 3671251881Speter history and adjust CHILD->REMAINING_RANGES in such a way we don't later try 3672251881Speter to describe nonexistent path/revisions to the merge report editor -- see 3673251881Speter drive_merge_report_editor(). 3674251881Speter 3675251881Speter If PRIMARY_URL@REVISION1 and PRIMARY_URL@REVISION2 describe an unbroken 3676251881Speter line of history then do nothing and leave CHILD->REMAINING_RANGES as-is. 3677251881Speter 3678251881Speter If neither PRIMARY_URL@REVISION1 nor PRIMARY_URL@REVISION2 exist then 3679251881Speter there is nothing to merge to CHILD->ABSPATH so set CHILD->REMAINING_RANGES 3680251881Speter equal to PARENT->REMAINING_RANGES. This will cause the subtree to 3681251881Speter effectively ignore CHILD -- see 'Note: If the first svn_merge_range_t...' 3682251881Speter in drive_merge_report_editor()'s doc string. 3683251881Speter 3684251881Speter If PRIMARY_URL@REVISION1 *xor* PRIMARY_URL@REVISION2 exist then we take the 3685251881Speter subset of REVISION1:REVISION2 in CHILD->REMAINING_RANGES at which 3686251881Speter PRIMARY_URL doesn't exist and set that subset equal to 3687251881Speter PARENT->REMAINING_RANGES' intersection with that non-existent range. Why? 3688251881Speter Because this causes CHILD->REMAINING_RANGES to be identical to 3689251881Speter PARENT->REMAINING_RANGES for revisions between REVISION1 and REVISION2 at 3690251881Speter which PRIMARY_URL doesn't exist. As mentioned above this means that 3691251881Speter drive_merge_report_editor() won't attempt to describe these non-existent 3692251881Speter subtree path/ranges to the reporter (which would break the merge). 3693251881Speter 3694251881Speter If the preceding paragraph wasn't terribly clear then what follows spells 3695251881Speter out this function's behavior a bit more explicitly: 3696251881Speter 3697251881Speter For forward merges (REVISION1 < REVISION2) 3698251881Speter 3699251881Speter If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then 3700251881Speter find the revision 'N' in which PRIMARY_URL@REVISION1 was deleted. Leave 3701251881Speter the subset of CHILD->REMAINING_RANGES that intersects with 3702251881Speter REVISION1:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES 3703251881Speter that intersects with (N - 1):REVISION2 equal to PARENT->REMAINING_RANGES' 3704251881Speter intersection with (N - 1):REVISION2. 3705251881Speter 3706251881Speter If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does, 3707251881Speter then find the revision 'M' in which PRIMARY_URL@REVISION2 came into 3708251881Speter existence. Leave the subset of CHILD->REMAINING_RANGES that intersects with 3709251881Speter (M - 1):REVISION2 as-is and set the subset of CHILD->REMAINING_RANGES 3710251881Speter that intersects with REVISION1:(M - 1) equal to PARENT->REMAINING_RANGES' 3711251881Speter intersection with REVISION1:(M - 1). 3712251881Speter 3713251881Speter For reverse merges (REVISION1 > REVISION2) 3714251881Speter 3715251881Speter If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then 3716251881Speter find the revision 'N' in which PRIMARY_URL@REVISION1 came into existence. 3717251881Speter Leave the subset of CHILD->REMAINING_RANGES that intersects with 3718251881Speter REVISION2:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES 3719251881Speter that intersects with (N - 1):REVISION1 equal to PARENT->REMAINING_RANGES' 3720251881Speter intersection with (N - 1):REVISION1. 3721251881Speter 3722251881Speter If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does, 3723251881Speter then find the revision 'M' in which PRIMARY_URL@REVISION2 came into 3724251881Speter existence. Leave the subset of CHILD->REMAINING_RANGES that intersects with 3725251881Speter REVISION2:(M - 1) as-is and set the subset of CHILD->REMAINING_RANGES 3726251881Speter that intersects with (M - 1):REVISION1 equal to PARENT->REMAINING_RANGES' 3727251881Speter intersection with REVISION1:(M - 1). 3728251881Speter 3729251881Speter SCRATCH_POOL is used for all temporary allocations. Changes to CHILD are 3730251881Speter allocated in RESULT_POOL. */ 3731251881Speterstatic svn_error_t * 3732251881Speteradjust_deleted_subtree_ranges(svn_client__merge_path_t *child, 3733251881Speter svn_client__merge_path_t *parent, 3734251881Speter svn_revnum_t revision1, 3735251881Speter svn_revnum_t revision2, 3736251881Speter const char *primary_url, 3737251881Speter svn_ra_session_t *ra_session, 3738251881Speter svn_client_ctx_t *ctx, 3739251881Speter apr_pool_t *result_pool, 3740251881Speter apr_pool_t *scratch_pool) 3741251881Speter{ 3742251881Speter svn_boolean_t is_rollback = revision2 < revision1; 3743251881Speter svn_revnum_t younger_rev = is_rollback ? revision1 : revision2; 3744251881Speter svn_revnum_t peg_rev = younger_rev; 3745251881Speter svn_revnum_t older_rev = is_rollback ? revision2 : revision1; 3746251881Speter apr_array_header_t *segments; 3747251881Speter svn_error_t *err; 3748251881Speter 3749251881Speter SVN_ERR_ASSERT(parent->remaining_ranges); 3750251881Speter 3751251881Speter err = svn_client__repos_location_segments(&segments, ra_session, 3752251881Speter primary_url, peg_rev, 3753251881Speter younger_rev, older_rev, ctx, 3754251881Speter scratch_pool); 3755251881Speter 3756251881Speter /* If PRIMARY_URL@peg_rev doesn't exist then 3757251881Speter svn_client__repos_location_segments() typically returns an 3758251881Speter SVN_ERR_FS_NOT_FOUND error, but if it doesn't exist for a 3759251881Speter forward merge over ra_neon then we get SVN_ERR_RA_DAV_REQUEST_FAILED. 3760251881Speter http://subversion.tigris.org/issues/show_bug.cgi?id=3137 fixed some of 3761251881Speter the cases where different RA layers returned different error codes to 3762251881Speter signal the "path not found"...but it looks like there is more to do. 3763251881Speter 3764251881Speter ### Do we still need to special case for ra_neon (since it no longer 3765251881Speter exists)? */ 3766251881Speter if (err) 3767251881Speter { 3768251881Speter if (err->apr_err == SVN_ERR_FS_NOT_FOUND 3769251881Speter || err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED) 3770251881Speter { 3771251881Speter /* PRIMARY_URL@peg_rev doesn't exist. Check if PRIMARY_URL@older_rev 3772251881Speter exists, if neither exist then the editor can simply ignore this 3773251881Speter subtree. */ 3774251881Speter const char *rel_source_path; /* PRIMARY_URL relative to RA_SESSION */ 3775251881Speter svn_node_kind_t kind; 3776251881Speter 3777251881Speter svn_error_clear(err); 3778251881Speter err = NULL; 3779251881Speter 3780251881Speter SVN_ERR(svn_ra_get_path_relative_to_session( 3781251881Speter ra_session, &rel_source_path, primary_url, scratch_pool)); 3782251881Speter 3783251881Speter SVN_ERR(svn_ra_check_path(ra_session, rel_source_path, 3784251881Speter older_rev, &kind, scratch_pool)); 3785251881Speter if (kind == svn_node_none) 3786251881Speter { 3787251881Speter /* Neither PRIMARY_URL@peg_rev nor PRIMARY_URL@older_rev exist, 3788251881Speter so there is nothing to merge. Set CHILD->REMAINING_RANGES 3789251881Speter identical to PARENT's. */ 3790251881Speter child->remaining_ranges = 3791251881Speter svn_rangelist_dup(parent->remaining_ranges, scratch_pool); 3792251881Speter } 3793251881Speter else 3794251881Speter { 3795251881Speter svn_rangelist_t *deleted_rangelist; 3796251881Speter svn_revnum_t rev_primary_url_deleted; 3797251881Speter 3798251881Speter /* PRIMARY_URL@older_rev exists, so it was deleted at some 3799251881Speter revision prior to peg_rev, find that revision. */ 3800251881Speter SVN_ERR(svn_ra_get_deleted_rev(ra_session, rel_source_path, 3801251881Speter older_rev, younger_rev, 3802251881Speter &rev_primary_url_deleted, 3803251881Speter scratch_pool)); 3804251881Speter 3805251881Speter /* PRIMARY_URL@older_rev exists and PRIMARY_URL@peg_rev doesn't, 3806251881Speter so svn_ra_get_deleted_rev() should always find the revision 3807251881Speter PRIMARY_URL@older_rev was deleted. */ 3808251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev_primary_url_deleted)); 3809251881Speter 3810251881Speter /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and 3811251881Speter PARENT->REMAINING_RANGES so both will work with the 3812251881Speter svn_rangelist_* APIs below. */ 3813251881Speter if (is_rollback) 3814251881Speter { 3815251881Speter /* svn_rangelist_reverse operates in place so it's safe 3816251881Speter to use our scratch_pool. */ 3817251881Speter SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, 3818251881Speter scratch_pool)); 3819251881Speter SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, 3820251881Speter scratch_pool)); 3821251881Speter } 3822251881Speter 3823251881Speter /* Find the intersection of CHILD->REMAINING_RANGES with the 3824251881Speter range over which PRIMARY_URL@older_rev exists (ending at 3825251881Speter the youngest revision at which it still exists). */ 3826251881Speter SVN_ERR(rangelist_intersect_range(&child->remaining_ranges, 3827251881Speter child->remaining_ranges, 3828251881Speter older_rev, 3829251881Speter rev_primary_url_deleted - 1, 3830251881Speter FALSE, 3831251881Speter scratch_pool, scratch_pool)); 3832251881Speter 3833251881Speter /* Merge into CHILD->REMAINING_RANGES the intersection of 3834251881Speter PARENT->REMAINING_RANGES with the range beginning when 3835251881Speter PRIMARY_URL@older_rev was deleted until younger_rev. */ 3836251881Speter SVN_ERR(rangelist_intersect_range(&deleted_rangelist, 3837251881Speter parent->remaining_ranges, 3838251881Speter rev_primary_url_deleted - 1, 3839251881Speter peg_rev, 3840251881Speter FALSE, 3841251881Speter scratch_pool, scratch_pool)); 3842251881Speter SVN_ERR(svn_rangelist_merge2(child->remaining_ranges, 3843251881Speter deleted_rangelist, scratch_pool, 3844251881Speter scratch_pool)); 3845251881Speter 3846251881Speter /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES 3847251881Speter to reverse order if necessary. */ 3848251881Speter if (is_rollback) 3849251881Speter { 3850251881Speter SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, 3851251881Speter scratch_pool)); 3852251881Speter SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, 3853251881Speter scratch_pool)); 3854251881Speter } 3855251881Speter } 3856251881Speter } 3857251881Speter else 3858251881Speter { 3859251881Speter return svn_error_trace(err); 3860251881Speter } 3861251881Speter } 3862251881Speter else /* PRIMARY_URL@peg_rev exists. */ 3863251881Speter { 3864251881Speter svn_rangelist_t *non_existent_rangelist; 3865251881Speter svn_location_segment_t *segment = 3866251881Speter APR_ARRAY_IDX(segments, (segments->nelts - 1), 3867251881Speter svn_location_segment_t *); 3868251881Speter 3869251881Speter /* We know PRIMARY_URL@peg_rev exists as the call to 3870251881Speter svn_client__repos_location_segments() succeeded. If there is only 3871251881Speter one segment that starts at oldest_rev then we know that 3872251881Speter PRIMARY_URL@oldest_rev:PRIMARY_URL@peg_rev describes an unbroken 3873251881Speter line of history, so there is nothing more to adjust in 3874251881Speter CHILD->REMAINING_RANGES. */ 3875251881Speter if (segment->range_start == older_rev) 3876251881Speter { 3877251881Speter return SVN_NO_ERROR; 3878251881Speter } 3879251881Speter 3880251881Speter /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and 3881251881Speter PARENT->REMAINING_RANGES so both will work with the 3882251881Speter svn_rangelist_* APIs below. */ 3883251881Speter if (is_rollback) 3884251881Speter { 3885251881Speter SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, 3886251881Speter scratch_pool)); 3887251881Speter SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, 3888251881Speter scratch_pool)); 3889251881Speter } 3890251881Speter 3891251881Speter /* Intersect CHILD->REMAINING_RANGES with the range where PRIMARY_URL 3892251881Speter exists. Since segment doesn't span older_rev:peg_rev we know 3893251881Speter PRIMARY_URL@peg_rev didn't come into existence until 3894251881Speter segment->range_start + 1. */ 3895251881Speter SVN_ERR(rangelist_intersect_range(&child->remaining_ranges, 3896251881Speter child->remaining_ranges, 3897251881Speter segment->range_start, peg_rev, 3898251881Speter FALSE, scratch_pool, scratch_pool)); 3899251881Speter 3900251881Speter /* Merge into CHILD->REMAINING_RANGES the intersection of 3901251881Speter PARENT->REMAINING_RANGES with the range before PRIMARY_URL@peg_rev 3902251881Speter came into existence. */ 3903251881Speter SVN_ERR(rangelist_intersect_range(&non_existent_rangelist, 3904251881Speter parent->remaining_ranges, 3905251881Speter older_rev, segment->range_start, 3906251881Speter FALSE, scratch_pool, scratch_pool)); 3907251881Speter SVN_ERR(svn_rangelist_merge2(child->remaining_ranges, 3908251881Speter non_existent_rangelist, scratch_pool, 3909251881Speter scratch_pool)); 3910251881Speter 3911251881Speter /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES 3912251881Speter to reverse order if necessary. */ 3913251881Speter if (is_rollback) 3914251881Speter { 3915251881Speter SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, 3916251881Speter scratch_pool)); 3917251881Speter SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, 3918251881Speter scratch_pool)); 3919251881Speter } 3920251881Speter } 3921251881Speter 3922251881Speter /* Make a lasting copy of CHILD->REMAINING_RANGES using POOL. */ 3923251881Speter child->remaining_ranges = svn_rangelist_dup(child->remaining_ranges, 3924251881Speter result_pool); 3925251881Speter return SVN_NO_ERROR; 3926251881Speter} 3927251881Speter 3928251881Speter/* Helper for do_directory_merge(). 3929251881Speter 3930251881Speter SOURCE is cascaded from the argument of the same name in 3931251881Speter do_directory_merge(). TARGET is the merge target. RA_SESSION is the 3932251881Speter session for the younger of SOURCE->loc1 and SOURCE->loc2. 3933251881Speter 3934251881Speter Adjust the subtrees in CHILDREN_WITH_MERGEINFO so that we don't 3935251881Speter later try to describe invalid paths in drive_merge_report_editor(). 3936251881Speter This function is just a thin wrapper around 3937251881Speter adjust_deleted_subtree_ranges(), which see for further details. 3938251881Speter 3939251881Speter SCRATCH_POOL is used for all temporary allocations. Changes to 3940251881Speter CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL. 3941251881Speter*/ 3942251881Speterstatic svn_error_t * 3943251881Speterfix_deleted_subtree_ranges(const merge_source_t *source, 3944251881Speter const merge_target_t *target, 3945251881Speter svn_ra_session_t *ra_session, 3946251881Speter apr_array_header_t *children_with_mergeinfo, 3947251881Speter svn_client_ctx_t *ctx, 3948251881Speter apr_pool_t *result_pool, 3949251881Speter apr_pool_t *scratch_pool) 3950251881Speter{ 3951251881Speter int i; 3952251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 3953251881Speter svn_boolean_t is_rollback = source->loc2->rev < source->loc1->rev; 3954251881Speter 3955251881Speter assert(session_url_is(ra_session, 3956251881Speter (is_rollback ? source->loc1 : source->loc2)->url, 3957251881Speter scratch_pool)); 3958251881Speter 3959251881Speter /* CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so 3960251881Speter start at index 1 to examine only subtrees. */ 3961251881Speter for (i = 1; i < children_with_mergeinfo->nelts; i++) 3962251881Speter { 3963251881Speter svn_client__merge_path_t *child = 3964251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); 3965251881Speter svn_client__merge_path_t *parent; 3966251881Speter svn_rangelist_t *deleted_rangelist, *added_rangelist; 3967251881Speter 3968251881Speter SVN_ERR_ASSERT(child); 3969251881Speter if (child->absent) 3970251881Speter continue; 3971251881Speter 3972251881Speter svn_pool_clear(iterpool); 3973251881Speter 3974251881Speter /* Find CHILD's parent. */ 3975251881Speter parent = find_nearest_ancestor(children_with_mergeinfo, 3976251881Speter FALSE, child->abspath); 3977251881Speter 3978251881Speter /* Since CHILD is a subtree then its parent must be in 3979251881Speter CHILDREN_WITH_MERGEINFO, see the global comment 3980251881Speter 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */ 3981251881Speter SVN_ERR_ASSERT(parent); 3982251881Speter 3983251881Speter /* If this is a reverse merge reorder CHILD->REMAINING_RANGES 3984251881Speter so it will work with the svn_rangelist_diff API. */ 3985251881Speter if (is_rollback) 3986251881Speter { 3987251881Speter SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool)); 3988251881Speter SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool)); 3989251881Speter } 3990251881Speter 3991251881Speter SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist, 3992251881Speter child->remaining_ranges, 3993251881Speter parent->remaining_ranges, 3994251881Speter TRUE, iterpool)); 3995251881Speter 3996251881Speter if (is_rollback) 3997251881Speter { 3998251881Speter SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool)); 3999251881Speter SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool)); 4000251881Speter } 4001251881Speter 4002251881Speter /* If CHILD is the merge target we then know that SOURCE is provided 4003251881Speter by normalize_merge_sources() -- see 'MERGEINFO MERGE SOURCE 4004251881Speter NORMALIZATION'. Due to this normalization we know that SOURCE 4005251881Speter describes an unbroken line of history such that the entire range 4006251881Speter described by SOURCE can potentially be merged to CHILD. 4007251881Speter 4008251881Speter But if CHILD is a subtree we don't have the same guarantees about 4009251881Speter SOURCE as we do for the merge target. SOURCE->loc1 and/or 4010251881Speter SOURCE->loc2 might not exist. 4011251881Speter 4012251881Speter If one or both doesn't exist, then adjust CHILD->REMAINING_RANGES 4013251881Speter such that we don't later try to describe invalid subtrees in 4014251881Speter drive_merge_report_editor(), as that will break the merge. 4015251881Speter If CHILD has the same remaining ranges as PARENT however, then 4016251881Speter there is no need to make these adjustments, since 4017251881Speter drive_merge_report_editor() won't attempt to describe CHILD in this 4018251881Speter case, see the 'Note' in drive_merge_report_editor's docstring. */ 4019251881Speter if (deleted_rangelist->nelts || added_rangelist->nelts) 4020251881Speter { 4021251881Speter const char *child_primary_source_url; 4022251881Speter const char *child_repos_src_path = 4023251881Speter svn_dirent_is_child(target->abspath, child->abspath, iterpool); 4024251881Speter 4025251881Speter /* This loop is only processing subtrees, so CHILD->ABSPATH 4026251881Speter better be a proper child of the merge target. */ 4027251881Speter SVN_ERR_ASSERT(child_repos_src_path); 4028251881Speter 4029251881Speter child_primary_source_url = 4030251881Speter svn_path_url_add_component2((source->loc1->rev < source->loc2->rev) 4031251881Speter ? source->loc2->url : source->loc1->url, 4032251881Speter child_repos_src_path, iterpool); 4033251881Speter 4034251881Speter SVN_ERR(adjust_deleted_subtree_ranges(child, parent, 4035251881Speter source->loc1->rev, 4036251881Speter source->loc2->rev, 4037251881Speter child_primary_source_url, 4038251881Speter ra_session, 4039251881Speter ctx, result_pool, iterpool)); 4040251881Speter } 4041251881Speter } 4042251881Speter 4043251881Speter svn_pool_destroy(iterpool); 4044251881Speter return SVN_NO_ERROR; 4045251881Speter} 4046251881Speter 4047251881Speter/*-----------------------------------------------------------------------*/ 4048251881Speter 4049251881Speter/*** Determining What Remains To Be Merged ***/ 4050251881Speter 4051251881Speter/* Get explicit and/or implicit mergeinfo for the working copy path 4052251881Speter TARGET_ABSPATH. 4053251881Speter 4054251881Speter If RECORDED_MERGEINFO is not NULL then set *RECORDED_MERGEINFO 4055251881Speter to TARGET_ABSPATH's explicit or inherited mergeinfo as dictated by 4056251881Speter INHERIT. 4057251881Speter 4058251881Speter If IMPLICIT_MERGEINFO is not NULL then set *IMPLICIT_MERGEINFO 4059251881Speter to TARGET_ABSPATH's implicit mergeinfo (a.k.a. natural history). 4060251881Speter 4061251881Speter If both RECORDED_MERGEINFO and IMPLICIT_MERGEINFO are not NULL and 4062251881Speter *RECORDED_MERGEINFO is inherited, then *IMPLICIT_MERGEINFO will be 4063251881Speter removed from *RECORDED_MERGEINFO. 4064251881Speter 4065251881Speter If INHERITED is not NULL set *INHERITED to TRUE if *RECORDED_MERGEINFO 4066251881Speter is inherited rather than explicit. If RECORDED_MERGEINFO is NULL then 4067251881Speter INHERITED is ignored. 4068251881Speter 4069251881Speter 4070251881Speter If IMPLICIT_MERGEINFO is not NULL then START and END are limits on 4071251881Speter the natural history sought, must both be valid revision numbers, and 4072251881Speter START must be greater than END. If TARGET_ABSPATH's base revision 4073251881Speter is older than START, then the base revision is used as the younger 4074251881Speter bound in place of START. 4075251881Speter 4076251881Speter RA_SESSION is an RA session open to the repository in which TARGET_ABSPATH 4077251881Speter lives. It may be temporarily reparented as needed by this function. 4078251881Speter 4079251881Speter Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL. 4080251881Speter Use SCRATCH_POOL for any temporary allocations. */ 4081251881Speterstatic svn_error_t * 4082251881Speterget_full_mergeinfo(svn_mergeinfo_t *recorded_mergeinfo, 4083251881Speter svn_mergeinfo_t *implicit_mergeinfo, 4084251881Speter svn_boolean_t *inherited, 4085251881Speter svn_mergeinfo_inheritance_t inherit, 4086251881Speter svn_ra_session_t *ra_session, 4087251881Speter const char *target_abspath, 4088251881Speter svn_revnum_t start, 4089251881Speter svn_revnum_t end, 4090251881Speter svn_client_ctx_t *ctx, 4091251881Speter apr_pool_t *result_pool, 4092251881Speter apr_pool_t *scratch_pool) 4093251881Speter{ 4094251881Speter /* First, we get the real mergeinfo. */ 4095251881Speter if (recorded_mergeinfo) 4096251881Speter { 4097251881Speter SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo, 4098251881Speter inherited, 4099251881Speter NULL /* from_repos */, 4100251881Speter FALSE, 4101251881Speter inherit, ra_session, 4102251881Speter target_abspath, 4103251881Speter ctx, result_pool)); 4104251881Speter } 4105251881Speter 4106251881Speter if (implicit_mergeinfo) 4107251881Speter { 4108251881Speter svn_client__pathrev_t *target; 4109251881Speter 4110251881Speter /* Assert that we have sane input. */ 4111251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start) && SVN_IS_VALID_REVNUM(end) 4112251881Speter && (start > end)); 4113251881Speter 4114251881Speter /* Retrieve the origin (original_*) of the node, or just the 4115251881Speter url if the node was not copied. */ 4116251881Speter SVN_ERR(svn_client__wc_node_get_origin(&target, target_abspath, ctx, 4117251881Speter scratch_pool, scratch_pool)); 4118251881Speter 4119251881Speter if (! target) 4120251881Speter { 4121251881Speter /* We've been asked to operate on a locally added target, so its 4122251881Speter * implicit mergeinfo is empty. */ 4123251881Speter *implicit_mergeinfo = apr_hash_make(result_pool); 4124251881Speter } 4125251881Speter else if (target->rev <= end) 4126251881Speter { 4127251881Speter /* We're asking about a range outside our natural history 4128251881Speter altogether. That means our implicit mergeinfo is empty. */ 4129251881Speter *implicit_mergeinfo = apr_hash_make(result_pool); 4130251881Speter } 4131251881Speter else 4132251881Speter { 4133251881Speter /* Fetch so-called "implicit mergeinfo" (that is, natural 4134251881Speter history). */ 4135251881Speter 4136251881Speter /* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future. 4137251881Speter TARGET_ABSPATH might not even exist, and even if it does the 4138251881Speter working copy is *at* TARGET_REV so its implicit history ends 4139251881Speter at TARGET_REV! */ 4140251881Speter if (target->rev < start) 4141251881Speter start = target->rev; 4142251881Speter 4143251881Speter /* Fetch the implicit mergeinfo. */ 4144251881Speter SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo, 4145251881Speter NULL, 4146251881Speter target, start, end, 4147251881Speter ra_session, ctx, 4148251881Speter result_pool)); 4149251881Speter } 4150251881Speter } /*if (implicit_mergeinfo) */ 4151251881Speter 4152251881Speter return SVN_NO_ERROR; 4153251881Speter} 4154251881Speter 4155251881Speter/* Helper for ensure_implicit_mergeinfo(). 4156251881Speter 4157251881Speter PARENT, CHILD, REVISION1, REVISION2 and CTX 4158251881Speter are all cascaded from the arguments of the same names in 4159251881Speter ensure_implicit_mergeinfo(). PARENT and CHILD must both exist, i.e. 4160251881Speter this function should never be called where CHILD is the merge target. 4161251881Speter 4162251881Speter If PARENT->IMPLICIT_MERGEINFO is NULL, obtain it from the server. 4163251881Speter 4164251881Speter Set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from 4165251881Speter PARENT->IMPLICIT_MERGEINFO. CHILD->IMPLICIT_MERGEINFO is allocated 4166251881Speter in RESULT_POOL. 4167251881Speter 4168251881Speter RA_SESSION is an RA session open to the repository that contains CHILD. 4169251881Speter It may be temporarily reparented by this function. 4170251881Speter */ 4171251881Speterstatic svn_error_t * 4172251881Speterinherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t *parent, 4173251881Speter svn_client__merge_path_t *child, 4174251881Speter svn_revnum_t revision1, 4175251881Speter svn_revnum_t revision2, 4176251881Speter svn_ra_session_t *ra_session, 4177251881Speter svn_client_ctx_t *ctx, 4178251881Speter apr_pool_t *result_pool, 4179251881Speter apr_pool_t *scratch_pool) 4180251881Speter{ 4181251881Speter const char *path_diff; 4182251881Speter 4183251881Speter /* This only works on subtrees! */ 4184251881Speter SVN_ERR_ASSERT(parent); 4185251881Speter SVN_ERR_ASSERT(child); 4186251881Speter 4187251881Speter /* While PARENT must exist, it is possible we've deferred 4188251881Speter getting its implicit mergeinfo. If so get it now. */ 4189251881Speter if (!parent->implicit_mergeinfo) 4190251881Speter SVN_ERR(get_full_mergeinfo(NULL, &(parent->implicit_mergeinfo), 4191251881Speter NULL, svn_mergeinfo_inherited, 4192251881Speter ra_session, child->abspath, 4193251881Speter MAX(revision1, revision2), 4194251881Speter MIN(revision1, revision2), 4195251881Speter ctx, result_pool, scratch_pool)); 4196251881Speter 4197251881Speter /* Let CHILD inherit PARENT's implicit mergeinfo. */ 4198251881Speter 4199251881Speter path_diff = svn_dirent_is_child(parent->abspath, child->abspath, 4200251881Speter scratch_pool); 4201251881Speter /* PARENT->PATH better be an ancestor of CHILD->ABSPATH! */ 4202251881Speter SVN_ERR_ASSERT(path_diff); 4203251881Speter 4204251881Speter SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo( 4205251881Speter &child->implicit_mergeinfo, parent->implicit_mergeinfo, 4206251881Speter path_diff, result_pool, scratch_pool)); 4207251881Speter child->implicit_mergeinfo = svn_mergeinfo_dup(child->implicit_mergeinfo, 4208251881Speter result_pool); 4209251881Speter return SVN_NO_ERROR; 4210251881Speter} 4211251881Speter 4212251881Speter/* Helper of filter_merged_revisions(). 4213251881Speter 4214251881Speter If we have deferred obtaining CHILD->IMPLICIT_MERGEINFO, then get 4215251881Speter it now, allocating it in RESULT_POOL. If CHILD_INHERITS_PARENT is true 4216251881Speter then set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from 4217251881Speter PARENT->IMPLICIT_MERGEINFO, otherwise contact the repository. Use 4218251881Speter SCRATCH_POOL for all temporary allocations. 4219251881Speter 4220251881Speter RA_SESSION is an RA session open to the repository that contains CHILD. 4221251881Speter It may be temporarily reparented by this function. 4222251881Speter 4223251881Speter PARENT, CHILD, REVISION1, REVISION2 and 4224251881Speter CTX are all cascaded from the arguments of the same name in 4225251881Speter filter_merged_revisions() and the same conditions for that function 4226251881Speter hold here. */ 4227251881Speterstatic svn_error_t * 4228251881Speterensure_implicit_mergeinfo(svn_client__merge_path_t *parent, 4229251881Speter svn_client__merge_path_t *child, 4230251881Speter svn_boolean_t child_inherits_parent, 4231251881Speter svn_revnum_t revision1, 4232251881Speter svn_revnum_t revision2, 4233251881Speter svn_ra_session_t *ra_session, 4234251881Speter svn_client_ctx_t *ctx, 4235251881Speter apr_pool_t *result_pool, 4236251881Speter apr_pool_t *scratch_pool) 4237251881Speter{ 4238251881Speter /* If we haven't already found CHILD->IMPLICIT_MERGEINFO then 4239251881Speter contact the server to get it. */ 4240251881Speter 4241251881Speter if (child->implicit_mergeinfo) 4242251881Speter return SVN_NO_ERROR; 4243251881Speter 4244251881Speter if (child_inherits_parent) 4245251881Speter SVN_ERR(inherit_implicit_mergeinfo_from_parent(parent, 4246251881Speter child, 4247251881Speter revision1, 4248251881Speter revision2, 4249251881Speter ra_session, 4250251881Speter ctx, 4251251881Speter result_pool, 4252251881Speter scratch_pool)); 4253251881Speter else 4254251881Speter SVN_ERR(get_full_mergeinfo(NULL, 4255251881Speter &(child->implicit_mergeinfo), 4256251881Speter NULL, svn_mergeinfo_inherited, 4257251881Speter ra_session, child->abspath, 4258251881Speter MAX(revision1, revision2), 4259251881Speter MIN(revision1, revision2), 4260251881Speter ctx, result_pool, scratch_pool)); 4261251881Speter 4262251881Speter return SVN_NO_ERROR; 4263251881Speter} 4264251881Speter 4265251881Speter/* Helper for calculate_remaining_ranges(). 4266251881Speter 4267251881Speter Initialize CHILD->REMAINING_RANGES to a rangelist representing the 4268251881Speter requested merge of REVISION1:REVISION2 from MERGEINFO_PATH to 4269251881Speter CHILD->ABSPATH. 4270251881Speter 4271251881Speter For forward merges remove any ranges from CHILD->REMAINING_RANGES that 4272251881Speter have already been merged to CHILD->ABSPATH per TARGET_MERGEINFO or 4273251881Speter CHILD->IMPLICIT_MERGEINFO. For reverse merges remove any ranges from 4274251881Speter CHILD->REMAINING_RANGES that have not already been merged to CHILD->ABSPATH 4275251881Speter per TARGET_MERGEINFO or CHILD->IMPLICIT_MERGEINFO. If we have deferred 4276251881Speter obtaining CHILD->IMPLICIT_MERGEINFO and it is necessary to use it for 4277251881Speter these calculations, then get it from the server, allocating it in 4278251881Speter RESULT_POOL. 4279251881Speter 4280251881Speter CHILD represents a working copy path which is the merge target or one of 4281251881Speter the target's subtrees. If not NULL, PARENT is CHILD's nearest path-wise 4282251881Speter ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. 4283251881Speter 4284251881Speter If the function needs to consider CHILD->IMPLICIT_MERGEINFO and 4285251881Speter CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the 4286251881Speter mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO. Otherwise contact 4287251881Speter the repository for CHILD->IMPLICIT_MERGEINFO. 4288251881Speter 4289251881Speter NOTE: If PARENT is present then this function must have previously been 4290251881Speter called for PARENT, i.e. if populate_remaining_ranges() is calling this 4291251881Speter function for a set of svn_client__merge_path_t* the calls must be made 4292251881Speter in depth-first order. 4293251881Speter 4294251881Speter MERGEINFO_PATH is the merge source relative to the repository root. 4295251881Speter 4296251881Speter REVISION1 and REVISION2 describe the merge range requested from 4297251881Speter MERGEINFO_PATH. 4298251881Speter 4299251881Speter TARGET_RANGELIST is the portion of CHILD->ABSPATH's explicit or inherited 4300251881Speter mergeinfo that intersects with the merge history described by 4301251881Speter MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2. TARGET_RANGELIST 4302251881Speter should be NULL if there is no explicit or inherited mergeinfo on 4303251881Speter CHILD->ABSPATH or an empty list if CHILD->ABSPATH has empty mergeinfo or 4304251881Speter explicit mergeinfo that exclusively describes non-intersecting history 4305251881Speter with MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2. 4306251881Speter 4307251881Speter SCRATCH_POOL is used for all temporary allocations. 4308251881Speter 4309251881Speter NOTE: This should only be called when honoring mergeinfo. 4310251881Speter 4311251881Speter NOTE: Like calculate_remaining_ranges() if PARENT is present then this 4312251881Speter function must have previously been called for PARENT. 4313251881Speter*/ 4314251881Speterstatic svn_error_t * 4315251881Speterfilter_merged_revisions(svn_client__merge_path_t *parent, 4316251881Speter svn_client__merge_path_t *child, 4317251881Speter const char *mergeinfo_path, 4318251881Speter svn_rangelist_t *target_rangelist, 4319251881Speter svn_revnum_t revision1, 4320251881Speter svn_revnum_t revision2, 4321251881Speter svn_boolean_t child_inherits_implicit, 4322251881Speter svn_ra_session_t *ra_session, 4323251881Speter svn_client_ctx_t *ctx, 4324251881Speter apr_pool_t *result_pool, 4325251881Speter apr_pool_t *scratch_pool) 4326251881Speter{ 4327251881Speter svn_rangelist_t *requested_rangelist, 4328251881Speter *target_implicit_rangelist, *explicit_rangelist; 4329251881Speter 4330251881Speter /* Convert REVISION1 and REVISION2 to a rangelist. 4331251881Speter 4332251881Speter Note: Talking about a requested merge range's inheritability 4333251881Speter doesn't make much sense, but as we are using svn_merge_range_t 4334251881Speter to describe it we need to pick *something*. Since all the 4335251881Speter rangelist manipulations in this function either don't consider 4336251881Speter inheritance by default or we are requesting that they don't (i.e. 4337251881Speter svn_rangelist_remove and svn_rangelist_intersect) then we could 4338251881Speter set the inheritability as FALSE, it won't matter either way. */ 4339251881Speter requested_rangelist = svn_rangelist__initialize(revision1, revision2, 4340251881Speter TRUE, scratch_pool); 4341251881Speter 4342251881Speter /* Now filter out revisions that have already been merged to CHILD. */ 4343251881Speter 4344251881Speter if (revision1 > revision2) /* This is a reverse merge. */ 4345251881Speter { 4346251881Speter svn_rangelist_t *added_rangelist, *deleted_rangelist; 4347251881Speter 4348251881Speter /* The revert range and will need to be reversed for 4349251881Speter our svn_rangelist_* APIs to work properly. */ 4350251881Speter SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool)); 4351251881Speter 4352251881Speter /* Set EXPLICIT_RANGELIST to the list of source-range revs that are 4353251881Speter already recorded as merged to target. */ 4354251881Speter if (target_rangelist) 4355251881Speter { 4356251881Speter /* Return the intersection of the revs which are both already 4357251881Speter represented by CHILD's explicit or inherited mergeinfo. 4358251881Speter 4359251881Speter We don't consider inheritance when determining intersecting 4360251881Speter ranges. If we *did* consider inheritance, then our calculation 4361251881Speter would be wrong. For example, if the CHILD->REMAINING_RANGES is 4362251881Speter 5:3 and TARGET_RANGELIST is r5* (non-inheritable) then the 4363251881Speter intersection would be r4. And that would be wrong as we clearly 4364251881Speter want to reverse merge both r4 and r5 in this case. Ignoring the 4365251881Speter ranges' inheritance results in an intersection of r4-5. 4366251881Speter 4367251881Speter You might be wondering about CHILD's children, doesn't the above 4368251881Speter imply that we will reverse merge r4-5 from them? Nope, this is 4369251881Speter safe to do because any path whose parent has non-inheritable 4370251881Speter ranges is always considered a subtree with differing mergeinfo 4371251881Speter even if that path has no explicit mergeinfo prior to the 4372251881Speter merge -- See condition 3 in the doc string for 4373251881Speter merge.c:get_mergeinfo_paths(). */ 4374251881Speter SVN_ERR(svn_rangelist_intersect(&explicit_rangelist, 4375251881Speter target_rangelist, 4376251881Speter requested_rangelist, 4377251881Speter FALSE, scratch_pool)); 4378251881Speter } 4379251881Speter else 4380251881Speter { 4381251881Speter explicit_rangelist = 4382251881Speter apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *)); 4383251881Speter } 4384251881Speter 4385251881Speter /* Was any part of the requested reverse merge not accounted for in 4386251881Speter CHILD's explicit or inherited mergeinfo? */ 4387251881Speter SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist, 4388251881Speter requested_rangelist, explicit_rangelist, 4389251881Speter FALSE, scratch_pool)); 4390251881Speter 4391251881Speter if (deleted_rangelist->nelts == 0) 4392251881Speter { 4393251881Speter /* The whole of REVISION1:REVISION2 was represented in CHILD's 4394251881Speter explicit/inherited mergeinfo, allocate CHILD's remaining 4395251881Speter ranges in POOL and then we are done. */ 4396251881Speter SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool)); 4397251881Speter child->remaining_ranges = svn_rangelist_dup(requested_rangelist, 4398251881Speter result_pool); 4399251881Speter } 4400251881Speter else /* We need to check CHILD's implicit mergeinfo. */ 4401251881Speter { 4402251881Speter svn_rangelist_t *implicit_rangelist; 4403251881Speter 4404251881Speter SVN_ERR(ensure_implicit_mergeinfo(parent, 4405251881Speter child, 4406251881Speter child_inherits_implicit, 4407251881Speter revision1, 4408251881Speter revision2, 4409251881Speter ra_session, 4410251881Speter ctx, 4411251881Speter result_pool, 4412251881Speter scratch_pool)); 4413251881Speter 4414251881Speter target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo, 4415251881Speter mergeinfo_path); 4416251881Speter 4417251881Speter if (target_implicit_rangelist) 4418251881Speter SVN_ERR(svn_rangelist_intersect(&implicit_rangelist, 4419251881Speter target_implicit_rangelist, 4420251881Speter requested_rangelist, 4421251881Speter FALSE, scratch_pool)); 4422251881Speter else 4423251881Speter implicit_rangelist = apr_array_make(scratch_pool, 0, 4424251881Speter sizeof(svn_merge_range_t *)); 4425251881Speter 4426251881Speter SVN_ERR(svn_rangelist_merge2(implicit_rangelist, 4427251881Speter explicit_rangelist, scratch_pool, 4428251881Speter scratch_pool)); 4429251881Speter SVN_ERR(svn_rangelist_reverse(implicit_rangelist, scratch_pool)); 4430251881Speter child->remaining_ranges = svn_rangelist_dup(implicit_rangelist, 4431251881Speter result_pool); 4432251881Speter } 4433251881Speter } 4434251881Speter else /* This is a forward merge */ 4435251881Speter { 4436251881Speter /* Set EXPLICIT_RANGELIST to the list of source-range revs that are 4437251881Speter NOT already recorded as merged to target. */ 4438251881Speter if (target_rangelist) 4439251881Speter { 4440251881Speter /* See earlier comment preceding svn_rangelist_intersect() for 4441251881Speter why we don't consider inheritance here. */ 4442251881Speter SVN_ERR(svn_rangelist_remove(&explicit_rangelist, 4443251881Speter target_rangelist, 4444251881Speter requested_rangelist, FALSE, 4445251881Speter scratch_pool)); 4446251881Speter } 4447251881Speter else 4448251881Speter { 4449251881Speter explicit_rangelist = svn_rangelist_dup(requested_rangelist, 4450251881Speter scratch_pool); 4451251881Speter } 4452251881Speter 4453251881Speter if (explicit_rangelist->nelts == 0) 4454251881Speter { 4455251881Speter child->remaining_ranges = 4456251881Speter apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *)); 4457251881Speter } 4458251881Speter else 4459251881Speter/* ### TODO: Which evil shall we choose? 4460251881Speter ### 4461251881Speter ### If we allow all forward-merges not already found in recorded 4462251881Speter ### mergeinfo, we destroy the ability to, say, merge the whole of a 4463251881Speter ### branch to the trunk while automatically ignoring the revisions 4464251881Speter ### common to both. That's bad. 4465251881Speter ### 4466251881Speter ### If we allow only forward-merges not found in either recorded 4467251881Speter ### mergeinfo or implicit mergeinfo (natural history), then the 4468251881Speter ### previous scenario works great, but we can't reverse-merge a 4469251881Speter ### previous change made to our line of history and then remake it 4470251881Speter ### (because the reverse-merge will leave no mergeinfo trace, and 4471251881Speter ### the remake-it attempt will still find the original change in 4472251881Speter ### natural mergeinfo. But you know, that we happen to use 'merge' 4473251881Speter ### for revision undoing is somewhat unnatural anyway, so I'm 4474251881Speter ### finding myself having little interest in caring too much about 4475251881Speter ### this. That said, if we had a way of storing reverse merge 4476251881Speter ### ranges, we'd be in good shape either way. 4477251881Speter*/ 4478251881Speter#ifdef SVN_MERGE__ALLOW_ALL_FORWARD_MERGES_FROM_SELF 4479251881Speter { 4480251881Speter /* ### Don't consider implicit mergeinfo. */ 4481251881Speter child->remaining_ranges = svn_rangelist_dup(explicit_rangelist, 4482251881Speter pool); 4483251881Speter } 4484251881Speter#else 4485251881Speter { 4486251881Speter /* Based on CHILD's TARGET_MERGEINFO there are ranges to merge. 4487251881Speter Check CHILD's implicit mergeinfo to see if these remaining 4488251881Speter ranges are represented there. */ 4489251881Speter SVN_ERR(ensure_implicit_mergeinfo(parent, 4490251881Speter child, 4491251881Speter child_inherits_implicit, 4492251881Speter revision1, 4493251881Speter revision2, 4494251881Speter ra_session, 4495251881Speter ctx, 4496251881Speter result_pool, 4497251881Speter scratch_pool)); 4498251881Speter 4499251881Speter target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo, 4500251881Speter mergeinfo_path); 4501251881Speter if (target_implicit_rangelist) 4502251881Speter SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges), 4503251881Speter target_implicit_rangelist, 4504251881Speter explicit_rangelist, 4505251881Speter FALSE, result_pool)); 4506251881Speter else 4507251881Speter child->remaining_ranges = svn_rangelist_dup(explicit_rangelist, 4508251881Speter result_pool); 4509251881Speter } 4510251881Speter#endif 4511251881Speter } 4512251881Speter 4513251881Speter return SVN_NO_ERROR; 4514251881Speter} 4515251881Speter 4516251881Speter/* Helper for do_file_merge and do_directory_merge (by way of 4517251881Speter populate_remaining_ranges() for the latter). 4518251881Speter 4519251881Speter Determine what portions of SOURCE have already 4520251881Speter been merged to CHILD->ABSPATH and populate CHILD->REMAINING_RANGES with 4521251881Speter the ranges that still need merging. 4522251881Speter 4523251881Speter SOURCE and CTX are all cascaded from the caller's arguments of the same 4524251881Speter names. Note that this means SOURCE adheres to the requirements noted in 4525251881Speter `MERGEINFO MERGE SOURCE NORMALIZATION'. 4526251881Speter 4527251881Speter CHILD represents a working copy path which is the merge target or one of 4528251881Speter the target's subtrees. If not NULL, PARENT is CHILD's nearest path-wise 4529251881Speter ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. TARGET_MERGEINFO is 4530251881Speter the working mergeinfo on CHILD. 4531251881Speter 4532251881Speter RA_SESSION is the session for the younger of SOURCE->loc1 and 4533251881Speter SOURCE->loc2. 4534251881Speter 4535251881Speter If the function needs to consider CHILD->IMPLICIT_MERGEINFO and 4536251881Speter CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the 4537251881Speter mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO. Otherwise contact 4538251881Speter the repository for CHILD->IMPLICIT_MERGEINFO. 4539251881Speter 4540251881Speter If not null, IMPLICIT_SRC_GAP is the gap, if any, in the natural history 4541251881Speter of SOURCE, see merge_cmd_baton_t.implicit_src_gap. 4542251881Speter 4543251881Speter SCRATCH_POOL is used for all temporary allocations. Changes to CHILD and 4544251881Speter PARENT are made in RESULT_POOL. 4545251881Speter 4546251881Speter NOTE: This should only be called when honoring mergeinfo. 4547251881Speter 4548251881Speter NOTE: If PARENT is present then this function must have previously been 4549251881Speter called for PARENT, i.e. if populate_remaining_ranges() is calling this 4550251881Speter function for a set of svn_client__merge_path_t* the calls must be made 4551251881Speter in depth-first order. 4552251881Speter 4553251881Speter NOTE: When performing reverse merges, return 4554251881Speter SVN_ERR_CLIENT_NOT_READY_TO_MERGE if both locations in SOURCE and 4555251881Speter CHILD->ABSPATH are all on the same line of history but CHILD->ABSPATH's 4556251881Speter base revision is older than the SOURCE->rev1:rev2 range, see comment re 4557251881Speter issue #2973 below. 4558251881Speter*/ 4559251881Speterstatic svn_error_t * 4560251881Spetercalculate_remaining_ranges(svn_client__merge_path_t *parent, 4561251881Speter svn_client__merge_path_t *child, 4562251881Speter const merge_source_t *source, 4563251881Speter svn_mergeinfo_t target_mergeinfo, 4564251881Speter const apr_array_header_t *implicit_src_gap, 4565251881Speter svn_boolean_t child_inherits_implicit, 4566251881Speter svn_ra_session_t *ra_session, 4567251881Speter svn_client_ctx_t *ctx, 4568251881Speter apr_pool_t *result_pool, 4569251881Speter apr_pool_t *scratch_pool) 4570251881Speter{ 4571251881Speter const svn_client__pathrev_t *primary_src 4572251881Speter = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1; 4573251881Speter const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src, 4574251881Speter scratch_pool); 4575251881Speter /* Intersection of TARGET_MERGEINFO and the merge history 4576251881Speter described by SOURCE. */ 4577251881Speter svn_rangelist_t *target_rangelist; 4578251881Speter svn_revnum_t child_base_revision; 4579251881Speter 4580251881Speter /* Since this function should only be called when honoring mergeinfo and 4581251881Speter * SOURCE adheres to the requirements noted in 'MERGEINFO MERGE SOURCE 4582251881Speter * NORMALIZATION', SOURCE must be 'ancestral'. */ 4583251881Speter SVN_ERR_ASSERT(source->ancestral); 4584251881Speter 4585251881Speter /* Determine which of the requested ranges to consider merging... */ 4586251881Speter 4587251881Speter /* Set TARGET_RANGELIST to the portion of TARGET_MERGEINFO that refers 4588251881Speter to SOURCE (excluding any gap in SOURCE): first get all ranges from 4589251881Speter TARGET_MERGEINFO that refer to the path of SOURCE, and then prune 4590251881Speter any ranges that lie in the gap in SOURCE. 4591251881Speter 4592251881Speter ### [JAF] In fact, that may still leave some ranges that lie entirely 4593251881Speter outside the range of SOURCE; it seems we don't care about that. */ 4594251881Speter if (target_mergeinfo) 4595251881Speter target_rangelist = svn_hash_gets(target_mergeinfo, mergeinfo_path); 4596251881Speter else 4597251881Speter target_rangelist = NULL; 4598251881Speter if (implicit_src_gap && target_rangelist) 4599251881Speter { 4600251881Speter /* Remove any mergeinfo referring to the 'gap' in SOURCE, as that 4601251881Speter mergeinfo doesn't really refer to SOURCE at all but instead 4602251881Speter refers to locations that are non-existent or on a different 4603251881Speter line of history. (Issue #3242.) */ 4604251881Speter SVN_ERR(svn_rangelist_remove(&target_rangelist, 4605251881Speter implicit_src_gap, target_rangelist, 4606251881Speter FALSE, result_pool)); 4607251881Speter } 4608251881Speter 4609251881Speter /* Initialize CHILD->REMAINING_RANGES and filter out revisions already 4610251881Speter merged (or, in the case of reverse merges, ranges not yet merged). */ 4611251881Speter SVN_ERR(filter_merged_revisions(parent, child, mergeinfo_path, 4612251881Speter target_rangelist, 4613251881Speter source->loc1->rev, source->loc2->rev, 4614251881Speter child_inherits_implicit, 4615251881Speter ra_session, ctx, result_pool, 4616251881Speter scratch_pool)); 4617251881Speter 4618251881Speter /* Issue #2973 -- from the continuing series of "Why, since the advent of 4619251881Speter merge tracking, allowing merges into mixed rev and locally modified 4620251881Speter working copies isn't simple and could be considered downright evil". 4621251881Speter 4622251881Speter If reverse merging a range to the WC path represented by CHILD, from 4623251881Speter that path's own history, where the path inherits no locally modified 4624251881Speter mergeinfo from its WC parents (i.e. there is no uncommitted merge to 4625251881Speter the WC), and the path's base revision is older than the range, then 4626251881Speter the merge will always be a no-op. This is because we only allow reverse 4627251881Speter merges of ranges in the path's explicit or natural mergeinfo and a 4628251881Speter reverse merge from the path's future history obviously isn't going to be 4629251881Speter in either, hence the no-op. 4630251881Speter 4631251881Speter The problem is two-fold. First, in a mixed rev WC, the change we 4632251881Speter want to revert might actually be to some child of the target path 4633251881Speter which is at a younger base revision. Sure, we can merge directly 4634251881Speter to that child or update the WC or even use --ignore-ancestry and then 4635251881Speter successfully run the reverse merge, but that gets to the second 4636251881Speter problem: Those courses of action are not very obvious. Before 1.5 if 4637251881Speter a user committed a change that didn't touch the commit target, then 4638251881Speter immediately decided to revert that change via a reverse merge it would 4639251881Speter just DTRT. But with the advent of merge tracking the user gets a no-op. 4640251881Speter 4641251881Speter So in the name of user friendliness, return an error suggesting a helpful 4642251881Speter course of action. 4643251881Speter */ 4644251881Speter SVN_ERR(svn_wc__node_get_base(NULL, &child_base_revision, 4645251881Speter NULL, NULL, NULL, NULL, 4646251881Speter ctx->wc_ctx, child->abspath, 4647251881Speter TRUE /* ignore_enoent */, 4648251881Speter FALSE /* show_hidden */, 4649251881Speter scratch_pool, scratch_pool)); 4650251881Speter /* If CHILD has no base revision then it hasn't been committed yet, so it 4651251881Speter can't have any "future" history. */ 4652251881Speter if (SVN_IS_VALID_REVNUM(child_base_revision) 4653251881Speter && ((child->remaining_ranges)->nelts == 0) /* Inoperative merge */ 4654251881Speter && (source->loc2->rev < source->loc1->rev) /* Reverse merge */ 4655251881Speter && (child_base_revision <= source->loc2->rev)) /* From CHILD's future */ 4656251881Speter { 4657251881Speter /* Hmmm, an inoperative reverse merge from the "future". If it is 4658251881Speter from our own future return a helpful error. */ 4659251881Speter svn_error_t *err; 4660251881Speter svn_client__pathrev_t *start_loc; 4661251881Speter 4662251881Speter err = svn_client__repos_location(&start_loc, 4663251881Speter ra_session, 4664251881Speter source->loc1, 4665251881Speter child_base_revision, 4666251881Speter ctx, scratch_pool, scratch_pool); 4667251881Speter if (err) 4668251881Speter { 4669251881Speter if (err->apr_err == SVN_ERR_FS_NOT_FOUND 4670251881Speter || err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES) 4671251881Speter svn_error_clear(err); 4672251881Speter else 4673251881Speter return svn_error_trace(err); 4674251881Speter } 4675251881Speter else 4676251881Speter { 4677251881Speter const char *url; 4678251881Speter 4679251881Speter SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, child->abspath, 4680251881Speter scratch_pool, scratch_pool)); 4681251881Speter if (strcmp(start_loc->url, url) == 0) 4682251881Speter return svn_error_create(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL, 4683251881Speter _("Cannot reverse-merge a range from a " 4684251881Speter "path's own future history; try " 4685251881Speter "updating first")); 4686251881Speter } 4687251881Speter } 4688251881Speter 4689251881Speter return SVN_NO_ERROR; 4690251881Speter} 4691251881Speter 4692251881Speter/* Helper for populate_remaining_ranges(). 4693251881Speter 4694251881Speter SOURCE is cascaded from the arguments of the same name in 4695251881Speter populate_remaining_ranges(). 4696251881Speter 4697251881Speter Note: The following comments assume a forward merge, i.e. 4698251881Speter SOURCE->loc1->rev < SOURCE->loc2->rev. If this is a reverse merge then 4699251881Speter all the following comments still apply, but with SOURCE->loc1 switched 4700251881Speter with SOURCE->loc2. 4701251881Speter 4702251881Speter Like populate_remaining_ranges(), SOURCE must adhere to the restrictions 4703251881Speter documented in 'MERGEINFO MERGE SOURCE NORMALIZATION'. These restrictions 4704251881Speter allow for a *single* gap in SOURCE, GAP_REV1:GAP_REV2 exclusive:inclusive 4705251881Speter (where SOURCE->loc1->rev == GAP_REV1 <= GAP_REV2 < SOURCE->loc2->rev), 4706251881Speter if SOURCE->loc2->url@(GAP_REV2+1) was copied from SOURCE->loc1. If such 4707251881Speter a gap exists, set *GAP_START and *GAP_END to the starting and ending 4708251881Speter revisions of the gap. Otherwise set both to SVN_INVALID_REVNUM. 4709251881Speter 4710251881Speter For example, if the natural history of URL@2:URL@9 is 'trunk/:2,7-9' this 4711251881Speter would indicate that trunk@7 was copied from trunk@2. This function would 4712251881Speter return GAP_START:GAP_END of 2:6 in this case. Note that a path 'trunk' 4713251881Speter might exist at r3-6, but it would not be on the same line of history as 4714251881Speter trunk@9. 4715251881Speter 4716251881Speter ### GAP_START is basically redundant, as (if there is a gap at all) it is 4717251881Speter necessarily the older revision of SOURCE. 4718251881Speter 4719251881Speter RA_SESSION is an open RA session to the repository in which SOURCE lives. 4720251881Speter*/ 4721251881Speterstatic svn_error_t * 4722251881Speterfind_gaps_in_merge_source_history(svn_revnum_t *gap_start, 4723251881Speter svn_revnum_t *gap_end, 4724251881Speter const merge_source_t *source, 4725251881Speter svn_ra_session_t *ra_session, 4726251881Speter svn_client_ctx_t *ctx, 4727251881Speter apr_pool_t *scratch_pool) 4728251881Speter{ 4729251881Speter svn_mergeinfo_t implicit_src_mergeinfo; 4730251881Speter svn_revnum_t old_rev = MIN(source->loc1->rev, source->loc2->rev); 4731251881Speter const svn_client__pathrev_t *primary_src 4732251881Speter = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1; 4733251881Speter const char *merge_src_fspath = svn_client__pathrev_fspath(primary_src, 4734251881Speter scratch_pool); 4735251881Speter svn_rangelist_t *rangelist; 4736251881Speter 4737251881Speter SVN_ERR_ASSERT(source->ancestral); 4738251881Speter 4739251881Speter /* Start by assuming there is no gap. */ 4740251881Speter *gap_start = *gap_end = SVN_INVALID_REVNUM; 4741251881Speter 4742251881Speter /* Easy out: There can't be a gap between adjacent revisions. */ 4743251881Speter if (abs(source->loc1->rev - source->loc2->rev) == 1) 4744251881Speter return SVN_NO_ERROR; 4745251881Speter 4746251881Speter /* Get SOURCE as mergeinfo. */ 4747251881Speter SVN_ERR(svn_client__get_history_as_mergeinfo(&implicit_src_mergeinfo, NULL, 4748251881Speter primary_src, 4749251881Speter primary_src->rev, old_rev, 4750251881Speter ra_session, 4751251881Speter ctx, scratch_pool)); 4752251881Speter 4753251881Speter rangelist = svn_hash_gets(implicit_src_mergeinfo, merge_src_fspath); 4754251881Speter 4755251881Speter if (!rangelist) /* ### Can we ever not find a rangelist? */ 4756251881Speter return SVN_NO_ERROR; 4757251881Speter 4758251881Speter /* A gap in natural history can result from either a copy or 4759251881Speter a rename. If from a copy then history as mergeinfo will look 4760251881Speter something like this: 4761251881Speter 4762251881Speter '/trunk:X,Y-Z' 4763251881Speter 4764251881Speter If from a rename it will look like this: 4765251881Speter 4766251881Speter '/trunk_old_name:X' 4767251881Speter '/trunk_new_name:Y-Z' 4768251881Speter 4769251881Speter In both cases the gap, if it exists, is M-N, where M = X + 1 and 4770251881Speter N = Y - 1. 4771251881Speter 4772251881Speter Note that per the rules of 'MERGEINFO MERGE SOURCE NORMALIZATION' we 4773251881Speter should never have multiple gaps, e.g. if we see anything like the 4774251881Speter following then something is quite wrong: 4775251881Speter 4776251881Speter '/trunk_old_name:A,B-C' 4777251881Speter '/trunk_new_name:D-E' 4778251881Speter */ 4779251881Speter 4780251881Speter if (rangelist->nelts > 1) /* Copy */ 4781251881Speter { 4782251881Speter const svn_merge_range_t *gap; 4783251881Speter /* As mentioned above, multiple gaps *shouldn't* be possible. */ 4784251881Speter SVN_ERR_ASSERT(apr_hash_count(implicit_src_mergeinfo) == 1); 4785251881Speter 4786251881Speter gap = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1, 4787251881Speter const svn_merge_range_t *); 4788251881Speter 4789251881Speter *gap_start = MIN(source->loc1->rev, source->loc2->rev); 4790251881Speter *gap_end = gap->start; 4791251881Speter 4792251881Speter /* ### Issue #4132: 4793251881Speter ### This assertion triggers in merge_tests.py svnmucc_abuse_1() 4794251881Speter ### when a node is replaced by an older copy of itself. 4795251881Speter 4796251881Speter BH: I think we should review this and the 'rename' case to find 4797251881Speter out which behavior we really want, and if we can really 4798251881Speter determine what happened this way. */ 4799251881Speter SVN_ERR_ASSERT(*gap_start < *gap_end); 4800251881Speter } 4801251881Speter else if (apr_hash_count(implicit_src_mergeinfo) > 1) /* Rename */ 4802251881Speter { 4803251881Speter svn_rangelist_t *requested_rangelist = 4804251881Speter svn_rangelist__initialize(MIN(source->loc1->rev, source->loc2->rev), 4805251881Speter MAX(source->loc1->rev, source->loc2->rev), 4806251881Speter TRUE, scratch_pool); 4807251881Speter svn_rangelist_t *implicit_rangelist = 4808251881Speter apr_array_make(scratch_pool, 2, sizeof(svn_merge_range_t *)); 4809251881Speter svn_rangelist_t *gap_rangelist; 4810251881Speter 4811251881Speter SVN_ERR(svn_rangelist__merge_many(implicit_rangelist, 4812251881Speter implicit_src_mergeinfo, 4813251881Speter scratch_pool, scratch_pool)); 4814251881Speter SVN_ERR(svn_rangelist_remove(&gap_rangelist, implicit_rangelist, 4815251881Speter requested_rangelist, FALSE, 4816251881Speter scratch_pool)); 4817251881Speter 4818251881Speter /* If there is anything left it is the gap. */ 4819251881Speter if (gap_rangelist->nelts) 4820251881Speter { 4821251881Speter svn_merge_range_t *gap_range = 4822251881Speter APR_ARRAY_IDX(gap_rangelist, 0, svn_merge_range_t *); 4823251881Speter 4824251881Speter *gap_start = gap_range->start; 4825251881Speter *gap_end = gap_range->end; 4826251881Speter } 4827251881Speter } 4828251881Speter 4829251881Speter SVN_ERR_ASSERT(*gap_start == MIN(source->loc1->rev, source->loc2->rev) 4830251881Speter || (*gap_start == SVN_INVALID_REVNUM 4831251881Speter && *gap_end == SVN_INVALID_REVNUM)); 4832251881Speter return SVN_NO_ERROR; 4833251881Speter} 4834251881Speter 4835251881Speter/* Helper for do_directory_merge(). 4836251881Speter 4837251881Speter For each (svn_client__merge_path_t *) child in CHILDREN_WITH_MERGEINFO, 4838251881Speter populate that child's 'remaining_ranges' list with (### ... what?), 4839251881Speter and populate that child's 'implicit_mergeinfo' with its implicit 4840251881Speter mergeinfo (natural history). CHILDREN_WITH_MERGEINFO is expected 4841251881Speter to be sorted in depth first order and each child must be processed in 4842251881Speter that order. The inheritability of all calculated ranges is TRUE. 4843251881Speter 4844251881Speter If mergeinfo is being honored (based on MERGE_B -- see HONOR_MERGEINFO() 4845251881Speter for how this is determined), this function will actually try to be 4846251881Speter intelligent about populating remaining_ranges list. Otherwise, it 4847251881Speter will claim that each child has a single remaining range, from 4848251881Speter SOURCE->rev1, to SOURCE->rev2. 4849251881Speter ### We also take the short-cut if doing record-only. Why? 4850251881Speter 4851251881Speter SCRATCH_POOL is used for all temporary allocations. Changes to 4852251881Speter CHILDREN_WITH_MERGEINFO are made in RESULT_POOL. 4853251881Speter 4854251881Speter Note that if SOURCE->rev1 > SOURCE->rev2, then each child's remaining_ranges 4855251881Speter member does not adhere to the API rules for rangelists described in 4856251881Speter svn_mergeinfo.h -- See svn_client__merge_path_t. 4857251881Speter 4858251881Speter See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements 4859251881Speter around SOURCE. 4860251881Speter*/ 4861251881Speterstatic svn_error_t * 4862251881Speterpopulate_remaining_ranges(apr_array_header_t *children_with_mergeinfo, 4863251881Speter const merge_source_t *source, 4864251881Speter svn_ra_session_t *ra_session, 4865251881Speter merge_cmd_baton_t *merge_b, 4866251881Speter apr_pool_t *result_pool, 4867251881Speter apr_pool_t *scratch_pool) 4868251881Speter{ 4869251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 4870251881Speter int i; 4871251881Speter svn_revnum_t gap_start, gap_end; 4872251881Speter 4873251881Speter /* If we aren't honoring mergeinfo or this is a --record-only merge, 4874251881Speter we'll make quick work of this by simply adding dummy SOURCE->rev1:rev2 4875251881Speter ranges for all children. */ 4876251881Speter if (! HONOR_MERGEINFO(merge_b) || merge_b->record_only) 4877251881Speter { 4878251881Speter for (i = 0; i < children_with_mergeinfo->nelts; i++) 4879251881Speter { 4880251881Speter svn_client__merge_path_t *child = 4881251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, 4882251881Speter svn_client__merge_path_t *); 4883251881Speter 4884251881Speter svn_pool_clear(iterpool); 4885251881Speter 4886251881Speter /* Issue #3646 'record-only merges create self-referential 4887251881Speter mergeinfo'. Get the merge target's implicit mergeinfo (natural 4888251881Speter history). We'll use it later to avoid setting self-referential 4889251881Speter mergeinfo -- see filter_natural_history_from_mergeinfo(). */ 4890251881Speter if (i == 0) /* First item is always the merge target. */ 4891251881Speter { 4892251881Speter SVN_ERR(get_full_mergeinfo(NULL, /* child->pre_merge_mergeinfo */ 4893251881Speter &(child->implicit_mergeinfo), 4894251881Speter NULL, /* child->inherited_mergeinfo */ 4895251881Speter svn_mergeinfo_inherited, ra_session, 4896251881Speter child->abspath, 4897251881Speter MAX(source->loc1->rev, 4898251881Speter source->loc2->rev), 4899251881Speter MIN(source->loc1->rev, 4900251881Speter source->loc2->rev), 4901251881Speter merge_b->ctx, result_pool, 4902251881Speter iterpool)); 4903251881Speter } 4904251881Speter else 4905251881Speter { 4906251881Speter /* Issue #3443 - Subtrees of the merge target can inherit 4907251881Speter their parent's implicit mergeinfo in most cases. */ 4908251881Speter svn_client__merge_path_t *parent 4909251881Speter = find_nearest_ancestor(children_with_mergeinfo, 4910251881Speter FALSE, child->abspath); 4911251881Speter svn_boolean_t child_inherits_implicit; 4912251881Speter 4913251881Speter /* If CHILD is a subtree then its parent must be in 4914251881Speter CHILDREN_WITH_MERGEINFO, see the global comment 4915251881Speter 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */ 4916251881Speter SVN_ERR_ASSERT(parent); 4917251881Speter 4918251881Speter child_inherits_implicit = (parent && !child->switched); 4919251881Speter SVN_ERR(ensure_implicit_mergeinfo(parent, child, 4920251881Speter child_inherits_implicit, 4921251881Speter source->loc1->rev, 4922251881Speter source->loc2->rev, 4923251881Speter ra_session, merge_b->ctx, 4924251881Speter result_pool, iterpool)); 4925251881Speter } 4926251881Speter 4927251881Speter child->remaining_ranges = svn_rangelist__initialize(source->loc1->rev, 4928251881Speter source->loc2->rev, 4929251881Speter TRUE, 4930251881Speter result_pool); 4931251881Speter } 4932251881Speter svn_pool_destroy(iterpool); 4933251881Speter return SVN_NO_ERROR; 4934251881Speter } 4935251881Speter 4936251881Speter /* If, in the merge source's history, there was a copy from an older 4937251881Speter revision, then SOURCE->loc2->url won't exist at some range M:N, where 4938251881Speter SOURCE->loc1->rev < M < N < SOURCE->loc2->rev. The rules of 'MERGEINFO 4939251881Speter MERGE SOURCE NORMALIZATION' allow this, but we must ignore these gaps 4940251881Speter when calculating what ranges remain to be merged from SOURCE. If we 4941251881Speter don't and try to merge any part of SOURCE->loc2->url@M:N we would 4942251881Speter break the editor since no part of that actually exists. See 4943251881Speter http://svn.haxx.se/dev/archive-2008-11/0618.shtml. 4944251881Speter 4945251881Speter Find the gaps in the merge target's history, if any. Eventually 4946251881Speter we will adjust CHILD->REMAINING_RANGES such that we don't describe 4947251881Speter non-existent paths to the editor. */ 4948251881Speter SVN_ERR(find_gaps_in_merge_source_history(&gap_start, &gap_end, 4949251881Speter source, 4950251881Speter ra_session, merge_b->ctx, 4951251881Speter iterpool)); 4952251881Speter 4953251881Speter /* Stash any gap in the merge command baton, we'll need it later when 4954251881Speter recording mergeinfo describing this merge. */ 4955251881Speter if (SVN_IS_VALID_REVNUM(gap_start) && SVN_IS_VALID_REVNUM(gap_end)) 4956251881Speter merge_b->implicit_src_gap = svn_rangelist__initialize(gap_start, gap_end, 4957251881Speter TRUE, result_pool); 4958251881Speter 4959251881Speter for (i = 0; i < children_with_mergeinfo->nelts; i++) 4960251881Speter { 4961251881Speter svn_client__merge_path_t *child = 4962251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); 4963251881Speter const char *child_repos_path 4964251881Speter = svn_dirent_skip_ancestor(merge_b->target->abspath, child->abspath); 4965251881Speter merge_source_t child_source; 4966251881Speter svn_client__merge_path_t *parent = NULL; 4967251881Speter svn_boolean_t child_inherits_implicit; 4968251881Speter 4969251881Speter svn_pool_clear(iterpool); 4970251881Speter 4971251881Speter /* If the path is absent don't do subtree merge either. */ 4972251881Speter SVN_ERR_ASSERT(child); 4973251881Speter if (child->absent) 4974251881Speter continue; 4975251881Speter 4976251881Speter SVN_ERR_ASSERT(child_repos_path != NULL); 4977251881Speter child_source.loc1 = svn_client__pathrev_join_relpath( 4978251881Speter source->loc1, child_repos_path, iterpool); 4979251881Speter child_source.loc2 = svn_client__pathrev_join_relpath( 4980251881Speter source->loc2, child_repos_path, iterpool); 4981251881Speter /* ### Is the child 'ancestral' over the same revision range? It's 4982251881Speter * not necessarily true that a child is 'ancestral' if the parent is, 4983251881Speter * nor that it's not if the parent is not. However, here we claim 4984251881Speter * that it is. Before we had this 'ancestral' field that we need to 4985251881Speter * set explicitly, the claim was implicit. Either way, the impact is 4986251881Speter * that we might pass calculate_remaining_ranges() a source that is 4987251881Speter * not in fact 'ancestral' (despite its 'ancestral' field being true), 4988251881Speter * contrary to its doc-string. */ 4989251881Speter child_source.ancestral = source->ancestral; 4990251881Speter 4991251881Speter /* Get the explicit/inherited mergeinfo for CHILD. If CHILD is the 4992251881Speter merge target then also get its implicit mergeinfo. Otherwise defer 4993251881Speter this until we know it is absolutely necessary, since it requires an 4994251881Speter expensive round trip communication with the server. */ 4995251881Speter SVN_ERR(get_full_mergeinfo( 4996251881Speter child->pre_merge_mergeinfo ? NULL : &(child->pre_merge_mergeinfo), 4997251881Speter /* Get implicit only for merge target. */ 4998251881Speter (i == 0) ? &(child->implicit_mergeinfo) : NULL, 4999251881Speter &(child->inherited_mergeinfo), 5000251881Speter svn_mergeinfo_inherited, ra_session, 5001251881Speter child->abspath, 5002251881Speter MAX(source->loc1->rev, source->loc2->rev), 5003251881Speter MIN(source->loc1->rev, source->loc2->rev), 5004251881Speter merge_b->ctx, result_pool, iterpool)); 5005251881Speter 5006251881Speter /* If CHILD isn't the merge target find its parent. */ 5007251881Speter if (i > 0) 5008251881Speter { 5009251881Speter parent = find_nearest_ancestor(children_with_mergeinfo, 5010251881Speter FALSE, child->abspath); 5011251881Speter /* If CHILD is a subtree then its parent must be in 5012251881Speter CHILDREN_WITH_MERGEINFO, see the global comment 5013251881Speter 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */ 5014251881Speter SVN_ERR_ASSERT(parent); 5015251881Speter } 5016251881Speter 5017251881Speter /* Issue #3443 - Can CHILD inherit PARENT's implicit mergeinfo, saving 5018251881Speter us from having to ask the repos? The only time we can't do this is if 5019251881Speter CHILD is the merge target and so there is no PARENT to inherit from 5020251881Speter or if CHILD is the root of a switched subtree, in which case PARENT 5021251881Speter exists but is not CHILD's repository parent. */ 5022251881Speter child_inherits_implicit = (parent && !child->switched); 5023251881Speter 5024251881Speter SVN_ERR(calculate_remaining_ranges(parent, child, 5025251881Speter &child_source, 5026251881Speter child->pre_merge_mergeinfo, 5027251881Speter merge_b->implicit_src_gap, 5028251881Speter child_inherits_implicit, 5029251881Speter ra_session, 5030251881Speter merge_b->ctx, result_pool, 5031251881Speter iterpool)); 5032251881Speter 5033251881Speter /* Deal with any gap in SOURCE's natural history. 5034251881Speter 5035251881Speter If the gap is a proper subset of CHILD->REMAINING_RANGES then we can 5036251881Speter safely ignore it since we won't describe this path/rev pair. 5037251881Speter 5038251881Speter If the gap exactly matches or is a superset of a range in 5039251881Speter CHILD->REMAINING_RANGES then we must remove that range so we don't 5040251881Speter attempt to describe non-existent paths via the reporter, this will 5041251881Speter break the editor and our merge. 5042251881Speter 5043251881Speter If the gap adjoins or overlaps a range in CHILD->REMAINING_RANGES 5044251881Speter then we must *add* the gap so we span the missing revisions. */ 5045251881Speter if (child->remaining_ranges->nelts 5046251881Speter && merge_b->implicit_src_gap) 5047251881Speter { 5048251881Speter int j; 5049251881Speter svn_boolean_t proper_subset = FALSE; 5050251881Speter svn_boolean_t overlaps_or_adjoins = FALSE; 5051251881Speter 5052251881Speter /* If this is a reverse merge reorder CHILD->REMAINING_RANGES 5053251881Speter so it will work with the svn_rangelist_* APIs below. */ 5054251881Speter if (source->loc1->rev > source->loc2->rev) 5055251881Speter SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool)); 5056251881Speter 5057251881Speter for (j = 0; j < child->remaining_ranges->nelts; j++) 5058251881Speter { 5059251881Speter svn_merge_range_t *range 5060251881Speter = APR_ARRAY_IDX(child->remaining_ranges, j, svn_merge_range_t *); 5061251881Speter 5062251881Speter if ((range->start <= gap_start && gap_end < range->end) 5063251881Speter || (range->start < gap_start && gap_end <= range->end)) 5064251881Speter { 5065251881Speter proper_subset = TRUE; 5066251881Speter break; 5067251881Speter } 5068251881Speter else if ((gap_start == range->start) && (range->end == gap_end)) 5069251881Speter { 5070251881Speter break; 5071251881Speter } 5072251881Speter else if (gap_start <= range->end && range->start <= gap_end) 5073251881Speter /* intersect */ 5074251881Speter { 5075251881Speter overlaps_or_adjoins = TRUE; 5076251881Speter break; 5077251881Speter } 5078251881Speter } 5079251881Speter 5080251881Speter if (!proper_subset) 5081251881Speter { 5082251881Speter /* We need to make adjustments. Remove from, or add the gap 5083251881Speter to, CHILD->REMAINING_RANGES as appropriate. */ 5084251881Speter 5085251881Speter if (overlaps_or_adjoins) 5086251881Speter SVN_ERR(svn_rangelist_merge2(child->remaining_ranges, 5087251881Speter merge_b->implicit_src_gap, 5088251881Speter result_pool, iterpool)); 5089251881Speter else /* equals == TRUE */ 5090251881Speter SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges), 5091251881Speter merge_b->implicit_src_gap, 5092251881Speter child->remaining_ranges, FALSE, 5093251881Speter result_pool)); 5094251881Speter } 5095251881Speter 5096251881Speter if (source->loc1->rev > source->loc2->rev) /* Reverse merge */ 5097251881Speter SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool)); 5098251881Speter } 5099251881Speter } 5100251881Speter 5101251881Speter svn_pool_destroy(iterpool); 5102251881Speter return SVN_NO_ERROR; 5103251881Speter} 5104251881Speter 5105251881Speter 5106251881Speter/*-----------------------------------------------------------------------*/ 5107251881Speter 5108251881Speter/*** Other Helper Functions ***/ 5109251881Speter 5110251881Speter/* Calculate the new mergeinfo for the target tree rooted at TARGET_ABSPATH 5111251881Speter based on MERGES (a mapping of absolute WC paths to rangelists representing 5112251881Speter a merge from the source SOURCE_FSPATH). 5113251881Speter 5114251881Speter If RESULT_CATALOG is NULL, then record the new mergeinfo in the WC (at, 5115251881Speter and possibly below, TARGET_ABSPATH). 5116251881Speter 5117251881Speter If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the 5118251881Speter WC, but instead record it in RESULT_CATALOG, where the keys are absolute 5119251881Speter working copy paths and the values are the new mergeinfos for each. 5120251881Speter Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was 5121251881Speter created in. */ 5122251881Speterstatic svn_error_t * 5123251881Speterupdate_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog, 5124251881Speter const char *target_abspath, 5125251881Speter const char *source_fspath, 5126251881Speter apr_hash_t *merges, 5127251881Speter svn_boolean_t is_rollback, 5128251881Speter svn_client_ctx_t *ctx, 5129251881Speter apr_pool_t *scratch_pool) 5130251881Speter{ 5131251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 5132251881Speter apr_hash_index_t *hi; 5133251881Speter 5134251881Speter /* Combine the mergeinfo for the revision range just merged into 5135251881Speter the WC with its on-disk mergeinfo. */ 5136251881Speter for (hi = apr_hash_first(scratch_pool, merges); hi; hi = apr_hash_next(hi)) 5137251881Speter { 5138251881Speter const char *local_abspath = svn__apr_hash_index_key(hi); 5139251881Speter svn_rangelist_t *ranges = svn__apr_hash_index_val(hi); 5140251881Speter svn_rangelist_t *rangelist; 5141251881Speter svn_error_t *err; 5142251881Speter const char *local_abspath_rel_to_target; 5143251881Speter const char *fspath; 5144251881Speter svn_mergeinfo_t mergeinfo; 5145251881Speter 5146251881Speter svn_pool_clear(iterpool); 5147251881Speter 5148251881Speter /* As some of the merges may've changed the WC's mergeinfo, get 5149251881Speter a fresh copy before using it to update the WC's mergeinfo. */ 5150251881Speter err = svn_client__parse_mergeinfo(&mergeinfo, ctx->wc_ctx, 5151251881Speter local_abspath, iterpool, iterpool); 5152251881Speter 5153251881Speter /* If a directory PATH was skipped because it is missing or was 5154251881Speter obstructed by an unversioned item then there's nothing we can 5155251881Speter do with that, so skip it. */ 5156251881Speter if (err) 5157251881Speter { 5158251881Speter if (err->apr_err == SVN_ERR_WC_NOT_LOCKED 5159251881Speter || err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 5160251881Speter { 5161251881Speter svn_error_clear(err); 5162251881Speter continue; 5163251881Speter } 5164251881Speter else 5165251881Speter { 5166251881Speter return svn_error_trace(err); 5167251881Speter } 5168251881Speter } 5169251881Speter 5170251881Speter /* If we are attempting to set empty revision range override mergeinfo 5171251881Speter on a path with no explicit mergeinfo, we first need the 5172251881Speter mergeinfo that path inherits. */ 5173251881Speter if (mergeinfo == NULL && ranges->nelts == 0) 5174251881Speter { 5175251881Speter SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, NULL, 5176251881Speter svn_mergeinfo_nearest_ancestor, 5177251881Speter local_abspath, NULL, NULL, 5178251881Speter FALSE, ctx, iterpool, iterpool)); 5179251881Speter } 5180251881Speter 5181251881Speter if (mergeinfo == NULL) 5182251881Speter mergeinfo = apr_hash_make(iterpool); 5183251881Speter 5184251881Speter local_abspath_rel_to_target = svn_dirent_skip_ancestor(target_abspath, 5185251881Speter local_abspath); 5186251881Speter SVN_ERR_ASSERT(local_abspath_rel_to_target != NULL); 5187251881Speter fspath = svn_fspath__join(source_fspath, 5188251881Speter local_abspath_rel_to_target, 5189251881Speter iterpool); 5190251881Speter rangelist = svn_hash_gets(mergeinfo, fspath); 5191251881Speter if (rangelist == NULL) 5192251881Speter rangelist = apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *)); 5193251881Speter 5194251881Speter if (is_rollback) 5195251881Speter { 5196251881Speter ranges = svn_rangelist_dup(ranges, iterpool); 5197251881Speter SVN_ERR(svn_rangelist_reverse(ranges, iterpool)); 5198251881Speter SVN_ERR(svn_rangelist_remove(&rangelist, ranges, rangelist, 5199251881Speter FALSE, 5200251881Speter iterpool)); 5201251881Speter } 5202251881Speter else 5203251881Speter { 5204251881Speter SVN_ERR(svn_rangelist_merge2(rangelist, ranges, iterpool, iterpool)); 5205251881Speter } 5206251881Speter /* Update the mergeinfo by adjusting the path's rangelist. */ 5207251881Speter svn_hash_sets(mergeinfo, fspath, rangelist); 5208251881Speter 5209251881Speter if (is_rollback && apr_hash_count(mergeinfo) == 0) 5210251881Speter mergeinfo = NULL; 5211251881Speter 5212251881Speter svn_mergeinfo__remove_empty_rangelists(mergeinfo, scratch_pool); 5213251881Speter 5214251881Speter if (result_catalog) 5215251881Speter { 5216251881Speter svn_mergeinfo_t existing_mergeinfo = 5217251881Speter svn_hash_gets(result_catalog, local_abspath); 5218251881Speter apr_pool_t *result_catalog_pool = apr_hash_pool_get(result_catalog); 5219251881Speter 5220251881Speter if (existing_mergeinfo) 5221251881Speter SVN_ERR(svn_mergeinfo_merge2(mergeinfo, existing_mergeinfo, 5222251881Speter result_catalog_pool, scratch_pool)); 5223251881Speter svn_hash_sets(result_catalog, 5224251881Speter apr_pstrdup(result_catalog_pool, local_abspath), 5225251881Speter svn_mergeinfo_dup(mergeinfo, result_catalog_pool)); 5226251881Speter } 5227251881Speter else 5228251881Speter { 5229251881Speter err = svn_client__record_wc_mergeinfo(local_abspath, mergeinfo, 5230251881Speter TRUE, ctx, iterpool); 5231251881Speter 5232251881Speter if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND) 5233251881Speter { 5234251881Speter /* PATH isn't just missing, it's not even versioned as far 5235251881Speter as this working copy knows. But it was included in 5236251881Speter MERGES, which means that the server knows about it. 5237251881Speter Likely we don't have access to the source due to authz 5238251881Speter restrictions. For now just clear the error and 5239251881Speter continue... 5240251881Speter 5241251881Speter ### TODO: Set non-inheritable mergeinfo on PATH's immediate 5242251881Speter ### parent and normal mergeinfo on PATH's siblings which we 5243251881Speter ### do have access to. */ 5244251881Speter svn_error_clear(err); 5245251881Speter } 5246251881Speter else 5247251881Speter SVN_ERR(err); 5248251881Speter } 5249251881Speter } 5250251881Speter 5251251881Speter svn_pool_destroy(iterpool); 5252251881Speter return SVN_NO_ERROR; 5253251881Speter} 5254251881Speter 5255251881Speter/* Helper for record_mergeinfo_for_dir_merge(). 5256251881Speter 5257251881Speter Record override mergeinfo on any paths skipped during a merge. 5258251881Speter 5259251881Speter Set empty mergeinfo on each path in MERGE_B->SKIPPED_ABSPATHS so the path 5260251881Speter does not incorrectly inherit mergeinfo that will later be describing 5261251881Speter the merge. 5262251881Speter 5263251881Speter MERGEINFO_PATH and MERGE_B are cascaded from 5264251881Speter arguments of the same name in the caller. 5265251881Speter 5266251881Speter IS_ROLLBACK is true if the caller is recording a reverse merge and false 5267251881Speter otherwise. RANGELIST is the set of revisions being merged from 5268251881Speter MERGEINFO_PATH to MERGE_B->target. */ 5269251881Speterstatic svn_error_t * 5270251881Speterrecord_skips_in_mergeinfo(const char *mergeinfo_path, 5271251881Speter const svn_rangelist_t *rangelist, 5272251881Speter svn_boolean_t is_rollback, 5273251881Speter merge_cmd_baton_t *merge_b, 5274251881Speter apr_pool_t *scratch_pool) 5275251881Speter{ 5276251881Speter apr_hash_index_t *hi; 5277251881Speter apr_hash_t *merges; 5278251881Speter apr_size_t nbr_skips = apr_hash_count(merge_b->skipped_abspaths); 5279251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 5280251881Speter 5281251881Speter if (nbr_skips == 0) 5282251881Speter return SVN_NO_ERROR; 5283251881Speter 5284251881Speter merges = apr_hash_make(scratch_pool); 5285251881Speter 5286251881Speter /* Override the mergeinfo for child paths which weren't actually merged. */ 5287251881Speter for (hi = apr_hash_first(scratch_pool, merge_b->skipped_abspaths); hi; 5288251881Speter hi = apr_hash_next(hi)) 5289251881Speter { 5290251881Speter const char *skipped_abspath = svn__apr_hash_index_key(hi); 5291251881Speter svn_wc_notify_state_t obstruction_state; 5292251881Speter 5293251881Speter svn_pool_clear(iterpool); 5294251881Speter 5295251881Speter /* Before we override, make sure this is a versioned path, it might 5296251881Speter be an external or missing from disk due to authz restrictions. */ 5297251881Speter SVN_ERR(perform_obstruction_check(&obstruction_state, NULL, NULL, 5298251881Speter NULL, NULL, 5299251881Speter merge_b, skipped_abspath, 5300251881Speter iterpool)); 5301251881Speter if (obstruction_state == svn_wc_notify_state_obstructed 5302251881Speter || obstruction_state == svn_wc_notify_state_missing) 5303251881Speter continue; 5304251881Speter 5305251881Speter /* Add an empty range list for this path. 5306251881Speter 5307251881Speter ### TODO: This works fine for a file path skipped because it is 5308251881Speter ### missing as long as the file's parent directory is present. 5309251881Speter ### But missing directory paths skipped are not handled yet, 5310251881Speter ### see issue #2915. 5311251881Speter 5312251881Speter ### TODO: An empty range is fine if the skipped path doesn't 5313251881Speter ### inherit any mergeinfo from a parent, but if it does 5314251881Speter ### we need to account for that. See issue #3440 5315251881Speter ### http://subversion.tigris.org/issues/show_bug.cgi?id=3440. */ 5316251881Speter svn_hash_sets(merges, skipped_abspath, 5317251881Speter apr_array_make(scratch_pool, 0, 5318251881Speter sizeof(svn_merge_range_t *))); 5319251881Speter 5320251881Speter /* if (nbr_skips < notify_b->nbr_notifications) 5321251881Speter ### Use RANGELIST as the mergeinfo for all children of 5322251881Speter ### this path which were not also explicitly 5323251881Speter ### skipped? */ 5324251881Speter } 5325251881Speter SVN_ERR(update_wc_mergeinfo(NULL, merge_b->target->abspath, 5326251881Speter mergeinfo_path, merges, 5327251881Speter is_rollback, merge_b->ctx, iterpool)); 5328251881Speter svn_pool_destroy(iterpool); 5329251881Speter return SVN_NO_ERROR; 5330251881Speter} 5331251881Speter 5332251881Speter/* Data for reporting when a merge aborted because of raising conflicts. 5333251881Speter */ 5334251881Spetertypedef struct single_range_conflict_report_t 5335251881Speter{ 5336251881Speter /* What sub-range of the requested source raised conflicts? 5337251881Speter * The 'inheritable' flag is ignored. */ 5338251881Speter merge_source_t *conflicted_range; 5339251881Speter /* What sub-range of the requested source remains to be merged? 5340251881Speter * NULL if no more. The 'inheritable' flag is ignored. */ 5341251881Speter merge_source_t *remaining_source; 5342251881Speter 5343251881Speter} single_range_conflict_report_t; 5344251881Speter 5345251881Speter/* Create a single_range_conflict_report_t, containing deep copies of 5346251881Speter * CONFLICTED_RANGE and REMAINING_SOURCE, allocated in RESULT_POOL. */ 5347251881Speterstatic single_range_conflict_report_t * 5348251881Spetersingle_range_conflict_report_create(const merge_source_t *conflicted_range, 5349251881Speter const merge_source_t *remaining_source, 5350251881Speter apr_pool_t *result_pool) 5351251881Speter{ 5352251881Speter single_range_conflict_report_t *report 5353251881Speter = apr_palloc(result_pool, sizeof(*report)); 5354251881Speter 5355251881Speter assert(conflicted_range != NULL); 5356251881Speter 5357251881Speter report->conflicted_range = merge_source_dup(conflicted_range, result_pool); 5358251881Speter report->remaining_source 5359251881Speter = remaining_source ? merge_source_dup(remaining_source, result_pool) 5360251881Speter : NULL; 5361251881Speter return report; 5362251881Speter} 5363251881Speter 5364251881Speter/* Data for reporting when a merge aborted because of raising conflicts. 5365251881Speter * 5366251881Speter * ### TODO: More info, including the ranges (or other parameters) the user 5367251881Speter * needs to complete the merge. 5368251881Speter */ 5369251881Spetertypedef struct conflict_report_t 5370251881Speter{ 5371251881Speter const char *target_abspath; 5372251881Speter /* The revision range during which conflicts were raised */ 5373251881Speter const merge_source_t *conflicted_range; 5374251881Speter /* Was the conflicted range the last range in the whole requested merge? */ 5375251881Speter svn_boolean_t was_last_range; 5376251881Speter} conflict_report_t; 5377251881Speter 5378251881Speter/* Return a new conflict_report_t containing deep copies of the parameters, 5379251881Speter * allocated in RESULT_POOL. */ 5380251881Speterstatic conflict_report_t * 5381251881Speterconflict_report_create(const char *target_abspath, 5382251881Speter const merge_source_t *conflicted_range, 5383251881Speter svn_boolean_t was_last_range, 5384251881Speter apr_pool_t *result_pool) 5385251881Speter{ 5386251881Speter conflict_report_t *report = apr_palloc(result_pool, sizeof(*report)); 5387251881Speter 5388251881Speter report->target_abspath = apr_pstrdup(result_pool, target_abspath); 5389251881Speter report->conflicted_range = merge_source_dup(conflicted_range, result_pool); 5390251881Speter report->was_last_range = was_last_range; 5391251881Speter return report; 5392251881Speter} 5393251881Speter 5394251881Speter/* Return a deep copy of REPORT, allocated in RESULT_POOL. */ 5395251881Speterstatic conflict_report_t * 5396251881Speterconflict_report_dup(const conflict_report_t *report, 5397251881Speter apr_pool_t *result_pool) 5398251881Speter{ 5399251881Speter conflict_report_t *new = apr_pmemdup(result_pool, report, sizeof(*new)); 5400251881Speter 5401251881Speter new->target_abspath = apr_pstrdup(result_pool, report->target_abspath); 5402251881Speter new->conflicted_range = merge_source_dup(report->conflicted_range, 5403251881Speter result_pool); 5404251881Speter return new; 5405251881Speter} 5406251881Speter 5407251881Speter/* Create and return an error structure appropriate for the unmerged 5408251881Speter revisions range(s). */ 5409251881Speterstatic APR_INLINE svn_error_t * 5410251881Spetermake_merge_conflict_error(conflict_report_t *report, 5411251881Speter apr_pool_t *scratch_pool) 5412251881Speter{ 5413251881Speter assert(!report || svn_dirent_is_absolute(report->target_abspath)); 5414251881Speter 5415251881Speter if (report && ! report->was_last_range) 5416251881Speter { 5417251881Speter svn_error_t *err = svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL, 5418251881Speter _("One or more conflicts were produced while merging r%ld:%ld into\n" 5419251881Speter "'%s' --\n" 5420251881Speter "resolve all conflicts and rerun the merge to apply the remaining\n" 5421251881Speter "unmerged revisions"), 5422251881Speter report->conflicted_range->loc1->rev, report->conflicted_range->loc2->rev, 5423251881Speter svn_dirent_local_style(report->target_abspath, scratch_pool)); 5424251881Speter assert(report->conflicted_range->loc1->rev != report->conflicted_range->loc2->rev); /* ### is a valid case in a 2-URL merge */ 5425251881Speter return err; 5426251881Speter } 5427251881Speter return SVN_NO_ERROR; 5428251881Speter} 5429251881Speter 5430251881Speter/* Helper for do_directory_merge(). 5431251881Speter 5432251881Speter TARGET_WCPATH is a directory and CHILDREN_WITH_MERGEINFO is filled 5433251881Speter with paths (svn_client__merge_path_t *) arranged in depth first order, 5434251881Speter which have mergeinfo set on them or meet one of the other criteria 5435251881Speter defined in get_mergeinfo_paths(). Remove any paths absent from disk 5436251881Speter or scheduled for deletion from CHILDREN_WITH_MERGEINFO which are equal to 5437251881Speter or are descendants of TARGET_WCPATH by setting those children to NULL. */ 5438251881Speterstatic void 5439251881Speterremove_absent_children(const char *target_wcpath, 5440251881Speter apr_array_header_t *children_with_mergeinfo) 5441251881Speter{ 5442251881Speter /* Before we try to override mergeinfo for skipped paths, make sure 5443251881Speter the path isn't absent due to authz restrictions, because there's 5444251881Speter nothing we can do about those. */ 5445251881Speter int i; 5446251881Speter for (i = 0; i < children_with_mergeinfo->nelts; i++) 5447251881Speter { 5448251881Speter svn_client__merge_path_t *child = 5449251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); 5450251881Speter if ((child->absent || child->scheduled_for_deletion) 5451251881Speter && svn_dirent_is_ancestor(target_wcpath, child->abspath)) 5452251881Speter { 5453251881Speter svn_sort__array_delete(children_with_mergeinfo, i--, 1); 5454251881Speter } 5455251881Speter } 5456251881Speter} 5457251881Speter 5458251881Speter/* Helper for do_directory_merge() to handle the case where a merge editor 5459251881Speter drive removes explicit mergeinfo from a subtree of the merge target. 5460251881Speter 5461251881Speter MERGE_B is cascaded from the argument of the same name in 5462251881Speter do_directory_merge(). For each path (if any) in 5463251881Speter MERGE_B->PATHS_WITH_DELETED_MERGEINFO remove that path from 5464251881Speter CHILDREN_WITH_MERGEINFO. 5465251881Speter 5466251881Speter The one exception is for the merge target itself, 5467251881Speter MERGE_B->target->abspath, this must always be present in 5468251881Speter CHILDREN_WITH_MERGEINFO so this is never removed by this 5469251881Speter function. */ 5470251881Speterstatic void 5471251881Speterremove_children_with_deleted_mergeinfo(merge_cmd_baton_t *merge_b, 5472251881Speter apr_array_header_t *children_with_mergeinfo) 5473251881Speter{ 5474251881Speter int i; 5475251881Speter 5476251881Speter if (!merge_b->paths_with_deleted_mergeinfo) 5477251881Speter return; 5478251881Speter 5479251881Speter /* CHILDREN_WITH_MERGEINFO[0] is the always the merge target 5480251881Speter so start at the first child. */ 5481251881Speter for (i = 1; i < children_with_mergeinfo->nelts; i++) 5482251881Speter { 5483251881Speter svn_client__merge_path_t *child = 5484251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); 5485251881Speter 5486251881Speter if (svn_hash_gets(merge_b->paths_with_deleted_mergeinfo, child->abspath)) 5487251881Speter { 5488251881Speter svn_sort__array_delete(children_with_mergeinfo, i--, 1); 5489251881Speter } 5490251881Speter } 5491251881Speter} 5492251881Speter 5493251881Speter/* Helper for do_directory_merge(). 5494251881Speter 5495251881Speter Set up the diff editor report to merge the SOURCE diff 5496251881Speter into TARGET_ABSPATH and drive it. 5497251881Speter 5498251881Speter If mergeinfo is not being honored (based on MERGE_B -- see the doc 5499251881Speter string for HONOR_MERGEINFO() for how this is determined), then ignore 5500251881Speter CHILDREN_WITH_MERGEINFO and merge the SOURCE diff to TARGET_ABSPATH. 5501251881Speter 5502251881Speter If mergeinfo is being honored then perform a history-aware merge, 5503251881Speter describing TARGET_ABSPATH and its subtrees to the reporter in such as way 5504251881Speter as to avoid repeating merges already performed per the mergeinfo and 5505251881Speter natural history of TARGET_ABSPATH and its subtrees. 5506251881Speter 5507251881Speter The ranges that still need to be merged to the TARGET_ABSPATH and its 5508251881Speter subtrees are described in CHILDREN_WITH_MERGEINFO, an array of 5509251881Speter svn_client__merge_path_t * -- see 'THE CHILDREN_WITH_MERGEINFO ARRAY' 5510251881Speter comment at the top of this file for more info. Note that it is possible 5511251881Speter TARGET_ABSPATH and/or some of its subtrees need only a subset, or no part, 5512251881Speter of SOURCE to be merged. Though there is little point to 5513251881Speter calling this function if TARGET_ABSPATH and all its subtrees have already 5514251881Speter had SOURCE merged, this will work but is a no-op. 5515251881Speter 5516251881Speter SOURCE->rev1 and SOURCE->rev2 must be bound by the set of remaining_ranges 5517251881Speter fields in CHILDREN_WITH_MERGEINFO's elements, specifically: 5518251881Speter 5519251881Speter For forward merges (SOURCE->rev1 < SOURCE->rev2): 5520251881Speter 5521251881Speter 1) The first svn_merge_range_t * element of each child's remaining_ranges 5522251881Speter array must meet one of the following conditions: 5523251881Speter 5524251881Speter a) The range's start field is greater than or equal to SOURCE->rev2. 5525251881Speter 5526251881Speter b) The range's end field is SOURCE->rev2. 5527251881Speter 5528251881Speter 2) Among all the ranges that meet condition 'b' the oldest start 5529251881Speter revision must equal SOURCE->rev1. 5530251881Speter 5531251881Speter For reverse merges (SOURCE->rev1 > SOURCE->rev2): 5532251881Speter 5533251881Speter 1) The first svn_merge_range_t * element of each child's remaining_ranges 5534251881Speter array must meet one of the following conditions: 5535251881Speter 5536251881Speter a) The range's start field is less than or equal to SOURCE->rev2. 5537251881Speter 5538251881Speter b) The range's end field is SOURCE->rev2. 5539251881Speter 5540251881Speter 2) Among all the ranges that meet condition 'b' the youngest start 5541251881Speter revision must equal SOURCE->rev1. 5542251881Speter 5543251881Speter Note: If the first svn_merge_range_t * element of some subtree child's 5544251881Speter remaining_ranges array is the same as the first range of that child's 5545251881Speter nearest path-wise ancestor, then the subtree child *will not* be described 5546251881Speter to the reporter. 5547251881Speter 5548251881Speter DEPTH, NOTIFY_B, and MERGE_B are cascaded from do_directory_merge(), see 5549251881Speter that function for more info. 5550251881Speter 5551251881Speter MERGE_B->ra_session1 and MERGE_B->ra_session2 are RA sessions open to any 5552251881Speter URL in the repository of SOURCE; they may be temporarily reparented within 5553251881Speter this function. 5554251881Speter 5555251881Speter If SOURCE->ancestral is set, then SOURCE->loc1 must be a 5556251881Speter historical ancestor of SOURCE->loc2, or vice-versa (see 5557251881Speter `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements around 5558251881Speter SOURCE in this case). 5559251881Speter*/ 5560251881Speterstatic svn_error_t * 5561251881Speterdrive_merge_report_editor(const char *target_abspath, 5562251881Speter const merge_source_t *source, 5563251881Speter const apr_array_header_t *children_with_mergeinfo, 5564251881Speter const svn_diff_tree_processor_t *processor, 5565251881Speter svn_depth_t depth, 5566251881Speter merge_cmd_baton_t *merge_b, 5567251881Speter apr_pool_t *scratch_pool) 5568251881Speter{ 5569251881Speter const svn_ra_reporter3_t *reporter; 5570251881Speter const svn_delta_editor_t *diff_editor; 5571251881Speter void *diff_edit_baton; 5572251881Speter void *report_baton; 5573251881Speter svn_revnum_t target_start; 5574251881Speter svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b); 5575251881Speter const char *old_sess1_url, *old_sess2_url; 5576251881Speter svn_boolean_t is_rollback = source->loc1->rev > source->loc2->rev; 5577251881Speter 5578251881Speter /* Start with a safe default starting revision for the editor and the 5579251881Speter merge target. */ 5580251881Speter target_start = source->loc1->rev; 5581251881Speter 5582251881Speter /* If we are honoring mergeinfo the starting revision for the merge target 5583251881Speter might not be SOURCE->rev1, in fact the merge target might not need *any* 5584251881Speter part of SOURCE merged -- Instead some subtree of the target 5585251881Speter needs SOURCE -- So get the right starting revision for the 5586251881Speter target. */ 5587251881Speter if (honor_mergeinfo) 5588251881Speter { 5589251881Speter svn_client__merge_path_t *child; 5590251881Speter 5591251881Speter /* CHILDREN_WITH_MERGEINFO must always exist if we are honoring 5592251881Speter mergeinfo and must have at least one element (describing the 5593251881Speter merge target). */ 5594251881Speter SVN_ERR_ASSERT(children_with_mergeinfo); 5595251881Speter SVN_ERR_ASSERT(children_with_mergeinfo->nelts); 5596251881Speter 5597251881Speter /* Get the merge target's svn_client__merge_path_t, which is always 5598251881Speter the first in the array due to depth first sorting requirement, 5599251881Speter see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */ 5600251881Speter child = APR_ARRAY_IDX(children_with_mergeinfo, 0, 5601251881Speter svn_client__merge_path_t *); 5602251881Speter SVN_ERR_ASSERT(child); 5603251881Speter if (child->remaining_ranges->nelts == 0) 5604251881Speter { 5605251881Speter /* The merge target doesn't need anything merged. */ 5606251881Speter target_start = source->loc2->rev; 5607251881Speter } 5608251881Speter else 5609251881Speter { 5610251881Speter /* The merge target has remaining revisions to merge. These 5611251881Speter ranges may fully or partially overlap the range described 5612251881Speter by SOURCE->rev1:rev2 or may not intersect that range at 5613251881Speter all. */ 5614251881Speter svn_merge_range_t *range = 5615251881Speter APR_ARRAY_IDX(child->remaining_ranges, 0, 5616251881Speter svn_merge_range_t *); 5617251881Speter if ((!is_rollback && range->start > source->loc2->rev) 5618251881Speter || (is_rollback && range->start < source->loc2->rev)) 5619251881Speter { 5620251881Speter /* Merge target's first remaining range doesn't intersect. */ 5621251881Speter target_start = source->loc2->rev; 5622251881Speter } 5623251881Speter else 5624251881Speter { 5625251881Speter /* Merge target's first remaining range partially or 5626251881Speter fully overlaps. */ 5627251881Speter target_start = range->start; 5628251881Speter } 5629251881Speter } 5630251881Speter } 5631251881Speter 5632251881Speter SVN_ERR(svn_client__ensure_ra_session_url(&old_sess1_url, 5633251881Speter merge_b->ra_session1, 5634251881Speter source->loc1->url, scratch_pool)); 5635251881Speter /* Temporarily point our second RA session to SOURCE->loc1->url, too. We use 5636251881Speter this to request individual file contents. */ 5637251881Speter SVN_ERR(svn_client__ensure_ra_session_url(&old_sess2_url, 5638251881Speter merge_b->ra_session2, 5639251881Speter source->loc1->url, scratch_pool)); 5640251881Speter 5641251881Speter /* Get the diff editor and a reporter with which to, ultimately, 5642251881Speter drive it. */ 5643251881Speter SVN_ERR(svn_client__get_diff_editor2(&diff_editor, &diff_edit_baton, 5644251881Speter merge_b->ra_session2, 5645251881Speter depth, 5646251881Speter source->loc1->rev, 5647251881Speter TRUE /* text_deltas */, 5648251881Speter processor, 5649251881Speter merge_b->ctx->cancel_func, 5650251881Speter merge_b->ctx->cancel_baton, 5651251881Speter scratch_pool)); 5652251881Speter SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1, 5653251881Speter &reporter, &report_baton, source->loc2->rev, 5654251881Speter "", depth, merge_b->diff_ignore_ancestry, 5655251881Speter TRUE, /* text_deltas */ 5656251881Speter source->loc2->url, diff_editor, diff_edit_baton, 5657251881Speter scratch_pool)); 5658251881Speter 5659251881Speter /* Drive the reporter. */ 5660251881Speter SVN_ERR(reporter->set_path(report_baton, "", target_start, depth, 5661251881Speter FALSE, NULL, scratch_pool)); 5662251881Speter if (honor_mergeinfo && children_with_mergeinfo) 5663251881Speter { 5664251881Speter /* Describe children with mergeinfo overlapping this merge 5665251881Speter operation such that no repeated diff is retrieved for them from 5666251881Speter the repository. */ 5667251881Speter int i; 5668251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 5669251881Speter 5670251881Speter /* Start with CHILDREN_WITH_MERGEINFO[1], CHILDREN_WITH_MERGEINFO[0] 5671251881Speter is always the merge target (TARGET_ABSPATH). */ 5672251881Speter for (i = 1; i < children_with_mergeinfo->nelts; i++) 5673251881Speter { 5674251881Speter svn_merge_range_t *range; 5675251881Speter const char *child_repos_path; 5676251881Speter const svn_client__merge_path_t *parent; 5677251881Speter const svn_client__merge_path_t *child = 5678251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, 5679251881Speter svn_client__merge_path_t *); 5680251881Speter 5681251881Speter SVN_ERR_ASSERT(child); 5682251881Speter if (child->absent) 5683251881Speter continue; 5684251881Speter 5685251881Speter svn_pool_clear(iterpool); 5686251881Speter 5687251881Speter /* Find this child's nearest wc ancestor with mergeinfo. */ 5688251881Speter parent = find_nearest_ancestor(children_with_mergeinfo, 5689251881Speter FALSE, child->abspath); 5690251881Speter 5691251881Speter /* If a subtree needs the same range applied as its nearest parent 5692251881Speter with mergeinfo or neither the subtree nor this parent need 5693251881Speter SOURCE->rev1:rev2 merged, then we don't need to describe the 5694251881Speter subtree separately. In the latter case this could break the 5695251881Speter editor if child->abspath didn't exist at SOURCE->rev2 and we 5696251881Speter attempt to describe it via a reporter set_path call. */ 5697251881Speter if (child->remaining_ranges->nelts) 5698251881Speter { 5699251881Speter range = APR_ARRAY_IDX(child->remaining_ranges, 0, 5700251881Speter svn_merge_range_t *); 5701251881Speter if ((!is_rollback && range->start > source->loc2->rev) 5702251881Speter || (is_rollback && range->start < source->loc2->rev)) 5703251881Speter { 5704251881Speter /* This child's first remaining range comes after the range 5705251881Speter we are currently merging, so skip it. We expect to get 5706251881Speter to it in a subsequent call to this function. */ 5707251881Speter continue; 5708251881Speter } 5709251881Speter else if (parent->remaining_ranges->nelts) 5710251881Speter { 5711251881Speter svn_merge_range_t *parent_range = 5712251881Speter APR_ARRAY_IDX(parent->remaining_ranges, 0, 5713251881Speter svn_merge_range_t *); 5714251881Speter svn_merge_range_t *child_range = 5715251881Speter APR_ARRAY_IDX(child->remaining_ranges, 0, 5716251881Speter svn_merge_range_t *); 5717251881Speter if (parent_range->start == child_range->start) 5718251881Speter continue; /* Subtree needs same range as parent. */ 5719251881Speter } 5720251881Speter } 5721251881Speter else /* child->remaining_ranges->nelts == 0*/ 5722251881Speter { 5723251881Speter /* If both the subtree and its parent need no ranges applied 5724251881Speter consider that as the "same ranges" and don't describe 5725251881Speter the subtree. */ 5726251881Speter if (parent->remaining_ranges->nelts == 0) 5727251881Speter continue; 5728251881Speter } 5729251881Speter 5730251881Speter /* Ok, we really need to describe this subtree as it needs different 5731251881Speter ranges applied than its nearest working copy parent. */ 5732251881Speter child_repos_path = svn_dirent_is_child(target_abspath, 5733251881Speter child->abspath, 5734251881Speter iterpool); 5735251881Speter /* This loop is only processing subtrees, so CHILD->ABSPATH 5736251881Speter better be a proper child of the merge target. */ 5737251881Speter SVN_ERR_ASSERT(child_repos_path); 5738251881Speter 5739251881Speter if ((child->remaining_ranges->nelts == 0) 5740251881Speter || (is_rollback && (range->start < source->loc2->rev)) 5741251881Speter || (!is_rollback && (range->start > source->loc2->rev))) 5742251881Speter { 5743251881Speter /* Nothing to merge to this child. We'll claim we have 5744251881Speter it up to date so the server doesn't send us 5745251881Speter anything. */ 5746251881Speter SVN_ERR(reporter->set_path(report_baton, child_repos_path, 5747251881Speter source->loc2->rev, depth, FALSE, 5748251881Speter NULL, iterpool)); 5749251881Speter } 5750251881Speter else 5751251881Speter { 5752251881Speter SVN_ERR(reporter->set_path(report_baton, child_repos_path, 5753251881Speter range->start, depth, FALSE, 5754251881Speter NULL, iterpool)); 5755251881Speter } 5756251881Speter } 5757251881Speter svn_pool_destroy(iterpool); 5758251881Speter } 5759251881Speter SVN_ERR(reporter->finish_report(report_baton, scratch_pool)); 5760251881Speter 5761251881Speter /* Point the merge baton's RA sessions back where they were. */ 5762251881Speter SVN_ERR(svn_ra_reparent(merge_b->ra_session1, old_sess1_url, scratch_pool)); 5763251881Speter SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_sess2_url, scratch_pool)); 5764251881Speter 5765251881Speter return SVN_NO_ERROR; 5766251881Speter} 5767251881Speter 5768251881Speter/* Iterate over each svn_client__merge_path_t * element in 5769251881Speter CHILDREN_WITH_MERGEINFO and, if START_REV is true, find the most inclusive 5770251881Speter start revision among those element's first remaining_ranges element. If 5771251881Speter START_REV is false, then look for the most inclusive end revision. 5772251881Speter 5773251881Speter If IS_ROLLBACK is true the youngest start or end (as per START_REV) 5774251881Speter revision is considered the "most inclusive" otherwise the oldest revision 5775251881Speter is. 5776251881Speter 5777251881Speter If none of CHILDREN_WITH_MERGEINFO's elements have any remaining ranges 5778251881Speter return SVN_INVALID_REVNUM. */ 5779251881Speterstatic svn_revnum_t 5780251881Speterget_most_inclusive_rev(const apr_array_header_t *children_with_mergeinfo, 5781251881Speter svn_boolean_t is_rollback, 5782251881Speter svn_boolean_t start_rev) 5783251881Speter{ 5784251881Speter int i; 5785251881Speter svn_revnum_t most_inclusive_rev = SVN_INVALID_REVNUM; 5786251881Speter 5787251881Speter for (i = 0; i < children_with_mergeinfo->nelts; i++) 5788251881Speter { 5789251881Speter svn_client__merge_path_t *child = 5790251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); 5791251881Speter 5792251881Speter if ((! child) || child->absent) 5793251881Speter continue; 5794251881Speter if (child->remaining_ranges->nelts > 0) 5795251881Speter { 5796251881Speter svn_merge_range_t *range = 5797251881Speter APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *); 5798251881Speter 5799251881Speter /* Are we looking for the most inclusive start or end rev? */ 5800251881Speter svn_revnum_t rev = start_rev ? range->start : range->end; 5801251881Speter 5802251881Speter if ((most_inclusive_rev == SVN_INVALID_REVNUM) 5803251881Speter || (is_rollback && (rev > most_inclusive_rev)) 5804251881Speter || ((! is_rollback) && (rev < most_inclusive_rev))) 5805251881Speter most_inclusive_rev = rev; 5806251881Speter } 5807251881Speter } 5808251881Speter return most_inclusive_rev; 5809251881Speter} 5810251881Speter 5811251881Speter 5812251881Speter/* If first item in each child of CHILDREN_WITH_MERGEINFO's 5813251881Speter remaining_ranges is inclusive of END_REV, Slice the first range in 5814251881Speter to two at END_REV. All the allocations are persistent and allocated 5815251881Speter from POOL. */ 5816251881Speterstatic void 5817251881Speterslice_remaining_ranges(apr_array_header_t *children_with_mergeinfo, 5818251881Speter svn_boolean_t is_rollback, svn_revnum_t end_rev, 5819251881Speter apr_pool_t *pool) 5820251881Speter{ 5821251881Speter int i; 5822251881Speter for (i = 0; i < children_with_mergeinfo->nelts; i++) 5823251881Speter { 5824251881Speter svn_client__merge_path_t *child = 5825251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, 5826251881Speter svn_client__merge_path_t *); 5827251881Speter if (!child || child->absent) 5828251881Speter continue; 5829251881Speter if (child->remaining_ranges->nelts > 0) 5830251881Speter { 5831251881Speter svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0, 5832251881Speter svn_merge_range_t *); 5833251881Speter if ((is_rollback && (range->start > end_rev) 5834251881Speter && (range->end < end_rev)) 5835251881Speter || (!is_rollback && (range->start < end_rev) 5836251881Speter && (range->end > end_rev))) 5837251881Speter { 5838251881Speter svn_merge_range_t *split_range1, *split_range2; 5839251881Speter 5840251881Speter split_range1 = svn_merge_range_dup(range, pool); 5841251881Speter split_range2 = svn_merge_range_dup(range, pool); 5842251881Speter split_range1->end = end_rev; 5843251881Speter split_range2->start = end_rev; 5844251881Speter APR_ARRAY_IDX(child->remaining_ranges, 0, 5845251881Speter svn_merge_range_t *) = split_range1; 5846251881Speter svn_sort__array_insert(&split_range2, child->remaining_ranges, 1); 5847251881Speter } 5848251881Speter } 5849251881Speter } 5850251881Speter} 5851251881Speter 5852251881Speter/* Helper for do_directory_merge(). 5853251881Speter 5854251881Speter For each child in CHILDREN_WITH_MERGEINFO remove the first remaining_ranges 5855251881Speter svn_merge_range_t *element of the child if that range has an end revision 5856251881Speter equal to REVISION. 5857251881Speter 5858251881Speter If a range is removed from a child's remaining_ranges array, allocate the 5859251881Speter new remaining_ranges array in POOL. 5860251881Speter */ 5861251881Speterstatic void 5862251881Speterremove_first_range_from_remaining_ranges(svn_revnum_t revision, 5863251881Speter apr_array_header_t 5864251881Speter *children_with_mergeinfo, 5865251881Speter apr_pool_t *pool) 5866251881Speter{ 5867251881Speter int i; 5868251881Speter 5869251881Speter for (i = 0; i < children_with_mergeinfo->nelts; i++) 5870251881Speter { 5871251881Speter svn_client__merge_path_t *child = 5872251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, 5873251881Speter svn_client__merge_path_t *); 5874251881Speter if (!child || child->absent) 5875251881Speter continue; 5876251881Speter if (child->remaining_ranges->nelts > 0) 5877251881Speter { 5878251881Speter svn_merge_range_t *first_range = 5879251881Speter APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *); 5880251881Speter if (first_range->end == revision) 5881251881Speter { 5882251881Speter svn_sort__array_delete(child->remaining_ranges, 0, 1); 5883251881Speter } 5884251881Speter } 5885251881Speter } 5886251881Speter} 5887251881Speter 5888251881Speter/* Get a file's content and properties from the repository. 5889251881Speter Set *FILENAME to the local path to a new temporary file holding its text, 5890251881Speter and set *PROPS to a new hash of its properties. 5891251881Speter 5892251881Speter RA_SESSION is a session open to the correct repository, which will be 5893251881Speter temporarily reparented to the URL of the file itself. LOCATION is the 5894251881Speter repository location of the file. 5895251881Speter 5896251881Speter The resulting file and the return values live as long as RESULT_POOL, all 5897251881Speter other allocations occur in SCRATCH_POOL. 5898251881Speter*/ 5899251881Speterstatic svn_error_t * 5900251881Spetersingle_file_merge_get_file(const char **filename, 5901251881Speter apr_hash_t **props, 5902251881Speter svn_ra_session_t *ra_session, 5903251881Speter const svn_client__pathrev_t *location, 5904251881Speter const char *wc_target, 5905251881Speter apr_pool_t *result_pool, 5906251881Speter apr_pool_t *scratch_pool) 5907251881Speter{ 5908251881Speter svn_stream_t *stream; 5909251881Speter const char *old_sess_url; 5910251881Speter svn_error_t *err; 5911251881Speter 5912251881Speter SVN_ERR(svn_stream_open_unique(&stream, filename, NULL, 5913251881Speter svn_io_file_del_on_pool_cleanup, 5914251881Speter result_pool, scratch_pool)); 5915251881Speter 5916251881Speter SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url, ra_session, location->url, 5917251881Speter scratch_pool)); 5918251881Speter err = svn_ra_get_file(ra_session, "", location->rev, 5919251881Speter stream, NULL, props, scratch_pool); 5920251881Speter SVN_ERR(svn_error_compose_create( 5921251881Speter err, svn_ra_reparent(ra_session, old_sess_url, scratch_pool))); 5922251881Speter 5923251881Speter return svn_error_trace(svn_stream_close(stream)); 5924251881Speter} 5925251881Speter 5926251881Speter/* Compare two svn_client__merge_path_t elements **A and **B, given the 5927251881Speter addresses of pointers to them. Return an integer less than, equal to, or 5928251881Speter greater than zero if A sorts before, the same as, or after B, respectively. 5929251881Speter This is a helper for qsort() and bsearch() on an array of such elements. */ 5930251881Speterstatic int 5931251881Spetercompare_merge_path_t_as_paths(const void *a, 5932251881Speter const void *b) 5933251881Speter{ 5934251881Speter const svn_client__merge_path_t *child1 5935251881Speter = *((const svn_client__merge_path_t * const *) a); 5936251881Speter const svn_client__merge_path_t *child2 5937251881Speter = *((const svn_client__merge_path_t * const *) b); 5938251881Speter 5939251881Speter return svn_path_compare_paths(child1->abspath, child2->abspath); 5940251881Speter} 5941251881Speter 5942251881Speter/* Return a pointer to the element of CHILDREN_WITH_MERGEINFO whose path 5943251881Speter * is PATH, or return NULL if there is no such element. */ 5944251881Speterstatic svn_client__merge_path_t * 5945251881Speterget_child_with_mergeinfo(const apr_array_header_t *children_with_mergeinfo, 5946251881Speter const char *abspath) 5947251881Speter{ 5948251881Speter svn_client__merge_path_t merge_path; 5949251881Speter svn_client__merge_path_t *key; 5950251881Speter svn_client__merge_path_t **pchild; 5951251881Speter 5952251881Speter merge_path.abspath = abspath; 5953251881Speter key = &merge_path; 5954251881Speter pchild = bsearch(&key, children_with_mergeinfo->elts, 5955251881Speter children_with_mergeinfo->nelts, 5956251881Speter children_with_mergeinfo->elt_size, 5957251881Speter compare_merge_path_t_as_paths); 5958251881Speter return pchild ? *pchild : NULL; 5959251881Speter} 5960251881Speter 5961251881Speter/* Insert a deep copy of INSERT_ELEMENT into the CHILDREN_WITH_MERGEINFO 5962251881Speter array at its correct position. Allocate the new storage in POOL. 5963251881Speter CHILDREN_WITH_MERGEINFO is a depth first sorted array of 5964251881Speter (svn_client__merge_path_t *). 5965251881Speter 5966251881Speter ### Most callers don't need this to deep-copy the new element. 5967251881Speter ### It may be more efficient for some callers to insert a bunch of items 5968251881Speter out of order and then sort afterwards. (One caller is doing a qsort 5969251881Speter after calling this anyway.) 5970251881Speter */ 5971251881Speterstatic void 5972251881Speterinsert_child_to_merge(apr_array_header_t *children_with_mergeinfo, 5973251881Speter const svn_client__merge_path_t *insert_element, 5974251881Speter apr_pool_t *pool) 5975251881Speter{ 5976251881Speter int insert_index; 5977251881Speter const svn_client__merge_path_t *new_element; 5978251881Speter 5979251881Speter /* Find where to insert the new element */ 5980251881Speter insert_index = 5981251881Speter svn_sort__bsearch_lower_bound(&insert_element, children_with_mergeinfo, 5982251881Speter compare_merge_path_t_as_paths); 5983251881Speter 5984251881Speter new_element = svn_client__merge_path_dup(insert_element, pool); 5985251881Speter svn_sort__array_insert(&new_element, children_with_mergeinfo, insert_index); 5986251881Speter} 5987251881Speter 5988251881Speter/* Helper for get_mergeinfo_paths(). 5989251881Speter 5990251881Speter CHILDREN_WITH_MERGEINFO, DEPTH, and POOL are 5991251881Speter all cascaded from the arguments of the same name to get_mergeinfo_paths(). 5992251881Speter 5993251881Speter TARGET is the merge target. 5994251881Speter 5995251881Speter *CHILD is the element in in CHILDREN_WITH_MERGEINFO that 5996251881Speter get_mergeinfo_paths() is iterating over and *CURR_INDEX is index for 5997251881Speter *CHILD. 5998251881Speter 5999251881Speter If CHILD->ABSPATH is equal to MERGE_CMD_BATON->target->abspath do nothing. 6000251881Speter Else if CHILD->ABSPATH is switched or absent then make sure its immediate 6001251881Speter (as opposed to nearest) parent in CHILDREN_WITH_MERGEINFO is marked as 6002251881Speter missing a child. If the immediate parent does not exist in 6003251881Speter CHILDREN_WITH_MERGEINFO then create it (and increment *CURR_INDEX so that 6004251881Speter caller doesn't process the inserted element). Also ensure that 6005251881Speter CHILD->ABSPATH's siblings which are not already present in 6006251881Speter CHILDREN_WITH_MERGEINFO are also added to the array, limited by DEPTH 6007251881Speter (e.g. don't add directory siblings of a switched file). 6008251881Speter Use POOL for temporary allocations only, any new CHILDREN_WITH_MERGEINFO 6009251881Speter elements are allocated in POOL. */ 6010251881Speterstatic svn_error_t * 6011251881Speterinsert_parent_and_sibs_of_sw_absent_del_subtree( 6012251881Speter apr_array_header_t *children_with_mergeinfo, 6013251881Speter const merge_target_t *target, 6014251881Speter int *curr_index, 6015251881Speter svn_client__merge_path_t *child, 6016251881Speter svn_depth_t depth, 6017251881Speter svn_client_ctx_t *ctx, 6018251881Speter apr_pool_t *pool) 6019251881Speter{ 6020251881Speter svn_client__merge_path_t *parent; 6021251881Speter const char *parent_abspath; 6022251881Speter apr_pool_t *iterpool; 6023251881Speter const apr_array_header_t *children; 6024251881Speter int i; 6025251881Speter 6026251881Speter if (!(child->absent 6027251881Speter || (child->switched 6028251881Speter && strcmp(target->abspath, 6029251881Speter child->abspath) != 0))) 6030251881Speter return SVN_NO_ERROR; 6031251881Speter 6032251881Speter parent_abspath = svn_dirent_dirname(child->abspath, pool); 6033251881Speter parent = get_child_with_mergeinfo(children_with_mergeinfo, parent_abspath); 6034251881Speter if (parent) 6035251881Speter { 6036251881Speter parent->missing_child = child->absent; 6037251881Speter parent->switched_child = child->switched; 6038251881Speter } 6039251881Speter else 6040251881Speter { 6041251881Speter /* Create a new element to insert into CHILDREN_WITH_MERGEINFO. */ 6042251881Speter parent = svn_client__merge_path_create(parent_abspath, pool); 6043251881Speter parent->missing_child = child->absent; 6044251881Speter parent->switched_child = child->switched; 6045251881Speter /* Insert PARENT into CHILDREN_WITH_MERGEINFO. */ 6046251881Speter insert_child_to_merge(children_with_mergeinfo, parent, pool); 6047251881Speter /* Increment for loop index so we don't process the inserted element. */ 6048251881Speter (*curr_index)++; 6049251881Speter } /*(parent == NULL) */ 6050251881Speter 6051251881Speter /* Add all of PARENT's non-missing children that are not already present.*/ 6052251881Speter SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx, 6053251881Speter parent_abspath, FALSE, pool, pool)); 6054251881Speter iterpool = svn_pool_create(pool); 6055251881Speter for (i = 0; i < children->nelts; i++) 6056251881Speter { 6057251881Speter const char *child_abspath = APR_ARRAY_IDX(children, i, const char *); 6058251881Speter svn_client__merge_path_t *sibling_of_missing; 6059251881Speter 6060251881Speter svn_pool_clear(iterpool); 6061251881Speter 6062251881Speter /* Does this child already exist in CHILDREN_WITH_MERGEINFO? */ 6063251881Speter sibling_of_missing = get_child_with_mergeinfo(children_with_mergeinfo, 6064251881Speter child_abspath); 6065251881Speter /* Create the missing child and insert it into CHILDREN_WITH_MERGEINFO.*/ 6066251881Speter if (!sibling_of_missing) 6067251881Speter { 6068251881Speter /* Don't add directory children if DEPTH is svn_depth_files. */ 6069251881Speter if (depth == svn_depth_files) 6070251881Speter { 6071251881Speter svn_node_kind_t child_kind; 6072251881Speter 6073251881Speter SVN_ERR(svn_wc_read_kind2(&child_kind, 6074251881Speter ctx->wc_ctx, child_abspath, 6075251881Speter FALSE, FALSE, iterpool)); 6076251881Speter if (child_kind != svn_node_file) 6077251881Speter continue; 6078251881Speter } 6079251881Speter 6080251881Speter sibling_of_missing = svn_client__merge_path_create(child_abspath, 6081251881Speter pool); 6082251881Speter insert_child_to_merge(children_with_mergeinfo, sibling_of_missing, 6083251881Speter pool); 6084251881Speter } 6085251881Speter } 6086251881Speter 6087251881Speter svn_pool_destroy(iterpool); 6088251881Speter 6089251881Speter return SVN_NO_ERROR; 6090251881Speter} 6091251881Speter 6092251881Speter/* pre_merge_status_cb's baton */ 6093251881Speterstruct pre_merge_status_baton_t 6094251881Speter{ 6095251881Speter svn_wc_context_t *wc_ctx; 6096251881Speter 6097251881Speter /* const char *absolute_wc_path to svn_depth_t * mapping for depths 6098251881Speter of empty, immediates, and files. */ 6099251881Speter apr_hash_t *shallow_subtrees; 6100251881Speter 6101251881Speter /* const char *absolute_wc_path to the same, for all paths missing 6102251881Speter from the working copy. */ 6103251881Speter apr_hash_t *missing_subtrees; 6104251881Speter 6105251881Speter /* const char *absolute_wc_path const char * repos relative path, describing 6106251881Speter the root of each switched subtree in the working copy and the repository 6107251881Speter relative path it is switched to. */ 6108251881Speter apr_hash_t *switched_subtrees; 6109251881Speter 6110251881Speter /* A pool to allocate additions to the above hashes in. */ 6111251881Speter apr_pool_t *pool; 6112251881Speter}; 6113251881Speter 6114251881Speter/* A svn_wc_status_func4_t callback used by get_mergeinfo_paths to gather 6115251881Speter all switched, depth filtered and missing subtrees under a merge target. 6116251881Speter 6117251881Speter Note that this doesn't see server and user excluded trees. */ 6118251881Speterstatic svn_error_t * 6119251881Speterpre_merge_status_cb(void *baton, 6120251881Speter const char *local_abspath, 6121251881Speter const svn_wc_status3_t *status, 6122251881Speter apr_pool_t *scratch_pool) 6123251881Speter{ 6124251881Speter struct pre_merge_status_baton_t *pmsb = baton; 6125251881Speter 6126251881Speter if (status->switched && !status->file_external) 6127251881Speter { 6128251881Speter store_path(pmsb->switched_subtrees, local_abspath); 6129251881Speter } 6130251881Speter 6131251881Speter if (status->depth == svn_depth_empty 6132251881Speter || status->depth == svn_depth_files) 6133251881Speter { 6134251881Speter const char *dup_abspath; 6135251881Speter svn_depth_t *depth = apr_pmemdup(pmsb->pool, &status->depth, 6136251881Speter sizeof *depth); 6137251881Speter 6138251881Speter dup_abspath = apr_pstrdup(pmsb->pool, local_abspath); 6139251881Speter 6140251881Speter svn_hash_sets(pmsb->shallow_subtrees, dup_abspath, depth); 6141251881Speter } 6142251881Speter 6143251881Speter if (status->node_status == svn_wc_status_missing) 6144251881Speter { 6145251881Speter svn_boolean_t new_missing_root = TRUE; 6146251881Speter apr_hash_index_t *hi; 6147251881Speter 6148251881Speter for (hi = apr_hash_first(scratch_pool, pmsb->missing_subtrees); 6149251881Speter hi; 6150251881Speter hi = apr_hash_next(hi)) 6151251881Speter { 6152251881Speter const char *missing_root_path = svn__apr_hash_index_key(hi); 6153251881Speter 6154251881Speter if (svn_dirent_is_ancestor(missing_root_path, 6155251881Speter local_abspath)) 6156251881Speter { 6157251881Speter new_missing_root = FALSE; 6158251881Speter break; 6159251881Speter } 6160251881Speter } 6161251881Speter 6162251881Speter if (new_missing_root) 6163251881Speter store_path(pmsb->missing_subtrees, local_abspath); 6164251881Speter } 6165251881Speter 6166251881Speter return SVN_NO_ERROR; 6167251881Speter} 6168251881Speter 6169251881Speter/* Find all the subtrees in the working copy tree rooted at TARGET_ABSPATH 6170251881Speter * that have explicit mergeinfo. 6171251881Speter * Set *SUBTREES_WITH_MERGEINFO to a hash mapping (const char *) absolute 6172251881Speter * WC path to (svn_mergeinfo_t *) mergeinfo. 6173251881Speter * 6174251881Speter * ### Is this function equivalent to: 6175251881Speter * 6176251881Speter * svn_client__get_wc_mergeinfo_catalog( 6177251881Speter * subtrees_with_mergeinfo, inherited=NULL, include_descendants=TRUE, 6178251881Speter * svn_mergeinfo_explicit, target_abspath, limit_path=NULL, 6179251881Speter * walked_path=NULL, ignore_invalid_mergeinfo=FALSE, ...) 6180251881Speter * 6181251881Speter * except for the catalog keys being abspaths instead of repo-relpaths? 6182251881Speter */ 6183251881Speterstatic svn_error_t * 6184251881Speterget_wc_explicit_mergeinfo_catalog(apr_hash_t **subtrees_with_mergeinfo, 6185251881Speter const char *target_abspath, 6186251881Speter svn_depth_t depth, 6187251881Speter svn_client_ctx_t *ctx, 6188251881Speter apr_pool_t *result_pool, 6189251881Speter apr_pool_t *scratch_pool) 6190251881Speter{ 6191251881Speter svn_opt_revision_t working_revision = { svn_opt_revision_working, { 0 } }; 6192251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 6193251881Speter apr_hash_index_t *hi; 6194251881Speter apr_hash_t *externals; 6195251881Speter 6196251881Speter SVN_ERR(svn_client_propget5(subtrees_with_mergeinfo, NULL, 6197251881Speter SVN_PROP_MERGEINFO, target_abspath, 6198251881Speter &working_revision, &working_revision, NULL, 6199251881Speter depth, NULL, ctx, result_pool, scratch_pool)); 6200251881Speter 6201251881Speter SVN_ERR(svn_wc__externals_defined_below(&externals, ctx->wc_ctx, 6202251881Speter target_abspath, scratch_pool, 6203251881Speter scratch_pool)); 6204251881Speter 6205251881Speter /* Convert property values to svn_mergeinfo_t. */ 6206251881Speter for (hi = apr_hash_first(scratch_pool, *subtrees_with_mergeinfo); 6207251881Speter hi; 6208251881Speter hi = apr_hash_next(hi)) 6209251881Speter { 6210251881Speter const char *wc_path = svn__apr_hash_index_key(hi); 6211251881Speter svn_string_t *mergeinfo_string = svn__apr_hash_index_val(hi); 6212251881Speter svn_mergeinfo_t mergeinfo; 6213251881Speter svn_error_t *err; 6214251881Speter 6215251881Speter /* svn_client_propget5 picks up file externals with 6216251881Speter mergeinfo, but we don't want those. */ 6217251881Speter if (svn_hash_gets(externals, wc_path)) 6218251881Speter { 6219251881Speter svn_hash_sets(*subtrees_with_mergeinfo, wc_path, NULL); 6220251881Speter continue; 6221251881Speter } 6222251881Speter 6223251881Speter svn_pool_clear(iterpool); 6224251881Speter 6225251881Speter err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_string->data, 6226251881Speter result_pool); 6227251881Speter if (err) 6228251881Speter { 6229251881Speter if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) 6230251881Speter { 6231251881Speter err = svn_error_createf( 6232251881Speter SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err, 6233251881Speter _("Invalid mergeinfo detected on '%s', " 6234251881Speter "merge tracking not possible"), 6235251881Speter svn_dirent_local_style(wc_path, scratch_pool)); 6236251881Speter } 6237251881Speter return svn_error_trace(err); 6238251881Speter } 6239251881Speter svn_hash_sets(*subtrees_with_mergeinfo, wc_path, mergeinfo); 6240251881Speter } 6241251881Speter svn_pool_destroy(iterpool); 6242251881Speter 6243251881Speter return SVN_NO_ERROR; 6244251881Speter} 6245251881Speter 6246251881Speter/* Helper for do_directory_merge() when performing merge-tracking aware 6247251881Speter merges. 6248251881Speter 6249251881Speter Walk of the working copy tree rooted at TARGET->abspath to 6250251881Speter depth DEPTH. Create an svn_client__merge_path_t * for any path which meets 6251251881Speter one or more of the following criteria: 6252251881Speter 6253251881Speter 1) Path has working svn:mergeinfo. 6254251881Speter 2) Path is switched. 6255251881Speter 3) Path is a subtree of the merge target (i.e. is not equal to 6256251881Speter TARGET->abspath) and has no mergeinfo of its own but 6257251881Speter its immediate parent has mergeinfo with non-inheritable ranges. If 6258251881Speter this isn't a dry-run and the merge is between differences in the same 6259251881Speter repository, then this function will set working mergeinfo on the path 6260251881Speter equal to the mergeinfo inheritable from its parent. 6261251881Speter 4) Path has an immediate child (or children) missing from the WC because 6262251881Speter the child is switched or absent from the WC, or due to a sparse 6263251881Speter checkout. 6264251881Speter 5) Path has a sibling (or siblings) missing from the WC because the 6265251881Speter sibling is switched, absent, scheduled for deletion, or missing due to 6266251881Speter a sparse checkout. 6267251881Speter 6) Path is absent from disk due to an authz restriction. 6268251881Speter 7) Path is equal to TARGET->abspath. 6269251881Speter 8) Path is an immediate *directory* child of 6270251881Speter TARGET->abspath and DEPTH is svn_depth_immediates. 6271251881Speter 9) Path is an immediate *file* child of TARGET->abspath 6272251881Speter and DEPTH is svn_depth_files. 6273251881Speter 10) Path is at a depth of 'empty' or 'files'. 6274251881Speter 11) Path is missing from disk (e.g. due to an OS-level deletion). 6275251881Speter 6276251881Speter If subtrees within the requested DEPTH are unexpectedly missing disk, 6277251881Speter then raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE. 6278251881Speter 6279251881Speter Store the svn_client__merge_path_t *'s in *CHILDREN_WITH_MERGEINFO in 6280251881Speter depth-first order based on the svn_client__merge_path_t *s path member as 6281251881Speter sorted by svn_path_compare_paths(). Set the remaining_ranges field of each 6282251881Speter element to NULL. 6283251881Speter 6284251881Speter Note: Since the walk is rooted at TARGET->abspath, the 6285251881Speter latter is guaranteed to be in *CHILDREN_WITH_MERGEINFO and due to the 6286251881Speter depth-first ordering it is guaranteed to be the first element in 6287251881Speter *CHILDREN_WITH_MERGEINFO. 6288251881Speter 6289251881Speter MERGE_CMD_BATON is cascaded from the argument of the same name in 6290251881Speter do_directory_merge(). 6291251881Speter*/ 6292251881Speterstatic svn_error_t * 6293251881Speterget_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo, 6294251881Speter const merge_target_t *target, 6295251881Speter svn_depth_t depth, 6296251881Speter svn_boolean_t dry_run, 6297251881Speter svn_boolean_t same_repos, 6298251881Speter svn_client_ctx_t *ctx, 6299251881Speter apr_pool_t *result_pool, 6300251881Speter apr_pool_t *scratch_pool) 6301251881Speter{ 6302251881Speter int i; 6303251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 6304251881Speter apr_hash_t *subtrees_with_mergeinfo; 6305251881Speter apr_hash_t *excluded_subtrees; 6306251881Speter apr_hash_t *switched_subtrees; 6307251881Speter apr_hash_t *shallow_subtrees; 6308251881Speter apr_hash_t *missing_subtrees; 6309251881Speter struct pre_merge_status_baton_t pre_merge_status_baton; 6310251881Speter 6311251881Speter /* Case 1: Subtrees with explicit mergeinfo. */ 6312251881Speter SVN_ERR(get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo, 6313251881Speter target->abspath, 6314251881Speter depth, ctx, 6315251881Speter result_pool, scratch_pool)); 6316251881Speter if (subtrees_with_mergeinfo) 6317251881Speter { 6318251881Speter apr_hash_index_t *hi; 6319251881Speter 6320251881Speter for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo); 6321251881Speter hi; 6322251881Speter hi = apr_hash_next(hi)) 6323251881Speter { 6324251881Speter const char *wc_path = svn__apr_hash_index_key(hi); 6325251881Speter svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi); 6326251881Speter svn_client__merge_path_t *mergeinfo_child = 6327251881Speter svn_client__merge_path_create(wc_path, result_pool); 6328251881Speter 6329251881Speter svn_pool_clear(iterpool); 6330251881Speter 6331251881Speter /* Stash this child's pre-existing mergeinfo. */ 6332251881Speter mergeinfo_child->pre_merge_mergeinfo = mergeinfo; 6333251881Speter 6334251881Speter /* Note if this child has non-inheritable mergeinfo */ 6335251881Speter mergeinfo_child->has_noninheritable 6336251881Speter = svn_mergeinfo__is_noninheritable( 6337251881Speter mergeinfo_child->pre_merge_mergeinfo, iterpool); 6338251881Speter 6339251881Speter /* Append it. We'll sort below. */ 6340251881Speter APR_ARRAY_PUSH(children_with_mergeinfo, svn_client__merge_path_t *) 6341251881Speter = svn_client__merge_path_dup(mergeinfo_child, result_pool); 6342251881Speter } 6343251881Speter 6344251881Speter /* Sort CHILDREN_WITH_MERGEINFO by each child's path (i.e. as per 6345251881Speter compare_merge_path_t_as_paths). Any subsequent insertions of new 6346251881Speter children with insert_child_to_merge() require this ordering. */ 6347251881Speter qsort(children_with_mergeinfo->elts, 6348251881Speter children_with_mergeinfo->nelts, 6349251881Speter children_with_mergeinfo->elt_size, 6350251881Speter compare_merge_path_t_as_paths); 6351251881Speter } 6352251881Speter 6353251881Speter /* Case 2: Switched subtrees 6354251881Speter Case 10: Paths at depths of 'empty' or 'files' 6355251881Speter Case 11: Paths missing from disk */ 6356251881Speter pre_merge_status_baton.wc_ctx = ctx->wc_ctx; 6357251881Speter switched_subtrees = apr_hash_make(scratch_pool); 6358251881Speter pre_merge_status_baton.switched_subtrees = switched_subtrees; 6359251881Speter shallow_subtrees = apr_hash_make(scratch_pool); 6360251881Speter pre_merge_status_baton.shallow_subtrees = shallow_subtrees; 6361251881Speter missing_subtrees = apr_hash_make(scratch_pool); 6362251881Speter pre_merge_status_baton.missing_subtrees = missing_subtrees; 6363251881Speter pre_merge_status_baton.pool = scratch_pool; 6364251881Speter SVN_ERR(svn_wc_walk_status(ctx->wc_ctx, 6365251881Speter target->abspath, 6366251881Speter depth, 6367251881Speter TRUE /* get_all */, 6368251881Speter FALSE /* no_ignore */, 6369251881Speter TRUE /* ignore_text_mods */, 6370251881Speter NULL /* ingore_patterns */, 6371251881Speter pre_merge_status_cb, &pre_merge_status_baton, 6372251881Speter ctx->cancel_func, ctx->cancel_baton, 6373251881Speter scratch_pool)); 6374251881Speter 6375251881Speter /* Issue #2915: Raise an error describing the roots of any missing 6376251881Speter subtrees, i.e. those that the WC thinks are on disk but have been 6377251881Speter removed outside of Subversion. */ 6378251881Speter if (apr_hash_count(missing_subtrees)) 6379251881Speter { 6380251881Speter apr_hash_index_t *hi; 6381251881Speter svn_stringbuf_t *missing_subtree_err_buf = 6382251881Speter svn_stringbuf_create(_("Merge tracking not allowed with missing " 6383251881Speter "subtrees; try restoring these items " 6384251881Speter "first:\n"), scratch_pool); 6385251881Speter 6386251881Speter for (hi = apr_hash_first(scratch_pool, missing_subtrees); 6387251881Speter hi; 6388251881Speter hi = apr_hash_next(hi)) 6389251881Speter { 6390251881Speter svn_pool_clear(iterpool); 6391251881Speter svn_stringbuf_appendcstr(missing_subtree_err_buf, 6392251881Speter svn_dirent_local_style( 6393251881Speter svn__apr_hash_index_key(hi), iterpool)); 6394251881Speter svn_stringbuf_appendcstr(missing_subtree_err_buf, "\n"); 6395251881Speter } 6396251881Speter 6397251881Speter return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, 6398251881Speter NULL, missing_subtree_err_buf->data); 6399251881Speter } 6400251881Speter 6401251881Speter if (apr_hash_count(switched_subtrees)) 6402251881Speter { 6403251881Speter apr_hash_index_t *hi; 6404251881Speter 6405251881Speter for (hi = apr_hash_first(scratch_pool, switched_subtrees); 6406251881Speter hi; 6407251881Speter hi = apr_hash_next(hi)) 6408251881Speter { 6409251881Speter const char *wc_path = svn__apr_hash_index_key(hi); 6410251881Speter svn_client__merge_path_t *child = get_child_with_mergeinfo( 6411251881Speter children_with_mergeinfo, wc_path); 6412251881Speter 6413251881Speter if (child) 6414251881Speter { 6415251881Speter child->switched = TRUE; 6416251881Speter } 6417251881Speter else 6418251881Speter { 6419251881Speter svn_client__merge_path_t *switched_child = 6420251881Speter svn_client__merge_path_create(wc_path, result_pool); 6421251881Speter switched_child->switched = TRUE; 6422251881Speter insert_child_to_merge(children_with_mergeinfo, switched_child, 6423251881Speter result_pool); 6424251881Speter } 6425251881Speter } 6426251881Speter } 6427251881Speter 6428251881Speter if (apr_hash_count(shallow_subtrees)) 6429251881Speter { 6430251881Speter apr_hash_index_t *hi; 6431251881Speter 6432251881Speter for (hi = apr_hash_first(scratch_pool, shallow_subtrees); 6433251881Speter hi; 6434251881Speter hi = apr_hash_next(hi)) 6435251881Speter { 6436251881Speter svn_boolean_t new_shallow_child = FALSE; 6437251881Speter const char *wc_path = svn__apr_hash_index_key(hi); 6438251881Speter svn_depth_t *child_depth = svn__apr_hash_index_val(hi); 6439251881Speter svn_client__merge_path_t *shallow_child = get_child_with_mergeinfo( 6440251881Speter children_with_mergeinfo, wc_path); 6441251881Speter 6442251881Speter if (shallow_child) 6443251881Speter { 6444251881Speter if (*child_depth == svn_depth_empty 6445251881Speter || *child_depth == svn_depth_files) 6446251881Speter shallow_child->missing_child = TRUE; 6447251881Speter } 6448251881Speter else 6449251881Speter { 6450251881Speter shallow_child = svn_client__merge_path_create(wc_path, 6451251881Speter result_pool); 6452251881Speter new_shallow_child = TRUE; 6453251881Speter 6454251881Speter if (*child_depth == svn_depth_empty 6455251881Speter || *child_depth == svn_depth_files) 6456251881Speter shallow_child->missing_child = TRUE; 6457251881Speter } 6458251881Speter 6459251881Speter /* A little trickery: If PATH doesn't have any mergeinfo or has 6460251881Speter only inheritable mergeinfo, we still describe it as having 6461251881Speter non-inheritable mergeinfo if it is missing a child due to 6462251881Speter a shallow depth. Why? Because the mergeinfo we'll add to PATH 6463251881Speter to describe the merge must be non-inheritable, so PATH's missing 6464251881Speter children don't inherit it. Marking these PATHs as non- 6465251881Speter inheritable allows the logic for case 3 to properly account 6466251881Speter for PATH's children. */ 6467251881Speter if (!shallow_child->has_noninheritable 6468251881Speter && (*child_depth == svn_depth_empty 6469251881Speter || *child_depth == svn_depth_files)) 6470251881Speter { 6471251881Speter shallow_child->has_noninheritable = TRUE; 6472251881Speter } 6473251881Speter 6474251881Speter if (new_shallow_child) 6475251881Speter insert_child_to_merge(children_with_mergeinfo, shallow_child, 6476251881Speter result_pool); 6477251881Speter } 6478251881Speter } 6479251881Speter 6480251881Speter /* Case 6: Paths absent from disk due to server or user exclusion. */ 6481251881Speter SVN_ERR(svn_wc__get_excluded_subtrees(&excluded_subtrees, 6482251881Speter ctx->wc_ctx, target->abspath, 6483251881Speter result_pool, scratch_pool)); 6484251881Speter if (excluded_subtrees) 6485251881Speter { 6486251881Speter apr_hash_index_t *hi; 6487251881Speter 6488251881Speter for (hi = apr_hash_first(scratch_pool, excluded_subtrees); 6489251881Speter hi; 6490251881Speter hi = apr_hash_next(hi)) 6491251881Speter { 6492251881Speter const char *wc_path = svn__apr_hash_index_key(hi); 6493251881Speter svn_client__merge_path_t *child = get_child_with_mergeinfo( 6494251881Speter children_with_mergeinfo, wc_path); 6495251881Speter 6496251881Speter if (child) 6497251881Speter { 6498251881Speter child->absent = TRUE; 6499251881Speter } 6500251881Speter else 6501251881Speter { 6502251881Speter svn_client__merge_path_t *absent_child = 6503251881Speter svn_client__merge_path_create(wc_path, result_pool); 6504251881Speter absent_child->absent = TRUE; 6505251881Speter insert_child_to_merge(children_with_mergeinfo, absent_child, 6506251881Speter result_pool); 6507251881Speter } 6508251881Speter } 6509251881Speter } 6510251881Speter 6511251881Speter /* Case 7: The merge target MERGE_CMD_BATON->target->abspath is always 6512251881Speter present. */ 6513251881Speter if (!get_child_with_mergeinfo(children_with_mergeinfo, 6514251881Speter target->abspath)) 6515251881Speter { 6516251881Speter svn_client__merge_path_t *target_child = 6517251881Speter svn_client__merge_path_create(target->abspath, 6518251881Speter result_pool); 6519251881Speter insert_child_to_merge(children_with_mergeinfo, target_child, 6520251881Speter result_pool); 6521251881Speter } 6522251881Speter 6523251881Speter /* Case 8: Path is an immediate *directory* child of 6524251881Speter MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_immediates. 6525251881Speter 6526251881Speter Case 9: Path is an immediate *file* child of 6527251881Speter MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_files. */ 6528251881Speter if (depth == svn_depth_immediates || depth == svn_depth_files) 6529251881Speter { 6530251881Speter int j; 6531251881Speter const apr_array_header_t *immediate_children; 6532251881Speter 6533251881Speter SVN_ERR(svn_wc__node_get_children_of_working_node( 6534251881Speter &immediate_children, ctx->wc_ctx, 6535251881Speter target->abspath, FALSE, scratch_pool, scratch_pool)); 6536251881Speter 6537251881Speter for (j = 0; j < immediate_children->nelts; j++) 6538251881Speter { 6539251881Speter const char *immediate_child_abspath = 6540251881Speter APR_ARRAY_IDX(immediate_children, j, const char *); 6541251881Speter svn_node_kind_t immediate_child_kind; 6542251881Speter 6543251881Speter svn_pool_clear(iterpool); 6544251881Speter SVN_ERR(svn_wc_read_kind2(&immediate_child_kind, 6545251881Speter ctx->wc_ctx, immediate_child_abspath, 6546251881Speter FALSE, FALSE, iterpool)); 6547251881Speter if ((immediate_child_kind == svn_node_dir 6548251881Speter && depth == svn_depth_immediates) 6549251881Speter || (immediate_child_kind == svn_node_file 6550251881Speter && depth == svn_depth_files)) 6551251881Speter { 6552251881Speter if (!get_child_with_mergeinfo(children_with_mergeinfo, 6553251881Speter immediate_child_abspath)) 6554251881Speter { 6555251881Speter svn_client__merge_path_t *immediate_child = 6556251881Speter svn_client__merge_path_create(immediate_child_abspath, 6557251881Speter result_pool); 6558251881Speter 6559251881Speter if (immediate_child_kind == svn_node_dir 6560251881Speter && depth == svn_depth_immediates) 6561251881Speter immediate_child->immediate_child_dir = TRUE; 6562251881Speter 6563251881Speter insert_child_to_merge(children_with_mergeinfo, 6564251881Speter immediate_child, result_pool); 6565251881Speter } 6566251881Speter } 6567251881Speter } 6568251881Speter } 6569251881Speter 6570251881Speter /* If DEPTH isn't empty then cover cases 3), 4), and 5), possibly adding 6571251881Speter elements to CHILDREN_WITH_MERGEINFO. */ 6572251881Speter if (depth <= svn_depth_empty) 6573251881Speter return SVN_NO_ERROR; 6574251881Speter 6575251881Speter for (i = 0; i < children_with_mergeinfo->nelts; i++) 6576251881Speter { 6577251881Speter svn_client__merge_path_t *child = 6578251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, 6579251881Speter svn_client__merge_path_t *); 6580251881Speter svn_pool_clear(iterpool); 6581251881Speter 6582251881Speter /* Case 3) Where merging to a path with a switched child the path 6583251881Speter gets non-inheritable mergeinfo for the merge range performed and 6584251881Speter the child gets its own set of mergeinfo. If the switched child 6585251881Speter later "returns", e.g. a switched path is unswitched, the child 6586251881Speter may not have any explicit mergeinfo. If the initial merge is 6587251881Speter repeated we don't want to repeat the merge for the path, but we 6588251881Speter do want to repeat it for the previously switched child. To 6589251881Speter ensure this we check if all of CHILD's non-missing children have 6590251881Speter explicit mergeinfo (they should already be present in 6591251881Speter CHILDREN_WITH_MERGEINFO if they do). If not, 6592251881Speter add the children without mergeinfo to CHILDREN_WITH_MERGEINFO so 6593251881Speter do_directory_merge() will merge them independently. 6594251881Speter 6595251881Speter But that's not enough! Since do_directory_merge() performs 6596251881Speter the merges on the paths in CHILDREN_WITH_MERGEINFO in a depth 6597251881Speter first manner it will merge the previously switched path's parent 6598251881Speter first. As part of this merge it will update the parent's 6599251881Speter previously non-inheritable mergeinfo and make it inheritable 6600251881Speter (since it notices the path has no missing children), then when 6601251881Speter do_directory_merge() finally merges the previously missing 6602251881Speter child it needs to get mergeinfo from the child's nearest 6603251881Speter ancestor, but since do_directory_merge() already tweaked that 6604251881Speter mergeinfo, removing the non-inheritable flag, it appears that the 6605251881Speter child already has been merged to. To prevent this we set 6606251881Speter override mergeinfo on the child now, before any merging is done, 6607251881Speter so it has explicit mergeinfo that reflects only CHILD's 6608251881Speter inheritable mergeinfo. */ 6609251881Speter 6610251881Speter /* If depth is immediates or files then don't add new children if 6611251881Speter CHILD is a subtree of the merge target; those children are below 6612251881Speter the operational depth of the merge. */ 6613251881Speter if (child->has_noninheritable 6614251881Speter && (i == 0 || depth == svn_depth_infinity)) 6615251881Speter { 6616251881Speter const apr_array_header_t *children; 6617251881Speter int j; 6618251881Speter 6619251881Speter SVN_ERR(svn_wc__node_get_children(&children, 6620251881Speter ctx->wc_ctx, 6621251881Speter child->abspath, FALSE, 6622251881Speter iterpool, iterpool)); 6623251881Speter for (j = 0; j < children->nelts; j++) 6624251881Speter { 6625251881Speter svn_client__merge_path_t *child_of_noninheritable; 6626251881Speter const char *child_abspath = APR_ARRAY_IDX(children, j, 6627251881Speter const char*); 6628251881Speter 6629251881Speter /* Does this child already exist in CHILDREN_WITH_MERGEINFO? 6630251881Speter If not, create it and insert it into 6631251881Speter CHILDREN_WITH_MERGEINFO and set override mergeinfo on 6632251881Speter it. */ 6633251881Speter child_of_noninheritable = 6634251881Speter get_child_with_mergeinfo(children_with_mergeinfo, 6635251881Speter child_abspath); 6636251881Speter if (!child_of_noninheritable) 6637251881Speter { 6638251881Speter /* Don't add directory children if DEPTH 6639251881Speter is svn_depth_files. */ 6640251881Speter if (depth == svn_depth_files) 6641251881Speter { 6642251881Speter svn_node_kind_t child_kind; 6643251881Speter SVN_ERR(svn_wc_read_kind2(&child_kind, 6644251881Speter ctx->wc_ctx, child_abspath, 6645251881Speter FALSE, FALSE, iterpool)); 6646251881Speter if (child_kind != svn_node_file) 6647251881Speter continue; 6648251881Speter } 6649251881Speter /* else DEPTH is infinity or immediates so we want both 6650251881Speter directory and file children. */ 6651251881Speter 6652251881Speter child_of_noninheritable = 6653251881Speter svn_client__merge_path_create(child_abspath, result_pool); 6654251881Speter child_of_noninheritable->child_of_noninheritable = TRUE; 6655251881Speter insert_child_to_merge(children_with_mergeinfo, 6656251881Speter child_of_noninheritable, 6657251881Speter result_pool); 6658251881Speter if (!dry_run && same_repos) 6659251881Speter { 6660251881Speter svn_mergeinfo_t mergeinfo; 6661251881Speter 6662251881Speter SVN_ERR(svn_client__get_wc_mergeinfo( 6663251881Speter &mergeinfo, NULL, 6664251881Speter svn_mergeinfo_nearest_ancestor, 6665251881Speter child_of_noninheritable->abspath, 6666251881Speter target->abspath, NULL, FALSE, 6667251881Speter ctx, iterpool, iterpool)); 6668251881Speter 6669251881Speter SVN_ERR(svn_client__record_wc_mergeinfo( 6670251881Speter child_of_noninheritable->abspath, mergeinfo, 6671251881Speter FALSE, ctx, iterpool)); 6672251881Speter } 6673251881Speter } 6674251881Speter } 6675251881Speter } 6676251881Speter /* Case 4 and 5 are handled by the following function. */ 6677251881Speter SVN_ERR(insert_parent_and_sibs_of_sw_absent_del_subtree( 6678251881Speter children_with_mergeinfo, target, &i, child, 6679251881Speter depth, ctx, result_pool)); 6680251881Speter } /* i < children_with_mergeinfo->nelts */ 6681251881Speter svn_pool_destroy(iterpool); 6682251881Speter 6683251881Speter return SVN_NO_ERROR; 6684251881Speter} 6685251881Speter 6686251881Speter 6687251881Speter/* Implements the svn_log_entry_receiver_t interface. 6688251881Speter * 6689251881Speter * BATON is an 'apr_array_header_t *' array of 'svn_revnum_t'. 6690251881Speter * Push a copy of LOG_ENTRY->revision onto BATON. Thus, a 6691251881Speter * series of invocations of this callback accumulates the 6692251881Speter * corresponding set of revisions into BATON. 6693251881Speter */ 6694251881Speterstatic svn_error_t * 6695251881Speterlog_changed_revs(void *baton, 6696251881Speter svn_log_entry_t *log_entry, 6697251881Speter apr_pool_t *pool) 6698251881Speter{ 6699251881Speter apr_array_header_t *revs = baton; 6700251881Speter 6701251881Speter APR_ARRAY_PUSH(revs, svn_revnum_t) = log_entry->revision; 6702251881Speter return SVN_NO_ERROR; 6703251881Speter} 6704251881Speter 6705251881Speter 6706251881Speter/* Set *MIN_REV_P to the oldest and *MAX_REV_P to the youngest start or end 6707251881Speter * revision occurring in RANGELIST, or to SVN_INVALID_REVNUM if RANGELIST 6708251881Speter * is empty. */ 6709251881Speterstatic void 6710251881Spetermerge_range_find_extremes(svn_revnum_t *min_rev_p, 6711251881Speter svn_revnum_t *max_rev_p, 6712251881Speter const svn_rangelist_t *rangelist) 6713251881Speter{ 6714251881Speter int i; 6715251881Speter 6716251881Speter *min_rev_p = SVN_INVALID_REVNUM; 6717251881Speter *max_rev_p = SVN_INVALID_REVNUM; 6718251881Speter for (i = 0; i < rangelist->nelts; i++) 6719251881Speter { 6720251881Speter svn_merge_range_t *range 6721251881Speter = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 6722251881Speter svn_revnum_t range_min = MIN(range->start, range->end); 6723251881Speter svn_revnum_t range_max = MAX(range->start, range->end); 6724251881Speter 6725251881Speter if ((! SVN_IS_VALID_REVNUM(*min_rev_p)) || (range_min < *min_rev_p)) 6726251881Speter *min_rev_p = range_min; 6727251881Speter if ((! SVN_IS_VALID_REVNUM(*max_rev_p)) || (range_max > *max_rev_p)) 6728251881Speter *max_rev_p = range_max; 6729251881Speter } 6730251881Speter} 6731251881Speter 6732251881Speter/* Wrapper around svn_ra_get_log2(). Invoke RECEIVER with RECEIVER_BATON 6733251881Speter * on each commit from YOUNGEST_REV to OLDEST_REV in which TARGET_RELPATH 6734251881Speter * changed. TARGET_RELPATH is relative to RA_SESSION's URL. 6735251881Speter * Important: Revision properties are not retrieved by this function for 6736251881Speter * performance reasons. 6737251881Speter */ 6738251881Speterstatic svn_error_t * 6739251881Speterget_log(svn_ra_session_t *ra_session, 6740251881Speter const char *target_relpath, 6741251881Speter svn_revnum_t youngest_rev, 6742251881Speter svn_revnum_t oldest_rev, 6743251881Speter svn_boolean_t discover_changed_paths, 6744251881Speter svn_log_entry_receiver_t receiver, 6745251881Speter void *receiver_baton, 6746251881Speter apr_pool_t *pool) 6747251881Speter{ 6748251881Speter apr_array_header_t *log_targets; 6749251881Speter apr_array_header_t *revprops; 6750251881Speter 6751251881Speter log_targets = apr_array_make(pool, 1, sizeof(const char *)); 6752251881Speter APR_ARRAY_PUSH(log_targets, const char *) = target_relpath; 6753251881Speter 6754251881Speter revprops = apr_array_make(pool, 0, sizeof(const char *)); 6755251881Speter 6756251881Speter SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev, 6757251881Speter oldest_rev, 0 /* limit */, discover_changed_paths, 6758251881Speter FALSE /* strict_node_history */, 6759251881Speter FALSE /* include_merged_revisions */, 6760251881Speter revprops, receiver, receiver_baton, pool)); 6761251881Speter 6762251881Speter return SVN_NO_ERROR; 6763251881Speter} 6764251881Speter 6765251881Speter/* Set *OPERATIVE_RANGES_P to an array of svn_merge_range_t * merge 6766251881Speter range objects copied wholesale from RANGES which have the property 6767251881Speter that in some revision within that range the object identified by 6768251881Speter RA_SESSION was modified (if by "modified" we mean "'svn log' would 6769251881Speter return that revision). *OPERATIVE_RANGES_P is allocated from the 6770251881Speter same pool as RANGES, and the ranges within it are shared with 6771251881Speter RANGES, too. 6772251881Speter 6773251881Speter *OPERATIVE_RANGES_P may be the same as RANGES (that is, the output 6774251881Speter parameter is set only after the input is no longer used). 6775251881Speter 6776251881Speter Use POOL for temporary allocations. */ 6777251881Speterstatic svn_error_t * 6778251881Speterremove_noop_merge_ranges(svn_rangelist_t **operative_ranges_p, 6779251881Speter svn_ra_session_t *ra_session, 6780251881Speter const svn_rangelist_t *ranges, 6781251881Speter apr_pool_t *pool) 6782251881Speter{ 6783251881Speter int i; 6784251881Speter svn_revnum_t oldest_rev, youngest_rev; 6785251881Speter apr_array_header_t *changed_revs = 6786251881Speter apr_array_make(pool, ranges->nelts, sizeof(svn_revnum_t)); 6787251881Speter svn_rangelist_t *operative_ranges = 6788251881Speter apr_array_make(ranges->pool, ranges->nelts, ranges->elt_size); 6789251881Speter 6790251881Speter /* Find the revision extremes of the RANGES we have. */ 6791251881Speter merge_range_find_extremes(&oldest_rev, &youngest_rev, ranges); 6792251881Speter if (SVN_IS_VALID_REVNUM(oldest_rev)) 6793251881Speter oldest_rev++; /* make it inclusive */ 6794251881Speter 6795251881Speter /* Get logs across those ranges, recording which revisions hold 6796251881Speter changes to our object's history. */ 6797251881Speter SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev, FALSE, 6798251881Speter log_changed_revs, changed_revs, pool)); 6799251881Speter 6800251881Speter /* Are there *any* changes? */ 6801251881Speter if (changed_revs->nelts) 6802251881Speter { 6803251881Speter /* Our list of changed revisions should be in youngest-to-oldest 6804251881Speter order. */ 6805251881Speter svn_revnum_t youngest_changed_rev 6806251881Speter = APR_ARRAY_IDX(changed_revs, 0, svn_revnum_t); 6807251881Speter svn_revnum_t oldest_changed_rev 6808251881Speter = APR_ARRAY_IDX(changed_revs, changed_revs->nelts - 1, svn_revnum_t); 6809251881Speter 6810251881Speter /* Now, copy from RANGES to *OPERATIVE_RANGES, filtering out ranges 6811251881Speter that aren't operative (by virtue of not having any revisions 6812251881Speter represented in the CHANGED_REVS array). */ 6813251881Speter for (i = 0; i < ranges->nelts; i++) 6814251881Speter { 6815251881Speter svn_merge_range_t *range = APR_ARRAY_IDX(ranges, i, 6816251881Speter svn_merge_range_t *); 6817251881Speter svn_revnum_t range_min = MIN(range->start, range->end) + 1; 6818251881Speter svn_revnum_t range_max = MAX(range->start, range->end); 6819251881Speter int j; 6820251881Speter 6821251881Speter /* If the merge range is entirely outside the range of changed 6822251881Speter revisions, we've no use for it. */ 6823251881Speter if ((range_min > youngest_changed_rev) 6824251881Speter || (range_max < oldest_changed_rev)) 6825251881Speter continue; 6826251881Speter 6827251881Speter /* Walk through the changed_revs to see if any of them fall 6828251881Speter inside our current range. */ 6829251881Speter for (j = 0; j < changed_revs->nelts; j++) 6830251881Speter { 6831251881Speter svn_revnum_t changed_rev 6832251881Speter = APR_ARRAY_IDX(changed_revs, j, svn_revnum_t); 6833251881Speter if ((changed_rev >= range_min) && (changed_rev <= range_max)) 6834251881Speter { 6835251881Speter APR_ARRAY_PUSH(operative_ranges, svn_merge_range_t *) = 6836251881Speter range; 6837251881Speter break; 6838251881Speter } 6839251881Speter } 6840251881Speter } 6841251881Speter } 6842251881Speter 6843251881Speter *operative_ranges_p = operative_ranges; 6844251881Speter return SVN_NO_ERROR; 6845251881Speter} 6846251881Speter 6847251881Speter 6848251881Speter/*-----------------------------------------------------------------------*/ 6849251881Speter 6850251881Speter/*** Merge Source Normalization ***/ 6851251881Speter 6852251881Speter/* qsort-compatible sort routine, rating merge_source_t * objects to 6853251881Speter be in descending (youngest-to-oldest) order based on their ->loc1->rev 6854251881Speter component. */ 6855251881Speterstatic int 6856251881Spetercompare_merge_source_ts(const void *a, 6857251881Speter const void *b) 6858251881Speter{ 6859251881Speter svn_revnum_t a_rev = (*(const merge_source_t *const *)a)->loc1->rev; 6860251881Speter svn_revnum_t b_rev = (*(const merge_source_t *const *)b)->loc1->rev; 6861251881Speter if (a_rev == b_rev) 6862251881Speter return 0; 6863251881Speter return a_rev < b_rev ? 1 : -1; 6864251881Speter} 6865251881Speter 6866251881Speter/* Set *MERGE_SOURCE_TS_P to a list of merge sources generated by 6867251881Speter slicing history location SEGMENTS with a given requested merge 6868251881Speter RANGE. Use SOURCE_LOC for full source URL calculation. 6869251881Speter 6870251881Speter Order the merge sources in *MERGE_SOURCE_TS_P from oldest to 6871251881Speter youngest. */ 6872251881Speterstatic svn_error_t * 6873251881Spetercombine_range_with_segments(apr_array_header_t **merge_source_ts_p, 6874251881Speter const svn_merge_range_t *range, 6875251881Speter const apr_array_header_t *segments, 6876251881Speter const svn_client__pathrev_t *source_loc, 6877251881Speter apr_pool_t *pool) 6878251881Speter{ 6879251881Speter apr_array_header_t *merge_source_ts = 6880251881Speter apr_array_make(pool, 1, sizeof(merge_source_t *)); 6881251881Speter svn_revnum_t minrev = MIN(range->start, range->end) + 1; 6882251881Speter svn_revnum_t maxrev = MAX(range->start, range->end); 6883251881Speter svn_boolean_t subtractive = (range->start > range->end); 6884251881Speter int i; 6885251881Speter 6886251881Speter for (i = 0; i < segments->nelts; i++) 6887251881Speter { 6888251881Speter svn_location_segment_t *segment = 6889251881Speter APR_ARRAY_IDX(segments, i, svn_location_segment_t *); 6890251881Speter svn_client__pathrev_t *loc1, *loc2; 6891251881Speter merge_source_t *merge_source; 6892251881Speter const char *path1 = NULL; 6893251881Speter svn_revnum_t rev1; 6894251881Speter 6895251881Speter /* If this segment doesn't overlap our range at all, or 6896251881Speter represents a gap, ignore it. */ 6897251881Speter if ((segment->range_end < minrev) 6898251881Speter || (segment->range_start > maxrev) 6899251881Speter || (! segment->path)) 6900251881Speter continue; 6901251881Speter 6902251881Speter /* If our range spans a segment boundary, we have to point our 6903251881Speter merge_source_t's path1 to the path of the immediately older 6904251881Speter segment, else it points to the same location as its path2. */ 6905251881Speter rev1 = MAX(segment->range_start, minrev) - 1; 6906251881Speter if (minrev <= segment->range_start) 6907251881Speter { 6908251881Speter if (i > 0) 6909251881Speter { 6910251881Speter path1 = (APR_ARRAY_IDX(segments, i - 1, 6911251881Speter svn_location_segment_t *))->path; 6912251881Speter } 6913251881Speter /* If we've backed PATH1 up into a segment gap, let's back 6914251881Speter it up further still to the segment before the gap. We'll 6915251881Speter have to adjust rev1, too. */ 6916251881Speter if ((! path1) && (i > 1)) 6917251881Speter { 6918251881Speter path1 = (APR_ARRAY_IDX(segments, i - 2, 6919251881Speter svn_location_segment_t *))->path; 6920251881Speter rev1 = (APR_ARRAY_IDX(segments, i - 2, 6921251881Speter svn_location_segment_t *))->range_end; 6922251881Speter } 6923251881Speter } 6924251881Speter else 6925251881Speter { 6926251881Speter path1 = apr_pstrdup(pool, segment->path); 6927251881Speter } 6928251881Speter 6929251881Speter /* If we don't have two valid paths, we won't know what to do 6930251881Speter when merging. This could happen if someone requested a merge 6931251881Speter where the source didn't exist in a particular revision or 6932251881Speter something. The merge code would probably bomb out anyway, so 6933251881Speter we'll just *not* create a merge source in this case. */ 6934251881Speter if (! (path1 && segment->path)) 6935251881Speter continue; 6936251881Speter 6937251881Speter /* Build our merge source structure. */ 6938251881Speter loc1 = svn_client__pathrev_create_with_relpath( 6939251881Speter source_loc->repos_root_url, source_loc->repos_uuid, 6940251881Speter rev1, path1, pool); 6941251881Speter loc2 = svn_client__pathrev_create_with_relpath( 6942251881Speter source_loc->repos_root_url, source_loc->repos_uuid, 6943251881Speter MIN(segment->range_end, maxrev), segment->path, pool); 6944251881Speter /* If this is subtractive, reverse the whole calculation. */ 6945251881Speter if (subtractive) 6946251881Speter merge_source = merge_source_create(loc2, loc1, TRUE /* ancestral */, 6947251881Speter pool); 6948251881Speter else 6949251881Speter merge_source = merge_source_create(loc1, loc2, TRUE /* ancestral */, 6950251881Speter pool); 6951251881Speter 6952251881Speter APR_ARRAY_PUSH(merge_source_ts, merge_source_t *) = merge_source; 6953251881Speter } 6954251881Speter 6955251881Speter /* If this was a subtractive merge, and we created more than one 6956251881Speter merge source, we need to reverse the sort ordering of our sources. */ 6957251881Speter if (subtractive && (merge_source_ts->nelts > 1)) 6958251881Speter qsort(merge_source_ts->elts, merge_source_ts->nelts, 6959251881Speter merge_source_ts->elt_size, compare_merge_source_ts); 6960251881Speter 6961251881Speter *merge_source_ts_p = merge_source_ts; 6962251881Speter return SVN_NO_ERROR; 6963251881Speter} 6964251881Speter 6965251881Speter/* Similar to normalize_merge_sources() except the input MERGE_RANGE_TS is a 6966251881Speter * rangelist. 6967251881Speter */ 6968251881Speterstatic svn_error_t * 6969251881Speternormalize_merge_sources_internal(apr_array_header_t **merge_sources_p, 6970251881Speter const svn_client__pathrev_t *source_loc, 6971251881Speter const svn_rangelist_t *merge_range_ts, 6972251881Speter svn_ra_session_t *ra_session, 6973251881Speter svn_client_ctx_t *ctx, 6974251881Speter apr_pool_t *result_pool, 6975251881Speter apr_pool_t *scratch_pool) 6976251881Speter{ 6977251881Speter svn_revnum_t source_peg_revnum = source_loc->rev; 6978251881Speter svn_revnum_t oldest_requested, youngest_requested; 6979251881Speter svn_revnum_t trim_revision = SVN_INVALID_REVNUM; 6980251881Speter apr_array_header_t *segments; 6981251881Speter int i; 6982251881Speter 6983251881Speter /* Initialize our return variable. */ 6984251881Speter *merge_sources_p = apr_array_make(result_pool, 1, sizeof(merge_source_t *)); 6985251881Speter 6986251881Speter /* No ranges to merge? No problem. */ 6987251881Speter if (merge_range_ts->nelts == 0) 6988251881Speter return SVN_NO_ERROR; 6989251881Speter 6990251881Speter /* Find the extremes of the revisions across our set of ranges. */ 6991251881Speter merge_range_find_extremes(&oldest_requested, &youngest_requested, 6992251881Speter merge_range_ts); 6993251881Speter 6994251881Speter /* ### FIXME: Our underlying APIs can't yet handle the case where 6995251881Speter the peg revision isn't the youngest of the three revisions. So 6996251881Speter we'll just verify that the source in the peg revision is related 6997251881Speter to the source in the youngest requested revision (which is 6998251881Speter all the underlying APIs would do in this case right now anyway). */ 6999251881Speter if (source_peg_revnum < youngest_requested) 7000251881Speter { 7001251881Speter svn_client__pathrev_t *start_loc; 7002251881Speter 7003251881Speter SVN_ERR(svn_client__repos_location(&start_loc, 7004251881Speter ra_session, source_loc, 7005251881Speter youngest_requested, 7006251881Speter ctx, scratch_pool, scratch_pool)); 7007251881Speter source_peg_revnum = youngest_requested; 7008251881Speter } 7009251881Speter 7010251881Speter /* Fetch the locations for our merge range span. */ 7011251881Speter SVN_ERR(svn_client__repos_location_segments(&segments, 7012251881Speter ra_session, source_loc->url, 7013251881Speter source_peg_revnum, 7014251881Speter youngest_requested, 7015251881Speter oldest_requested, 7016251881Speter ctx, result_pool)); 7017251881Speter 7018251881Speter /* See if we fetched enough history to do the job. "Surely we did," 7019251881Speter you say. "After all, we covered the entire requested merge 7020251881Speter range." Yes, that's true, but if our first segment doesn't 7021251881Speter extend back to the oldest request revision, we've got a special 7022251881Speter case to deal with. Or if the first segment represents a gap, 7023251881Speter that's another special case. */ 7024251881Speter trim_revision = SVN_INVALID_REVNUM; 7025251881Speter if (segments->nelts) 7026251881Speter { 7027251881Speter svn_location_segment_t *first_segment = 7028251881Speter APR_ARRAY_IDX(segments, 0, svn_location_segment_t *); 7029251881Speter 7030251881Speter /* If the first segment doesn't start with the OLDEST_REQUESTED 7031251881Speter revision, we'll need to pass a trim revision to our range 7032251881Speter cruncher. */ 7033251881Speter if (first_segment->range_start != oldest_requested) 7034251881Speter { 7035251881Speter trim_revision = first_segment->range_start; 7036251881Speter } 7037251881Speter 7038251881Speter /* Else, if the first segment has no path (and therefore is a 7039251881Speter gap), then we'll fetch the copy source revision from the 7040251881Speter second segment (provided there is one, of course) and use it 7041251881Speter to prepend an extra pathful segment to our list. 7042251881Speter 7043251881Speter ### We could avoid this bit entirely if we'd passed 7044251881Speter ### SVN_INVALID_REVNUM instead of OLDEST_REQUESTED to 7045251881Speter ### svn_client__repos_location_segments(), but that would 7046251881Speter ### really penalize clients hitting pre-1.5 repositories with 7047251881Speter ### the typical small merge range request (because of the 7048251881Speter ### lack of a node-origins cache in the repository). */ 7049251881Speter else if (! first_segment->path) 7050251881Speter { 7051251881Speter if (segments->nelts > 1) 7052251881Speter { 7053251881Speter svn_location_segment_t *second_segment = 7054251881Speter APR_ARRAY_IDX(segments, 1, svn_location_segment_t *); 7055251881Speter const char *segment_url; 7056251881Speter const char *original_repos_relpath; 7057251881Speter svn_revnum_t original_revision; 7058251881Speter svn_opt_revision_t range_start_rev; 7059251881Speter range_start_rev.kind = svn_opt_revision_number; 7060251881Speter range_start_rev.value.number = second_segment->range_start; 7061251881Speter 7062251881Speter segment_url = svn_path_url_add_component2( 7063251881Speter source_loc->repos_root_url, second_segment->path, 7064251881Speter scratch_pool); 7065251881Speter SVN_ERR(svn_client__get_copy_source(&original_repos_relpath, 7066251881Speter &original_revision, 7067251881Speter segment_url, 7068251881Speter &range_start_rev, ctx, 7069251881Speter result_pool, scratch_pool)); 7070251881Speter /* Got copyfrom data? Fix up the first segment to cover 7071251881Speter back to COPYFROM_REV + 1, and then prepend a new 7072251881Speter segment covering just COPYFROM_REV. */ 7073251881Speter if (original_repos_relpath) 7074251881Speter { 7075251881Speter svn_location_segment_t *new_segment = 7076251881Speter apr_pcalloc(result_pool, sizeof(*new_segment)); 7077251881Speter 7078251881Speter new_segment->path = original_repos_relpath; 7079251881Speter new_segment->range_start = original_revision; 7080251881Speter new_segment->range_end = original_revision; 7081251881Speter svn_sort__array_insert(&new_segment, segments, 0); 7082251881Speter } 7083251881Speter } 7084251881Speter } 7085251881Speter } 7086251881Speter 7087251881Speter /* For each range in our requested range set, try to determine the 7088251881Speter path(s) associated with that range. */ 7089251881Speter for (i = 0; i < merge_range_ts->nelts; i++) 7090251881Speter { 7091251881Speter svn_merge_range_t *range = 7092251881Speter APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *); 7093251881Speter apr_array_header_t *merge_sources; 7094251881Speter 7095251881Speter if (SVN_IS_VALID_REVNUM(trim_revision)) 7096251881Speter { 7097251881Speter /* If the range predates the trim revision, discard it. */ 7098251881Speter if (MAX(range->start, range->end) < trim_revision) 7099251881Speter continue; 7100251881Speter 7101251881Speter /* If the range overlaps the trim revision, trim it. */ 7102251881Speter if (range->start < trim_revision) 7103251881Speter range->start = trim_revision; 7104251881Speter if (range->end < trim_revision) 7105251881Speter range->end = trim_revision; 7106251881Speter } 7107251881Speter 7108251881Speter /* Copy the resulting merge sources into master list thereof. */ 7109251881Speter SVN_ERR(combine_range_with_segments(&merge_sources, range, 7110251881Speter segments, source_loc, 7111251881Speter result_pool)); 7112251881Speter apr_array_cat(*merge_sources_p, merge_sources); 7113251881Speter } 7114251881Speter 7115251881Speter return SVN_NO_ERROR; 7116251881Speter} 7117251881Speter 7118251881Speter/* Determine the normalized ranges to merge from a given line of history. 7119251881Speter 7120251881Speter Calculate the result by intersecting the list of location segments at 7121251881Speter which SOURCE_LOC existed along its line of history with the requested 7122251881Speter revision ranges in RANGES_TO_MERGE. RANGES_TO_MERGE is an array of 7123251881Speter (svn_opt_revision_range_t *) revision ranges. Use SOURCE_PATH_OR_URL to 7124251881Speter resolve any WC-relative revision specifiers (such as 'base') in 7125251881Speter RANGES_TO_MERGE. 7126251881Speter 7127251881Speter Set *MERGE_SOURCES_P to an array of merge_source_t * objects, each 7128251881Speter describing a normalized range of revisions to be merged from the line 7129251881Speter history of SOURCE_LOC. Order the objects from oldest to youngest. 7130251881Speter 7131251881Speter RA_SESSION is an RA session open to the repository of SOURCE_LOC; it may 7132251881Speter be temporarily reparented within this function. Use RA_SESSION to find 7133251881Speter the location segments along the line of history of SOURCE_LOC. 7134251881Speter 7135251881Speter Allocate MERGE_SOURCES_P and its contents in RESULT_POOL. 7136251881Speter 7137251881Speter See `MERGEINFO MERGE SOURCE NORMALIZATION' for more on the 7138251881Speter background of this function. 7139251881Speter*/ 7140251881Speterstatic svn_error_t * 7141251881Speternormalize_merge_sources(apr_array_header_t **merge_sources_p, 7142251881Speter const char *source_path_or_url, 7143251881Speter const svn_client__pathrev_t *source_loc, 7144251881Speter const apr_array_header_t *ranges_to_merge, 7145251881Speter svn_ra_session_t *ra_session, 7146251881Speter svn_client_ctx_t *ctx, 7147251881Speter apr_pool_t *result_pool, 7148251881Speter apr_pool_t *scratch_pool) 7149251881Speter{ 7150251881Speter const char *source_abspath_or_url; 7151251881Speter svn_revnum_t youngest_rev = SVN_INVALID_REVNUM; 7152251881Speter svn_rangelist_t *merge_range_ts; 7153251881Speter int i; 7154251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 7155251881Speter 7156251881Speter if(!svn_path_is_url(source_path_or_url)) 7157251881Speter SVN_ERR(svn_dirent_get_absolute(&source_abspath_or_url, source_path_or_url, 7158251881Speter scratch_pool)); 7159251881Speter else 7160251881Speter source_abspath_or_url = source_path_or_url; 7161251881Speter 7162251881Speter /* Create a list to hold svn_merge_range_t's. */ 7163251881Speter merge_range_ts = apr_array_make(scratch_pool, ranges_to_merge->nelts, 7164251881Speter sizeof(svn_merge_range_t *)); 7165251881Speter 7166251881Speter for (i = 0; i < ranges_to_merge->nelts; i++) 7167251881Speter { 7168251881Speter svn_opt_revision_range_t *range 7169251881Speter = APR_ARRAY_IDX(ranges_to_merge, i, svn_opt_revision_range_t *); 7170251881Speter svn_merge_range_t mrange; 7171251881Speter 7172251881Speter svn_pool_clear(iterpool); 7173251881Speter 7174251881Speter /* Resolve revisions to real numbers, validating as we go. */ 7175251881Speter if ((range->start.kind == svn_opt_revision_unspecified) 7176251881Speter || (range->end.kind == svn_opt_revision_unspecified)) 7177251881Speter return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, 7178251881Speter _("Not all required revisions are specified")); 7179251881Speter 7180251881Speter SVN_ERR(svn_client__get_revision_number(&mrange.start, &youngest_rev, 7181251881Speter ctx->wc_ctx, 7182251881Speter source_abspath_or_url, 7183251881Speter ra_session, &range->start, 7184251881Speter iterpool)); 7185251881Speter SVN_ERR(svn_client__get_revision_number(&mrange.end, &youngest_rev, 7186251881Speter ctx->wc_ctx, 7187251881Speter source_abspath_or_url, 7188251881Speter ra_session, &range->end, 7189251881Speter iterpool)); 7190251881Speter 7191251881Speter /* If this isn't a no-op range... */ 7192251881Speter if (mrange.start != mrange.end) 7193251881Speter { 7194251881Speter /* ...then add it to the list. */ 7195251881Speter mrange.inheritable = TRUE; 7196251881Speter APR_ARRAY_PUSH(merge_range_ts, svn_merge_range_t *) 7197251881Speter = svn_merge_range_dup(&mrange, scratch_pool); 7198251881Speter } 7199251881Speter } 7200251881Speter 7201251881Speter SVN_ERR(normalize_merge_sources_internal( 7202251881Speter merge_sources_p, source_loc, 7203251881Speter merge_range_ts, ra_session, ctx, result_pool, scratch_pool)); 7204251881Speter 7205251881Speter svn_pool_destroy(iterpool); 7206251881Speter return SVN_NO_ERROR; 7207251881Speter} 7208251881Speter 7209251881Speter 7210251881Speter/*-----------------------------------------------------------------------*/ 7211251881Speter 7212251881Speter/*** Merge Workhorse Functions ***/ 7213251881Speter 7214251881Speter/* Helper for do_directory_merge() and do_file_merge() which filters out a 7215251881Speter path's own natural history from the mergeinfo describing a merge. 7216251881Speter 7217251881Speter Given the natural history IMPLICIT_MERGEINFO of some wc merge target path, 7218251881Speter the repository-relative merge source path SOURCE_REL_PATH, and the 7219251881Speter requested merge range REQUESTED_RANGE from SOURCE_REL_PATH, remove any 7220251881Speter portion of REQUESTED_RANGE which is already described in 7221251881Speter IMPLICIT_MERGEINFO. Store the result in *FILTERED_RANGELIST. 7222251881Speter 7223251881Speter This function only filters natural history for mergeinfo that will be 7224251881Speter *added* during a forward merge. Removing natural history from explicit 7225251881Speter mergeinfo is harmless. If REQUESTED_RANGE describes a reverse merge, 7226251881Speter then *FILTERED_RANGELIST is simply populated with one range described 7227251881Speter by REQUESTED_RANGE. *FILTERED_RANGELIST is never NULL. 7228251881Speter 7229251881Speter Allocate *FILTERED_RANGELIST in POOL. */ 7230251881Speterstatic svn_error_t * 7231251881Speterfilter_natural_history_from_mergeinfo(svn_rangelist_t **filtered_rangelist, 7232251881Speter const char *source_rel_path, 7233251881Speter svn_mergeinfo_t implicit_mergeinfo, 7234251881Speter svn_merge_range_t *requested_range, 7235251881Speter apr_pool_t *pool) 7236251881Speter{ 7237251881Speter /* Make the REQUESTED_RANGE into a rangelist. */ 7238251881Speter svn_rangelist_t *requested_rangelist = 7239251881Speter svn_rangelist__initialize(requested_range->start, requested_range->end, 7240251881Speter requested_range->inheritable, pool); 7241251881Speter 7242251881Speter *filtered_rangelist = NULL; 7243251881Speter 7244251881Speter /* For forward merges: If the IMPLICIT_MERGEINFO already describes ranges 7245251881Speter associated with SOURCE_REL_PATH then filter those ranges out. */ 7246251881Speter if (implicit_mergeinfo 7247251881Speter && (requested_range->start < requested_range->end)) 7248251881Speter { 7249251881Speter svn_rangelist_t *implied_rangelist = 7250251881Speter svn_hash_gets(implicit_mergeinfo, source_rel_path); 7251251881Speter 7252251881Speter if (implied_rangelist) 7253251881Speter SVN_ERR(svn_rangelist_remove(filtered_rangelist, 7254251881Speter implied_rangelist, 7255251881Speter requested_rangelist, 7256251881Speter FALSE, pool)); 7257251881Speter } 7258251881Speter 7259251881Speter /* If no filtering was performed the filtered rangelist is 7260251881Speter simply the requested rangelist.*/ 7261251881Speter if (! (*filtered_rangelist)) 7262251881Speter *filtered_rangelist = requested_rangelist; 7263251881Speter 7264251881Speter return SVN_NO_ERROR; 7265251881Speter} 7266251881Speter 7267251881Speter/* Return a merge source representing the sub-range from START_REV to 7268251881Speter END_REV of SOURCE. SOURCE obeys the rules described in the 7269251881Speter 'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file. 7270251881Speter The younger of START_REV and END_REV is inclusive while the older is 7271251881Speter exclusive. 7272251881Speter 7273251881Speter Allocate the result structure in POOL but leave the URLs in it as shallow 7274251881Speter copies of the URLs in SOURCE. 7275251881Speter*/ 7276251881Speterstatic merge_source_t * 7277251881Spetersubrange_source(const merge_source_t *source, 7278251881Speter svn_revnum_t start_rev, 7279251881Speter svn_revnum_t end_rev, 7280251881Speter apr_pool_t *pool) 7281251881Speter{ 7282251881Speter svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev); 7283251881Speter svn_boolean_t same_urls = (strcmp(source->loc1->url, source->loc2->url) == 0); 7284251881Speter svn_client__pathrev_t loc1 = *source->loc1; 7285251881Speter svn_client__pathrev_t loc2 = *source->loc2; 7286251881Speter 7287251881Speter /* For this function we require that the input source is 'ancestral'. */ 7288251881Speter SVN_ERR_ASSERT_NO_RETURN(source->ancestral); 7289251881Speter SVN_ERR_ASSERT_NO_RETURN(start_rev != end_rev); 7290251881Speter 7291251881Speter loc1.rev = start_rev; 7292251881Speter loc2.rev = end_rev; 7293251881Speter if (! same_urls) 7294251881Speter { 7295251881Speter if (is_rollback && (end_rev != source->loc2->rev)) 7296251881Speter { 7297251881Speter loc2.url = source->loc1->url; 7298251881Speter } 7299251881Speter if ((! is_rollback) && (start_rev != source->loc1->rev)) 7300251881Speter { 7301251881Speter loc1.url = source->loc2->url; 7302251881Speter } 7303251881Speter } 7304251881Speter return merge_source_create(&loc1, &loc2, source->ancestral, pool); 7305251881Speter} 7306251881Speter 7307251881Speter/* The single-file, simplified version of do_directory_merge(), which see for 7308251881Speter parameter descriptions. 7309251881Speter 7310251881Speter Additional parameters: 7311251881Speter 7312251881Speter If SOURCES_RELATED is set, the "left" and "right" sides of SOURCE are 7313251881Speter historically related (ancestors, uncles, second 7314251881Speter cousins thrice removed, etc...). (This is used to simulate the 7315251881Speter history checks that the repository logic does in the directory case.) 7316251881Speter 7317251881Speter If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG 7318251881Speter is not NULL, then don't record the new mergeinfo on the TARGET_ABSPATH, 7319251881Speter but instead record it in RESULT_CATALOG, where the key is TARGET_ABSPATH 7320251881Speter and the value is the new mergeinfo for that path. Allocate additions 7321251881Speter to RESULT_CATALOG in pool which RESULT_CATALOG was created in. 7322251881Speter 7323251881Speter CONFLICTED_RANGE is as documented for do_directory_merge(). 7324251881Speter 7325251881Speter Note: MERGE_B->RA_SESSION1 must be associated with SOURCE->loc1->url and 7326251881Speter MERGE_B->RA_SESSION2 with SOURCE->loc2->url. 7327251881Speter*/ 7328251881Speterstatic svn_error_t * 7329251881Speterdo_file_merge(svn_mergeinfo_catalog_t result_catalog, 7330251881Speter single_range_conflict_report_t **conflict_report, 7331251881Speter const merge_source_t *source, 7332251881Speter const char *target_abspath, 7333251881Speter const svn_diff_tree_processor_t *processor, 7334251881Speter svn_boolean_t sources_related, 7335251881Speter svn_boolean_t squelch_mergeinfo_notifications, 7336251881Speter merge_cmd_baton_t *merge_b, 7337251881Speter apr_pool_t *result_pool, 7338251881Speter apr_pool_t *scratch_pool) 7339251881Speter{ 7340251881Speter svn_rangelist_t *remaining_ranges; 7341251881Speter svn_client_ctx_t *ctx = merge_b->ctx; 7342251881Speter svn_merge_range_t range; 7343251881Speter svn_mergeinfo_t target_mergeinfo; 7344251881Speter svn_boolean_t inherited = FALSE; 7345251881Speter svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev); 7346251881Speter const svn_client__pathrev_t *primary_src 7347251881Speter = is_rollback ? source->loc1 : source->loc2; 7348251881Speter svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b); 7349251881Speter svn_client__merge_path_t *merge_target = NULL; 7350251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 7351251881Speter 7352251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath)); 7353251881Speter 7354251881Speter *conflict_report = NULL; 7355251881Speter 7356251881Speter /* Note that this is a single-file merge. */ 7357251881Speter range.start = source->loc1->rev; 7358251881Speter range.end = source->loc2->rev; 7359251881Speter range.inheritable = TRUE; 7360251881Speter 7361251881Speter merge_target = svn_client__merge_path_create(target_abspath, scratch_pool); 7362251881Speter 7363251881Speter if (honor_mergeinfo) 7364251881Speter { 7365251881Speter svn_error_t *err; 7366251881Speter 7367251881Speter /* Fetch mergeinfo. */ 7368251881Speter err = get_full_mergeinfo(&target_mergeinfo, 7369251881Speter &(merge_target->implicit_mergeinfo), 7370251881Speter &inherited, svn_mergeinfo_inherited, 7371251881Speter merge_b->ra_session1, target_abspath, 7372251881Speter MAX(source->loc1->rev, source->loc2->rev), 7373251881Speter MIN(source->loc1->rev, source->loc2->rev), 7374251881Speter ctx, scratch_pool, iterpool); 7375251881Speter 7376251881Speter if (err) 7377251881Speter { 7378251881Speter if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR) 7379251881Speter { 7380251881Speter err = svn_error_createf( 7381251881Speter SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err, 7382251881Speter _("Invalid mergeinfo detected on merge target '%s', " 7383251881Speter "merge tracking not possible"), 7384251881Speter svn_dirent_local_style(target_abspath, scratch_pool)); 7385251881Speter } 7386251881Speter return svn_error_trace(err); 7387251881Speter } 7388251881Speter 7389251881Speter /* Calculate remaining merges unless this is a record only merge. 7390251881Speter In that case the remaining range is the whole range described 7391251881Speter by SOURCE->rev1:rev2. */ 7392251881Speter if (!merge_b->record_only) 7393251881Speter { 7394251881Speter /* ### Bug? calculate_remaining_ranges() needs 'source' to adhere 7395251881Speter * to the requirements of 'MERGEINFO MERGE SOURCE NORMALIZATION' 7396251881Speter * here, but it doesn't appear to be guaranteed so. */ 7397251881Speter SVN_ERR(calculate_remaining_ranges(NULL, merge_target, 7398251881Speter source, 7399251881Speter target_mergeinfo, 7400251881Speter merge_b->implicit_src_gap, FALSE, 7401251881Speter merge_b->ra_session1, 7402251881Speter ctx, scratch_pool, 7403251881Speter iterpool)); 7404251881Speter remaining_ranges = merge_target->remaining_ranges; 7405251881Speter 7406251881Speter /* We are honoring mergeinfo and this is not a simple record only 7407251881Speter merge which blindly records mergeinfo describing the merge of 7408251881Speter SOURCE->LOC1->URL@SOURCE->LOC1->REV through 7409251881Speter SOURCE->LOC2->URL@SOURCE->LOC2->REV. This means that the oldest 7410251881Speter and youngest revisions merged (as determined above by 7411251881Speter calculate_remaining_ranges) might differ from those described 7412251881Speter in SOURCE. To keep the '--- Merging *' notifications consistent 7413251881Speter with the '--- Recording mergeinfo *' notifications, we adjust 7414251881Speter RANGE to account for such changes. */ 7415251881Speter if (remaining_ranges->nelts) 7416251881Speter { 7417251881Speter svn_merge_range_t *adj_start_range = 7418251881Speter APR_ARRAY_IDX(remaining_ranges, 0, svn_merge_range_t *); 7419251881Speter svn_merge_range_t *adj_end_range = 7420251881Speter APR_ARRAY_IDX(remaining_ranges, remaining_ranges->nelts - 1, 7421251881Speter svn_merge_range_t *); 7422251881Speter range.start = adj_start_range->start; 7423251881Speter range.end = adj_end_range->end; 7424251881Speter } 7425251881Speter } 7426251881Speter } 7427251881Speter 7428251881Speter /* The simple cases where our remaining range is SOURCE->rev1:rev2. */ 7429251881Speter if (!honor_mergeinfo || merge_b->record_only) 7430251881Speter { 7431251881Speter remaining_ranges = apr_array_make(scratch_pool, 1, sizeof(&range)); 7432251881Speter APR_ARRAY_PUSH(remaining_ranges, svn_merge_range_t *) = ⦥ 7433251881Speter } 7434251881Speter 7435251881Speter if (!merge_b->record_only) 7436251881Speter { 7437251881Speter svn_rangelist_t *ranges_to_merge = apr_array_copy(scratch_pool, 7438251881Speter remaining_ranges); 7439251881Speter const char *target_relpath = ""; /* relative to root of merge */ 7440251881Speter 7441251881Speter if (source->ancestral) 7442251881Speter { 7443251881Speter apr_array_header_t *child_with_mergeinfo; 7444251881Speter svn_client__merge_path_t *target_info; 7445251881Speter 7446251881Speter /* If we have ancestrally related sources and more than one 7447251881Speter range to merge, eliminate no-op ranges before going through 7448251881Speter the effort of downloading the many copies of the file 7449251881Speter required to do these merges (two copies per range). */ 7450251881Speter if (remaining_ranges->nelts > 1) 7451251881Speter { 7452251881Speter const char *old_sess_url; 7453251881Speter svn_error_t *err; 7454251881Speter 7455251881Speter SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url, 7456251881Speter merge_b->ra_session1, 7457251881Speter primary_src->url, 7458251881Speter iterpool)); 7459251881Speter err = remove_noop_merge_ranges(&ranges_to_merge, 7460251881Speter merge_b->ra_session1, 7461251881Speter remaining_ranges, scratch_pool); 7462251881Speter SVN_ERR(svn_error_compose_create( 7463251881Speter err, svn_ra_reparent(merge_b->ra_session1, 7464251881Speter old_sess_url, iterpool))); 7465251881Speter } 7466251881Speter 7467251881Speter /* To support notify_merge_begin() initialize our 7468251881Speter CHILD_WITH_MERGEINFO. See the comment 7469251881Speter 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */ 7470251881Speter 7471251881Speter child_with_mergeinfo = apr_array_make(scratch_pool, 1, 7472251881Speter sizeof(svn_client__merge_path_t *)); 7473251881Speter 7474251881Speter /* ### Create a fake copy of merge_target as we don't keep 7475251881Speter remaining_ranges in sync (yet). */ 7476251881Speter target_info = apr_pcalloc(scratch_pool, sizeof(*target_info)); 7477251881Speter 7478251881Speter target_info->abspath = merge_target->abspath; 7479251881Speter target_info->remaining_ranges = ranges_to_merge; 7480251881Speter 7481251881Speter APR_ARRAY_PUSH(child_with_mergeinfo, svn_client__merge_path_t *) 7482251881Speter = target_info; 7483251881Speter 7484251881Speter /* And store in baton to allow using it from notify_merge_begin() */ 7485251881Speter merge_b->notify_begin.nodes_with_mergeinfo = child_with_mergeinfo; 7486251881Speter } 7487251881Speter 7488251881Speter while (ranges_to_merge->nelts > 0) 7489251881Speter { 7490251881Speter svn_merge_range_t *r = APR_ARRAY_IDX(ranges_to_merge, 0, 7491251881Speter svn_merge_range_t *); 7492251881Speter const merge_source_t *real_source; 7493251881Speter const char *left_file, *right_file; 7494251881Speter apr_hash_t *left_props, *right_props; 7495251881Speter const svn_diff_source_t *left_source; 7496251881Speter const svn_diff_source_t *right_source; 7497251881Speter 7498251881Speter svn_pool_clear(iterpool); 7499251881Speter 7500251881Speter /* Ensure any subsequent drives gets their own notification. */ 7501251881Speter merge_b->notify_begin.last_abspath = NULL; 7502251881Speter 7503251881Speter /* While we currently don't allow it, in theory we could be 7504251881Speter fetching two fulltexts from two different repositories here. */ 7505251881Speter if (source->ancestral) 7506251881Speter real_source = subrange_source(source, r->start, r->end, iterpool); 7507251881Speter else 7508251881Speter real_source = source; 7509251881Speter SVN_ERR(single_file_merge_get_file(&left_file, &left_props, 7510251881Speter merge_b->ra_session1, 7511251881Speter real_source->loc1, 7512251881Speter target_abspath, 7513251881Speter iterpool, iterpool)); 7514251881Speter SVN_ERR(single_file_merge_get_file(&right_file, &right_props, 7515251881Speter merge_b->ra_session2, 7516251881Speter real_source->loc2, 7517251881Speter target_abspath, 7518251881Speter iterpool, iterpool)); 7519251881Speter /* Calculate sources for the diff processor */ 7520251881Speter left_source = svn_diff__source_create(r->start, iterpool); 7521251881Speter right_source = svn_diff__source_create(r->end, iterpool); 7522251881Speter 7523251881Speter 7524251881Speter /* If the sources are related or we're ignoring ancestry in diffs, 7525251881Speter do a text-n-props merge; otherwise, do a delete-n-add merge. */ 7526251881Speter if (! (merge_b->diff_ignore_ancestry || sources_related)) 7527251881Speter { 7528251881Speter struct merge_dir_baton_t dir_baton; 7529251881Speter void *file_baton; 7530251881Speter svn_boolean_t skip; 7531251881Speter 7532251881Speter /* Initialize minimal dir baton to allow calculating 'R'eplace 7533251881Speter from 'D'elete + 'A'dd. */ 7534251881Speter 7535251881Speter memset(&dir_baton, 0, sizeof(dir_baton)); 7536251881Speter dir_baton.pool = iterpool; 7537251881Speter dir_baton.tree_conflict_reason = CONFLICT_REASON_NONE; 7538251881Speter dir_baton.tree_conflict_action = svn_wc_conflict_action_edit; 7539251881Speter dir_baton.skip_reason = svn_wc_notify_state_unknown; 7540251881Speter 7541251881Speter /* Delete... */ 7542251881Speter file_baton = NULL; 7543251881Speter skip = FALSE; 7544251881Speter SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath, 7545251881Speter left_source, 7546251881Speter NULL /* right_source */, 7547251881Speter NULL /* copyfrom_source */, 7548251881Speter &dir_baton, 7549251881Speter processor, 7550251881Speter iterpool, iterpool)); 7551251881Speter if (! skip) 7552251881Speter SVN_ERR(processor->file_deleted(target_relpath, 7553251881Speter left_source, 7554251881Speter left_file, 7555251881Speter left_props, 7556251881Speter file_baton, 7557251881Speter processor, 7558251881Speter iterpool)); 7559251881Speter 7560251881Speter /* ...plus add... */ 7561251881Speter file_baton = NULL; 7562251881Speter skip = FALSE; 7563251881Speter SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath, 7564251881Speter NULL /* left_source */, 7565251881Speter right_source, 7566251881Speter NULL /* copyfrom_source */, 7567251881Speter &dir_baton, 7568251881Speter processor, 7569251881Speter iterpool, iterpool)); 7570251881Speter if (! skip) 7571251881Speter SVN_ERR(processor->file_added(target_relpath, 7572251881Speter NULL /* copyfrom_source */, 7573251881Speter right_source, 7574251881Speter NULL /* copyfrom_file */, 7575251881Speter right_file, 7576251881Speter NULL /* copyfrom_props */, 7577251881Speter right_props, 7578251881Speter file_baton, 7579251881Speter processor, 7580251881Speter iterpool)); 7581251881Speter /* ... equals replace. */ 7582251881Speter } 7583251881Speter else 7584251881Speter { 7585251881Speter void *file_baton = NULL; 7586251881Speter svn_boolean_t skip = FALSE; 7587251881Speter apr_array_header_t *propchanges; 7588251881Speter 7589251881Speter 7590251881Speter /* Deduce property diffs. */ 7591251881Speter SVN_ERR(svn_prop_diffs(&propchanges, right_props, left_props, 7592251881Speter iterpool)); 7593251881Speter 7594251881Speter SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath, 7595251881Speter left_source, 7596251881Speter right_source, 7597251881Speter NULL /* copyfrom_source */, 7598251881Speter NULL /* dir_baton */, 7599251881Speter processor, 7600251881Speter iterpool, iterpool)); 7601251881Speter if (! skip) 7602251881Speter SVN_ERR(processor->file_changed(target_relpath, 7603251881Speter left_source, 7604251881Speter right_source, 7605251881Speter left_file, 7606251881Speter right_file, 7607251881Speter left_props, 7608251881Speter right_props, 7609251881Speter TRUE /* file changed */, 7610251881Speter propchanges, 7611251881Speter file_baton, 7612251881Speter processor, 7613251881Speter iterpool)); 7614251881Speter } 7615251881Speter 7616251881Speter if (is_path_conflicted_by_merge(merge_b)) 7617251881Speter { 7618251881Speter merge_source_t *remaining_range = NULL; 7619251881Speter 7620251881Speter if (real_source->loc2->rev != source->loc2->rev) 7621251881Speter remaining_range = subrange_source(source, 7622251881Speter real_source->loc2->rev, 7623251881Speter source->loc2->rev, 7624251881Speter scratch_pool); 7625251881Speter *conflict_report = single_range_conflict_report_create( 7626251881Speter real_source, remaining_range, result_pool); 7627251881Speter 7628251881Speter /* Only record partial mergeinfo if only a partial merge was 7629251881Speter performed before a conflict was encountered. */ 7630251881Speter range.end = r->end; 7631251881Speter break; 7632251881Speter } 7633251881Speter 7634251881Speter /* Now delete the just merged range from the hash 7635251881Speter (This list is used from notify_merge_begin) 7636251881Speter 7637251881Speter Directory merges use remove_first_range_from_remaining_ranges() */ 7638251881Speter svn_sort__array_delete(ranges_to_merge, 0, 1); 7639251881Speter } 7640251881Speter merge_b->notify_begin.last_abspath = NULL; 7641251881Speter } /* !merge_b->record_only */ 7642251881Speter 7643251881Speter /* Record updated WC mergeinfo to account for our new merges, minus 7644251881Speter any unresolved conflicts and skips. We use the original 7645251881Speter REMAINING_RANGES here because we want to record all the requested 7646251881Speter merge ranges, include the noop ones. */ 7647251881Speter if (RECORD_MERGEINFO(merge_b) && remaining_ranges->nelts) 7648251881Speter { 7649251881Speter const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src, 7650251881Speter scratch_pool); 7651251881Speter svn_rangelist_t *filtered_rangelist; 7652251881Speter 7653251881Speter /* Filter any ranges from TARGET_WCPATH's own history, there is no 7654251881Speter need to record this explicitly in mergeinfo, it is already part 7655251881Speter of TARGET_WCPATH's natural history (implicit mergeinfo). */ 7656251881Speter SVN_ERR(filter_natural_history_from_mergeinfo( 7657251881Speter &filtered_rangelist, 7658251881Speter mergeinfo_path, 7659251881Speter merge_target->implicit_mergeinfo, 7660251881Speter &range, 7661251881Speter iterpool)); 7662251881Speter 7663251881Speter /* Only record mergeinfo if there is something other than 7664251881Speter self-referential mergeinfo, but don't record mergeinfo if 7665251881Speter TARGET_WCPATH was skipped. */ 7666251881Speter if (filtered_rangelist->nelts 7667251881Speter && (apr_hash_count(merge_b->skipped_abspaths) == 0)) 7668251881Speter { 7669251881Speter apr_hash_t *merges = apr_hash_make(iterpool); 7670251881Speter 7671251881Speter /* If merge target has inherited mergeinfo set it before 7672251881Speter recording the first merge range. */ 7673251881Speter if (inherited) 7674251881Speter SVN_ERR(svn_client__record_wc_mergeinfo(target_abspath, 7675251881Speter target_mergeinfo, 7676251881Speter FALSE, ctx, 7677251881Speter iterpool)); 7678251881Speter 7679251881Speter svn_hash_sets(merges, target_abspath, filtered_rangelist); 7680251881Speter 7681251881Speter if (!squelch_mergeinfo_notifications) 7682251881Speter { 7683251881Speter /* Notify that we are recording mergeinfo describing a merge. */ 7684251881Speter svn_merge_range_t n_range; 7685251881Speter 7686251881Speter SVN_ERR(svn_mergeinfo__get_range_endpoints( 7687251881Speter &n_range.end, &n_range.start, merges, iterpool)); 7688251881Speter n_range.inheritable = TRUE; 7689251881Speter notify_mergeinfo_recording(target_abspath, &n_range, 7690251881Speter merge_b->ctx, iterpool); 7691251881Speter } 7692251881Speter 7693251881Speter SVN_ERR(update_wc_mergeinfo(result_catalog, target_abspath, 7694251881Speter mergeinfo_path, merges, is_rollback, 7695251881Speter ctx, iterpool)); 7696251881Speter } 7697251881Speter } 7698251881Speter 7699251881Speter merge_b->notify_begin.nodes_with_mergeinfo = NULL; 7700251881Speter 7701251881Speter svn_pool_destroy(iterpool); 7702251881Speter 7703251881Speter return SVN_NO_ERROR; 7704251881Speter} 7705251881Speter 7706251881Speter/* Helper for do_directory_merge() to handle the case where a merge editor 7707251881Speter drive adds explicit mergeinfo to a path which didn't have any explicit 7708251881Speter mergeinfo previously. 7709251881Speter 7710251881Speter MERGE_B is cascaded from the argument of the same 7711251881Speter name in do_directory_merge(). Should be called only after 7712251881Speter do_directory_merge() has called populate_remaining_ranges() and populated 7713251881Speter the remaining_ranges field of each child in 7714251881Speter CHILDREN_WITH_MERGEINFO (i.e. the remaining_ranges fields can be 7715251881Speter empty but never NULL). 7716251881Speter 7717251881Speter If MERGE_B->DRY_RUN is true do nothing, if it is false then 7718251881Speter for each path (if any) in MERGE_B->PATHS_WITH_NEW_MERGEINFO merge that 7719251881Speter path's inherited mergeinfo (if any) with its working explicit mergeinfo 7720251881Speter and set that as the path's new explicit mergeinfo. Then add an 7721251881Speter svn_client__merge_path_t * element representing the path to 7722251881Speter CHILDREN_WITH_MERGEINFO if it isn't already present. All fields 7723251881Speter in any elements added to CHILDREN_WITH_MERGEINFO are initialized 7724251881Speter to FALSE/NULL with the exception of 'path' and 'remaining_ranges'. The 7725251881Speter latter is set to a rangelist equal to the remaining_ranges of the path's 7726251881Speter nearest path-wise ancestor in CHILDREN_WITH_MERGEINFO. 7727251881Speter 7728251881Speter Any elements added to CHILDREN_WITH_MERGEINFO are allocated 7729251881Speter in POOL. */ 7730251881Speterstatic svn_error_t * 7731251881Speterprocess_children_with_new_mergeinfo(merge_cmd_baton_t *merge_b, 7732251881Speter apr_array_header_t *children_with_mergeinfo, 7733251881Speter apr_pool_t *pool) 7734251881Speter{ 7735251881Speter apr_pool_t *iterpool; 7736251881Speter apr_hash_index_t *hi; 7737251881Speter 7738251881Speter if (!merge_b->paths_with_new_mergeinfo || merge_b->dry_run) 7739251881Speter return SVN_NO_ERROR; 7740251881Speter 7741251881Speter /* Iterate over each path with explicit mergeinfo added by the merge. */ 7742251881Speter iterpool = svn_pool_create(pool); 7743251881Speter for (hi = apr_hash_first(pool, merge_b->paths_with_new_mergeinfo); 7744251881Speter hi; 7745251881Speter hi = apr_hash_next(hi)) 7746251881Speter { 7747251881Speter const char *abspath_with_new_mergeinfo = svn__apr_hash_index_key(hi); 7748251881Speter svn_mergeinfo_t path_inherited_mergeinfo; 7749251881Speter svn_mergeinfo_t path_explicit_mergeinfo; 7750251881Speter svn_client__merge_path_t *new_child; 7751251881Speter 7752251881Speter svn_pool_clear(iterpool); 7753251881Speter 7754251881Speter /* Note: We could skip recording inherited mergeinfo here if this path 7755251881Speter was added (with preexisting mergeinfo) by the merge. That's actually 7756251881Speter more correct, since the inherited mergeinfo likely describes 7757251881Speter non-existent or unrelated merge history, but it's not quite so simple 7758251881Speter as that, see http://subversion.tigris.org/issues/show_bug.cgi?id=4309 7759251881Speter */ 7760251881Speter 7761251881Speter /* Get the path's new explicit mergeinfo... */ 7762251881Speter SVN_ERR(svn_client__get_wc_mergeinfo(&path_explicit_mergeinfo, NULL, 7763251881Speter svn_mergeinfo_explicit, 7764251881Speter abspath_with_new_mergeinfo, 7765251881Speter NULL, NULL, FALSE, 7766251881Speter merge_b->ctx, 7767251881Speter iterpool, iterpool)); 7768251881Speter /* ...there *should* always be explicit mergeinfo at this point 7769251881Speter but you can't be too careful. */ 7770251881Speter if (path_explicit_mergeinfo) 7771251881Speter { 7772251881Speter /* Get the mergeinfo the path would have inherited before 7773251881Speter the merge. */ 7774251881Speter SVN_ERR(svn_client__get_wc_or_repos_mergeinfo( 7775251881Speter &path_inherited_mergeinfo, 7776251881Speter NULL, NULL, 7777251881Speter FALSE, 7778251881Speter svn_mergeinfo_nearest_ancestor, /* We only want inherited MI */ 7779251881Speter merge_b->ra_session2, 7780251881Speter abspath_with_new_mergeinfo, 7781251881Speter merge_b->ctx, 7782251881Speter iterpool)); 7783251881Speter 7784251881Speter /* If the path inherited any mergeinfo then merge that with the 7785251881Speter explicit mergeinfo and record the result as the path's new 7786251881Speter explicit mergeinfo. */ 7787251881Speter if (path_inherited_mergeinfo) 7788251881Speter { 7789251881Speter SVN_ERR(svn_mergeinfo_merge2(path_explicit_mergeinfo, 7790251881Speter path_inherited_mergeinfo, 7791251881Speter iterpool, iterpool)); 7792251881Speter SVN_ERR(svn_client__record_wc_mergeinfo( 7793251881Speter abspath_with_new_mergeinfo, 7794251881Speter path_explicit_mergeinfo, 7795251881Speter FALSE, merge_b->ctx, iterpool)); 7796251881Speter } 7797251881Speter 7798251881Speter /* If the path is not in CHILDREN_WITH_MERGEINFO then add it. */ 7799251881Speter new_child = 7800251881Speter get_child_with_mergeinfo(children_with_mergeinfo, 7801251881Speter abspath_with_new_mergeinfo); 7802251881Speter if (!new_child) 7803251881Speter { 7804251881Speter const svn_client__merge_path_t *parent 7805251881Speter = find_nearest_ancestor(children_with_mergeinfo, 7806251881Speter FALSE, abspath_with_new_mergeinfo); 7807251881Speter new_child 7808251881Speter = svn_client__merge_path_create(abspath_with_new_mergeinfo, 7809251881Speter pool); 7810251881Speter 7811251881Speter /* If path_with_new_mergeinfo is the merge target itself 7812251881Speter then it should already be in 7813251881Speter CHILDREN_WITH_MERGEINFO per the criteria of 7814251881Speter get_mergeinfo_paths() and we shouldn't be in this block. 7815251881Speter If path_with_new_mergeinfo is a subtree then it must have 7816251881Speter a parent in CHILDREN_WITH_MERGEINFO if only 7817251881Speter the merge target itself...so if we don't find a parent 7818251881Speter the caller has done something quite wrong. */ 7819251881Speter SVN_ERR_ASSERT(parent); 7820251881Speter SVN_ERR_ASSERT(parent->remaining_ranges); 7821251881Speter 7822251881Speter /* Set the path's remaining_ranges equal to its parent's. */ 7823251881Speter new_child->remaining_ranges = svn_rangelist_dup( 7824251881Speter parent->remaining_ranges, pool); 7825251881Speter insert_child_to_merge(children_with_mergeinfo, new_child, pool); 7826251881Speter } 7827251881Speter } 7828251881Speter } 7829251881Speter svn_pool_destroy(iterpool); 7830251881Speter 7831251881Speter return SVN_NO_ERROR; 7832251881Speter} 7833251881Speter 7834251881Speter/* Return true if any path in SUBTREES is equal to, or is a subtree of, 7835251881Speter LOCAL_ABSPATH. Return false otherwise. The keys of SUBTREES are 7836251881Speter (const char *) absolute paths and its values are irrelevant. 7837251881Speter If SUBTREES is NULL return false. */ 7838251881Speterstatic svn_boolean_t 7839251881Speterpath_is_subtree(const char *local_abspath, 7840251881Speter apr_hash_t *subtrees, 7841251881Speter apr_pool_t *pool) 7842251881Speter{ 7843251881Speter if (subtrees) 7844251881Speter { 7845251881Speter apr_hash_index_t *hi; 7846251881Speter 7847251881Speter for (hi = apr_hash_first(pool, subtrees); 7848251881Speter hi; hi = apr_hash_next(hi)) 7849251881Speter { 7850251881Speter const char *path_touched_by_merge = svn__apr_hash_index_key(hi); 7851251881Speter if (svn_dirent_is_ancestor(local_abspath, path_touched_by_merge)) 7852251881Speter return TRUE; 7853251881Speter } 7854251881Speter } 7855251881Speter return FALSE; 7856251881Speter} 7857251881Speter 7858251881Speter/* Return true if any merged, skipped, added or tree-conflicted path 7859251881Speter recorded in MERGE_B is equal to, or is a subtree of LOCAL_ABSPATH. Return 7860251881Speter false otherwise. 7861251881Speter 7862251881Speter ### Why not text- or prop-conflicted paths? Are such paths guaranteed 7863251881Speter to be recorded as 'merged' or 'skipped' or 'added', perhaps? 7864251881Speter*/ 7865251881Speterstatic svn_boolean_t 7866251881Spetersubtree_touched_by_merge(const char *local_abspath, 7867251881Speter merge_cmd_baton_t *merge_b, 7868251881Speter apr_pool_t *pool) 7869251881Speter{ 7870251881Speter return (path_is_subtree(local_abspath, merge_b->merged_abspaths, pool) 7871251881Speter || path_is_subtree(local_abspath, merge_b->skipped_abspaths, pool) 7872251881Speter || path_is_subtree(local_abspath, merge_b->added_abspaths, pool) 7873251881Speter || path_is_subtree(local_abspath, merge_b->tree_conflicted_abspaths, 7874251881Speter pool)); 7875251881Speter} 7876251881Speter 7877251881Speter/* Helper for do_directory_merge() when performing mergeinfo unaware merges. 7878251881Speter 7879251881Speter Merge the SOURCE diff into TARGET_DIR_WCPATH. 7880251881Speter 7881251881Speter SOURCE, DEPTH, NOTIFY_B, and MERGE_B 7882251881Speter are all cascaded from do_directory_merge's arguments of the same names. 7883251881Speter 7884251881Speter CONFLICT_REPORT is as documented for do_directory_merge(). 7885251881Speter 7886251881Speter NOTE: This is a very thin wrapper around drive_merge_report_editor() and 7887251881Speter exists only to populate CHILDREN_WITH_MERGEINFO with the single element 7888251881Speter expected during mergeinfo unaware merges. 7889251881Speter*/ 7890251881Speterstatic svn_error_t * 7891251881Speterdo_mergeinfo_unaware_dir_merge(single_range_conflict_report_t **conflict_report, 7892251881Speter const merge_source_t *source, 7893251881Speter const char *target_dir_wcpath, 7894251881Speter apr_array_header_t *children_with_mergeinfo, 7895251881Speter const svn_diff_tree_processor_t *processor, 7896251881Speter svn_depth_t depth, 7897251881Speter merge_cmd_baton_t *merge_b, 7898251881Speter apr_pool_t *result_pool, 7899251881Speter apr_pool_t *scratch_pool) 7900251881Speter{ 7901251881Speter /* Initialize CHILDREN_WITH_MERGEINFO and populate it with 7902251881Speter one element describing the merge of SOURCE->rev1:rev2 to 7903251881Speter TARGET_DIR_WCPATH. */ 7904251881Speter svn_client__merge_path_t *item 7905251881Speter = svn_client__merge_path_create(target_dir_wcpath, scratch_pool); 7906251881Speter 7907251881Speter *conflict_report = NULL; 7908251881Speter item->remaining_ranges = svn_rangelist__initialize(source->loc1->rev, 7909251881Speter source->loc2->rev, 7910251881Speter TRUE, scratch_pool); 7911251881Speter APR_ARRAY_PUSH(children_with_mergeinfo, 7912251881Speter svn_client__merge_path_t *) = item; 7913251881Speter SVN_ERR(drive_merge_report_editor(target_dir_wcpath, 7914251881Speter source, 7915251881Speter NULL, processor, depth, 7916251881Speter merge_b, scratch_pool)); 7917251881Speter if (is_path_conflicted_by_merge(merge_b)) 7918251881Speter { 7919251881Speter *conflict_report = single_range_conflict_report_create( 7920251881Speter source, NULL, result_pool); 7921251881Speter } 7922251881Speter return SVN_NO_ERROR; 7923251881Speter} 7924251881Speter 7925251881Speter/* A svn_log_entry_receiver_t baton for log_find_operative_subtree_revs(). */ 7926251881Spetertypedef struct log_find_operative_subtree_baton_t 7927251881Speter{ 7928251881Speter /* Mapping of const char * absolute working copy paths to those 7929251881Speter path's const char * repos absolute paths. */ 7930251881Speter apr_hash_t *operative_children; 7931251881Speter 7932251881Speter /* As per the arguments of the same name to 7933251881Speter get_operative_immediate_children(). */ 7934251881Speter const char *merge_source_fspath; 7935251881Speter const char *merge_target_abspath; 7936251881Speter svn_depth_t depth; 7937251881Speter svn_wc_context_t *wc_ctx; 7938251881Speter 7939251881Speter /* A pool to allocate additions to the hashes in. */ 7940251881Speter apr_pool_t *result_pool; 7941251881Speter} log_find_operative_subtree_baton_t; 7942251881Speter 7943251881Speter/* A svn_log_entry_receiver_t callback for 7944251881Speter get_inoperative_immediate_children(). */ 7945251881Speterstatic svn_error_t * 7946251881Speterlog_find_operative_subtree_revs(void *baton, 7947251881Speter svn_log_entry_t *log_entry, 7948251881Speter apr_pool_t *pool) 7949251881Speter{ 7950251881Speter log_find_operative_subtree_baton_t *log_baton = baton; 7951251881Speter apr_hash_index_t *hi; 7952251881Speter apr_pool_t *iterpool; 7953251881Speter 7954251881Speter /* It's possible that authz restrictions on the merge source prevent us 7955251881Speter from knowing about any of the changes for LOG_ENTRY->REVISION. */ 7956251881Speter if (!log_entry->changed_paths2) 7957251881Speter return SVN_NO_ERROR; 7958251881Speter 7959251881Speter iterpool = svn_pool_create(pool); 7960251881Speter 7961251881Speter for (hi = apr_hash_first(pool, log_entry->changed_paths2); 7962251881Speter hi; 7963251881Speter hi = apr_hash_next(hi)) 7964251881Speter { 7965251881Speter const char *path = svn__apr_hash_index_key(hi); 7966251881Speter svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi); 7967251881Speter 7968251881Speter { 7969251881Speter const char *child; 7970251881Speter const char *potential_child; 7971251881Speter const char *rel_path = 7972251881Speter svn_fspath__skip_ancestor(log_baton->merge_source_fspath, path); 7973251881Speter 7974251881Speter /* Some affected paths might be the root of the merge source or 7975251881Speter entirely outside our subtree of interest. In either case they 7976251881Speter are not operative *immediate* children. */ 7977251881Speter if (rel_path == NULL 7978251881Speter || rel_path[0] == '\0') 7979251881Speter continue; 7980251881Speter 7981251881Speter svn_pool_clear(iterpool); 7982251881Speter 7983251881Speter child = svn_relpath_dirname(rel_path, iterpool); 7984251881Speter if (child[0] == '\0') 7985251881Speter { 7986251881Speter /* The svn_log_changed_path2_t.node_kind members in 7987251881Speter LOG_ENTRY->CHANGED_PATHS2 may be set to 7988251881Speter svn_node_unknown, see svn_log_changed_path2_t and 7989251881Speter svn_fs_paths_changed2. In that case we check the 7990251881Speter type of the corresponding subtree in the merge 7991251881Speter target. */ 7992251881Speter svn_node_kind_t node_kind; 7993251881Speter 7994251881Speter if (change->node_kind == svn_node_unknown) 7995251881Speter { 7996251881Speter const char *wc_child_abspath = 7997251881Speter svn_dirent_join(log_baton->merge_target_abspath, 7998251881Speter rel_path, iterpool); 7999251881Speter 8000251881Speter SVN_ERR(svn_wc_read_kind2(&node_kind, log_baton->wc_ctx, 8001251881Speter wc_child_abspath, FALSE, FALSE, 8002251881Speter iterpool)); 8003251881Speter } 8004251881Speter else 8005251881Speter { 8006251881Speter node_kind = change->node_kind; 8007251881Speter } 8008251881Speter 8009251881Speter /* We only care about immediate directory children if 8010251881Speter DEPTH is svn_depth_files. */ 8011251881Speter if (log_baton->depth == svn_depth_files 8012251881Speter && node_kind != svn_node_dir) 8013251881Speter continue; 8014251881Speter 8015251881Speter /* If depth is svn_depth_immediates, then we only care 8016251881Speter about changes to proper subtrees of PATH. If the change 8017251881Speter is to PATH itself then PATH is within the operational 8018251881Speter depth of the merge. */ 8019251881Speter if (log_baton->depth == svn_depth_immediates) 8020251881Speter continue; 8021251881Speter 8022251881Speter child = rel_path; 8023251881Speter } 8024251881Speter 8025251881Speter potential_child = svn_dirent_join(log_baton->merge_target_abspath, 8026251881Speter child, iterpool); 8027251881Speter 8028251881Speter if (change->action == 'A' 8029251881Speter || !svn_hash_gets(log_baton->operative_children, 8030251881Speter potential_child)) 8031251881Speter { 8032251881Speter svn_hash_sets(log_baton->operative_children, 8033251881Speter apr_pstrdup(log_baton->result_pool, 8034251881Speter potential_child), 8035251881Speter apr_pstrdup(log_baton->result_pool, path)); 8036251881Speter } 8037251881Speter } 8038251881Speter } 8039251881Speter svn_pool_destroy(iterpool); 8040251881Speter return SVN_NO_ERROR; 8041251881Speter} 8042251881Speter 8043251881Speter/* Find immediate subtrees of MERGE_TARGET_ABSPATH which would have 8044251881Speter additional differences applied if record_mergeinfo_for_dir_merge() were 8045251881Speter recording mergeinfo describing a merge at svn_depth_infinity, rather 8046251881Speter than at DEPTH (which is assumed to be shallow; if 8047251881Speter DEPTH == svn_depth_infinity then this function does nothing beyond 8048251881Speter setting *OPERATIVE_CHILDREN to an empty hash). 8049251881Speter 8050251881Speter MERGE_SOURCE_FSPATH is the absolute repository path of the merge 8051251881Speter source. OLDEST_REV and YOUNGEST_REV are the revisions merged from 8052251881Speter MERGE_SOURCE_FSPATH to MERGE_TARGET_ABSPATH. 8053251881Speter 8054251881Speter RA_SESSION points to MERGE_SOURCE_FSPATH. 8055251881Speter 8056251881Speter Set *OPERATIVE_CHILDREN to a hash (mapping const char * absolute 8057251881Speter working copy paths to those path's const char * repos absolute paths) 8058251881Speter containing all the immediate subtrees of MERGE_TARGET_ABSPATH which would 8059251881Speter have a different diff applied if MERGE_SOURCE_FSPATH 8060251881Speter -r(OLDEST_REV - 1):YOUNGEST_REV were merged to MERGE_TARGET_ABSPATH at 8061251881Speter svn_depth_infinity rather than DEPTH. 8062251881Speter 8063251881Speter RESULT_POOL is used to allocate the contents of *OPERATIVE_CHILDREN. 8064251881Speter SCRATCH_POOL is used for temporary allocations. */ 8065251881Speterstatic svn_error_t * 8066251881Speterget_operative_immediate_children(apr_hash_t **operative_children, 8067251881Speter const char *merge_source_fspath, 8068251881Speter svn_revnum_t oldest_rev, 8069251881Speter svn_revnum_t youngest_rev, 8070251881Speter const char *merge_target_abspath, 8071251881Speter svn_depth_t depth, 8072251881Speter svn_wc_context_t *wc_ctx, 8073251881Speter svn_ra_session_t *ra_session, 8074251881Speter apr_pool_t *result_pool, 8075251881Speter apr_pool_t *scratch_pool) 8076251881Speter{ 8077251881Speter log_find_operative_subtree_baton_t log_baton; 8078251881Speter 8079251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev)); 8080251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); 8081251881Speter SVN_ERR_ASSERT(oldest_rev <= youngest_rev); 8082251881Speter 8083251881Speter *operative_children = apr_hash_make(result_pool); 8084251881Speter 8085251881Speter if (depth == svn_depth_infinity) 8086251881Speter return SVN_NO_ERROR; 8087251881Speter 8088251881Speter /* Now remove any paths from *OPERATIVE_CHILDREN that are inoperative when 8089251881Speter merging MERGE_SOURCE_REPOS_PATH -r(OLDEST_REV - 1):YOUNGEST_REV to 8090251881Speter MERGE_TARGET_ABSPATH at --depth infinity. */ 8091251881Speter log_baton.operative_children = *operative_children; 8092251881Speter log_baton.merge_source_fspath = merge_source_fspath; 8093251881Speter log_baton.merge_target_abspath = merge_target_abspath; 8094251881Speter log_baton.depth = depth; 8095251881Speter log_baton.wc_ctx = wc_ctx; 8096251881Speter log_baton.result_pool = result_pool; 8097251881Speter 8098251881Speter SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev, 8099251881Speter TRUE, /* discover_changed_paths */ 8100251881Speter log_find_operative_subtree_revs, 8101251881Speter &log_baton, scratch_pool)); 8102251881Speter 8103251881Speter return SVN_NO_ERROR; 8104251881Speter} 8105251881Speter 8106251881Speter/* Helper for record_mergeinfo_for_dir_merge(): Identify which elements of 8107251881Speter CHILDREN_WITH_MERGEINFO need new mergeinfo set to accurately 8108251881Speter describe a merge, what inheritance type such new mergeinfo should have, 8109251881Speter and what subtrees can be ignored altogether. 8110251881Speter 8111251881Speter For each svn_client__merge_path_t CHILD in CHILDREN_WITH_MERGEINFO, 8112251881Speter set CHILD->RECORD_MERGEINFO and CHILD->RECORD_NONINHERITABLE to true 8113251881Speter if the subtree needs mergeinfo to describe the merge and if that 8114251881Speter mergeinfo should be non-inheritable respectively. 8115251881Speter 8116251881Speter If OPERATIVE_MERGE is true, then the merge being described is operative 8117251881Speter as per subtree_touched_by_merge(). OPERATIVE_MERGE is false otherwise. 8118251881Speter 8119251881Speter MERGED_RANGE, MERGEINFO_FSPATH, DEPTH, NOTIFY_B, and MERGE_B are all 8120251881Speter cascaded from record_mergeinfo_for_dir_merge's arguments of the same 8121251881Speter names. 8122251881Speter 8123251881Speter SCRATCH_POOL is used for temporary allocations. 8124251881Speter*/ 8125251881Speterstatic svn_error_t * 8126251881Speterflag_subtrees_needing_mergeinfo(svn_boolean_t operative_merge, 8127251881Speter const svn_merge_range_t *merged_range, 8128251881Speter apr_array_header_t *children_with_mergeinfo, 8129251881Speter const char *mergeinfo_fspath, 8130251881Speter svn_depth_t depth, 8131251881Speter merge_cmd_baton_t *merge_b, 8132251881Speter apr_pool_t *scratch_pool) 8133251881Speter{ 8134251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 8135251881Speter int i; 8136251881Speter apr_hash_t *operative_immediate_children = NULL; 8137251881Speter 8138251881Speter assert(! merge_b->dry_run); 8139251881Speter 8140251881Speter if (!merge_b->record_only 8141251881Speter && merged_range->start <= merged_range->end 8142251881Speter && (depth < svn_depth_infinity)) 8143251881Speter SVN_ERR(get_operative_immediate_children( 8144251881Speter &operative_immediate_children, 8145251881Speter mergeinfo_fspath, merged_range->start + 1, merged_range->end, 8146251881Speter merge_b->target->abspath, depth, merge_b->ctx->wc_ctx, 8147251881Speter merge_b->ra_session1, scratch_pool, iterpool)); 8148251881Speter 8149251881Speter /* Issue #4056: Walk NOTIFY_B->CHILDREN_WITH_MERGEINFO reverse depth-first 8150251881Speter order. This way each child knows if it has operative missing/switched 8151251881Speter children which necessitates non-inheritable mergeinfo. */ 8152251881Speter for (i = children_with_mergeinfo->nelts - 1; i >= 0; i--) 8153251881Speter { 8154251881Speter svn_client__merge_path_t *child = 8155251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, 8156251881Speter svn_client__merge_path_t *); 8157251881Speter 8158251881Speter /* Can't record mergeinfo on something that isn't here. */ 8159251881Speter if (child->absent) 8160251881Speter continue; 8161251881Speter 8162251881Speter /* Verify that remove_children_with_deleted_mergeinfo() did its job */ 8163251881Speter assert((i == 0) 8164251881Speter ||! merge_b->paths_with_deleted_mergeinfo 8165251881Speter || !svn_hash_gets(merge_b->paths_with_deleted_mergeinfo, 8166251881Speter child->abspath)); 8167251881Speter 8168251881Speter /* Don't record mergeinfo on skipped paths. */ 8169251881Speter if (svn_hash_gets(merge_b->skipped_abspaths, child->abspath)) 8170251881Speter continue; 8171251881Speter 8172251881Speter /* ### ptb: Yes, we could combine the following into a single 8173251881Speter ### conditional, but clarity would suffer (even more than 8174251881Speter ### it does now). */ 8175251881Speter if (i == 0) 8176251881Speter { 8177251881Speter /* Always record mergeinfo on the merge target. */ 8178251881Speter child->record_mergeinfo = TRUE; 8179251881Speter } 8180251881Speter else if (merge_b->record_only && !merge_b->reintegrate_merge) 8181251881Speter { 8182251881Speter /* Always record mergeinfo for --record-only merges. */ 8183251881Speter child->record_mergeinfo = TRUE; 8184251881Speter } 8185251881Speter else if (child->immediate_child_dir 8186251881Speter && !child->pre_merge_mergeinfo 8187251881Speter && operative_immediate_children 8188251881Speter && svn_hash_gets(operative_immediate_children, child->abspath)) 8189251881Speter { 8190251881Speter /* We must record mergeinfo on those issue #3642 children 8191251881Speter that are operative at a greater depth. */ 8192251881Speter child->record_mergeinfo = TRUE; 8193251881Speter } 8194251881Speter 8195251881Speter if (operative_merge 8196251881Speter && subtree_touched_by_merge(child->abspath, merge_b, iterpool)) 8197251881Speter { 8198251881Speter svn_pool_clear(iterpool); 8199251881Speter 8200251881Speter /* This subtree was affected by the merge. */ 8201251881Speter child->record_mergeinfo = TRUE; 8202251881Speter 8203251881Speter /* Were any CHILD's missing children skipped by the merge? 8204251881Speter If not, then CHILD's missing children don't need to be 8205251881Speter considered when recording mergeinfo describing the merge. */ 8206251881Speter if (! merge_b->reintegrate_merge 8207251881Speter && child->missing_child 8208251881Speter && !path_is_subtree(child->abspath, 8209251881Speter merge_b->skipped_abspaths, 8210251881Speter iterpool)) 8211251881Speter { 8212251881Speter child->missing_child = FALSE; 8213251881Speter } 8214251881Speter 8215251881Speter /* If CHILD has an immediate switched child or children and 8216251881Speter none of these were touched by the merge, then we don't need 8217251881Speter need to do any special handling of those switched subtrees 8218251881Speter (e.g. record non-inheritable mergeinfo) when recording 8219251881Speter mergeinfo describing the merge. */ 8220251881Speter if (child->switched_child) 8221251881Speter { 8222251881Speter int j; 8223251881Speter svn_boolean_t operative_switched_child = FALSE; 8224251881Speter 8225251881Speter for (j = i + 1; 8226251881Speter j < children_with_mergeinfo->nelts; 8227251881Speter j++) 8228251881Speter { 8229251881Speter svn_client__merge_path_t *potential_child = 8230251881Speter APR_ARRAY_IDX(children_with_mergeinfo, j, 8231251881Speter svn_client__merge_path_t *); 8232251881Speter if (!svn_dirent_is_ancestor(child->abspath, 8233251881Speter potential_child->abspath)) 8234251881Speter break; 8235251881Speter 8236251881Speter /* POTENTIAL_CHILD is a subtree of CHILD, but is it 8237251881Speter an immediate child? */ 8238251881Speter if (strcmp(child->abspath, 8239251881Speter svn_dirent_dirname(potential_child->abspath, 8240251881Speter iterpool))) 8241251881Speter continue; 8242251881Speter 8243251881Speter if (potential_child->switched 8244251881Speter && potential_child->record_mergeinfo) 8245251881Speter { 8246251881Speter operative_switched_child = TRUE; 8247251881Speter break; 8248251881Speter } 8249251881Speter } 8250251881Speter 8251251881Speter /* Can we treat CHILD as if it has no switched children? */ 8252251881Speter if (! operative_switched_child) 8253251881Speter child->switched_child = FALSE; 8254251881Speter } 8255251881Speter } 8256251881Speter 8257251881Speter if (child->record_mergeinfo) 8258251881Speter { 8259251881Speter /* We need to record mergeinfo, but should that mergeinfo be 8260251881Speter non-inheritable? */ 8261251881Speter svn_node_kind_t path_kind; 8262251881Speter SVN_ERR(svn_wc_read_kind2(&path_kind, merge_b->ctx->wc_ctx, 8263251881Speter child->abspath, FALSE, FALSE, iterpool)); 8264251881Speter 8265251881Speter /* Only directories can have non-inheritable mergeinfo. */ 8266251881Speter if (path_kind == svn_node_dir) 8267251881Speter { 8268251881Speter /* There are two general cases where non-inheritable mergeinfo 8269251881Speter is required: 8270251881Speter 8271251881Speter 1) There merge target has missing subtrees (due to authz 8272251881Speter restrictions, switched subtrees, or a shallow working 8273251881Speter copy). 8274251881Speter 8275251881Speter 2) The operational depth of the merge itself is shallow. */ 8276251881Speter 8277251881Speter /* We've already determined the first case. */ 8278251881Speter child->record_noninheritable = 8279251881Speter child->missing_child || child->switched_child; 8280251881Speter 8281251881Speter /* The second case requires a bit more work. */ 8282251881Speter if (i == 0) 8283251881Speter { 8284251881Speter /* If CHILD is the root of the merge target and the 8285251881Speter operational depth is empty or files, then the mere 8286251881Speter existence of operative immediate children means we 8287251881Speter must record non-inheritable mergeinfo. 8288251881Speter 8289251881Speter ### What about svn_depth_immediates? In that case 8290251881Speter ### the merge target needs only normal inheritable 8291251881Speter ### mergeinfo and the target's immediate children will 8292251881Speter ### get non-inheritable mergeinfo, assuming they 8293251881Speter ### need even that. */ 8294251881Speter if (depth < svn_depth_immediates 8295251881Speter && operative_immediate_children 8296251881Speter && apr_hash_count(operative_immediate_children)) 8297251881Speter child->record_noninheritable = TRUE; 8298251881Speter } 8299251881Speter else if (depth == svn_depth_immediates) 8300251881Speter { 8301251881Speter /* An immediate directory child of the merge target, which 8302251881Speter was affected by a --depth=immediates merge, needs 8303251881Speter non-inheritable mergeinfo. */ 8304251881Speter if (svn_hash_gets(operative_immediate_children, 8305251881Speter child->abspath)) 8306251881Speter child->record_noninheritable = TRUE; 8307251881Speter } 8308251881Speter } 8309251881Speter } 8310251881Speter else /* child->record_mergeinfo */ 8311251881Speter { 8312251881Speter /* If CHILD is in NOTIFY_B->CHILDREN_WITH_MERGEINFO simply 8313251881Speter because it had no explicit mergeinfo of its own at the 8314251881Speter start of the merge but is the child of of some path with 8315251881Speter non-inheritable mergeinfo, then the explicit mergeinfo it 8316251881Speter has *now* was set by get_mergeinfo_paths() -- see criteria 8317251881Speter 3 in that function's doc string. So since CHILD->ABSPATH 8318251881Speter was not touched by the merge we can remove the 8319251881Speter mergeinfo. */ 8320251881Speter if (child->child_of_noninheritable) 8321251881Speter SVN_ERR(svn_client__record_wc_mergeinfo(child->abspath, 8322251881Speter NULL, FALSE, 8323251881Speter merge_b->ctx, 8324251881Speter iterpool)); 8325251881Speter } 8326251881Speter } 8327251881Speter 8328251881Speter svn_pool_destroy(iterpool); 8329251881Speter return SVN_NO_ERROR; 8330251881Speter} 8331251881Speter 8332251881Speter/* Helper for do_directory_merge(). 8333251881Speter 8334251881Speter If RESULT_CATALOG is NULL then record mergeinfo describing a merge of 8335251881Speter MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path 8336251881Speter MERGEINFO_FSPATH to the merge target (and possibly its subtrees) described 8337251881Speter by NOTIFY_B->CHILDREN_WITH_MERGEINFO -- see the global comment 8338251881Speter 'THE CHILDREN_WITH_MERGEINFO ARRAY'. Obviously this should only 8339251881Speter be called if recording mergeinfo -- see doc string for RECORD_MERGEINFO(). 8340251881Speter 8341251881Speter If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the 8342251881Speter WC, but instead record it in RESULT_CATALOG, where the keys are absolute 8343251881Speter working copy paths and the values are the new mergeinfos for each. 8344251881Speter Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was 8345251881Speter created in. 8346251881Speter 8347251881Speter DEPTH, NOTIFY_B, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS are all 8348251881Speter cascaded from do_directory_merge's arguments of the same names. 8349251881Speter 8350251881Speter SCRATCH_POOL is used for temporary allocations. 8351251881Speter*/ 8352251881Speterstatic svn_error_t * 8353251881Speterrecord_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog, 8354251881Speter const svn_merge_range_t *merged_range, 8355251881Speter const char *mergeinfo_fspath, 8356251881Speter apr_array_header_t *children_with_mergeinfo, 8357251881Speter svn_depth_t depth, 8358251881Speter svn_boolean_t squelch_mergeinfo_notifications, 8359251881Speter merge_cmd_baton_t *merge_b, 8360251881Speter apr_pool_t *scratch_pool) 8361251881Speter{ 8362251881Speter int i; 8363251881Speter svn_boolean_t is_rollback = (merged_range->start > merged_range->end); 8364251881Speter svn_boolean_t operative_merge; 8365251881Speter 8366251881Speter /* Update the WC mergeinfo here to account for our new 8367251881Speter merges, minus any unresolved conflicts and skips. */ 8368251881Speter 8369251881Speter /* We need a scratch pool for iterations below. */ 8370251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 8371251881Speter 8372251881Speter svn_merge_range_t range = *merged_range; 8373251881Speter 8374251881Speter assert(! merge_b->dry_run); 8375251881Speter 8376251881Speter /* Regardless of what subtrees in MERGE_B->target->abspath might be missing 8377251881Speter could this merge have been operative? */ 8378251881Speter operative_merge = subtree_touched_by_merge(merge_b->target->abspath, 8379251881Speter merge_b, iterpool); 8380251881Speter 8381251881Speter /* If this couldn't be an operative merge then don't bother with 8382251881Speter the added complexity (and user confusion) of non-inheritable ranges. 8383251881Speter There is no harm in subtrees inheriting inoperative mergeinfo. */ 8384251881Speter if (!operative_merge) 8385251881Speter range.inheritable = TRUE; 8386251881Speter 8387251881Speter /* Remove absent children at or under MERGE_B->target->abspath from 8388251881Speter NOTIFY_B->CHILDREN_WITH_MERGEINFO 8389251881Speter before we calculate the merges performed. */ 8390251881Speter remove_absent_children(merge_b->target->abspath, 8391251881Speter children_with_mergeinfo); 8392251881Speter 8393251881Speter /* Determine which subtrees of interest need mergeinfo recorded... */ 8394251881Speter SVN_ERR(flag_subtrees_needing_mergeinfo(operative_merge, &range, 8395251881Speter children_with_mergeinfo, 8396251881Speter mergeinfo_fspath, depth, 8397251881Speter merge_b, iterpool)); 8398251881Speter 8399251881Speter /* ...and then record it. */ 8400251881Speter for (i = 0; i < children_with_mergeinfo->nelts; i++) 8401251881Speter { 8402251881Speter const char *child_repos_path; 8403251881Speter const char *child_merge_src_fspath; 8404251881Speter svn_rangelist_t *child_merge_rangelist; 8405251881Speter apr_hash_t *child_merges; 8406251881Speter svn_client__merge_path_t *child = 8407251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, 8408251881Speter svn_client__merge_path_t *); 8409251881Speter SVN_ERR_ASSERT(child); 8410251881Speter 8411251881Speter svn_pool_clear(iterpool); 8412251881Speter 8413251881Speter if (child->record_mergeinfo) 8414251881Speter { 8415251881Speter child_repos_path = svn_dirent_skip_ancestor(merge_b->target->abspath, 8416251881Speter child->abspath); 8417251881Speter SVN_ERR_ASSERT(child_repos_path != NULL); 8418251881Speter child_merge_src_fspath = svn_fspath__join(mergeinfo_fspath, 8419251881Speter child_repos_path, 8420251881Speter iterpool); 8421251881Speter /* Filter any ranges from each child's natural history before 8422251881Speter setting mergeinfo describing the merge. */ 8423251881Speter SVN_ERR(filter_natural_history_from_mergeinfo( 8424251881Speter &child_merge_rangelist, child_merge_src_fspath, 8425251881Speter child->implicit_mergeinfo, &range, iterpool)); 8426251881Speter 8427251881Speter if (child_merge_rangelist->nelts == 0) 8428251881Speter continue; 8429251881Speter 8430251881Speter if (!squelch_mergeinfo_notifications) 8431251881Speter { 8432251881Speter /* If the merge source has a gap, then don't mention 8433251881Speter those gap revisions in the notification. */ 8434251881Speter remove_source_gap(&range, merge_b->implicit_src_gap); 8435251881Speter notify_mergeinfo_recording(child->abspath, &range, 8436251881Speter merge_b->ctx, iterpool); 8437251881Speter } 8438251881Speter 8439251881Speter /* If we are here we know we will be recording some mergeinfo, but 8440251881Speter before we do, set override mergeinfo on skipped paths so they 8441251881Speter don't incorrectly inherit the mergeinfo we are about to set. */ 8442251881Speter if (i == 0) 8443251881Speter SVN_ERR(record_skips_in_mergeinfo(mergeinfo_fspath, 8444251881Speter child_merge_rangelist, 8445251881Speter is_rollback, merge_b, iterpool)); 8446251881Speter 8447251881Speter /* We may need to record non-inheritable mergeinfo that applies 8448251881Speter only to CHILD->ABSPATH. */ 8449251881Speter if (child->record_noninheritable) 8450251881Speter svn_rangelist__set_inheritance(child_merge_rangelist, FALSE); 8451251881Speter 8452251881Speter /* If CHILD has inherited mergeinfo set it before 8453251881Speter recording the first merge range. */ 8454251881Speter if (child->inherited_mergeinfo) 8455251881Speter SVN_ERR(svn_client__record_wc_mergeinfo( 8456251881Speter child->abspath, 8457251881Speter child->pre_merge_mergeinfo, 8458251881Speter FALSE, merge_b->ctx, 8459251881Speter iterpool)); 8460251881Speter if (merge_b->implicit_src_gap) 8461251881Speter { 8462251881Speter /* If this is a reverse merge reorder CHILD->REMAINING_RANGES 8463251881Speter so it will work with the svn_rangelist_remove API. */ 8464251881Speter if (is_rollback) 8465251881Speter SVN_ERR(svn_rangelist_reverse(child_merge_rangelist, 8466251881Speter iterpool)); 8467251881Speter 8468251881Speter SVN_ERR(svn_rangelist_remove(&child_merge_rangelist, 8469251881Speter merge_b->implicit_src_gap, 8470251881Speter child_merge_rangelist, FALSE, 8471251881Speter iterpool)); 8472251881Speter if (is_rollback) 8473251881Speter SVN_ERR(svn_rangelist_reverse(child_merge_rangelist, 8474251881Speter iterpool)); 8475251881Speter } 8476251881Speter 8477251881Speter child_merges = apr_hash_make(iterpool); 8478251881Speter 8479251881Speter /* The short story: 8480251881Speter 8481251881Speter If we are describing a forward merge, then the naive mergeinfo 8482251881Speter defined by MERGE_SOURCE_PATH:MERGED_RANGE->START: 8483251881Speter MERGE_SOURCE_PATH:MERGED_RANGE->END may contain non-existent 8484251881Speter path-revs or may describe other lines of history. We must 8485251881Speter remove these invalid portion(s) before recording mergeinfo 8486251881Speter describing the merge. 8487251881Speter 8488251881Speter The long story: 8489251881Speter 8490251881Speter If CHILD is the merge target we know that 8491251881Speter MERGE_SOURCE_PATH:MERGED_RANGE->END exists. Further, if there 8492251881Speter were no copies in MERGE_SOURCE_PATH's history going back to 8493251881Speter RANGE->START then we know that 8494251881Speter MERGE_SOURCE_PATH:MERGED_RANGE->START exists too and the two 8495251881Speter describe an unbroken line of history, and thus 8496251881Speter MERGE_SOURCE_PATH:MERGED_RANGE->START: 8497251881Speter MERGE_SOURCE_PATH:MERGED_RANGE->END is a valid description of 8498251881Speter the merge -- see normalize_merge_sources() and the global comment 8499251881Speter 'MERGEINFO MERGE SOURCE NORMALIZATION'. 8500251881Speter 8501251881Speter However, if there *was* a copy, then 8502251881Speter MERGE_SOURCE_PATH:MERGED_RANGE->START doesn't exist or is 8503251881Speter unrelated to MERGE_SOURCE_PATH:MERGED_RANGE->END. Also, we 8504251881Speter don't know if (MERGE_SOURCE_PATH:MERGED_RANGE->START)+1 through 8505251881Speter (MERGE_SOURCE_PATH:MERGED_RANGE->END)-1 actually exist. 8506251881Speter 8507251881Speter If CHILD is a subtree of the merge target, then nothing is 8508251881Speter guaranteed beyond the fact that MERGE_SOURCE_PATH exists at 8509251881Speter MERGED_RANGE->END. */ 8510251881Speter if ((!merge_b->record_only || merge_b->reintegrate_merge) 8511251881Speter && (!is_rollback)) 8512251881Speter { 8513251881Speter svn_error_t *err; 8514251881Speter svn_mergeinfo_t subtree_history_as_mergeinfo; 8515251881Speter svn_rangelist_t *child_merge_src_rangelist; 8516251881Speter svn_client__pathrev_t *subtree_mergeinfo_pathrev 8517251881Speter = svn_client__pathrev_create_with_relpath( 8518251881Speter merge_b->target->loc.repos_root_url, 8519251881Speter merge_b->target->loc.repos_uuid, 8520251881Speter merged_range->end, child_merge_src_fspath + 1, 8521251881Speter iterpool); 8522251881Speter 8523251881Speter /* Confirm that the naive mergeinfo we want to set on 8524251881Speter CHILD->ABSPATH both exists and is part of 8525251881Speter (MERGE_SOURCE_PATH+CHILD_REPOS_PATH)@MERGED_RANGE->END's 8526251881Speter history. */ 8527251881Speter /* We know MERGED_RANGE->END is younger than MERGE_RANGE->START 8528251881Speter because we only do this for forward merges. */ 8529251881Speter err = svn_client__get_history_as_mergeinfo( 8530251881Speter &subtree_history_as_mergeinfo, NULL, 8531251881Speter subtree_mergeinfo_pathrev, 8532251881Speter merged_range->end, merged_range->start, 8533251881Speter merge_b->ra_session2, merge_b->ctx, iterpool); 8534251881Speter 8535251881Speter /* If CHILD is a subtree it may have been deleted prior to 8536251881Speter MERGED_RANGE->END so the above call to get its history 8537251881Speter will fail. */ 8538251881Speter if (err) 8539251881Speter { 8540251881Speter if (err->apr_err != SVN_ERR_FS_NOT_FOUND) 8541251881Speter return svn_error_trace(err); 8542251881Speter svn_error_clear(err); 8543251881Speter } 8544251881Speter else 8545251881Speter { 8546251881Speter child_merge_src_rangelist = svn_hash_gets( 8547251881Speter subtree_history_as_mergeinfo, 8548251881Speter child_merge_src_fspath); 8549251881Speter SVN_ERR(svn_rangelist_intersect(&child_merge_rangelist, 8550251881Speter child_merge_rangelist, 8551251881Speter child_merge_src_rangelist, 8552251881Speter FALSE, iterpool)); 8553251881Speter if (child->record_noninheritable) 8554251881Speter svn_rangelist__set_inheritance(child_merge_rangelist, 8555251881Speter FALSE); 8556251881Speter } 8557251881Speter } 8558251881Speter 8559251881Speter svn_hash_sets(child_merges, child->abspath, child_merge_rangelist); 8560251881Speter SVN_ERR(update_wc_mergeinfo(result_catalog, 8561251881Speter child->abspath, 8562251881Speter child_merge_src_fspath, 8563251881Speter child_merges, is_rollback, 8564251881Speter merge_b->ctx, iterpool)); 8565251881Speter 8566251881Speter /* Once is enough: We don't need to record mergeinfo describing 8567251881Speter the merge a second. If CHILD->ABSPATH is in 8568251881Speter MERGE_B->ADDED_ABSPATHS, we'll do just that, so remove the 8569251881Speter former from the latter. */ 8570251881Speter svn_hash_sets(merge_b->added_abspaths, child->abspath, NULL); 8571251881Speter } 8572251881Speter 8573251881Speter /* Elide explicit subtree mergeinfo whether or not we updated it. */ 8574251881Speter if (i > 0) 8575251881Speter { 8576251881Speter svn_boolean_t in_switched_subtree = FALSE; 8577251881Speter 8578251881Speter if (child->switched) 8579251881Speter in_switched_subtree = TRUE; 8580251881Speter else if (i > 1) 8581251881Speter { 8582251881Speter /* Check if CHILD is part of a switched subtree */ 8583251881Speter svn_client__merge_path_t *parent; 8584251881Speter int j = i - 1; 8585251881Speter for (; j > 0; j--) 8586251881Speter { 8587251881Speter parent = APR_ARRAY_IDX(children_with_mergeinfo, 8588251881Speter j, svn_client__merge_path_t *); 8589251881Speter if (parent 8590251881Speter && parent->switched 8591251881Speter && svn_dirent_is_ancestor(parent->abspath, 8592251881Speter child->abspath)) 8593251881Speter { 8594251881Speter in_switched_subtree = TRUE; 8595251881Speter break; 8596251881Speter } 8597251881Speter } 8598251881Speter } 8599251881Speter 8600251881Speter /* Allow mergeinfo on switched subtrees to elide to the 8601251881Speter repository. Otherwise limit elision to the merge target 8602251881Speter for now. do_directory_merge() will eventually try to 8603251881Speter elide that when the merge is complete. */ 8604251881Speter SVN_ERR(svn_client__elide_mergeinfo( 8605251881Speter child->abspath, 8606251881Speter in_switched_subtree ? NULL : merge_b->target->abspath, 8607251881Speter merge_b->ctx, iterpool)); 8608251881Speter } 8609251881Speter } /* (i = 0; i < notify_b->children_with_mergeinfo->nelts; i++) */ 8610251881Speter 8611251881Speter svn_pool_destroy(iterpool); 8612251881Speter return SVN_NO_ERROR; 8613251881Speter} 8614251881Speter 8615251881Speter/* Helper for do_directory_merge(). 8616251881Speter 8617251881Speter Record mergeinfo describing a merge of 8618251881Speter MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path 8619251881Speter MERGEINFO_FSPATH to each path in ADDED_ABSPATHS which has explicit 8620251881Speter mergeinfo or is the immediate child of a parent with explicit 8621251881Speter non-inheritable mergeinfo. 8622251881Speter 8623251881Speter DEPTH, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS, are 8624251881Speter cascaded from do_directory_merge's arguments of the same names. 8625251881Speter 8626251881Speter Note: This is intended to support forward merges only, i.e. 8627251881Speter MERGED_RANGE->START must be older than MERGED_RANGE->END. 8628251881Speter*/ 8629251881Speterstatic svn_error_t * 8630251881Speterrecord_mergeinfo_for_added_subtrees( 8631251881Speter svn_merge_range_t *merged_range, 8632251881Speter const char *mergeinfo_fspath, 8633251881Speter svn_depth_t depth, 8634251881Speter svn_boolean_t squelch_mergeinfo_notifications, 8635251881Speter apr_hash_t *added_abspaths, 8636251881Speter merge_cmd_baton_t *merge_b, 8637251881Speter apr_pool_t *pool) 8638251881Speter{ 8639251881Speter apr_pool_t *iterpool; 8640251881Speter apr_hash_index_t *hi; 8641251881Speter 8642251881Speter /* If no paths were added by the merge then we have nothing to do. */ 8643251881Speter if (!added_abspaths) 8644251881Speter return SVN_NO_ERROR; 8645251881Speter 8646251881Speter SVN_ERR_ASSERT(merged_range->start < merged_range->end); 8647251881Speter 8648251881Speter iterpool = svn_pool_create(pool); 8649251881Speter for (hi = apr_hash_first(pool, added_abspaths); hi; hi = apr_hash_next(hi)) 8650251881Speter { 8651251881Speter const char *added_abspath = svn__apr_hash_index_key(hi); 8652251881Speter const char *dir_abspath; 8653251881Speter svn_mergeinfo_t parent_mergeinfo; 8654251881Speter svn_mergeinfo_t added_path_mergeinfo; 8655251881Speter 8656251881Speter svn_pool_clear(iterpool); 8657251881Speter dir_abspath = svn_dirent_dirname(added_abspath, iterpool); 8658251881Speter 8659251881Speter /* Grab the added path's explicit mergeinfo. */ 8660251881Speter SVN_ERR(svn_client__get_wc_mergeinfo(&added_path_mergeinfo, NULL, 8661251881Speter svn_mergeinfo_explicit, 8662251881Speter added_abspath, NULL, NULL, FALSE, 8663251881Speter merge_b->ctx, iterpool, iterpool)); 8664251881Speter 8665251881Speter /* If the added path doesn't have explicit mergeinfo, does its immediate 8666251881Speter parent have non-inheritable mergeinfo? */ 8667251881Speter if (!added_path_mergeinfo) 8668251881Speter SVN_ERR(svn_client__get_wc_mergeinfo(&parent_mergeinfo, NULL, 8669251881Speter svn_mergeinfo_explicit, 8670251881Speter dir_abspath, NULL, NULL, FALSE, 8671251881Speter merge_b->ctx, 8672251881Speter iterpool, iterpool)); 8673251881Speter 8674251881Speter if (added_path_mergeinfo 8675251881Speter || svn_mergeinfo__is_noninheritable(parent_mergeinfo, iterpool)) 8676251881Speter { 8677251881Speter svn_node_kind_t added_path_kind; 8678251881Speter svn_mergeinfo_t merge_mergeinfo; 8679251881Speter svn_mergeinfo_t adds_history_as_mergeinfo; 8680251881Speter svn_rangelist_t *rangelist; 8681251881Speter const char *rel_added_path; 8682251881Speter const char *added_path_mergeinfo_fspath; 8683251881Speter svn_client__pathrev_t *added_path_pathrev; 8684251881Speter 8685251881Speter SVN_ERR(svn_wc_read_kind2(&added_path_kind, merge_b->ctx->wc_ctx, 8686251881Speter added_abspath, FALSE, FALSE, iterpool)); 8687251881Speter 8688251881Speter /* Calculate the naive mergeinfo describing the merge. */ 8689251881Speter merge_mergeinfo = apr_hash_make(iterpool); 8690251881Speter rangelist = svn_rangelist__initialize( 8691251881Speter merged_range->start, merged_range->end, 8692251881Speter ((added_path_kind == svn_node_file) 8693251881Speter || (!(depth == svn_depth_infinity 8694251881Speter || depth == svn_depth_immediates))), 8695251881Speter iterpool); 8696251881Speter 8697251881Speter /* Create the new mergeinfo path for added_path's mergeinfo. 8698251881Speter (added_abspath had better be a child of MERGE_B->target->abspath 8699251881Speter or something is *really* wrong.) */ 8700251881Speter rel_added_path = svn_dirent_is_child(merge_b->target->abspath, 8701251881Speter added_abspath, iterpool); 8702251881Speter SVN_ERR_ASSERT(rel_added_path); 8703251881Speter added_path_mergeinfo_fspath = svn_fspath__join(mergeinfo_fspath, 8704251881Speter rel_added_path, 8705251881Speter iterpool); 8706251881Speter svn_hash_sets(merge_mergeinfo, added_path_mergeinfo_fspath, 8707251881Speter rangelist); 8708251881Speter 8709251881Speter /* Don't add new mergeinfo to describe the merge if that mergeinfo 8710251881Speter contains non-existent merge sources. 8711251881Speter 8712251881Speter We know that MERGEINFO_PATH/rel_added_path's history does not 8713251881Speter span MERGED_RANGE->START:MERGED_RANGE->END but rather that it 8714251881Speter was added at some revions greater than MERGED_RANGE->START 8715251881Speter (assuming this is a forward merge). It may have been added, 8716251881Speter deleted, and re-added many times. The point is that we cannot 8717251881Speter blindly apply the naive mergeinfo calculated above because it 8718251881Speter will describe non-existent merge sources. To avoid this we get 8719251881Speter take the intersection of the naive mergeinfo with 8720251881Speter MERGEINFO_PATH/rel_added_path's history. */ 8721251881Speter added_path_pathrev = svn_client__pathrev_create_with_relpath( 8722251881Speter merge_b->target->loc.repos_root_url, 8723251881Speter merge_b->target->loc.repos_uuid, 8724251881Speter MAX(merged_range->start, merged_range->end), 8725251881Speter added_path_mergeinfo_fspath + 1, iterpool); 8726251881Speter SVN_ERR(svn_client__get_history_as_mergeinfo( 8727251881Speter &adds_history_as_mergeinfo, NULL, 8728251881Speter added_path_pathrev, 8729251881Speter MAX(merged_range->start, merged_range->end), 8730251881Speter MIN(merged_range->start, merged_range->end), 8731251881Speter merge_b->ra_session2, merge_b->ctx, iterpool)); 8732251881Speter 8733251881Speter SVN_ERR(svn_mergeinfo_intersect2(&merge_mergeinfo, 8734251881Speter merge_mergeinfo, 8735251881Speter adds_history_as_mergeinfo, 8736251881Speter FALSE, iterpool, iterpool)); 8737251881Speter 8738251881Speter /* Combine the explicit mergeinfo on the added path (if any) 8739251881Speter with the mergeinfo describing this merge. */ 8740251881Speter if (added_path_mergeinfo) 8741251881Speter SVN_ERR(svn_mergeinfo_merge2(merge_mergeinfo, 8742251881Speter added_path_mergeinfo, 8743251881Speter iterpool, iterpool)); 8744251881Speter SVN_ERR(svn_client__record_wc_mergeinfo( 8745251881Speter added_abspath, merge_mergeinfo, 8746251881Speter !squelch_mergeinfo_notifications, merge_b->ctx, iterpool)); 8747251881Speter } 8748251881Speter } 8749251881Speter svn_pool_destroy(iterpool); 8750251881Speter 8751251881Speter return SVN_NO_ERROR; 8752251881Speter} 8753251881Speter/* Baton structure for log_noop_revs. */ 8754251881Spetertypedef struct log_noop_baton_t 8755251881Speter{ 8756251881Speter /* See the comment 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start 8757251881Speter of this file.*/ 8758251881Speter apr_array_header_t *children_with_mergeinfo; 8759251881Speter 8760251881Speter /* Absolute repository path of younger of the two merge sources 8761251881Speter being diffed. */ 8762251881Speter const char *source_fspath; 8763251881Speter 8764251881Speter /* The merge target. */ 8765251881Speter const merge_target_t *target; 8766251881Speter 8767251881Speter /* Initially empty rangelists allocated in POOL. The rangelists are 8768251881Speter * populated across multiple invocations of log_noop_revs(). */ 8769251881Speter svn_rangelist_t *operative_ranges; 8770251881Speter svn_rangelist_t *merged_ranges; 8771251881Speter 8772251881Speter /* Pool to store the rangelists. */ 8773251881Speter apr_pool_t *pool; 8774251881Speter} log_noop_baton_t; 8775251881Speter 8776251881Speter/* Helper for log_noop_revs: Merge a svn_merge_range_t representation of 8777251881Speter REVISION into RANGELIST. New elements added to rangelist are allocated 8778251881Speter in RESULT_POOL. 8779251881Speter 8780251881Speter This is *not* a general purpose rangelist merge but a special replacement 8781251881Speter for svn_rangelist_merge when REVISION is guaranteed to be younger than any 8782251881Speter element in RANGELIST. svn_rangelist_merge is O(n) worst-case (i.e. when 8783251881Speter all the ranges in output rangelist are older than the incoming changes). 8784251881Speter This turns the special case of a single incoming younger range into O(1). 8785251881Speter */ 8786251881Speterstatic svn_error_t * 8787251881Speterrangelist_merge_revision(svn_rangelist_t *rangelist, 8788251881Speter svn_revnum_t revision, 8789251881Speter apr_pool_t *result_pool) 8790251881Speter{ 8791251881Speter svn_merge_range_t *new_range; 8792251881Speter if (rangelist->nelts) 8793251881Speter { 8794251881Speter svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1, 8795251881Speter svn_merge_range_t *); 8796251881Speter if (range->end == revision - 1) 8797251881Speter { 8798251881Speter /* REVISION is adjacent to the youngest range in RANGELIST 8799251881Speter so we can simply expand that range to encompass REVISION. */ 8800251881Speter range->end = revision; 8801251881Speter return SVN_NO_ERROR; 8802251881Speter } 8803251881Speter } 8804251881Speter new_range = apr_palloc(result_pool, sizeof(*new_range)); 8805251881Speter new_range->start = revision - 1; 8806251881Speter new_range->end = revision; 8807251881Speter new_range->inheritable = TRUE; 8808251881Speter 8809251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = new_range; 8810251881Speter 8811251881Speter return SVN_NO_ERROR; 8812251881Speter} 8813251881Speter 8814251881Speter/* Implements the svn_log_entry_receiver_t interface. 8815251881Speter 8816251881Speter BATON is an log_noop_baton_t *. 8817251881Speter 8818251881Speter Add LOG_ENTRY->REVISION to BATON->OPERATIVE_RANGES. 8819251881Speter 8820251881Speter If LOG_ENTRY->REVISION has already been fully merged to 8821251881Speter BATON->target->abspath per the mergeinfo in BATON->CHILDREN_WITH_MERGEINFO, 8822251881Speter then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES. 8823251881Speter 8824251881Speter Use SCRATCH_POOL for temporary allocations. Allocate additions to 8825251881Speter BATON->MERGED_RANGES and BATON->OPERATIVE_RANGES in BATON->POOL. 8826251881Speter 8827251881Speter Note: This callback must be invoked from oldest LOG_ENTRY->REVISION 8828251881Speter to youngest LOG_ENTRY->REVISION -- see rangelist_merge_revision(). 8829251881Speter*/ 8830251881Speterstatic svn_error_t * 8831251881Speterlog_noop_revs(void *baton, 8832251881Speter svn_log_entry_t *log_entry, 8833251881Speter apr_pool_t *scratch_pool) 8834251881Speter{ 8835251881Speter log_noop_baton_t *log_gap_baton = baton; 8836251881Speter apr_hash_index_t *hi; 8837251881Speter svn_revnum_t revision; 8838251881Speter svn_boolean_t log_entry_rev_required = FALSE; 8839251881Speter 8840251881Speter revision = log_entry->revision; 8841251881Speter 8842251881Speter /* It's possible that authz restrictions on the merge source prevent us 8843251881Speter from knowing about any of the changes for LOG_ENTRY->REVISION. */ 8844251881Speter if (!log_entry->changed_paths2) 8845251881Speter return SVN_NO_ERROR; 8846251881Speter 8847251881Speter /* Unconditionally add LOG_ENTRY->REVISION to BATON->OPERATIVE_MERGES. */ 8848251881Speter SVN_ERR(rangelist_merge_revision(log_gap_baton->operative_ranges, 8849251881Speter revision, 8850251881Speter log_gap_baton->pool)); 8851251881Speter 8852251881Speter /* Examine each path affected by LOG_ENTRY->REVISION. If the explicit or 8853251881Speter inherited mergeinfo for *all* of the corresponding paths under 8854251881Speter BATON->target->abspath reflects that LOG_ENTRY->REVISION has been 8855251881Speter merged, then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES. */ 8856251881Speter for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths2); 8857251881Speter hi; 8858251881Speter hi = apr_hash_next(hi)) 8859251881Speter { 8860251881Speter const char *fspath = svn__apr_hash_index_key(hi); 8861251881Speter const char *rel_path; 8862251881Speter const char *cwmi_abspath; 8863251881Speter svn_rangelist_t *paths_explicit_rangelist = NULL; 8864251881Speter svn_boolean_t mergeinfo_inherited = FALSE; 8865251881Speter 8866251881Speter /* Adjust REL_PATH so it is relative to the merge source then use it to 8867251881Speter calculate what path in the merge target would be affected by this 8868251881Speter revision. */ 8869251881Speter rel_path = svn_fspath__skip_ancestor(log_gap_baton->source_fspath, 8870251881Speter fspath); 8871251881Speter /* Is PATH even within the merge target? If it isn't we 8872251881Speter can disregard it altogether. */ 8873251881Speter if (rel_path == NULL) 8874251881Speter continue; 8875251881Speter cwmi_abspath = svn_dirent_join(log_gap_baton->target->abspath, 8876251881Speter rel_path, scratch_pool); 8877251881Speter 8878251881Speter /* Find any explicit or inherited mergeinfo for PATH. */ 8879251881Speter while (!log_entry_rev_required) 8880251881Speter { 8881251881Speter svn_client__merge_path_t *child = get_child_with_mergeinfo( 8882251881Speter log_gap_baton->children_with_mergeinfo, cwmi_abspath); 8883251881Speter 8884251881Speter if (child && child->pre_merge_mergeinfo) 8885251881Speter { 8886251881Speter /* Found some explicit mergeinfo, grab any ranges 8887251881Speter for PATH. */ 8888251881Speter paths_explicit_rangelist = 8889251881Speter svn_hash_gets(child->pre_merge_mergeinfo, fspath); 8890251881Speter break; 8891251881Speter } 8892251881Speter 8893251881Speter if (cwmi_abspath[0] == '\0' 8894251881Speter || svn_dirent_is_root(cwmi_abspath, strlen(cwmi_abspath)) 8895251881Speter || strcmp(log_gap_baton->target->abspath, cwmi_abspath) == 0) 8896251881Speter { 8897251881Speter /* Can't crawl any higher. */ 8898251881Speter break; 8899251881Speter } 8900251881Speter 8901251881Speter /* Didn't find anything so crawl up to the parent. */ 8902251881Speter cwmi_abspath = svn_dirent_dirname(cwmi_abspath, scratch_pool); 8903251881Speter fspath = svn_fspath__dirname(fspath, scratch_pool); 8904251881Speter 8905251881Speter /* At this point *if* we find mergeinfo it will be inherited. */ 8906251881Speter mergeinfo_inherited = TRUE; 8907251881Speter } 8908251881Speter 8909251881Speter if (paths_explicit_rangelist) 8910251881Speter { 8911251881Speter svn_rangelist_t *intersecting_range; 8912251881Speter svn_rangelist_t *rangelist; 8913251881Speter 8914251881Speter rangelist = svn_rangelist__initialize(revision - 1, revision, TRUE, 8915251881Speter scratch_pool); 8916251881Speter 8917251881Speter /* If PATH inherited mergeinfo we must consider inheritance in the 8918251881Speter event the inherited mergeinfo is actually non-inheritable. */ 8919251881Speter SVN_ERR(svn_rangelist_intersect(&intersecting_range, 8920251881Speter paths_explicit_rangelist, 8921251881Speter rangelist, 8922251881Speter mergeinfo_inherited, scratch_pool)); 8923251881Speter 8924251881Speter if (intersecting_range->nelts == 0) 8925251881Speter log_entry_rev_required = TRUE; 8926251881Speter } 8927251881Speter else 8928251881Speter { 8929251881Speter log_entry_rev_required = TRUE; 8930251881Speter } 8931251881Speter } 8932251881Speter 8933251881Speter if (!log_entry_rev_required) 8934251881Speter SVN_ERR(rangelist_merge_revision(log_gap_baton->merged_ranges, 8935251881Speter revision, 8936251881Speter log_gap_baton->pool)); 8937251881Speter 8938251881Speter return SVN_NO_ERROR; 8939251881Speter} 8940251881Speter 8941251881Speter/* Helper for do_directory_merge(). 8942251881Speter 8943251881Speter SOURCE is cascaded from the argument of the same name in 8944251881Speter do_directory_merge(). TARGET is the merge target. RA_SESSION is the 8945251881Speter session for SOURCE->loc2. 8946251881Speter 8947251881Speter Find all the ranges required by subtrees in 8948251881Speter CHILDREN_WITH_MERGEINFO that are *not* required by 8949251881Speter TARGET->abspath (i.e. CHILDREN_WITH_MERGEINFO[0]). If such 8950251881Speter ranges exist, then find any subset of ranges which, if merged, would be 8951251881Speter inoperative. Finally, if any inoperative ranges are found then remove 8952251881Speter these ranges from all of the subtree's REMAINING_RANGES. 8953251881Speter 8954251881Speter This function should only be called when honoring mergeinfo during 8955251881Speter forward merges (i.e. SOURCE->rev1 < SOURCE->rev2). 8956251881Speter*/ 8957251881Speterstatic svn_error_t * 8958251881Speterremove_noop_subtree_ranges(const merge_source_t *source, 8959251881Speter const merge_target_t *target, 8960251881Speter svn_ra_session_t *ra_session, 8961251881Speter apr_array_header_t *children_with_mergeinfo, 8962251881Speter apr_pool_t *result_pool, 8963251881Speter apr_pool_t *scratch_pool) 8964251881Speter{ 8965251881Speter /* ### Do we need to check that we are at a uniform working revision? */ 8966251881Speter int i; 8967251881Speter svn_client__merge_path_t *root_child = 8968251881Speter APR_ARRAY_IDX(children_with_mergeinfo, 0, svn_client__merge_path_t *); 8969251881Speter svn_rangelist_t *requested_ranges; 8970251881Speter svn_rangelist_t *subtree_gap_ranges; 8971251881Speter svn_rangelist_t *subtree_remaining_ranges; 8972251881Speter log_noop_baton_t log_gap_baton; 8973251881Speter svn_merge_range_t *oldest_gap_rev; 8974251881Speter svn_merge_range_t *youngest_gap_rev; 8975251881Speter svn_rangelist_t *inoperative_ranges; 8976251881Speter apr_pool_t *iterpool; 8977251881Speter const char *longest_common_subtree_ancestor = NULL; 8978251881Speter svn_error_t *err; 8979251881Speter 8980251881Speter assert(session_url_is(ra_session, source->loc2->url, scratch_pool)); 8981251881Speter 8982251881Speter /* This function is only intended to work with forward merges. */ 8983251881Speter if (source->loc1->rev > source->loc2->rev) 8984251881Speter return SVN_NO_ERROR; 8985251881Speter 8986251881Speter /* Another easy out: There are no subtrees. */ 8987251881Speter if (children_with_mergeinfo->nelts < 2) 8988251881Speter return SVN_NO_ERROR; 8989251881Speter 8990251881Speter subtree_remaining_ranges = apr_array_make(scratch_pool, 1, 8991251881Speter sizeof(svn_merge_range_t *)); 8992251881Speter 8993251881Speter /* Given the requested merge of SOURCE->rev1:rev2 might there be any 8994251881Speter part of this range required for subtrees but not for the target? */ 8995251881Speter requested_ranges = svn_rangelist__initialize(MIN(source->loc1->rev, 8996251881Speter source->loc2->rev), 8997251881Speter MAX(source->loc1->rev, 8998251881Speter source->loc2->rev), 8999251881Speter TRUE, scratch_pool); 9000251881Speter SVN_ERR(svn_rangelist_remove(&subtree_gap_ranges, 9001251881Speter root_child->remaining_ranges, 9002251881Speter requested_ranges, FALSE, scratch_pool)); 9003251881Speter 9004251881Speter /* Early out, nothing to operate on */ 9005251881Speter if (!subtree_gap_ranges->nelts) 9006251881Speter return SVN_NO_ERROR; 9007251881Speter 9008251881Speter /* Create a rangelist describing every range required across all subtrees. */ 9009251881Speter iterpool = svn_pool_create(scratch_pool); 9010251881Speter for (i = 1; i < children_with_mergeinfo->nelts; i++) 9011251881Speter { 9012251881Speter svn_client__merge_path_t *child = 9013251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); 9014251881Speter 9015251881Speter svn_pool_clear(iterpool); 9016251881Speter 9017251881Speter /* Issue #4269: Keep track of the longest common ancestor of all the 9018251881Speter subtrees which require merges. This may be a child of 9019251881Speter TARGET->ABSPATH, which will allow us to narrow the log request 9020251881Speter below. */ 9021251881Speter if (child->remaining_ranges && child->remaining_ranges->nelts) 9022251881Speter { 9023251881Speter if (longest_common_subtree_ancestor) 9024251881Speter longest_common_subtree_ancestor = svn_dirent_get_longest_ancestor( 9025251881Speter longest_common_subtree_ancestor, child->abspath, scratch_pool); 9026251881Speter else 9027251881Speter longest_common_subtree_ancestor = child->abspath; 9028251881Speter } 9029251881Speter 9030251881Speter /* CHILD->REMAINING_RANGES will be NULL if child is absent. */ 9031251881Speter if (child->remaining_ranges && child->remaining_ranges->nelts) 9032251881Speter SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges, 9033251881Speter child->remaining_ranges, 9034251881Speter scratch_pool, iterpool)); 9035251881Speter } 9036251881Speter svn_pool_destroy(iterpool); 9037251881Speter 9038251881Speter /* It's possible that none of the subtrees had any remaining ranges. */ 9039251881Speter if (!subtree_remaining_ranges->nelts) 9040251881Speter return SVN_NO_ERROR; 9041251881Speter 9042251881Speter /* Ok, *finally* we can answer what part(s) of SOURCE->rev1:rev2 are 9043251881Speter required for the subtrees but not the target. */ 9044251881Speter SVN_ERR(svn_rangelist_intersect(&subtree_gap_ranges, 9045251881Speter subtree_gap_ranges, 9046251881Speter subtree_remaining_ranges, FALSE, 9047251881Speter scratch_pool)); 9048251881Speter 9049251881Speter /* Another early out */ 9050251881Speter if (!subtree_gap_ranges->nelts) 9051251881Speter return SVN_NO_ERROR; 9052251881Speter 9053251881Speter /* One or more subtrees need some revisions that the target doesn't need. 9054251881Speter Use log to determine if any of these revisions are inoperative. */ 9055251881Speter oldest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges, 0, svn_merge_range_t *); 9056251881Speter youngest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges, 9057251881Speter subtree_gap_ranges->nelts - 1, svn_merge_range_t *); 9058251881Speter 9059251881Speter /* Set up the log baton. */ 9060251881Speter log_gap_baton.children_with_mergeinfo = children_with_mergeinfo; 9061251881Speter log_gap_baton.source_fspath 9062251881Speter = svn_client__pathrev_fspath(source->loc2, result_pool); 9063251881Speter log_gap_baton.target = target; 9064251881Speter log_gap_baton.merged_ranges = apr_array_make(scratch_pool, 0, 9065251881Speter sizeof(svn_revnum_t *)); 9066251881Speter log_gap_baton.operative_ranges = apr_array_make(scratch_pool, 0, 9067251881Speter sizeof(svn_revnum_t *)); 9068251881Speter log_gap_baton.pool = svn_pool_create(scratch_pool); 9069251881Speter 9070251881Speter /* Find the longest common ancestor of all subtrees relative to 9071251881Speter RA_SESSION's URL. */ 9072251881Speter if (longest_common_subtree_ancestor) 9073251881Speter longest_common_subtree_ancestor = 9074251881Speter svn_dirent_skip_ancestor(target->abspath, 9075251881Speter longest_common_subtree_ancestor); 9076251881Speter else 9077251881Speter longest_common_subtree_ancestor = ""; 9078251881Speter 9079251881Speter /* Invoke the svn_log_entry_receiver_t receiver log_noop_revs() from 9080251881Speter oldest to youngest. The receiver is optimized to add ranges to 9081251881Speter log_gap_baton.merged_ranges and log_gap_baton.operative_ranges, but 9082251881Speter requires that the revs arrive oldest to youngest -- see log_noop_revs() 9083251881Speter and rangelist_merge_revision(). */ 9084251881Speter err = get_log(ra_session, longest_common_subtree_ancestor, 9085251881Speter oldest_gap_rev->start + 1, youngest_gap_rev->end, TRUE, 9086251881Speter log_noop_revs, &log_gap_baton, scratch_pool); 9087251881Speter 9088251881Speter /* It's possible that the only subtrees with mergeinfo in TARGET don't have 9089251881Speter any corresponding subtree in SOURCE between SOURCE->REV1 < SOURCE->REV2. 9090251881Speter So it's also possible that we may ask for the logs of non-existent paths. 9091251881Speter If we do, then assume that no subtree requires any ranges that are not 9092251881Speter already required by the TARGET. */ 9093251881Speter if (err) 9094251881Speter { 9095251881Speter if (err->apr_err != SVN_ERR_FS_NOT_FOUND 9096251881Speter && longest_common_subtree_ancestor[0] != '\0') 9097251881Speter return svn_error_trace(err); 9098251881Speter 9099251881Speter /* Asked about a non-existent subtree in SOURCE. */ 9100251881Speter svn_error_clear(err); 9101251881Speter log_gap_baton.merged_ranges = 9102251881Speter svn_rangelist__initialize(oldest_gap_rev->start, 9103251881Speter youngest_gap_rev->end, 9104251881Speter TRUE, scratch_pool); 9105251881Speter } 9106251881Speter else 9107251881Speter { 9108251881Speter inoperative_ranges = svn_rangelist__initialize(oldest_gap_rev->start, 9109251881Speter youngest_gap_rev->end, 9110251881Speter TRUE, scratch_pool); 9111251881Speter SVN_ERR(svn_rangelist_remove(&(inoperative_ranges), 9112251881Speter log_gap_baton.operative_ranges, 9113251881Speter inoperative_ranges, FALSE, scratch_pool)); 9114251881Speter SVN_ERR(svn_rangelist_merge2(log_gap_baton.merged_ranges, inoperative_ranges, 9115251881Speter scratch_pool, scratch_pool)); 9116251881Speter } 9117251881Speter 9118251881Speter for (i = 1; i < children_with_mergeinfo->nelts; i++) 9119251881Speter { 9120251881Speter svn_client__merge_path_t *child = 9121251881Speter APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *); 9122251881Speter 9123251881Speter /* CHILD->REMAINING_RANGES will be NULL if child is absent. */ 9124251881Speter if (child->remaining_ranges && child->remaining_ranges->nelts) 9125251881Speter { 9126251881Speter /* Remove inoperative ranges from all children so we don't perform 9127251881Speter inoperative editor drives. */ 9128251881Speter SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges), 9129251881Speter log_gap_baton.merged_ranges, 9130251881Speter child->remaining_ranges, 9131251881Speter FALSE, result_pool)); 9132251881Speter } 9133251881Speter } 9134251881Speter 9135251881Speter svn_pool_destroy(log_gap_baton.pool); 9136251881Speter 9137251881Speter return SVN_NO_ERROR; 9138251881Speter} 9139251881Speter 9140251881Speter/* Perform a merge of changes in SOURCE to the working copy path 9141251881Speter TARGET_ABSPATH. Both URLs in SOURCE, and TARGET_ABSPATH all represent 9142251881Speter directories -- for the single file case, the caller should use 9143251881Speter do_file_merge(). 9144251881Speter 9145251881Speter CHILDREN_WITH_MERGEINFO and MERGE_B describe the merge being performed 9146251881Speter As this function is for a mergeinfo-aware merge, SOURCE->ancestral 9147251881Speter should be TRUE, and SOURCE->loc1 must be a historical ancestor of 9148251881Speter SOURCE->loc2, or vice-versa (see `MERGEINFO MERGE SOURCE NORMALIZATION' 9149251881Speter for more requirements around SOURCE). 9150251881Speter 9151251881Speter Mergeinfo changes will be recorded unless MERGE_B->dry_run is true. 9152251881Speter 9153251881Speter If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE, 9154251881Speter and MERGE_B->CTX->NOTIFY_FUNC2 is not NULL, then call 9155251881Speter MERGE_B->CTX->NOTIFY_FUNC2 with MERGE_B->CTX->NOTIFY_BATON2 and a 9156251881Speter svn_wc_notify_merge_record_info_begin notification before any mergeinfo 9157251881Speter changes are made to describe the merge performed. 9158251881Speter 9159251881Speter If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG 9160251881Speter is not NULL, then don't record the new mergeinfo on the WC, but instead 9161251881Speter record it in RESULT_CATALOG, where the keys are absolute working copy 9162251881Speter paths and the values are the new mergeinfos for each. Allocate additions 9163251881Speter to RESULT_CATALOG in pool which RESULT_CATALOG was created in. 9164251881Speter 9165251881Speter Handle DEPTH as documented for svn_client_merge5(). 9166251881Speter 9167251881Speter CONFLICT_REPORT is as documented for do_directory_merge(). 9168251881Speter 9169251881Speter Perform any temporary allocations in SCRATCH_POOL. 9170251881Speter 9171251881Speter NOTE: This is a wrapper around drive_merge_report_editor() which 9172251881Speter handles the complexities inherent to situations where a given 9173251881Speter directory's children may have intersecting merges (because they 9174251881Speter meet one or more of the criteria described in get_mergeinfo_paths()). 9175251881Speter*/ 9176251881Speterstatic svn_error_t * 9177251881Speterdo_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog, 9178251881Speter single_range_conflict_report_t **conflict_report, 9179251881Speter const merge_source_t *source, 9180251881Speter const char *target_abspath, 9181251881Speter apr_array_header_t *children_with_mergeinfo, 9182251881Speter const svn_diff_tree_processor_t *processor, 9183251881Speter svn_depth_t depth, 9184251881Speter svn_boolean_t squelch_mergeinfo_notifications, 9185251881Speter merge_cmd_baton_t *merge_b, 9186251881Speter apr_pool_t *result_pool, 9187251881Speter apr_pool_t *scratch_pool) 9188251881Speter{ 9189251881Speter /* The range defining the mergeinfo we will record to describe the merge 9190251881Speter (assuming we are recording mergeinfo 9191251881Speter 9192251881Speter Note: This may be a subset of SOURCE->rev1:rev2 if 9193251881Speter populate_remaining_ranges() determines that some part of 9194251881Speter SOURCE->rev1:rev2 has already been wholly merged to TARGET_ABSPATH. 9195251881Speter Also, the actual editor drive(s) may be a subset of RANGE, if 9196251881Speter remove_noop_subtree_ranges() and/or fix_deleted_subtree_ranges() 9197251881Speter further tweak things. */ 9198251881Speter svn_merge_range_t range; 9199251881Speter 9200251881Speter svn_ra_session_t *ra_session; 9201251881Speter svn_client__merge_path_t *target_merge_path; 9202251881Speter svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev); 9203251881Speter 9204251881Speter SVN_ERR_ASSERT(source->ancestral); 9205251881Speter 9206251881Speter /*** If we get here, we're dealing with related sources from the 9207251881Speter same repository as the target -- merge tracking might be 9208251881Speter happenin'! ***/ 9209251881Speter 9210251881Speter *conflict_report = NULL; 9211251881Speter 9212251881Speter /* Point our RA_SESSION to the URL of our youngest merge source side. */ 9213251881Speter ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2; 9214251881Speter 9215251881Speter /* Fill NOTIFY_B->CHILDREN_WITH_MERGEINFO with child paths (const 9216251881Speter svn_client__merge_path_t *) which might have intersecting merges 9217251881Speter because they meet one or more of the criteria described in 9218251881Speter get_mergeinfo_paths(). Here the paths are arranged in a depth 9219251881Speter first order. */ 9220251881Speter SVN_ERR(get_mergeinfo_paths(children_with_mergeinfo, 9221251881Speter merge_b->target, depth, 9222251881Speter merge_b->dry_run, merge_b->same_repos, 9223251881Speter merge_b->ctx, scratch_pool, scratch_pool)); 9224251881Speter 9225251881Speter /* The first item from the NOTIFY_B->CHILDREN_WITH_MERGEINFO is always 9226251881Speter the target thanks to depth-first ordering. */ 9227251881Speter target_merge_path = APR_ARRAY_IDX(children_with_mergeinfo, 0, 9228251881Speter svn_client__merge_path_t *); 9229251881Speter 9230251881Speter /* If we are honoring mergeinfo, then for each item in 9231251881Speter NOTIFY_B->CHILDREN_WITH_MERGEINFO, we need to calculate what needs to be 9232251881Speter merged, and then merge it. Otherwise, we just merge what we were asked 9233251881Speter to merge across the whole tree. */ 9234251881Speter SVN_ERR(populate_remaining_ranges(children_with_mergeinfo, 9235251881Speter source, ra_session, 9236251881Speter merge_b, scratch_pool, scratch_pool)); 9237251881Speter 9238251881Speter /* Always start with a range which describes the most inclusive merge 9239251881Speter possible, i.e. SOURCE->rev1:rev2. */ 9240251881Speter range.start = source->loc1->rev; 9241251881Speter range.end = source->loc2->rev; 9242251881Speter range.inheritable = TRUE; 9243251881Speter 9244251881Speter if (!merge_b->reintegrate_merge) 9245251881Speter { 9246251881Speter svn_revnum_t new_range_start, start_rev; 9247251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 9248251881Speter 9249251881Speter /* The merge target TARGET_ABSPATH and/or its subtrees may not need all 9250251881Speter of SOURCE->rev1:rev2 applied. So examine 9251251881Speter NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest starting 9252251881Speter revision that actually needs to be merged (for reverse merges this is 9253251881Speter the youngest starting revision). 9254251881Speter 9255251881Speter We'll do this twice, right now for the start of the mergeinfo we will 9256251881Speter ultimately record to describe this merge and then later for the 9257251881Speter start of the actual editor drive. */ 9258251881Speter new_range_start = get_most_inclusive_rev( 9259251881Speter children_with_mergeinfo, is_rollback, TRUE); 9260251881Speter if (SVN_IS_VALID_REVNUM(new_range_start)) 9261251881Speter range.start = new_range_start; 9262251881Speter 9263251881Speter /* Remove inoperative ranges from any subtrees' remaining_ranges 9264251881Speter to spare the expense of noop editor drives. */ 9265251881Speter if (!is_rollback) 9266251881Speter SVN_ERR(remove_noop_subtree_ranges(source, merge_b->target, 9267251881Speter ra_session, 9268251881Speter children_with_mergeinfo, 9269251881Speter scratch_pool, iterpool)); 9270251881Speter 9271251881Speter /* Adjust subtrees' remaining_ranges to deal with issue #3067: 9272251881Speter * "subtrees that don't exist at the start or end of a merge range 9273251881Speter * shouldn't break the merge". */ 9274251881Speter SVN_ERR(fix_deleted_subtree_ranges(source, merge_b->target, 9275251881Speter ra_session, 9276251881Speter children_with_mergeinfo, 9277251881Speter merge_b->ctx, scratch_pool, iterpool)); 9278251881Speter 9279251881Speter /* remove_noop_subtree_ranges() and/or fix_deleted_subtree_range() 9280251881Speter may have further refined the starting revision for our editor 9281251881Speter drive. */ 9282251881Speter start_rev = 9283251881Speter get_most_inclusive_rev(children_with_mergeinfo, 9284251881Speter is_rollback, TRUE); 9285251881Speter 9286251881Speter /* Is there anything to merge? */ 9287251881Speter if (SVN_IS_VALID_REVNUM(start_rev)) 9288251881Speter { 9289251881Speter /* Now examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest 9290251881Speter ending revision that actually needs to be merged (for reverse 9291251881Speter merges this is the youngest ending revision). */ 9292251881Speter svn_revnum_t end_rev = 9293251881Speter get_most_inclusive_rev(children_with_mergeinfo, 9294251881Speter is_rollback, FALSE); 9295251881Speter 9296251881Speter /* While END_REV is valid, do the following: 9297251881Speter 9298251881Speter 1. Tweak each NOTIFY_B->CHILDREN_WITH_MERGEINFO element so that 9299251881Speter the element's remaining_ranges member has as its first element 9300251881Speter a range that ends with end_rev. 9301251881Speter 9302251881Speter 2. Starting with start_rev, call drive_merge_report_editor() 9303251881Speter on MERGE_B->target->abspath for start_rev:end_rev. 9304251881Speter 9305251881Speter 3. Remove the first element from each 9306251881Speter NOTIFY_B->CHILDREN_WITH_MERGEINFO element's remaining_ranges 9307251881Speter member. 9308251881Speter 9309251881Speter 4. Again examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the most 9310251881Speter inclusive starting revision that actually needs to be merged and 9311251881Speter update start_rev. This prevents us from needlessly contacting the 9312251881Speter repository and doing a diff where we describe the entire target 9313251881Speter tree as *not* needing any of the requested range. This can happen 9314251881Speter whenever we have mergeinfo with gaps in it for the merge source. 9315251881Speter 9316251881Speter 5. Again examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the most 9317251881Speter inclusive ending revision that actually needs to be merged and 9318251881Speter update end_rev. 9319251881Speter 9320251881Speter 6. Lather, rinse, repeat. 9321251881Speter */ 9322251881Speter 9323251881Speter while (end_rev != SVN_INVALID_REVNUM) 9324251881Speter { 9325251881Speter merge_source_t *real_source; 9326251881Speter svn_merge_range_t *first_target_range 9327251881Speter = (target_merge_path->remaining_ranges->nelts == 0 ? NULL 9328251881Speter : APR_ARRAY_IDX(target_merge_path->remaining_ranges, 0, 9329251881Speter svn_merge_range_t *)); 9330251881Speter 9331251881Speter /* Issue #3324: Stop editor abuse! Don't call 9332251881Speter drive_merge_report_editor() in such a way that we request an 9333251881Speter editor with svn_client__get_diff_editor() for some rev X, 9334251881Speter then call svn_ra_do_diff3() for some revision Y, and then 9335251881Speter call reporter->set_path(PATH=="") to set the root revision 9336251881Speter for the editor drive to revision Z where 9337251881Speter (X != Z && X < Z < Y). This is bogus because the server will 9338251881Speter send us the diff between X:Y but the client is expecting the 9339251881Speter diff between Y:Z. See issue #3324 for full details on the 9340251881Speter problems this can cause. */ 9341251881Speter if (first_target_range 9342251881Speter && start_rev != first_target_range->start) 9343251881Speter { 9344251881Speter if (is_rollback) 9345251881Speter { 9346251881Speter if (end_rev < first_target_range->start) 9347251881Speter end_rev = first_target_range->start; 9348251881Speter } 9349251881Speter else 9350251881Speter { 9351251881Speter if (end_rev > first_target_range->start) 9352251881Speter end_rev = first_target_range->start; 9353251881Speter } 9354251881Speter } 9355251881Speter 9356251881Speter svn_pool_clear(iterpool); 9357251881Speter 9358251881Speter slice_remaining_ranges(children_with_mergeinfo, 9359251881Speter is_rollback, end_rev, scratch_pool); 9360251881Speter 9361251881Speter /* Reset variables that must be reset for every drive */ 9362251881Speter merge_b->notify_begin.last_abspath = NULL; 9363251881Speter 9364251881Speter real_source = subrange_source(source, start_rev, end_rev, iterpool); 9365251881Speter SVN_ERR(drive_merge_report_editor( 9366251881Speter merge_b->target->abspath, 9367251881Speter real_source, 9368251881Speter children_with_mergeinfo, 9369251881Speter processor, 9370251881Speter depth, 9371251881Speter merge_b, 9372251881Speter iterpool)); 9373251881Speter 9374251881Speter /* If any paths picked up explicit mergeinfo as a result of 9375251881Speter the merge we need to make sure any mergeinfo those paths 9376251881Speter inherited is recorded and then add these paths to 9377251881Speter NOTIFY_B->CHILDREN_WITH_MERGEINFO.*/ 9378251881Speter SVN_ERR(process_children_with_new_mergeinfo( 9379251881Speter merge_b, children_with_mergeinfo, 9380251881Speter scratch_pool)); 9381251881Speter 9382251881Speter /* If any subtrees had their explicit mergeinfo deleted as a 9383251881Speter result of the merge then remove these paths from 9384251881Speter NOTIFY_B->CHILDREN_WITH_MERGEINFO since there is no need 9385251881Speter to consider these subtrees for subsequent editor drives 9386251881Speter nor do we want to record mergeinfo on them describing 9387251881Speter the merge itself. */ 9388251881Speter remove_children_with_deleted_mergeinfo( 9389251881Speter merge_b, children_with_mergeinfo); 9390251881Speter 9391251881Speter /* Prepare for the next iteration (if any). */ 9392251881Speter remove_first_range_from_remaining_ranges( 9393251881Speter end_rev, children_with_mergeinfo, scratch_pool); 9394251881Speter 9395251881Speter /* If we raised any conflicts, break out and report how much 9396251881Speter we have merged. */ 9397251881Speter if (is_path_conflicted_by_merge(merge_b)) 9398251881Speter { 9399251881Speter merge_source_t *remaining_range = NULL; 9400251881Speter 9401251881Speter if (real_source->loc2->rev != source->loc2->rev) 9402251881Speter remaining_range = subrange_source(source, 9403251881Speter real_source->loc2->rev, 9404251881Speter source->loc2->rev, 9405251881Speter scratch_pool); 9406251881Speter *conflict_report = single_range_conflict_report_create( 9407251881Speter real_source, remaining_range, 9408251881Speter result_pool); 9409251881Speter 9410251881Speter range.end = end_rev; 9411251881Speter break; 9412251881Speter } 9413251881Speter 9414251881Speter start_rev = 9415251881Speter get_most_inclusive_rev(children_with_mergeinfo, 9416251881Speter is_rollback, TRUE); 9417251881Speter end_rev = 9418251881Speter get_most_inclusive_rev(children_with_mergeinfo, 9419251881Speter is_rollback, FALSE); 9420251881Speter } 9421251881Speter } 9422251881Speter svn_pool_destroy(iterpool); 9423251881Speter } 9424251881Speter else 9425251881Speter { 9426251881Speter if (!merge_b->record_only) 9427251881Speter { 9428251881Speter /* Reset cur_ancestor_abspath to null so that subsequent cherry 9429251881Speter picked revision ranges will be notified upon subsequent 9430251881Speter operative merge. */ 9431251881Speter merge_b->notify_begin.last_abspath = NULL; 9432251881Speter 9433251881Speter SVN_ERR(drive_merge_report_editor(merge_b->target->abspath, 9434251881Speter source, 9435251881Speter NULL, 9436251881Speter processor, 9437251881Speter depth, 9438251881Speter merge_b, 9439251881Speter scratch_pool)); 9440251881Speter } 9441251881Speter } 9442251881Speter 9443251881Speter /* Record mergeinfo where appropriate.*/ 9444251881Speter if (RECORD_MERGEINFO(merge_b)) 9445251881Speter { 9446251881Speter const svn_client__pathrev_t *primary_src 9447251881Speter = is_rollback ? source->loc1 : source->loc2; 9448251881Speter const char *mergeinfo_path 9449251881Speter = svn_client__pathrev_fspath(primary_src, scratch_pool); 9450251881Speter 9451251881Speter SVN_ERR(record_mergeinfo_for_dir_merge(result_catalog, 9452251881Speter &range, 9453251881Speter mergeinfo_path, 9454251881Speter children_with_mergeinfo, 9455251881Speter depth, 9456251881Speter squelch_mergeinfo_notifications, 9457251881Speter merge_b, 9458251881Speter scratch_pool)); 9459251881Speter 9460251881Speter /* If a path has an immediate parent with non-inheritable mergeinfo at 9461251881Speter this point, then it meets criteria 3 or 5 described in 9462251881Speter get_mergeinfo_paths' doc string. For paths which exist prior to a 9463251881Speter merge explicit mergeinfo has already been set. But for paths added 9464251881Speter during the merge this is not the case. The path might have explicit 9465251881Speter mergeinfo from the merge source, but no mergeinfo yet exists 9466251881Speter describing *this* merge. So the added path has either incomplete 9467251881Speter explicit mergeinfo or inherits incomplete mergeinfo from its 9468251881Speter immediate parent (if any, the parent might have only non-inheritable 9469251881Speter ranges in which case the path simply inherits empty mergeinfo). 9470251881Speter 9471251881Speter So here we look at the root path of each subtree added during the 9472251881Speter merge and set explicit mergeinfo on it if it meets the aforementioned 9473251881Speter conditions. */ 9474251881Speter if (range.start < range.end) /* Nothing to record on added subtrees 9475251881Speter resulting from reverse merges. */ 9476251881Speter { 9477251881Speter SVN_ERR(record_mergeinfo_for_added_subtrees( 9478251881Speter &range, mergeinfo_path, depth, 9479251881Speter squelch_mergeinfo_notifications, 9480251881Speter merge_b->added_abspaths, merge_b, scratch_pool)); 9481251881Speter } 9482251881Speter } 9483251881Speter 9484251881Speter return SVN_NO_ERROR; 9485251881Speter} 9486251881Speter 9487251881Speter/* Helper for do_merge() when the merge target is a directory. 9488251881Speter * 9489251881Speter * If any conflict is raised during the merge, set *CONFLICTED_RANGE to 9490251881Speter * the revision sub-range that raised the conflict. In this case, the 9491251881Speter * merge will have ended at revision CONFLICTED_RANGE and mergeinfo will 9492251881Speter * have been recorded for all revision sub-ranges up to and including 9493251881Speter * CONFLICTED_RANGE. Otherwise, set *CONFLICTED_RANGE to NULL. 9494251881Speter */ 9495251881Speterstatic svn_error_t * 9496251881Speterdo_directory_merge(svn_mergeinfo_catalog_t result_catalog, 9497251881Speter single_range_conflict_report_t **conflict_report, 9498251881Speter const merge_source_t *source, 9499251881Speter const char *target_abspath, 9500251881Speter const svn_diff_tree_processor_t *processor, 9501251881Speter svn_depth_t depth, 9502251881Speter svn_boolean_t squelch_mergeinfo_notifications, 9503251881Speter merge_cmd_baton_t *merge_b, 9504251881Speter apr_pool_t *result_pool, 9505251881Speter apr_pool_t *scratch_pool) 9506251881Speter{ 9507251881Speter apr_array_header_t *children_with_mergeinfo; 9508251881Speter 9509251881Speter /* Initialize CHILDREN_WITH_MERGEINFO. See the comment 9510251881Speter 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */ 9511251881Speter children_with_mergeinfo = 9512251881Speter apr_array_make(scratch_pool, 16, sizeof(svn_client__merge_path_t *)); 9513251881Speter 9514251881Speter /* And make it read-only accessible from the baton */ 9515251881Speter merge_b->notify_begin.nodes_with_mergeinfo = children_with_mergeinfo; 9516251881Speter 9517251881Speter /* If we are not honoring mergeinfo we can skip right to the 9518251881Speter business of merging changes! */ 9519251881Speter if (HONOR_MERGEINFO(merge_b)) 9520251881Speter SVN_ERR(do_mergeinfo_aware_dir_merge(result_catalog, conflict_report, 9521251881Speter source, target_abspath, 9522251881Speter children_with_mergeinfo, 9523251881Speter processor, depth, 9524251881Speter squelch_mergeinfo_notifications, 9525251881Speter merge_b, result_pool, scratch_pool)); 9526251881Speter else 9527251881Speter SVN_ERR(do_mergeinfo_unaware_dir_merge(conflict_report, 9528251881Speter source, target_abspath, 9529251881Speter children_with_mergeinfo, 9530251881Speter processor, depth, 9531251881Speter merge_b, result_pool, scratch_pool)); 9532251881Speter 9533251881Speter merge_b->notify_begin.nodes_with_mergeinfo = NULL; 9534251881Speter 9535251881Speter return SVN_NO_ERROR; 9536251881Speter} 9537251881Speter 9538251881Speter/** Ensure that *RA_SESSION is opened to URL, either by reusing 9539251881Speter * *RA_SESSION if it is non-null and already opened to URL's 9540251881Speter * repository, or by allocating a new *RA_SESSION in POOL. 9541251881Speter * (RA_SESSION itself cannot be null, of course.) 9542251881Speter * 9543251881Speter * CTX is used as for svn_client_open_ra_session(). 9544251881Speter */ 9545251881Speterstatic svn_error_t * 9546251881Speterensure_ra_session_url(svn_ra_session_t **ra_session, 9547251881Speter const char *url, 9548251881Speter const char *wri_abspath, 9549251881Speter svn_client_ctx_t *ctx, 9550251881Speter apr_pool_t *pool) 9551251881Speter{ 9552251881Speter svn_error_t *err = SVN_NO_ERROR; 9553251881Speter 9554251881Speter if (*ra_session) 9555251881Speter { 9556251881Speter err = svn_ra_reparent(*ra_session, url, pool); 9557251881Speter } 9558251881Speter 9559251881Speter /* SVN_ERR_RA_ILLEGAL_URL is raised when url doesn't point to the same 9560251881Speter repository as ra_session. */ 9561251881Speter if (! *ra_session || (err && err->apr_err == SVN_ERR_RA_ILLEGAL_URL)) 9562251881Speter { 9563251881Speter svn_error_clear(err); 9564251881Speter err = svn_client_open_ra_session2(ra_session, url, wri_abspath, 9565251881Speter ctx, pool, pool); 9566251881Speter } 9567251881Speter SVN_ERR(err); 9568251881Speter 9569251881Speter return SVN_NO_ERROR; 9570251881Speter} 9571251881Speter 9572251881Speter/* Drive a merge of MERGE_SOURCES into working copy node TARGET 9573251881Speter and possibly record mergeinfo describing the merge -- see 9574251881Speter RECORD_MERGEINFO(). 9575251881Speter 9576251881Speter If MODIFIED_SUBTREES is not NULL and all the MERGE_SOURCES are 'ancestral' 9577251881Speter or REINTEGRATE_MERGE is true, then replace *MODIFIED_SUBTREES with a new 9578251881Speter hash containing all the paths that *MODIFIED_SUBTREES contained before, 9579251881Speter and also every path modified, skipped, added, or tree-conflicted 9580251881Speter by the merge. Keys and values of the hash are both (const char *) 9581251881Speter absolute paths. The contents of the hash are allocated in RESULT_POOL. 9582251881Speter 9583251881Speter If the merge raises any conflicts while merging a revision range, return 9584251881Speter early and set *CONFLICT_REPORT to describe the details. (In this case, 9585251881Speter notify that the merge is complete if and only if this was the last 9586251881Speter revision range of the merge.) If there are no conflicts, set 9587251881Speter *CONFLICT_REPORT to NULL. A revision range here can be one specified 9588251881Speter in MERGE_SOURCES or an internally generated sub-range of one of those 9589251881Speter when merge tracking is in use. 9590251881Speter 9591251881Speter For every (const merge_source_t *) merge source in MERGE_SOURCES, if 9592251881Speter SOURCE->ANCESTRAL is set, then the "left" and "right" side are 9593251881Speter ancestrally related. (See 'MERGEINFO MERGE SOURCE NORMALIZATION' 9594251881Speter for more on what that means and how it matters.) 9595251881Speter 9596251881Speter If SOURCES_RELATED is set, the "left" and "right" sides of the 9597251881Speter merge source are historically related (ancestors, uncles, second 9598251881Speter cousins thrice removed, etc...). (This is passed through to 9599251881Speter do_file_merge() to simulate the history checks that the repository 9600251881Speter logic does in the directory case.) 9601251881Speter 9602251881Speter SAME_REPOS is TRUE iff the merge sources live in the same 9603251881Speter repository as the one from which the target working copy has been 9604251881Speter checked out. 9605251881Speter 9606251881Speter If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE, 9607251881Speter and CTX->NOTIFY_FUNC2 is not NULL, then call CTX->NOTIFY_FUNC2 with 9608251881Speter CTX->NOTIFY_BATON2 and a svn_wc_notify_merge_record_info_begin 9609251881Speter notification before any mergeinfo changes are made to describe the merge 9610251881Speter performed. 9611251881Speter 9612251881Speter If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG 9613251881Speter is not NULL, then don't record the new mergeinfo on the WC, but instead 9614251881Speter record it in RESULT_CATALOG, where the keys are absolute working copy 9615251881Speter paths and the values are the new mergeinfos for each. Allocate additions 9616251881Speter to RESULT_CATALOG in pool which RESULT_CATALOG was created in. 9617251881Speter 9618251881Speter FORCE_DELETE, DRY_RUN, RECORD_ONLY, DEPTH, MERGE_OPTIONS, 9619251881Speter and CTX are as described in the docstring for svn_client_merge_peg3(). 9620251881Speter 9621251881Speter If IGNORE_MERGEINFO is true, disable merge tracking, by treating the two 9622251881Speter sources as unrelated even if they actually have a common ancestor. See 9623251881Speter the macro HONOR_MERGEINFO(). 9624251881Speter 9625251881Speter If DIFF_IGNORE_ANCESTRY is true, diff the 'left' and 'right' versions 9626251881Speter of a node (if they are the same kind) as if they were related, even if 9627251881Speter they are not related. Otherwise, diff unrelated items as a deletion 9628251881Speter of one thing and the addition of another. 9629251881Speter 9630251881Speter If not NULL, RECORD_ONLY_PATHS is a hash of (const char *) paths mapped 9631251881Speter to the same. If RECORD_ONLY is true and RECORD_ONLY_PATHS is not NULL, 9632251881Speter then record mergeinfo describing the merge only on subtrees which contain 9633251881Speter items from RECORD_ONLY_PATHS. If RECORD_ONLY is true and RECORD_ONLY_PATHS 9634251881Speter is NULL, then record mergeinfo on every subtree with mergeinfo in 9635251881Speter TARGET. 9636251881Speter 9637251881Speter REINTEGRATE_MERGE is TRUE if this is a reintegrate merge. 9638251881Speter 9639251881Speter *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp 9640251881Speter integrity, *USE_SLEEP will be unchanged if no sleep is required. 9641251881Speter 9642251881Speter SCRATCH_POOL is used for all temporary allocations. 9643251881Speter*/ 9644251881Speterstatic svn_error_t * 9645251881Speterdo_merge(apr_hash_t **modified_subtrees, 9646251881Speter svn_mergeinfo_catalog_t result_catalog, 9647251881Speter conflict_report_t **conflict_report, 9648251881Speter svn_boolean_t *use_sleep, 9649251881Speter const apr_array_header_t *merge_sources, 9650251881Speter const merge_target_t *target, 9651251881Speter svn_ra_session_t *src_session, 9652251881Speter svn_boolean_t sources_related, 9653251881Speter svn_boolean_t same_repos, 9654251881Speter svn_boolean_t ignore_mergeinfo, 9655251881Speter svn_boolean_t diff_ignore_ancestry, 9656251881Speter svn_boolean_t force_delete, 9657251881Speter svn_boolean_t dry_run, 9658251881Speter svn_boolean_t record_only, 9659251881Speter apr_hash_t *record_only_paths, 9660251881Speter svn_boolean_t reintegrate_merge, 9661251881Speter svn_boolean_t squelch_mergeinfo_notifications, 9662251881Speter svn_depth_t depth, 9663251881Speter const apr_array_header_t *merge_options, 9664251881Speter svn_client_ctx_t *ctx, 9665251881Speter apr_pool_t *result_pool, 9666251881Speter apr_pool_t *scratch_pool) 9667251881Speter{ 9668251881Speter merge_cmd_baton_t merge_cmd_baton = { 0 }; 9669251881Speter svn_config_t *cfg; 9670251881Speter const char *diff3_cmd; 9671251881Speter int i; 9672251881Speter svn_boolean_t checked_mergeinfo_capability = FALSE; 9673251881Speter svn_ra_session_t *ra_session1 = NULL, *ra_session2 = NULL; 9674251881Speter const char *old_src_session_url = NULL; 9675251881Speter apr_pool_t *iterpool; 9676251881Speter const svn_diff_tree_processor_t *processor; 9677251881Speter 9678251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath)); 9679251881Speter 9680251881Speter *conflict_report = NULL; 9681251881Speter 9682251881Speter /* Check from some special conditions when in record-only mode 9683251881Speter (which is a merge-tracking thing). */ 9684251881Speter if (record_only) 9685251881Speter { 9686251881Speter svn_boolean_t sources_ancestral = TRUE; 9687251881Speter int j; 9688251881Speter 9689251881Speter /* Find out whether all of the sources are 'ancestral'. */ 9690251881Speter for (j = 0; j < merge_sources->nelts; j++) 9691251881Speter if (! APR_ARRAY_IDX(merge_sources, j, merge_source_t *)->ancestral) 9692251881Speter { 9693251881Speter sources_ancestral = FALSE; 9694251881Speter break; 9695251881Speter } 9696251881Speter 9697251881Speter /* We can't do a record-only merge if the sources aren't related. */ 9698251881Speter if (! sources_ancestral) 9699251881Speter return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 9700251881Speter _("Use of two URLs is not compatible with " 9701251881Speter "mergeinfo modification")); 9702251881Speter 9703251881Speter /* We can't do a record-only merge if the sources aren't from 9704251881Speter the same repository as the target. */ 9705251881Speter if (! same_repos) 9706251881Speter return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 9707251881Speter _("Merge from foreign repository is not " 9708251881Speter "compatible with mergeinfo modification")); 9709251881Speter 9710251881Speter /* If this is a dry-run record-only merge, there's nothing to do. */ 9711251881Speter if (dry_run) 9712251881Speter return SVN_NO_ERROR; 9713251881Speter } 9714251881Speter 9715251881Speter iterpool = svn_pool_create(scratch_pool); 9716251881Speter 9717251881Speter /* Ensure a known depth. */ 9718251881Speter if (depth == svn_depth_unknown) 9719251881Speter depth = svn_depth_infinity; 9720251881Speter 9721251881Speter /* Set up the diff3 command, so various callers don't have to. */ 9722251881Speter cfg = ctx->config 9723251881Speter ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) 9724251881Speter : NULL; 9725251881Speter svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS, 9726251881Speter SVN_CONFIG_OPTION_DIFF3_CMD, NULL); 9727251881Speter 9728251881Speter if (diff3_cmd != NULL) 9729251881Speter SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool)); 9730251881Speter 9731251881Speter /* Build the merge context baton (or at least the parts of it that 9732251881Speter don't need to be reset for each merge source). */ 9733251881Speter merge_cmd_baton.force_delete = force_delete; 9734251881Speter merge_cmd_baton.dry_run = dry_run; 9735251881Speter merge_cmd_baton.record_only = record_only; 9736251881Speter merge_cmd_baton.ignore_mergeinfo = ignore_mergeinfo; 9737251881Speter merge_cmd_baton.diff_ignore_ancestry = diff_ignore_ancestry; 9738251881Speter merge_cmd_baton.same_repos = same_repos; 9739251881Speter merge_cmd_baton.mergeinfo_capable = FALSE; 9740251881Speter merge_cmd_baton.ctx = ctx; 9741251881Speter merge_cmd_baton.reintegrate_merge = reintegrate_merge; 9742251881Speter merge_cmd_baton.target = target; 9743251881Speter merge_cmd_baton.pool = iterpool; 9744251881Speter merge_cmd_baton.merge_options = merge_options; 9745251881Speter merge_cmd_baton.diff3_cmd = diff3_cmd; 9746251881Speter merge_cmd_baton.use_sleep = use_sleep; 9747251881Speter 9748251881Speter /* Do we already know the specific subtrees with mergeinfo we want 9749251881Speter to record-only mergeinfo on? */ 9750251881Speter if (record_only && record_only_paths) 9751251881Speter merge_cmd_baton.merged_abspaths = record_only_paths; 9752251881Speter else 9753251881Speter merge_cmd_baton.merged_abspaths = apr_hash_make(result_pool); 9754251881Speter 9755251881Speter merge_cmd_baton.skipped_abspaths = apr_hash_make(result_pool); 9756251881Speter merge_cmd_baton.added_abspaths = apr_hash_make(result_pool); 9757251881Speter merge_cmd_baton.tree_conflicted_abspaths = apr_hash_make(result_pool); 9758251881Speter 9759251881Speter { 9760251881Speter svn_diff_tree_processor_t *merge_processor; 9761251881Speter 9762251881Speter merge_processor = svn_diff__tree_processor_create(&merge_cmd_baton, 9763251881Speter scratch_pool); 9764251881Speter 9765251881Speter merge_processor->dir_opened = merge_dir_opened; 9766251881Speter merge_processor->dir_changed = merge_dir_changed; 9767251881Speter merge_processor->dir_added = merge_dir_added; 9768251881Speter merge_processor->dir_deleted = merge_dir_deleted; 9769251881Speter merge_processor->dir_closed = merge_dir_closed; 9770251881Speter 9771251881Speter merge_processor->file_opened = merge_file_opened; 9772251881Speter merge_processor->file_changed = merge_file_changed; 9773251881Speter merge_processor->file_added = merge_file_added; 9774251881Speter merge_processor->file_deleted = merge_file_deleted; 9775251881Speter /* Not interested in file_closed() */ 9776251881Speter 9777251881Speter merge_processor->node_absent = merge_node_absent; 9778251881Speter 9779251881Speter processor = merge_processor; 9780251881Speter } 9781251881Speter 9782251881Speter if (src_session) 9783251881Speter { 9784251881Speter SVN_ERR(svn_ra_get_session_url(src_session, &old_src_session_url, 9785251881Speter scratch_pool)); 9786251881Speter ra_session1 = src_session; 9787251881Speter } 9788251881Speter 9789251881Speter for (i = 0; i < merge_sources->nelts; i++) 9790251881Speter { 9791251881Speter svn_node_kind_t src1_kind; 9792251881Speter merge_source_t *source = 9793251881Speter APR_ARRAY_IDX(merge_sources, i, merge_source_t *); 9794251881Speter single_range_conflict_report_t *conflicted_range_report; 9795251881Speter 9796251881Speter svn_pool_clear(iterpool); 9797251881Speter 9798251881Speter /* Sanity check: if our left- and right-side merge sources are 9799251881Speter the same, there's nothing to here. */ 9800251881Speter if ((strcmp(source->loc1->url, source->loc2->url) == 0) 9801251881Speter && (source->loc1->rev == source->loc2->rev)) 9802251881Speter continue; 9803251881Speter 9804251881Speter /* Establish RA sessions to our URLs, reuse where possible. */ 9805251881Speter SVN_ERR(ensure_ra_session_url(&ra_session1, source->loc1->url, 9806251881Speter target->abspath, ctx, scratch_pool)); 9807251881Speter SVN_ERR(ensure_ra_session_url(&ra_session2, source->loc2->url, 9808251881Speter target->abspath, ctx, scratch_pool)); 9809251881Speter 9810251881Speter /* Populate the portions of the merge context baton that need to 9811251881Speter be reset for each merge source iteration. */ 9812251881Speter merge_cmd_baton.merge_source = *source; 9813251881Speter merge_cmd_baton.implicit_src_gap = NULL; 9814251881Speter merge_cmd_baton.conflicted_paths = NULL; 9815251881Speter merge_cmd_baton.paths_with_new_mergeinfo = NULL; 9816251881Speter merge_cmd_baton.paths_with_deleted_mergeinfo = NULL; 9817251881Speter merge_cmd_baton.ra_session1 = ra_session1; 9818251881Speter merge_cmd_baton.ra_session2 = ra_session2; 9819251881Speter 9820251881Speter merge_cmd_baton.notify_begin.last_abspath = NULL; 9821251881Speter 9822251881Speter /* Populate the portions of the merge context baton that require 9823251881Speter an RA session to set, but shouldn't be reset for each iteration. */ 9824251881Speter if (! checked_mergeinfo_capability) 9825251881Speter { 9826251881Speter SVN_ERR(svn_ra_has_capability(ra_session1, 9827251881Speter &merge_cmd_baton.mergeinfo_capable, 9828251881Speter SVN_RA_CAPABILITY_MERGEINFO, 9829251881Speter iterpool)); 9830251881Speter checked_mergeinfo_capability = TRUE; 9831251881Speter } 9832251881Speter 9833251881Speter SVN_ERR(svn_ra_check_path(ra_session1, "", source->loc1->rev, 9834251881Speter &src1_kind, iterpool)); 9835251881Speter 9836251881Speter /* Run the merge; if there are conflicts, allow the callback to 9837251881Speter * resolve them, and if it resolves all of them, then run the 9838251881Speter * merge again with the remaining revision range, until it is all 9839251881Speter * done. */ 9840251881Speter do 9841251881Speter { 9842251881Speter /* Merge as far as possible without resolving any conflicts */ 9843251881Speter if (src1_kind != svn_node_dir) 9844251881Speter { 9845251881Speter SVN_ERR(do_file_merge(result_catalog, &conflicted_range_report, 9846251881Speter source, target->abspath, 9847251881Speter processor, 9848251881Speter sources_related, 9849251881Speter squelch_mergeinfo_notifications, 9850251881Speter &merge_cmd_baton, iterpool, iterpool)); 9851251881Speter } 9852251881Speter else /* Directory */ 9853251881Speter { 9854251881Speter SVN_ERR(do_directory_merge(result_catalog, &conflicted_range_report, 9855251881Speter source, target->abspath, 9856251881Speter processor, 9857251881Speter depth, squelch_mergeinfo_notifications, 9858251881Speter &merge_cmd_baton, iterpool, iterpool)); 9859251881Speter } 9860251881Speter 9861251881Speter /* Give the conflict resolver callback the opportunity to 9862251881Speter * resolve any conflicts that were raised. If it resolves all 9863251881Speter * of them, go around again to merge the next sub-range (if any). */ 9864251881Speter if (conflicted_range_report && ctx->conflict_func2 && ! dry_run) 9865251881Speter { 9866251881Speter svn_boolean_t conflicts_remain; 9867251881Speter 9868251881Speter SVN_ERR(svn_client__resolve_conflicts( 9869251881Speter &conflicts_remain, merge_cmd_baton.conflicted_paths, 9870251881Speter ctx, iterpool)); 9871251881Speter if (conflicts_remain) 9872251881Speter break; 9873251881Speter 9874251881Speter merge_cmd_baton.conflicted_paths = NULL; 9875251881Speter /* Caution: this source is in iterpool */ 9876251881Speter source = conflicted_range_report->remaining_source; 9877251881Speter conflicted_range_report = NULL; 9878251881Speter } 9879251881Speter else 9880251881Speter break; 9881251881Speter } 9882251881Speter while (source); 9883251881Speter 9884251881Speter /* The final mergeinfo on TARGET_WCPATH may itself elide. */ 9885251881Speter if (! dry_run) 9886251881Speter SVN_ERR(svn_client__elide_mergeinfo(target->abspath, NULL, 9887251881Speter ctx, iterpool)); 9888251881Speter 9889251881Speter /* If conflicts occurred while merging any but the very last 9890251881Speter * range of a multi-pass merge, we raise an error that aborts 9891251881Speter * the merge. The user will be asked to resolve conflicts 9892251881Speter * before merging subsequent revision ranges. */ 9893251881Speter if (conflicted_range_report) 9894251881Speter { 9895251881Speter *conflict_report = conflict_report_create( 9896251881Speter target->abspath, conflicted_range_report->conflicted_range, 9897251881Speter (i == merge_sources->nelts - 1 9898251881Speter && ! conflicted_range_report->remaining_source), 9899251881Speter result_pool); 9900251881Speter break; 9901251881Speter } 9902251881Speter } 9903251881Speter 9904251881Speter if (! *conflict_report || (*conflict_report)->was_last_range) 9905251881Speter { 9906251881Speter /* Let everyone know we're finished here. */ 9907251881Speter notify_merge_completed(target->abspath, ctx, iterpool); 9908251881Speter } 9909251881Speter 9910251881Speter /* Does the caller want to know what the merge has done? */ 9911251881Speter if (modified_subtrees) 9912251881Speter { 9913251881Speter *modified_subtrees = 9914251881Speter apr_hash_overlay(result_pool, *modified_subtrees, 9915251881Speter merge_cmd_baton.merged_abspaths); 9916251881Speter *modified_subtrees = 9917251881Speter apr_hash_overlay(result_pool, *modified_subtrees, 9918251881Speter merge_cmd_baton.added_abspaths); 9919251881Speter *modified_subtrees = 9920251881Speter apr_hash_overlay(result_pool, *modified_subtrees, 9921251881Speter merge_cmd_baton.skipped_abspaths); 9922251881Speter *modified_subtrees = 9923251881Speter apr_hash_overlay(result_pool, *modified_subtrees, 9924251881Speter merge_cmd_baton.tree_conflicted_abspaths); 9925251881Speter } 9926251881Speter 9927251881Speter if (src_session) 9928251881Speter SVN_ERR(svn_ra_reparent(src_session, old_src_session_url, iterpool)); 9929251881Speter 9930251881Speter svn_pool_destroy(iterpool); 9931251881Speter return SVN_NO_ERROR; 9932251881Speter} 9933251881Speter 9934251881Speter/* Perform a two-URL merge between URLs which are related, but neither 9935251881Speter is a direct ancestor of the other. This first does a real two-URL 9936251881Speter merge (unless this is record-only), followed by record-only merges 9937251881Speter to represent the changed mergeinfo. 9938251881Speter 9939251881Speter Set *CONFLICT_REPORT to indicate if there were any conflicts, as in 9940251881Speter do_merge(). 9941251881Speter 9942251881Speter The diff to be merged is between SOURCE->loc1 (in URL1_RA_SESSION1) 9943251881Speter and SOURCE->loc2 (in URL2_RA_SESSION2); YCA is their youngest 9944251881Speter common ancestor. 9945251881Speter 9946251881Speter SAME_REPOS must be true if and only if the source URLs are in the same 9947251881Speter repository as the target working copy. 9948251881Speter 9949251881Speter DIFF_IGNORE_ANCESTRY is as in do_merge(). 9950251881Speter 9951251881Speter Other arguments are as in all of the public merge APIs. 9952251881Speter 9953251881Speter *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp 9954251881Speter integrity, *USE_SLEEP will be unchanged if no sleep is required. 9955251881Speter 9956251881Speter SCRATCH_POOL is used for all temporary allocations. 9957251881Speter */ 9958251881Speterstatic svn_error_t * 9959251881Spetermerge_cousins_and_supplement_mergeinfo(conflict_report_t **conflict_report, 9960251881Speter svn_boolean_t *use_sleep, 9961251881Speter const merge_target_t *target, 9962251881Speter svn_ra_session_t *URL1_ra_session, 9963251881Speter svn_ra_session_t *URL2_ra_session, 9964251881Speter const merge_source_t *source, 9965251881Speter const svn_client__pathrev_t *yca, 9966251881Speter svn_boolean_t same_repos, 9967251881Speter svn_depth_t depth, 9968251881Speter svn_boolean_t diff_ignore_ancestry, 9969251881Speter svn_boolean_t force_delete, 9970251881Speter svn_boolean_t record_only, 9971251881Speter svn_boolean_t dry_run, 9972251881Speter const apr_array_header_t *merge_options, 9973251881Speter svn_client_ctx_t *ctx, 9974251881Speter apr_pool_t *result_pool, 9975251881Speter apr_pool_t *scratch_pool) 9976251881Speter{ 9977251881Speter apr_array_header_t *remove_sources, *add_sources; 9978251881Speter apr_hash_t *modified_subtrees = NULL; 9979251881Speter 9980251881Speter /* Sure we could use SCRATCH_POOL throughout this function, but since this 9981251881Speter is a wrapper around three separate merges we'll create a subpool we can 9982251881Speter clear between each of the three. If the merge target has a lot of 9983251881Speter subtree mergeinfo, then this will help keep memory use in check. */ 9984251881Speter apr_pool_t *subpool = svn_pool_create(scratch_pool); 9985251881Speter 9986251881Speter assert(session_url_is(URL1_ra_session, source->loc1->url, scratch_pool)); 9987251881Speter assert(session_url_is(URL2_ra_session, source->loc2->url, scratch_pool)); 9988251881Speter 9989251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath)); 9990251881Speter SVN_ERR_ASSERT(! source->ancestral); 9991251881Speter 9992251881Speter SVN_ERR(normalize_merge_sources_internal( 9993251881Speter &remove_sources, source->loc1, 9994251881Speter svn_rangelist__initialize(source->loc1->rev, yca->rev, TRUE, 9995251881Speter scratch_pool), 9996251881Speter URL1_ra_session, ctx, scratch_pool, subpool)); 9997251881Speter 9998251881Speter SVN_ERR(normalize_merge_sources_internal( 9999251881Speter &add_sources, source->loc2, 10000251881Speter svn_rangelist__initialize(yca->rev, source->loc2->rev, TRUE, 10001251881Speter scratch_pool), 10002251881Speter URL2_ra_session, ctx, scratch_pool, subpool)); 10003251881Speter 10004251881Speter *conflict_report = NULL; 10005251881Speter 10006251881Speter /* If this isn't a record-only merge, we'll first do a stupid 10007251881Speter point-to-point merge... */ 10008251881Speter if (! record_only) 10009251881Speter { 10010251881Speter apr_array_header_t *faux_sources = 10011251881Speter apr_array_make(scratch_pool, 1, sizeof(merge_source_t *)); 10012251881Speter 10013251881Speter modified_subtrees = apr_hash_make(scratch_pool); 10014251881Speter APR_ARRAY_PUSH(faux_sources, const merge_source_t *) = source; 10015251881Speter SVN_ERR(do_merge(&modified_subtrees, NULL, conflict_report, use_sleep, 10016251881Speter faux_sources, target, 10017251881Speter URL1_ra_session, TRUE, same_repos, 10018251881Speter FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry, 10019251881Speter force_delete, dry_run, FALSE, NULL, TRUE, 10020251881Speter FALSE, depth, merge_options, ctx, 10021251881Speter scratch_pool, subpool)); 10022251881Speter if (*conflict_report) 10023251881Speter { 10024251881Speter *conflict_report = conflict_report_dup(*conflict_report, result_pool); 10025251881Speter if (! (*conflict_report)->was_last_range) 10026251881Speter return SVN_NO_ERROR; 10027251881Speter } 10028251881Speter } 10029251881Speter else if (! same_repos) 10030251881Speter { 10031251881Speter return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 10032251881Speter _("Merge from foreign repository is not " 10033251881Speter "compatible with mergeinfo modification")); 10034251881Speter } 10035251881Speter 10036251881Speter /* ... and now, if we're doing the mergeinfo thang, we execute a 10037251881Speter pair of record-only merges using the real sources we've 10038251881Speter calculated. 10039251881Speter 10040251881Speter Issue #3648: We don't actually perform these two record-only merges 10041251881Speter on the WC at first, but rather see what each would do and store that 10042251881Speter in two mergeinfo catalogs. We then merge the catalogs together and 10043251881Speter then record the result in the WC. This prevents the second record 10044251881Speter only merge from removing legitimate mergeinfo history, from the same 10045251881Speter source, that was made in prior merges. */ 10046251881Speter if (same_repos && !dry_run) 10047251881Speter { 10048251881Speter svn_mergeinfo_catalog_t add_result_catalog = 10049251881Speter apr_hash_make(scratch_pool); 10050251881Speter svn_mergeinfo_catalog_t remove_result_catalog = 10051251881Speter apr_hash_make(scratch_pool); 10052251881Speter 10053251881Speter notify_mergeinfo_recording(target->abspath, NULL, ctx, scratch_pool); 10054251881Speter svn_pool_clear(subpool); 10055251881Speter SVN_ERR(do_merge(NULL, add_result_catalog, conflict_report, use_sleep, 10056251881Speter add_sources, target, 10057251881Speter URL1_ra_session, TRUE, same_repos, 10058251881Speter FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry, 10059251881Speter force_delete, dry_run, TRUE, 10060251881Speter modified_subtrees, TRUE, 10061251881Speter TRUE, depth, merge_options, ctx, 10062251881Speter scratch_pool, subpool)); 10063251881Speter if (*conflict_report) 10064251881Speter { 10065251881Speter *conflict_report = conflict_report_dup(*conflict_report, result_pool); 10066251881Speter if (! (*conflict_report)->was_last_range) 10067251881Speter return SVN_NO_ERROR; 10068251881Speter } 10069251881Speter svn_pool_clear(subpool); 10070251881Speter SVN_ERR(do_merge(NULL, remove_result_catalog, conflict_report, use_sleep, 10071251881Speter remove_sources, target, 10072251881Speter URL1_ra_session, TRUE, same_repos, 10073251881Speter FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry, 10074251881Speter force_delete, dry_run, TRUE, 10075251881Speter modified_subtrees, TRUE, 10076251881Speter TRUE, depth, merge_options, ctx, 10077251881Speter scratch_pool, subpool)); 10078251881Speter if (*conflict_report) 10079251881Speter { 10080251881Speter *conflict_report = conflict_report_dup(*conflict_report, result_pool); 10081251881Speter if (! (*conflict_report)->was_last_range) 10082251881Speter return SVN_NO_ERROR; 10083251881Speter } 10084251881Speter SVN_ERR(svn_mergeinfo_catalog_merge(add_result_catalog, 10085251881Speter remove_result_catalog, 10086251881Speter scratch_pool, scratch_pool)); 10087251881Speter SVN_ERR(svn_client__record_wc_mergeinfo_catalog(add_result_catalog, 10088251881Speter ctx, scratch_pool)); 10089251881Speter } 10090251881Speter 10091251881Speter svn_pool_destroy(subpool); 10092251881Speter return SVN_NO_ERROR; 10093251881Speter} 10094251881Speter 10095251881Speter/* Perform checks to determine whether the working copy at TARGET_ABSPATH 10096251881Speter * can safely be used as a merge target. Checks are performed according to 10097251881Speter * the ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, and ALLOW_SWITCHED_SUBTREES 10098251881Speter * parameters. If any checks fail, raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE. 10099251881Speter * 10100251881Speter * E.g. if all the ALLOW_* parameters are FALSE, TARGET_ABSPATH must 10101251881Speter * be a single-revision, pristine, unswitched working copy. 10102251881Speter * In other words, it must reflect a subtree of the repository as found 10103251881Speter * at single revision -- although sparse checkouts are permitted. */ 10104251881Speterstatic svn_error_t * 10105251881Speterensure_wc_is_suitable_merge_target(const char *target_abspath, 10106251881Speter svn_client_ctx_t *ctx, 10107251881Speter svn_boolean_t allow_mixed_rev, 10108251881Speter svn_boolean_t allow_local_mods, 10109251881Speter svn_boolean_t allow_switched_subtrees, 10110251881Speter apr_pool_t *scratch_pool) 10111251881Speter{ 10112251881Speter svn_node_kind_t target_kind; 10113251881Speter 10114251881Speter /* Check the target exists. */ 10115251881Speter SVN_ERR(svn_io_check_path(target_abspath, &target_kind, scratch_pool)); 10116251881Speter if (target_kind == svn_node_none) 10117251881Speter return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 10118251881Speter _("Path '%s' does not exist"), 10119251881Speter svn_dirent_local_style(target_abspath, 10120251881Speter scratch_pool)); 10121251881Speter SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, target_abspath, 10122251881Speter FALSE, FALSE, scratch_pool)); 10123251881Speter if (target_kind != svn_node_dir && target_kind != svn_node_file) 10124251881Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, 10125251881Speter _("Merge target '%s' does not exist in the " 10126251881Speter "working copy"), target_abspath); 10127251881Speter 10128251881Speter /* Perform the mixed-revision check first because it's the cheapest one. */ 10129251881Speter if (! allow_mixed_rev) 10130251881Speter { 10131251881Speter svn_revnum_t min_rev; 10132251881Speter svn_revnum_t max_rev; 10133251881Speter 10134251881Speter SVN_ERR(svn_client_min_max_revisions(&min_rev, &max_rev, target_abspath, 10135251881Speter FALSE, ctx, scratch_pool)); 10136251881Speter 10137251881Speter if (!(SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev))) 10138251881Speter { 10139251881Speter svn_boolean_t is_added; 10140251881Speter 10141251881Speter /* Allow merge into added nodes. */ 10142251881Speter SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, target_abspath, 10143251881Speter scratch_pool)); 10144251881Speter if (is_added) 10145251881Speter return SVN_NO_ERROR; 10146251881Speter else 10147251881Speter return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, 10148251881Speter _("Cannot determine revision of working " 10149251881Speter "copy")); 10150251881Speter } 10151251881Speter 10152251881Speter if (min_rev != max_rev) 10153251881Speter return svn_error_createf(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL, 10154251881Speter _("Cannot merge into mixed-revision working " 10155251881Speter "copy [%ld:%ld]; try updating first"), 10156251881Speter min_rev, max_rev); 10157251881Speter } 10158251881Speter 10159251881Speter /* Next, check for switched subtrees. */ 10160251881Speter if (! allow_switched_subtrees) 10161251881Speter { 10162251881Speter svn_boolean_t is_switched; 10163251881Speter 10164251881Speter SVN_ERR(svn_wc__has_switched_subtrees(&is_switched, ctx->wc_ctx, 10165251881Speter target_abspath, NULL, 10166251881Speter scratch_pool)); 10167251881Speter if (is_switched) 10168251881Speter return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, 10169251881Speter _("Cannot merge into a working copy " 10170251881Speter "with a switched subtree")); 10171251881Speter } 10172251881Speter 10173251881Speter /* This is the most expensive check, so it is performed last.*/ 10174251881Speter if (! allow_local_mods) 10175251881Speter { 10176251881Speter svn_boolean_t is_modified; 10177251881Speter 10178251881Speter SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx, 10179251881Speter target_abspath, 10180251881Speter ctx->cancel_func, 10181251881Speter ctx->cancel_baton, 10182251881Speter scratch_pool)); 10183251881Speter if (is_modified) 10184251881Speter return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, 10185251881Speter _("Cannot merge into a working copy " 10186251881Speter "that has local modifications")); 10187251881Speter } 10188251881Speter 10189251881Speter return SVN_NO_ERROR; 10190251881Speter} 10191251881Speter 10192251881Speter/* Throw an error if PATH_OR_URL is a path and REVISION isn't a repository 10193251881Speter * revision. */ 10194251881Speterstatic svn_error_t * 10195251881Speterensure_wc_path_has_repo_revision(const char *path_or_url, 10196251881Speter const svn_opt_revision_t *revision, 10197251881Speter apr_pool_t *scratch_pool) 10198251881Speter{ 10199251881Speter if (revision->kind != svn_opt_revision_number 10200251881Speter && revision->kind != svn_opt_revision_date 10201251881Speter && revision->kind != svn_opt_revision_head 10202251881Speter && ! svn_path_is_url(path_or_url)) 10203251881Speter return svn_error_createf( 10204251881Speter SVN_ERR_CLIENT_BAD_REVISION, NULL, 10205251881Speter _("Invalid merge source '%s'; a working copy path can only be " 10206251881Speter "used with a repository revision (a number, a date, or head)"), 10207251881Speter svn_dirent_local_style(path_or_url, scratch_pool)); 10208251881Speter return SVN_NO_ERROR; 10209251881Speter} 10210251881Speter 10211251881Speter/* "Open" the target WC for a merge. That means: 10212251881Speter * - find out its exact repository location 10213251881Speter * - check the WC for suitability (throw an error if unsuitable) 10214251881Speter * 10215251881Speter * Set *TARGET_P to a new, fully initialized, target description structure. 10216251881Speter * 10217251881Speter * ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, ALLOW_SWITCHED_SUBTREES determine 10218251881Speter * whether the WC is deemed suitable; see ensure_wc_is_suitable_merge_target() 10219251881Speter * for details. 10220251881Speter * 10221251881Speter * If the node is locally added, the rev and URL will be null/invalid. Some 10222251881Speter * kinds of merge can use such a target; others can't. 10223251881Speter */ 10224251881Speterstatic svn_error_t * 10225251881Speteropen_target_wc(merge_target_t **target_p, 10226251881Speter const char *wc_abspath, 10227251881Speter svn_boolean_t allow_mixed_rev, 10228251881Speter svn_boolean_t allow_local_mods, 10229251881Speter svn_boolean_t allow_switched_subtrees, 10230251881Speter svn_client_ctx_t *ctx, 10231251881Speter apr_pool_t *result_pool, 10232251881Speter apr_pool_t *scratch_pool) 10233251881Speter{ 10234251881Speter merge_target_t *target = apr_palloc(result_pool, sizeof(*target)); 10235251881Speter svn_client__pathrev_t *origin; 10236251881Speter 10237251881Speter target->abspath = apr_pstrdup(result_pool, wc_abspath); 10238251881Speter 10239251881Speter SVN_ERR(svn_client__wc_node_get_origin(&origin, wc_abspath, ctx, 10240251881Speter result_pool, scratch_pool)); 10241251881Speter if (origin) 10242251881Speter { 10243251881Speter target->loc = *origin; 10244251881Speter } 10245251881Speter else 10246251881Speter { 10247251881Speter svn_error_t *err; 10248251881Speter /* The node has no location in the repository. It's unversioned or 10249251881Speter * locally added or locally deleted. 10250251881Speter * 10251251881Speter * If it's locally added or deleted, find the repository root 10252251881Speter * URL and UUID anyway, and leave the node URL and revision as NULL 10253251881Speter * and INVALID. If it's unversioned, this will throw an error. */ 10254251881Speter err = svn_wc__node_get_repos_info(NULL, NULL, 10255251881Speter &target->loc.repos_root_url, 10256251881Speter &target->loc.repos_uuid, 10257251881Speter ctx->wc_ctx, wc_abspath, 10258251881Speter result_pool, scratch_pool); 10259251881Speter 10260251881Speter if (err) 10261251881Speter { 10262251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND 10263251881Speter && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY 10264251881Speter && err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED) 10265251881Speter return svn_error_trace(err); 10266251881Speter 10267251881Speter return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, err, 10268251881Speter _("Merge target '%s' does not exist in the " 10269251881Speter "working copy"), 10270251881Speter svn_dirent_local_style(wc_abspath, 10271251881Speter scratch_pool)); 10272251881Speter } 10273251881Speter 10274251881Speter target->loc.rev = SVN_INVALID_REVNUM; 10275251881Speter target->loc.url = NULL; 10276251881Speter } 10277251881Speter 10278251881Speter SVN_ERR(ensure_wc_is_suitable_merge_target( 10279251881Speter wc_abspath, ctx, 10280251881Speter allow_mixed_rev, allow_local_mods, allow_switched_subtrees, 10281251881Speter scratch_pool)); 10282251881Speter 10283251881Speter *target_p = target; 10284251881Speter return SVN_NO_ERROR; 10285251881Speter} 10286251881Speter 10287251881Speter/*-----------------------------------------------------------------------*/ 10288251881Speter 10289251881Speter/*** Public APIs ***/ 10290251881Speter 10291251881Speter/* The body of svn_client_merge5(), which see for details. 10292251881Speter * 10293251881Speter * If SOURCE1 @ REVISION1 is related to SOURCE2 @ REVISION2 then use merge 10294251881Speter * tracking (subject to other constraints -- see HONOR_MERGEINFO()); 10295251881Speter * otherwise disable merge tracking. 10296251881Speter * 10297251881Speter * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge(). 10298251881Speter */ 10299251881Speterstatic svn_error_t * 10300251881Spetermerge_locked(conflict_report_t **conflict_report, 10301251881Speter const char *source1, 10302251881Speter const svn_opt_revision_t *revision1, 10303251881Speter const char *source2, 10304251881Speter const svn_opt_revision_t *revision2, 10305251881Speter const char *target_abspath, 10306251881Speter svn_depth_t depth, 10307251881Speter svn_boolean_t ignore_mergeinfo, 10308251881Speter svn_boolean_t diff_ignore_ancestry, 10309251881Speter svn_boolean_t force_delete, 10310251881Speter svn_boolean_t record_only, 10311251881Speter svn_boolean_t dry_run, 10312251881Speter svn_boolean_t allow_mixed_rev, 10313251881Speter const apr_array_header_t *merge_options, 10314251881Speter svn_client_ctx_t *ctx, 10315251881Speter apr_pool_t *result_pool, 10316251881Speter apr_pool_t *scratch_pool) 10317251881Speter{ 10318251881Speter merge_target_t *target; 10319251881Speter svn_client__pathrev_t *source1_loc, *source2_loc; 10320251881Speter svn_boolean_t sources_related = FALSE; 10321251881Speter svn_ra_session_t *ra_session1, *ra_session2; 10322251881Speter apr_array_header_t *merge_sources; 10323251881Speter svn_error_t *err; 10324251881Speter svn_boolean_t use_sleep = FALSE; 10325251881Speter svn_client__pathrev_t *yca = NULL; 10326251881Speter apr_pool_t *sesspool; 10327251881Speter svn_boolean_t same_repos; 10328251881Speter 10329251881Speter /* ### FIXME: This function really ought to do a history check on 10330251881Speter the left and right sides of the merge source, and -- if one is an 10331251881Speter ancestor of the other -- just call svn_client_merge_peg3() with 10332251881Speter the appropriate args. */ 10333251881Speter 10334251881Speter SVN_ERR(open_target_wc(&target, target_abspath, 10335251881Speter allow_mixed_rev, TRUE, TRUE, 10336251881Speter ctx, scratch_pool, scratch_pool)); 10337251881Speter 10338251881Speter /* Open RA sessions to both sides of our merge source, and resolve URLs 10339251881Speter * and revisions. */ 10340251881Speter sesspool = svn_pool_create(scratch_pool); 10341251881Speter SVN_ERR(svn_client__ra_session_from_path2( 10342251881Speter &ra_session1, &source1_loc, 10343251881Speter source1, NULL, revision1, revision1, ctx, sesspool)); 10344251881Speter SVN_ERR(svn_client__ra_session_from_path2( 10345251881Speter &ra_session2, &source2_loc, 10346251881Speter source2, NULL, revision2, revision2, ctx, sesspool)); 10347251881Speter 10348251881Speter /* We can't do a diff between different repositories. */ 10349251881Speter /* ### We should also insist that the root URLs of the two sources match, 10350251881Speter * as we are only carrying around a single source-repos-root from now 10351251881Speter * on, and URL calculations will go wrong if they differ. 10352251881Speter * Alternatively, teach the code to cope with differing root URLs. */ 10353251881Speter SVN_ERR(check_same_repos(source1_loc, source1_loc->url, 10354251881Speter source2_loc, source2_loc->url, 10355251881Speter FALSE /* strict_urls */, scratch_pool)); 10356251881Speter 10357251881Speter /* Do our working copy and sources come from the same repository? */ 10358251881Speter same_repos = is_same_repos(&target->loc, source1_loc, TRUE /* strict_urls */); 10359251881Speter 10360251881Speter /* Unless we're ignoring ancestry, see if the two sources are related. */ 10361251881Speter if (! ignore_mergeinfo) 10362251881Speter SVN_ERR(svn_client__get_youngest_common_ancestor( 10363251881Speter &yca, source1_loc, source2_loc, ra_session1, ctx, 10364251881Speter scratch_pool, scratch_pool)); 10365251881Speter 10366251881Speter /* Check for a youngest common ancestor. If we have one, we'll be 10367251881Speter doing merge tracking. 10368251881Speter 10369251881Speter So, given a requested merge of the differences between A and 10370251881Speter B, and a common ancestor of C, we will find ourselves in one of 10371251881Speter four positions, and four different approaches: 10372251881Speter 10373251881Speter A == B == C there's nothing to merge 10374251881Speter 10375251881Speter A == C != B we merge the changes between A (or C) and B 10376251881Speter 10377251881Speter B == C != A we merge the changes between B (or C) and A 10378251881Speter 10379251881Speter A != B != C we merge the changes between A and B without 10380251881Speter merge recording, then record-only two merges: 10381251881Speter from A to C, and from C to B 10382251881Speter */ 10383251881Speter if (yca) 10384251881Speter { 10385251881Speter /* Note that our merge sources are related. */ 10386251881Speter sources_related = TRUE; 10387251881Speter 10388251881Speter /* If the common ancestor matches the right side of our merge, 10389251881Speter then we only need to reverse-merge the left side. */ 10390251881Speter if ((strcmp(yca->url, source2_loc->url) == 0) 10391251881Speter && (yca->rev == source2_loc->rev)) 10392251881Speter { 10393251881Speter SVN_ERR(normalize_merge_sources_internal( 10394251881Speter &merge_sources, source1_loc, 10395251881Speter svn_rangelist__initialize(source1_loc->rev, yca->rev, TRUE, 10396251881Speter scratch_pool), 10397251881Speter ra_session1, ctx, scratch_pool, scratch_pool)); 10398251881Speter } 10399251881Speter /* If the common ancestor matches the left side of our merge, 10400251881Speter then we only need to merge the right side. */ 10401251881Speter else if ((strcmp(yca->url, source1_loc->url) == 0) 10402251881Speter && (yca->rev == source1_loc->rev)) 10403251881Speter { 10404251881Speter SVN_ERR(normalize_merge_sources_internal( 10405251881Speter &merge_sources, source2_loc, 10406251881Speter svn_rangelist__initialize(yca->rev, source2_loc->rev, TRUE, 10407251881Speter scratch_pool), 10408251881Speter ra_session2, ctx, scratch_pool, scratch_pool)); 10409251881Speter } 10410251881Speter /* And otherwise, we need to do both: reverse merge the left 10411251881Speter side, and merge the right. */ 10412251881Speter else 10413251881Speter { 10414251881Speter merge_source_t source; 10415251881Speter 10416251881Speter source.loc1 = source1_loc; 10417251881Speter source.loc2 = source2_loc; 10418251881Speter source.ancestral = FALSE; 10419251881Speter 10420251881Speter err = merge_cousins_and_supplement_mergeinfo(conflict_report, 10421251881Speter &use_sleep, 10422251881Speter target, 10423251881Speter ra_session1, 10424251881Speter ra_session2, 10425251881Speter &source, 10426251881Speter yca, 10427251881Speter same_repos, 10428251881Speter depth, 10429251881Speter diff_ignore_ancestry, 10430251881Speter force_delete, 10431251881Speter record_only, dry_run, 10432251881Speter merge_options, 10433251881Speter ctx, 10434251881Speter result_pool, 10435251881Speter scratch_pool); 10436251881Speter /* Close our temporary RA sessions (this could've happened 10437251881Speter after the second call to normalize_merge_sources() inside 10438251881Speter the merge_cousins_and_supplement_mergeinfo() routine). */ 10439251881Speter svn_pool_destroy(sesspool); 10440251881Speter 10441251881Speter if (use_sleep) 10442251881Speter svn_io_sleep_for_timestamps(target->abspath, scratch_pool); 10443251881Speter 10444251881Speter SVN_ERR(err); 10445251881Speter return SVN_NO_ERROR; 10446251881Speter } 10447251881Speter } 10448251881Speter else 10449251881Speter { 10450251881Speter merge_source_t source; 10451251881Speter 10452251881Speter source.loc1 = source1_loc; 10453251881Speter source.loc2 = source2_loc; 10454251881Speter source.ancestral = FALSE; 10455251881Speter 10456251881Speter /* Build a single-item merge_source_t array. */ 10457251881Speter merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *)); 10458251881Speter APR_ARRAY_PUSH(merge_sources, merge_source_t *) = &source; 10459251881Speter } 10460251881Speter 10461251881Speter err = do_merge(NULL, NULL, conflict_report, &use_sleep, 10462251881Speter merge_sources, target, 10463251881Speter ra_session1, sources_related, same_repos, 10464251881Speter ignore_mergeinfo, diff_ignore_ancestry, force_delete, dry_run, 10465251881Speter record_only, NULL, FALSE, FALSE, depth, merge_options, 10466251881Speter ctx, result_pool, scratch_pool); 10467251881Speter 10468251881Speter /* Close our temporary RA sessions. */ 10469251881Speter svn_pool_destroy(sesspool); 10470251881Speter 10471251881Speter if (use_sleep) 10472251881Speter svn_io_sleep_for_timestamps(target->abspath, scratch_pool); 10473251881Speter 10474251881Speter SVN_ERR(err); 10475251881Speter return SVN_NO_ERROR; 10476251881Speter} 10477251881Speter 10478251881Speter/* Set *TARGET_ABSPATH to the absolute path of, and *LOCK_ABSPATH to 10479251881Speter the absolute path to lock for, TARGET_WCPATH. */ 10480251881Speterstatic svn_error_t * 10481251881Speterget_target_and_lock_abspath(const char **target_abspath, 10482251881Speter const char **lock_abspath, 10483251881Speter const char *target_wcpath, 10484251881Speter svn_client_ctx_t *ctx, 10485251881Speter apr_pool_t *result_pool) 10486251881Speter{ 10487251881Speter svn_node_kind_t kind; 10488251881Speter SVN_ERR(svn_dirent_get_absolute(target_abspath, target_wcpath, 10489251881Speter result_pool)); 10490251881Speter SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, *target_abspath, 10491251881Speter FALSE, FALSE, result_pool)); 10492251881Speter if (kind == svn_node_dir) 10493251881Speter *lock_abspath = *target_abspath; 10494251881Speter else 10495251881Speter *lock_abspath = svn_dirent_dirname(*target_abspath, result_pool); 10496251881Speter 10497251881Speter return SVN_NO_ERROR; 10498251881Speter} 10499251881Speter 10500251881Spetersvn_error_t * 10501251881Spetersvn_client_merge5(const char *source1, 10502251881Speter const svn_opt_revision_t *revision1, 10503251881Speter const char *source2, 10504251881Speter const svn_opt_revision_t *revision2, 10505251881Speter const char *target_wcpath, 10506251881Speter svn_depth_t depth, 10507251881Speter svn_boolean_t ignore_mergeinfo, 10508251881Speter svn_boolean_t diff_ignore_ancestry, 10509251881Speter svn_boolean_t force_delete, 10510251881Speter svn_boolean_t record_only, 10511251881Speter svn_boolean_t dry_run, 10512251881Speter svn_boolean_t allow_mixed_rev, 10513251881Speter const apr_array_header_t *merge_options, 10514251881Speter svn_client_ctx_t *ctx, 10515251881Speter apr_pool_t *pool) 10516251881Speter{ 10517251881Speter const char *target_abspath, *lock_abspath; 10518251881Speter conflict_report_t *conflict_report; 10519251881Speter 10520251881Speter /* Sanity check our input -- we require specified revisions, 10521251881Speter * and either 2 paths or 2 URLs. */ 10522251881Speter if ((revision1->kind == svn_opt_revision_unspecified) 10523251881Speter || (revision2->kind == svn_opt_revision_unspecified)) 10524251881Speter return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, 10525251881Speter _("Not all required revisions are specified")); 10526251881Speter if (svn_path_is_url(source1) != svn_path_is_url(source2)) 10527251881Speter return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, 10528251881Speter _("Merge sources must both be " 10529251881Speter "either paths or URLs")); 10530251881Speter /* A WC path must be used with a repository revision, as we can't 10531251881Speter * (currently) use the WC itself as a source, we can only read the URL 10532251881Speter * from it and use that. */ 10533251881Speter SVN_ERR(ensure_wc_path_has_repo_revision(source1, revision1, pool)); 10534251881Speter SVN_ERR(ensure_wc_path_has_repo_revision(source2, revision2, pool)); 10535251881Speter 10536251881Speter SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath, 10537251881Speter target_wcpath, ctx, pool)); 10538251881Speter 10539251881Speter if (!dry_run) 10540251881Speter SVN_WC__CALL_WITH_WRITE_LOCK( 10541251881Speter merge_locked(&conflict_report, 10542251881Speter source1, revision1, source2, revision2, 10543251881Speter target_abspath, depth, ignore_mergeinfo, 10544251881Speter diff_ignore_ancestry, 10545251881Speter force_delete, record_only, dry_run, 10546251881Speter allow_mixed_rev, merge_options, ctx, pool, pool), 10547251881Speter ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool); 10548251881Speter else 10549251881Speter SVN_ERR(merge_locked(&conflict_report, 10550251881Speter source1, revision1, source2, revision2, 10551251881Speter target_abspath, depth, ignore_mergeinfo, 10552251881Speter diff_ignore_ancestry, 10553251881Speter force_delete, record_only, dry_run, 10554251881Speter allow_mixed_rev, merge_options, ctx, pool, pool)); 10555251881Speter 10556251881Speter SVN_ERR(make_merge_conflict_error(conflict_report, pool)); 10557251881Speter return SVN_NO_ERROR; 10558251881Speter} 10559251881Speter 10560251881Speter 10561251881Speter/* Check if mergeinfo for a given path is described explicitly or via 10562251881Speter inheritance in a mergeinfo catalog. 10563251881Speter 10564251881Speter If REPOS_REL_PATH exists in CATALOG and has mergeinfo containing 10565251881Speter MERGEINFO, then set *IN_CATALOG to TRUE. If REPOS_REL_PATH does 10566251881Speter not exist in CATALOG, then find its nearest parent which does exist. 10567251881Speter If the mergeinfo REPOS_REL_PATH would inherit from that parent 10568251881Speter contains MERGEINFO then set *IN_CATALOG to TRUE. Set *IN_CATALOG 10569251881Speter to FALSE in all other cases. 10570251881Speter 10571251881Speter Set *CAT_KEY_PATH to the key path in CATALOG for REPOS_REL_PATH's 10572251881Speter explicit or inherited mergeinfo. If no explicit or inherited mergeinfo 10573251881Speter is found for REPOS_REL_PATH then set *CAT_KEY_PATH to NULL. 10574251881Speter 10575251881Speter User RESULT_POOL to allocate *CAT_KEY_PATH. Use SCRATCH_POOL for 10576251881Speter temporary allocations. */ 10577251881Speterstatic svn_error_t * 10578251881Spetermergeinfo_in_catalog(svn_boolean_t *in_catalog, 10579251881Speter const char **cat_key_path, 10580251881Speter const char *repos_rel_path, 10581251881Speter svn_mergeinfo_t mergeinfo, 10582251881Speter svn_mergeinfo_catalog_t catalog, 10583251881Speter apr_pool_t *result_pool, 10584251881Speter apr_pool_t *scratch_pool) 10585251881Speter{ 10586251881Speter const char *walk_path = NULL; 10587251881Speter 10588251881Speter *in_catalog = FALSE; 10589251881Speter *cat_key_path = NULL; 10590251881Speter 10591251881Speter if (mergeinfo && catalog && apr_hash_count(catalog)) 10592251881Speter { 10593251881Speter const char *path = repos_rel_path; 10594251881Speter 10595251881Speter /* Start with the assumption there is no explicit or inherited 10596251881Speter mergeinfo for REPOS_REL_PATH in CATALOG. */ 10597251881Speter svn_mergeinfo_t mergeinfo_in_cat = NULL; 10598251881Speter 10599251881Speter while (1) 10600251881Speter { 10601251881Speter mergeinfo_in_cat = svn_hash_gets(catalog, path); 10602251881Speter 10603251881Speter if (mergeinfo_in_cat) /* Found it! */ 10604251881Speter { 10605251881Speter *cat_key_path = apr_pstrdup(result_pool, path); 10606251881Speter break; 10607251881Speter } 10608251881Speter else /* Look for inherited mergeinfo. */ 10609251881Speter { 10610251881Speter walk_path = svn_relpath_join(svn_relpath_basename(path, 10611251881Speter scratch_pool), 10612251881Speter walk_path ? walk_path : "", 10613251881Speter scratch_pool); 10614251881Speter path = svn_relpath_dirname(path, scratch_pool); 10615251881Speter 10616251881Speter if (path[0] == '\0') /* No mergeinfo to inherit. */ 10617251881Speter break; 10618251881Speter } 10619251881Speter } 10620251881Speter 10621251881Speter if (mergeinfo_in_cat) 10622251881Speter { 10623251881Speter if (walk_path) 10624251881Speter SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(&mergeinfo_in_cat, 10625251881Speter mergeinfo_in_cat, 10626251881Speter walk_path, 10627251881Speter scratch_pool, 10628251881Speter scratch_pool)); 10629251881Speter SVN_ERR(svn_mergeinfo_intersect2(&mergeinfo_in_cat, 10630251881Speter mergeinfo_in_cat, mergeinfo, 10631251881Speter TRUE, 10632251881Speter scratch_pool, scratch_pool)); 10633251881Speter SVN_ERR(svn_mergeinfo__equals(in_catalog, mergeinfo_in_cat, 10634251881Speter mergeinfo, TRUE, scratch_pool)); 10635251881Speter } 10636251881Speter } 10637251881Speter 10638251881Speter return SVN_NO_ERROR; 10639251881Speter} 10640251881Speter 10641251881Speter/* A svn_log_entry_receiver_t baton for log_find_operative_revs(). */ 10642251881Spetertypedef struct log_find_operative_baton_t 10643251881Speter{ 10644251881Speter /* The catalog of explicit mergeinfo on a reintegrate source. */ 10645251881Speter svn_mergeinfo_catalog_t merged_catalog; 10646251881Speter 10647251881Speter /* The catalog of unmerged history from the reintegrate target to 10648251881Speter the source which we will create. Allocated in RESULT_POOL. */ 10649251881Speter svn_mergeinfo_catalog_t unmerged_catalog; 10650251881Speter 10651251881Speter /* The repository absolute path of the reintegrate target. */ 10652251881Speter const char *target_fspath; 10653251881Speter 10654251881Speter /* The path of the reintegrate source relative to the repository root. */ 10655251881Speter const char *source_repos_rel_path; 10656251881Speter 10657251881Speter apr_pool_t *result_pool; 10658251881Speter} log_find_operative_baton_t; 10659251881Speter 10660251881Speter/* A svn_log_entry_receiver_t callback for find_unsynced_ranges(). */ 10661251881Speterstatic svn_error_t * 10662251881Speterlog_find_operative_revs(void *baton, 10663251881Speter svn_log_entry_t *log_entry, 10664251881Speter apr_pool_t *pool) 10665251881Speter{ 10666251881Speter log_find_operative_baton_t *log_baton = baton; 10667251881Speter apr_hash_index_t *hi; 10668251881Speter svn_revnum_t revision; 10669251881Speter 10670251881Speter /* It's possible that authz restrictions on the merge source prevent us 10671251881Speter from knowing about any of the changes for LOG_ENTRY->REVISION. */ 10672251881Speter if (!log_entry->changed_paths2) 10673251881Speter return SVN_NO_ERROR; 10674251881Speter 10675251881Speter revision = log_entry->revision; 10676251881Speter 10677251881Speter for (hi = apr_hash_first(pool, log_entry->changed_paths2); 10678251881Speter hi; 10679251881Speter hi = apr_hash_next(hi)) 10680251881Speter { 10681251881Speter const char *subtree_missing_this_rev; 10682251881Speter const char *path = svn__apr_hash_index_key(hi); 10683251881Speter const char *rel_path; 10684251881Speter const char *source_rel_path; 10685251881Speter svn_boolean_t in_catalog; 10686251881Speter svn_mergeinfo_t log_entry_as_mergeinfo; 10687251881Speter 10688251881Speter rel_path = svn_fspath__skip_ancestor(log_baton->target_fspath, path); 10689251881Speter /* Easy out: The path is not within the tree of interest. */ 10690251881Speter if (rel_path == NULL) 10691251881Speter continue; 10692251881Speter 10693251881Speter source_rel_path = svn_relpath_join(log_baton->source_repos_rel_path, 10694251881Speter rel_path, pool); 10695251881Speter 10696251881Speter SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo, 10697251881Speter apr_psprintf(pool, "%s:%ld", 10698251881Speter path, revision), 10699251881Speter pool)); 10700251881Speter 10701251881Speter SVN_ERR(mergeinfo_in_catalog(&in_catalog, &subtree_missing_this_rev, 10702251881Speter source_rel_path, log_entry_as_mergeinfo, 10703251881Speter log_baton->merged_catalog, 10704251881Speter pool, pool)); 10705251881Speter 10706251881Speter if (!in_catalog) 10707251881Speter { 10708251881Speter svn_mergeinfo_t unmerged_for_key; 10709251881Speter const char *suffix, *missing_path; 10710251881Speter 10711251881Speter /* If there is no mergeinfo on the source tree we'll say 10712251881Speter the "subtree" missing this revision is the root of the 10713251881Speter source. */ 10714251881Speter if (!subtree_missing_this_rev) 10715251881Speter subtree_missing_this_rev = log_baton->source_repos_rel_path; 10716251881Speter 10717251881Speter suffix = svn_relpath_skip_ancestor(subtree_missing_this_rev, 10718251881Speter source_rel_path); 10719251881Speter if (suffix) 10720251881Speter { 10721251881Speter missing_path = apr_pstrmemdup(pool, path, 10722251881Speter strlen(path) - strlen(suffix) - 1); 10723251881Speter } 10724251881Speter else 10725251881Speter { 10726251881Speter missing_path = path; 10727251881Speter } 10728251881Speter 10729251881Speter SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo, 10730251881Speter apr_psprintf(pool, "%s:%ld", 10731251881Speter missing_path, revision), 10732251881Speter log_baton->result_pool)); 10733251881Speter unmerged_for_key = svn_hash_gets(log_baton->unmerged_catalog, 10734251881Speter subtree_missing_this_rev); 10735251881Speter 10736251881Speter if (unmerged_for_key) 10737251881Speter { 10738251881Speter SVN_ERR(svn_mergeinfo_merge2(unmerged_for_key, 10739251881Speter log_entry_as_mergeinfo, 10740251881Speter log_baton->result_pool, 10741251881Speter pool)); 10742251881Speter } 10743251881Speter else 10744251881Speter { 10745251881Speter svn_hash_sets(log_baton->unmerged_catalog, 10746251881Speter apr_pstrdup(log_baton->result_pool, 10747251881Speter subtree_missing_this_rev), 10748251881Speter log_entry_as_mergeinfo); 10749251881Speter } 10750251881Speter 10751251881Speter } 10752251881Speter } 10753251881Speter return SVN_NO_ERROR; 10754251881Speter} 10755251881Speter 10756251881Speter/* Determine if the mergeinfo on a reintegrate source SOURCE_LOC, 10757251881Speter reflects that the source is fully synced with the reintegrate target 10758251881Speter TARGET_LOC, even if a naive interpretation of the source's 10759251881Speter mergeinfo says otherwise -- See issue #3577. 10760251881Speter 10761251881Speter UNMERGED_CATALOG represents the history (as mergeinfo) from 10762251881Speter TARGET_LOC that is not represented in SOURCE_LOC's 10763251881Speter explicit/inherited mergeinfo as represented by MERGED_CATALOG. 10764251881Speter MERGEINFO_CATALOG may be empty if the source has no explicit or inherited 10765251881Speter mergeinfo. 10766251881Speter 10767251881Speter Check that all of the unmerged revisions in UNMERGED_CATALOG's 10768251881Speter mergeinfos are "phantoms", that is, one of the following conditions holds: 10769251881Speter 10770251881Speter 1) The revision affects no corresponding paths in SOURCE_LOC. 10771251881Speter 10772251881Speter 2) The revision affects corresponding paths in SOURCE_LOC, 10773251881Speter but based on the mergeinfo in MERGED_CATALOG, the change was 10774251881Speter previously merged. 10775251881Speter 10776251881Speter Make a deep copy, allocated in RESULT_POOL, of any portions of 10777251881Speter UNMERGED_CATALOG that are not phantoms, to TRUE_UNMERGED_CATALOG. 10778251881Speter 10779251881Speter Note: The keys in all mergeinfo catalogs used here are relative to the 10780251881Speter root of the repository. 10781251881Speter 10782251881Speter RA_SESSION is an RA session open to the repository of TARGET_LOC; it may 10783251881Speter be temporarily reparented within this function. 10784251881Speter 10785251881Speter Use SCRATCH_POOL for all temporary allocations. */ 10786251881Speterstatic svn_error_t * 10787251881Speterfind_unsynced_ranges(const svn_client__pathrev_t *source_loc, 10788251881Speter const svn_client__pathrev_t *target_loc, 10789251881Speter svn_mergeinfo_catalog_t unmerged_catalog, 10790251881Speter svn_mergeinfo_catalog_t merged_catalog, 10791251881Speter svn_mergeinfo_catalog_t true_unmerged_catalog, 10792251881Speter svn_ra_session_t *ra_session, 10793251881Speter apr_pool_t *result_pool, 10794251881Speter apr_pool_t *scratch_pool) 10795251881Speter{ 10796251881Speter svn_rangelist_t *potentially_unmerged_ranges = NULL; 10797251881Speter 10798251881Speter /* Convert all the unmerged history to a rangelist. */ 10799251881Speter if (apr_hash_count(unmerged_catalog)) 10800251881Speter { 10801251881Speter apr_hash_index_t *hi_catalog; 10802251881Speter 10803251881Speter potentially_unmerged_ranges = 10804251881Speter apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *)); 10805251881Speter 10806251881Speter for (hi_catalog = apr_hash_first(scratch_pool, unmerged_catalog); 10807251881Speter hi_catalog; 10808251881Speter hi_catalog = apr_hash_next(hi_catalog)) 10809251881Speter { 10810251881Speter svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi_catalog); 10811251881Speter 10812251881Speter SVN_ERR(svn_rangelist__merge_many(potentially_unmerged_ranges, 10813251881Speter mergeinfo, 10814251881Speter scratch_pool, scratch_pool)); 10815251881Speter } 10816251881Speter } 10817251881Speter 10818251881Speter /* Find any unmerged revisions which both affect the source and 10819251881Speter are not yet merged to it. */ 10820251881Speter if (potentially_unmerged_ranges) 10821251881Speter { 10822251881Speter svn_revnum_t oldest_rev = 10823251881Speter (APR_ARRAY_IDX(potentially_unmerged_ranges, 10824251881Speter 0, 10825251881Speter svn_merge_range_t *))->start + 1; 10826251881Speter svn_revnum_t youngest_rev = 10827251881Speter (APR_ARRAY_IDX(potentially_unmerged_ranges, 10828251881Speter potentially_unmerged_ranges->nelts - 1, 10829251881Speter svn_merge_range_t *))->end; 10830251881Speter log_find_operative_baton_t log_baton; 10831251881Speter const char *old_session_url; 10832251881Speter svn_error_t *err; 10833251881Speter 10834251881Speter log_baton.merged_catalog = merged_catalog; 10835251881Speter log_baton.unmerged_catalog = true_unmerged_catalog; 10836251881Speter log_baton.source_repos_rel_path 10837251881Speter = svn_client__pathrev_relpath(source_loc, scratch_pool); 10838251881Speter log_baton.target_fspath 10839251881Speter = svn_client__pathrev_fspath(target_loc, scratch_pool); 10840251881Speter log_baton.result_pool = result_pool; 10841251881Speter 10842251881Speter SVN_ERR(svn_client__ensure_ra_session_url( 10843251881Speter &old_session_url, ra_session, target_loc->url, scratch_pool)); 10844251881Speter err = get_log(ra_session, "", youngest_rev, oldest_rev, 10845251881Speter TRUE, /* discover_changed_paths */ 10846251881Speter log_find_operative_revs, &log_baton, 10847251881Speter scratch_pool); 10848251881Speter SVN_ERR(svn_error_compose_create( 10849251881Speter err, svn_ra_reparent(ra_session, old_session_url, scratch_pool))); 10850251881Speter } 10851251881Speter 10852251881Speter return SVN_NO_ERROR; 10853251881Speter} 10854251881Speter 10855251881Speter 10856251881Speter/* Find the youngest revision that has been merged from target to source. 10857251881Speter * 10858251881Speter * If any location in TARGET_HISTORY_AS_MERGEINFO is mentioned in 10859251881Speter * SOURCE_MERGEINFO, then we know that at least one merge was done from the 10860251881Speter * target to the source. In that case, set *YOUNGEST_MERGED_REV to the 10861251881Speter * youngest revision of that intersection (unless *YOUNGEST_MERGED_REV is 10862251881Speter * already younger than that). Otherwise, leave *YOUNGEST_MERGED_REV alone. 10863251881Speter */ 10864251881Speterstatic svn_error_t * 10865251881Speterfind_youngest_merged_rev(svn_revnum_t *youngest_merged_rev, 10866251881Speter svn_mergeinfo_t target_history_as_mergeinfo, 10867251881Speter svn_mergeinfo_t source_mergeinfo, 10868251881Speter apr_pool_t *scratch_pool) 10869251881Speter{ 10870251881Speter svn_mergeinfo_t explicit_source_target_history_intersection; 10871251881Speter 10872251881Speter SVN_ERR(svn_mergeinfo_intersect2( 10873251881Speter &explicit_source_target_history_intersection, 10874251881Speter source_mergeinfo, target_history_as_mergeinfo, TRUE, 10875251881Speter scratch_pool, scratch_pool)); 10876251881Speter if (apr_hash_count(explicit_source_target_history_intersection)) 10877251881Speter { 10878251881Speter svn_revnum_t old_rev, young_rev; 10879251881Speter 10880251881Speter /* Keep track of the youngest revision merged from target to source. */ 10881251881Speter SVN_ERR(svn_mergeinfo__get_range_endpoints( 10882251881Speter &young_rev, &old_rev, 10883251881Speter explicit_source_target_history_intersection, scratch_pool)); 10884251881Speter if (!SVN_IS_VALID_REVNUM(*youngest_merged_rev) 10885251881Speter || (young_rev > *youngest_merged_rev)) 10886251881Speter *youngest_merged_rev = young_rev; 10887251881Speter } 10888251881Speter 10889251881Speter return SVN_NO_ERROR; 10890251881Speter} 10891251881Speter 10892251881Speter/* Set *FILTERED_MERGEINFO_P to the parts of TARGET_HISTORY_AS_MERGEINFO 10893251881Speter * that are not present in the source branch. 10894251881Speter * 10895251881Speter * SOURCE_MERGEINFO is the explicit or inherited mergeinfo of the source 10896251881Speter * branch SOURCE_PATHREV. Extend SOURCE_MERGEINFO, modifying it in 10897251881Speter * place, to include the natural history (implicit mergeinfo) of 10898251881Speter * SOURCE_PATHREV. ### But make these additions in SCRATCH_POOL. 10899251881Speter * 10900251881Speter * SOURCE_RA_SESSION is an RA session open to the repository containing 10901251881Speter * SOURCE_PATHREV; it may be temporarily reparented within this function. 10902251881Speter * 10903251881Speter * ### [JAF] This function is named '..._subroutine' simply because I 10904251881Speter * factored it out based on code similarity, without knowing what it's 10905251881Speter * purpose is. We should clarify its purpose and choose a better name. 10906251881Speter */ 10907251881Speterstatic svn_error_t * 10908251881Speterfind_unmerged_mergeinfo_subroutine(svn_mergeinfo_t *filtered_mergeinfo_p, 10909251881Speter svn_mergeinfo_t target_history_as_mergeinfo, 10910251881Speter svn_mergeinfo_t source_mergeinfo, 10911251881Speter const svn_client__pathrev_t *source_pathrev, 10912251881Speter svn_ra_session_t *source_ra_session, 10913251881Speter svn_client_ctx_t *ctx, 10914251881Speter apr_pool_t *result_pool, 10915251881Speter apr_pool_t *scratch_pool) 10916251881Speter{ 10917251881Speter svn_mergeinfo_t source_history_as_mergeinfo; 10918251881Speter 10919251881Speter /* Get the source path's natural history and merge it into source 10920251881Speter path's explicit or inherited mergeinfo. */ 10921251881Speter SVN_ERR(svn_client__get_history_as_mergeinfo( 10922251881Speter &source_history_as_mergeinfo, NULL /* has_rev_zero_history */, 10923251881Speter source_pathrev, source_pathrev->rev, SVN_INVALID_REVNUM, 10924251881Speter source_ra_session, ctx, scratch_pool)); 10925251881Speter SVN_ERR(svn_mergeinfo_merge2(source_mergeinfo, 10926251881Speter source_history_as_mergeinfo, 10927251881Speter scratch_pool, scratch_pool)); 10928251881Speter 10929251881Speter /* Now source_mergeinfo represents everything we know about 10930251881Speter source_path's history. Now we need to know what part, if any, of the 10931251881Speter corresponding target's history is *not* part of source_path's total 10932251881Speter history; because it is neither shared history nor was it ever merged 10933251881Speter from the target to the source. */ 10934251881Speter SVN_ERR(svn_mergeinfo_remove2(filtered_mergeinfo_p, 10935251881Speter source_mergeinfo, 10936251881Speter target_history_as_mergeinfo, TRUE, 10937251881Speter result_pool, scratch_pool)); 10938251881Speter return SVN_NO_ERROR; 10939251881Speter} 10940251881Speter 10941251881Speter/* Helper for calculate_left_hand_side() which produces a mergeinfo catalog 10942251881Speter describing what parts of of the reintegrate target have not previously been 10943251881Speter merged to the reintegrate source. 10944251881Speter 10945251881Speter SOURCE_CATALOG is the collection of explicit mergeinfo on SOURCE_LOC and 10946251881Speter all its children, i.e. the mergeinfo catalog for the reintegrate source. 10947251881Speter 10948251881Speter TARGET_HISTORY_HASH is a hash of (const char *) paths mapped to 10949251881Speter svn_mergeinfo_t representing the location history. Each of these 10950251881Speter path keys represent a path in the reintegrate target, relative to the 10951251881Speter repository root, which has explicit mergeinfo and/or is the reintegrate 10952251881Speter target itself. The svn_mergeinfo_t's contain the natural history of each 10953251881Speter path@TARGET_REV. Effectively this is the mergeinfo catalog on the 10954251881Speter reintegrate target. 10955251881Speter 10956251881Speter YC_ANCESTOR_REV is the revision of the youngest common ancestor of the 10957251881Speter reintegrate source and the reintegrate target. 10958251881Speter 10959251881Speter SOURCE_LOC is the reintegrate source. 10960251881Speter 10961251881Speter SOURCE_RA_SESSION is a session opened to the URL of SOURCE_LOC 10962251881Speter and TARGET_RA_SESSION is open to TARGET->loc.url. 10963251881Speter 10964251881Speter For each entry in TARGET_HISTORY_HASH check that the history it 10965251881Speter represents is contained in either the explicit mergeinfo for the 10966251881Speter corresponding path in SOURCE_CATALOG, the corresponding path's inherited 10967251881Speter mergeinfo (if no explicit mergeinfo for the path is found in 10968251881Speter SOURCE_CATALOG), or the corresponding path's natural history. Populate 10969251881Speter *UNMERGED_TO_SOURCE_CATALOG with the corresponding source paths mapped to 10970251881Speter the mergeinfo from the target's natural history which is *not* found. Also 10971251881Speter include any mergeinfo from SOURCE_CATALOG which explicitly describes the 10972251881Speter target's history but for which *no* entry was found in 10973251881Speter TARGET_HISTORY_HASH. 10974251881Speter 10975251881Speter If no part of TARGET_HISTORY_HASH is found in SOURCE_CATALOG set 10976251881Speter *YOUNGEST_MERGED_REV to SVN_INVALID_REVNUM; otherwise set it to the youngest 10977251881Speter revision previously merged from the target to the source, and filter 10978251881Speter *UNMERGED_TO_SOURCE_CATALOG so that it contains no ranges greater than 10979251881Speter *YOUNGEST_MERGED_REV. 10980251881Speter 10981251881Speter *UNMERGED_TO_SOURCE_CATALOG is (deeply) allocated in RESULT_POOL. 10982251881Speter SCRATCH_POOL is used for all temporary allocations. */ 10983251881Speterstatic svn_error_t * 10984251881Speterfind_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog, 10985251881Speter svn_revnum_t *youngest_merged_rev, 10986251881Speter svn_revnum_t yc_ancestor_rev, 10987251881Speter svn_mergeinfo_catalog_t source_catalog, 10988251881Speter apr_hash_t *target_history_hash, 10989251881Speter const svn_client__pathrev_t *source_loc, 10990251881Speter const merge_target_t *target, 10991251881Speter svn_ra_session_t *source_ra_session, 10992251881Speter svn_ra_session_t *target_ra_session, 10993251881Speter svn_client_ctx_t *ctx, 10994251881Speter apr_pool_t *result_pool, 10995251881Speter apr_pool_t *scratch_pool) 10996251881Speter{ 10997251881Speter const char *source_repos_rel_path 10998251881Speter = svn_client__pathrev_relpath(source_loc, scratch_pool); 10999251881Speter const char *target_repos_rel_path 11000251881Speter = svn_client__pathrev_relpath(&target->loc, scratch_pool); 11001251881Speter apr_hash_index_t *hi; 11002251881Speter svn_mergeinfo_catalog_t new_catalog = apr_hash_make(result_pool); 11003251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 11004251881Speter 11005251881Speter assert(session_url_is(source_ra_session, source_loc->url, scratch_pool)); 11006251881Speter assert(session_url_is(target_ra_session, target->loc.url, scratch_pool)); 11007251881Speter 11008251881Speter *youngest_merged_rev = SVN_INVALID_REVNUM; 11009251881Speter 11010251881Speter /* Examine the natural history of each path in the reintegrate target 11011251881Speter with explicit mergeinfo. */ 11012251881Speter for (hi = apr_hash_first(scratch_pool, target_history_hash); 11013251881Speter hi; 11014251881Speter hi = apr_hash_next(hi)) 11015251881Speter { 11016251881Speter const char *target_path = svn__apr_hash_index_key(hi); 11017251881Speter svn_mergeinfo_t target_history_as_mergeinfo = svn__apr_hash_index_val(hi); 11018251881Speter const char *path_rel_to_session 11019251881Speter = svn_relpath_skip_ancestor(target_repos_rel_path, target_path); 11020251881Speter const char *source_path; 11021251881Speter svn_client__pathrev_t *source_pathrev; 11022251881Speter svn_mergeinfo_t source_mergeinfo, filtered_mergeinfo; 11023251881Speter 11024251881Speter svn_pool_clear(iterpool); 11025251881Speter 11026251881Speter source_path = svn_relpath_join(source_repos_rel_path, 11027251881Speter path_rel_to_session, iterpool); 11028251881Speter source_pathrev = svn_client__pathrev_join_relpath( 11029251881Speter source_loc, path_rel_to_session, iterpool); 11030251881Speter 11031251881Speter /* Remove any target history that is also part of the source's history, 11032251881Speter i.e. their common ancestry. By definition this has already been 11033251881Speter "merged" from the target to the source. If the source has explicit 11034251881Speter self referential mergeinfo it would intersect with the target's 11035251881Speter history below, making it appear that some merges had been done from 11036251881Speter the target to the source, when this might not actually be the case. */ 11037251881Speter SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( 11038251881Speter &target_history_as_mergeinfo, target_history_as_mergeinfo, 11039251881Speter source_loc->rev, yc_ancestor_rev, TRUE, iterpool, iterpool)); 11040251881Speter 11041251881Speter /* Look for any explicit mergeinfo on the source path corresponding to 11042251881Speter the target path. If we find any remove that from SOURCE_CATALOG. 11043251881Speter When this iteration over TARGET_HISTORY_HASH is complete all that 11044251881Speter should be left in SOURCE_CATALOG are subtrees that have explicit 11045251881Speter mergeinfo on the reintegrate source where there is no corresponding 11046251881Speter explicit mergeinfo on the reintegrate target. */ 11047251881Speter source_mergeinfo = svn_hash_gets(source_catalog, source_path); 11048251881Speter if (source_mergeinfo) 11049251881Speter { 11050251881Speter svn_hash_sets(source_catalog, source_path, NULL); 11051251881Speter 11052251881Speter SVN_ERR(find_youngest_merged_rev(youngest_merged_rev, 11053251881Speter target_history_as_mergeinfo, 11054251881Speter source_mergeinfo, 11055251881Speter iterpool)); 11056251881Speter } 11057251881Speter else 11058251881Speter { 11059251881Speter /* There is no mergeinfo on source_path *or* source_path doesn't 11060251881Speter exist at all. If simply doesn't exist we can ignore it 11061251881Speter altogether. */ 11062251881Speter svn_node_kind_t kind; 11063251881Speter 11064251881Speter SVN_ERR(svn_ra_check_path(source_ra_session, 11065251881Speter path_rel_to_session, 11066251881Speter source_loc->rev, &kind, iterpool)); 11067251881Speter if (kind == svn_node_none) 11068251881Speter continue; 11069251881Speter /* Else source_path does exist though it has no explicit mergeinfo. 11070251881Speter Find its inherited mergeinfo. If it doesn't have any then simply 11071251881Speter set source_mergeinfo to an empty hash. */ 11072251881Speter SVN_ERR(svn_client__get_repos_mergeinfo( 11073251881Speter &source_mergeinfo, source_ra_session, 11074251881Speter source_pathrev->url, source_pathrev->rev, 11075251881Speter svn_mergeinfo_inherited, FALSE /*squelch_incapable*/, 11076251881Speter iterpool)); 11077251881Speter if (!source_mergeinfo) 11078251881Speter source_mergeinfo = apr_hash_make(iterpool); 11079251881Speter } 11080251881Speter 11081251881Speter /* Use scratch_pool rather than iterpool because filtered_mergeinfo 11082251881Speter is going into new_catalog below and needs to last to the end of 11083251881Speter this function. */ 11084251881Speter SVN_ERR(find_unmerged_mergeinfo_subroutine( 11085251881Speter &filtered_mergeinfo, target_history_as_mergeinfo, 11086251881Speter source_mergeinfo, source_pathrev, 11087251881Speter source_ra_session, ctx, scratch_pool, iterpool)); 11088251881Speter svn_hash_sets(new_catalog, apr_pstrdup(scratch_pool, source_path), 11089251881Speter filtered_mergeinfo); 11090251881Speter } 11091251881Speter 11092251881Speter /* Are there any subtrees with explicit mergeinfo still left in the merge 11093251881Speter source where there was no explicit mergeinfo for the corresponding path 11094251881Speter in the merge target? If so, add the intersection of those path's 11095251881Speter mergeinfo and the corresponding target path's mergeinfo to 11096251881Speter new_catalog. */ 11097251881Speter for (hi = apr_hash_first(scratch_pool, source_catalog); 11098251881Speter hi; 11099251881Speter hi = apr_hash_next(hi)) 11100251881Speter { 11101251881Speter const char *source_path = svn__apr_hash_index_key(hi); 11102251881Speter const char *path_rel_to_session = 11103251881Speter svn_relpath_skip_ancestor(source_repos_rel_path, source_path); 11104251881Speter const char *source_url; 11105251881Speter svn_mergeinfo_t source_mergeinfo = svn__apr_hash_index_val(hi); 11106251881Speter svn_mergeinfo_t filtered_mergeinfo; 11107251881Speter svn_client__pathrev_t *target_pathrev; 11108251881Speter svn_mergeinfo_t target_history_as_mergeinfo; 11109251881Speter svn_error_t *err; 11110251881Speter 11111251881Speter svn_pool_clear(iterpool); 11112251881Speter 11113251881Speter source_url = svn_path_url_add_component2(source_loc->url, 11114251881Speter path_rel_to_session, iterpool); 11115251881Speter target_pathrev = svn_client__pathrev_join_relpath( 11116251881Speter &target->loc, path_rel_to_session, iterpool); 11117251881Speter err = svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo, 11118251881Speter NULL /* has_rev_zero_history */, 11119251881Speter target_pathrev, 11120251881Speter target->loc.rev, 11121251881Speter SVN_INVALID_REVNUM, 11122251881Speter target_ra_session, 11123251881Speter ctx, iterpool); 11124251881Speter if (err) 11125251881Speter { 11126251881Speter if (err->apr_err == SVN_ERR_FS_NOT_FOUND 11127251881Speter || err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED) 11128251881Speter { 11129251881Speter /* This path with explicit mergeinfo in the source doesn't 11130251881Speter exist on the target. */ 11131251881Speter svn_error_clear(err); 11132251881Speter err = NULL; 11133251881Speter } 11134251881Speter else 11135251881Speter { 11136251881Speter return svn_error_trace(err); 11137251881Speter } 11138251881Speter } 11139251881Speter else 11140251881Speter { 11141251881Speter svn_client__pathrev_t *pathrev; 11142251881Speter 11143251881Speter SVN_ERR(find_youngest_merged_rev(youngest_merged_rev, 11144251881Speter target_history_as_mergeinfo, 11145251881Speter source_mergeinfo, 11146251881Speter iterpool)); 11147251881Speter 11148251881Speter /* Use scratch_pool rather than iterpool because filtered_mergeinfo 11149251881Speter is going into new_catalog below and needs to last to the end of 11150251881Speter this function. */ 11151251881Speter /* ### Why looking at SOURCE_url at TARGET_rev? */ 11152251881Speter SVN_ERR(svn_client__pathrev_create_with_session( 11153251881Speter &pathrev, source_ra_session, target->loc.rev, source_url, 11154251881Speter iterpool)); 11155251881Speter SVN_ERR(find_unmerged_mergeinfo_subroutine( 11156251881Speter &filtered_mergeinfo, target_history_as_mergeinfo, 11157251881Speter source_mergeinfo, pathrev, 11158251881Speter source_ra_session, ctx, scratch_pool, iterpool)); 11159251881Speter if (apr_hash_count(filtered_mergeinfo)) 11160251881Speter svn_hash_sets(new_catalog, 11161251881Speter apr_pstrdup(scratch_pool, source_path), 11162251881Speter filtered_mergeinfo); 11163251881Speter } 11164251881Speter } 11165251881Speter 11166251881Speter /* Limit new_catalog to the youngest revisions previously merged from 11167251881Speter the target to the source. */ 11168251881Speter if (SVN_IS_VALID_REVNUM(*youngest_merged_rev)) 11169251881Speter SVN_ERR(svn_mergeinfo__filter_catalog_by_ranges(&new_catalog, 11170251881Speter new_catalog, 11171251881Speter *youngest_merged_rev, 11172251881Speter 0, /* No oldest bound. */ 11173251881Speter TRUE, 11174251881Speter scratch_pool, 11175251881Speter scratch_pool)); 11176251881Speter 11177251881Speter /* Make a shiny new copy before blowing away all the temporary pools. */ 11178251881Speter *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(new_catalog, 11179251881Speter result_pool); 11180251881Speter svn_pool_destroy(iterpool); 11181251881Speter return SVN_NO_ERROR; 11182251881Speter} 11183251881Speter 11184251881Speter/* Helper for svn_client_merge_reintegrate() which calculates the 11185251881Speter 'left hand side' of the underlying two-URL merge that a --reintegrate 11186251881Speter merge actually performs. If no merge should be performed, set 11187251881Speter *LEFT_P to NULL. 11188251881Speter 11189251881Speter TARGET->abspath is the absolute working copy path of the reintegrate 11190251881Speter merge. 11191251881Speter 11192251881Speter SOURCE_LOC is the reintegrate source. 11193251881Speter 11194251881Speter SUBTREES_WITH_MERGEINFO is a hash of (const char *) absolute paths mapped 11195251881Speter to (svn_mergeinfo_t *) mergeinfo values for each working copy path with 11196251881Speter explicit mergeinfo in TARGET->abspath. Actually we only need to know the 11197251881Speter paths, not the mergeinfo. 11198251881Speter 11199251881Speter TARGET->loc.rev is the working revision the entire WC tree rooted at 11200251881Speter TARGET is at. 11201251881Speter 11202251881Speter Populate *UNMERGED_TO_SOURCE_CATALOG with the mergeinfo describing what 11203251881Speter parts of TARGET->loc have not been merged to SOURCE_LOC, up to the 11204251881Speter youngest revision ever merged from the TARGET->abspath to the source if 11205251881Speter such exists, see doc string for find_unmerged_mergeinfo(). 11206251881Speter 11207251881Speter SOURCE_RA_SESSION is a session opened to the SOURCE_LOC 11208251881Speter and TARGET_RA_SESSION is open to TARGET->loc.url. 11209251881Speter 11210251881Speter *LEFT_P, *MERGED_TO_SOURCE_CATALOG , and *UNMERGED_TO_SOURCE_CATALOG are 11211251881Speter allocated in RESULT_POOL. SCRATCH_POOL is used for all temporary 11212251881Speter allocations. */ 11213251881Speterstatic svn_error_t * 11214251881Spetercalculate_left_hand_side(svn_client__pathrev_t **left_p, 11215251881Speter svn_mergeinfo_catalog_t *merged_to_source_catalog, 11216251881Speter svn_mergeinfo_catalog_t *unmerged_to_source_catalog, 11217251881Speter const merge_target_t *target, 11218251881Speter apr_hash_t *subtrees_with_mergeinfo, 11219251881Speter const svn_client__pathrev_t *source_loc, 11220251881Speter svn_ra_session_t *source_ra_session, 11221251881Speter svn_ra_session_t *target_ra_session, 11222251881Speter svn_client_ctx_t *ctx, 11223251881Speter apr_pool_t *result_pool, 11224251881Speter apr_pool_t *scratch_pool) 11225251881Speter{ 11226251881Speter svn_mergeinfo_catalog_t mergeinfo_catalog, unmerged_catalog; 11227251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 11228251881Speter apr_hash_index_t *hi; 11229251881Speter /* hash of paths mapped to arrays of svn_mergeinfo_t. */ 11230251881Speter apr_hash_t *target_history_hash = apr_hash_make(scratch_pool); 11231251881Speter svn_revnum_t youngest_merged_rev; 11232251881Speter svn_client__pathrev_t *yc_ancestor; 11233251881Speter 11234251881Speter assert(session_url_is(source_ra_session, source_loc->url, scratch_pool)); 11235251881Speter assert(session_url_is(target_ra_session, target->loc.url, scratch_pool)); 11236251881Speter 11237251881Speter /* Initialize our return variables. */ 11238251881Speter *left_p = NULL; 11239251881Speter 11240251881Speter /* TARGET->abspath may not have explicit mergeinfo and thus may not be 11241251881Speter contained within SUBTREES_WITH_MERGEINFO. If this is the case then 11242251881Speter add a dummy item for TARGET->abspath so we get its history (i.e. implicit 11243251881Speter mergeinfo) below. */ 11244251881Speter if (!svn_hash_gets(subtrees_with_mergeinfo, target->abspath)) 11245251881Speter svn_hash_sets(subtrees_with_mergeinfo, target->abspath, 11246251881Speter apr_hash_make(result_pool)); 11247251881Speter 11248251881Speter /* Get the history segments (as mergeinfo) for TARGET->abspath and any of 11249251881Speter its subtrees with explicit mergeinfo. */ 11250251881Speter for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo); 11251251881Speter hi; 11252251881Speter hi = apr_hash_next(hi)) 11253251881Speter { 11254251881Speter const char *local_abspath = svn__apr_hash_index_key(hi); 11255251881Speter svn_client__pathrev_t *target_child; 11256251881Speter const char *repos_relpath; 11257251881Speter svn_mergeinfo_t target_history_as_mergeinfo; 11258251881Speter 11259251881Speter svn_pool_clear(iterpool); 11260251881Speter 11261251881Speter /* Convert the absolute path with mergeinfo on it to a path relative 11262251881Speter to the session root. */ 11263251881Speter SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, NULL, NULL, 11264251881Speter ctx->wc_ctx, local_abspath, 11265251881Speter scratch_pool, iterpool)); 11266251881Speter target_child = svn_client__pathrev_create_with_relpath( 11267251881Speter target->loc.repos_root_url, target->loc.repos_uuid, 11268251881Speter target->loc.rev, repos_relpath, iterpool); 11269251881Speter SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo, 11270251881Speter NULL /* has_rev_zero_hist */, 11271251881Speter target_child, 11272251881Speter target->loc.rev, 11273251881Speter SVN_INVALID_REVNUM, 11274251881Speter target_ra_session, 11275251881Speter ctx, scratch_pool)); 11276251881Speter 11277251881Speter svn_hash_sets(target_history_hash, repos_relpath, 11278251881Speter target_history_as_mergeinfo); 11279251881Speter } 11280251881Speter 11281251881Speter /* Check that SOURCE_LOC and TARGET->loc are 11282251881Speter actually related, we can't reintegrate if they are not. Also 11283251881Speter get an initial value for the YCA revision number. */ 11284251881Speter SVN_ERR(svn_client__get_youngest_common_ancestor( 11285251881Speter &yc_ancestor, source_loc, &target->loc, target_ra_session, ctx, 11286251881Speter iterpool, iterpool)); 11287251881Speter if (! yc_ancestor) 11288251881Speter return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, 11289251881Speter _("'%s@%ld' must be ancestrally related to " 11290251881Speter "'%s@%ld'"), source_loc->url, source_loc->rev, 11291251881Speter target->loc.url, target->loc.rev); 11292251881Speter 11293251881Speter /* If the source revision is the same as the youngest common 11294251881Speter revision, then there can't possibly be any unmerged revisions 11295251881Speter that we need to apply to target. */ 11296251881Speter if (source_loc->rev == yc_ancestor->rev) 11297251881Speter { 11298251881Speter svn_pool_destroy(iterpool); 11299251881Speter return SVN_NO_ERROR; 11300251881Speter } 11301251881Speter 11302251881Speter /* Get the mergeinfo from the source, including its descendants 11303251881Speter with differing explicit mergeinfo. */ 11304251881Speter SVN_ERR(svn_client__get_repos_mergeinfo_catalog( 11305251881Speter &mergeinfo_catalog, source_ra_session, 11306251881Speter source_loc->url, source_loc->rev, 11307251881Speter svn_mergeinfo_inherited, FALSE /* squelch_incapable */, 11308251881Speter TRUE /* include_descendants */, iterpool, iterpool)); 11309251881Speter 11310251881Speter if (!mergeinfo_catalog) 11311251881Speter mergeinfo_catalog = apr_hash_make(iterpool); 11312251881Speter 11313251881Speter *merged_to_source_catalog = svn_mergeinfo_catalog_dup(mergeinfo_catalog, 11314251881Speter result_pool); 11315251881Speter 11316251881Speter /* Filter the source's mergeinfo catalog so that we are left with 11317251881Speter mergeinfo that describes what has *not* previously been merged from 11318251881Speter TARGET->loc to SOURCE_LOC. */ 11319251881Speter SVN_ERR(find_unmerged_mergeinfo(&unmerged_catalog, 11320251881Speter &youngest_merged_rev, 11321251881Speter yc_ancestor->rev, 11322251881Speter mergeinfo_catalog, 11323251881Speter target_history_hash, 11324251881Speter source_loc, 11325251881Speter target, 11326251881Speter source_ra_session, 11327251881Speter target_ra_session, 11328251881Speter ctx, 11329251881Speter iterpool, iterpool)); 11330251881Speter 11331251881Speter /* Simplify unmerged_catalog through elision then make a copy in POOL. */ 11332251881Speter SVN_ERR(svn_client__elide_mergeinfo_catalog(unmerged_catalog, 11333251881Speter iterpool)); 11334251881Speter *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(unmerged_catalog, 11335251881Speter result_pool); 11336251881Speter 11337251881Speter if (youngest_merged_rev == SVN_INVALID_REVNUM) 11338251881Speter { 11339251881Speter /* We never merged to the source. Just return the branch point. */ 11340251881Speter *left_p = svn_client__pathrev_dup(yc_ancestor, result_pool); 11341251881Speter } 11342251881Speter else 11343251881Speter { 11344251881Speter /* We've previously merged some or all of the target, up to 11345251881Speter youngest_merged_rev, to the source. Set 11346251881Speter *LEFT_P to cover the youngest part of this range. */ 11347251881Speter SVN_ERR(svn_client__repos_location(left_p, target_ra_session, 11348251881Speter &target->loc, youngest_merged_rev, 11349251881Speter ctx, result_pool, iterpool)); 11350251881Speter } 11351251881Speter 11352251881Speter svn_pool_destroy(iterpool); 11353251881Speter return SVN_NO_ERROR; 11354251881Speter} 11355251881Speter 11356251881Speter/* Determine the URLs and revisions needed to perform a reintegrate merge 11357251881Speter * from SOURCE_LOC into the working copy at TARGET. 11358251881Speter * 11359251881Speter * SOURCE_RA_SESSION and TARGET_RA_SESSION are RA sessions opened to the 11360251881Speter * URLs of SOURCE_LOC and TARGET->loc respectively. 11361251881Speter * 11362251881Speter * Set *SOURCE_P to 11363251881Speter * the source-left and source-right locations of the required merge. Set 11364251881Speter * *YC_ANCESTOR_P to the location of the youngest ancestor. 11365251881Speter * Any of these output pointers may be NULL if not wanted. 11366251881Speter * 11367251881Speter * See svn_client_find_reintegrate_merge() for other details. 11368251881Speter */ 11369251881Speterstatic svn_error_t * 11370251881Speterfind_reintegrate_merge(merge_source_t **source_p, 11371251881Speter svn_client__pathrev_t **yc_ancestor_p, 11372251881Speter svn_ra_session_t *source_ra_session, 11373251881Speter const svn_client__pathrev_t *source_loc, 11374251881Speter svn_ra_session_t *target_ra_session, 11375251881Speter const merge_target_t *target, 11376251881Speter svn_client_ctx_t *ctx, 11377251881Speter apr_pool_t *result_pool, 11378251881Speter apr_pool_t *scratch_pool) 11379251881Speter{ 11380251881Speter svn_client__pathrev_t *yc_ancestor; 11381251881Speter svn_client__pathrev_t *loc1; 11382251881Speter merge_source_t source; 11383251881Speter svn_mergeinfo_catalog_t unmerged_to_source_mergeinfo_catalog; 11384251881Speter svn_mergeinfo_catalog_t merged_to_source_mergeinfo_catalog; 11385251881Speter svn_error_t *err; 11386251881Speter apr_hash_t *subtrees_with_mergeinfo; 11387251881Speter 11388251881Speter assert(session_url_is(source_ra_session, source_loc->url, scratch_pool)); 11389251881Speter assert(session_url_is(target_ra_session, target->loc.url, scratch_pool)); 11390251881Speter 11391251881Speter /* As the WC tree is "pure", use its last-updated-to revision as 11392251881Speter the default revision for the left side of our merge, since that's 11393251881Speter what the repository sub-tree is required to be up to date with 11394251881Speter (with regard to the WC). */ 11395251881Speter /* ### Bogus/obsolete comment? */ 11396251881Speter 11397251881Speter /* Can't reintegrate to or from the root of the repository. */ 11398251881Speter if (strcmp(source_loc->url, source_loc->repos_root_url) == 0 11399251881Speter || strcmp(target->loc.url, target->loc.repos_root_url) == 0) 11400251881Speter return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, 11401251881Speter _("Neither the reintegrate source nor target " 11402251881Speter "can be the root of the repository")); 11403251881Speter 11404251881Speter /* Find all the subtrees in TARGET_WCPATH that have explicit mergeinfo. */ 11405251881Speter err = get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo, 11406251881Speter target->abspath, svn_depth_infinity, 11407251881Speter ctx, scratch_pool, scratch_pool); 11408251881Speter /* Issue #3896: If invalid mergeinfo in the reintegrate target 11409251881Speter prevents us from proceeding, then raise the best error possible. */ 11410251881Speter if (err && err->apr_err == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING) 11411251881Speter err = svn_error_quick_wrap(err, _("Reintegrate merge not possible")); 11412251881Speter SVN_ERR(err); 11413251881Speter 11414251881Speter SVN_ERR(calculate_left_hand_side(&loc1, 11415251881Speter &merged_to_source_mergeinfo_catalog, 11416251881Speter &unmerged_to_source_mergeinfo_catalog, 11417251881Speter target, 11418251881Speter subtrees_with_mergeinfo, 11419251881Speter source_loc, 11420251881Speter source_ra_session, 11421251881Speter target_ra_session, 11422251881Speter ctx, 11423251881Speter scratch_pool, scratch_pool)); 11424251881Speter 11425251881Speter /* Did calculate_left_hand_side() decide that there was no merge to 11426251881Speter be performed here? */ 11427251881Speter if (! loc1) 11428251881Speter { 11429251881Speter if (source_p) 11430251881Speter *source_p = NULL; 11431251881Speter if (yc_ancestor_p) 11432251881Speter *yc_ancestor_p = NULL; 11433251881Speter return SVN_NO_ERROR; 11434251881Speter } 11435251881Speter 11436251881Speter source.loc1 = loc1; 11437251881Speter source.loc2 = source_loc; 11438251881Speter 11439251881Speter /* If the target was moved after the source was branched from it, 11440251881Speter it is possible that the left URL differs from the target's current 11441251881Speter URL. If so, then adjust TARGET_RA_SESSION to point to the old URL. */ 11442251881Speter if (strcmp(source.loc1->url, target->loc.url)) 11443251881Speter SVN_ERR(svn_ra_reparent(target_ra_session, source.loc1->url, scratch_pool)); 11444251881Speter 11445251881Speter SVN_ERR(svn_client__get_youngest_common_ancestor( 11446251881Speter &yc_ancestor, source.loc2, source.loc1, target_ra_session, 11447251881Speter ctx, scratch_pool, scratch_pool)); 11448251881Speter 11449251881Speter if (! yc_ancestor) 11450251881Speter return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, 11451251881Speter _("'%s@%ld' must be ancestrally related to " 11452251881Speter "'%s@%ld'"), 11453251881Speter source.loc1->url, source.loc1->rev, 11454251881Speter source.loc2->url, source.loc2->rev); 11455251881Speter 11456251881Speter /* The source side of a reintegrate merge is not 'ancestral', except in 11457251881Speter * the degenerate case where source == YCA. */ 11458251881Speter source.ancestral = (loc1->rev == yc_ancestor->rev); 11459251881Speter 11460251881Speter if (source.loc1->rev > yc_ancestor->rev) 11461251881Speter { 11462251881Speter /* Have we actually merged anything to the source from the 11463251881Speter target? If so, make sure we've merged a contiguous 11464251881Speter prefix. */ 11465251881Speter svn_mergeinfo_catalog_t final_unmerged_catalog = apr_hash_make(scratch_pool); 11466251881Speter 11467251881Speter SVN_ERR(find_unsynced_ranges(source_loc, yc_ancestor, 11468251881Speter unmerged_to_source_mergeinfo_catalog, 11469251881Speter merged_to_source_mergeinfo_catalog, 11470251881Speter final_unmerged_catalog, 11471251881Speter target_ra_session, scratch_pool, 11472251881Speter scratch_pool)); 11473251881Speter 11474251881Speter if (apr_hash_count(final_unmerged_catalog)) 11475251881Speter { 11476251881Speter svn_string_t *source_mergeinfo_cat_string; 11477251881Speter 11478251881Speter SVN_ERR(svn_mergeinfo__catalog_to_formatted_string( 11479251881Speter &source_mergeinfo_cat_string, 11480251881Speter final_unmerged_catalog, 11481251881Speter " ", " Missing ranges: ", scratch_pool)); 11482251881Speter return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, 11483251881Speter NULL, 11484251881Speter _("Reintegrate can only be used if " 11485251881Speter "revisions %ld through %ld were " 11486251881Speter "previously merged from %s to the " 11487251881Speter "reintegrate source, but this is " 11488251881Speter "not the case:\n%s"), 11489251881Speter yc_ancestor->rev + 1, source.loc2->rev, 11490251881Speter target->loc.url, 11491251881Speter source_mergeinfo_cat_string->data); 11492251881Speter } 11493251881Speter } 11494251881Speter 11495251881Speter /* Left side: trunk@youngest-trunk-rev-merged-to-branch-at-specified-peg-rev 11496251881Speter * Right side: branch@specified-peg-revision */ 11497251881Speter if (source_p) 11498251881Speter *source_p = merge_source_dup(&source, result_pool); 11499251881Speter 11500251881Speter if (yc_ancestor_p) 11501251881Speter *yc_ancestor_p = svn_client__pathrev_dup(yc_ancestor, result_pool); 11502251881Speter return SVN_NO_ERROR; 11503251881Speter} 11504251881Speter 11505251881Speter/* Resolve the source and target locations and open RA sessions to them, and 11506251881Speter * perform some checks appropriate for a reintegrate merge. 11507251881Speter * 11508251881Speter * Set *SOURCE_RA_SESSION_P and *SOURCE_LOC_P to a new session and the 11509251881Speter * repository location of SOURCE_PATH_OR_URL at SOURCE_PEG_REVISION. Set 11510251881Speter * *TARGET_RA_SESSION_P and *TARGET_P to a new session and the repository 11511251881Speter * location of the WC at TARGET_ABSPATH. 11512251881Speter * 11513251881Speter * Throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES error if the target WC node is 11514251881Speter * a locally added node or if the source and target are not in the same 11515251881Speter * repository. Throw a SVN_ERR_CLIENT_NOT_READY_TO_MERGE error if the 11516251881Speter * target WC is not at a single revision without switched subtrees and 11517251881Speter * without local mods. 11518251881Speter * 11519251881Speter * Allocate all the outputs in RESULT_POOL. 11520251881Speter */ 11521251881Speterstatic svn_error_t * 11522251881Speteropen_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p, 11523251881Speter svn_client__pathrev_t **source_loc_p, 11524251881Speter svn_ra_session_t **target_ra_session_p, 11525251881Speter merge_target_t **target_p, 11526251881Speter const char *source_path_or_url, 11527251881Speter const svn_opt_revision_t *source_peg_revision, 11528251881Speter const char *target_abspath, 11529251881Speter svn_client_ctx_t *ctx, 11530251881Speter apr_pool_t *result_pool, 11531251881Speter apr_pool_t *scratch_pool) 11532251881Speter{ 11533251881Speter svn_client__pathrev_t *source_loc; 11534251881Speter merge_target_t *target; 11535251881Speter 11536251881Speter /* Open the target WC. A reintegrate merge requires the merge target to 11537251881Speter * reflect a subtree of the repository as found at a single revision. */ 11538251881Speter SVN_ERR(open_target_wc(&target, target_abspath, 11539251881Speter FALSE, FALSE, FALSE, 11540251881Speter ctx, scratch_pool, scratch_pool)); 11541251881Speter SVN_ERR(svn_client_open_ra_session2(target_ra_session_p, 11542251881Speter target->loc.url, target->abspath, 11543251881Speter ctx, result_pool, scratch_pool)); 11544251881Speter if (! target->loc.url) 11545251881Speter return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL, 11546251881Speter _("Can't reintegrate into '%s' because it is " 11547251881Speter "locally added and therefore not related to " 11548251881Speter "the merge source"), 11549251881Speter svn_dirent_local_style(target->abspath, 11550251881Speter scratch_pool)); 11551251881Speter 11552251881Speter SVN_ERR(svn_client__ra_session_from_path2( 11553251881Speter source_ra_session_p, &source_loc, 11554251881Speter source_path_or_url, NULL, source_peg_revision, source_peg_revision, 11555251881Speter ctx, result_pool)); 11556251881Speter 11557251881Speter /* source_loc and target->loc are required to be in the same repository, 11558251881Speter as mergeinfo doesn't come into play for cross-repository merging. */ 11559251881Speter SVN_ERR(check_same_repos(source_loc, 11560251881Speter svn_dirent_local_style(source_path_or_url, 11561251881Speter scratch_pool), 11562251881Speter &target->loc, 11563251881Speter svn_dirent_local_style(target->abspath, 11564251881Speter scratch_pool), 11565251881Speter TRUE /* strict_urls */, scratch_pool)); 11566251881Speter 11567251881Speter *source_loc_p = source_loc; 11568251881Speter *target_p = target; 11569251881Speter return SVN_NO_ERROR; 11570251881Speter} 11571251881Speter 11572251881Speter/* The body of svn_client_merge_reintegrate(), which see for details. */ 11573251881Speterstatic svn_error_t * 11574251881Spetermerge_reintegrate_locked(conflict_report_t **conflict_report, 11575251881Speter const char *source_path_or_url, 11576251881Speter const svn_opt_revision_t *source_peg_revision, 11577251881Speter const char *target_abspath, 11578251881Speter svn_boolean_t diff_ignore_ancestry, 11579251881Speter svn_boolean_t dry_run, 11580251881Speter const apr_array_header_t *merge_options, 11581251881Speter svn_client_ctx_t *ctx, 11582251881Speter apr_pool_t *result_pool, 11583251881Speter apr_pool_t *scratch_pool) 11584251881Speter{ 11585251881Speter svn_ra_session_t *target_ra_session, *source_ra_session; 11586251881Speter merge_target_t *target; 11587251881Speter svn_client__pathrev_t *source_loc; 11588251881Speter merge_source_t *source; 11589251881Speter svn_client__pathrev_t *yc_ancestor; 11590251881Speter svn_boolean_t use_sleep = FALSE; 11591251881Speter svn_error_t *err; 11592251881Speter 11593251881Speter SVN_ERR(open_reintegrate_source_and_target( 11594251881Speter &source_ra_session, &source_loc, &target_ra_session, &target, 11595251881Speter source_path_or_url, source_peg_revision, target_abspath, 11596251881Speter ctx, scratch_pool, scratch_pool)); 11597251881Speter 11598251881Speter SVN_ERR(find_reintegrate_merge(&source, &yc_ancestor, 11599251881Speter source_ra_session, source_loc, 11600251881Speter target_ra_session, target, 11601251881Speter ctx, scratch_pool, scratch_pool)); 11602251881Speter 11603251881Speter if (! source) 11604251881Speter { 11605251881Speter return SVN_NO_ERROR; 11606251881Speter } 11607251881Speter 11608251881Speter /* Do the real merge! */ 11609251881Speter /* ### TODO(reint): Make sure that one isn't the same line ancestor 11610251881Speter ### of the other (what's erroneously referred to as "ancestrally 11611251881Speter ### related" in this source file). For now, we just say the source 11612251881Speter ### isn't "ancestral" even if it is (in the degenerate case where 11613251881Speter ### source-left equals YCA). */ 11614251881Speter source->ancestral = FALSE; 11615251881Speter err = merge_cousins_and_supplement_mergeinfo(conflict_report, 11616251881Speter &use_sleep, 11617251881Speter target, 11618251881Speter target_ra_session, 11619251881Speter source_ra_session, 11620251881Speter source, yc_ancestor, 11621251881Speter TRUE /* same_repos */, 11622251881Speter svn_depth_infinity, 11623251881Speter diff_ignore_ancestry, 11624251881Speter FALSE /* force_delete */, 11625251881Speter FALSE /* record_only */, 11626251881Speter dry_run, 11627251881Speter merge_options, 11628251881Speter ctx, 11629251881Speter result_pool, scratch_pool); 11630251881Speter 11631251881Speter if (use_sleep) 11632251881Speter svn_io_sleep_for_timestamps(target_abspath, scratch_pool); 11633251881Speter 11634251881Speter SVN_ERR(err); 11635251881Speter return SVN_NO_ERROR; 11636251881Speter} 11637251881Speter 11638251881Spetersvn_error_t * 11639251881Spetersvn_client_merge_reintegrate(const char *source_path_or_url, 11640251881Speter const svn_opt_revision_t *source_peg_revision, 11641251881Speter const char *target_wcpath, 11642251881Speter svn_boolean_t dry_run, 11643251881Speter const apr_array_header_t *merge_options, 11644251881Speter svn_client_ctx_t *ctx, 11645251881Speter apr_pool_t *pool) 11646251881Speter{ 11647251881Speter const char *target_abspath, *lock_abspath; 11648251881Speter conflict_report_t *conflict_report; 11649251881Speter 11650251881Speter SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath, 11651251881Speter target_wcpath, ctx, pool)); 11652251881Speter 11653251881Speter if (!dry_run) 11654251881Speter SVN_WC__CALL_WITH_WRITE_LOCK( 11655251881Speter merge_reintegrate_locked(&conflict_report, 11656251881Speter source_path_or_url, source_peg_revision, 11657251881Speter target_abspath, 11658251881Speter FALSE /*diff_ignore_ancestry*/, 11659251881Speter dry_run, merge_options, ctx, pool, pool), 11660251881Speter ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool); 11661251881Speter else 11662251881Speter SVN_ERR(merge_reintegrate_locked(&conflict_report, 11663251881Speter source_path_or_url, source_peg_revision, 11664251881Speter target_abspath, 11665251881Speter FALSE /*diff_ignore_ancestry*/, 11666251881Speter dry_run, merge_options, ctx, pool, pool)); 11667251881Speter 11668251881Speter SVN_ERR(make_merge_conflict_error(conflict_report, pool)); 11669251881Speter return SVN_NO_ERROR; 11670251881Speter} 11671251881Speter 11672251881Speter 11673251881Speter/* The body of svn_client_merge_peg5(), which see for details. 11674251881Speter * 11675251881Speter * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge(). 11676251881Speter */ 11677251881Speterstatic svn_error_t * 11678251881Spetermerge_peg_locked(conflict_report_t **conflict_report, 11679251881Speter const char *source_path_or_url, 11680251881Speter const svn_opt_revision_t *source_peg_revision, 11681251881Speter const svn_rangelist_t *ranges_to_merge, 11682251881Speter const char *target_abspath, 11683251881Speter svn_depth_t depth, 11684251881Speter svn_boolean_t ignore_mergeinfo, 11685251881Speter svn_boolean_t diff_ignore_ancestry, 11686251881Speter svn_boolean_t force_delete, 11687251881Speter svn_boolean_t record_only, 11688251881Speter svn_boolean_t dry_run, 11689251881Speter svn_boolean_t allow_mixed_rev, 11690251881Speter const apr_array_header_t *merge_options, 11691251881Speter svn_client_ctx_t *ctx, 11692251881Speter apr_pool_t *result_pool, 11693251881Speter apr_pool_t *scratch_pool) 11694251881Speter{ 11695251881Speter merge_target_t *target; 11696251881Speter svn_client__pathrev_t *source_loc; 11697251881Speter apr_array_header_t *merge_sources; 11698251881Speter svn_ra_session_t *ra_session; 11699251881Speter apr_pool_t *sesspool; 11700251881Speter svn_boolean_t use_sleep = FALSE; 11701251881Speter svn_error_t *err; 11702251881Speter svn_boolean_t same_repos; 11703251881Speter 11704251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath)); 11705251881Speter 11706251881Speter SVN_ERR(open_target_wc(&target, target_abspath, 11707251881Speter allow_mixed_rev, TRUE, TRUE, 11708251881Speter ctx, scratch_pool, scratch_pool)); 11709251881Speter 11710251881Speter /* Create a short lived session pool */ 11711251881Speter sesspool = svn_pool_create(scratch_pool); 11712251881Speter 11713251881Speter /* Open an RA session to our source URL, and determine its root URL. */ 11714251881Speter SVN_ERR(svn_client__ra_session_from_path2( 11715251881Speter &ra_session, &source_loc, 11716251881Speter source_path_or_url, NULL, source_peg_revision, source_peg_revision, 11717251881Speter ctx, sesspool)); 11718251881Speter 11719251881Speter /* Normalize our merge sources. */ 11720251881Speter SVN_ERR(normalize_merge_sources(&merge_sources, source_path_or_url, 11721251881Speter source_loc, 11722251881Speter ranges_to_merge, ra_session, ctx, 11723251881Speter scratch_pool, scratch_pool)); 11724251881Speter 11725251881Speter /* Check for same_repos. */ 11726251881Speter same_repos = is_same_repos(&target->loc, source_loc, TRUE /* strict_urls */); 11727251881Speter 11728251881Speter /* Do the real merge! (We say with confidence that our merge 11729251881Speter sources are both ancestral and related.) */ 11730251881Speter err = do_merge(NULL, NULL, conflict_report, &use_sleep, 11731251881Speter merge_sources, target, ra_session, 11732251881Speter TRUE /*sources_related*/, same_repos, ignore_mergeinfo, 11733251881Speter diff_ignore_ancestry, force_delete, dry_run, 11734251881Speter record_only, NULL, FALSE, FALSE, depth, merge_options, 11735251881Speter ctx, result_pool, scratch_pool); 11736251881Speter 11737251881Speter /* We're done with our RA session. */ 11738251881Speter svn_pool_destroy(sesspool); 11739251881Speter 11740251881Speter if (use_sleep) 11741251881Speter svn_io_sleep_for_timestamps(target_abspath, scratch_pool); 11742251881Speter 11743251881Speter SVN_ERR(err); 11744251881Speter return SVN_NO_ERROR; 11745251881Speter} 11746251881Speter 11747251881Speter/* Details of an automatic merge. */ 11748251881Spetertypedef struct automatic_merge_t 11749251881Speter{ 11750251881Speter svn_client__pathrev_t *yca, *base, *right, *target; 11751251881Speter svn_boolean_t is_reintegrate_like; 11752251881Speter svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees; 11753251881Speter} automatic_merge_t; 11754251881Speter 11755251881Speterstatic svn_error_t * 11756251881Speterclient_find_automatic_merge(automatic_merge_t **merge_p, 11757251881Speter const char *source_path_or_url, 11758251881Speter const svn_opt_revision_t *source_revision, 11759251881Speter const char *target_abspath, 11760251881Speter svn_boolean_t allow_mixed_rev, 11761251881Speter svn_boolean_t allow_local_mods, 11762251881Speter svn_boolean_t allow_switched_subtrees, 11763251881Speter svn_client_ctx_t *ctx, 11764251881Speter apr_pool_t *result_pool, 11765251881Speter apr_pool_t *scratch_pool); 11766251881Speter 11767251881Speterstatic svn_error_t * 11768251881Speterdo_automatic_merge_locked(conflict_report_t **conflict_report, 11769251881Speter const automatic_merge_t *merge, 11770251881Speter const char *target_abspath, 11771251881Speter svn_depth_t depth, 11772251881Speter svn_boolean_t diff_ignore_ancestry, 11773251881Speter svn_boolean_t force_delete, 11774251881Speter svn_boolean_t record_only, 11775251881Speter svn_boolean_t dry_run, 11776251881Speter const apr_array_header_t *merge_options, 11777251881Speter svn_client_ctx_t *ctx, 11778251881Speter apr_pool_t *result_pool, 11779251881Speter apr_pool_t *scratch_pool); 11780251881Speter 11781251881Spetersvn_error_t * 11782251881Spetersvn_client_merge_peg5(const char *source_path_or_url, 11783251881Speter const apr_array_header_t *ranges_to_merge, 11784251881Speter const svn_opt_revision_t *source_peg_revision, 11785251881Speter const char *target_wcpath, 11786251881Speter svn_depth_t depth, 11787251881Speter svn_boolean_t ignore_mergeinfo, 11788251881Speter svn_boolean_t diff_ignore_ancestry, 11789251881Speter svn_boolean_t force_delete, 11790251881Speter svn_boolean_t record_only, 11791251881Speter svn_boolean_t dry_run, 11792251881Speter svn_boolean_t allow_mixed_rev, 11793251881Speter const apr_array_header_t *merge_options, 11794251881Speter svn_client_ctx_t *ctx, 11795251881Speter apr_pool_t *pool) 11796251881Speter{ 11797251881Speter const char *target_abspath, *lock_abspath; 11798251881Speter conflict_report_t *conflict_report; 11799251881Speter 11800251881Speter /* No ranges to merge? No problem. */ 11801251881Speter if (ranges_to_merge != NULL && ranges_to_merge->nelts == 0) 11802251881Speter return SVN_NO_ERROR; 11803251881Speter 11804251881Speter SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath, 11805251881Speter target_wcpath, ctx, pool)); 11806251881Speter 11807251881Speter /* Do an automatic merge if no revision ranges are specified. */ 11808251881Speter if (ranges_to_merge == NULL) 11809251881Speter { 11810251881Speter automatic_merge_t *merge; 11811251881Speter 11812251881Speter if (ignore_mergeinfo) 11813251881Speter return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 11814251881Speter _("Cannot merge automatically while " 11815251881Speter "ignoring mergeinfo")); 11816251881Speter 11817251881Speter /* Find the details of the merge needed. */ 11818251881Speter SVN_ERR(client_find_automatic_merge( 11819251881Speter &merge, 11820251881Speter source_path_or_url, source_peg_revision, 11821251881Speter target_abspath, 11822251881Speter allow_mixed_rev, 11823251881Speter TRUE /*allow_local_mods*/, 11824251881Speter TRUE /*allow_switched_subtrees*/, 11825251881Speter ctx, pool, pool)); 11826251881Speter 11827251881Speter if (!dry_run) 11828251881Speter SVN_WC__CALL_WITH_WRITE_LOCK( 11829251881Speter do_automatic_merge_locked(&conflict_report, 11830251881Speter merge, 11831251881Speter target_abspath, depth, 11832251881Speter diff_ignore_ancestry, 11833251881Speter force_delete, record_only, dry_run, 11834251881Speter merge_options, ctx, pool, pool), 11835251881Speter ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool); 11836251881Speter else 11837251881Speter SVN_ERR(do_automatic_merge_locked(&conflict_report, 11838251881Speter merge, 11839251881Speter target_abspath, depth, 11840251881Speter diff_ignore_ancestry, 11841251881Speter force_delete, record_only, dry_run, 11842251881Speter merge_options, ctx, pool, pool)); 11843251881Speter } 11844251881Speter else if (!dry_run) 11845251881Speter SVN_WC__CALL_WITH_WRITE_LOCK( 11846251881Speter merge_peg_locked(&conflict_report, 11847251881Speter source_path_or_url, source_peg_revision, 11848251881Speter ranges_to_merge, 11849251881Speter target_abspath, depth, ignore_mergeinfo, 11850251881Speter diff_ignore_ancestry, 11851251881Speter force_delete, record_only, dry_run, 11852251881Speter allow_mixed_rev, merge_options, ctx, pool, pool), 11853251881Speter ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool); 11854251881Speter else 11855251881Speter SVN_ERR(merge_peg_locked(&conflict_report, 11856251881Speter source_path_or_url, source_peg_revision, 11857251881Speter ranges_to_merge, 11858251881Speter target_abspath, depth, ignore_mergeinfo, 11859251881Speter diff_ignore_ancestry, 11860251881Speter force_delete, record_only, dry_run, 11861251881Speter allow_mixed_rev, merge_options, ctx, pool, pool)); 11862251881Speter 11863251881Speter SVN_ERR(make_merge_conflict_error(conflict_report, pool)); 11864251881Speter return SVN_NO_ERROR; 11865251881Speter} 11866251881Speter 11867251881Speter 11868251881Speter/* The location-history of a branch. 11869251881Speter * 11870251881Speter * This structure holds the set of path-revisions occupied by a branch, 11871251881Speter * from an externally chosen 'tip' location back to its origin. The 11872251881Speter * 'tip' location is the youngest location that we are considering on 11873251881Speter * the branch. */ 11874251881Spetertypedef struct branch_history_t 11875251881Speter{ 11876251881Speter /* The tip location of the branch. That is, the youngest location that's 11877251881Speter * in the repository and that we're considering. If we're considering a 11878251881Speter * target branch right up to an uncommitted WC, then this is the WC base 11879251881Speter * (pristine) location. */ 11880251881Speter svn_client__pathrev_t *tip; 11881251881Speter /* The location-segment history, as mergeinfo. */ 11882251881Speter svn_mergeinfo_t history; 11883251881Speter /* Whether the location-segment history reached as far as (necessarily 11884251881Speter the root path in) revision 0 -- a fact that can't be represented as 11885251881Speter mergeinfo. */ 11886251881Speter svn_boolean_t has_r0_history; 11887251881Speter} branch_history_t; 11888251881Speter 11889251881Speter/* Return the location on BRANCH_HISTORY at revision REV, or NULL if none. */ 11890251881Speterstatic svn_client__pathrev_t * 11891251881Speterlocation_on_branch_at_rev(const branch_history_t *branch_history, 11892251881Speter svn_revnum_t rev, 11893251881Speter apr_pool_t *result_pool, 11894251881Speter apr_pool_t *scratch_pool) 11895251881Speter{ 11896251881Speter apr_hash_index_t *hi; 11897251881Speter 11898251881Speter for (hi = apr_hash_first(scratch_pool, branch_history->history); hi; 11899251881Speter hi = apr_hash_next(hi)) 11900251881Speter { 11901251881Speter const char *fspath = svn__apr_hash_index_key(hi); 11902251881Speter svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 11903251881Speter int i; 11904251881Speter 11905251881Speter for (i = 0; i < rangelist->nelts; i++) 11906251881Speter { 11907251881Speter svn_merge_range_t *r = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 11908251881Speter if (r->start < rev && rev <= r->end) 11909251881Speter { 11910251881Speter return svn_client__pathrev_create_with_relpath( 11911251881Speter branch_history->tip->repos_root_url, 11912251881Speter branch_history->tip->repos_uuid, 11913251881Speter rev, fspath + 1, result_pool); 11914251881Speter } 11915251881Speter } 11916251881Speter } 11917251881Speter return NULL; 11918251881Speter} 11919251881Speter 11920251881Speter/* */ 11921251881Spetertypedef struct source_and_target_t 11922251881Speter{ 11923251881Speter svn_client__pathrev_t *source; 11924251881Speter svn_ra_session_t *source_ra_session; 11925251881Speter branch_history_t source_branch; 11926251881Speter 11927251881Speter merge_target_t *target; 11928251881Speter svn_ra_session_t *target_ra_session; 11929251881Speter branch_history_t target_branch; 11930251881Speter 11931251881Speter /* Repos location of the youngest common ancestor of SOURCE and TARGET. */ 11932251881Speter svn_client__pathrev_t *yca; 11933251881Speter} source_and_target_t; 11934251881Speter 11935251881Speter/* Set *INTERSECTION_P to the intersection of BRANCH_HISTORY with the 11936251881Speter * revision range OLDEST_REV to YOUNGEST_REV (inclusive). 11937251881Speter * 11938251881Speter * If the intersection is empty, the result will be a branch history object 11939251881Speter * containing an empty (not null) history. 11940251881Speter * 11941251881Speter * ### The 'tip' of the result is currently unchanged. 11942251881Speter */ 11943251881Speterstatic svn_error_t * 11944251881Speterbranch_history_intersect_range(branch_history_t **intersection_p, 11945251881Speter const branch_history_t *branch_history, 11946251881Speter svn_revnum_t oldest_rev, 11947251881Speter svn_revnum_t youngest_rev, 11948251881Speter apr_pool_t *result_pool, 11949251881Speter apr_pool_t *scratch_pool) 11950251881Speter{ 11951251881Speter branch_history_t *result = apr_palloc(result_pool, sizeof(*result)); 11952251881Speter 11953251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev)); 11954251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); 11955251881Speter SVN_ERR_ASSERT(oldest_rev >= 1); 11956251881Speter /* Allow a just-empty range (oldest = youngest + 1) but not an 11957251881Speter * arbitrary reverse range (such as oldest = youngest + 2). */ 11958251881Speter SVN_ERR_ASSERT(oldest_rev <= youngest_rev + 1); 11959251881Speter 11960251881Speter if (oldest_rev <= youngest_rev) 11961251881Speter { 11962251881Speter SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges( 11963251881Speter &result->history, branch_history->history, 11964251881Speter youngest_rev, oldest_rev - 1, TRUE /* include_range */, 11965251881Speter result_pool, scratch_pool)); 11966251881Speter result->history = svn_mergeinfo_dup(result->history, result_pool); 11967251881Speter } 11968251881Speter else 11969251881Speter { 11970251881Speter result->history = apr_hash_make(result_pool); 11971251881Speter } 11972251881Speter result->has_r0_history = FALSE; 11973251881Speter 11974251881Speter /* ### TODO: Set RESULT->tip to the tip of the intersection. */ 11975251881Speter result->tip = svn_client__pathrev_dup(branch_history->tip, result_pool); 11976251881Speter 11977251881Speter *intersection_p = result; 11978251881Speter return SVN_NO_ERROR; 11979251881Speter} 11980251881Speter 11981251881Speter/* Set *OLDEST_P and *YOUNGEST_P to the oldest and youngest locations 11982251881Speter * (inclusive) along BRANCH. OLDEST_P and/or YOUNGEST_P may be NULL if not 11983251881Speter * wanted. 11984251881Speter */ 11985251881Speterstatic svn_error_t * 11986251881Speterbranch_history_get_endpoints(svn_client__pathrev_t **oldest_p, 11987251881Speter svn_client__pathrev_t **youngest_p, 11988251881Speter const branch_history_t *branch, 11989251881Speter apr_pool_t *result_pool, 11990251881Speter apr_pool_t *scratch_pool) 11991251881Speter{ 11992251881Speter svn_revnum_t youngest_rev, oldest_rev; 11993251881Speter 11994251881Speter SVN_ERR(svn_mergeinfo__get_range_endpoints( 11995251881Speter &youngest_rev, &oldest_rev, 11996251881Speter branch->history, scratch_pool)); 11997251881Speter if (oldest_p) 11998251881Speter *oldest_p = location_on_branch_at_rev( 11999251881Speter branch, oldest_rev + 1, result_pool, scratch_pool); 12000251881Speter if (youngest_p) 12001251881Speter *youngest_p = location_on_branch_at_rev( 12002251881Speter branch, youngest_rev, result_pool, scratch_pool); 12003251881Speter return SVN_NO_ERROR; 12004251881Speter} 12005251881Speter 12006251881Speter/* Implements the svn_log_entry_receiver_t interface. 12007251881Speter 12008251881Speter Set *BATON to LOG_ENTRY->revision and return SVN_ERR_CEASE_INVOCATION. */ 12009251881Speterstatic svn_error_t * 12010251881Speteroperative_rev_receiver(void *baton, 12011251881Speter svn_log_entry_t *log_entry, 12012251881Speter apr_pool_t *pool) 12013251881Speter{ 12014251881Speter svn_revnum_t *operative_rev = baton; 12015251881Speter 12016251881Speter *operative_rev = log_entry->revision; 12017251881Speter 12018251881Speter /* We've found the youngest merged or oldest eligible revision, so 12019251881Speter we're done... 12020251881Speter 12021251881Speter ...but wait, shouldn't we care if LOG_ENTRY->NON_INHERITABLE is 12022251881Speter true? Because if it is, then LOG_ENTRY->REVISION is only 12023251881Speter partially merged/elgibile! And our only caller, 12024251881Speter find_last_merged_location (via short_circuit_mergeinfo_log) is 12025251881Speter interested in *fully* merged revisions. That's all true, but if 12026251881Speter find_last_merged_location() finds the youngest merged revision it 12027251881Speter will also check for the oldest eligible revision. So in the case 12028251881Speter the youngest merged rev is non-inheritable, the *same* non-inheritable 12029251881Speter rev will be found as the oldest eligible rev -- and 12030251881Speter find_last_merged_location() handles that situation. */ 12031251881Speter return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL); 12032251881Speter} 12033251881Speter 12034253734Speter/* Wrapper around svn_client__mergeinfo_log. All arguments are as per 12035253734Speter that private API. The discover_changed_paths, depth, and revprops args to 12036253734Speter svn_client__mergeinfo_log are always TRUE, svn_depth_infinity_t, 12037253734Speter and empty array respectively. 12038251881Speter 12039251881Speter If RECEIVER raises a SVN_ERR_CEASE_INVOCATION error, but still sets 12040251881Speter *REVISION to a valid revnum, then clear the error. Otherwise return 12041251881Speter any error. */ 12042251881Speterstatic svn_error_t* 12043253734Spetershort_circuit_mergeinfo_log(svn_mergeinfo_catalog_t *target_mergeinfo_cat, 12044253734Speter svn_boolean_t finding_merged, 12045251881Speter const char *target_path_or_url, 12046251881Speter const svn_opt_revision_t *target_peg_revision, 12047251881Speter const char *source_path_or_url, 12048251881Speter const svn_opt_revision_t *source_peg_revision, 12049251881Speter const svn_opt_revision_t *source_start_revision, 12050251881Speter const svn_opt_revision_t *source_end_revision, 12051251881Speter svn_log_entry_receiver_t receiver, 12052251881Speter svn_revnum_t *revision, 12053251881Speter svn_client_ctx_t *ctx, 12054253734Speter apr_pool_t *result_pool, 12055251881Speter apr_pool_t *scratch_pool) 12056251881Speter{ 12057253734Speter apr_array_header_t *revprops; 12058253734Speter svn_error_t *err; 12059251881Speter 12060253734Speter revprops = apr_array_make(scratch_pool, 0, sizeof(const char *)); 12061253734Speter err = svn_client__mergeinfo_log(finding_merged, 12062253734Speter target_path_or_url, 12063253734Speter target_peg_revision, 12064253734Speter target_mergeinfo_cat, 12065253734Speter source_path_or_url, 12066253734Speter source_peg_revision, 12067253734Speter source_start_revision, 12068253734Speter source_end_revision, 12069253734Speter receiver, revision, 12070253734Speter TRUE, svn_depth_infinity, 12071253734Speter revprops, ctx, result_pool, 12072253734Speter scratch_pool); 12073253734Speter 12074251881Speter if (err) 12075251881Speter { 12076251881Speter /* We expect RECEIVER to short-circuit the (potentially expensive) log 12077251881Speter by raising an SVN_ERR_CEASE_INVOCATION -- see operative_rev_receiver. 12078251881Speter So we can ignore that error, but only as long as we actually found a 12079251881Speter valid revision. */ 12080251881Speter if (SVN_IS_VALID_REVNUM(*revision) 12081251881Speter && err->apr_err == SVN_ERR_CEASE_INVOCATION) 12082251881Speter { 12083251881Speter svn_error_clear(err); 12084251881Speter err = NULL; 12085251881Speter } 12086251881Speter else 12087251881Speter { 12088251881Speter return svn_error_trace(err); 12089251881Speter } 12090251881Speter } 12091251881Speter return SVN_NO_ERROR; 12092251881Speter} 12093251881Speter 12094251881Speter/* Set *BASE_P to the last location on SOURCE_BRANCH such that all changes 12095251881Speter * on SOURCE_BRANCH after YCA up to and including *BASE_P have already 12096251881Speter * been fully merged into TARGET. 12097251881Speter * 12098251881Speter * *BASE_P TIP 12099251881Speter * o-------o-----------o--- SOURCE_BRANCH 12100251881Speter * / \ 12101251881Speter * -----o prev. \ 12102251881Speter * YCA \ merges \ 12103251881Speter * o-----------o----------- TARGET branch 12104251881Speter * 12105251881Speter * In terms of mergeinfo: 12106251881Speter * 12107251881Speter * Source a--... o=change, -=no-op revision 12108251881Speter * branch / \ 12109251881Speter * YCA --> o a---o---o---o---o--- d=delete, a=add-as-a-copy 12110251881Speter * 12111251881Speter * Eligible -.eee.eeeeeeeeeeeeeeeeeeee .=not a source branch location 12112251881Speter * 12113251881Speter * Tgt-mi -.mmm.mm-mm-------m------- m=merged to root of TARGET or 12114251881Speter * subtree of TARGET with no 12115251881Speter * operative changes outside of that 12116251881Speter * subtree, -=not merged 12117251881Speter * 12118251881Speter * Eligible -.---.--e--eeeeeee-eeeeeee 12119251881Speter * 12120251881Speter * Next --------^----------------- BASE is just before here. 12121251881Speter * 12122251881Speter * / \ 12123251881Speter * -----o prev. \ 12124251881Speter * YCA \ merges \ 12125251881Speter * o-----------o------------- 12126251881Speter * 12127251881Speter * If no revisions from SOURCE_BRANCH have been completely merged to TARGET, 12128251881Speter * then set *BASE_P to the YCA. 12129251881Speter */ 12130251881Speterstatic svn_error_t * 12131251881Speterfind_last_merged_location(svn_client__pathrev_t **base_p, 12132251881Speter svn_client__pathrev_t *yca, 12133251881Speter const branch_history_t *source_branch, 12134251881Speter svn_client__pathrev_t *target, 12135251881Speter svn_client_ctx_t *ctx, 12136251881Speter apr_pool_t *result_pool, 12137251881Speter apr_pool_t *scratch_pool) 12138251881Speter{ 12139251881Speter svn_opt_revision_t source_peg_rev, source_start_rev, source_end_rev, 12140251881Speter target_opt_rev; 12141251881Speter svn_revnum_t youngest_merged_rev = SVN_INVALID_REVNUM; 12142253734Speter svn_mergeinfo_catalog_t target_mergeinfo_cat = NULL; 12143251881Speter 12144251881Speter source_peg_rev.kind = svn_opt_revision_number; 12145251881Speter source_peg_rev.value.number = source_branch->tip->rev; 12146251881Speter source_start_rev.kind = svn_opt_revision_number; 12147251881Speter source_start_rev.value.number = yca->rev; 12148251881Speter source_end_rev.kind = svn_opt_revision_number; 12149251881Speter source_end_rev.value.number = source_branch->tip->rev; 12150251881Speter target_opt_rev.kind = svn_opt_revision_number; 12151251881Speter target_opt_rev.value.number = target->rev; 12152251881Speter 12153251881Speter /* Find the youngest revision fully merged from SOURCE_BRANCH to TARGET, 12154251881Speter if such a revision exists. */ 12155253734Speter SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat, 12156253734Speter TRUE, /* Find merged */ 12157251881Speter target->url, &target_opt_rev, 12158251881Speter source_branch->tip->url, 12159251881Speter &source_peg_rev, 12160251881Speter &source_end_rev, &source_start_rev, 12161251881Speter operative_rev_receiver, 12162251881Speter &youngest_merged_rev, 12163253734Speter ctx, result_pool, scratch_pool)); 12164251881Speter 12165251881Speter if (!SVN_IS_VALID_REVNUM(youngest_merged_rev)) 12166251881Speter { 12167251881Speter /* No revisions have been completely merged from SOURCE_BRANCH to 12168251881Speter TARGET so the base for the next merge is the YCA. */ 12169251881Speter *base_p = yca; 12170251881Speter } 12171251881Speter else 12172251881Speter { 12173251881Speter /* One or more revisions have already been completely merged from 12174251881Speter SOURCE_BRANCH to TARGET, now find the oldest revision, older 12175251881Speter than the youngest merged revision, which is still eligible to 12176251881Speter be merged, if such exists. */ 12177251881Speter branch_history_t *contiguous_source; 12178251881Speter svn_revnum_t base_rev; 12179251881Speter svn_revnum_t oldest_eligible_rev = SVN_INVALID_REVNUM; 12180251881Speter 12181251881Speter /* If the only revisions eligible are younger than the youngest merged 12182251881Speter revision we can simply assume that the youngest eligible revision 12183251881Speter is the youngest merged revision. Obviously this may not be true! 12184251881Speter The revisions between the youngest merged revision and the tip of 12185251881Speter the branch may have several inoperative revisions -- they may *all* 12186251881Speter be inoperative revisions! But for the purpose of this function 12187251881Speter (i.e. finding the youngest revision after the YCA where all revs have 12188251881Speter been merged) that doesn't matter. */ 12189251881Speter source_end_rev.value.number = youngest_merged_rev; 12190253734Speter SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat, 12191253734Speter FALSE, /* Find eligible */ 12192251881Speter target->url, &target_opt_rev, 12193251881Speter source_branch->tip->url, 12194251881Speter &source_peg_rev, 12195251881Speter &source_start_rev, &source_end_rev, 12196251881Speter operative_rev_receiver, 12197251881Speter &oldest_eligible_rev, 12198253734Speter ctx, scratch_pool, scratch_pool)); 12199251881Speter 12200251881Speter /* If there are revisions eligible for merging, use the oldest one 12201251881Speter to calculate the base. Otherwise there are no operative revisions 12202251881Speter to merge and we can simple set the base to the youngest revision 12203251881Speter already merged. */ 12204251881Speter if (SVN_IS_VALID_REVNUM(oldest_eligible_rev)) 12205251881Speter base_rev = oldest_eligible_rev - 1; 12206251881Speter else 12207251881Speter base_rev = youngest_merged_rev; 12208251881Speter 12209251881Speter /* Find the branch location just before the oldest eligible rev. 12210251881Speter (We can't just use the base revs calculated above because the branch 12211251881Speter might have a gap there.) */ 12212251881Speter SVN_ERR(branch_history_intersect_range(&contiguous_source, 12213251881Speter source_branch, yca->rev, 12214251881Speter base_rev, 12215251881Speter scratch_pool, scratch_pool)); 12216251881Speter SVN_ERR(branch_history_get_endpoints(NULL, base_p, contiguous_source, 12217251881Speter result_pool, scratch_pool)); 12218251881Speter } 12219251881Speter 12220251881Speter return SVN_NO_ERROR; 12221251881Speter} 12222251881Speter 12223251881Speter/* Find a merge base location on the target branch, like in a sync 12224251881Speter * merge. 12225251881Speter * 12226251881Speter * BASE S_T->source 12227251881Speter * o-------o-----------o--- 12228251881Speter * / \ \ 12229251881Speter * -----o prev. \ \ this 12230251881Speter * YCA \ merge \ \ merge 12231251881Speter * o-----------o-----------o 12232251881Speter * S_T->target 12233251881Speter * 12234251881Speter * Set *BASE_P to BASE, the youngest location in the history of S_T->source 12235251881Speter * (at or after the YCA) at which all revisions up to BASE are effectively 12236251881Speter * merged into S_T->target. 12237251881Speter * 12238251881Speter * If no locations on the history of S_T->source are effectively merged to 12239251881Speter * S_T->target, set *BASE_P to the YCA. 12240251881Speter */ 12241251881Speterstatic svn_error_t * 12242251881Speterfind_base_on_source(svn_client__pathrev_t **base_p, 12243251881Speter source_and_target_t *s_t, 12244251881Speter svn_client_ctx_t *ctx, 12245251881Speter apr_pool_t *result_pool, 12246251881Speter apr_pool_t *scratch_pool) 12247251881Speter{ 12248251881Speter SVN_ERR(find_last_merged_location(base_p, 12249251881Speter s_t->yca, 12250251881Speter &s_t->source_branch, 12251251881Speter s_t->target_branch.tip, 12252251881Speter ctx, result_pool, scratch_pool)); 12253251881Speter return SVN_NO_ERROR; 12254251881Speter} 12255251881Speter 12256251881Speter/* Find a merge base location on the target branch, like in a reintegrate 12257251881Speter * merge. 12258251881Speter * 12259251881Speter * S_T->source 12260251881Speter * o-----------o-------o--- 12261251881Speter * / prev. / \ 12262251881Speter * -----o merge / \ this 12263251881Speter * YCA \ / \ merge 12264251881Speter * o-------o---------------o 12265251881Speter * BASE S_T->target 12266251881Speter * 12267251881Speter * Set *BASE_P to BASE, the youngest location in the history of S_T->target 12268251881Speter * (at or after the YCA) at which all revisions up to BASE are effectively 12269251881Speter * merged into S_T->source. 12270251881Speter * 12271251881Speter * If no locations on the history of S_T->target are effectively merged to 12272251881Speter * S_T->source, set *BASE_P to the YCA. 12273251881Speter */ 12274251881Speterstatic svn_error_t * 12275251881Speterfind_base_on_target(svn_client__pathrev_t **base_p, 12276251881Speter source_and_target_t *s_t, 12277251881Speter svn_client_ctx_t *ctx, 12278251881Speter apr_pool_t *result_pool, 12279251881Speter apr_pool_t *scratch_pool) 12280251881Speter{ 12281251881Speter SVN_ERR(find_last_merged_location(base_p, 12282251881Speter s_t->yca, 12283251881Speter &s_t->target_branch, 12284251881Speter s_t->source, 12285251881Speter ctx, result_pool, scratch_pool)); 12286251881Speter 12287251881Speter return SVN_NO_ERROR; 12288251881Speter} 12289251881Speter 12290251881Speter/* The body of client_find_automatic_merge(), which see. 12291251881Speter */ 12292251881Speterstatic svn_error_t * 12293251881Speterfind_automatic_merge(svn_client__pathrev_t **base_p, 12294251881Speter svn_boolean_t *is_reintegrate_like, 12295251881Speter source_and_target_t *s_t, 12296251881Speter svn_client_ctx_t *ctx, 12297251881Speter apr_pool_t *result_pool, 12298251881Speter apr_pool_t *scratch_pool) 12299251881Speter{ 12300251881Speter svn_client__pathrev_t *base_on_source, *base_on_target; 12301251881Speter 12302251881Speter /* Get the location-history of each branch. */ 12303251881Speter s_t->source_branch.tip = s_t->source; 12304251881Speter SVN_ERR(svn_client__get_history_as_mergeinfo( 12305251881Speter &s_t->source_branch.history, &s_t->source_branch.has_r0_history, 12306251881Speter s_t->source, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, 12307251881Speter s_t->source_ra_session, ctx, scratch_pool)); 12308251881Speter s_t->target_branch.tip = &s_t->target->loc; 12309251881Speter SVN_ERR(svn_client__get_history_as_mergeinfo( 12310251881Speter &s_t->target_branch.history, &s_t->target_branch.has_r0_history, 12311251881Speter &s_t->target->loc, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, 12312251881Speter s_t->target_ra_session, ctx, scratch_pool)); 12313251881Speter 12314253734Speter SVN_ERR(svn_client__calc_youngest_common_ancestor( 12315253734Speter &s_t->yca, s_t->source, s_t->source_branch.history, 12316253734Speter s_t->source_branch.has_r0_history, 12317253734Speter &s_t->target->loc, s_t->target_branch.history, 12318253734Speter s_t->target_branch.has_r0_history, 12319253734Speter result_pool, scratch_pool)); 12320253734Speter 12321251881Speter if (! s_t->yca) 12322251881Speter return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL, 12323251881Speter _("'%s@%ld' must be ancestrally related to " 12324251881Speter "'%s@%ld'"), 12325251881Speter s_t->source->url, s_t->source->rev, 12326251881Speter s_t->target->loc.url, s_t->target->loc.rev); 12327251881Speter 12328251881Speter /* Find the latest revision of A synced to B and the latest 12329251881Speter * revision of B synced to A. 12330251881Speter * 12331251881Speter * base_on_source = youngest_complete_synced_point(source, target) 12332251881Speter * base_on_target = youngest_complete_synced_point(target, source) 12333251881Speter */ 12334251881Speter SVN_ERR(find_base_on_source(&base_on_source, s_t, 12335251881Speter ctx, scratch_pool, scratch_pool)); 12336251881Speter SVN_ERR(find_base_on_target(&base_on_target, s_t, 12337251881Speter ctx, scratch_pool, scratch_pool)); 12338251881Speter 12339251881Speter /* Choose a base. */ 12340251881Speter if (base_on_source->rev >= base_on_target->rev) 12341251881Speter { 12342251881Speter *base_p = base_on_source; 12343251881Speter *is_reintegrate_like = FALSE; 12344251881Speter } 12345251881Speter else 12346251881Speter { 12347251881Speter *base_p = base_on_target; 12348251881Speter *is_reintegrate_like = TRUE; 12349251881Speter } 12350251881Speter 12351251881Speter return SVN_NO_ERROR; 12352251881Speter} 12353251881Speter 12354251881Speter/** Find out what kind of automatic merge would be needed, when the target 12355251881Speter * is only known as a repository location rather than a WC. 12356251881Speter * 12357251881Speter * Like find_automatic_merge() except that the target is 12358251881Speter * specified by @a target_path_or_url at @a target_revision, which must 12359251881Speter * refer to a repository location, instead of by a WC path argument. 12360251881Speter */ 12361251881Speterstatic svn_error_t * 12362251881Speterfind_automatic_merge_no_wc(automatic_merge_t **merge_p, 12363251881Speter const char *source_path_or_url, 12364251881Speter const svn_opt_revision_t *source_revision, 12365251881Speter const char *target_path_or_url, 12366251881Speter const svn_opt_revision_t *target_revision, 12367251881Speter svn_client_ctx_t *ctx, 12368251881Speter apr_pool_t *result_pool, 12369251881Speter apr_pool_t *scratch_pool) 12370251881Speter{ 12371251881Speter source_and_target_t *s_t = apr_palloc(scratch_pool, sizeof(*s_t)); 12372251881Speter svn_client__pathrev_t *target_loc; 12373251881Speter automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge)); 12374251881Speter 12375251881Speter /* Source */ 12376251881Speter SVN_ERR(svn_client__ra_session_from_path2( 12377251881Speter &s_t->source_ra_session, &s_t->source, 12378251881Speter source_path_or_url, NULL, source_revision, source_revision, 12379251881Speter ctx, result_pool)); 12380251881Speter 12381251881Speter /* Target */ 12382251881Speter SVN_ERR(svn_client__ra_session_from_path2( 12383251881Speter &s_t->target_ra_session, &target_loc, 12384251881Speter target_path_or_url, NULL, target_revision, target_revision, 12385251881Speter ctx, result_pool)); 12386251881Speter s_t->target = apr_palloc(scratch_pool, sizeof(*s_t->target)); 12387251881Speter s_t->target->abspath = NULL; /* indicate the target is not a WC */ 12388251881Speter s_t->target->loc = *target_loc; 12389251881Speter 12390251881Speter SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t, 12391251881Speter ctx, result_pool, scratch_pool)); 12392251881Speter 12393251881Speter merge->right = s_t->source; 12394251881Speter merge->target = &s_t->target->loc; 12395251881Speter merge->yca = s_t->yca; 12396251881Speter *merge_p = merge; 12397251881Speter 12398251881Speter return SVN_NO_ERROR; 12399251881Speter} 12400251881Speter 12401251881Speter/* Find the information needed to merge all unmerged changes from a source 12402251881Speter * branch into a target branch. 12403251881Speter * 12404251881Speter * Set @a *merge_p to the information needed to merge all unmerged changes 12405251881Speter * (up to @a source_revision) from the source branch @a source_path_or_url 12406251881Speter * at @a source_revision into the target WC at @a target_abspath. 12407251881Speter * 12408251881Speter * The flags @a allow_mixed_rev, @a allow_local_mods and 12409251881Speter * @a allow_switched_subtrees enable merging into a WC that is in any or all 12410251881Speter * of the states described by their names, but only if this function decides 12411251881Speter * that the merge will be in the same direction as the last automatic merge. 12412251881Speter * If, on the other hand, the last automatic merge was in the opposite 12413251881Speter * direction, then such states of the WC are not allowed regardless 12414251881Speter * of these flags. This function merely records these flags in the 12415251881Speter * @a *merge_p structure; do_automatic_merge_locked() checks the WC 12416251881Speter * state for compliance. 12417251881Speter * 12418251881Speter * Allocate the @a *merge_p structure in @a result_pool. 12419251881Speter */ 12420251881Speterstatic svn_error_t * 12421251881Speterclient_find_automatic_merge(automatic_merge_t **merge_p, 12422251881Speter const char *source_path_or_url, 12423251881Speter const svn_opt_revision_t *source_revision, 12424251881Speter const char *target_abspath, 12425251881Speter svn_boolean_t allow_mixed_rev, 12426251881Speter svn_boolean_t allow_local_mods, 12427251881Speter svn_boolean_t allow_switched_subtrees, 12428251881Speter svn_client_ctx_t *ctx, 12429251881Speter apr_pool_t *result_pool, 12430251881Speter apr_pool_t *scratch_pool) 12431251881Speter{ 12432251881Speter source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t)); 12433251881Speter automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge)); 12434251881Speter 12435251881Speter /* "Open" the target WC. Check the target WC for mixed-rev, local mods and 12436251881Speter * switched subtrees yet to faster exit and notify user before contacting 12437251881Speter * with server. After we find out what kind of merge is required, then if a 12438251881Speter * reintegrate-like merge is required we'll do the stricter checks, in 12439251881Speter * do_automatic_merge_locked(). */ 12440251881Speter SVN_ERR(open_target_wc(&s_t->target, target_abspath, 12441251881Speter allow_mixed_rev, 12442251881Speter allow_local_mods, 12443251881Speter allow_switched_subtrees, 12444251881Speter ctx, result_pool, scratch_pool)); 12445251881Speter 12446251881Speter /* Open RA sessions to the source and target trees. */ 12447251881Speter SVN_ERR(svn_client_open_ra_session2(&s_t->target_ra_session, 12448251881Speter s_t->target->loc.url, 12449251881Speter s_t->target->abspath, 12450251881Speter ctx, result_pool, scratch_pool)); 12451251881Speter /* ### check for null URL (i.e. added path) here, like in reintegrate? */ 12452251881Speter SVN_ERR(svn_client__ra_session_from_path2( 12453251881Speter &s_t->source_ra_session, &s_t->source, 12454251881Speter source_path_or_url, NULL, source_revision, source_revision, 12455251881Speter ctx, result_pool)); 12456251881Speter 12457251881Speter /* Check source is in same repos as target. */ 12458251881Speter SVN_ERR(check_same_repos(s_t->source, source_path_or_url, 12459251881Speter &s_t->target->loc, target_abspath, 12460251881Speter TRUE /* strict_urls */, scratch_pool)); 12461251881Speter 12462251881Speter SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t, 12463251881Speter ctx, result_pool, scratch_pool)); 12464251881Speter merge->yca = s_t->yca; 12465251881Speter merge->right = s_t->source; 12466251881Speter merge->allow_mixed_rev = allow_mixed_rev; 12467251881Speter merge->allow_local_mods = allow_local_mods; 12468251881Speter merge->allow_switched_subtrees = allow_switched_subtrees; 12469251881Speter 12470251881Speter *merge_p = merge; 12471251881Speter 12472251881Speter /* TODO: Close the source and target sessions here? */ 12473251881Speter 12474251881Speter return SVN_NO_ERROR; 12475251881Speter} 12476251881Speter 12477251881Speter/* Perform an automatic merge, given the information in MERGE which 12478251881Speter * must have come from calling client_find_automatic_merge(). 12479251881Speter * 12480251881Speter * Four locations are inputs: YCA, BASE, RIGHT, TARGET, as shown 12481251881Speter * depending on whether the base is on the source branch or the target 12482251881Speter * branch of this merge. 12483251881Speter * 12484251881Speter * RIGHT (is_reintegrate_like) 12485251881Speter * o-----------o-------o--- 12486251881Speter * / prev. / \ 12487251881Speter * -----o merge / \ this 12488251881Speter * YCA \ / \ merge 12489251881Speter * o-------o---------------o 12490251881Speter * BASE TARGET 12491251881Speter * 12492251881Speter * or 12493251881Speter * 12494251881Speter * BASE RIGHT (! is_reintegrate_like) 12495251881Speter * o-------o-----------o--- 12496251881Speter * / \ \ 12497251881Speter * -----o prev. \ \ this 12498251881Speter * YCA \ merge \ \ merge 12499251881Speter * o-----------o-----------o 12500251881Speter * TARGET 12501251881Speter * 12502251881Speter * ### TODO: The reintegrate-like code path does not yet 12503251881Speter * eliminate already-cherry-picked revisions from the source. 12504251881Speter */ 12505251881Speterstatic svn_error_t * 12506251881Speterdo_automatic_merge_locked(conflict_report_t **conflict_report, 12507251881Speter const automatic_merge_t *merge, 12508251881Speter const char *target_abspath, 12509251881Speter svn_depth_t depth, 12510251881Speter svn_boolean_t diff_ignore_ancestry, 12511251881Speter svn_boolean_t force_delete, 12512251881Speter svn_boolean_t record_only, 12513251881Speter svn_boolean_t dry_run, 12514251881Speter const apr_array_header_t *merge_options, 12515251881Speter svn_client_ctx_t *ctx, 12516251881Speter apr_pool_t *result_pool, 12517251881Speter apr_pool_t *scratch_pool) 12518251881Speter{ 12519251881Speter merge_target_t *target; 12520251881Speter svn_boolean_t reintegrate_like = merge->is_reintegrate_like; 12521251881Speter svn_boolean_t use_sleep = FALSE; 12522251881Speter svn_error_t *err; 12523251881Speter 12524251881Speter SVN_ERR(open_target_wc(&target, target_abspath, 12525251881Speter merge->allow_mixed_rev && ! reintegrate_like, 12526251881Speter merge->allow_local_mods && ! reintegrate_like, 12527251881Speter merge->allow_switched_subtrees && ! reintegrate_like, 12528251881Speter ctx, scratch_pool, scratch_pool)); 12529251881Speter 12530251881Speter if (reintegrate_like) 12531251881Speter { 12532251881Speter merge_source_t source; 12533251881Speter svn_ra_session_t *base_ra_session = NULL; 12534251881Speter svn_ra_session_t *right_ra_session = NULL; 12535251881Speter svn_ra_session_t *target_ra_session = NULL; 12536251881Speter 12537251881Speter if (record_only) 12538251881Speter return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 12539251881Speter _("The required merge is reintegrate-like, " 12540251881Speter "and the record-only option " 12541251881Speter "cannot be used with this kind of merge")); 12542251881Speter 12543251881Speter if (depth != svn_depth_unknown) 12544251881Speter return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 12545251881Speter _("The required merge is reintegrate-like, " 12546251881Speter "and the depth option " 12547251881Speter "cannot be used with this kind of merge")); 12548251881Speter 12549251881Speter if (force_delete) 12550251881Speter return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 12551251881Speter _("The required merge is reintegrate-like, " 12552251881Speter "and the force_delete option " 12553251881Speter "cannot be used with this kind of merge")); 12554251881Speter 12555251881Speter SVN_ERR(ensure_ra_session_url(&base_ra_session, merge->base->url, 12556251881Speter target->abspath, ctx, scratch_pool)); 12557251881Speter SVN_ERR(ensure_ra_session_url(&right_ra_session, merge->right->url, 12558251881Speter target->abspath, ctx, scratch_pool)); 12559251881Speter SVN_ERR(ensure_ra_session_url(&target_ra_session, target->loc.url, 12560251881Speter target->abspath, ctx, scratch_pool)); 12561251881Speter 12562251881Speter /* Check for and reject any abnormalities -- such as revisions that 12563251881Speter * have not yet been merged in the opposite direction -- that a 12564251881Speter * 'reintegrate' merge would have rejected. */ 12565251881Speter { 12566251881Speter merge_source_t *source2; 12567251881Speter 12568251881Speter SVN_ERR(find_reintegrate_merge(&source2, NULL, 12569251881Speter right_ra_session, merge->right, 12570251881Speter target_ra_session, target, 12571251881Speter ctx, scratch_pool, scratch_pool)); 12572251881Speter } 12573251881Speter 12574251881Speter source.loc1 = merge->base; 12575251881Speter source.loc2 = merge->right; 12576251881Speter source.ancestral = ! merge->is_reintegrate_like; 12577251881Speter 12578251881Speter err = merge_cousins_and_supplement_mergeinfo(conflict_report, 12579251881Speter &use_sleep, 12580251881Speter target, 12581251881Speter base_ra_session, 12582251881Speter right_ra_session, 12583251881Speter &source, merge->yca, 12584251881Speter TRUE /* same_repos */, 12585251881Speter depth, 12586251881Speter FALSE /*diff_ignore_ancestry*/, 12587251881Speter force_delete, record_only, 12588251881Speter dry_run, 12589251881Speter merge_options, 12590251881Speter ctx, 12591251881Speter result_pool, scratch_pool); 12592251881Speter } 12593251881Speter else /* ! merge->is_reintegrate_like */ 12594251881Speter { 12595251881Speter /* Ignoring the base that we found, we pass the YCA instead and let 12596251881Speter do_merge() work out which subtrees need which revision ranges to 12597251881Speter be merged. This enables do_merge() to fill in revision-range 12598251881Speter gaps that are older than the base that we calculated (which is 12599251881Speter for the root path of the merge). 12600251881Speter 12601251881Speter An improvement would be to change find_automatic_merge() to 12602251881Speter find the base for each sutree, and then here use the oldest base 12603251881Speter among all subtrees. */ 12604251881Speter apr_array_header_t *merge_sources; 12605251881Speter svn_ra_session_t *ra_session = NULL; 12606251881Speter 12607251881Speter /* Normalize our merge sources, do_merge() requires this. See the 12608251881Speter 'MERGEINFO MERGE SOURCE NORMALIZATION' global comment. */ 12609251881Speter SVN_ERR(ensure_ra_session_url(&ra_session, merge->right->url, 12610251881Speter target->abspath, ctx, scratch_pool)); 12611251881Speter SVN_ERR(normalize_merge_sources_internal( 12612251881Speter &merge_sources, merge->right, 12613251881Speter svn_rangelist__initialize(merge->yca->rev, merge->right->rev, TRUE, 12614251881Speter scratch_pool), 12615251881Speter ra_session, ctx, scratch_pool, scratch_pool)); 12616251881Speter 12617251881Speter err = do_merge(NULL, NULL, conflict_report, &use_sleep, 12618251881Speter merge_sources, target, ra_session, 12619251881Speter TRUE /*related*/, TRUE /*same_repos*/, 12620251881Speter FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry, 12621251881Speter force_delete, dry_run, 12622251881Speter record_only, NULL, FALSE, FALSE, depth, merge_options, 12623251881Speter ctx, result_pool, scratch_pool); 12624251881Speter } 12625251881Speter 12626251881Speter if (use_sleep) 12627251881Speter svn_io_sleep_for_timestamps(target_abspath, scratch_pool); 12628251881Speter 12629251881Speter SVN_ERR(err); 12630251881Speter 12631251881Speter return SVN_NO_ERROR; 12632251881Speter} 12633251881Speter 12634251881Spetersvn_error_t * 12635251881Spetersvn_client_get_merging_summary(svn_boolean_t *needs_reintegration, 12636251881Speter const char **yca_url, svn_revnum_t *yca_rev, 12637251881Speter const char **base_url, svn_revnum_t *base_rev, 12638251881Speter const char **right_url, svn_revnum_t *right_rev, 12639251881Speter const char **target_url, svn_revnum_t *target_rev, 12640251881Speter const char **repos_root_url, 12641251881Speter const char *source_path_or_url, 12642251881Speter const svn_opt_revision_t *source_revision, 12643251881Speter const char *target_path_or_url, 12644251881Speter const svn_opt_revision_t *target_revision, 12645251881Speter svn_client_ctx_t *ctx, 12646251881Speter apr_pool_t *result_pool, 12647251881Speter apr_pool_t *scratch_pool) 12648251881Speter{ 12649251881Speter svn_boolean_t target_is_wc; 12650251881Speter automatic_merge_t *merge; 12651251881Speter 12652251881Speter target_is_wc = (! svn_path_is_url(target_path_or_url)) 12653251881Speter && (target_revision->kind == svn_opt_revision_unspecified 12654251881Speter || target_revision->kind == svn_opt_revision_working); 12655251881Speter if (target_is_wc) 12656251881Speter SVN_ERR(client_find_automatic_merge( 12657251881Speter &merge, 12658251881Speter source_path_or_url, source_revision, 12659251881Speter target_path_or_url, 12660251881Speter TRUE, TRUE, TRUE, /* allow_* */ 12661251881Speter ctx, scratch_pool, scratch_pool)); 12662251881Speter else 12663251881Speter SVN_ERR(find_automatic_merge_no_wc( 12664251881Speter &merge, 12665251881Speter source_path_or_url, source_revision, 12666251881Speter target_path_or_url, target_revision, 12667251881Speter ctx, scratch_pool, scratch_pool)); 12668251881Speter 12669251881Speter if (needs_reintegration) 12670251881Speter *needs_reintegration = merge->is_reintegrate_like; 12671251881Speter if (yca_url) 12672251881Speter *yca_url = apr_pstrdup(result_pool, merge->yca->url); 12673251881Speter if (yca_rev) 12674251881Speter *yca_rev = merge->yca->rev; 12675251881Speter if (base_url) 12676251881Speter *base_url = apr_pstrdup(result_pool, merge->base->url); 12677251881Speter if (base_rev) 12678251881Speter *base_rev = merge->base->rev; 12679251881Speter if (right_url) 12680251881Speter *right_url = apr_pstrdup(result_pool, merge->right->url); 12681251881Speter if (right_rev) 12682251881Speter *right_rev = merge->right->rev; 12683251881Speter if (target_url) 12684251881Speter *target_url = apr_pstrdup(result_pool, merge->target->url); 12685251881Speter if (target_rev) 12686251881Speter *target_rev = merge->target->rev; 12687251881Speter if (repos_root_url) 12688251881Speter *repos_root_url = apr_pstrdup(result_pool, merge->yca->repos_root_url); 12689251881Speter 12690251881Speter return SVN_NO_ERROR; 12691251881Speter} 12692