1/* 2 * mergeinfo.c: Mergeinfo parsing and handling 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23#include <assert.h> 24#include <ctype.h> 25 26#include "svn_path.h" 27#include "svn_types.h" 28#include "svn_ctype.h" 29#include "svn_pools.h" 30#include "svn_sorts.h" 31#include "svn_error.h" 32#include "svn_error_codes.h" 33#include "svn_string.h" 34#include "svn_mergeinfo.h" 35#include "private/svn_fspath.h" 36#include "private/svn_mergeinfo_private.h" 37#include "private/svn_string_private.h" 38#include "private/svn_subr_private.h" 39#include "svn_private_config.h" 40#include "svn_hash.h" 41#include "private/svn_dep_compat.h" 42 43/* Attempt to combine two ranges, IN1 and IN2. If they are adjacent or 44 overlapping, and their inheritability allows them to be combined, put 45 the result in OUTPUT and return TRUE, otherwise return FALSE. 46 47 CONSIDER_INHERITANCE determines how to account for the inheritability 48 of IN1 and IN2 when trying to combine ranges. If ranges with different 49 inheritability are combined (CONSIDER_INHERITANCE must be FALSE for this 50 to happen) the result is inheritable. If both ranges are inheritable the 51 result is inheritable. Only and if both ranges are non-inheritable is 52 the result is non-inheritable. 53 54 Range overlapping detection algorithm from 55 http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap 56*/ 57static svn_boolean_t 58combine_ranges(svn_merge_range_t *output, 59 const svn_merge_range_t *in1, 60 const svn_merge_range_t *in2, 61 svn_boolean_t consider_inheritance) 62{ 63 if (in1->start <= in2->end && in2->start <= in1->end) 64 { 65 if (!consider_inheritance 66 || (consider_inheritance 67 && (in1->inheritable == in2->inheritable))) 68 { 69 output->start = MIN(in1->start, in2->start); 70 output->end = MAX(in1->end, in2->end); 71 output->inheritable = (in1->inheritable || in2->inheritable); 72 return TRUE; 73 } 74 } 75 return FALSE; 76} 77 78/* pathname -> PATHNAME */ 79static svn_error_t * 80parse_pathname(const char **input, 81 const char *end, 82 const char **pathname, 83 apr_pool_t *pool) 84{ 85 const char *curr = *input; 86 const char *last_colon = NULL; 87 88 /* A pathname may contain colons, so find the last colon before END 89 or newline. We'll consider this the divider between the pathname 90 and the revisionlist. */ 91 while (curr < end && *curr != '\n') 92 { 93 if (*curr == ':') 94 last_colon = curr; 95 curr++; 96 } 97 98 if (!last_colon) 99 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 100 _("Pathname not terminated by ':'")); 101 if (last_colon == *input) 102 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 103 _("No pathname preceding ':'")); 104 105 /* Tolerate relative repository paths, but convert them to absolute. 106 ### Efficiency? 1 string duplication here, 2 in canonicalize. */ 107 *pathname = svn_fspath__canonicalize(apr_pstrndup(pool, *input, 108 last_colon - *input), 109 pool); 110 111 *input = last_colon; 112 113 return SVN_NO_ERROR; 114} 115 116/* Return TRUE iff (svn_merge_range_t *) RANGE describes a valid, forward 117 * revision range. 118 * 119 * Note: The smallest valid value of RANGE->start is 0 because it is an 120 * exclusive endpoint, being one less than the revision number of the first 121 * change described by the range, and the oldest possible change is "r1" as 122 * there cannot be a change "r0". */ 123#define IS_VALID_FORWARD_RANGE(range) \ 124 (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end)) 125 126/* Ways in which two svn_merge_range_t can intersect or adjoin, if at all. */ 127typedef enum intersection_type_t 128{ 129 /* Ranges don't intersect and don't adjoin. */ 130 svn__no_intersection, 131 132 /* Ranges are equal. */ 133 svn__equal_intersection, 134 135 /* Ranges adjoin but don't overlap. */ 136 svn__adjoining_intersection, 137 138 /* Ranges overlap but neither is a subset of the other. */ 139 svn__overlapping_intersection, 140 141 /* One range is a proper subset of the other. */ 142 svn__proper_subset_intersection 143} intersection_type_t; 144 145/* Given ranges R1 and R2, both of which must be forward merge ranges, 146 set *INTERSECTION_TYPE to describe how the ranges intersect, if they 147 do at all. The inheritance type of the ranges is not considered. */ 148static svn_error_t * 149get_type_of_intersection(const svn_merge_range_t *r1, 150 const svn_merge_range_t *r2, 151 intersection_type_t *intersection_type) 152{ 153 SVN_ERR_ASSERT(r1); 154 SVN_ERR_ASSERT(r2); 155 SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r1)); 156 SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r2)); 157 158 if (!(r1->start <= r2->end && r2->start <= r1->end)) 159 *intersection_type = svn__no_intersection; 160 else if (r1->start == r2->start && r1->end == r2->end) 161 *intersection_type = svn__equal_intersection; 162 else if (r1->end == r2->start || r2->end == r1->start) 163 *intersection_type = svn__adjoining_intersection; 164 else if (r1->start <= r2->start && r1->end >= r2->end) 165 *intersection_type = svn__proper_subset_intersection; 166 else if (r2->start <= r1->start && r2->end >= r1->end) 167 *intersection_type = svn__proper_subset_intersection; 168 else 169 *intersection_type = svn__overlapping_intersection; 170 171 return SVN_NO_ERROR; 172} 173 174/* Modify or extend RANGELIST (a list of merge ranges) to incorporate 175 NEW_RANGE. RANGELIST is a "rangelist" as defined in svn_mergeinfo.h. 176 177 OVERVIEW 178 179 Determine the minimal set of non-overlapping merge ranges required to 180 represent the combination of RANGELIST and NEW_RANGE. The result depends 181 on whether and how NEW_RANGE overlaps any merge range[*] in RANGELIST, 182 and also on any differences in the inheritability of each range, 183 according to the rules described below. Modify RANGELIST to represent 184 this result, by adjusting the last range in it and/or appending one or 185 two more ranges. 186 187 ([*] Due to the simplifying assumption below, only the last range in 188 RANGELIST is considered.) 189 190 DETAILS 191 192 If RANGELIST is not empty assume NEW_RANGE does not intersect with any 193 range before the last one in RANGELIST. 194 195 If RANGELIST is empty or NEW_RANGE does not intersect with the lastrange 196 in RANGELIST, then append a copy of NEW_RANGE, allocated in RESULT_POOL, 197 to RANGELIST. 198 199 If NEW_RANGE intersects with the last range in RANGELIST then combine 200 these two ranges as described below: 201 202 If the intersecting ranges have the same inheritability then simply 203 combine the ranges in place. Otherwise, if the ranges intersect but 204 differ in inheritability, then merge the ranges as dictated by 205 CONSIDER_INHERITANCE: 206 207 If CONSIDER_INHERITANCE is false then intersecting ranges are combined 208 into a single range. The inheritability of the resulting range is 209 non-inheritable *only* if both ranges are non-inheritable, otherwise the 210 combined range is inheritable, e.g.: 211 212 Last range in NEW_RANGE RESULTING RANGES 213 RANGELIST 214 ------------- --------- ---------------- 215 4-10* 6-13 4-13 216 4-10 6-13* 4-13 217 4-10* 6-13* 4-13* 218 219 If CONSIDER_INHERITANCE is true, then only the intersection between the 220 two ranges is combined, with the inheritability of the resulting range 221 non-inheritable only if both ranges were non-inheritable. The 222 non-intersecting portions are added as separate ranges allocated in 223 RESULT_POOL, e.g.: 224 225 Last range in NEW_RANGE RESULTING RANGES 226 RANGELIST 227 ------------- --------- ---------------- 228 4-10* 6 4-5*, 6, 7-10* 229 4-10* 6-12 4-5*, 6-12 230 231 Note that the standard rules for rangelists still apply and overlapping 232 ranges are not allowed. So if the above would result in overlapping 233 ranges of the same inheritance, the overlapping ranges are merged into a 234 single range, e.g.: 235 236 Last range in NEW_RANGE RESULTING RANGES 237 RANGELIST 238 ------------- --------- ---------------- 239 4-10 6* 4-10 (Not 4-5, 6, 7-10) 240 241 When replacing the last range in RANGELIST, either allocate a new range in 242 RESULT_POOL or modify the existing range in place. Any new ranges added 243 to RANGELIST are allocated in RESULT_POOL. 244*/ 245static svn_error_t * 246combine_with_lastrange(const svn_merge_range_t *new_range, 247 svn_rangelist_t *rangelist, 248 svn_boolean_t consider_inheritance, 249 apr_pool_t *result_pool) 250{ 251 svn_merge_range_t *lastrange; 252 svn_merge_range_t combined_range; 253 254 /* We don't accept a NULL RANGELIST. */ 255 SVN_ERR_ASSERT(rangelist); 256 257 if (rangelist->nelts > 0) 258 lastrange = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1, svn_merge_range_t *); 259 else 260 lastrange = NULL; 261 262 if (!lastrange) 263 { 264 /* No *LASTRANGE so push NEW_RANGE onto RANGELIST and we are done. */ 265 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = 266 svn_merge_range_dup(new_range, result_pool); 267 } 268 else if (!consider_inheritance) 269 { 270 /* We are not considering inheritance so we can merge intersecting 271 ranges of different inheritability. Of course if the ranges 272 don't intersect at all we simply push NEW_RANGE only RANGELIST. */ 273 if (combine_ranges(&combined_range, lastrange, new_range, FALSE)) 274 { 275 *lastrange = combined_range; 276 } 277 else 278 { 279 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = 280 svn_merge_range_dup(new_range, result_pool); 281 } 282 } 283 else /* Considering inheritance */ 284 { 285 if (combine_ranges(&combined_range, lastrange, new_range, TRUE)) 286 { 287 /* Even when considering inheritance two intersection ranges 288 of the same inheritability can simply be combined. */ 289 *lastrange = combined_range; 290 } 291 else 292 { 293 /* If we are here then the ranges either don't intersect or do 294 intersect but have differing inheritability. Check for the 295 first case as that is easy to handle. */ 296 intersection_type_t intersection_type; 297 svn_boolean_t sorted = FALSE; 298 299 SVN_ERR(get_type_of_intersection(new_range, lastrange, 300 &intersection_type)); 301 302 switch (intersection_type) 303 { 304 case svn__no_intersection: 305 /* NEW_RANGE and *LASTRANGE *really* don't intersect so 306 just push NEW_RANGE only RANGELIST. */ 307 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = 308 svn_merge_range_dup(new_range, result_pool); 309 sorted = (svn_sort_compare_ranges(&lastrange, 310 &new_range) < 0); 311 break; 312 313 case svn__equal_intersection: 314 /* They range are equal so all we do is force the 315 inheritability of lastrange to true. */ 316 lastrange->inheritable = TRUE; 317 sorted = TRUE; 318 break; 319 320 case svn__adjoining_intersection: 321 /* They adjoin but don't overlap so just push NEW_RANGE 322 onto RANGELIST. */ 323 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = 324 svn_merge_range_dup(new_range, result_pool); 325 sorted = (svn_sort_compare_ranges(&lastrange, 326 &new_range) < 0); 327 break; 328 329 case svn__overlapping_intersection: 330 /* They ranges overlap but neither is a proper subset of 331 the other. We'll end up pusing two new ranges onto 332 RANGELIST, the intersecting part and the part unique to 333 NEW_RANGE.*/ 334 { 335 svn_merge_range_t *r1 = svn_merge_range_dup(lastrange, 336 result_pool); 337 svn_merge_range_t *r2 = svn_merge_range_dup(new_range, 338 result_pool); 339 340 /* Pop off *LASTRANGE to make our manipulations 341 easier. */ 342 apr_array_pop(rangelist); 343 344 /* Ensure R1 is the older range. */ 345 if (r2->start < r1->start) 346 { 347 /* Swap R1 and R2. */ 348 *r2 = *r1; 349 *r1 = *new_range; 350 } 351 352 /* Absorb the intersecting ranges into the 353 inheritable range. */ 354 if (r1->inheritable) 355 r2->start = r1->end; 356 else 357 r1->end = r2->start; 358 359 /* Push everything back onto RANGELIST. */ 360 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1; 361 sorted = (svn_sort_compare_ranges(&lastrange, 362 &r1) < 0); 363 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2; 364 if (sorted) 365 sorted = (svn_sort_compare_ranges(&r1, &r2) < 0); 366 break; 367 } 368 369 default: /* svn__proper_subset_intersection */ 370 { 371 /* One range is a proper subset of the other. */ 372 svn_merge_range_t *r1 = svn_merge_range_dup(lastrange, 373 result_pool); 374 svn_merge_range_t *r2 = svn_merge_range_dup(new_range, 375 result_pool); 376 svn_merge_range_t *r3 = NULL; 377 378 /* Pop off *LASTRANGE to make our manipulations 379 easier. */ 380 apr_array_pop(rangelist); 381 382 /* Ensure R1 is the superset. */ 383 if (r2->start < r1->start || r2->end > r1->end) 384 { 385 /* Swap R1 and R2. */ 386 *r2 = *r1; 387 *r1 = *new_range; 388 } 389 390 if (r1->inheritable) 391 { 392 /* The simple case: The superset is inheritable, so 393 just combine r1 and r2. */ 394 r1->start = MIN(r1->start, r2->start); 395 r1->end = MAX(r1->end, r2->end); 396 r2 = NULL; 397 } 398 else if (r1->start == r2->start) 399 { 400 svn_revnum_t tmp_revnum; 401 402 /* *LASTRANGE and NEW_RANGE share an end point. */ 403 tmp_revnum = r1->end; 404 r1->end = r2->end; 405 r2->inheritable = r1->inheritable; 406 r1->inheritable = TRUE; 407 r2->start = r1->end; 408 r2->end = tmp_revnum; 409 } 410 else if (r1->end == r2->end) 411 { 412 /* *LASTRANGE and NEW_RANGE share an end point. */ 413 r1->end = r2->start; 414 r2->inheritable = TRUE; 415 } 416 else 417 { 418 /* NEW_RANGE and *LASTRANGE share neither start 419 nor end points. */ 420 r3 = apr_pcalloc(result_pool, sizeof(*r3)); 421 r3->start = r2->end; 422 r3->end = r1->end; 423 r3->inheritable = r1->inheritable; 424 r2->inheritable = TRUE; 425 r1->end = r2->start; 426 } 427 428 /* Push everything back onto RANGELIST. */ 429 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1; 430 sorted = (svn_sort_compare_ranges(&lastrange, &r1) < 0); 431 if (r2) 432 { 433 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2; 434 if (sorted) 435 sorted = (svn_sort_compare_ranges(&r1, &r2) < 0); 436 } 437 if (r3) 438 { 439 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r3; 440 if (sorted) 441 { 442 if (r2) 443 sorted = (svn_sort_compare_ranges(&r2, 444 &r3) < 0); 445 else 446 sorted = (svn_sort_compare_ranges(&r1, 447 &r3) < 0); 448 } 449 } 450 break; 451 } 452 } 453 454 /* Some of the above cases might have put *RANGELIST out of 455 order, so re-sort.*/ 456 if (!sorted) 457 qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size, 458 svn_sort_compare_ranges); 459 } 460 } 461 462 return SVN_NO_ERROR; 463} 464 465/* Convert a single svn_merge_range_t *RANGE back into a string. */ 466static char * 467range_to_string(const svn_merge_range_t *range, 468 apr_pool_t *pool) 469{ 470 const char *mark 471 = range->inheritable ? "" : SVN_MERGEINFO_NONINHERITABLE_STR; 472 473 if (range->start == range->end - 1) 474 return apr_psprintf(pool, "%ld%s", range->end, mark); 475 else if (range->start - 1 == range->end) 476 return apr_psprintf(pool, "-%ld%s", range->start, mark); 477 else if (range->start < range->end) 478 return apr_psprintf(pool, "%ld-%ld%s", range->start + 1, range->end, mark); 479 else 480 return apr_psprintf(pool, "%ld-%ld%s", range->start, range->end + 1, mark); 481} 482 483/* Helper for svn_mergeinfo_parse() 484 Append revision ranges onto the array RANGELIST to represent the range 485 descriptions found in the string *INPUT. Read only as far as a newline 486 or the position END, whichever comes first. Set *INPUT to the position 487 after the last character of INPUT that was used. 488 489 revisionlist -> (revisionelement)(COMMA revisionelement)* 490 revisionrange -> REVISION "-" REVISION("*") 491 revisionelement -> revisionrange | REVISION("*") 492*/ 493static svn_error_t * 494parse_rangelist(const char **input, const char *end, 495 svn_rangelist_t *rangelist, 496 apr_pool_t *pool) 497{ 498 const char *curr = *input; 499 500 /* Eat any leading horizontal white-space before the rangelist. */ 501 while (curr < end && *curr != '\n' && isspace(*curr)) 502 curr++; 503 504 if (*curr == '\n' || curr == end) 505 { 506 /* Empty range list. */ 507 *input = curr; 508 return SVN_NO_ERROR; 509 } 510 511 while (curr < end && *curr != '\n') 512 { 513 /* Parse individual revisions or revision ranges. */ 514 svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange)); 515 svn_revnum_t firstrev; 516 517 SVN_ERR(svn_revnum_parse(&firstrev, curr, &curr)); 518 if (*curr != '-' && *curr != '\n' && *curr != ',' && *curr != '*' 519 && curr != end) 520 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 521 _("Invalid character '%c' found in revision " 522 "list"), *curr); 523 mrange->start = firstrev - 1; 524 mrange->end = firstrev; 525 mrange->inheritable = TRUE; 526 527 if (firstrev == 0) 528 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 529 _("Invalid revision number '0' found in " 530 "range list")); 531 532 if (*curr == '-') 533 { 534 svn_revnum_t secondrev; 535 536 curr++; 537 SVN_ERR(svn_revnum_parse(&secondrev, curr, &curr)); 538 if (firstrev > secondrev) 539 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 540 _("Unable to parse reversed revision " 541 "range '%ld-%ld'"), 542 firstrev, secondrev); 543 else if (firstrev == secondrev) 544 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 545 _("Unable to parse revision range " 546 "'%ld-%ld' with same start and end " 547 "revisions"), firstrev, secondrev); 548 mrange->end = secondrev; 549 } 550 551 if (*curr == '\n' || curr == end) 552 { 553 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange; 554 *input = curr; 555 return SVN_NO_ERROR; 556 } 557 else if (*curr == ',') 558 { 559 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange; 560 curr++; 561 } 562 else if (*curr == '*') 563 { 564 mrange->inheritable = FALSE; 565 curr++; 566 if (*curr == ',' || *curr == '\n' || curr == end) 567 { 568 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange; 569 if (*curr == ',') 570 { 571 curr++; 572 } 573 else 574 { 575 *input = curr; 576 return SVN_NO_ERROR; 577 } 578 } 579 else 580 { 581 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 582 _("Invalid character '%c' found in " 583 "range list"), *curr); 584 } 585 } 586 else 587 { 588 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 589 _("Invalid character '%c' found in " 590 "range list"), *curr); 591 } 592 593 } 594 if (*curr != '\n') 595 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 596 _("Range list parsing ended before hitting " 597 "newline")); 598 *input = curr; 599 return SVN_NO_ERROR; 600} 601 602svn_error_t * 603svn_rangelist__parse(svn_rangelist_t **rangelist, 604 const char *str, 605 apr_pool_t *result_pool) 606{ 607 const char *s = str; 608 609 *rangelist = apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *)); 610 SVN_ERR(parse_rangelist(&s, s + strlen(s), *rangelist, result_pool)); 611 return SVN_NO_ERROR; 612} 613 614svn_error_t * 615svn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist, 616 apr_pool_t *scratch_pool) 617{ 618 int i; 619 svn_merge_range_t *range, *lastrange; 620 621 lastrange = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *); 622 623 for (i = 1; i < rangelist->nelts; i++) 624 { 625 range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 626 if (lastrange->start <= range->end 627 && range->start <= lastrange->end) 628 { 629 /* The ranges are adjacent or intersect. */ 630 631 /* svn_mergeinfo_parse promises to combine overlapping 632 ranges as long as their inheritability is the same. */ 633 if (range->start < lastrange->end 634 && range->inheritable != lastrange->inheritable) 635 { 636 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 637 _("Unable to parse overlapping " 638 "revision ranges '%s' and '%s' " 639 "with different inheritance " 640 "types"), 641 range_to_string(lastrange, 642 scratch_pool), 643 range_to_string(range, 644 scratch_pool)); 645 } 646 647 /* Combine overlapping or adjacent ranges with the 648 same inheritability. */ 649 if (lastrange->inheritable == range->inheritable) 650 { 651 lastrange->end = MAX(range->end, lastrange->end); 652 svn_sort__array_delete(rangelist, i, 1); 653 i--; 654 } 655 } 656 lastrange = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 657 } 658 659 return SVN_NO_ERROR; 660} 661 662/* revisionline -> PATHNAME COLON revisionlist */ 663static svn_error_t * 664parse_revision_line(const char **input, const char *end, svn_mergeinfo_t hash, 665 apr_pool_t *scratch_pool) 666{ 667 const char *pathname = ""; 668 apr_ssize_t klen; 669 svn_rangelist_t *existing_rangelist; 670 svn_rangelist_t *rangelist = apr_array_make(scratch_pool, 1, 671 sizeof(svn_merge_range_t *)); 672 673 SVN_ERR(parse_pathname(input, end, &pathname, scratch_pool)); 674 675 if (*(*input) != ':') 676 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 677 _("Pathname not terminated by ':'")); 678 679 *input = *input + 1; 680 681 SVN_ERR(parse_rangelist(input, end, rangelist, scratch_pool)); 682 683 if (rangelist->nelts == 0) 684 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 685 _("Mergeinfo for '%s' maps to an " 686 "empty revision range"), pathname); 687 if (*input != end && *(*input) != '\n') 688 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 689 _("Could not find end of line in range list line " 690 "in '%s'"), *input); 691 692 if (*input != end) 693 *input = *input + 1; 694 695 /* Sort the rangelist, combine adjacent ranges into single ranges, 696 and make sure there are no overlapping ranges. */ 697 if (rangelist->nelts > 1) 698 { 699 qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size, 700 svn_sort_compare_ranges); 701 702 SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool)); 703 } 704 705 /* Handle any funky mergeinfo with relative merge source paths that 706 might exist due to issue #3547. It's possible that this issue allowed 707 the creation of mergeinfo with path keys that differ only by a 708 leading slash, e.g. "trunk:4033\n/trunk:4039-4995". In the event 709 we encounter this we merge the rangelists together under a single 710 absolute path key. */ 711 klen = strlen(pathname); 712 existing_rangelist = apr_hash_get(hash, pathname, klen); 713 if (existing_rangelist) 714 SVN_ERR(svn_rangelist_merge2(rangelist, existing_rangelist, 715 scratch_pool, scratch_pool)); 716 717 apr_hash_set(hash, apr_pstrmemdup(apr_hash_pool_get(hash), pathname, klen), 718 klen, svn_rangelist_dup(rangelist, apr_hash_pool_get(hash))); 719 720 return SVN_NO_ERROR; 721} 722 723/* top -> revisionline (NEWLINE revisionline)* */ 724static svn_error_t * 725parse_top(const char **input, const char *end, svn_mergeinfo_t hash, 726 apr_pool_t *pool) 727{ 728 apr_pool_t *iterpool = svn_pool_create(pool); 729 730 while (*input < end) 731 { 732 svn_pool_clear(iterpool); 733 SVN_ERR(parse_revision_line(input, end, hash, iterpool)); 734 } 735 svn_pool_destroy(iterpool); 736 737 return SVN_NO_ERROR; 738} 739 740svn_error_t * 741svn_mergeinfo_parse(svn_mergeinfo_t *mergeinfo, 742 const char *input, 743 apr_pool_t *pool) 744{ 745 svn_error_t *err; 746 747 *mergeinfo = svn_hash__make(pool); 748 err = parse_top(&input, input + strlen(input), *mergeinfo, pool); 749 750 /* Always return SVN_ERR_MERGEINFO_PARSE_ERROR as the topmost error. */ 751 if (err && err->apr_err != SVN_ERR_MERGEINFO_PARSE_ERROR) 752 err = svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, err, 753 _("Could not parse mergeinfo string '%s'"), 754 input); 755 return err; 756} 757 758/* Cleanup after svn_rangelist_merge2 when it modifies the ending range of 759 a single rangelist element in-place. 760 761 If *RANGE_INDEX is not a valid element in RANGELIST do nothing. Otherwise 762 ensure that RANGELIST[*RANGE_INDEX]->END does not adjoin or overlap any 763 subsequent ranges in RANGELIST. 764 765 If overlap is found, then remove, modify, and/or add elements to RANGELIST 766 as per the invariants for rangelists documented in svn_mergeinfo.h. If 767 RANGELIST[*RANGE_INDEX]->END adjoins a subsequent element then combine the 768 elements if their inheritability permits -- The inheritance of intersecting 769 and adjoining ranges is handled as per svn_mergeinfo_merge2. Upon return 770 set *RANGE_INDEX to the index of the youngest element modified, added, or 771 adjoined to RANGELIST[*RANGE_INDEX]. 772 773 Note: Adjoining rangelist elements are those where the end rev of the older 774 element is equal to the start rev of the younger element. 775 776 Any new elements inserted into RANGELIST are allocated in RESULT_POOL.*/ 777static void 778adjust_remaining_ranges(svn_rangelist_t *rangelist, 779 int *range_index, 780 apr_pool_t *result_pool) 781{ 782 int i; 783 int starting_index; 784 int elements_to_delete = 0; 785 svn_merge_range_t *modified_range; 786 787 if (*range_index >= rangelist->nelts) 788 return; 789 790 starting_index = *range_index + 1; 791 modified_range = APR_ARRAY_IDX(rangelist, *range_index, svn_merge_range_t *); 792 793 for (i = *range_index + 1; i < rangelist->nelts; i++) 794 { 795 svn_merge_range_t *next_range = APR_ARRAY_IDX(rangelist, i, 796 svn_merge_range_t *); 797 798 /* If MODIFIED_RANGE doesn't adjoin or overlap the next range in 799 RANGELIST then we are finished. */ 800 if (modified_range->end < next_range->start) 801 break; 802 803 /* Does MODIFIED_RANGE adjoin NEXT_RANGE? */ 804 if (modified_range->end == next_range->start) 805 { 806 if (modified_range->inheritable == next_range->inheritable) 807 { 808 /* Combine adjoining ranges with the same inheritability. */ 809 modified_range->end = next_range->end; 810 elements_to_delete++; 811 } 812 else 813 { 814 /* Cannot join because inheritance differs. */ 815 (*range_index)++; 816 } 817 break; 818 } 819 820 /* Alright, we know MODIFIED_RANGE overlaps NEXT_RANGE, but how? */ 821 if (modified_range->end > next_range->end) 822 { 823 /* NEXT_RANGE is a proper subset of MODIFIED_RANGE and the two 824 don't share the same end range. */ 825 if (modified_range->inheritable 826 || (modified_range->inheritable == next_range->inheritable)) 827 { 828 /* MODIFIED_RANGE absorbs NEXT_RANGE. */ 829 elements_to_delete++; 830 } 831 else 832 { 833 /* NEXT_RANGE is a proper subset MODIFIED_RANGE but 834 MODIFIED_RANGE is non-inheritable and NEXT_RANGE is 835 inheritable. This means MODIFIED_RANGE is truncated, 836 NEXT_RANGE remains, and the portion of MODIFIED_RANGE 837 younger than NEXT_RANGE is added as a separate range: 838 ______________________________________________ 839 | | 840 M MODIFIED_RANGE N 841 | (!inhertiable) | 842 |______________________________________________| 843 | | 844 O NEXT_RANGE P 845 | (inheritable)| 846 |______________| 847 | 848 V 849 _______________________________________________ 850 | | | | 851 M MODIFIED_RANGE O NEXT_RANGE P NEW_RANGE N 852 | (!inhertiable) | (inheritable)| (!inheritable)| 853 |________________|______________|_______________| 854 */ 855 svn_merge_range_t *new_modified_range = 856 apr_palloc(result_pool, sizeof(*new_modified_range)); 857 new_modified_range->start = next_range->end; 858 new_modified_range->end = modified_range->end; 859 new_modified_range->inheritable = FALSE; 860 modified_range->end = next_range->start; 861 (*range_index)+=2; 862 svn_sort__array_insert(&new_modified_range, rangelist, 863 *range_index); 864 /* Recurse with the new range. */ 865 adjust_remaining_ranges(rangelist, range_index, result_pool); 866 break; 867 } 868 } 869 else if (modified_range->end == next_range->end) 870 { 871 /* NEXT_RANGE is a proper subset MODIFIED_RANGE and share 872 the same end range. */ 873 if (modified_range->inheritable 874 || (modified_range->inheritable == next_range->inheritable)) 875 { 876 /* MODIFIED_RANGE absorbs NEXT_RANGE. */ 877 elements_to_delete++; 878 } 879 else 880 { 881 /* The intersection between MODIFIED_RANGE and NEXT_RANGE is 882 absorbed by the latter. */ 883 modified_range->end = next_range->start; 884 (*range_index)++; 885 } 886 break; 887 } 888 else 889 { 890 /* NEXT_RANGE and MODIFIED_RANGE intersect but NEXT_RANGE is not 891 a proper subset of MODIFIED_RANGE, nor do the two share the 892 same end revision, i.e. they overlap. */ 893 if (modified_range->inheritable == next_range->inheritable) 894 { 895 /* Combine overlapping ranges with the same inheritability. */ 896 modified_range->end = next_range->end; 897 elements_to_delete++; 898 } 899 else if (modified_range->inheritable) 900 { 901 /* MODIFIED_RANGE absorbs the portion of NEXT_RANGE it overlaps 902 and NEXT_RANGE is truncated. */ 903 next_range->start = modified_range->end; 904 (*range_index)++; 905 } 906 else 907 { 908 /* NEXT_RANGE absorbs the portion of MODIFIED_RANGE it overlaps 909 and MODIFIED_RANGE is truncated. */ 910 modified_range->end = next_range->start; 911 (*range_index)++; 912 } 913 break; 914 } 915 } 916 917 if (elements_to_delete) 918 svn_sort__array_delete(rangelist, starting_index, elements_to_delete); 919} 920 921svn_error_t * 922svn_rangelist_merge2(svn_rangelist_t *rangelist, 923 const svn_rangelist_t *changes, 924 apr_pool_t *result_pool, 925 apr_pool_t *scratch_pool) 926{ 927 int i = 0; 928 int j = 0; 929 930 /* We may modify CHANGES, so make a copy in SCRATCH_POOL. */ 931 changes = svn_rangelist_dup(changes, scratch_pool); 932 933 while (i < rangelist->nelts && j < changes->nelts) 934 { 935 svn_merge_range_t *range = 936 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 937 svn_merge_range_t *change = 938 APR_ARRAY_IDX(changes, j, svn_merge_range_t *); 939 int res = svn_sort_compare_ranges(&range, &change); 940 941 if (res == 0) 942 { 943 /* Only when merging two non-inheritable ranges is the result also 944 non-inheritable. In all other cases ensure an inheritiable 945 result. */ 946 if (range->inheritable || change->inheritable) 947 range->inheritable = TRUE; 948 i++; 949 j++; 950 } 951 else if (res < 0) /* CHANGE is younger than RANGE */ 952 { 953 if (range->end < change->start) 954 { 955 /* RANGE is older than CHANGE and the two do not 956 adjoin or overlap */ 957 i++; 958 } 959 else if (range->end == change->start) 960 { 961 /* RANGE and CHANGE adjoin */ 962 if (range->inheritable == change->inheritable) 963 { 964 /* RANGE and CHANGE have the same inheritability so 965 RANGE expands to absord CHANGE. */ 966 range->end = change->end; 967 adjust_remaining_ranges(rangelist, &i, result_pool); 968 j++; 969 } 970 else 971 { 972 /* RANGE and CHANGE adjoin, but have different 973 inheritability. Since RANGE is older, just 974 move on to the next RANGE. */ 975 i++; 976 } 977 } 978 else 979 { 980 /* RANGE and CHANGE overlap, but how? */ 981 if ((range->inheritable == change->inheritable) 982 || range->inheritable) 983 { 984 /* If CHANGE is a proper subset of RANGE, it absorbs RANGE 985 with no adjustment otherwise only the intersection is 986 absorbed and CHANGE is truncated. */ 987 if (range->end >= change->end) 988 j++; 989 else 990 change->start = range->end; 991 } 992 else 993 { 994 /* RANGE is non-inheritable and CHANGE is inheritable. */ 995 if (range->start < change->start) 996 { 997 /* CHANGE absorbs intersection with RANGE and RANGE 998 is truncated. */ 999 svn_merge_range_t *range_copy = 1000 svn_merge_range_dup(range, result_pool); 1001 range_copy->end = change->start; 1002 range->start = change->start; 1003 svn_sort__array_insert(&range_copy, rangelist, i++); 1004 } 1005 else 1006 { 1007 /* CHANGE and RANGE share the same start rev, but 1008 RANGE is considered older because its end rev 1009 is older. */ 1010 range->inheritable = TRUE; 1011 change->start = range->end; 1012 } 1013 } 1014 } 1015 } 1016 else /* res > 0, CHANGE is older than RANGE */ 1017 { 1018 if (change->end < range->start) 1019 { 1020 /* CHANGE is older than RANGE and the two do not 1021 adjoin or overlap, so insert a copy of CHANGE 1022 into RANGELIST. */ 1023 svn_merge_range_t *change_copy = 1024 svn_merge_range_dup(change, result_pool); 1025 svn_sort__array_insert(&change_copy, rangelist, i++); 1026 j++; 1027 } 1028 else if (change->end == range->start) 1029 { 1030 /* RANGE and CHANGE adjoin */ 1031 if (range->inheritable == change->inheritable) 1032 { 1033 /* RANGE and CHANGE have the same inheritability so we 1034 can simply combine the two in place. */ 1035 range->start = change->start; 1036 j++; 1037 } 1038 else 1039 { 1040 /* RANGE and CHANGE have different inheritability so insert 1041 a copy of CHANGE into RANGELIST. */ 1042 svn_merge_range_t *change_copy = 1043 svn_merge_range_dup(change, result_pool); 1044 svn_sort__array_insert(&change_copy, rangelist, i); 1045 j++; 1046 } 1047 } 1048 else 1049 { 1050 /* RANGE and CHANGE overlap. */ 1051 if (range->inheritable == change->inheritable) 1052 { 1053 /* RANGE and CHANGE have the same inheritability so we 1054 can simply combine the two in place... */ 1055 range->start = change->start; 1056 if (range->end < change->end) 1057 { 1058 /* ...but if RANGE is expanded ensure that we don't 1059 violate any rangelist invariants. */ 1060 range->end = change->end; 1061 adjust_remaining_ranges(rangelist, &i, result_pool); 1062 } 1063 j++; 1064 } 1065 else if (range->inheritable) 1066 { 1067 if (change->start < range->start) 1068 { 1069 /* RANGE is inheritable so absorbs any part of CHANGE 1070 it overlaps. CHANGE is truncated and the remainder 1071 inserted into RANGELIST. */ 1072 svn_merge_range_t *change_copy = 1073 svn_merge_range_dup(change, result_pool); 1074 change_copy->end = range->start; 1075 change->start = range->start; 1076 svn_sort__array_insert(&change_copy, rangelist, i++); 1077 } 1078 else 1079 { 1080 /* CHANGE and RANGE share the same start rev, but 1081 CHANGE is considered older because CHANGE->END is 1082 older than RANGE->END. */ 1083 j++; 1084 } 1085 } 1086 else 1087 { 1088 /* RANGE is non-inheritable and CHANGE is inheritable. */ 1089 if (change->start < range->start) 1090 { 1091 if (change->end == range->end) 1092 { 1093 /* RANGE is a proper subset of CHANGE and share the 1094 same end revision, so set RANGE equal to CHANGE. */ 1095 range->start = change->start; 1096 range->inheritable = TRUE; 1097 j++; 1098 } 1099 else if (change->end > range->end) 1100 { 1101 /* RANGE is a proper subset of CHANGE and CHANGE has 1102 a younger end revision, so set RANGE equal to its 1103 intersection with CHANGE and truncate CHANGE. */ 1104 range->start = change->start; 1105 range->inheritable = TRUE; 1106 change->start = range->end; 1107 } 1108 else 1109 { 1110 /* CHANGE and RANGE overlap. Set RANGE equal to its 1111 intersection with CHANGE and take the remainder 1112 of RANGE and insert it into RANGELIST. */ 1113 svn_merge_range_t *range_copy = 1114 svn_merge_range_dup(range, result_pool); 1115 range_copy->start = change->end; 1116 range->start = change->start; 1117 range->end = change->end; 1118 range->inheritable = TRUE; 1119 svn_sort__array_insert(&range_copy, rangelist, ++i); 1120 j++; 1121 } 1122 } 1123 else 1124 { 1125 /* CHANGE and RANGE share the same start rev, but 1126 CHANGE is considered older because its end rev 1127 is older. 1128 1129 Insert the intersection of RANGE and CHANGE into 1130 RANGELIST and then set RANGE to the non-intersecting 1131 portion of RANGE. */ 1132 svn_merge_range_t *range_copy = 1133 svn_merge_range_dup(range, result_pool); 1134 range_copy->end = change->end; 1135 range_copy->inheritable = TRUE; 1136 range->start = change->end; 1137 svn_sort__array_insert(&range_copy, rangelist, i++); 1138 j++; 1139 } 1140 } 1141 } 1142 } 1143 } 1144 1145 /* Copy any remaining elements in CHANGES into RANGELIST. */ 1146 for (; j < (changes)->nelts; j++) 1147 { 1148 svn_merge_range_t *change = 1149 APR_ARRAY_IDX(changes, j, svn_merge_range_t *); 1150 svn_merge_range_t *change_copy = svn_merge_range_dup(change, 1151 result_pool); 1152 svn_sort__array_insert(&change_copy, rangelist, rangelist->nelts); 1153 } 1154 1155 return SVN_NO_ERROR; 1156} 1157 1158/* Return TRUE iff the forward revision ranges FIRST and SECOND overlap and 1159 * (if CONSIDER_INHERITANCE is TRUE) have the same inheritability. */ 1160static svn_boolean_t 1161range_intersect(const svn_merge_range_t *first, const svn_merge_range_t *second, 1162 svn_boolean_t consider_inheritance) 1163{ 1164 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first)); 1165 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second)); 1166 1167 return (first->start + 1 <= second->end) 1168 && (second->start + 1 <= first->end) 1169 && (!consider_inheritance 1170 || (!(first->inheritable) == !(second->inheritable))); 1171} 1172 1173/* Return TRUE iff the forward revision range FIRST wholly contains the 1174 * forward revision range SECOND and (if CONSIDER_INHERITANCE is TRUE) has 1175 * the same inheritability. */ 1176static svn_boolean_t 1177range_contains(const svn_merge_range_t *first, const svn_merge_range_t *second, 1178 svn_boolean_t consider_inheritance) 1179{ 1180 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first)); 1181 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second)); 1182 1183 return (first->start <= second->start) && (second->end <= first->end) 1184 && (!consider_inheritance 1185 || (!(first->inheritable) == !(second->inheritable))); 1186} 1187 1188/* Swap start and end fields of RANGE. */ 1189static void 1190range_swap_endpoints(svn_merge_range_t *range) 1191{ 1192 svn_revnum_t swap = range->start; 1193 range->start = range->end; 1194 range->end = swap; 1195} 1196 1197svn_error_t * 1198svn_rangelist_reverse(svn_rangelist_t *rangelist, apr_pool_t *pool) 1199{ 1200 int i; 1201 1202 svn_sort__array_reverse(rangelist, pool); 1203 1204 for (i = 0; i < rangelist->nelts; i++) 1205 { 1206 range_swap_endpoints(APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *)); 1207 } 1208 1209 return SVN_NO_ERROR; 1210} 1211 1212void 1213svn_rangelist__set_inheritance(svn_rangelist_t *rangelist, 1214 svn_boolean_t inheritable) 1215{ 1216 if (rangelist) 1217 { 1218 int i; 1219 svn_merge_range_t *range; 1220 1221 for (i = 0; i < rangelist->nelts; i++) 1222 { 1223 range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 1224 range->inheritable = inheritable; 1225 } 1226 } 1227 return; 1228} 1229 1230void 1231svn_mergeinfo__set_inheritance(svn_mergeinfo_t mergeinfo, 1232 svn_boolean_t inheritable, 1233 apr_pool_t *scratch_pool) 1234{ 1235 if (mergeinfo) 1236 { 1237 apr_hash_index_t *hi; 1238 1239 for (hi = apr_hash_first(scratch_pool, mergeinfo); 1240 hi; 1241 hi = apr_hash_next(hi)) 1242 { 1243 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 1244 1245 if (rangelist) 1246 svn_rangelist__set_inheritance(rangelist, inheritable); 1247 } 1248 } 1249 return; 1250} 1251 1252/* If DO_REMOVE is true, then remove any overlapping ranges described by 1253 RANGELIST1 from RANGELIST2 and place the results in *OUTPUT. When 1254 DO_REMOVE is true, RANGELIST1 is effectively the "eraser" and RANGELIST2 1255 the "whiteboard". 1256 1257 If DO_REMOVE is false, then capture the intersection between RANGELIST1 1258 and RANGELIST2 and place the results in *OUTPUT. The ordering of 1259 RANGELIST1 and RANGELIST2 doesn't matter when DO_REMOVE is false. 1260 1261 If CONSIDER_INHERITANCE is true, then take the inheritance of the 1262 ranges in RANGELIST1 and RANGELIST2 into account when comparing them 1263 for intersection, see the doc string for svn_rangelist_intersect(). 1264 1265 If CONSIDER_INHERITANCE is false, then ranges with differing inheritance 1266 may intersect, but the resulting intersection is non-inheritable only 1267 if both ranges were non-inheritable, e.g.: 1268 1269 RANGELIST1 RANGELIST2 CONSIDER DO_REMOVE *OUTPUT 1270 INHERITANCE 1271 ---------- ------ ----------- --------- ------- 1272 1273 90-420* 1-100 TRUE FALSE Empty Rangelist 1274 90-420 1-100* TRUE FALSE Empty Rangelist 1275 90-420 1-100 TRUE FALSE 90-100 1276 90-420* 1-100* TRUE FALSE 90-100* 1277 1278 90-420* 1-100 FALSE FALSE 90-100 1279 90-420 1-100* FALSE FALSE 90-100 1280 90-420 1-100 FALSE FALSE 90-100 1281 90-420* 1-100* FALSE FALSE 90-100* 1282 1283 Allocate the contents of *OUTPUT in POOL. */ 1284static svn_error_t * 1285rangelist_intersect_or_remove(svn_rangelist_t **output, 1286 const svn_rangelist_t *rangelist1, 1287 const svn_rangelist_t *rangelist2, 1288 svn_boolean_t do_remove, 1289 svn_boolean_t consider_inheritance, 1290 apr_pool_t *pool) 1291{ 1292 int i1, i2, lasti2; 1293 svn_merge_range_t working_elt2; 1294 1295 *output = apr_array_make(pool, 1, sizeof(svn_merge_range_t *)); 1296 1297 i1 = 0; 1298 i2 = 0; 1299 lasti2 = -1; /* Initialized to a value that "i2" will never be. */ 1300 1301 while (i1 < rangelist1->nelts && i2 < rangelist2->nelts) 1302 { 1303 svn_merge_range_t *elt1, *elt2; 1304 1305 elt1 = APR_ARRAY_IDX(rangelist1, i1, svn_merge_range_t *); 1306 1307 /* Instead of making a copy of the entire array of rangelist2 1308 elements, we just keep a copy of the current rangelist2 element 1309 that needs to be used, and modify our copy if necessary. */ 1310 if (i2 != lasti2) 1311 { 1312 working_elt2 = 1313 *(APR_ARRAY_IDX(rangelist2, i2, svn_merge_range_t *)); 1314 lasti2 = i2; 1315 } 1316 1317 elt2 = &working_elt2; 1318 1319 /* If the rangelist2 range is contained completely in the 1320 rangelist1, we increment the rangelist2. 1321 If the ranges intersect, and match exactly, we increment both 1322 rangelist1 and rangelist2. 1323 Otherwise, we have to generate a range for the left part of 1324 the removal of rangelist1 from rangelist2, and possibly change 1325 the rangelist2 to the remaining portion of the right part of 1326 the removal, to test against. */ 1327 if (range_contains(elt1, elt2, consider_inheritance)) 1328 { 1329 if (!do_remove) 1330 { 1331 svn_merge_range_t tmp_range; 1332 tmp_range.start = elt2->start; 1333 tmp_range.end = elt2->end; 1334 /* The intersection of two ranges is non-inheritable only 1335 if both ranges are non-inheritable. */ 1336 tmp_range.inheritable = 1337 (elt2->inheritable || elt1->inheritable); 1338 SVN_ERR(combine_with_lastrange(&tmp_range, *output, 1339 consider_inheritance, 1340 pool)); 1341 } 1342 1343 i2++; 1344 1345 if (elt2->start == elt1->start && elt2->end == elt1->end) 1346 i1++; 1347 } 1348 else if (range_intersect(elt1, elt2, consider_inheritance)) 1349 { 1350 if (elt2->start < elt1->start) 1351 { 1352 /* The rangelist2 range starts before the rangelist1 range. */ 1353 svn_merge_range_t tmp_range; 1354 if (do_remove) 1355 { 1356 /* Retain the range that falls before the rangelist1 1357 start. */ 1358 tmp_range.start = elt2->start; 1359 tmp_range.end = elt1->start; 1360 tmp_range.inheritable = elt2->inheritable; 1361 } 1362 else 1363 { 1364 /* Retain the range that falls between the rangelist1 1365 start and rangelist2 end. */ 1366 tmp_range.start = elt1->start; 1367 tmp_range.end = MIN(elt2->end, elt1->end); 1368 /* The intersection of two ranges is non-inheritable only 1369 if both ranges are non-inheritable. */ 1370 tmp_range.inheritable = 1371 (elt2->inheritable || elt1->inheritable); 1372 } 1373 1374 SVN_ERR(combine_with_lastrange(&tmp_range, 1375 *output, consider_inheritance, 1376 pool)); 1377 } 1378 1379 /* Set up the rest of the rangelist2 range for further 1380 processing. */ 1381 if (elt2->end > elt1->end) 1382 { 1383 /* The rangelist2 range ends after the rangelist1 range. */ 1384 if (!do_remove) 1385 { 1386 /* Partial overlap. */ 1387 svn_merge_range_t tmp_range; 1388 tmp_range.start = MAX(elt2->start, elt1->start); 1389 tmp_range.end = elt1->end; 1390 /* The intersection of two ranges is non-inheritable only 1391 if both ranges are non-inheritable. */ 1392 tmp_range.inheritable = 1393 (elt2->inheritable || elt1->inheritable); 1394 SVN_ERR(combine_with_lastrange(&tmp_range, 1395 *output, 1396 consider_inheritance, 1397 pool)); 1398 } 1399 1400 working_elt2.start = elt1->end; 1401 working_elt2.end = elt2->end; 1402 } 1403 else 1404 i2++; 1405 } 1406 else /* ranges don't intersect */ 1407 { 1408 /* See which side of the rangelist2 the rangelist1 is on. If it 1409 is on the left side, we need to move the rangelist1. 1410 1411 If it is on past the rangelist2 on the right side, we 1412 need to output the rangelist2 and increment the 1413 rangelist2. */ 1414 if (svn_sort_compare_ranges(&elt1, &elt2) < 0) 1415 i1++; 1416 else 1417 { 1418 svn_merge_range_t *lastrange; 1419 1420 if ((*output)->nelts > 0) 1421 lastrange = APR_ARRAY_IDX(*output, (*output)->nelts - 1, 1422 svn_merge_range_t *); 1423 else 1424 lastrange = NULL; 1425 1426 if (do_remove && !(lastrange && 1427 combine_ranges(lastrange, lastrange, elt2, 1428 consider_inheritance))) 1429 { 1430 lastrange = svn_merge_range_dup(elt2, pool); 1431 APR_ARRAY_PUSH(*output, svn_merge_range_t *) = lastrange; 1432 } 1433 i2++; 1434 } 1435 } 1436 } 1437 1438 if (do_remove) 1439 { 1440 /* Copy the current rangelist2 element if we didn't hit the end 1441 of the rangelist2, and we still had it around. This element 1442 may have been touched, so we can't just walk the rangelist2 1443 array, we have to use our copy. This case only happens when 1444 we ran out of rangelist1 before rangelist2, *and* we had changed 1445 the rangelist2 element. */ 1446 if (i2 == lasti2 && i2 < rangelist2->nelts) 1447 { 1448 SVN_ERR(combine_with_lastrange(&working_elt2, *output, 1449 consider_inheritance, pool)); 1450 i2++; 1451 } 1452 1453 /* Copy any other remaining untouched rangelist2 elements. */ 1454 for (; i2 < rangelist2->nelts; i2++) 1455 { 1456 svn_merge_range_t *elt = APR_ARRAY_IDX(rangelist2, i2, 1457 svn_merge_range_t *); 1458 1459 SVN_ERR(combine_with_lastrange(elt, *output, 1460 consider_inheritance, pool)); 1461 } 1462 } 1463 1464 return SVN_NO_ERROR; 1465} 1466 1467 1468svn_error_t * 1469svn_rangelist_intersect(svn_rangelist_t **output, 1470 const svn_rangelist_t *rangelist1, 1471 const svn_rangelist_t *rangelist2, 1472 svn_boolean_t consider_inheritance, 1473 apr_pool_t *pool) 1474{ 1475 return rangelist_intersect_or_remove(output, rangelist1, rangelist2, FALSE, 1476 consider_inheritance, pool); 1477} 1478 1479svn_error_t * 1480svn_rangelist_remove(svn_rangelist_t **output, 1481 const svn_rangelist_t *eraser, 1482 const svn_rangelist_t *whiteboard, 1483 svn_boolean_t consider_inheritance, 1484 apr_pool_t *pool) 1485{ 1486 return rangelist_intersect_or_remove(output, eraser, whiteboard, TRUE, 1487 consider_inheritance, pool); 1488} 1489 1490svn_error_t * 1491svn_rangelist_diff(svn_rangelist_t **deleted, svn_rangelist_t **added, 1492 const svn_rangelist_t *from, const svn_rangelist_t *to, 1493 svn_boolean_t consider_inheritance, 1494 apr_pool_t *pool) 1495{ 1496 /* The following diagrams illustrate some common range delta scenarios: 1497 1498 (from) deleted 1499 r0 <===========(=========)============[=========]===========> rHEAD 1500 [to] added 1501 1502 (from) deleted deleted 1503 r0 <===========(=========[============]=========)===========> rHEAD 1504 [to] 1505 1506 (from) deleted 1507 r0 <===========(=========[============)=========]===========> rHEAD 1508 [to] added 1509 1510 (from) deleted 1511 r0 <===========[=========(============]=========)===========> rHEAD 1512 [to] added 1513 1514 (from) 1515 r0 <===========[=========(============)=========]===========> rHEAD 1516 [to] added added 1517 1518 (from) d d d 1519 r0 <===(=[=)=]=[==]=[=(=)=]=[=]=[=(===|===(=)==|=|==[=(=]=)=> rHEAD 1520 [to] a a a a a a a 1521 */ 1522 1523 /* The items that are present in from, but not in to, must have been 1524 deleted. */ 1525 SVN_ERR(svn_rangelist_remove(deleted, to, from, consider_inheritance, 1526 pool)); 1527 /* The items that are present in to, but not in from, must have been 1528 added. */ 1529 return svn_rangelist_remove(added, from, to, consider_inheritance, pool); 1530} 1531 1532struct mergeinfo_diff_baton 1533{ 1534 svn_mergeinfo_t from; 1535 svn_mergeinfo_t to; 1536 svn_mergeinfo_t deleted; 1537 svn_mergeinfo_t added; 1538 svn_boolean_t consider_inheritance; 1539 apr_pool_t *pool; 1540}; 1541 1542/* This implements the 'svn_hash_diff_func_t' interface. 1543 BATON is of type 'struct mergeinfo_diff_baton *'. 1544*/ 1545static svn_error_t * 1546mergeinfo_hash_diff_cb(const void *key, apr_ssize_t klen, 1547 enum svn_hash_diff_key_status status, 1548 void *baton) 1549{ 1550 /* hash_a is FROM mergeinfo, 1551 hash_b is TO mergeinfo. */ 1552 struct mergeinfo_diff_baton *cb = baton; 1553 svn_rangelist_t *from_rangelist, *to_rangelist; 1554 const char *path = key; 1555 if (status == svn_hash_diff_key_both) 1556 { 1557 /* Record any deltas (additions or deletions). */ 1558 svn_rangelist_t *deleted_rangelist, *added_rangelist; 1559 from_rangelist = apr_hash_get(cb->from, path, klen); 1560 to_rangelist = apr_hash_get(cb->to, path, klen); 1561 SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist, 1562 from_rangelist, to_rangelist, 1563 cb->consider_inheritance, cb->pool)); 1564 if (cb->deleted && deleted_rangelist->nelts > 0) 1565 apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen), 1566 klen, deleted_rangelist); 1567 if (cb->added && added_rangelist->nelts > 0) 1568 apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen), 1569 klen, added_rangelist); 1570 } 1571 else if ((status == svn_hash_diff_key_a) && cb->deleted) 1572 { 1573 from_rangelist = apr_hash_get(cb->from, path, klen); 1574 apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen), klen, 1575 svn_rangelist_dup(from_rangelist, cb->pool)); 1576 } 1577 else if ((status == svn_hash_diff_key_b) && cb->added) 1578 { 1579 to_rangelist = apr_hash_get(cb->to, path, klen); 1580 apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen), klen, 1581 svn_rangelist_dup(to_rangelist, cb->pool)); 1582 } 1583 return SVN_NO_ERROR; 1584} 1585 1586/* Record deletions and additions of entire range lists (by path 1587 presence), and delegate to svn_rangelist_diff() for delta 1588 calculations on a specific path. */ 1589static svn_error_t * 1590walk_mergeinfo_hash_for_diff(svn_mergeinfo_t from, svn_mergeinfo_t to, 1591 svn_mergeinfo_t deleted, svn_mergeinfo_t added, 1592 svn_boolean_t consider_inheritance, 1593 apr_pool_t *result_pool, 1594 apr_pool_t *scratch_pool) 1595{ 1596 struct mergeinfo_diff_baton mdb; 1597 mdb.from = from; 1598 mdb.to = to; 1599 mdb.deleted = deleted; 1600 mdb.added = added; 1601 mdb.consider_inheritance = consider_inheritance; 1602 mdb.pool = result_pool; 1603 1604 return svn_hash_diff(from, to, mergeinfo_hash_diff_cb, &mdb, scratch_pool); 1605} 1606 1607svn_error_t * 1608svn_mergeinfo_diff2(svn_mergeinfo_t *deleted, svn_mergeinfo_t *added, 1609 svn_mergeinfo_t from, svn_mergeinfo_t to, 1610 svn_boolean_t consider_inheritance, 1611 apr_pool_t *result_pool, 1612 apr_pool_t *scratch_pool) 1613{ 1614 if (from && to == NULL) 1615 { 1616 *deleted = svn_mergeinfo_dup(from, result_pool); 1617 *added = svn_hash__make(result_pool); 1618 } 1619 else if (from == NULL && to) 1620 { 1621 *deleted = svn_hash__make(result_pool); 1622 *added = svn_mergeinfo_dup(to, result_pool); 1623 } 1624 else 1625 { 1626 *deleted = svn_hash__make(result_pool); 1627 *added = svn_hash__make(result_pool); 1628 1629 if (from && to) 1630 { 1631 SVN_ERR(walk_mergeinfo_hash_for_diff(from, to, *deleted, *added, 1632 consider_inheritance, 1633 result_pool, scratch_pool)); 1634 } 1635 } 1636 1637 return SVN_NO_ERROR; 1638} 1639 1640svn_error_t * 1641svn_mergeinfo__equals(svn_boolean_t *is_equal, 1642 svn_mergeinfo_t info1, 1643 svn_mergeinfo_t info2, 1644 svn_boolean_t consider_inheritance, 1645 apr_pool_t *pool) 1646{ 1647 apr_hash_index_t *hi; 1648 1649 *is_equal = FALSE; 1650 1651 /* special cases: at least one side has no merge info */ 1652 if (info1 == NULL && info2 == NULL) 1653 { 1654 *is_equal = TRUE; 1655 return SVN_NO_ERROR; 1656 } 1657 1658 if (info1 == NULL || info2 == NULL) 1659 return SVN_NO_ERROR; 1660 1661 /* trivial case: different number of paths -> unequal */ 1662 if (apr_hash_count(info1) != apr_hash_count(info2)) 1663 return SVN_NO_ERROR; 1664 1665 /* compare range lists for all paths */ 1666 for (hi = apr_hash_first(pool, info1); hi; hi = apr_hash_next(hi)) 1667 { 1668 const char *key; 1669 apr_ssize_t key_length; 1670 svn_rangelist_t *lhs, *rhs; 1671 int i; 1672 svn_rangelist_t *deleted, *added; 1673 1674 /* get both path lists */ 1675 apr_hash_this(hi, (const void**)&key, &key_length, (void **)&lhs); 1676 rhs = apr_hash_get(info2, key, key_length); 1677 1678 /* missing on one side? */ 1679 if (rhs == NULL) 1680 return SVN_NO_ERROR; 1681 1682 /* quick compare: the range lists will often be a perfect match */ 1683 if (lhs->nelts == rhs->nelts) 1684 { 1685 for (i = 0; i < lhs->nelts; ++i) 1686 { 1687 svn_merge_range_t *lrange 1688 = APR_ARRAY_IDX(lhs, i, svn_merge_range_t *); 1689 svn_merge_range_t *rrange 1690 = APR_ARRAY_IDX(rhs, i, svn_merge_range_t *); 1691 1692 /* range mismatch? -> needs detailed comparison */ 1693 if ( lrange->start != rrange->start 1694 || lrange->end != rrange->end) 1695 break; 1696 1697 /* inheritance mismatch? -> merge info differs */ 1698 if ( consider_inheritance 1699 && lrange->inheritable != rrange->inheritable) 1700 return SVN_NO_ERROR; 1701 } 1702 1703 /* all ranges found to match -> next path */ 1704 if (i == lhs->nelts) 1705 continue; 1706 } 1707 1708 /* range lists differ but there are many ways to sort and aggregate 1709 revisions into ranges. Do a full diff on them. */ 1710 SVN_ERR(svn_rangelist_diff(&deleted, &added, lhs, rhs, 1711 consider_inheritance, pool)); 1712 if (deleted->nelts || added->nelts) 1713 return SVN_NO_ERROR; 1714 } 1715 1716 /* no mismatch found */ 1717 *is_equal = TRUE; 1718 return SVN_NO_ERROR; 1719} 1720 1721svn_error_t * 1722svn_mergeinfo_merge2(svn_mergeinfo_t mergeinfo, 1723 svn_mergeinfo_t changes, 1724 apr_pool_t *result_pool, 1725 apr_pool_t *scratch_pool) 1726{ 1727 apr_hash_index_t *hi; 1728 apr_pool_t *iterpool; 1729 1730 if (!apr_hash_count(changes)) 1731 return SVN_NO_ERROR; 1732 1733 iterpool = svn_pool_create(scratch_pool); 1734 for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi)) 1735 { 1736 const char *key; 1737 apr_ssize_t klen; 1738 svn_rangelist_t *to_insert; 1739 svn_rangelist_t *target; 1740 1741 /* get ranges to insert and the target ranges list of that insertion */ 1742 apr_hash_this(hi, (const void**)&key, &klen, (void*)&to_insert); 1743 target = apr_hash_get(mergeinfo, key, klen); 1744 1745 /* if range list exists, just expand on it. 1746 * Otherwise, add new hash entry. */ 1747 if (target) 1748 { 1749 SVN_ERR(svn_rangelist_merge2(target, to_insert, result_pool, 1750 iterpool)); 1751 svn_pool_clear(iterpool); 1752 } 1753 else 1754 apr_hash_set(mergeinfo, key, klen, to_insert); 1755 } 1756 1757 svn_pool_destroy(iterpool); 1758 1759 return SVN_NO_ERROR; 1760} 1761 1762svn_error_t * 1763svn_mergeinfo_catalog_merge(svn_mergeinfo_catalog_t mergeinfo_cat, 1764 svn_mergeinfo_catalog_t changes_cat, 1765 apr_pool_t *result_pool, 1766 apr_pool_t *scratch_pool) 1767{ 1768 int i = 0; 1769 int j = 0; 1770 apr_array_header_t *sorted_cat = 1771 svn_sort__hash(mergeinfo_cat, svn_sort_compare_items_as_paths, 1772 scratch_pool); 1773 apr_array_header_t *sorted_changes = 1774 svn_sort__hash(changes_cat, svn_sort_compare_items_as_paths, 1775 scratch_pool); 1776 1777 while (i < sorted_cat->nelts && j < sorted_changes->nelts) 1778 { 1779 svn_sort__item_t cat_elt, change_elt; 1780 int res; 1781 1782 cat_elt = APR_ARRAY_IDX(sorted_cat, i, svn_sort__item_t); 1783 change_elt = APR_ARRAY_IDX(sorted_changes, j, svn_sort__item_t); 1784 res = svn_sort_compare_items_as_paths(&cat_elt, &change_elt); 1785 1786 if (res == 0) /* Both catalogs have mergeinfo for a given path. */ 1787 { 1788 svn_mergeinfo_t mergeinfo = cat_elt.value; 1789 svn_mergeinfo_t changes_mergeinfo = change_elt.value; 1790 1791 SVN_ERR(svn_mergeinfo_merge2(mergeinfo, changes_mergeinfo, 1792 result_pool, scratch_pool)); 1793 apr_hash_set(mergeinfo_cat, cat_elt.key, cat_elt.klen, mergeinfo); 1794 i++; 1795 j++; 1796 } 1797 else if (res < 0) /* Only MERGEINFO_CAT has mergeinfo for this path. */ 1798 { 1799 i++; 1800 } 1801 else /* Only CHANGES_CAT has mergeinfo for this path. */ 1802 { 1803 apr_hash_set(mergeinfo_cat, 1804 apr_pstrdup(result_pool, change_elt.key), 1805 change_elt.klen, 1806 svn_mergeinfo_dup(change_elt.value, result_pool)); 1807 j++; 1808 } 1809 } 1810 1811 /* Copy back any remaining elements from the CHANGES_CAT catalog. */ 1812 for (; j < sorted_changes->nelts; j++) 1813 { 1814 svn_sort__item_t elt = APR_ARRAY_IDX(sorted_changes, j, 1815 svn_sort__item_t); 1816 apr_hash_set(mergeinfo_cat, 1817 apr_pstrdup(result_pool, elt.key), 1818 elt.klen, 1819 svn_mergeinfo_dup(elt.value, result_pool)); 1820 } 1821 1822 return SVN_NO_ERROR; 1823} 1824 1825svn_error_t * 1826svn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo, 1827 svn_mergeinfo_t mergeinfo1, 1828 svn_mergeinfo_t mergeinfo2, 1829 svn_boolean_t consider_inheritance, 1830 apr_pool_t *result_pool, 1831 apr_pool_t *scratch_pool) 1832{ 1833 apr_hash_index_t *hi; 1834 apr_pool_t *iterpool; 1835 1836 *mergeinfo = apr_hash_make(result_pool); 1837 iterpool = svn_pool_create(scratch_pool); 1838 1839 /* ### TODO(reint): Do we care about the case when a path in one 1840 ### mergeinfo hash has inheritable mergeinfo, and in the other 1841 ### has non-inhertiable mergeinfo? It seems like that path 1842 ### itself should really be an intersection, while child paths 1843 ### should not be... */ 1844 for (hi = apr_hash_first(scratch_pool, mergeinfo1); 1845 hi; hi = apr_hash_next(hi)) 1846 { 1847 const char *path = svn__apr_hash_index_key(hi); 1848 svn_rangelist_t *rangelist1 = svn__apr_hash_index_val(hi); 1849 svn_rangelist_t *rangelist2; 1850 1851 svn_pool_clear(iterpool); 1852 rangelist2 = svn_hash_gets(mergeinfo2, path); 1853 if (rangelist2) 1854 { 1855 SVN_ERR(svn_rangelist_intersect(&rangelist2, rangelist1, rangelist2, 1856 consider_inheritance, iterpool)); 1857 if (rangelist2->nelts > 0) 1858 svn_hash_sets(*mergeinfo, apr_pstrdup(result_pool, path), 1859 svn_rangelist_dup(rangelist2, result_pool)); 1860 } 1861 } 1862 svn_pool_destroy(iterpool); 1863 return SVN_NO_ERROR; 1864} 1865 1866svn_error_t * 1867svn_mergeinfo_remove2(svn_mergeinfo_t *mergeinfo, 1868 svn_mergeinfo_t eraser, 1869 svn_mergeinfo_t whiteboard, 1870 svn_boolean_t consider_inheritance, 1871 apr_pool_t *result_pool, 1872 apr_pool_t *scratch_pool) 1873{ 1874 *mergeinfo = apr_hash_make(result_pool); 1875 return walk_mergeinfo_hash_for_diff(whiteboard, eraser, *mergeinfo, NULL, 1876 consider_inheritance, result_pool, 1877 scratch_pool); 1878} 1879 1880svn_error_t * 1881svn_rangelist_to_string(svn_string_t **output, 1882 const svn_rangelist_t *rangelist, 1883 apr_pool_t *pool) 1884{ 1885 svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool); 1886 1887 if (rangelist->nelts > 0) 1888 { 1889 int i; 1890 svn_merge_range_t *range; 1891 1892 /* Handle the elements that need commas at the end. */ 1893 for (i = 0; i < rangelist->nelts - 1; i++) 1894 { 1895 range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 1896 svn_stringbuf_appendcstr(buf, range_to_string(range, pool)); 1897 svn_stringbuf_appendcstr(buf, ","); 1898 } 1899 1900 /* Now handle the last element, which needs no comma. */ 1901 range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 1902 svn_stringbuf_appendcstr(buf, range_to_string(range, pool)); 1903 } 1904 1905 *output = svn_stringbuf__morph_into_string(buf); 1906 1907 return SVN_NO_ERROR; 1908} 1909 1910/* Converts a mergeinfo INPUT to an unparsed mergeinfo in OUTPUT. If PREFIX 1911 is not NULL then prepend PREFIX to each line in OUTPUT. If INPUT contains 1912 no elements, return the empty string. If INPUT contains any merge source 1913 path keys that are relative then convert these to absolute paths in 1914 *OUTPUT. 1915 */ 1916static svn_error_t * 1917mergeinfo_to_stringbuf(svn_stringbuf_t **output, 1918 svn_mergeinfo_t input, 1919 const char *prefix, 1920 apr_pool_t *pool) 1921{ 1922 *output = svn_stringbuf_create_empty(pool); 1923 1924 if (apr_hash_count(input) > 0) 1925 { 1926 apr_array_header_t *sorted = 1927 svn_sort__hash(input, svn_sort_compare_items_as_paths, pool); 1928 int i; 1929 1930 for (i = 0; i < sorted->nelts; i++) 1931 { 1932 svn_sort__item_t elt = APR_ARRAY_IDX(sorted, i, svn_sort__item_t); 1933 svn_string_t *revlist; 1934 1935 SVN_ERR(svn_rangelist_to_string(&revlist, elt.value, pool)); 1936 svn_stringbuf_appendcstr( 1937 *output, 1938 apr_psprintf(pool, "%s%s%s:%s", 1939 prefix ? prefix : "", 1940 *((const char *) elt.key) == '/' ? "" : "/", 1941 (const char *) elt.key, 1942 revlist->data)); 1943 if (i < sorted->nelts - 1) 1944 svn_stringbuf_appendcstr(*output, "\n"); 1945 } 1946 } 1947 1948 return SVN_NO_ERROR; 1949} 1950 1951svn_error_t * 1952svn_mergeinfo_to_string(svn_string_t **output, svn_mergeinfo_t input, 1953 apr_pool_t *pool) 1954{ 1955 svn_stringbuf_t *mergeinfo_buf; 1956 1957 SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_buf, input, NULL, pool)); 1958 *output = svn_stringbuf__morph_into_string(mergeinfo_buf); 1959 return SVN_NO_ERROR; 1960} 1961 1962svn_error_t * 1963svn_mergeinfo_sort(svn_mergeinfo_t input, apr_pool_t *pool) 1964{ 1965 apr_hash_index_t *hi; 1966 1967 for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi)) 1968 { 1969 apr_array_header_t *rl = svn__apr_hash_index_val(hi); 1970 1971 qsort(rl->elts, rl->nelts, rl->elt_size, svn_sort_compare_ranges); 1972 } 1973 return SVN_NO_ERROR; 1974} 1975 1976svn_mergeinfo_catalog_t 1977svn_mergeinfo_catalog_dup(svn_mergeinfo_catalog_t mergeinfo_catalog, 1978 apr_pool_t *pool) 1979{ 1980 svn_mergeinfo_t new_mergeinfo_catalog = apr_hash_make(pool); 1981 apr_hash_index_t *hi; 1982 1983 for (hi = apr_hash_first(pool, mergeinfo_catalog); 1984 hi; 1985 hi = apr_hash_next(hi)) 1986 { 1987 const char *key = svn__apr_hash_index_key(hi); 1988 svn_mergeinfo_t val = svn__apr_hash_index_val(hi); 1989 1990 svn_hash_sets(new_mergeinfo_catalog, apr_pstrdup(pool, key), 1991 svn_mergeinfo_dup(val, pool)); 1992 } 1993 1994 return new_mergeinfo_catalog; 1995} 1996 1997svn_mergeinfo_t 1998svn_mergeinfo_dup(svn_mergeinfo_t mergeinfo, apr_pool_t *pool) 1999{ 2000 svn_mergeinfo_t new_mergeinfo = svn_hash__make(pool); 2001 apr_hash_index_t *hi; 2002 2003 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2004 { 2005 const char *path = svn__apr_hash_index_key(hi); 2006 apr_ssize_t pathlen = svn__apr_hash_index_klen(hi); 2007 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2008 2009 apr_hash_set(new_mergeinfo, apr_pstrmemdup(pool, path, pathlen), pathlen, 2010 svn_rangelist_dup(rangelist, pool)); 2011 } 2012 2013 return new_mergeinfo; 2014} 2015 2016svn_error_t * 2017svn_mergeinfo_inheritable2(svn_mergeinfo_t *output, 2018 svn_mergeinfo_t mergeinfo, 2019 const char *path, 2020 svn_revnum_t start, 2021 svn_revnum_t end, 2022 svn_boolean_t inheritable, 2023 apr_pool_t *result_pool, 2024 apr_pool_t *scratch_pool) 2025{ 2026 apr_hash_index_t *hi; 2027 svn_mergeinfo_t inheritable_mergeinfo = apr_hash_make(result_pool); 2028 2029 for (hi = apr_hash_first(scratch_pool, mergeinfo); 2030 hi; 2031 hi = apr_hash_next(hi)) 2032 { 2033 const char *key = svn__apr_hash_index_key(hi); 2034 apr_ssize_t keylen = svn__apr_hash_index_klen(hi); 2035 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2036 svn_rangelist_t *inheritable_rangelist; 2037 2038 if (!path || svn_path_compare_paths(path, key) == 0) 2039 SVN_ERR(svn_rangelist_inheritable2(&inheritable_rangelist, rangelist, 2040 start, end, inheritable, 2041 result_pool, scratch_pool)); 2042 else 2043 inheritable_rangelist = svn_rangelist_dup(rangelist, result_pool); 2044 2045 /* Only add this rangelist if some ranges remain. A rangelist with 2046 a path mapped to an empty rangelist is not syntactically valid */ 2047 if (inheritable_rangelist->nelts) 2048 apr_hash_set(inheritable_mergeinfo, 2049 apr_pstrmemdup(result_pool, key, keylen), keylen, 2050 inheritable_rangelist); 2051 } 2052 *output = inheritable_mergeinfo; 2053 return SVN_NO_ERROR; 2054} 2055 2056 2057svn_error_t * 2058svn_rangelist_inheritable2(svn_rangelist_t **inheritable_rangelist, 2059 const svn_rangelist_t *rangelist, 2060 svn_revnum_t start, 2061 svn_revnum_t end, 2062 svn_boolean_t inheritable, 2063 apr_pool_t *result_pool, 2064 apr_pool_t *scratch_pool) 2065{ 2066 *inheritable_rangelist = apr_array_make(result_pool, 1, 2067 sizeof(svn_merge_range_t *)); 2068 if (rangelist->nelts) 2069 { 2070 if (!SVN_IS_VALID_REVNUM(start) 2071 || !SVN_IS_VALID_REVNUM(end) 2072 || end < start) 2073 { 2074 int i; 2075 /* We want all non-inheritable ranges removed. */ 2076 for (i = 0; i < rangelist->nelts; i++) 2077 { 2078 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i, 2079 svn_merge_range_t *); 2080 if (range->inheritable == inheritable) 2081 { 2082 svn_merge_range_t *inheritable_range = 2083 apr_palloc(result_pool, sizeof(*inheritable_range)); 2084 inheritable_range->start = range->start; 2085 inheritable_range->end = range->end; 2086 inheritable_range->inheritable = TRUE; 2087 APR_ARRAY_PUSH(*inheritable_rangelist, 2088 svn_merge_range_t *) = range; 2089 } 2090 } 2091 } 2092 else 2093 { 2094 /* We want only the non-inheritable ranges bound by START 2095 and END removed. */ 2096 svn_rangelist_t *ranges_inheritable = 2097 svn_rangelist__initialize(start, end, inheritable, scratch_pool); 2098 2099 if (rangelist->nelts) 2100 SVN_ERR(svn_rangelist_remove(inheritable_rangelist, 2101 ranges_inheritable, 2102 rangelist, 2103 TRUE, 2104 result_pool)); 2105 } 2106 } 2107 return SVN_NO_ERROR; 2108} 2109 2110svn_boolean_t 2111svn_mergeinfo__remove_empty_rangelists(svn_mergeinfo_t mergeinfo, 2112 apr_pool_t *pool) 2113{ 2114 apr_hash_index_t *hi; 2115 svn_boolean_t removed_some_ranges = FALSE; 2116 2117 if (mergeinfo) 2118 { 2119 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2120 { 2121 const char *path = svn__apr_hash_index_key(hi); 2122 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2123 2124 if (rangelist->nelts == 0) 2125 { 2126 svn_hash_sets(mergeinfo, path, NULL); 2127 removed_some_ranges = TRUE; 2128 } 2129 } 2130 } 2131 return removed_some_ranges; 2132} 2133 2134svn_error_t * 2135svn_mergeinfo__remove_prefix_from_catalog(svn_mergeinfo_catalog_t *out_catalog, 2136 svn_mergeinfo_catalog_t in_catalog, 2137 const char *prefix_path, 2138 apr_pool_t *pool) 2139{ 2140 apr_hash_index_t *hi; 2141 2142 SVN_ERR_ASSERT(prefix_path[0] == '/'); 2143 2144 *out_catalog = apr_hash_make(pool); 2145 2146 for (hi = apr_hash_first(pool, in_catalog); hi; hi = apr_hash_next(hi)) 2147 { 2148 const char *original_path = svn__apr_hash_index_key(hi); 2149 svn_mergeinfo_t value = svn__apr_hash_index_val(hi); 2150 const char *new_path; 2151 2152 new_path = svn_fspath__skip_ancestor(prefix_path, original_path); 2153 SVN_ERR_ASSERT(new_path); 2154 2155 svn_hash_sets(*out_catalog, new_path, value); 2156 } 2157 2158 return SVN_NO_ERROR; 2159} 2160 2161svn_error_t * 2162svn_mergeinfo__add_prefix_to_catalog(svn_mergeinfo_catalog_t *out_catalog, 2163 svn_mergeinfo_catalog_t in_catalog, 2164 const char *prefix_path, 2165 apr_pool_t *result_pool, 2166 apr_pool_t *scratch_pool) 2167{ 2168 apr_hash_index_t *hi; 2169 2170 *out_catalog = apr_hash_make(result_pool); 2171 2172 for (hi = apr_hash_first(scratch_pool, in_catalog); 2173 hi; 2174 hi = apr_hash_next(hi)) 2175 { 2176 const char *original_path = svn__apr_hash_index_key(hi); 2177 svn_mergeinfo_t value = svn__apr_hash_index_val(hi); 2178 2179 if (original_path[0] == '/') 2180 original_path++; 2181 2182 svn_hash_sets(*out_catalog, 2183 svn_dirent_join(prefix_path, original_path, result_pool), 2184 value); 2185 } 2186 2187 return SVN_NO_ERROR; 2188} 2189 2190svn_error_t * 2191svn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo, 2192 svn_mergeinfo_t mergeinfo, 2193 const char *suffix_relpath, 2194 apr_pool_t *result_pool, 2195 apr_pool_t *scratch_pool) 2196{ 2197 apr_hash_index_t *hi; 2198 2199 SVN_ERR_ASSERT(suffix_relpath && svn_relpath_is_canonical(suffix_relpath)); 2200 2201 *out_mergeinfo = apr_hash_make(result_pool); 2202 2203 for (hi = apr_hash_first(scratch_pool, mergeinfo); 2204 hi; 2205 hi = apr_hash_next(hi)) 2206 { 2207 const char *fspath = svn__apr_hash_index_key(hi); 2208 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2209 2210 svn_hash_sets(*out_mergeinfo, 2211 svn_fspath__join(fspath, suffix_relpath, result_pool), 2212 rangelist); 2213 } 2214 2215 return SVN_NO_ERROR; 2216} 2217 2218svn_rangelist_t * 2219svn_rangelist_dup(const svn_rangelist_t *rangelist, apr_pool_t *pool) 2220{ 2221 svn_rangelist_t *new_rl = apr_array_make(pool, rangelist->nelts, 2222 sizeof(svn_merge_range_t *)); 2223 2224 /* allocate target range buffer with a single operation */ 2225 svn_merge_range_t *copy = apr_palloc(pool, sizeof(*copy) * rangelist->nelts); 2226 int i; 2227 2228 /* fill it iteratively and link it into the range list */ 2229 for (i = 0; i < rangelist->nelts; i++) 2230 { 2231 memcpy(copy + i, 2232 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *), 2233 sizeof(*copy)); 2234 APR_ARRAY_PUSH(new_rl, svn_merge_range_t *) = copy + i; 2235 } 2236 2237 return new_rl; 2238} 2239 2240svn_merge_range_t * 2241svn_merge_range_dup(const svn_merge_range_t *range, apr_pool_t *pool) 2242{ 2243 svn_merge_range_t *new_range = apr_palloc(pool, sizeof(*new_range)); 2244 memcpy(new_range, range, sizeof(*new_range)); 2245 return new_range; 2246} 2247 2248svn_boolean_t 2249svn_merge_range_contains_rev(const svn_merge_range_t *range, svn_revnum_t rev) 2250{ 2251 assert(SVN_IS_VALID_REVNUM(range->start)); 2252 assert(SVN_IS_VALID_REVNUM(range->end)); 2253 assert(range->start != range->end); 2254 2255 if (range->start < range->end) 2256 return rev > range->start && rev <= range->end; 2257 else 2258 return rev > range->end && rev <= range->start; 2259} 2260 2261svn_error_t * 2262svn_mergeinfo__catalog_to_formatted_string(svn_string_t **output, 2263 svn_mergeinfo_catalog_t catalog, 2264 const char *key_prefix, 2265 const char *val_prefix, 2266 apr_pool_t *pool) 2267{ 2268 svn_stringbuf_t *output_buf = NULL; 2269 2270 if (catalog && apr_hash_count(catalog)) 2271 { 2272 int i; 2273 apr_array_header_t *sorted_catalog = 2274 svn_sort__hash(catalog, svn_sort_compare_items_as_paths, pool); 2275 2276 output_buf = svn_stringbuf_create_empty(pool); 2277 for (i = 0; i < sorted_catalog->nelts; i++) 2278 { 2279 svn_sort__item_t elt = 2280 APR_ARRAY_IDX(sorted_catalog, i, svn_sort__item_t); 2281 const char *path1; 2282 svn_mergeinfo_t mergeinfo; 2283 svn_stringbuf_t *mergeinfo_output_buf; 2284 2285 path1 = elt.key; 2286 mergeinfo = elt.value; 2287 if (key_prefix) 2288 svn_stringbuf_appendcstr(output_buf, key_prefix); 2289 svn_stringbuf_appendcstr(output_buf, path1); 2290 svn_stringbuf_appendcstr(output_buf, "\n"); 2291 SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_output_buf, mergeinfo, 2292 val_prefix ? val_prefix : "", pool)); 2293 svn_stringbuf_appendstr(output_buf, mergeinfo_output_buf); 2294 svn_stringbuf_appendcstr(output_buf, "\n"); 2295 } 2296 } 2297#if SVN_DEBUG 2298 else if (!catalog) 2299 { 2300 output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool); 2301 svn_stringbuf_appendcstr(output_buf, _("NULL mergeinfo catalog\n")); 2302 } 2303 else if (apr_hash_count(catalog) == 0) 2304 { 2305 output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool); 2306 svn_stringbuf_appendcstr(output_buf, _("empty mergeinfo catalog\n")); 2307 } 2308#endif 2309 2310 /* If we have an output_buf, convert it to an svn_string_t; 2311 otherwise, return a new string containing only a newline 2312 character. */ 2313 if (output_buf) 2314 *output = svn_stringbuf__morph_into_string(output_buf); 2315 else 2316 *output = svn_string_create("\n", pool); 2317 2318 return SVN_NO_ERROR; 2319} 2320 2321svn_error_t * 2322svn_mergeinfo__get_range_endpoints(svn_revnum_t *youngest_rev, 2323 svn_revnum_t *oldest_rev, 2324 svn_mergeinfo_t mergeinfo, 2325 apr_pool_t *pool) 2326{ 2327 *youngest_rev = *oldest_rev = SVN_INVALID_REVNUM; 2328 if (mergeinfo) 2329 { 2330 apr_hash_index_t *hi; 2331 2332 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2333 { 2334 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2335 2336 if (rangelist->nelts) 2337 { 2338 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, 2339 rangelist->nelts - 1, 2340 svn_merge_range_t *); 2341 if (!SVN_IS_VALID_REVNUM(*youngest_rev) 2342 || (range->end > *youngest_rev)) 2343 *youngest_rev = range->end; 2344 2345 range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *); 2346 if (!SVN_IS_VALID_REVNUM(*oldest_rev) 2347 || (range->start < *oldest_rev)) 2348 *oldest_rev = range->start; 2349 } 2350 } 2351 } 2352 return SVN_NO_ERROR; 2353} 2354 2355svn_error_t * 2356svn_mergeinfo__filter_catalog_by_ranges(svn_mergeinfo_catalog_t *filtered_cat, 2357 svn_mergeinfo_catalog_t catalog, 2358 svn_revnum_t youngest_rev, 2359 svn_revnum_t oldest_rev, 2360 svn_boolean_t include_range, 2361 apr_pool_t *result_pool, 2362 apr_pool_t *scratch_pool) 2363{ 2364 apr_hash_index_t *hi; 2365 2366 *filtered_cat = apr_hash_make(result_pool); 2367 for (hi = apr_hash_first(scratch_pool, catalog); 2368 hi; 2369 hi = apr_hash_next(hi)) 2370 { 2371 const char *path = svn__apr_hash_index_key(hi); 2372 svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi); 2373 svn_mergeinfo_t filtered_mergeinfo; 2374 2375 SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&filtered_mergeinfo, 2376 mergeinfo, 2377 youngest_rev, 2378 oldest_rev, 2379 include_range, 2380 result_pool, 2381 scratch_pool)); 2382 if (apr_hash_count(filtered_mergeinfo)) 2383 svn_hash_sets(*filtered_cat, 2384 apr_pstrdup(result_pool, path), filtered_mergeinfo); 2385 } 2386 2387 return SVN_NO_ERROR; 2388} 2389 2390svn_error_t * 2391svn_mergeinfo__filter_mergeinfo_by_ranges(svn_mergeinfo_t *filtered_mergeinfo, 2392 svn_mergeinfo_t mergeinfo, 2393 svn_revnum_t youngest_rev, 2394 svn_revnum_t oldest_rev, 2395 svn_boolean_t include_range, 2396 apr_pool_t *result_pool, 2397 apr_pool_t *scratch_pool) 2398{ 2399 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); 2400 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev)); 2401 SVN_ERR_ASSERT(oldest_rev < youngest_rev); 2402 2403 *filtered_mergeinfo = apr_hash_make(result_pool); 2404 2405 if (mergeinfo) 2406 { 2407 apr_hash_index_t *hi; 2408 svn_rangelist_t *filter_rangelist = 2409 svn_rangelist__initialize(oldest_rev, youngest_rev, TRUE, 2410 scratch_pool); 2411 2412 for (hi = apr_hash_first(scratch_pool, mergeinfo); 2413 hi; 2414 hi = apr_hash_next(hi)) 2415 { 2416 const char *path = svn__apr_hash_index_key(hi); 2417 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2418 2419 if (rangelist->nelts) 2420 { 2421 svn_rangelist_t *new_rangelist; 2422 2423 SVN_ERR(rangelist_intersect_or_remove( 2424 &new_rangelist, filter_rangelist, rangelist, 2425 ! include_range, FALSE, result_pool)); 2426 2427 if (new_rangelist->nelts) 2428 svn_hash_sets(*filtered_mergeinfo, 2429 apr_pstrdup(result_pool, path), new_rangelist); 2430 } 2431 } 2432 } 2433 return SVN_NO_ERROR; 2434} 2435 2436svn_error_t * 2437svn_mergeinfo__adjust_mergeinfo_rangelists(svn_mergeinfo_t *adjusted_mergeinfo, 2438 svn_mergeinfo_t mergeinfo, 2439 svn_revnum_t offset, 2440 apr_pool_t *result_pool, 2441 apr_pool_t *scratch_pool) 2442{ 2443 apr_hash_index_t *hi; 2444 *adjusted_mergeinfo = apr_hash_make(result_pool); 2445 2446 if (mergeinfo) 2447 { 2448 for (hi = apr_hash_first(scratch_pool, mergeinfo); 2449 hi; 2450 hi = apr_hash_next(hi)) 2451 { 2452 int i; 2453 const char *path = svn__apr_hash_index_key(hi); 2454 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2455 svn_rangelist_t *adjusted_rangelist = 2456 apr_array_make(result_pool, rangelist->nelts, 2457 sizeof(svn_merge_range_t *)); 2458 2459 for (i = 0; i < rangelist->nelts; i++) 2460 { 2461 svn_merge_range_t *range = 2462 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 2463 2464 if (range->start + offset > 0 && range->end + offset > 0) 2465 { 2466 if (range->start + offset < 0) 2467 range->start = 0; 2468 else 2469 range->start = range->start + offset; 2470 2471 if (range->end + offset < 0) 2472 range->end = 0; 2473 else 2474 range->end = range->end + offset; 2475 APR_ARRAY_PUSH(adjusted_rangelist, svn_merge_range_t *) = 2476 range; 2477 } 2478 } 2479 2480 if (adjusted_rangelist->nelts) 2481 svn_hash_sets(*adjusted_mergeinfo, apr_pstrdup(result_pool, path), 2482 adjusted_rangelist); 2483 } 2484 } 2485 return SVN_NO_ERROR; 2486} 2487 2488svn_boolean_t 2489svn_mergeinfo__is_noninheritable(svn_mergeinfo_t mergeinfo, 2490 apr_pool_t *scratch_pool) 2491{ 2492 if (mergeinfo) 2493 { 2494 apr_hash_index_t *hi; 2495 2496 for (hi = apr_hash_first(scratch_pool, mergeinfo); 2497 hi; 2498 hi = apr_hash_next(hi)) 2499 { 2500 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2501 int i; 2502 2503 for (i = 0; i < rangelist->nelts; i++) 2504 { 2505 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i, 2506 svn_merge_range_t *); 2507 if (!range->inheritable) 2508 return TRUE; 2509 } 2510 } 2511 } 2512 return FALSE; 2513} 2514 2515svn_rangelist_t * 2516svn_rangelist__initialize(svn_revnum_t start, 2517 svn_revnum_t end, 2518 svn_boolean_t inheritable, 2519 apr_pool_t *result_pool) 2520{ 2521 svn_rangelist_t *rangelist = 2522 apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *)); 2523 svn_merge_range_t *range = apr_pcalloc(result_pool, sizeof(*range)); 2524 2525 range->start = start; 2526 range->end = end; 2527 range->inheritable = inheritable; 2528 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range; 2529 return rangelist; 2530} 2531 2532svn_error_t * 2533svn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p, 2534 const apr_array_header_t *segments, 2535 apr_pool_t *pool) 2536{ 2537 svn_mergeinfo_t mergeinfo = apr_hash_make(pool); 2538 int i; 2539 2540 /* Translate location segments into merge sources and ranges. */ 2541 for (i = 0; i < segments->nelts; i++) 2542 { 2543 svn_location_segment_t *segment = 2544 APR_ARRAY_IDX(segments, i, svn_location_segment_t *); 2545 svn_rangelist_t *path_ranges; 2546 svn_merge_range_t *range; 2547 const char *source_path; 2548 2549 /* No path segment? Skip it. */ 2550 if (! segment->path) 2551 continue; 2552 2553 /* Prepend a leading slash to our path. */ 2554 source_path = apr_pstrcat(pool, "/", segment->path, (char *)NULL); 2555 2556 /* See if we already stored ranges for this path. If not, make 2557 a new list. */ 2558 path_ranges = svn_hash_gets(mergeinfo, source_path); 2559 if (! path_ranges) 2560 path_ranges = apr_array_make(pool, 1, sizeof(range)); 2561 2562 /* A svn_location_segment_t may have legitimately describe only 2563 revision 0, but there is no corresponding representation for 2564 this in a svn_merge_range_t. */ 2565 if (segment->range_start == 0 && segment->range_end == 0) 2566 continue; 2567 2568 /* Build a merge range, push it onto the list of ranges, and for 2569 good measure, (re)store it in the hash. */ 2570 range = apr_pcalloc(pool, sizeof(*range)); 2571 range->start = MAX(segment->range_start - 1, 0); 2572 range->end = segment->range_end; 2573 range->inheritable = TRUE; 2574 APR_ARRAY_PUSH(path_ranges, svn_merge_range_t *) = range; 2575 svn_hash_sets(mergeinfo, source_path, path_ranges); 2576 } 2577 2578 *mergeinfo_p = mergeinfo; 2579 return SVN_NO_ERROR; 2580} 2581 2582svn_error_t * 2583svn_rangelist__merge_many(svn_rangelist_t *merged_rangelist, 2584 svn_mergeinfo_t merge_history, 2585 apr_pool_t *result_pool, 2586 apr_pool_t *scratch_pool) 2587{ 2588 if (apr_hash_count(merge_history)) 2589 { 2590 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2591 apr_hash_index_t *hi; 2592 2593 for (hi = apr_hash_first(scratch_pool, merge_history); 2594 hi; 2595 hi = apr_hash_next(hi)) 2596 { 2597 svn_rangelist_t *subtree_rangelist = svn__apr_hash_index_val(hi); 2598 2599 svn_pool_clear(iterpool); 2600 SVN_ERR(svn_rangelist_merge2(merged_rangelist, subtree_rangelist, 2601 result_pool, iterpool)); 2602 } 2603 svn_pool_destroy(iterpool); 2604 } 2605 return SVN_NO_ERROR; 2606} 2607 2608 2609const char * 2610svn_inheritance_to_word(svn_mergeinfo_inheritance_t inherit) 2611{ 2612 switch (inherit) 2613 { 2614 case svn_mergeinfo_inherited: 2615 return "inherited"; 2616 case svn_mergeinfo_nearest_ancestor: 2617 return "nearest-ancestor"; 2618 default: 2619 return "explicit"; 2620 } 2621} 2622 2623svn_mergeinfo_inheritance_t 2624svn_inheritance_from_word(const char *word) 2625{ 2626 if (strcmp(word, "inherited") == 0) 2627 return svn_mergeinfo_inherited; 2628 if (strcmp(word, "nearest-ancestor") == 0) 2629 return svn_mergeinfo_nearest_ancestor; 2630 return svn_mergeinfo_explicit; 2631} 2632