1251881Speter/* 2251881Speter * mergeinfo.c: Mergeinfo parsing and handling 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#include <assert.h> 24251881Speter#include <ctype.h> 25251881Speter 26251881Speter#include "svn_path.h" 27251881Speter#include "svn_types.h" 28251881Speter#include "svn_ctype.h" 29251881Speter#include "svn_pools.h" 30251881Speter#include "svn_sorts.h" 31251881Speter#include "svn_error.h" 32251881Speter#include "svn_error_codes.h" 33251881Speter#include "svn_string.h" 34251881Speter#include "svn_mergeinfo.h" 35251881Speter#include "private/svn_fspath.h" 36251881Speter#include "private/svn_mergeinfo_private.h" 37251881Speter#include "private/svn_string_private.h" 38251881Speter#include "private/svn_subr_private.h" 39251881Speter#include "svn_private_config.h" 40251881Speter#include "svn_hash.h" 41251881Speter#include "private/svn_dep_compat.h" 42251881Speter 43251881Speter/* Attempt to combine two ranges, IN1 and IN2. If they are adjacent or 44251881Speter overlapping, and their inheritability allows them to be combined, put 45251881Speter the result in OUTPUT and return TRUE, otherwise return FALSE. 46251881Speter 47251881Speter CONSIDER_INHERITANCE determines how to account for the inheritability 48251881Speter of IN1 and IN2 when trying to combine ranges. If ranges with different 49251881Speter inheritability are combined (CONSIDER_INHERITANCE must be FALSE for this 50251881Speter to happen) the result is inheritable. If both ranges are inheritable the 51251881Speter result is inheritable. Only and if both ranges are non-inheritable is 52251881Speter the result is non-inheritable. 53251881Speter 54251881Speter Range overlapping detection algorithm from 55251881Speter http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap 56251881Speter*/ 57251881Speterstatic svn_boolean_t 58251881Spetercombine_ranges(svn_merge_range_t *output, 59251881Speter const svn_merge_range_t *in1, 60251881Speter const svn_merge_range_t *in2, 61251881Speter svn_boolean_t consider_inheritance) 62251881Speter{ 63251881Speter if (in1->start <= in2->end && in2->start <= in1->end) 64251881Speter { 65251881Speter if (!consider_inheritance 66251881Speter || (consider_inheritance 67251881Speter && (in1->inheritable == in2->inheritable))) 68251881Speter { 69251881Speter output->start = MIN(in1->start, in2->start); 70251881Speter output->end = MAX(in1->end, in2->end); 71251881Speter output->inheritable = (in1->inheritable || in2->inheritable); 72251881Speter return TRUE; 73251881Speter } 74251881Speter } 75251881Speter return FALSE; 76251881Speter} 77251881Speter 78251881Speter/* pathname -> PATHNAME */ 79251881Speterstatic svn_error_t * 80251881Speterparse_pathname(const char **input, 81251881Speter const char *end, 82251881Speter const char **pathname, 83251881Speter apr_pool_t *pool) 84251881Speter{ 85251881Speter const char *curr = *input; 86251881Speter const char *last_colon = NULL; 87251881Speter 88251881Speter /* A pathname may contain colons, so find the last colon before END 89251881Speter or newline. We'll consider this the divider between the pathname 90251881Speter and the revisionlist. */ 91251881Speter while (curr < end && *curr != '\n') 92251881Speter { 93251881Speter if (*curr == ':') 94251881Speter last_colon = curr; 95251881Speter curr++; 96251881Speter } 97251881Speter 98251881Speter if (!last_colon) 99251881Speter return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 100251881Speter _("Pathname not terminated by ':'")); 101251881Speter if (last_colon == *input) 102251881Speter return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 103251881Speter _("No pathname preceding ':'")); 104251881Speter 105251881Speter /* Tolerate relative repository paths, but convert them to absolute. 106251881Speter ### Efficiency? 1 string duplication here, 2 in canonicalize. */ 107251881Speter *pathname = svn_fspath__canonicalize(apr_pstrndup(pool, *input, 108251881Speter last_colon - *input), 109251881Speter pool); 110251881Speter 111251881Speter *input = last_colon; 112251881Speter 113251881Speter return SVN_NO_ERROR; 114251881Speter} 115251881Speter 116251881Speter/* Return TRUE iff (svn_merge_range_t *) RANGE describes a valid, forward 117251881Speter * revision range. 118251881Speter * 119251881Speter * Note: The smallest valid value of RANGE->start is 0 because it is an 120251881Speter * exclusive endpoint, being one less than the revision number of the first 121251881Speter * change described by the range, and the oldest possible change is "r1" as 122251881Speter * there cannot be a change "r0". */ 123251881Speter#define IS_VALID_FORWARD_RANGE(range) \ 124251881Speter (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end)) 125251881Speter 126251881Speter/* Ways in which two svn_merge_range_t can intersect or adjoin, if at all. */ 127251881Spetertypedef enum intersection_type_t 128251881Speter{ 129251881Speter /* Ranges don't intersect and don't adjoin. */ 130251881Speter svn__no_intersection, 131251881Speter 132251881Speter /* Ranges are equal. */ 133251881Speter svn__equal_intersection, 134251881Speter 135251881Speter /* Ranges adjoin but don't overlap. */ 136251881Speter svn__adjoining_intersection, 137251881Speter 138251881Speter /* Ranges overlap but neither is a subset of the other. */ 139251881Speter svn__overlapping_intersection, 140251881Speter 141251881Speter /* One range is a proper subset of the other. */ 142251881Speter svn__proper_subset_intersection 143251881Speter} intersection_type_t; 144251881Speter 145251881Speter/* Given ranges R1 and R2, both of which must be forward merge ranges, 146251881Speter set *INTERSECTION_TYPE to describe how the ranges intersect, if they 147251881Speter do at all. The inheritance type of the ranges is not considered. */ 148251881Speterstatic svn_error_t * 149251881Speterget_type_of_intersection(const svn_merge_range_t *r1, 150251881Speter const svn_merge_range_t *r2, 151251881Speter intersection_type_t *intersection_type) 152251881Speter{ 153251881Speter SVN_ERR_ASSERT(r1); 154251881Speter SVN_ERR_ASSERT(r2); 155251881Speter SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r1)); 156251881Speter SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r2)); 157251881Speter 158251881Speter if (!(r1->start <= r2->end && r2->start <= r1->end)) 159251881Speter *intersection_type = svn__no_intersection; 160251881Speter else if (r1->start == r2->start && r1->end == r2->end) 161251881Speter *intersection_type = svn__equal_intersection; 162251881Speter else if (r1->end == r2->start || r2->end == r1->start) 163251881Speter *intersection_type = svn__adjoining_intersection; 164251881Speter else if (r1->start <= r2->start && r1->end >= r2->end) 165251881Speter *intersection_type = svn__proper_subset_intersection; 166251881Speter else if (r2->start <= r1->start && r2->end >= r1->end) 167251881Speter *intersection_type = svn__proper_subset_intersection; 168251881Speter else 169251881Speter *intersection_type = svn__overlapping_intersection; 170251881Speter 171251881Speter return SVN_NO_ERROR; 172251881Speter} 173251881Speter 174251881Speter/* Modify or extend RANGELIST (a list of merge ranges) to incorporate 175251881Speter NEW_RANGE. RANGELIST is a "rangelist" as defined in svn_mergeinfo.h. 176251881Speter 177251881Speter OVERVIEW 178251881Speter 179251881Speter Determine the minimal set of non-overlapping merge ranges required to 180251881Speter represent the combination of RANGELIST and NEW_RANGE. The result depends 181251881Speter on whether and how NEW_RANGE overlaps any merge range[*] in RANGELIST, 182251881Speter and also on any differences in the inheritability of each range, 183251881Speter according to the rules described below. Modify RANGELIST to represent 184251881Speter this result, by adjusting the last range in it and/or appending one or 185251881Speter two more ranges. 186251881Speter 187251881Speter ([*] Due to the simplifying assumption below, only the last range in 188251881Speter RANGELIST is considered.) 189251881Speter 190251881Speter DETAILS 191251881Speter 192251881Speter If RANGELIST is not empty assume NEW_RANGE does not intersect with any 193251881Speter range before the last one in RANGELIST. 194251881Speter 195251881Speter If RANGELIST is empty or NEW_RANGE does not intersect with the lastrange 196251881Speter in RANGELIST, then append a copy of NEW_RANGE, allocated in RESULT_POOL, 197251881Speter to RANGELIST. 198251881Speter 199251881Speter If NEW_RANGE intersects with the last range in RANGELIST then combine 200251881Speter these two ranges as described below: 201251881Speter 202251881Speter If the intersecting ranges have the same inheritability then simply 203251881Speter combine the ranges in place. Otherwise, if the ranges intersect but 204251881Speter differ in inheritability, then merge the ranges as dictated by 205251881Speter CONSIDER_INHERITANCE: 206251881Speter 207251881Speter If CONSIDER_INHERITANCE is false then intersecting ranges are combined 208251881Speter into a single range. The inheritability of the resulting range is 209251881Speter non-inheritable *only* if both ranges are non-inheritable, otherwise the 210251881Speter combined range is inheritable, e.g.: 211251881Speter 212251881Speter Last range in NEW_RANGE RESULTING RANGES 213251881Speter RANGELIST 214251881Speter ------------- --------- ---------------- 215251881Speter 4-10* 6-13 4-13 216251881Speter 4-10 6-13* 4-13 217251881Speter 4-10* 6-13* 4-13* 218251881Speter 219251881Speter If CONSIDER_INHERITANCE is true, then only the intersection between the 220251881Speter two ranges is combined, with the inheritability of the resulting range 221251881Speter non-inheritable only if both ranges were non-inheritable. The 222251881Speter non-intersecting portions are added as separate ranges allocated in 223251881Speter RESULT_POOL, e.g.: 224251881Speter 225251881Speter Last range in NEW_RANGE RESULTING RANGES 226251881Speter RANGELIST 227251881Speter ------------- --------- ---------------- 228251881Speter 4-10* 6 4-5*, 6, 7-10* 229251881Speter 4-10* 6-12 4-5*, 6-12 230251881Speter 231251881Speter Note that the standard rules for rangelists still apply and overlapping 232251881Speter ranges are not allowed. So if the above would result in overlapping 233251881Speter ranges of the same inheritance, the overlapping ranges are merged into a 234251881Speter single range, e.g.: 235251881Speter 236251881Speter Last range in NEW_RANGE RESULTING RANGES 237251881Speter RANGELIST 238251881Speter ------------- --------- ---------------- 239251881Speter 4-10 6* 4-10 (Not 4-5, 6, 7-10) 240251881Speter 241251881Speter When replacing the last range in RANGELIST, either allocate a new range in 242251881Speter RESULT_POOL or modify the existing range in place. Any new ranges added 243251881Speter to RANGELIST are allocated in RESULT_POOL. 244251881Speter*/ 245251881Speterstatic svn_error_t * 246251881Spetercombine_with_lastrange(const svn_merge_range_t *new_range, 247251881Speter svn_rangelist_t *rangelist, 248251881Speter svn_boolean_t consider_inheritance, 249251881Speter apr_pool_t *result_pool) 250251881Speter{ 251251881Speter svn_merge_range_t *lastrange; 252251881Speter svn_merge_range_t combined_range; 253251881Speter 254251881Speter /* We don't accept a NULL RANGELIST. */ 255251881Speter SVN_ERR_ASSERT(rangelist); 256251881Speter 257251881Speter if (rangelist->nelts > 0) 258251881Speter lastrange = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1, svn_merge_range_t *); 259251881Speter else 260251881Speter lastrange = NULL; 261251881Speter 262251881Speter if (!lastrange) 263251881Speter { 264251881Speter /* No *LASTRANGE so push NEW_RANGE onto RANGELIST and we are done. */ 265251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = 266251881Speter svn_merge_range_dup(new_range, result_pool); 267251881Speter } 268251881Speter else if (!consider_inheritance) 269251881Speter { 270251881Speter /* We are not considering inheritance so we can merge intersecting 271251881Speter ranges of different inheritability. Of course if the ranges 272251881Speter don't intersect at all we simply push NEW_RANGE only RANGELIST. */ 273251881Speter if (combine_ranges(&combined_range, lastrange, new_range, FALSE)) 274251881Speter { 275251881Speter *lastrange = combined_range; 276251881Speter } 277251881Speter else 278251881Speter { 279251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = 280251881Speter svn_merge_range_dup(new_range, result_pool); 281251881Speter } 282251881Speter } 283251881Speter else /* Considering inheritance */ 284251881Speter { 285251881Speter if (combine_ranges(&combined_range, lastrange, new_range, TRUE)) 286251881Speter { 287251881Speter /* Even when considering inheritance two intersection ranges 288251881Speter of the same inheritability can simply be combined. */ 289251881Speter *lastrange = combined_range; 290251881Speter } 291251881Speter else 292251881Speter { 293251881Speter /* If we are here then the ranges either don't intersect or do 294251881Speter intersect but have differing inheritability. Check for the 295251881Speter first case as that is easy to handle. */ 296251881Speter intersection_type_t intersection_type; 297251881Speter svn_boolean_t sorted = FALSE; 298251881Speter 299251881Speter SVN_ERR(get_type_of_intersection(new_range, lastrange, 300251881Speter &intersection_type)); 301251881Speter 302251881Speter switch (intersection_type) 303251881Speter { 304251881Speter case svn__no_intersection: 305251881Speter /* NEW_RANGE and *LASTRANGE *really* don't intersect so 306251881Speter just push NEW_RANGE only RANGELIST. */ 307251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = 308251881Speter svn_merge_range_dup(new_range, result_pool); 309251881Speter sorted = (svn_sort_compare_ranges(&lastrange, 310251881Speter &new_range) < 0); 311251881Speter break; 312251881Speter 313251881Speter case svn__equal_intersection: 314251881Speter /* They range are equal so all we do is force the 315251881Speter inheritability of lastrange to true. */ 316251881Speter lastrange->inheritable = TRUE; 317251881Speter sorted = TRUE; 318251881Speter break; 319251881Speter 320251881Speter case svn__adjoining_intersection: 321251881Speter /* They adjoin but don't overlap so just push NEW_RANGE 322251881Speter onto RANGELIST. */ 323251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = 324251881Speter svn_merge_range_dup(new_range, result_pool); 325251881Speter sorted = (svn_sort_compare_ranges(&lastrange, 326251881Speter &new_range) < 0); 327251881Speter break; 328251881Speter 329251881Speter case svn__overlapping_intersection: 330251881Speter /* They ranges overlap but neither is a proper subset of 331251881Speter the other. We'll end up pusing two new ranges onto 332251881Speter RANGELIST, the intersecting part and the part unique to 333251881Speter NEW_RANGE.*/ 334251881Speter { 335251881Speter svn_merge_range_t *r1 = svn_merge_range_dup(lastrange, 336251881Speter result_pool); 337251881Speter svn_merge_range_t *r2 = svn_merge_range_dup(new_range, 338251881Speter result_pool); 339251881Speter 340251881Speter /* Pop off *LASTRANGE to make our manipulations 341251881Speter easier. */ 342251881Speter apr_array_pop(rangelist); 343251881Speter 344251881Speter /* Ensure R1 is the older range. */ 345251881Speter if (r2->start < r1->start) 346251881Speter { 347251881Speter /* Swap R1 and R2. */ 348251881Speter *r2 = *r1; 349251881Speter *r1 = *new_range; 350251881Speter } 351251881Speter 352251881Speter /* Absorb the intersecting ranges into the 353251881Speter inheritable range. */ 354251881Speter if (r1->inheritable) 355251881Speter r2->start = r1->end; 356251881Speter else 357251881Speter r1->end = r2->start; 358251881Speter 359251881Speter /* Push everything back onto RANGELIST. */ 360251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1; 361251881Speter sorted = (svn_sort_compare_ranges(&lastrange, 362251881Speter &r1) < 0); 363251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2; 364251881Speter if (sorted) 365251881Speter sorted = (svn_sort_compare_ranges(&r1, &r2) < 0); 366251881Speter break; 367251881Speter } 368251881Speter 369251881Speter default: /* svn__proper_subset_intersection */ 370251881Speter { 371251881Speter /* One range is a proper subset of the other. */ 372251881Speter svn_merge_range_t *r1 = svn_merge_range_dup(lastrange, 373251881Speter result_pool); 374251881Speter svn_merge_range_t *r2 = svn_merge_range_dup(new_range, 375251881Speter result_pool); 376251881Speter svn_merge_range_t *r3 = NULL; 377251881Speter 378251881Speter /* Pop off *LASTRANGE to make our manipulations 379251881Speter easier. */ 380251881Speter apr_array_pop(rangelist); 381251881Speter 382251881Speter /* Ensure R1 is the superset. */ 383251881Speter if (r2->start < r1->start || r2->end > r1->end) 384251881Speter { 385251881Speter /* Swap R1 and R2. */ 386251881Speter *r2 = *r1; 387251881Speter *r1 = *new_range; 388251881Speter } 389251881Speter 390251881Speter if (r1->inheritable) 391251881Speter { 392251881Speter /* The simple case: The superset is inheritable, so 393251881Speter just combine r1 and r2. */ 394251881Speter r1->start = MIN(r1->start, r2->start); 395251881Speter r1->end = MAX(r1->end, r2->end); 396251881Speter r2 = NULL; 397251881Speter } 398251881Speter else if (r1->start == r2->start) 399251881Speter { 400251881Speter svn_revnum_t tmp_revnum; 401251881Speter 402251881Speter /* *LASTRANGE and NEW_RANGE share an end point. */ 403251881Speter tmp_revnum = r1->end; 404251881Speter r1->end = r2->end; 405251881Speter r2->inheritable = r1->inheritable; 406251881Speter r1->inheritable = TRUE; 407251881Speter r2->start = r1->end; 408251881Speter r2->end = tmp_revnum; 409251881Speter } 410251881Speter else if (r1->end == r2->end) 411251881Speter { 412251881Speter /* *LASTRANGE and NEW_RANGE share an end point. */ 413251881Speter r1->end = r2->start; 414251881Speter r2->inheritable = TRUE; 415251881Speter } 416251881Speter else 417251881Speter { 418251881Speter /* NEW_RANGE and *LASTRANGE share neither start 419251881Speter nor end points. */ 420251881Speter r3 = apr_pcalloc(result_pool, sizeof(*r3)); 421251881Speter r3->start = r2->end; 422251881Speter r3->end = r1->end; 423251881Speter r3->inheritable = r1->inheritable; 424251881Speter r2->inheritable = TRUE; 425251881Speter r1->end = r2->start; 426251881Speter } 427251881Speter 428251881Speter /* Push everything back onto RANGELIST. */ 429251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1; 430251881Speter sorted = (svn_sort_compare_ranges(&lastrange, &r1) < 0); 431251881Speter if (r2) 432251881Speter { 433251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2; 434251881Speter if (sorted) 435251881Speter sorted = (svn_sort_compare_ranges(&r1, &r2) < 0); 436251881Speter } 437251881Speter if (r3) 438251881Speter { 439251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r3; 440251881Speter if (sorted) 441251881Speter { 442251881Speter if (r2) 443251881Speter sorted = (svn_sort_compare_ranges(&r2, 444251881Speter &r3) < 0); 445251881Speter else 446251881Speter sorted = (svn_sort_compare_ranges(&r1, 447251881Speter &r3) < 0); 448251881Speter } 449251881Speter } 450251881Speter break; 451251881Speter } 452251881Speter } 453251881Speter 454251881Speter /* Some of the above cases might have put *RANGELIST out of 455251881Speter order, so re-sort.*/ 456251881Speter if (!sorted) 457251881Speter qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size, 458251881Speter svn_sort_compare_ranges); 459251881Speter } 460251881Speter } 461251881Speter 462251881Speter return SVN_NO_ERROR; 463251881Speter} 464251881Speter 465251881Speter/* Convert a single svn_merge_range_t *RANGE back into a string. */ 466251881Speterstatic char * 467251881Speterrange_to_string(const svn_merge_range_t *range, 468251881Speter apr_pool_t *pool) 469251881Speter{ 470251881Speter const char *mark 471251881Speter = range->inheritable ? "" : SVN_MERGEINFO_NONINHERITABLE_STR; 472251881Speter 473251881Speter if (range->start == range->end - 1) 474251881Speter return apr_psprintf(pool, "%ld%s", range->end, mark); 475251881Speter else if (range->start - 1 == range->end) 476251881Speter return apr_psprintf(pool, "-%ld%s", range->start, mark); 477251881Speter else if (range->start < range->end) 478251881Speter return apr_psprintf(pool, "%ld-%ld%s", range->start + 1, range->end, mark); 479251881Speter else 480251881Speter return apr_psprintf(pool, "%ld-%ld%s", range->start, range->end + 1, mark); 481251881Speter} 482251881Speter 483251881Speter/* Helper for svn_mergeinfo_parse() 484251881Speter Append revision ranges onto the array RANGELIST to represent the range 485251881Speter descriptions found in the string *INPUT. Read only as far as a newline 486251881Speter or the position END, whichever comes first. Set *INPUT to the position 487251881Speter after the last character of INPUT that was used. 488251881Speter 489251881Speter revisionlist -> (revisionelement)(COMMA revisionelement)* 490251881Speter revisionrange -> REVISION "-" REVISION("*") 491251881Speter revisionelement -> revisionrange | REVISION("*") 492251881Speter*/ 493251881Speterstatic svn_error_t * 494251881Speterparse_rangelist(const char **input, const char *end, 495251881Speter svn_rangelist_t *rangelist, 496251881Speter apr_pool_t *pool) 497251881Speter{ 498251881Speter const char *curr = *input; 499251881Speter 500251881Speter /* Eat any leading horizontal white-space before the rangelist. */ 501251881Speter while (curr < end && *curr != '\n' && isspace(*curr)) 502251881Speter curr++; 503251881Speter 504251881Speter if (*curr == '\n' || curr == end) 505251881Speter { 506251881Speter /* Empty range list. */ 507251881Speter *input = curr; 508251881Speter return SVN_NO_ERROR; 509251881Speter } 510251881Speter 511251881Speter while (curr < end && *curr != '\n') 512251881Speter { 513251881Speter /* Parse individual revisions or revision ranges. */ 514251881Speter svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange)); 515251881Speter svn_revnum_t firstrev; 516251881Speter 517251881Speter SVN_ERR(svn_revnum_parse(&firstrev, curr, &curr)); 518251881Speter if (*curr != '-' && *curr != '\n' && *curr != ',' && *curr != '*' 519251881Speter && curr != end) 520251881Speter return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 521251881Speter _("Invalid character '%c' found in revision " 522251881Speter "list"), *curr); 523251881Speter mrange->start = firstrev - 1; 524251881Speter mrange->end = firstrev; 525251881Speter mrange->inheritable = TRUE; 526251881Speter 527251881Speter if (firstrev == 0) 528251881Speter return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 529251881Speter _("Invalid revision number '0' found in " 530251881Speter "range list")); 531251881Speter 532251881Speter if (*curr == '-') 533251881Speter { 534251881Speter svn_revnum_t secondrev; 535251881Speter 536251881Speter curr++; 537251881Speter SVN_ERR(svn_revnum_parse(&secondrev, curr, &curr)); 538251881Speter if (firstrev > secondrev) 539251881Speter return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 540251881Speter _("Unable to parse reversed revision " 541251881Speter "range '%ld-%ld'"), 542251881Speter firstrev, secondrev); 543251881Speter else if (firstrev == secondrev) 544251881Speter return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 545251881Speter _("Unable to parse revision range " 546251881Speter "'%ld-%ld' with same start and end " 547251881Speter "revisions"), firstrev, secondrev); 548251881Speter mrange->end = secondrev; 549251881Speter } 550251881Speter 551251881Speter if (*curr == '\n' || curr == end) 552251881Speter { 553251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange; 554251881Speter *input = curr; 555251881Speter return SVN_NO_ERROR; 556251881Speter } 557251881Speter else if (*curr == ',') 558251881Speter { 559251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange; 560251881Speter curr++; 561251881Speter } 562251881Speter else if (*curr == '*') 563251881Speter { 564251881Speter mrange->inheritable = FALSE; 565251881Speter curr++; 566251881Speter if (*curr == ',' || *curr == '\n' || curr == end) 567251881Speter { 568251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange; 569251881Speter if (*curr == ',') 570251881Speter { 571251881Speter curr++; 572251881Speter } 573251881Speter else 574251881Speter { 575251881Speter *input = curr; 576251881Speter return SVN_NO_ERROR; 577251881Speter } 578251881Speter } 579251881Speter else 580251881Speter { 581251881Speter return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 582251881Speter _("Invalid character '%c' found in " 583251881Speter "range list"), *curr); 584251881Speter } 585251881Speter } 586251881Speter else 587251881Speter { 588251881Speter return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 589251881Speter _("Invalid character '%c' found in " 590251881Speter "range list"), *curr); 591251881Speter } 592251881Speter 593251881Speter } 594251881Speter if (*curr != '\n') 595251881Speter return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 596251881Speter _("Range list parsing ended before hitting " 597251881Speter "newline")); 598251881Speter *input = curr; 599251881Speter return SVN_NO_ERROR; 600251881Speter} 601251881Speter 602251881Spetersvn_error_t * 603251881Spetersvn_rangelist__parse(svn_rangelist_t **rangelist, 604251881Speter const char *str, 605251881Speter apr_pool_t *result_pool) 606251881Speter{ 607251881Speter const char *s = str; 608251881Speter 609251881Speter *rangelist = apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *)); 610251881Speter SVN_ERR(parse_rangelist(&s, s + strlen(s), *rangelist, result_pool)); 611251881Speter return SVN_NO_ERROR; 612251881Speter} 613251881Speter 614251881Spetersvn_error_t * 615251881Spetersvn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist, 616251881Speter apr_pool_t *scratch_pool) 617251881Speter{ 618251881Speter int i; 619251881Speter svn_merge_range_t *range, *lastrange; 620251881Speter 621251881Speter lastrange = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *); 622251881Speter 623251881Speter for (i = 1; i < rangelist->nelts; i++) 624251881Speter { 625251881Speter range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 626251881Speter if (lastrange->start <= range->end 627251881Speter && range->start <= lastrange->end) 628251881Speter { 629251881Speter /* The ranges are adjacent or intersect. */ 630251881Speter 631251881Speter /* svn_mergeinfo_parse promises to combine overlapping 632251881Speter ranges as long as their inheritability is the same. */ 633251881Speter if (range->start < lastrange->end 634251881Speter && range->inheritable != lastrange->inheritable) 635251881Speter { 636251881Speter return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 637251881Speter _("Unable to parse overlapping " 638251881Speter "revision ranges '%s' and '%s' " 639251881Speter "with different inheritance " 640251881Speter "types"), 641251881Speter range_to_string(lastrange, 642251881Speter scratch_pool), 643251881Speter range_to_string(range, 644251881Speter scratch_pool)); 645251881Speter } 646251881Speter 647251881Speter /* Combine overlapping or adjacent ranges with the 648251881Speter same inheritability. */ 649251881Speter if (lastrange->inheritable == range->inheritable) 650251881Speter { 651251881Speter lastrange->end = MAX(range->end, lastrange->end); 652251881Speter svn_sort__array_delete(rangelist, i, 1); 653251881Speter i--; 654251881Speter } 655251881Speter } 656251881Speter lastrange = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 657251881Speter } 658251881Speter 659251881Speter return SVN_NO_ERROR; 660251881Speter} 661251881Speter 662251881Speter/* revisionline -> PATHNAME COLON revisionlist */ 663251881Speterstatic svn_error_t * 664251881Speterparse_revision_line(const char **input, const char *end, svn_mergeinfo_t hash, 665251881Speter apr_pool_t *scratch_pool) 666251881Speter{ 667251881Speter const char *pathname = ""; 668251881Speter apr_ssize_t klen; 669251881Speter svn_rangelist_t *existing_rangelist; 670251881Speter svn_rangelist_t *rangelist = apr_array_make(scratch_pool, 1, 671251881Speter sizeof(svn_merge_range_t *)); 672251881Speter 673251881Speter SVN_ERR(parse_pathname(input, end, &pathname, scratch_pool)); 674251881Speter 675251881Speter if (*(*input) != ':') 676251881Speter return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 677251881Speter _("Pathname not terminated by ':'")); 678251881Speter 679251881Speter *input = *input + 1; 680251881Speter 681251881Speter SVN_ERR(parse_rangelist(input, end, rangelist, scratch_pool)); 682251881Speter 683251881Speter if (rangelist->nelts == 0) 684251881Speter return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 685251881Speter _("Mergeinfo for '%s' maps to an " 686251881Speter "empty revision range"), pathname); 687251881Speter if (*input != end && *(*input) != '\n') 688251881Speter return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 689251881Speter _("Could not find end of line in range list line " 690251881Speter "in '%s'"), *input); 691251881Speter 692251881Speter if (*input != end) 693251881Speter *input = *input + 1; 694251881Speter 695251881Speter /* Sort the rangelist, combine adjacent ranges into single ranges, 696251881Speter and make sure there are no overlapping ranges. */ 697251881Speter if (rangelist->nelts > 1) 698251881Speter { 699251881Speter qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size, 700251881Speter svn_sort_compare_ranges); 701251881Speter 702251881Speter SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool)); 703251881Speter } 704251881Speter 705251881Speter /* Handle any funky mergeinfo with relative merge source paths that 706251881Speter might exist due to issue #3547. It's possible that this issue allowed 707251881Speter the creation of mergeinfo with path keys that differ only by a 708251881Speter leading slash, e.g. "trunk:4033\n/trunk:4039-4995". In the event 709251881Speter we encounter this we merge the rangelists together under a single 710251881Speter absolute path key. */ 711251881Speter klen = strlen(pathname); 712251881Speter existing_rangelist = apr_hash_get(hash, pathname, klen); 713251881Speter if (existing_rangelist) 714251881Speter SVN_ERR(svn_rangelist_merge2(rangelist, existing_rangelist, 715251881Speter scratch_pool, scratch_pool)); 716251881Speter 717251881Speter apr_hash_set(hash, apr_pstrmemdup(apr_hash_pool_get(hash), pathname, klen), 718251881Speter klen, svn_rangelist_dup(rangelist, apr_hash_pool_get(hash))); 719251881Speter 720251881Speter return SVN_NO_ERROR; 721251881Speter} 722251881Speter 723251881Speter/* top -> revisionline (NEWLINE revisionline)* */ 724251881Speterstatic svn_error_t * 725251881Speterparse_top(const char **input, const char *end, svn_mergeinfo_t hash, 726251881Speter apr_pool_t *pool) 727251881Speter{ 728251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 729251881Speter 730251881Speter while (*input < end) 731251881Speter { 732251881Speter svn_pool_clear(iterpool); 733251881Speter SVN_ERR(parse_revision_line(input, end, hash, iterpool)); 734251881Speter } 735251881Speter svn_pool_destroy(iterpool); 736251881Speter 737251881Speter return SVN_NO_ERROR; 738251881Speter} 739251881Speter 740251881Spetersvn_error_t * 741251881Spetersvn_mergeinfo_parse(svn_mergeinfo_t *mergeinfo, 742251881Speter const char *input, 743251881Speter apr_pool_t *pool) 744251881Speter{ 745251881Speter svn_error_t *err; 746251881Speter 747251881Speter *mergeinfo = svn_hash__make(pool); 748251881Speter err = parse_top(&input, input + strlen(input), *mergeinfo, pool); 749251881Speter 750251881Speter /* Always return SVN_ERR_MERGEINFO_PARSE_ERROR as the topmost error. */ 751251881Speter if (err && err->apr_err != SVN_ERR_MERGEINFO_PARSE_ERROR) 752251881Speter err = svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, err, 753251881Speter _("Could not parse mergeinfo string '%s'"), 754251881Speter input); 755251881Speter return err; 756251881Speter} 757251881Speter 758251881Speter/* Cleanup after svn_rangelist_merge2 when it modifies the ending range of 759251881Speter a single rangelist element in-place. 760251881Speter 761251881Speter If *RANGE_INDEX is not a valid element in RANGELIST do nothing. Otherwise 762251881Speter ensure that RANGELIST[*RANGE_INDEX]->END does not adjoin or overlap any 763251881Speter subsequent ranges in RANGELIST. 764251881Speter 765251881Speter If overlap is found, then remove, modify, and/or add elements to RANGELIST 766251881Speter as per the invariants for rangelists documented in svn_mergeinfo.h. If 767251881Speter RANGELIST[*RANGE_INDEX]->END adjoins a subsequent element then combine the 768251881Speter elements if their inheritability permits -- The inheritance of intersecting 769251881Speter and adjoining ranges is handled as per svn_mergeinfo_merge2. Upon return 770251881Speter set *RANGE_INDEX to the index of the youngest element modified, added, or 771251881Speter adjoined to RANGELIST[*RANGE_INDEX]. 772251881Speter 773251881Speter Note: Adjoining rangelist elements are those where the end rev of the older 774251881Speter element is equal to the start rev of the younger element. 775251881Speter 776251881Speter Any new elements inserted into RANGELIST are allocated in RESULT_POOL.*/ 777251881Speterstatic void 778251881Speteradjust_remaining_ranges(svn_rangelist_t *rangelist, 779251881Speter int *range_index, 780251881Speter apr_pool_t *result_pool) 781251881Speter{ 782251881Speter int i; 783251881Speter int starting_index; 784251881Speter int elements_to_delete = 0; 785251881Speter svn_merge_range_t *modified_range; 786251881Speter 787251881Speter if (*range_index >= rangelist->nelts) 788251881Speter return; 789251881Speter 790251881Speter starting_index = *range_index + 1; 791251881Speter modified_range = APR_ARRAY_IDX(rangelist, *range_index, svn_merge_range_t *); 792251881Speter 793251881Speter for (i = *range_index + 1; i < rangelist->nelts; i++) 794251881Speter { 795251881Speter svn_merge_range_t *next_range = APR_ARRAY_IDX(rangelist, i, 796251881Speter svn_merge_range_t *); 797251881Speter 798251881Speter /* If MODIFIED_RANGE doesn't adjoin or overlap the next range in 799251881Speter RANGELIST then we are finished. */ 800251881Speter if (modified_range->end < next_range->start) 801251881Speter break; 802251881Speter 803251881Speter /* Does MODIFIED_RANGE adjoin NEXT_RANGE? */ 804251881Speter if (modified_range->end == next_range->start) 805251881Speter { 806251881Speter if (modified_range->inheritable == next_range->inheritable) 807251881Speter { 808251881Speter /* Combine adjoining ranges with the same inheritability. */ 809251881Speter modified_range->end = next_range->end; 810251881Speter elements_to_delete++; 811251881Speter } 812251881Speter else 813251881Speter { 814251881Speter /* Cannot join because inheritance differs. */ 815251881Speter (*range_index)++; 816251881Speter } 817251881Speter break; 818251881Speter } 819251881Speter 820251881Speter /* Alright, we know MODIFIED_RANGE overlaps NEXT_RANGE, but how? */ 821251881Speter if (modified_range->end > next_range->end) 822251881Speter { 823251881Speter /* NEXT_RANGE is a proper subset of MODIFIED_RANGE and the two 824251881Speter don't share the same end range. */ 825251881Speter if (modified_range->inheritable 826251881Speter || (modified_range->inheritable == next_range->inheritable)) 827251881Speter { 828251881Speter /* MODIFIED_RANGE absorbs NEXT_RANGE. */ 829251881Speter elements_to_delete++; 830251881Speter } 831251881Speter else 832251881Speter { 833251881Speter /* NEXT_RANGE is a proper subset MODIFIED_RANGE but 834251881Speter MODIFIED_RANGE is non-inheritable and NEXT_RANGE is 835251881Speter inheritable. This means MODIFIED_RANGE is truncated, 836251881Speter NEXT_RANGE remains, and the portion of MODIFIED_RANGE 837251881Speter younger than NEXT_RANGE is added as a separate range: 838251881Speter ______________________________________________ 839251881Speter | | 840251881Speter M MODIFIED_RANGE N 841251881Speter | (!inhertiable) | 842251881Speter |______________________________________________| 843251881Speter | | 844251881Speter O NEXT_RANGE P 845251881Speter | (inheritable)| 846251881Speter |______________| 847251881Speter | 848251881Speter V 849251881Speter _______________________________________________ 850251881Speter | | | | 851251881Speter M MODIFIED_RANGE O NEXT_RANGE P NEW_RANGE N 852251881Speter | (!inhertiable) | (inheritable)| (!inheritable)| 853251881Speter |________________|______________|_______________| 854251881Speter */ 855251881Speter svn_merge_range_t *new_modified_range = 856251881Speter apr_palloc(result_pool, sizeof(*new_modified_range)); 857251881Speter new_modified_range->start = next_range->end; 858251881Speter new_modified_range->end = modified_range->end; 859251881Speter new_modified_range->inheritable = FALSE; 860251881Speter modified_range->end = next_range->start; 861251881Speter (*range_index)+=2; 862251881Speter svn_sort__array_insert(&new_modified_range, rangelist, 863251881Speter *range_index); 864251881Speter /* Recurse with the new range. */ 865251881Speter adjust_remaining_ranges(rangelist, range_index, result_pool); 866251881Speter break; 867251881Speter } 868251881Speter } 869251881Speter else if (modified_range->end == next_range->end) 870251881Speter { 871251881Speter /* NEXT_RANGE is a proper subset MODIFIED_RANGE and share 872251881Speter the same end range. */ 873251881Speter if (modified_range->inheritable 874251881Speter || (modified_range->inheritable == next_range->inheritable)) 875251881Speter { 876251881Speter /* MODIFIED_RANGE absorbs NEXT_RANGE. */ 877251881Speter elements_to_delete++; 878251881Speter } 879251881Speter else 880251881Speter { 881251881Speter /* The intersection between MODIFIED_RANGE and NEXT_RANGE is 882251881Speter absorbed by the latter. */ 883251881Speter modified_range->end = next_range->start; 884251881Speter (*range_index)++; 885251881Speter } 886251881Speter break; 887251881Speter } 888251881Speter else 889251881Speter { 890251881Speter /* NEXT_RANGE and MODIFIED_RANGE intersect but NEXT_RANGE is not 891251881Speter a proper subset of MODIFIED_RANGE, nor do the two share the 892251881Speter same end revision, i.e. they overlap. */ 893251881Speter if (modified_range->inheritable == next_range->inheritable) 894251881Speter { 895251881Speter /* Combine overlapping ranges with the same inheritability. */ 896251881Speter modified_range->end = next_range->end; 897251881Speter elements_to_delete++; 898251881Speter } 899251881Speter else if (modified_range->inheritable) 900251881Speter { 901251881Speter /* MODIFIED_RANGE absorbs the portion of NEXT_RANGE it overlaps 902251881Speter and NEXT_RANGE is truncated. */ 903251881Speter next_range->start = modified_range->end; 904251881Speter (*range_index)++; 905251881Speter } 906251881Speter else 907251881Speter { 908251881Speter /* NEXT_RANGE absorbs the portion of MODIFIED_RANGE it overlaps 909251881Speter and MODIFIED_RANGE is truncated. */ 910251881Speter modified_range->end = next_range->start; 911251881Speter (*range_index)++; 912251881Speter } 913251881Speter break; 914251881Speter } 915251881Speter } 916251881Speter 917251881Speter if (elements_to_delete) 918251881Speter svn_sort__array_delete(rangelist, starting_index, elements_to_delete); 919251881Speter} 920251881Speter 921251881Spetersvn_error_t * 922251881Spetersvn_rangelist_merge2(svn_rangelist_t *rangelist, 923251881Speter const svn_rangelist_t *changes, 924251881Speter apr_pool_t *result_pool, 925251881Speter apr_pool_t *scratch_pool) 926251881Speter{ 927251881Speter int i = 0; 928251881Speter int j = 0; 929251881Speter 930251881Speter /* We may modify CHANGES, so make a copy in SCRATCH_POOL. */ 931251881Speter changes = svn_rangelist_dup(changes, scratch_pool); 932251881Speter 933251881Speter while (i < rangelist->nelts && j < changes->nelts) 934251881Speter { 935251881Speter svn_merge_range_t *range = 936251881Speter APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 937251881Speter svn_merge_range_t *change = 938251881Speter APR_ARRAY_IDX(changes, j, svn_merge_range_t *); 939251881Speter int res = svn_sort_compare_ranges(&range, &change); 940251881Speter 941251881Speter if (res == 0) 942251881Speter { 943251881Speter /* Only when merging two non-inheritable ranges is the result also 944251881Speter non-inheritable. In all other cases ensure an inheritiable 945251881Speter result. */ 946251881Speter if (range->inheritable || change->inheritable) 947251881Speter range->inheritable = TRUE; 948251881Speter i++; 949251881Speter j++; 950251881Speter } 951251881Speter else if (res < 0) /* CHANGE is younger than RANGE */ 952251881Speter { 953251881Speter if (range->end < change->start) 954251881Speter { 955251881Speter /* RANGE is older than CHANGE and the two do not 956251881Speter adjoin or overlap */ 957251881Speter i++; 958251881Speter } 959251881Speter else if (range->end == change->start) 960251881Speter { 961251881Speter /* RANGE and CHANGE adjoin */ 962251881Speter if (range->inheritable == change->inheritable) 963251881Speter { 964251881Speter /* RANGE and CHANGE have the same inheritability so 965251881Speter RANGE expands to absord CHANGE. */ 966251881Speter range->end = change->end; 967251881Speter adjust_remaining_ranges(rangelist, &i, result_pool); 968251881Speter j++; 969251881Speter } 970251881Speter else 971251881Speter { 972251881Speter /* RANGE and CHANGE adjoin, but have different 973251881Speter inheritability. Since RANGE is older, just 974251881Speter move on to the next RANGE. */ 975251881Speter i++; 976251881Speter } 977251881Speter } 978251881Speter else 979251881Speter { 980251881Speter /* RANGE and CHANGE overlap, but how? */ 981251881Speter if ((range->inheritable == change->inheritable) 982251881Speter || range->inheritable) 983251881Speter { 984251881Speter /* If CHANGE is a proper subset of RANGE, it absorbs RANGE 985251881Speter with no adjustment otherwise only the intersection is 986251881Speter absorbed and CHANGE is truncated. */ 987251881Speter if (range->end >= change->end) 988251881Speter j++; 989251881Speter else 990251881Speter change->start = range->end; 991251881Speter } 992251881Speter else 993251881Speter { 994251881Speter /* RANGE is non-inheritable and CHANGE is inheritable. */ 995251881Speter if (range->start < change->start) 996251881Speter { 997251881Speter /* CHANGE absorbs intersection with RANGE and RANGE 998251881Speter is truncated. */ 999251881Speter svn_merge_range_t *range_copy = 1000251881Speter svn_merge_range_dup(range, result_pool); 1001251881Speter range_copy->end = change->start; 1002251881Speter range->start = change->start; 1003251881Speter svn_sort__array_insert(&range_copy, rangelist, i++); 1004251881Speter } 1005251881Speter else 1006251881Speter { 1007251881Speter /* CHANGE and RANGE share the same start rev, but 1008251881Speter RANGE is considered older because its end rev 1009251881Speter is older. */ 1010251881Speter range->inheritable = TRUE; 1011251881Speter change->start = range->end; 1012251881Speter } 1013251881Speter } 1014251881Speter } 1015251881Speter } 1016251881Speter else /* res > 0, CHANGE is older than RANGE */ 1017251881Speter { 1018251881Speter if (change->end < range->start) 1019251881Speter { 1020251881Speter /* CHANGE is older than RANGE and the two do not 1021251881Speter adjoin or overlap, so insert a copy of CHANGE 1022251881Speter into RANGELIST. */ 1023251881Speter svn_merge_range_t *change_copy = 1024251881Speter svn_merge_range_dup(change, result_pool); 1025251881Speter svn_sort__array_insert(&change_copy, rangelist, i++); 1026251881Speter j++; 1027251881Speter } 1028251881Speter else if (change->end == range->start) 1029251881Speter { 1030251881Speter /* RANGE and CHANGE adjoin */ 1031251881Speter if (range->inheritable == change->inheritable) 1032251881Speter { 1033251881Speter /* RANGE and CHANGE have the same inheritability so we 1034251881Speter can simply combine the two in place. */ 1035251881Speter range->start = change->start; 1036251881Speter j++; 1037251881Speter } 1038251881Speter else 1039251881Speter { 1040251881Speter /* RANGE and CHANGE have different inheritability so insert 1041251881Speter a copy of CHANGE into RANGELIST. */ 1042251881Speter svn_merge_range_t *change_copy = 1043251881Speter svn_merge_range_dup(change, result_pool); 1044251881Speter svn_sort__array_insert(&change_copy, rangelist, i); 1045251881Speter j++; 1046251881Speter } 1047251881Speter } 1048251881Speter else 1049251881Speter { 1050251881Speter /* RANGE and CHANGE overlap. */ 1051251881Speter if (range->inheritable == change->inheritable) 1052251881Speter { 1053251881Speter /* RANGE and CHANGE have the same inheritability so we 1054251881Speter can simply combine the two in place... */ 1055251881Speter range->start = change->start; 1056251881Speter if (range->end < change->end) 1057251881Speter { 1058251881Speter /* ...but if RANGE is expanded ensure that we don't 1059251881Speter violate any rangelist invariants. */ 1060251881Speter range->end = change->end; 1061251881Speter adjust_remaining_ranges(rangelist, &i, result_pool); 1062251881Speter } 1063251881Speter j++; 1064251881Speter } 1065251881Speter else if (range->inheritable) 1066251881Speter { 1067251881Speter if (change->start < range->start) 1068251881Speter { 1069251881Speter /* RANGE is inheritable so absorbs any part of CHANGE 1070251881Speter it overlaps. CHANGE is truncated and the remainder 1071251881Speter inserted into RANGELIST. */ 1072251881Speter svn_merge_range_t *change_copy = 1073251881Speter svn_merge_range_dup(change, result_pool); 1074251881Speter change_copy->end = range->start; 1075251881Speter change->start = range->start; 1076251881Speter svn_sort__array_insert(&change_copy, rangelist, i++); 1077251881Speter } 1078251881Speter else 1079251881Speter { 1080251881Speter /* CHANGE and RANGE share the same start rev, but 1081251881Speter CHANGE is considered older because CHANGE->END is 1082251881Speter older than RANGE->END. */ 1083251881Speter j++; 1084251881Speter } 1085251881Speter } 1086251881Speter else 1087251881Speter { 1088251881Speter /* RANGE is non-inheritable and CHANGE is inheritable. */ 1089251881Speter if (change->start < range->start) 1090251881Speter { 1091251881Speter if (change->end == range->end) 1092251881Speter { 1093251881Speter /* RANGE is a proper subset of CHANGE and share the 1094251881Speter same end revision, so set RANGE equal to CHANGE. */ 1095251881Speter range->start = change->start; 1096251881Speter range->inheritable = TRUE; 1097251881Speter j++; 1098251881Speter } 1099251881Speter else if (change->end > range->end) 1100251881Speter { 1101251881Speter /* RANGE is a proper subset of CHANGE and CHANGE has 1102251881Speter a younger end revision, so set RANGE equal to its 1103251881Speter intersection with CHANGE and truncate CHANGE. */ 1104251881Speter range->start = change->start; 1105251881Speter range->inheritable = TRUE; 1106251881Speter change->start = range->end; 1107251881Speter } 1108251881Speter else 1109251881Speter { 1110251881Speter /* CHANGE and RANGE overlap. Set RANGE equal to its 1111251881Speter intersection with CHANGE and take the remainder 1112251881Speter of RANGE and insert it into RANGELIST. */ 1113251881Speter svn_merge_range_t *range_copy = 1114251881Speter svn_merge_range_dup(range, result_pool); 1115251881Speter range_copy->start = change->end; 1116251881Speter range->start = change->start; 1117251881Speter range->end = change->end; 1118251881Speter range->inheritable = TRUE; 1119251881Speter svn_sort__array_insert(&range_copy, rangelist, ++i); 1120251881Speter j++; 1121251881Speter } 1122251881Speter } 1123251881Speter else 1124251881Speter { 1125251881Speter /* CHANGE and RANGE share the same start rev, but 1126251881Speter CHANGE is considered older because its end rev 1127251881Speter is older. 1128251881Speter 1129251881Speter Insert the intersection of RANGE and CHANGE into 1130251881Speter RANGELIST and then set RANGE to the non-intersecting 1131251881Speter portion of RANGE. */ 1132251881Speter svn_merge_range_t *range_copy = 1133251881Speter svn_merge_range_dup(range, result_pool); 1134251881Speter range_copy->end = change->end; 1135251881Speter range_copy->inheritable = TRUE; 1136251881Speter range->start = change->end; 1137251881Speter svn_sort__array_insert(&range_copy, rangelist, i++); 1138251881Speter j++; 1139251881Speter } 1140251881Speter } 1141251881Speter } 1142251881Speter } 1143251881Speter } 1144251881Speter 1145251881Speter /* Copy any remaining elements in CHANGES into RANGELIST. */ 1146251881Speter for (; j < (changes)->nelts; j++) 1147251881Speter { 1148251881Speter svn_merge_range_t *change = 1149251881Speter APR_ARRAY_IDX(changes, j, svn_merge_range_t *); 1150251881Speter svn_merge_range_t *change_copy = svn_merge_range_dup(change, 1151251881Speter result_pool); 1152251881Speter svn_sort__array_insert(&change_copy, rangelist, rangelist->nelts); 1153251881Speter } 1154251881Speter 1155251881Speter return SVN_NO_ERROR; 1156251881Speter} 1157251881Speter 1158251881Speter/* Return TRUE iff the forward revision ranges FIRST and SECOND overlap and 1159251881Speter * (if CONSIDER_INHERITANCE is TRUE) have the same inheritability. */ 1160251881Speterstatic svn_boolean_t 1161251881Speterrange_intersect(const svn_merge_range_t *first, const svn_merge_range_t *second, 1162251881Speter svn_boolean_t consider_inheritance) 1163251881Speter{ 1164251881Speter SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first)); 1165251881Speter SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second)); 1166251881Speter 1167251881Speter return (first->start + 1 <= second->end) 1168251881Speter && (second->start + 1 <= first->end) 1169251881Speter && (!consider_inheritance 1170251881Speter || (!(first->inheritable) == !(second->inheritable))); 1171251881Speter} 1172251881Speter 1173251881Speter/* Return TRUE iff the forward revision range FIRST wholly contains the 1174251881Speter * forward revision range SECOND and (if CONSIDER_INHERITANCE is TRUE) has 1175251881Speter * the same inheritability. */ 1176251881Speterstatic svn_boolean_t 1177251881Speterrange_contains(const svn_merge_range_t *first, const svn_merge_range_t *second, 1178251881Speter svn_boolean_t consider_inheritance) 1179251881Speter{ 1180251881Speter SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first)); 1181251881Speter SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second)); 1182251881Speter 1183251881Speter return (first->start <= second->start) && (second->end <= first->end) 1184251881Speter && (!consider_inheritance 1185251881Speter || (!(first->inheritable) == !(second->inheritable))); 1186251881Speter} 1187251881Speter 1188251881Speter/* Swap start and end fields of RANGE. */ 1189251881Speterstatic void 1190251881Speterrange_swap_endpoints(svn_merge_range_t *range) 1191251881Speter{ 1192251881Speter svn_revnum_t swap = range->start; 1193251881Speter range->start = range->end; 1194251881Speter range->end = swap; 1195251881Speter} 1196251881Speter 1197251881Spetersvn_error_t * 1198251881Spetersvn_rangelist_reverse(svn_rangelist_t *rangelist, apr_pool_t *pool) 1199251881Speter{ 1200251881Speter int i; 1201251881Speter 1202251881Speter svn_sort__array_reverse(rangelist, pool); 1203251881Speter 1204251881Speter for (i = 0; i < rangelist->nelts; i++) 1205251881Speter { 1206251881Speter range_swap_endpoints(APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *)); 1207251881Speter } 1208251881Speter 1209251881Speter return SVN_NO_ERROR; 1210251881Speter} 1211251881Speter 1212251881Spetervoid 1213251881Spetersvn_rangelist__set_inheritance(svn_rangelist_t *rangelist, 1214251881Speter svn_boolean_t inheritable) 1215251881Speter{ 1216251881Speter if (rangelist) 1217251881Speter { 1218251881Speter int i; 1219251881Speter svn_merge_range_t *range; 1220251881Speter 1221251881Speter for (i = 0; i < rangelist->nelts; i++) 1222251881Speter { 1223251881Speter range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 1224251881Speter range->inheritable = inheritable; 1225251881Speter } 1226251881Speter } 1227251881Speter return; 1228251881Speter} 1229251881Speter 1230251881Spetervoid 1231251881Spetersvn_mergeinfo__set_inheritance(svn_mergeinfo_t mergeinfo, 1232251881Speter svn_boolean_t inheritable, 1233251881Speter apr_pool_t *scratch_pool) 1234251881Speter{ 1235251881Speter if (mergeinfo) 1236251881Speter { 1237251881Speter apr_hash_index_t *hi; 1238251881Speter 1239251881Speter for (hi = apr_hash_first(scratch_pool, mergeinfo); 1240251881Speter hi; 1241251881Speter hi = apr_hash_next(hi)) 1242251881Speter { 1243251881Speter svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 1244251881Speter 1245251881Speter if (rangelist) 1246251881Speter svn_rangelist__set_inheritance(rangelist, inheritable); 1247251881Speter } 1248251881Speter } 1249251881Speter return; 1250251881Speter} 1251251881Speter 1252251881Speter/* If DO_REMOVE is true, then remove any overlapping ranges described by 1253251881Speter RANGELIST1 from RANGELIST2 and place the results in *OUTPUT. When 1254251881Speter DO_REMOVE is true, RANGELIST1 is effectively the "eraser" and RANGELIST2 1255251881Speter the "whiteboard". 1256251881Speter 1257251881Speter If DO_REMOVE is false, then capture the intersection between RANGELIST1 1258251881Speter and RANGELIST2 and place the results in *OUTPUT. The ordering of 1259251881Speter RANGELIST1 and RANGELIST2 doesn't matter when DO_REMOVE is false. 1260251881Speter 1261251881Speter If CONSIDER_INHERITANCE is true, then take the inheritance of the 1262251881Speter ranges in RANGELIST1 and RANGELIST2 into account when comparing them 1263251881Speter for intersection, see the doc string for svn_rangelist_intersect(). 1264251881Speter 1265251881Speter If CONSIDER_INHERITANCE is false, then ranges with differing inheritance 1266251881Speter may intersect, but the resulting intersection is non-inheritable only 1267251881Speter if both ranges were non-inheritable, e.g.: 1268251881Speter 1269251881Speter RANGELIST1 RANGELIST2 CONSIDER DO_REMOVE *OUTPUT 1270251881Speter INHERITANCE 1271251881Speter ---------- ------ ----------- --------- ------- 1272251881Speter 1273251881Speter 90-420* 1-100 TRUE FALSE Empty Rangelist 1274251881Speter 90-420 1-100* TRUE FALSE Empty Rangelist 1275251881Speter 90-420 1-100 TRUE FALSE 90-100 1276251881Speter 90-420* 1-100* TRUE FALSE 90-100* 1277251881Speter 1278251881Speter 90-420* 1-100 FALSE FALSE 90-100 1279251881Speter 90-420 1-100* FALSE FALSE 90-100 1280251881Speter 90-420 1-100 FALSE FALSE 90-100 1281251881Speter 90-420* 1-100* FALSE FALSE 90-100* 1282251881Speter 1283251881Speter Allocate the contents of *OUTPUT in POOL. */ 1284251881Speterstatic svn_error_t * 1285251881Speterrangelist_intersect_or_remove(svn_rangelist_t **output, 1286251881Speter const svn_rangelist_t *rangelist1, 1287251881Speter const svn_rangelist_t *rangelist2, 1288251881Speter svn_boolean_t do_remove, 1289251881Speter svn_boolean_t consider_inheritance, 1290251881Speter apr_pool_t *pool) 1291251881Speter{ 1292251881Speter int i1, i2, lasti2; 1293251881Speter svn_merge_range_t working_elt2; 1294251881Speter 1295251881Speter *output = apr_array_make(pool, 1, sizeof(svn_merge_range_t *)); 1296251881Speter 1297251881Speter i1 = 0; 1298251881Speter i2 = 0; 1299251881Speter lasti2 = -1; /* Initialized to a value that "i2" will never be. */ 1300251881Speter 1301251881Speter while (i1 < rangelist1->nelts && i2 < rangelist2->nelts) 1302251881Speter { 1303251881Speter svn_merge_range_t *elt1, *elt2; 1304251881Speter 1305251881Speter elt1 = APR_ARRAY_IDX(rangelist1, i1, svn_merge_range_t *); 1306251881Speter 1307251881Speter /* Instead of making a copy of the entire array of rangelist2 1308251881Speter elements, we just keep a copy of the current rangelist2 element 1309251881Speter that needs to be used, and modify our copy if necessary. */ 1310251881Speter if (i2 != lasti2) 1311251881Speter { 1312251881Speter working_elt2 = 1313251881Speter *(APR_ARRAY_IDX(rangelist2, i2, svn_merge_range_t *)); 1314251881Speter lasti2 = i2; 1315251881Speter } 1316251881Speter 1317251881Speter elt2 = &working_elt2; 1318251881Speter 1319251881Speter /* If the rangelist2 range is contained completely in the 1320251881Speter rangelist1, we increment the rangelist2. 1321251881Speter If the ranges intersect, and match exactly, we increment both 1322251881Speter rangelist1 and rangelist2. 1323251881Speter Otherwise, we have to generate a range for the left part of 1324251881Speter the removal of rangelist1 from rangelist2, and possibly change 1325251881Speter the rangelist2 to the remaining portion of the right part of 1326251881Speter the removal, to test against. */ 1327251881Speter if (range_contains(elt1, elt2, consider_inheritance)) 1328251881Speter { 1329251881Speter if (!do_remove) 1330251881Speter { 1331251881Speter svn_merge_range_t tmp_range; 1332251881Speter tmp_range.start = elt2->start; 1333251881Speter tmp_range.end = elt2->end; 1334251881Speter /* The intersection of two ranges is non-inheritable only 1335251881Speter if both ranges are non-inheritable. */ 1336251881Speter tmp_range.inheritable = 1337251881Speter (elt2->inheritable || elt1->inheritable); 1338251881Speter SVN_ERR(combine_with_lastrange(&tmp_range, *output, 1339251881Speter consider_inheritance, 1340251881Speter pool)); 1341251881Speter } 1342251881Speter 1343251881Speter i2++; 1344251881Speter 1345251881Speter if (elt2->start == elt1->start && elt2->end == elt1->end) 1346251881Speter i1++; 1347251881Speter } 1348251881Speter else if (range_intersect(elt1, elt2, consider_inheritance)) 1349251881Speter { 1350251881Speter if (elt2->start < elt1->start) 1351251881Speter { 1352251881Speter /* The rangelist2 range starts before the rangelist1 range. */ 1353251881Speter svn_merge_range_t tmp_range; 1354251881Speter if (do_remove) 1355251881Speter { 1356251881Speter /* Retain the range that falls before the rangelist1 1357251881Speter start. */ 1358251881Speter tmp_range.start = elt2->start; 1359251881Speter tmp_range.end = elt1->start; 1360251881Speter tmp_range.inheritable = elt2->inheritable; 1361251881Speter } 1362251881Speter else 1363251881Speter { 1364251881Speter /* Retain the range that falls between the rangelist1 1365251881Speter start and rangelist2 end. */ 1366251881Speter tmp_range.start = elt1->start; 1367251881Speter tmp_range.end = MIN(elt2->end, elt1->end); 1368251881Speter /* The intersection of two ranges is non-inheritable only 1369251881Speter if both ranges are non-inheritable. */ 1370251881Speter tmp_range.inheritable = 1371251881Speter (elt2->inheritable || elt1->inheritable); 1372251881Speter } 1373251881Speter 1374251881Speter SVN_ERR(combine_with_lastrange(&tmp_range, 1375251881Speter *output, consider_inheritance, 1376251881Speter pool)); 1377251881Speter } 1378251881Speter 1379251881Speter /* Set up the rest of the rangelist2 range for further 1380251881Speter processing. */ 1381251881Speter if (elt2->end > elt1->end) 1382251881Speter { 1383251881Speter /* The rangelist2 range ends after the rangelist1 range. */ 1384251881Speter if (!do_remove) 1385251881Speter { 1386251881Speter /* Partial overlap. */ 1387251881Speter svn_merge_range_t tmp_range; 1388251881Speter tmp_range.start = MAX(elt2->start, elt1->start); 1389251881Speter tmp_range.end = elt1->end; 1390251881Speter /* The intersection of two ranges is non-inheritable only 1391251881Speter if both ranges are non-inheritable. */ 1392251881Speter tmp_range.inheritable = 1393251881Speter (elt2->inheritable || elt1->inheritable); 1394251881Speter SVN_ERR(combine_with_lastrange(&tmp_range, 1395251881Speter *output, 1396251881Speter consider_inheritance, 1397251881Speter pool)); 1398251881Speter } 1399251881Speter 1400251881Speter working_elt2.start = elt1->end; 1401251881Speter working_elt2.end = elt2->end; 1402251881Speter } 1403251881Speter else 1404251881Speter i2++; 1405251881Speter } 1406251881Speter else /* ranges don't intersect */ 1407251881Speter { 1408251881Speter /* See which side of the rangelist2 the rangelist1 is on. If it 1409251881Speter is on the left side, we need to move the rangelist1. 1410251881Speter 1411251881Speter If it is on past the rangelist2 on the right side, we 1412251881Speter need to output the rangelist2 and increment the 1413251881Speter rangelist2. */ 1414251881Speter if (svn_sort_compare_ranges(&elt1, &elt2) < 0) 1415251881Speter i1++; 1416251881Speter else 1417251881Speter { 1418251881Speter svn_merge_range_t *lastrange; 1419251881Speter 1420251881Speter if ((*output)->nelts > 0) 1421251881Speter lastrange = APR_ARRAY_IDX(*output, (*output)->nelts - 1, 1422251881Speter svn_merge_range_t *); 1423251881Speter else 1424251881Speter lastrange = NULL; 1425251881Speter 1426251881Speter if (do_remove && !(lastrange && 1427251881Speter combine_ranges(lastrange, lastrange, elt2, 1428251881Speter consider_inheritance))) 1429251881Speter { 1430251881Speter lastrange = svn_merge_range_dup(elt2, pool); 1431251881Speter APR_ARRAY_PUSH(*output, svn_merge_range_t *) = lastrange; 1432251881Speter } 1433251881Speter i2++; 1434251881Speter } 1435251881Speter } 1436251881Speter } 1437251881Speter 1438251881Speter if (do_remove) 1439251881Speter { 1440251881Speter /* Copy the current rangelist2 element if we didn't hit the end 1441251881Speter of the rangelist2, and we still had it around. This element 1442251881Speter may have been touched, so we can't just walk the rangelist2 1443251881Speter array, we have to use our copy. This case only happens when 1444251881Speter we ran out of rangelist1 before rangelist2, *and* we had changed 1445251881Speter the rangelist2 element. */ 1446251881Speter if (i2 == lasti2 && i2 < rangelist2->nelts) 1447251881Speter { 1448251881Speter SVN_ERR(combine_with_lastrange(&working_elt2, *output, 1449251881Speter consider_inheritance, pool)); 1450251881Speter i2++; 1451251881Speter } 1452251881Speter 1453251881Speter /* Copy any other remaining untouched rangelist2 elements. */ 1454251881Speter for (; i2 < rangelist2->nelts; i2++) 1455251881Speter { 1456251881Speter svn_merge_range_t *elt = APR_ARRAY_IDX(rangelist2, i2, 1457251881Speter svn_merge_range_t *); 1458251881Speter 1459251881Speter SVN_ERR(combine_with_lastrange(elt, *output, 1460251881Speter consider_inheritance, pool)); 1461251881Speter } 1462251881Speter } 1463251881Speter 1464251881Speter return SVN_NO_ERROR; 1465251881Speter} 1466251881Speter 1467251881Speter 1468251881Spetersvn_error_t * 1469251881Spetersvn_rangelist_intersect(svn_rangelist_t **output, 1470251881Speter const svn_rangelist_t *rangelist1, 1471251881Speter const svn_rangelist_t *rangelist2, 1472251881Speter svn_boolean_t consider_inheritance, 1473251881Speter apr_pool_t *pool) 1474251881Speter{ 1475251881Speter return rangelist_intersect_or_remove(output, rangelist1, rangelist2, FALSE, 1476251881Speter consider_inheritance, pool); 1477251881Speter} 1478251881Speter 1479251881Spetersvn_error_t * 1480251881Spetersvn_rangelist_remove(svn_rangelist_t **output, 1481251881Speter const svn_rangelist_t *eraser, 1482251881Speter const svn_rangelist_t *whiteboard, 1483251881Speter svn_boolean_t consider_inheritance, 1484251881Speter apr_pool_t *pool) 1485251881Speter{ 1486251881Speter return rangelist_intersect_or_remove(output, eraser, whiteboard, TRUE, 1487251881Speter consider_inheritance, pool); 1488251881Speter} 1489251881Speter 1490251881Spetersvn_error_t * 1491251881Spetersvn_rangelist_diff(svn_rangelist_t **deleted, svn_rangelist_t **added, 1492251881Speter const svn_rangelist_t *from, const svn_rangelist_t *to, 1493251881Speter svn_boolean_t consider_inheritance, 1494251881Speter apr_pool_t *pool) 1495251881Speter{ 1496251881Speter /* The following diagrams illustrate some common range delta scenarios: 1497251881Speter 1498251881Speter (from) deleted 1499251881Speter r0 <===========(=========)============[=========]===========> rHEAD 1500251881Speter [to] added 1501251881Speter 1502251881Speter (from) deleted deleted 1503251881Speter r0 <===========(=========[============]=========)===========> rHEAD 1504251881Speter [to] 1505251881Speter 1506251881Speter (from) deleted 1507251881Speter r0 <===========(=========[============)=========]===========> rHEAD 1508251881Speter [to] added 1509251881Speter 1510251881Speter (from) deleted 1511251881Speter r0 <===========[=========(============]=========)===========> rHEAD 1512251881Speter [to] added 1513251881Speter 1514251881Speter (from) 1515251881Speter r0 <===========[=========(============)=========]===========> rHEAD 1516251881Speter [to] added added 1517251881Speter 1518251881Speter (from) d d d 1519251881Speter r0 <===(=[=)=]=[==]=[=(=)=]=[=]=[=(===|===(=)==|=|==[=(=]=)=> rHEAD 1520251881Speter [to] a a a a a a a 1521251881Speter */ 1522251881Speter 1523251881Speter /* The items that are present in from, but not in to, must have been 1524251881Speter deleted. */ 1525251881Speter SVN_ERR(svn_rangelist_remove(deleted, to, from, consider_inheritance, 1526251881Speter pool)); 1527251881Speter /* The items that are present in to, but not in from, must have been 1528251881Speter added. */ 1529251881Speter return svn_rangelist_remove(added, from, to, consider_inheritance, pool); 1530251881Speter} 1531251881Speter 1532251881Speterstruct mergeinfo_diff_baton 1533251881Speter{ 1534251881Speter svn_mergeinfo_t from; 1535251881Speter svn_mergeinfo_t to; 1536251881Speter svn_mergeinfo_t deleted; 1537251881Speter svn_mergeinfo_t added; 1538251881Speter svn_boolean_t consider_inheritance; 1539251881Speter apr_pool_t *pool; 1540251881Speter}; 1541251881Speter 1542251881Speter/* This implements the 'svn_hash_diff_func_t' interface. 1543251881Speter BATON is of type 'struct mergeinfo_diff_baton *'. 1544251881Speter*/ 1545251881Speterstatic svn_error_t * 1546251881Spetermergeinfo_hash_diff_cb(const void *key, apr_ssize_t klen, 1547251881Speter enum svn_hash_diff_key_status status, 1548251881Speter void *baton) 1549251881Speter{ 1550251881Speter /* hash_a is FROM mergeinfo, 1551251881Speter hash_b is TO mergeinfo. */ 1552251881Speter struct mergeinfo_diff_baton *cb = baton; 1553251881Speter svn_rangelist_t *from_rangelist, *to_rangelist; 1554251881Speter const char *path = key; 1555251881Speter if (status == svn_hash_diff_key_both) 1556251881Speter { 1557251881Speter /* Record any deltas (additions or deletions). */ 1558251881Speter svn_rangelist_t *deleted_rangelist, *added_rangelist; 1559251881Speter from_rangelist = apr_hash_get(cb->from, path, klen); 1560251881Speter to_rangelist = apr_hash_get(cb->to, path, klen); 1561251881Speter SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist, 1562251881Speter from_rangelist, to_rangelist, 1563251881Speter cb->consider_inheritance, cb->pool)); 1564251881Speter if (cb->deleted && deleted_rangelist->nelts > 0) 1565251881Speter apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen), 1566251881Speter klen, deleted_rangelist); 1567251881Speter if (cb->added && added_rangelist->nelts > 0) 1568251881Speter apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen), 1569251881Speter klen, added_rangelist); 1570251881Speter } 1571251881Speter else if ((status == svn_hash_diff_key_a) && cb->deleted) 1572251881Speter { 1573251881Speter from_rangelist = apr_hash_get(cb->from, path, klen); 1574251881Speter apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen), klen, 1575251881Speter svn_rangelist_dup(from_rangelist, cb->pool)); 1576251881Speter } 1577251881Speter else if ((status == svn_hash_diff_key_b) && cb->added) 1578251881Speter { 1579251881Speter to_rangelist = apr_hash_get(cb->to, path, klen); 1580251881Speter apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen), klen, 1581251881Speter svn_rangelist_dup(to_rangelist, cb->pool)); 1582251881Speter } 1583251881Speter return SVN_NO_ERROR; 1584251881Speter} 1585251881Speter 1586251881Speter/* Record deletions and additions of entire range lists (by path 1587251881Speter presence), and delegate to svn_rangelist_diff() for delta 1588251881Speter calculations on a specific path. */ 1589251881Speterstatic svn_error_t * 1590251881Speterwalk_mergeinfo_hash_for_diff(svn_mergeinfo_t from, svn_mergeinfo_t to, 1591251881Speter svn_mergeinfo_t deleted, svn_mergeinfo_t added, 1592251881Speter svn_boolean_t consider_inheritance, 1593251881Speter apr_pool_t *result_pool, 1594251881Speter apr_pool_t *scratch_pool) 1595251881Speter{ 1596251881Speter struct mergeinfo_diff_baton mdb; 1597251881Speter mdb.from = from; 1598251881Speter mdb.to = to; 1599251881Speter mdb.deleted = deleted; 1600251881Speter mdb.added = added; 1601251881Speter mdb.consider_inheritance = consider_inheritance; 1602251881Speter mdb.pool = result_pool; 1603251881Speter 1604251881Speter return svn_hash_diff(from, to, mergeinfo_hash_diff_cb, &mdb, scratch_pool); 1605251881Speter} 1606251881Speter 1607251881Spetersvn_error_t * 1608251881Spetersvn_mergeinfo_diff2(svn_mergeinfo_t *deleted, svn_mergeinfo_t *added, 1609251881Speter svn_mergeinfo_t from, svn_mergeinfo_t to, 1610251881Speter svn_boolean_t consider_inheritance, 1611251881Speter apr_pool_t *result_pool, 1612251881Speter apr_pool_t *scratch_pool) 1613251881Speter{ 1614251881Speter if (from && to == NULL) 1615251881Speter { 1616251881Speter *deleted = svn_mergeinfo_dup(from, result_pool); 1617251881Speter *added = svn_hash__make(result_pool); 1618251881Speter } 1619251881Speter else if (from == NULL && to) 1620251881Speter { 1621251881Speter *deleted = svn_hash__make(result_pool); 1622251881Speter *added = svn_mergeinfo_dup(to, result_pool); 1623251881Speter } 1624251881Speter else 1625251881Speter { 1626251881Speter *deleted = svn_hash__make(result_pool); 1627251881Speter *added = svn_hash__make(result_pool); 1628251881Speter 1629251881Speter if (from && to) 1630251881Speter { 1631251881Speter SVN_ERR(walk_mergeinfo_hash_for_diff(from, to, *deleted, *added, 1632251881Speter consider_inheritance, 1633251881Speter result_pool, scratch_pool)); 1634251881Speter } 1635251881Speter } 1636251881Speter 1637251881Speter return SVN_NO_ERROR; 1638251881Speter} 1639251881Speter 1640251881Spetersvn_error_t * 1641251881Spetersvn_mergeinfo__equals(svn_boolean_t *is_equal, 1642251881Speter svn_mergeinfo_t info1, 1643251881Speter svn_mergeinfo_t info2, 1644251881Speter svn_boolean_t consider_inheritance, 1645251881Speter apr_pool_t *pool) 1646251881Speter{ 1647251881Speter apr_hash_index_t *hi; 1648251881Speter 1649251881Speter *is_equal = FALSE; 1650251881Speter 1651251881Speter /* special cases: at least one side has no merge info */ 1652251881Speter if (info1 == NULL && info2 == NULL) 1653251881Speter { 1654251881Speter *is_equal = TRUE; 1655251881Speter return SVN_NO_ERROR; 1656251881Speter } 1657251881Speter 1658251881Speter if (info1 == NULL || info2 == NULL) 1659251881Speter return SVN_NO_ERROR; 1660251881Speter 1661251881Speter /* trivial case: different number of paths -> unequal */ 1662251881Speter if (apr_hash_count(info1) != apr_hash_count(info2)) 1663251881Speter return SVN_NO_ERROR; 1664251881Speter 1665251881Speter /* compare range lists for all paths */ 1666251881Speter for (hi = apr_hash_first(pool, info1); hi; hi = apr_hash_next(hi)) 1667251881Speter { 1668251881Speter const char *key; 1669251881Speter apr_ssize_t key_length; 1670251881Speter svn_rangelist_t *lhs, *rhs; 1671251881Speter int i; 1672251881Speter svn_rangelist_t *deleted, *added; 1673251881Speter 1674251881Speter /* get both path lists */ 1675251881Speter apr_hash_this(hi, (const void**)&key, &key_length, (void **)&lhs); 1676251881Speter rhs = apr_hash_get(info2, key, key_length); 1677251881Speter 1678251881Speter /* missing on one side? */ 1679251881Speter if (rhs == NULL) 1680251881Speter return SVN_NO_ERROR; 1681251881Speter 1682251881Speter /* quick compare: the range lists will often be a perfect match */ 1683251881Speter if (lhs->nelts == rhs->nelts) 1684251881Speter { 1685251881Speter for (i = 0; i < lhs->nelts; ++i) 1686251881Speter { 1687251881Speter svn_merge_range_t *lrange 1688251881Speter = APR_ARRAY_IDX(lhs, i, svn_merge_range_t *); 1689251881Speter svn_merge_range_t *rrange 1690251881Speter = APR_ARRAY_IDX(rhs, i, svn_merge_range_t *); 1691251881Speter 1692251881Speter /* range mismatch? -> needs detailed comparison */ 1693251881Speter if ( lrange->start != rrange->start 1694251881Speter || lrange->end != rrange->end) 1695251881Speter break; 1696251881Speter 1697251881Speter /* inheritance mismatch? -> merge info differs */ 1698251881Speter if ( consider_inheritance 1699251881Speter && lrange->inheritable != rrange->inheritable) 1700251881Speter return SVN_NO_ERROR; 1701251881Speter } 1702251881Speter 1703251881Speter /* all ranges found to match -> next path */ 1704251881Speter if (i == lhs->nelts) 1705251881Speter continue; 1706251881Speter } 1707251881Speter 1708251881Speter /* range lists differ but there are many ways to sort and aggregate 1709251881Speter revisions into ranges. Do a full diff on them. */ 1710251881Speter SVN_ERR(svn_rangelist_diff(&deleted, &added, lhs, rhs, 1711251881Speter consider_inheritance, pool)); 1712251881Speter if (deleted->nelts || added->nelts) 1713251881Speter return SVN_NO_ERROR; 1714251881Speter } 1715251881Speter 1716251881Speter /* no mismatch found */ 1717251881Speter *is_equal = TRUE; 1718251881Speter return SVN_NO_ERROR; 1719251881Speter} 1720251881Speter 1721251881Spetersvn_error_t * 1722251881Spetersvn_mergeinfo_merge2(svn_mergeinfo_t mergeinfo, 1723251881Speter svn_mergeinfo_t changes, 1724251881Speter apr_pool_t *result_pool, 1725251881Speter apr_pool_t *scratch_pool) 1726251881Speter{ 1727251881Speter apr_hash_index_t *hi; 1728251881Speter apr_pool_t *iterpool; 1729251881Speter 1730251881Speter if (!apr_hash_count(changes)) 1731251881Speter return SVN_NO_ERROR; 1732251881Speter 1733251881Speter iterpool = svn_pool_create(scratch_pool); 1734251881Speter for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi)) 1735251881Speter { 1736251881Speter const char *key; 1737251881Speter apr_ssize_t klen; 1738251881Speter svn_rangelist_t *to_insert; 1739251881Speter svn_rangelist_t *target; 1740251881Speter 1741251881Speter /* get ranges to insert and the target ranges list of that insertion */ 1742251881Speter apr_hash_this(hi, (const void**)&key, &klen, (void*)&to_insert); 1743251881Speter target = apr_hash_get(mergeinfo, key, klen); 1744251881Speter 1745251881Speter /* if range list exists, just expand on it. 1746251881Speter * Otherwise, add new hash entry. */ 1747251881Speter if (target) 1748251881Speter { 1749251881Speter SVN_ERR(svn_rangelist_merge2(target, to_insert, result_pool, 1750251881Speter iterpool)); 1751251881Speter svn_pool_clear(iterpool); 1752251881Speter } 1753251881Speter else 1754251881Speter apr_hash_set(mergeinfo, key, klen, to_insert); 1755251881Speter } 1756251881Speter 1757251881Speter svn_pool_destroy(iterpool); 1758251881Speter 1759251881Speter return SVN_NO_ERROR; 1760251881Speter} 1761251881Speter 1762251881Spetersvn_error_t * 1763251881Spetersvn_mergeinfo_catalog_merge(svn_mergeinfo_catalog_t mergeinfo_cat, 1764251881Speter svn_mergeinfo_catalog_t changes_cat, 1765251881Speter apr_pool_t *result_pool, 1766251881Speter apr_pool_t *scratch_pool) 1767251881Speter{ 1768251881Speter int i = 0; 1769251881Speter int j = 0; 1770251881Speter apr_array_header_t *sorted_cat = 1771251881Speter svn_sort__hash(mergeinfo_cat, svn_sort_compare_items_as_paths, 1772251881Speter scratch_pool); 1773251881Speter apr_array_header_t *sorted_changes = 1774251881Speter svn_sort__hash(changes_cat, svn_sort_compare_items_as_paths, 1775251881Speter scratch_pool); 1776251881Speter 1777251881Speter while (i < sorted_cat->nelts && j < sorted_changes->nelts) 1778251881Speter { 1779251881Speter svn_sort__item_t cat_elt, change_elt; 1780251881Speter int res; 1781251881Speter 1782251881Speter cat_elt = APR_ARRAY_IDX(sorted_cat, i, svn_sort__item_t); 1783251881Speter change_elt = APR_ARRAY_IDX(sorted_changes, j, svn_sort__item_t); 1784251881Speter res = svn_sort_compare_items_as_paths(&cat_elt, &change_elt); 1785251881Speter 1786251881Speter if (res == 0) /* Both catalogs have mergeinfo for a given path. */ 1787251881Speter { 1788251881Speter svn_mergeinfo_t mergeinfo = cat_elt.value; 1789251881Speter svn_mergeinfo_t changes_mergeinfo = change_elt.value; 1790251881Speter 1791251881Speter SVN_ERR(svn_mergeinfo_merge2(mergeinfo, changes_mergeinfo, 1792251881Speter result_pool, scratch_pool)); 1793251881Speter apr_hash_set(mergeinfo_cat, cat_elt.key, cat_elt.klen, mergeinfo); 1794251881Speter i++; 1795251881Speter j++; 1796251881Speter } 1797251881Speter else if (res < 0) /* Only MERGEINFO_CAT has mergeinfo for this path. */ 1798251881Speter { 1799251881Speter i++; 1800251881Speter } 1801251881Speter else /* Only CHANGES_CAT has mergeinfo for this path. */ 1802251881Speter { 1803251881Speter apr_hash_set(mergeinfo_cat, 1804251881Speter apr_pstrdup(result_pool, change_elt.key), 1805251881Speter change_elt.klen, 1806251881Speter svn_mergeinfo_dup(change_elt.value, result_pool)); 1807251881Speter j++; 1808251881Speter } 1809251881Speter } 1810251881Speter 1811251881Speter /* Copy back any remaining elements from the CHANGES_CAT catalog. */ 1812251881Speter for (; j < sorted_changes->nelts; j++) 1813251881Speter { 1814251881Speter svn_sort__item_t elt = APR_ARRAY_IDX(sorted_changes, j, 1815251881Speter svn_sort__item_t); 1816251881Speter apr_hash_set(mergeinfo_cat, 1817251881Speter apr_pstrdup(result_pool, elt.key), 1818251881Speter elt.klen, 1819251881Speter svn_mergeinfo_dup(elt.value, result_pool)); 1820251881Speter } 1821251881Speter 1822251881Speter return SVN_NO_ERROR; 1823251881Speter} 1824251881Speter 1825251881Spetersvn_error_t * 1826251881Spetersvn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo, 1827251881Speter svn_mergeinfo_t mergeinfo1, 1828251881Speter svn_mergeinfo_t mergeinfo2, 1829251881Speter svn_boolean_t consider_inheritance, 1830251881Speter apr_pool_t *result_pool, 1831251881Speter apr_pool_t *scratch_pool) 1832251881Speter{ 1833251881Speter apr_hash_index_t *hi; 1834251881Speter apr_pool_t *iterpool; 1835251881Speter 1836251881Speter *mergeinfo = apr_hash_make(result_pool); 1837251881Speter iterpool = svn_pool_create(scratch_pool); 1838251881Speter 1839251881Speter /* ### TODO(reint): Do we care about the case when a path in one 1840251881Speter ### mergeinfo hash has inheritable mergeinfo, and in the other 1841251881Speter ### has non-inhertiable mergeinfo? It seems like that path 1842251881Speter ### itself should really be an intersection, while child paths 1843251881Speter ### should not be... */ 1844251881Speter for (hi = apr_hash_first(scratch_pool, mergeinfo1); 1845251881Speter hi; hi = apr_hash_next(hi)) 1846251881Speter { 1847251881Speter const char *path = svn__apr_hash_index_key(hi); 1848251881Speter svn_rangelist_t *rangelist1 = svn__apr_hash_index_val(hi); 1849251881Speter svn_rangelist_t *rangelist2; 1850251881Speter 1851251881Speter svn_pool_clear(iterpool); 1852251881Speter rangelist2 = svn_hash_gets(mergeinfo2, path); 1853251881Speter if (rangelist2) 1854251881Speter { 1855251881Speter SVN_ERR(svn_rangelist_intersect(&rangelist2, rangelist1, rangelist2, 1856251881Speter consider_inheritance, iterpool)); 1857251881Speter if (rangelist2->nelts > 0) 1858251881Speter svn_hash_sets(*mergeinfo, apr_pstrdup(result_pool, path), 1859251881Speter svn_rangelist_dup(rangelist2, result_pool)); 1860251881Speter } 1861251881Speter } 1862251881Speter svn_pool_destroy(iterpool); 1863251881Speter return SVN_NO_ERROR; 1864251881Speter} 1865251881Speter 1866251881Spetersvn_error_t * 1867251881Spetersvn_mergeinfo_remove2(svn_mergeinfo_t *mergeinfo, 1868251881Speter svn_mergeinfo_t eraser, 1869251881Speter svn_mergeinfo_t whiteboard, 1870251881Speter svn_boolean_t consider_inheritance, 1871251881Speter apr_pool_t *result_pool, 1872251881Speter apr_pool_t *scratch_pool) 1873251881Speter{ 1874251881Speter *mergeinfo = apr_hash_make(result_pool); 1875251881Speter return walk_mergeinfo_hash_for_diff(whiteboard, eraser, *mergeinfo, NULL, 1876251881Speter consider_inheritance, result_pool, 1877251881Speter scratch_pool); 1878251881Speter} 1879251881Speter 1880251881Spetersvn_error_t * 1881251881Spetersvn_rangelist_to_string(svn_string_t **output, 1882251881Speter const svn_rangelist_t *rangelist, 1883251881Speter apr_pool_t *pool) 1884251881Speter{ 1885251881Speter svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool); 1886251881Speter 1887251881Speter if (rangelist->nelts > 0) 1888251881Speter { 1889251881Speter int i; 1890251881Speter svn_merge_range_t *range; 1891251881Speter 1892251881Speter /* Handle the elements that need commas at the end. */ 1893251881Speter for (i = 0; i < rangelist->nelts - 1; i++) 1894251881Speter { 1895251881Speter range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 1896251881Speter svn_stringbuf_appendcstr(buf, range_to_string(range, pool)); 1897251881Speter svn_stringbuf_appendcstr(buf, ","); 1898251881Speter } 1899251881Speter 1900251881Speter /* Now handle the last element, which needs no comma. */ 1901251881Speter range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 1902251881Speter svn_stringbuf_appendcstr(buf, range_to_string(range, pool)); 1903251881Speter } 1904251881Speter 1905251881Speter *output = svn_stringbuf__morph_into_string(buf); 1906251881Speter 1907251881Speter return SVN_NO_ERROR; 1908251881Speter} 1909251881Speter 1910251881Speter/* Converts a mergeinfo INPUT to an unparsed mergeinfo in OUTPUT. If PREFIX 1911251881Speter is not NULL then prepend PREFIX to each line in OUTPUT. If INPUT contains 1912251881Speter no elements, return the empty string. If INPUT contains any merge source 1913251881Speter path keys that are relative then convert these to absolute paths in 1914251881Speter *OUTPUT. 1915251881Speter */ 1916251881Speterstatic svn_error_t * 1917251881Spetermergeinfo_to_stringbuf(svn_stringbuf_t **output, 1918251881Speter svn_mergeinfo_t input, 1919251881Speter const char *prefix, 1920251881Speter apr_pool_t *pool) 1921251881Speter{ 1922251881Speter *output = svn_stringbuf_create_empty(pool); 1923251881Speter 1924251881Speter if (apr_hash_count(input) > 0) 1925251881Speter { 1926251881Speter apr_array_header_t *sorted = 1927251881Speter svn_sort__hash(input, svn_sort_compare_items_as_paths, pool); 1928251881Speter int i; 1929251881Speter 1930251881Speter for (i = 0; i < sorted->nelts; i++) 1931251881Speter { 1932251881Speter svn_sort__item_t elt = APR_ARRAY_IDX(sorted, i, svn_sort__item_t); 1933251881Speter svn_string_t *revlist; 1934251881Speter 1935251881Speter SVN_ERR(svn_rangelist_to_string(&revlist, elt.value, pool)); 1936251881Speter svn_stringbuf_appendcstr( 1937251881Speter *output, 1938251881Speter apr_psprintf(pool, "%s%s%s:%s", 1939251881Speter prefix ? prefix : "", 1940251881Speter *((const char *) elt.key) == '/' ? "" : "/", 1941251881Speter (const char *) elt.key, 1942251881Speter revlist->data)); 1943251881Speter if (i < sorted->nelts - 1) 1944251881Speter svn_stringbuf_appendcstr(*output, "\n"); 1945251881Speter } 1946251881Speter } 1947251881Speter 1948251881Speter return SVN_NO_ERROR; 1949251881Speter} 1950251881Speter 1951251881Spetersvn_error_t * 1952251881Spetersvn_mergeinfo_to_string(svn_string_t **output, svn_mergeinfo_t input, 1953251881Speter apr_pool_t *pool) 1954251881Speter{ 1955251881Speter svn_stringbuf_t *mergeinfo_buf; 1956251881Speter 1957251881Speter SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_buf, input, NULL, pool)); 1958251881Speter *output = svn_stringbuf__morph_into_string(mergeinfo_buf); 1959251881Speter return SVN_NO_ERROR; 1960251881Speter} 1961251881Speter 1962251881Spetersvn_error_t * 1963251881Spetersvn_mergeinfo_sort(svn_mergeinfo_t input, apr_pool_t *pool) 1964251881Speter{ 1965251881Speter apr_hash_index_t *hi; 1966251881Speter 1967251881Speter for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi)) 1968251881Speter { 1969251881Speter apr_array_header_t *rl = svn__apr_hash_index_val(hi); 1970251881Speter 1971251881Speter qsort(rl->elts, rl->nelts, rl->elt_size, svn_sort_compare_ranges); 1972251881Speter } 1973251881Speter return SVN_NO_ERROR; 1974251881Speter} 1975251881Speter 1976251881Spetersvn_mergeinfo_catalog_t 1977251881Spetersvn_mergeinfo_catalog_dup(svn_mergeinfo_catalog_t mergeinfo_catalog, 1978251881Speter apr_pool_t *pool) 1979251881Speter{ 1980251881Speter svn_mergeinfo_t new_mergeinfo_catalog = apr_hash_make(pool); 1981251881Speter apr_hash_index_t *hi; 1982251881Speter 1983251881Speter for (hi = apr_hash_first(pool, mergeinfo_catalog); 1984251881Speter hi; 1985251881Speter hi = apr_hash_next(hi)) 1986251881Speter { 1987251881Speter const char *key = svn__apr_hash_index_key(hi); 1988251881Speter svn_mergeinfo_t val = svn__apr_hash_index_val(hi); 1989251881Speter 1990251881Speter svn_hash_sets(new_mergeinfo_catalog, apr_pstrdup(pool, key), 1991251881Speter svn_mergeinfo_dup(val, pool)); 1992251881Speter } 1993251881Speter 1994251881Speter return new_mergeinfo_catalog; 1995251881Speter} 1996251881Speter 1997251881Spetersvn_mergeinfo_t 1998251881Spetersvn_mergeinfo_dup(svn_mergeinfo_t mergeinfo, apr_pool_t *pool) 1999251881Speter{ 2000251881Speter svn_mergeinfo_t new_mergeinfo = svn_hash__make(pool); 2001251881Speter apr_hash_index_t *hi; 2002251881Speter 2003251881Speter for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2004251881Speter { 2005251881Speter const char *path = svn__apr_hash_index_key(hi); 2006251881Speter apr_ssize_t pathlen = svn__apr_hash_index_klen(hi); 2007251881Speter svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2008251881Speter 2009251881Speter apr_hash_set(new_mergeinfo, apr_pstrmemdup(pool, path, pathlen), pathlen, 2010251881Speter svn_rangelist_dup(rangelist, pool)); 2011251881Speter } 2012251881Speter 2013251881Speter return new_mergeinfo; 2014251881Speter} 2015251881Speter 2016251881Spetersvn_error_t * 2017251881Spetersvn_mergeinfo_inheritable2(svn_mergeinfo_t *output, 2018251881Speter svn_mergeinfo_t mergeinfo, 2019251881Speter const char *path, 2020251881Speter svn_revnum_t start, 2021251881Speter svn_revnum_t end, 2022251881Speter svn_boolean_t inheritable, 2023251881Speter apr_pool_t *result_pool, 2024251881Speter apr_pool_t *scratch_pool) 2025251881Speter{ 2026251881Speter apr_hash_index_t *hi; 2027251881Speter svn_mergeinfo_t inheritable_mergeinfo = apr_hash_make(result_pool); 2028251881Speter 2029251881Speter for (hi = apr_hash_first(scratch_pool, mergeinfo); 2030251881Speter hi; 2031251881Speter hi = apr_hash_next(hi)) 2032251881Speter { 2033251881Speter const char *key = svn__apr_hash_index_key(hi); 2034251881Speter apr_ssize_t keylen = svn__apr_hash_index_klen(hi); 2035251881Speter svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2036251881Speter svn_rangelist_t *inheritable_rangelist; 2037251881Speter 2038251881Speter if (!path || svn_path_compare_paths(path, key) == 0) 2039251881Speter SVN_ERR(svn_rangelist_inheritable2(&inheritable_rangelist, rangelist, 2040251881Speter start, end, inheritable, 2041251881Speter result_pool, scratch_pool)); 2042251881Speter else 2043251881Speter inheritable_rangelist = svn_rangelist_dup(rangelist, result_pool); 2044251881Speter 2045251881Speter /* Only add this rangelist if some ranges remain. A rangelist with 2046251881Speter a path mapped to an empty rangelist is not syntactically valid */ 2047251881Speter if (inheritable_rangelist->nelts) 2048251881Speter apr_hash_set(inheritable_mergeinfo, 2049251881Speter apr_pstrmemdup(result_pool, key, keylen), keylen, 2050251881Speter inheritable_rangelist); 2051251881Speter } 2052251881Speter *output = inheritable_mergeinfo; 2053251881Speter return SVN_NO_ERROR; 2054251881Speter} 2055251881Speter 2056251881Speter 2057251881Spetersvn_error_t * 2058251881Spetersvn_rangelist_inheritable2(svn_rangelist_t **inheritable_rangelist, 2059251881Speter const svn_rangelist_t *rangelist, 2060251881Speter svn_revnum_t start, 2061251881Speter svn_revnum_t end, 2062251881Speter svn_boolean_t inheritable, 2063251881Speter apr_pool_t *result_pool, 2064251881Speter apr_pool_t *scratch_pool) 2065251881Speter{ 2066251881Speter *inheritable_rangelist = apr_array_make(result_pool, 1, 2067251881Speter sizeof(svn_merge_range_t *)); 2068251881Speter if (rangelist->nelts) 2069251881Speter { 2070251881Speter if (!SVN_IS_VALID_REVNUM(start) 2071251881Speter || !SVN_IS_VALID_REVNUM(end) 2072251881Speter || end < start) 2073251881Speter { 2074251881Speter int i; 2075251881Speter /* We want all non-inheritable ranges removed. */ 2076251881Speter for (i = 0; i < rangelist->nelts; i++) 2077251881Speter { 2078251881Speter svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i, 2079251881Speter svn_merge_range_t *); 2080251881Speter if (range->inheritable == inheritable) 2081251881Speter { 2082251881Speter svn_merge_range_t *inheritable_range = 2083251881Speter apr_palloc(result_pool, sizeof(*inheritable_range)); 2084251881Speter inheritable_range->start = range->start; 2085251881Speter inheritable_range->end = range->end; 2086251881Speter inheritable_range->inheritable = TRUE; 2087251881Speter APR_ARRAY_PUSH(*inheritable_rangelist, 2088251881Speter svn_merge_range_t *) = range; 2089251881Speter } 2090251881Speter } 2091251881Speter } 2092251881Speter else 2093251881Speter { 2094251881Speter /* We want only the non-inheritable ranges bound by START 2095251881Speter and END removed. */ 2096251881Speter svn_rangelist_t *ranges_inheritable = 2097251881Speter svn_rangelist__initialize(start, end, inheritable, scratch_pool); 2098251881Speter 2099251881Speter if (rangelist->nelts) 2100251881Speter SVN_ERR(svn_rangelist_remove(inheritable_rangelist, 2101251881Speter ranges_inheritable, 2102251881Speter rangelist, 2103251881Speter TRUE, 2104251881Speter result_pool)); 2105251881Speter } 2106251881Speter } 2107251881Speter return SVN_NO_ERROR; 2108251881Speter} 2109251881Speter 2110251881Spetersvn_boolean_t 2111251881Spetersvn_mergeinfo__remove_empty_rangelists(svn_mergeinfo_t mergeinfo, 2112251881Speter apr_pool_t *pool) 2113251881Speter{ 2114251881Speter apr_hash_index_t *hi; 2115251881Speter svn_boolean_t removed_some_ranges = FALSE; 2116251881Speter 2117251881Speter if (mergeinfo) 2118251881Speter { 2119251881Speter for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2120251881Speter { 2121251881Speter const char *path = svn__apr_hash_index_key(hi); 2122251881Speter svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2123251881Speter 2124251881Speter if (rangelist->nelts == 0) 2125251881Speter { 2126251881Speter svn_hash_sets(mergeinfo, path, NULL); 2127251881Speter removed_some_ranges = TRUE; 2128251881Speter } 2129251881Speter } 2130251881Speter } 2131251881Speter return removed_some_ranges; 2132251881Speter} 2133251881Speter 2134251881Spetersvn_error_t * 2135251881Spetersvn_mergeinfo__remove_prefix_from_catalog(svn_mergeinfo_catalog_t *out_catalog, 2136251881Speter svn_mergeinfo_catalog_t in_catalog, 2137251881Speter const char *prefix_path, 2138251881Speter apr_pool_t *pool) 2139251881Speter{ 2140251881Speter apr_hash_index_t *hi; 2141251881Speter 2142251881Speter SVN_ERR_ASSERT(prefix_path[0] == '/'); 2143251881Speter 2144251881Speter *out_catalog = apr_hash_make(pool); 2145251881Speter 2146251881Speter for (hi = apr_hash_first(pool, in_catalog); hi; hi = apr_hash_next(hi)) 2147251881Speter { 2148251881Speter const char *original_path = svn__apr_hash_index_key(hi); 2149251881Speter svn_mergeinfo_t value = svn__apr_hash_index_val(hi); 2150251881Speter const char *new_path; 2151251881Speter 2152251881Speter new_path = svn_fspath__skip_ancestor(prefix_path, original_path); 2153251881Speter SVN_ERR_ASSERT(new_path); 2154251881Speter 2155251881Speter svn_hash_sets(*out_catalog, new_path, value); 2156251881Speter } 2157251881Speter 2158251881Speter return SVN_NO_ERROR; 2159251881Speter} 2160251881Speter 2161251881Spetersvn_error_t * 2162251881Spetersvn_mergeinfo__add_prefix_to_catalog(svn_mergeinfo_catalog_t *out_catalog, 2163251881Speter svn_mergeinfo_catalog_t in_catalog, 2164251881Speter const char *prefix_path, 2165251881Speter apr_pool_t *result_pool, 2166251881Speter apr_pool_t *scratch_pool) 2167251881Speter{ 2168251881Speter apr_hash_index_t *hi; 2169251881Speter 2170251881Speter *out_catalog = apr_hash_make(result_pool); 2171251881Speter 2172251881Speter for (hi = apr_hash_first(scratch_pool, in_catalog); 2173251881Speter hi; 2174251881Speter hi = apr_hash_next(hi)) 2175251881Speter { 2176251881Speter const char *original_path = svn__apr_hash_index_key(hi); 2177251881Speter svn_mergeinfo_t value = svn__apr_hash_index_val(hi); 2178251881Speter 2179251881Speter if (original_path[0] == '/') 2180251881Speter original_path++; 2181251881Speter 2182251881Speter svn_hash_sets(*out_catalog, 2183251881Speter svn_dirent_join(prefix_path, original_path, result_pool), 2184251881Speter value); 2185251881Speter } 2186251881Speter 2187251881Speter return SVN_NO_ERROR; 2188251881Speter} 2189251881Speter 2190251881Spetersvn_error_t * 2191251881Spetersvn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo, 2192251881Speter svn_mergeinfo_t mergeinfo, 2193251881Speter const char *suffix_relpath, 2194251881Speter apr_pool_t *result_pool, 2195251881Speter apr_pool_t *scratch_pool) 2196251881Speter{ 2197251881Speter apr_hash_index_t *hi; 2198251881Speter 2199251881Speter SVN_ERR_ASSERT(suffix_relpath && svn_relpath_is_canonical(suffix_relpath)); 2200251881Speter 2201251881Speter *out_mergeinfo = apr_hash_make(result_pool); 2202251881Speter 2203251881Speter for (hi = apr_hash_first(scratch_pool, mergeinfo); 2204251881Speter hi; 2205251881Speter hi = apr_hash_next(hi)) 2206251881Speter { 2207251881Speter const char *fspath = svn__apr_hash_index_key(hi); 2208251881Speter svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2209251881Speter 2210251881Speter svn_hash_sets(*out_mergeinfo, 2211251881Speter svn_fspath__join(fspath, suffix_relpath, result_pool), 2212251881Speter rangelist); 2213251881Speter } 2214251881Speter 2215251881Speter return SVN_NO_ERROR; 2216251881Speter} 2217251881Speter 2218251881Spetersvn_rangelist_t * 2219251881Spetersvn_rangelist_dup(const svn_rangelist_t *rangelist, apr_pool_t *pool) 2220251881Speter{ 2221251881Speter svn_rangelist_t *new_rl = apr_array_make(pool, rangelist->nelts, 2222251881Speter sizeof(svn_merge_range_t *)); 2223251881Speter 2224251881Speter /* allocate target range buffer with a single operation */ 2225251881Speter svn_merge_range_t *copy = apr_palloc(pool, sizeof(*copy) * rangelist->nelts); 2226251881Speter int i; 2227251881Speter 2228251881Speter /* fill it iteratively and link it into the range list */ 2229251881Speter for (i = 0; i < rangelist->nelts; i++) 2230251881Speter { 2231251881Speter memcpy(copy + i, 2232251881Speter APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *), 2233251881Speter sizeof(*copy)); 2234251881Speter APR_ARRAY_PUSH(new_rl, svn_merge_range_t *) = copy + i; 2235251881Speter } 2236251881Speter 2237251881Speter return new_rl; 2238251881Speter} 2239251881Speter 2240251881Spetersvn_merge_range_t * 2241251881Spetersvn_merge_range_dup(const svn_merge_range_t *range, apr_pool_t *pool) 2242251881Speter{ 2243251881Speter svn_merge_range_t *new_range = apr_palloc(pool, sizeof(*new_range)); 2244251881Speter memcpy(new_range, range, sizeof(*new_range)); 2245251881Speter return new_range; 2246251881Speter} 2247251881Speter 2248251881Spetersvn_boolean_t 2249251881Spetersvn_merge_range_contains_rev(const svn_merge_range_t *range, svn_revnum_t rev) 2250251881Speter{ 2251251881Speter assert(SVN_IS_VALID_REVNUM(range->start)); 2252251881Speter assert(SVN_IS_VALID_REVNUM(range->end)); 2253251881Speter assert(range->start != range->end); 2254251881Speter 2255251881Speter if (range->start < range->end) 2256251881Speter return rev > range->start && rev <= range->end; 2257251881Speter else 2258251881Speter return rev > range->end && rev <= range->start; 2259251881Speter} 2260251881Speter 2261251881Spetersvn_error_t * 2262251881Spetersvn_mergeinfo__catalog_to_formatted_string(svn_string_t **output, 2263251881Speter svn_mergeinfo_catalog_t catalog, 2264251881Speter const char *key_prefix, 2265251881Speter const char *val_prefix, 2266251881Speter apr_pool_t *pool) 2267251881Speter{ 2268251881Speter svn_stringbuf_t *output_buf = NULL; 2269251881Speter 2270251881Speter if (catalog && apr_hash_count(catalog)) 2271251881Speter { 2272251881Speter int i; 2273251881Speter apr_array_header_t *sorted_catalog = 2274251881Speter svn_sort__hash(catalog, svn_sort_compare_items_as_paths, pool); 2275251881Speter 2276251881Speter output_buf = svn_stringbuf_create_empty(pool); 2277251881Speter for (i = 0; i < sorted_catalog->nelts; i++) 2278251881Speter { 2279251881Speter svn_sort__item_t elt = 2280251881Speter APR_ARRAY_IDX(sorted_catalog, i, svn_sort__item_t); 2281251881Speter const char *path1; 2282251881Speter svn_mergeinfo_t mergeinfo; 2283251881Speter svn_stringbuf_t *mergeinfo_output_buf; 2284251881Speter 2285251881Speter path1 = elt.key; 2286251881Speter mergeinfo = elt.value; 2287251881Speter if (key_prefix) 2288251881Speter svn_stringbuf_appendcstr(output_buf, key_prefix); 2289251881Speter svn_stringbuf_appendcstr(output_buf, path1); 2290251881Speter svn_stringbuf_appendcstr(output_buf, "\n"); 2291251881Speter SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_output_buf, mergeinfo, 2292251881Speter val_prefix ? val_prefix : "", pool)); 2293251881Speter svn_stringbuf_appendstr(output_buf, mergeinfo_output_buf); 2294251881Speter svn_stringbuf_appendcstr(output_buf, "\n"); 2295251881Speter } 2296251881Speter } 2297251881Speter#if SVN_DEBUG 2298251881Speter else if (!catalog) 2299251881Speter { 2300251881Speter output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool); 2301251881Speter svn_stringbuf_appendcstr(output_buf, _("NULL mergeinfo catalog\n")); 2302251881Speter } 2303251881Speter else if (apr_hash_count(catalog) == 0) 2304251881Speter { 2305251881Speter output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool); 2306251881Speter svn_stringbuf_appendcstr(output_buf, _("empty mergeinfo catalog\n")); 2307251881Speter } 2308251881Speter#endif 2309251881Speter 2310251881Speter /* If we have an output_buf, convert it to an svn_string_t; 2311251881Speter otherwise, return a new string containing only a newline 2312251881Speter character. */ 2313251881Speter if (output_buf) 2314251881Speter *output = svn_stringbuf__morph_into_string(output_buf); 2315251881Speter else 2316251881Speter *output = svn_string_create("\n", pool); 2317251881Speter 2318251881Speter return SVN_NO_ERROR; 2319251881Speter} 2320251881Speter 2321251881Spetersvn_error_t * 2322251881Spetersvn_mergeinfo__get_range_endpoints(svn_revnum_t *youngest_rev, 2323251881Speter svn_revnum_t *oldest_rev, 2324251881Speter svn_mergeinfo_t mergeinfo, 2325251881Speter apr_pool_t *pool) 2326251881Speter{ 2327251881Speter *youngest_rev = *oldest_rev = SVN_INVALID_REVNUM; 2328251881Speter if (mergeinfo) 2329251881Speter { 2330251881Speter apr_hash_index_t *hi; 2331251881Speter 2332251881Speter for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2333251881Speter { 2334251881Speter svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2335251881Speter 2336251881Speter if (rangelist->nelts) 2337251881Speter { 2338251881Speter svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, 2339251881Speter rangelist->nelts - 1, 2340251881Speter svn_merge_range_t *); 2341251881Speter if (!SVN_IS_VALID_REVNUM(*youngest_rev) 2342251881Speter || (range->end > *youngest_rev)) 2343251881Speter *youngest_rev = range->end; 2344251881Speter 2345251881Speter range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *); 2346251881Speter if (!SVN_IS_VALID_REVNUM(*oldest_rev) 2347251881Speter || (range->start < *oldest_rev)) 2348251881Speter *oldest_rev = range->start; 2349251881Speter } 2350251881Speter } 2351251881Speter } 2352251881Speter return SVN_NO_ERROR; 2353251881Speter} 2354251881Speter 2355251881Spetersvn_error_t * 2356251881Spetersvn_mergeinfo__filter_catalog_by_ranges(svn_mergeinfo_catalog_t *filtered_cat, 2357251881Speter svn_mergeinfo_catalog_t catalog, 2358251881Speter svn_revnum_t youngest_rev, 2359251881Speter svn_revnum_t oldest_rev, 2360251881Speter svn_boolean_t include_range, 2361251881Speter apr_pool_t *result_pool, 2362251881Speter apr_pool_t *scratch_pool) 2363251881Speter{ 2364251881Speter apr_hash_index_t *hi; 2365251881Speter 2366251881Speter *filtered_cat = apr_hash_make(result_pool); 2367251881Speter for (hi = apr_hash_first(scratch_pool, catalog); 2368251881Speter hi; 2369251881Speter hi = apr_hash_next(hi)) 2370251881Speter { 2371251881Speter const char *path = svn__apr_hash_index_key(hi); 2372251881Speter svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi); 2373251881Speter svn_mergeinfo_t filtered_mergeinfo; 2374251881Speter 2375251881Speter SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&filtered_mergeinfo, 2376251881Speter mergeinfo, 2377251881Speter youngest_rev, 2378251881Speter oldest_rev, 2379251881Speter include_range, 2380251881Speter result_pool, 2381251881Speter scratch_pool)); 2382251881Speter if (apr_hash_count(filtered_mergeinfo)) 2383251881Speter svn_hash_sets(*filtered_cat, 2384251881Speter apr_pstrdup(result_pool, path), filtered_mergeinfo); 2385251881Speter } 2386251881Speter 2387251881Speter return SVN_NO_ERROR; 2388251881Speter} 2389251881Speter 2390251881Spetersvn_error_t * 2391251881Spetersvn_mergeinfo__filter_mergeinfo_by_ranges(svn_mergeinfo_t *filtered_mergeinfo, 2392251881Speter svn_mergeinfo_t mergeinfo, 2393251881Speter svn_revnum_t youngest_rev, 2394251881Speter svn_revnum_t oldest_rev, 2395251881Speter svn_boolean_t include_range, 2396251881Speter apr_pool_t *result_pool, 2397251881Speter apr_pool_t *scratch_pool) 2398251881Speter{ 2399251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); 2400251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev)); 2401251881Speter SVN_ERR_ASSERT(oldest_rev < youngest_rev); 2402251881Speter 2403251881Speter *filtered_mergeinfo = apr_hash_make(result_pool); 2404251881Speter 2405251881Speter if (mergeinfo) 2406251881Speter { 2407251881Speter apr_hash_index_t *hi; 2408251881Speter svn_rangelist_t *filter_rangelist = 2409251881Speter svn_rangelist__initialize(oldest_rev, youngest_rev, TRUE, 2410251881Speter scratch_pool); 2411251881Speter 2412251881Speter for (hi = apr_hash_first(scratch_pool, mergeinfo); 2413251881Speter hi; 2414251881Speter hi = apr_hash_next(hi)) 2415251881Speter { 2416251881Speter const char *path = svn__apr_hash_index_key(hi); 2417251881Speter svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2418251881Speter 2419251881Speter if (rangelist->nelts) 2420251881Speter { 2421251881Speter svn_rangelist_t *new_rangelist; 2422251881Speter 2423251881Speter SVN_ERR(rangelist_intersect_or_remove( 2424251881Speter &new_rangelist, filter_rangelist, rangelist, 2425251881Speter ! include_range, FALSE, result_pool)); 2426251881Speter 2427251881Speter if (new_rangelist->nelts) 2428251881Speter svn_hash_sets(*filtered_mergeinfo, 2429251881Speter apr_pstrdup(result_pool, path), new_rangelist); 2430251881Speter } 2431251881Speter } 2432251881Speter } 2433251881Speter return SVN_NO_ERROR; 2434251881Speter} 2435251881Speter 2436251881Spetersvn_error_t * 2437251881Spetersvn_mergeinfo__adjust_mergeinfo_rangelists(svn_mergeinfo_t *adjusted_mergeinfo, 2438251881Speter svn_mergeinfo_t mergeinfo, 2439251881Speter svn_revnum_t offset, 2440251881Speter apr_pool_t *result_pool, 2441251881Speter apr_pool_t *scratch_pool) 2442251881Speter{ 2443251881Speter apr_hash_index_t *hi; 2444251881Speter *adjusted_mergeinfo = apr_hash_make(result_pool); 2445251881Speter 2446251881Speter if (mergeinfo) 2447251881Speter { 2448251881Speter for (hi = apr_hash_first(scratch_pool, mergeinfo); 2449251881Speter hi; 2450251881Speter hi = apr_hash_next(hi)) 2451251881Speter { 2452251881Speter int i; 2453251881Speter const char *path = svn__apr_hash_index_key(hi); 2454251881Speter svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2455251881Speter svn_rangelist_t *adjusted_rangelist = 2456251881Speter apr_array_make(result_pool, rangelist->nelts, 2457251881Speter sizeof(svn_merge_range_t *)); 2458251881Speter 2459251881Speter for (i = 0; i < rangelist->nelts; i++) 2460251881Speter { 2461251881Speter svn_merge_range_t *range = 2462251881Speter APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 2463251881Speter 2464251881Speter if (range->start + offset > 0 && range->end + offset > 0) 2465251881Speter { 2466251881Speter if (range->start + offset < 0) 2467251881Speter range->start = 0; 2468251881Speter else 2469251881Speter range->start = range->start + offset; 2470251881Speter 2471251881Speter if (range->end + offset < 0) 2472251881Speter range->end = 0; 2473251881Speter else 2474251881Speter range->end = range->end + offset; 2475251881Speter APR_ARRAY_PUSH(adjusted_rangelist, svn_merge_range_t *) = 2476251881Speter range; 2477251881Speter } 2478251881Speter } 2479251881Speter 2480251881Speter if (adjusted_rangelist->nelts) 2481251881Speter svn_hash_sets(*adjusted_mergeinfo, apr_pstrdup(result_pool, path), 2482251881Speter adjusted_rangelist); 2483251881Speter } 2484251881Speter } 2485251881Speter return SVN_NO_ERROR; 2486251881Speter} 2487251881Speter 2488251881Spetersvn_boolean_t 2489251881Spetersvn_mergeinfo__is_noninheritable(svn_mergeinfo_t mergeinfo, 2490251881Speter apr_pool_t *scratch_pool) 2491251881Speter{ 2492251881Speter if (mergeinfo) 2493251881Speter { 2494251881Speter apr_hash_index_t *hi; 2495251881Speter 2496251881Speter for (hi = apr_hash_first(scratch_pool, mergeinfo); 2497251881Speter hi; 2498251881Speter hi = apr_hash_next(hi)) 2499251881Speter { 2500251881Speter svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2501251881Speter int i; 2502251881Speter 2503251881Speter for (i = 0; i < rangelist->nelts; i++) 2504251881Speter { 2505251881Speter svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i, 2506251881Speter svn_merge_range_t *); 2507251881Speter if (!range->inheritable) 2508251881Speter return TRUE; 2509251881Speter } 2510251881Speter } 2511251881Speter } 2512251881Speter return FALSE; 2513251881Speter} 2514251881Speter 2515251881Spetersvn_rangelist_t * 2516251881Spetersvn_rangelist__initialize(svn_revnum_t start, 2517251881Speter svn_revnum_t end, 2518251881Speter svn_boolean_t inheritable, 2519251881Speter apr_pool_t *result_pool) 2520251881Speter{ 2521251881Speter svn_rangelist_t *rangelist = 2522251881Speter apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *)); 2523251881Speter svn_merge_range_t *range = apr_pcalloc(result_pool, sizeof(*range)); 2524251881Speter 2525251881Speter range->start = start; 2526251881Speter range->end = end; 2527251881Speter range->inheritable = inheritable; 2528251881Speter APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range; 2529251881Speter return rangelist; 2530251881Speter} 2531251881Speter 2532251881Spetersvn_error_t * 2533251881Spetersvn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p, 2534251881Speter const apr_array_header_t *segments, 2535251881Speter apr_pool_t *pool) 2536251881Speter{ 2537251881Speter svn_mergeinfo_t mergeinfo = apr_hash_make(pool); 2538251881Speter int i; 2539251881Speter 2540251881Speter /* Translate location segments into merge sources and ranges. */ 2541251881Speter for (i = 0; i < segments->nelts; i++) 2542251881Speter { 2543251881Speter svn_location_segment_t *segment = 2544251881Speter APR_ARRAY_IDX(segments, i, svn_location_segment_t *); 2545251881Speter svn_rangelist_t *path_ranges; 2546251881Speter svn_merge_range_t *range; 2547251881Speter const char *source_path; 2548251881Speter 2549251881Speter /* No path segment? Skip it. */ 2550251881Speter if (! segment->path) 2551251881Speter continue; 2552251881Speter 2553251881Speter /* Prepend a leading slash to our path. */ 2554251881Speter source_path = apr_pstrcat(pool, "/", segment->path, (char *)NULL); 2555251881Speter 2556251881Speter /* See if we already stored ranges for this path. If not, make 2557251881Speter a new list. */ 2558251881Speter path_ranges = svn_hash_gets(mergeinfo, source_path); 2559251881Speter if (! path_ranges) 2560251881Speter path_ranges = apr_array_make(pool, 1, sizeof(range)); 2561251881Speter 2562251881Speter /* A svn_location_segment_t may have legitimately describe only 2563251881Speter revision 0, but there is no corresponding representation for 2564251881Speter this in a svn_merge_range_t. */ 2565251881Speter if (segment->range_start == 0 && segment->range_end == 0) 2566251881Speter continue; 2567251881Speter 2568251881Speter /* Build a merge range, push it onto the list of ranges, and for 2569251881Speter good measure, (re)store it in the hash. */ 2570251881Speter range = apr_pcalloc(pool, sizeof(*range)); 2571251881Speter range->start = MAX(segment->range_start - 1, 0); 2572251881Speter range->end = segment->range_end; 2573251881Speter range->inheritable = TRUE; 2574251881Speter APR_ARRAY_PUSH(path_ranges, svn_merge_range_t *) = range; 2575251881Speter svn_hash_sets(mergeinfo, source_path, path_ranges); 2576251881Speter } 2577251881Speter 2578251881Speter *mergeinfo_p = mergeinfo; 2579251881Speter return SVN_NO_ERROR; 2580251881Speter} 2581251881Speter 2582251881Spetersvn_error_t * 2583251881Spetersvn_rangelist__merge_many(svn_rangelist_t *merged_rangelist, 2584251881Speter svn_mergeinfo_t merge_history, 2585251881Speter apr_pool_t *result_pool, 2586251881Speter apr_pool_t *scratch_pool) 2587251881Speter{ 2588251881Speter if (apr_hash_count(merge_history)) 2589251881Speter { 2590251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2591251881Speter apr_hash_index_t *hi; 2592251881Speter 2593251881Speter for (hi = apr_hash_first(scratch_pool, merge_history); 2594251881Speter hi; 2595251881Speter hi = apr_hash_next(hi)) 2596251881Speter { 2597251881Speter svn_rangelist_t *subtree_rangelist = svn__apr_hash_index_val(hi); 2598251881Speter 2599251881Speter svn_pool_clear(iterpool); 2600251881Speter SVN_ERR(svn_rangelist_merge2(merged_rangelist, subtree_rangelist, 2601251881Speter result_pool, iterpool)); 2602251881Speter } 2603251881Speter svn_pool_destroy(iterpool); 2604251881Speter } 2605251881Speter return SVN_NO_ERROR; 2606251881Speter} 2607251881Speter 2608251881Speter 2609251881Speterconst char * 2610251881Spetersvn_inheritance_to_word(svn_mergeinfo_inheritance_t inherit) 2611251881Speter{ 2612251881Speter switch (inherit) 2613251881Speter { 2614251881Speter case svn_mergeinfo_inherited: 2615251881Speter return "inherited"; 2616251881Speter case svn_mergeinfo_nearest_ancestor: 2617251881Speter return "nearest-ancestor"; 2618251881Speter default: 2619251881Speter return "explicit"; 2620251881Speter } 2621251881Speter} 2622251881Speter 2623251881Spetersvn_mergeinfo_inheritance_t 2624251881Spetersvn_inheritance_from_word(const char *word) 2625251881Speter{ 2626251881Speter if (strcmp(word, "inherited") == 0) 2627251881Speter return svn_mergeinfo_inherited; 2628251881Speter if (strcmp(word, "nearest-ancestor") == 0) 2629251881Speter return svn_mergeinfo_nearest_ancestor; 2630251881Speter return svn_mergeinfo_explicit; 2631251881Speter} 2632