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