mergeinfo.c revision 289166
1275680Strasz/* 2275680Strasz * mergeinfo.c: Mergeinfo parsing and handling 3275680Strasz * 4275680Strasz * ==================================================================== 5275680Strasz * Licensed to the Apache Software Foundation (ASF) under one 6275680Strasz * or more contributor license agreements. See the NOTICE file 7275680Strasz * distributed with this work for additional information 8275680Strasz * regarding copyright ownership. The ASF licenses this file 9275680Strasz * to you under the Apache License, Version 2.0 (the 10275680Strasz * "License"); you may not use this file except in compliance 11275680Strasz * with the License. You may obtain a copy of the License at 12275680Strasz * 13275680Strasz * http://www.apache.org/licenses/LICENSE-2.0 14275680Strasz * 15275680Strasz * Unless required by applicable law or agreed to in writing, 16275680Strasz * software distributed under the License is distributed on an 17275680Strasz * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18275680Strasz * KIND, either express or implied. See the License for the 19275680Strasz * specific language governing permissions and limitations 20275680Strasz * under the License. 21275680Strasz * ==================================================================== 22275680Strasz */ 23275680Strasz#include <assert.h> 24275680Strasz#include <ctype.h> 25275680Strasz 26275680Strasz#include "svn_path.h" 27275680Strasz#include "svn_types.h" 28275680Strasz#include "svn_ctype.h" 29275680Strasz#include "svn_pools.h" 30275680Strasz#include "svn_sorts.h" 31275680Strasz#include "svn_error.h" 32275680Strasz#include "svn_error_codes.h" 33275680Strasz#include "svn_string.h" 34275680Strasz#include "svn_mergeinfo.h" 35275680Strasz#include "private/svn_fspath.h" 36275680Strasz#include "private/svn_mergeinfo_private.h" 37275680Strasz#include "private/svn_string_private.h" 38275680Strasz#include "private/svn_subr_private.h" 39275680Strasz#include "svn_private_config.h" 40275680Strasz#include "svn_hash.h" 41275680Strasz#include "private/svn_dep_compat.h" 42275680Strasz 43275680Strasz/* Attempt to combine two ranges, IN1 and IN2. If they are adjacent or 44275680Strasz overlapping, and their inheritability allows them to be combined, put 45275680Strasz the result in OUTPUT and return TRUE, otherwise return FALSE. 46275680Strasz 47275680Strasz CONSIDER_INHERITANCE determines how to account for the inheritability 48275680Strasz of IN1 and IN2 when trying to combine ranges. If ranges with different 49275680Strasz inheritability are combined (CONSIDER_INHERITANCE must be FALSE for this 50275680Strasz to happen) the result is inheritable. If both ranges are inheritable the 51275680Strasz result is inheritable. Only and if both ranges are non-inheritable is 52275680Strasz the result is non-inheritable. 53275680Strasz 54275680Strasz Range overlapping detection algorithm from 55275680Strasz http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap 56275680Strasz*/ 57275680Straszstatic svn_boolean_t 58275680Straszcombine_ranges(svn_merge_range_t *output, 59275680Strasz const svn_merge_range_t *in1, 60275680Strasz const svn_merge_range_t *in2, 61275680Strasz svn_boolean_t consider_inheritance) 62275680Strasz{ 63275680Strasz if (in1->start <= in2->end && in2->start <= in1->end) 64275680Strasz { 65275680Strasz if (!consider_inheritance 66275680Strasz || (consider_inheritance 67275680Strasz && (in1->inheritable == in2->inheritable))) 68275680Strasz { 69275680Strasz output->start = MIN(in1->start, in2->start); 70275680Strasz output->end = MAX(in1->end, in2->end); 71275680Strasz output->inheritable = (in1->inheritable || in2->inheritable); 72275680Strasz return TRUE; 73275680Strasz } 74275680Strasz } 75275680Strasz return FALSE; 76275680Strasz} 77275680Strasz 78275680Strasz/* pathname -> PATHNAME */ 79275680Straszstatic svn_error_t * 80275680Straszparse_pathname(const char **input, 81275680Strasz const char *end, 82275680Strasz const char **pathname, 83275680Strasz apr_pool_t *pool) 84275680Strasz{ 85275680Strasz const char *curr = *input; 86275680Strasz const char *last_colon = NULL; 87275680Strasz 88275680Strasz /* A pathname may contain colons, so find the last colon before END 89275680Strasz or newline. We'll consider this the divider between the pathname 90275680Strasz and the revisionlist. */ 91275680Strasz while (curr < end && *curr != '\n') 92275680Strasz { 93275680Strasz if (*curr == ':') 94275680Strasz last_colon = curr; 95275680Strasz curr++; 96275680Strasz } 97275680Strasz 98275680Strasz if (!last_colon) 99275680Strasz return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 100275680Strasz _("Pathname not terminated by ':'")); 101275680Strasz if (last_colon == *input) 102275680Strasz return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 103275680Strasz _("No pathname preceding ':'")); 104275680Strasz 105275680Strasz /* Tolerate relative repository paths, but convert them to absolute. 106275680Strasz ### Efficiency? 1 string duplication here, 2 in canonicalize. */ 107275680Strasz *pathname = svn_fspath__canonicalize(apr_pstrndup(pool, *input, 108275680Strasz last_colon - *input), 109275680Strasz pool); 110275680Strasz 111277436Strasz *input = last_colon; 112275680Strasz 113275680Strasz return SVN_NO_ERROR; 114275680Strasz} 115275680Strasz 116275680Strasz/* Return TRUE iff (svn_merge_range_t *) RANGE describes a valid, forward 117275680Strasz * revision range. 118275680Strasz * 119275680Strasz * Note: The smallest valid value of RANGE->start is 0 because it is an 120275680Strasz * exclusive endpoint, being one less than the revision number of the first 121275680Strasz * change described by the range, and the oldest possible change is "r1" as 122275680Strasz * there cannot be a change "r0". */ 123275680Strasz#define IS_VALID_FORWARD_RANGE(range) \ 124275680Strasz (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end)) 125275680Strasz 126275680Strasz/* Ways in which two svn_merge_range_t can intersect or adjoin, if at all. */ 127275680Strasztypedef enum intersection_type_t 128275680Strasz{ 129275680Strasz /* Ranges don't intersect and don't adjoin. */ 130275680Strasz svn__no_intersection, 131275680Strasz 132275680Strasz /* Ranges are equal. */ 133275680Strasz svn__equal_intersection, 134275680Strasz 135275680Strasz /* Ranges adjoin but don't overlap. */ 136275680Strasz svn__adjoining_intersection, 137275680Strasz 138275680Strasz /* Ranges overlap but neither is a subset of the other. */ 139275680Strasz svn__overlapping_intersection, 140275680Strasz 141275680Strasz /* One range is a proper subset of the other. */ 142275680Strasz svn__proper_subset_intersection 143275680Strasz} intersection_type_t; 144275680Strasz 145275680Strasz/* Given ranges R1 and R2, both of which must be forward merge ranges, 146275680Strasz set *INTERSECTION_TYPE to describe how the ranges intersect, if they 147275680Strasz do at all. The inheritance type of the ranges is not considered. */ 148275680Straszstatic svn_error_t * 149275680Straszget_type_of_intersection(const svn_merge_range_t *r1, 150275680Strasz const svn_merge_range_t *r2, 151275680Strasz intersection_type_t *intersection_type) 152275680Strasz{ 153275680Strasz SVN_ERR_ASSERT(r1); 154275680Strasz SVN_ERR_ASSERT(r2); 155275680Strasz SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r1)); 156275680Strasz SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r2)); 157275680Strasz 158275680Strasz if (!(r1->start <= r2->end && r2->start <= r1->end)) 159275680Strasz *intersection_type = svn__no_intersection; 160275680Strasz else if (r1->start == r2->start && r1->end == r2->end) 161275680Strasz *intersection_type = svn__equal_intersection; 162275680Strasz else if (r1->end == r2->start || r2->end == r1->start) 163275680Strasz *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 614/* Return TRUE, if all ranges in RANGELIST are in ascending order and do 615 * not overlap and are not adjacent. 616 * 617 * ### Can yield false negatives: ranges of differing inheritance are 618 * allowed to be adjacent. 619 * 620 * If this returns FALSE, you probaly want to qsort() the 621 * ranges and then call svn_rangelist__combine_adjacent_ranges(). 622 */ 623static svn_boolean_t 624is_rangelist_normalized(svn_rangelist_t *rangelist) 625{ 626 int i; 627 svn_merge_range_t **ranges = (svn_merge_range_t **)rangelist->elts; 628 629 for (i = 0; i < rangelist->nelts-1; ++i) 630 if (ranges[i]->end >= ranges[i+1]->start) 631 return FALSE; 632 633 return TRUE; 634} 635 636svn_error_t * 637svn_rangelist__canonicalize(svn_rangelist_t *rangelist, 638 apr_pool_t *scratch_pool) 639{ 640 if (! is_rangelist_normalized(rangelist)) 641 { 642 qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size, 643 svn_sort_compare_ranges); 644 645 SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool)); 646 } 647 648 return SVN_NO_ERROR; 649} 650 651svn_error_t * 652svn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist, 653 apr_pool_t *scratch_pool) 654{ 655 int i; 656 svn_merge_range_t *range, *lastrange; 657 658 lastrange = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *); 659 660 for (i = 1; i < rangelist->nelts; i++) 661 { 662 range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 663 if (lastrange->start <= range->end 664 && range->start <= lastrange->end) 665 { 666 /* The ranges are adjacent or intersect. */ 667 668 /* svn_mergeinfo_parse promises to combine overlapping 669 ranges as long as their inheritability is the same. */ 670 if (range->start < lastrange->end 671 && range->inheritable != lastrange->inheritable) 672 { 673 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 674 _("Unable to parse overlapping " 675 "revision ranges '%s' and '%s' " 676 "with different inheritance " 677 "types"), 678 range_to_string(lastrange, 679 scratch_pool), 680 range_to_string(range, 681 scratch_pool)); 682 } 683 684 /* Combine overlapping or adjacent ranges with the 685 same inheritability. */ 686 if (lastrange->inheritable == range->inheritable) 687 { 688 lastrange->end = MAX(range->end, lastrange->end); 689 svn_sort__array_delete(rangelist, i, 1); 690 i--; 691 } 692 } 693 lastrange = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 694 } 695 696 return SVN_NO_ERROR; 697} 698 699/* revisionline -> PATHNAME COLON revisionlist */ 700static svn_error_t * 701parse_revision_line(const char **input, const char *end, svn_mergeinfo_t hash, 702 apr_pool_t *scratch_pool) 703{ 704 const char *pathname = ""; 705 apr_ssize_t klen; 706 svn_rangelist_t *existing_rangelist; 707 svn_rangelist_t *rangelist = apr_array_make(scratch_pool, 1, 708 sizeof(svn_merge_range_t *)); 709 710 SVN_ERR(parse_pathname(input, end, &pathname, scratch_pool)); 711 712 if (*(*input) != ':') 713 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 714 _("Pathname not terminated by ':'")); 715 716 *input = *input + 1; 717 718 SVN_ERR(parse_rangelist(input, end, rangelist, scratch_pool)); 719 720 if (rangelist->nelts == 0) 721 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 722 _("Mergeinfo for '%s' maps to an " 723 "empty revision range"), pathname); 724 if (*input != end && *(*input) != '\n') 725 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL, 726 _("Could not find end of line in range list line " 727 "in '%s'"), *input); 728 729 if (*input != end) 730 *input = *input + 1; 731 732 /* Sort the rangelist, combine adjacent ranges into single ranges, and 733 make sure there are no overlapping ranges. Luckily, most data in 734 svn:mergeinfo will already be in normalized form and this will be quick. 735 */ 736 SVN_ERR(svn_rangelist__canonicalize(rangelist, scratch_pool)); 737 738 /* Handle any funky mergeinfo with relative merge source paths that 739 might exist due to issue #3547. It's possible that this issue allowed 740 the creation of mergeinfo with path keys that differ only by a 741 leading slash, e.g. "trunk:4033\n/trunk:4039-4995". In the event 742 we encounter this we merge the rangelists together under a single 743 absolute path key. */ 744 klen = strlen(pathname); 745 existing_rangelist = apr_hash_get(hash, pathname, klen); 746 if (existing_rangelist) 747 SVN_ERR(svn_rangelist_merge2(rangelist, existing_rangelist, 748 scratch_pool, scratch_pool)); 749 750 apr_hash_set(hash, apr_pstrmemdup(apr_hash_pool_get(hash), pathname, klen), 751 klen, svn_rangelist_dup(rangelist, apr_hash_pool_get(hash))); 752 753 return SVN_NO_ERROR; 754} 755 756/* top -> revisionline (NEWLINE revisionline)* */ 757static svn_error_t * 758parse_top(const char **input, const char *end, svn_mergeinfo_t hash, 759 apr_pool_t *pool) 760{ 761 apr_pool_t *iterpool = svn_pool_create(pool); 762 763 while (*input < end) 764 { 765 svn_pool_clear(iterpool); 766 SVN_ERR(parse_revision_line(input, end, hash, iterpool)); 767 } 768 svn_pool_destroy(iterpool); 769 770 return SVN_NO_ERROR; 771} 772 773svn_error_t * 774svn_mergeinfo_parse(svn_mergeinfo_t *mergeinfo, 775 const char *input, 776 apr_pool_t *pool) 777{ 778 svn_error_t *err; 779 780 *mergeinfo = svn_hash__make(pool); 781 err = parse_top(&input, input + strlen(input), *mergeinfo, pool); 782 783 /* Always return SVN_ERR_MERGEINFO_PARSE_ERROR as the topmost error. */ 784 if (err && err->apr_err != SVN_ERR_MERGEINFO_PARSE_ERROR) 785 err = svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, err, 786 _("Could not parse mergeinfo string '%s'"), 787 input); 788 return err; 789} 790 791/* Cleanup after svn_rangelist_merge2 when it modifies the ending range of 792 a single rangelist element in-place. 793 794 If *RANGE_INDEX is not a valid element in RANGELIST do nothing. Otherwise 795 ensure that RANGELIST[*RANGE_INDEX]->END does not adjoin or overlap any 796 subsequent ranges in RANGELIST. 797 798 If overlap is found, then remove, modify, and/or add elements to RANGELIST 799 as per the invariants for rangelists documented in svn_mergeinfo.h. If 800 RANGELIST[*RANGE_INDEX]->END adjoins a subsequent element then combine the 801 elements if their inheritability permits -- The inheritance of intersecting 802 and adjoining ranges is handled as per svn_mergeinfo_merge2. Upon return 803 set *RANGE_INDEX to the index of the youngest element modified, added, or 804 adjoined to RANGELIST[*RANGE_INDEX]. 805 806 Note: Adjoining rangelist elements are those where the end rev of the older 807 element is equal to the start rev of the younger element. 808 809 Any new elements inserted into RANGELIST are allocated in RESULT_POOL.*/ 810static void 811adjust_remaining_ranges(svn_rangelist_t *rangelist, 812 int *range_index, 813 apr_pool_t *result_pool) 814{ 815 int i; 816 int starting_index; 817 int elements_to_delete = 0; 818 svn_merge_range_t *modified_range; 819 820 if (*range_index >= rangelist->nelts) 821 return; 822 823 starting_index = *range_index + 1; 824 modified_range = APR_ARRAY_IDX(rangelist, *range_index, svn_merge_range_t *); 825 826 for (i = *range_index + 1; i < rangelist->nelts; i++) 827 { 828 svn_merge_range_t *next_range = APR_ARRAY_IDX(rangelist, i, 829 svn_merge_range_t *); 830 831 /* If MODIFIED_RANGE doesn't adjoin or overlap the next range in 832 RANGELIST then we are finished. */ 833 if (modified_range->end < next_range->start) 834 break; 835 836 /* Does MODIFIED_RANGE adjoin NEXT_RANGE? */ 837 if (modified_range->end == next_range->start) 838 { 839 if (modified_range->inheritable == next_range->inheritable) 840 { 841 /* Combine adjoining ranges with the same inheritability. */ 842 modified_range->end = next_range->end; 843 elements_to_delete++; 844 } 845 else 846 { 847 /* Cannot join because inheritance differs. */ 848 (*range_index)++; 849 } 850 break; 851 } 852 853 /* Alright, we know MODIFIED_RANGE overlaps NEXT_RANGE, but how? */ 854 if (modified_range->end > next_range->end) 855 { 856 /* NEXT_RANGE is a proper subset of MODIFIED_RANGE and the two 857 don't share the same end range. */ 858 if (modified_range->inheritable 859 || (modified_range->inheritable == next_range->inheritable)) 860 { 861 /* MODIFIED_RANGE absorbs NEXT_RANGE. */ 862 elements_to_delete++; 863 } 864 else 865 { 866 /* NEXT_RANGE is a proper subset MODIFIED_RANGE but 867 MODIFIED_RANGE is non-inheritable and NEXT_RANGE is 868 inheritable. This means MODIFIED_RANGE is truncated, 869 NEXT_RANGE remains, and the portion of MODIFIED_RANGE 870 younger than NEXT_RANGE is added as a separate range: 871 ______________________________________________ 872 | | 873 M MODIFIED_RANGE N 874 | (!inhertiable) | 875 |______________________________________________| 876 | | 877 O NEXT_RANGE P 878 | (inheritable)| 879 |______________| 880 | 881 V 882 _______________________________________________ 883 | | | | 884 M MODIFIED_RANGE O NEXT_RANGE P NEW_RANGE N 885 | (!inhertiable) | (inheritable)| (!inheritable)| 886 |________________|______________|_______________| 887 */ 888 svn_merge_range_t *new_modified_range = 889 apr_palloc(result_pool, sizeof(*new_modified_range)); 890 new_modified_range->start = next_range->end; 891 new_modified_range->end = modified_range->end; 892 new_modified_range->inheritable = FALSE; 893 modified_range->end = next_range->start; 894 (*range_index)+=2; 895 svn_sort__array_insert(&new_modified_range, rangelist, 896 *range_index); 897 /* Recurse with the new range. */ 898 adjust_remaining_ranges(rangelist, range_index, result_pool); 899 break; 900 } 901 } 902 else if (modified_range->end == next_range->end) 903 { 904 /* NEXT_RANGE is a proper subset MODIFIED_RANGE and share 905 the same end range. */ 906 if (modified_range->inheritable 907 || (modified_range->inheritable == next_range->inheritable)) 908 { 909 /* MODIFIED_RANGE absorbs NEXT_RANGE. */ 910 elements_to_delete++; 911 } 912 else 913 { 914 /* The intersection between MODIFIED_RANGE and NEXT_RANGE is 915 absorbed by the latter. */ 916 modified_range->end = next_range->start; 917 (*range_index)++; 918 } 919 break; 920 } 921 else 922 { 923 /* NEXT_RANGE and MODIFIED_RANGE intersect but NEXT_RANGE is not 924 a proper subset of MODIFIED_RANGE, nor do the two share the 925 same end revision, i.e. they overlap. */ 926 if (modified_range->inheritable == next_range->inheritable) 927 { 928 /* Combine overlapping ranges with the same inheritability. */ 929 modified_range->end = next_range->end; 930 elements_to_delete++; 931 } 932 else if (modified_range->inheritable) 933 { 934 /* MODIFIED_RANGE absorbs the portion of NEXT_RANGE it overlaps 935 and NEXT_RANGE is truncated. */ 936 next_range->start = modified_range->end; 937 (*range_index)++; 938 } 939 else 940 { 941 /* NEXT_RANGE absorbs the portion of MODIFIED_RANGE it overlaps 942 and MODIFIED_RANGE is truncated. */ 943 modified_range->end = next_range->start; 944 (*range_index)++; 945 } 946 break; 947 } 948 } 949 950 if (elements_to_delete) 951 svn_sort__array_delete(rangelist, starting_index, elements_to_delete); 952} 953 954svn_error_t * 955svn_rangelist_merge2(svn_rangelist_t *rangelist, 956 const svn_rangelist_t *changes, 957 apr_pool_t *result_pool, 958 apr_pool_t *scratch_pool) 959{ 960 int i = 0; 961 int j = 0; 962 963 /* We may modify CHANGES, so make a copy in SCRATCH_POOL. */ 964 changes = svn_rangelist_dup(changes, scratch_pool); 965 966 while (i < rangelist->nelts && j < changes->nelts) 967 { 968 svn_merge_range_t *range = 969 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 970 svn_merge_range_t *change = 971 APR_ARRAY_IDX(changes, j, svn_merge_range_t *); 972 int res = svn_sort_compare_ranges(&range, &change); 973 974 if (res == 0) 975 { 976 /* Only when merging two non-inheritable ranges is the result also 977 non-inheritable. In all other cases ensure an inheritiable 978 result. */ 979 if (range->inheritable || change->inheritable) 980 range->inheritable = TRUE; 981 i++; 982 j++; 983 } 984 else if (res < 0) /* CHANGE is younger than RANGE */ 985 { 986 if (range->end < change->start) 987 { 988 /* RANGE is older than CHANGE and the two do not 989 adjoin or overlap */ 990 i++; 991 } 992 else if (range->end == change->start) 993 { 994 /* RANGE and CHANGE adjoin */ 995 if (range->inheritable == change->inheritable) 996 { 997 /* RANGE and CHANGE have the same inheritability so 998 RANGE expands to absord CHANGE. */ 999 range->end = change->end; 1000 adjust_remaining_ranges(rangelist, &i, result_pool); 1001 j++; 1002 } 1003 else 1004 { 1005 /* RANGE and CHANGE adjoin, but have different 1006 inheritability. Since RANGE is older, just 1007 move on to the next RANGE. */ 1008 i++; 1009 } 1010 } 1011 else 1012 { 1013 /* RANGE and CHANGE overlap, but how? */ 1014 if ((range->inheritable == change->inheritable) 1015 || range->inheritable) 1016 { 1017 /* If CHANGE is a proper subset of RANGE, it absorbs RANGE 1018 with no adjustment otherwise only the intersection is 1019 absorbed and CHANGE is truncated. */ 1020 if (range->end >= change->end) 1021 j++; 1022 else 1023 change->start = range->end; 1024 } 1025 else 1026 { 1027 /* RANGE is non-inheritable and CHANGE is inheritable. */ 1028 if (range->start < change->start) 1029 { 1030 /* CHANGE absorbs intersection with RANGE and RANGE 1031 is truncated. */ 1032 svn_merge_range_t *range_copy = 1033 svn_merge_range_dup(range, result_pool); 1034 range_copy->end = change->start; 1035 range->start = change->start; 1036 svn_sort__array_insert(&range_copy, rangelist, i++); 1037 } 1038 else 1039 { 1040 /* CHANGE and RANGE share the same start rev, but 1041 RANGE is considered older because its end rev 1042 is older. */ 1043 range->inheritable = TRUE; 1044 change->start = range->end; 1045 } 1046 } 1047 } 1048 } 1049 else /* res > 0, CHANGE is older than RANGE */ 1050 { 1051 if (change->end < range->start) 1052 { 1053 /* CHANGE is older than RANGE and the two do not 1054 adjoin or overlap, so insert a copy of CHANGE 1055 into RANGELIST. */ 1056 svn_merge_range_t *change_copy = 1057 svn_merge_range_dup(change, result_pool); 1058 svn_sort__array_insert(&change_copy, rangelist, i++); 1059 j++; 1060 } 1061 else if (change->end == range->start) 1062 { 1063 /* RANGE and CHANGE adjoin */ 1064 if (range->inheritable == change->inheritable) 1065 { 1066 /* RANGE and CHANGE have the same inheritability so we 1067 can simply combine the two in place. */ 1068 range->start = change->start; 1069 j++; 1070 } 1071 else 1072 { 1073 /* RANGE and CHANGE have different inheritability so insert 1074 a copy of CHANGE into RANGELIST. */ 1075 svn_merge_range_t *change_copy = 1076 svn_merge_range_dup(change, result_pool); 1077 svn_sort__array_insert(&change_copy, rangelist, i); 1078 j++; 1079 } 1080 } 1081 else 1082 { 1083 /* RANGE and CHANGE overlap. */ 1084 if (range->inheritable == change->inheritable) 1085 { 1086 /* RANGE and CHANGE have the same inheritability so we 1087 can simply combine the two in place... */ 1088 range->start = change->start; 1089 if (range->end < change->end) 1090 { 1091 /* ...but if RANGE is expanded ensure that we don't 1092 violate any rangelist invariants. */ 1093 range->end = change->end; 1094 adjust_remaining_ranges(rangelist, &i, result_pool); 1095 } 1096 j++; 1097 } 1098 else if (range->inheritable) 1099 { 1100 if (change->start < range->start) 1101 { 1102 /* RANGE is inheritable so absorbs any part of CHANGE 1103 it overlaps. CHANGE is truncated and the remainder 1104 inserted into RANGELIST. */ 1105 svn_merge_range_t *change_copy = 1106 svn_merge_range_dup(change, result_pool); 1107 change_copy->end = range->start; 1108 change->start = range->start; 1109 svn_sort__array_insert(&change_copy, rangelist, i++); 1110 } 1111 else 1112 { 1113 /* CHANGE and RANGE share the same start rev, but 1114 CHANGE is considered older because CHANGE->END is 1115 older than RANGE->END. */ 1116 j++; 1117 } 1118 } 1119 else 1120 { 1121 /* RANGE is non-inheritable and CHANGE is inheritable. */ 1122 if (change->start < range->start) 1123 { 1124 if (change->end == range->end) 1125 { 1126 /* RANGE is a proper subset of CHANGE and share the 1127 same end revision, so set RANGE equal to CHANGE. */ 1128 range->start = change->start; 1129 range->inheritable = TRUE; 1130 j++; 1131 } 1132 else if (change->end > range->end) 1133 { 1134 /* RANGE is a proper subset of CHANGE and CHANGE has 1135 a younger end revision, so set RANGE equal to its 1136 intersection with CHANGE and truncate CHANGE. */ 1137 range->start = change->start; 1138 range->inheritable = TRUE; 1139 change->start = range->end; 1140 } 1141 else 1142 { 1143 /* CHANGE and RANGE overlap. Set RANGE equal to its 1144 intersection with CHANGE and take the remainder 1145 of RANGE and insert it into RANGELIST. */ 1146 svn_merge_range_t *range_copy = 1147 svn_merge_range_dup(range, result_pool); 1148 range_copy->start = change->end; 1149 range->start = change->start; 1150 range->end = change->end; 1151 range->inheritable = TRUE; 1152 svn_sort__array_insert(&range_copy, rangelist, ++i); 1153 j++; 1154 } 1155 } 1156 else 1157 { 1158 /* CHANGE and RANGE share the same start rev, but 1159 CHANGE is considered older because its end rev 1160 is older. 1161 1162 Insert the intersection of RANGE and CHANGE into 1163 RANGELIST and then set RANGE to the non-intersecting 1164 portion of RANGE. */ 1165 svn_merge_range_t *range_copy = 1166 svn_merge_range_dup(range, result_pool); 1167 range_copy->end = change->end; 1168 range_copy->inheritable = TRUE; 1169 range->start = change->end; 1170 svn_sort__array_insert(&range_copy, rangelist, i++); 1171 j++; 1172 } 1173 } 1174 } 1175 } 1176 } 1177 1178 /* Copy any remaining elements in CHANGES into RANGELIST. */ 1179 for (; j < (changes)->nelts; j++) 1180 { 1181 svn_merge_range_t *change = 1182 APR_ARRAY_IDX(changes, j, svn_merge_range_t *); 1183 svn_merge_range_t *change_copy = svn_merge_range_dup(change, 1184 result_pool); 1185 svn_sort__array_insert(&change_copy, rangelist, rangelist->nelts); 1186 } 1187 1188 return SVN_NO_ERROR; 1189} 1190 1191/* Return TRUE iff the forward revision ranges FIRST and SECOND overlap and 1192 * (if CONSIDER_INHERITANCE is TRUE) have the same inheritability. */ 1193static svn_boolean_t 1194range_intersect(const svn_merge_range_t *first, const svn_merge_range_t *second, 1195 svn_boolean_t consider_inheritance) 1196{ 1197 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first)); 1198 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second)); 1199 1200 return (first->start + 1 <= second->end) 1201 && (second->start + 1 <= first->end) 1202 && (!consider_inheritance 1203 || (!(first->inheritable) == !(second->inheritable))); 1204} 1205 1206/* Return TRUE iff the forward revision range FIRST wholly contains the 1207 * forward revision range SECOND and (if CONSIDER_INHERITANCE is TRUE) has 1208 * the same inheritability. */ 1209static svn_boolean_t 1210range_contains(const svn_merge_range_t *first, const svn_merge_range_t *second, 1211 svn_boolean_t consider_inheritance) 1212{ 1213 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first)); 1214 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second)); 1215 1216 return (first->start <= second->start) && (second->end <= first->end) 1217 && (!consider_inheritance 1218 || (!(first->inheritable) == !(second->inheritable))); 1219} 1220 1221/* Swap start and end fields of RANGE. */ 1222static void 1223range_swap_endpoints(svn_merge_range_t *range) 1224{ 1225 svn_revnum_t swap = range->start; 1226 range->start = range->end; 1227 range->end = swap; 1228} 1229 1230svn_error_t * 1231svn_rangelist_reverse(svn_rangelist_t *rangelist, apr_pool_t *pool) 1232{ 1233 int i; 1234 1235 svn_sort__array_reverse(rangelist, pool); 1236 1237 for (i = 0; i < rangelist->nelts; i++) 1238 { 1239 range_swap_endpoints(APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *)); 1240 } 1241 1242 return SVN_NO_ERROR; 1243} 1244 1245void 1246svn_rangelist__set_inheritance(svn_rangelist_t *rangelist, 1247 svn_boolean_t inheritable) 1248{ 1249 if (rangelist) 1250 { 1251 int i; 1252 svn_merge_range_t *range; 1253 1254 for (i = 0; i < rangelist->nelts; i++) 1255 { 1256 range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 1257 range->inheritable = inheritable; 1258 } 1259 } 1260 return; 1261} 1262 1263void 1264svn_mergeinfo__set_inheritance(svn_mergeinfo_t mergeinfo, 1265 svn_boolean_t inheritable, 1266 apr_pool_t *scratch_pool) 1267{ 1268 if (mergeinfo) 1269 { 1270 apr_hash_index_t *hi; 1271 1272 for (hi = apr_hash_first(scratch_pool, mergeinfo); 1273 hi; 1274 hi = apr_hash_next(hi)) 1275 { 1276 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 1277 1278 if (rangelist) 1279 svn_rangelist__set_inheritance(rangelist, inheritable); 1280 } 1281 } 1282 return; 1283} 1284 1285/* If DO_REMOVE is true, then remove any overlapping ranges described by 1286 RANGELIST1 from RANGELIST2 and place the results in *OUTPUT. When 1287 DO_REMOVE is true, RANGELIST1 is effectively the "eraser" and RANGELIST2 1288 the "whiteboard". 1289 1290 If DO_REMOVE is false, then capture the intersection between RANGELIST1 1291 and RANGELIST2 and place the results in *OUTPUT. The ordering of 1292 RANGELIST1 and RANGELIST2 doesn't matter when DO_REMOVE is false. 1293 1294 If CONSIDER_INHERITANCE is true, then take the inheritance of the 1295 ranges in RANGELIST1 and RANGELIST2 into account when comparing them 1296 for intersection, see the doc string for svn_rangelist_intersect(). 1297 1298 If CONSIDER_INHERITANCE is false, then ranges with differing inheritance 1299 may intersect, but the resulting intersection is non-inheritable only 1300 if both ranges were non-inheritable, e.g.: 1301 1302 RANGELIST1 RANGELIST2 CONSIDER DO_REMOVE *OUTPUT 1303 INHERITANCE 1304 ---------- ------ ----------- --------- ------- 1305 1306 90-420* 1-100 TRUE FALSE Empty Rangelist 1307 90-420 1-100* TRUE FALSE Empty Rangelist 1308 90-420 1-100 TRUE FALSE 90-100 1309 90-420* 1-100* TRUE FALSE 90-100* 1310 1311 90-420* 1-100 FALSE FALSE 90-100 1312 90-420 1-100* FALSE FALSE 90-100 1313 90-420 1-100 FALSE FALSE 90-100 1314 90-420* 1-100* FALSE FALSE 90-100* 1315 1316 Allocate the contents of *OUTPUT in POOL. */ 1317static svn_error_t * 1318rangelist_intersect_or_remove(svn_rangelist_t **output, 1319 const svn_rangelist_t *rangelist1, 1320 const svn_rangelist_t *rangelist2, 1321 svn_boolean_t do_remove, 1322 svn_boolean_t consider_inheritance, 1323 apr_pool_t *pool) 1324{ 1325 int i1, i2, lasti2; 1326 svn_merge_range_t working_elt2; 1327 1328 *output = apr_array_make(pool, 1, sizeof(svn_merge_range_t *)); 1329 1330 i1 = 0; 1331 i2 = 0; 1332 lasti2 = -1; /* Initialized to a value that "i2" will never be. */ 1333 1334 while (i1 < rangelist1->nelts && i2 < rangelist2->nelts) 1335 { 1336 svn_merge_range_t *elt1, *elt2; 1337 1338 elt1 = APR_ARRAY_IDX(rangelist1, i1, svn_merge_range_t *); 1339 1340 /* Instead of making a copy of the entire array of rangelist2 1341 elements, we just keep a copy of the current rangelist2 element 1342 that needs to be used, and modify our copy if necessary. */ 1343 if (i2 != lasti2) 1344 { 1345 working_elt2 = 1346 *(APR_ARRAY_IDX(rangelist2, i2, svn_merge_range_t *)); 1347 lasti2 = i2; 1348 } 1349 1350 elt2 = &working_elt2; 1351 1352 /* If the rangelist2 range is contained completely in the 1353 rangelist1, we increment the rangelist2. 1354 If the ranges intersect, and match exactly, we increment both 1355 rangelist1 and rangelist2. 1356 Otherwise, we have to generate a range for the left part of 1357 the removal of rangelist1 from rangelist2, and possibly change 1358 the rangelist2 to the remaining portion of the right part of 1359 the removal, to test against. */ 1360 if (range_contains(elt1, elt2, consider_inheritance)) 1361 { 1362 if (!do_remove) 1363 { 1364 svn_merge_range_t tmp_range; 1365 tmp_range.start = elt2->start; 1366 tmp_range.end = elt2->end; 1367 /* The intersection of two ranges is non-inheritable only 1368 if both ranges are non-inheritable. */ 1369 tmp_range.inheritable = 1370 (elt2->inheritable || elt1->inheritable); 1371 SVN_ERR(combine_with_lastrange(&tmp_range, *output, 1372 consider_inheritance, 1373 pool)); 1374 } 1375 1376 i2++; 1377 1378 if (elt2->start == elt1->start && elt2->end == elt1->end) 1379 i1++; 1380 } 1381 else if (range_intersect(elt1, elt2, consider_inheritance)) 1382 { 1383 if (elt2->start < elt1->start) 1384 { 1385 /* The rangelist2 range starts before the rangelist1 range. */ 1386 svn_merge_range_t tmp_range; 1387 if (do_remove) 1388 { 1389 /* Retain the range that falls before the rangelist1 1390 start. */ 1391 tmp_range.start = elt2->start; 1392 tmp_range.end = elt1->start; 1393 tmp_range.inheritable = elt2->inheritable; 1394 } 1395 else 1396 { 1397 /* Retain the range that falls between the rangelist1 1398 start and rangelist2 end. */ 1399 tmp_range.start = elt1->start; 1400 tmp_range.end = MIN(elt2->end, elt1->end); 1401 /* The intersection of two ranges is non-inheritable only 1402 if both ranges are non-inheritable. */ 1403 tmp_range.inheritable = 1404 (elt2->inheritable || elt1->inheritable); 1405 } 1406 1407 SVN_ERR(combine_with_lastrange(&tmp_range, 1408 *output, consider_inheritance, 1409 pool)); 1410 } 1411 1412 /* Set up the rest of the rangelist2 range for further 1413 processing. */ 1414 if (elt2->end > elt1->end) 1415 { 1416 /* The rangelist2 range ends after the rangelist1 range. */ 1417 if (!do_remove) 1418 { 1419 /* Partial overlap. */ 1420 svn_merge_range_t tmp_range; 1421 tmp_range.start = MAX(elt2->start, elt1->start); 1422 tmp_range.end = elt1->end; 1423 /* The intersection of two ranges is non-inheritable only 1424 if both ranges are non-inheritable. */ 1425 tmp_range.inheritable = 1426 (elt2->inheritable || elt1->inheritable); 1427 SVN_ERR(combine_with_lastrange(&tmp_range, 1428 *output, 1429 consider_inheritance, 1430 pool)); 1431 } 1432 1433 working_elt2.start = elt1->end; 1434 working_elt2.end = elt2->end; 1435 } 1436 else 1437 i2++; 1438 } 1439 else /* ranges don't intersect */ 1440 { 1441 /* See which side of the rangelist2 the rangelist1 is on. If it 1442 is on the left side, we need to move the rangelist1. 1443 1444 If it is on past the rangelist2 on the right side, we 1445 need to output the rangelist2 and increment the 1446 rangelist2. */ 1447 if (svn_sort_compare_ranges(&elt1, &elt2) < 0) 1448 i1++; 1449 else 1450 { 1451 svn_merge_range_t *lastrange; 1452 1453 if ((*output)->nelts > 0) 1454 lastrange = APR_ARRAY_IDX(*output, (*output)->nelts - 1, 1455 svn_merge_range_t *); 1456 else 1457 lastrange = NULL; 1458 1459 if (do_remove && !(lastrange && 1460 combine_ranges(lastrange, lastrange, elt2, 1461 consider_inheritance))) 1462 { 1463 lastrange = svn_merge_range_dup(elt2, pool); 1464 APR_ARRAY_PUSH(*output, svn_merge_range_t *) = lastrange; 1465 } 1466 i2++; 1467 } 1468 } 1469 } 1470 1471 if (do_remove) 1472 { 1473 /* Copy the current rangelist2 element if we didn't hit the end 1474 of the rangelist2, and we still had it around. This element 1475 may have been touched, so we can't just walk the rangelist2 1476 array, we have to use our copy. This case only happens when 1477 we ran out of rangelist1 before rangelist2, *and* we had changed 1478 the rangelist2 element. */ 1479 if (i2 == lasti2 && i2 < rangelist2->nelts) 1480 { 1481 SVN_ERR(combine_with_lastrange(&working_elt2, *output, 1482 consider_inheritance, pool)); 1483 i2++; 1484 } 1485 1486 /* Copy any other remaining untouched rangelist2 elements. */ 1487 for (; i2 < rangelist2->nelts; i2++) 1488 { 1489 svn_merge_range_t *elt = APR_ARRAY_IDX(rangelist2, i2, 1490 svn_merge_range_t *); 1491 1492 SVN_ERR(combine_with_lastrange(elt, *output, 1493 consider_inheritance, pool)); 1494 } 1495 } 1496 1497 return SVN_NO_ERROR; 1498} 1499 1500 1501svn_error_t * 1502svn_rangelist_intersect(svn_rangelist_t **output, 1503 const svn_rangelist_t *rangelist1, 1504 const svn_rangelist_t *rangelist2, 1505 svn_boolean_t consider_inheritance, 1506 apr_pool_t *pool) 1507{ 1508 return rangelist_intersect_or_remove(output, rangelist1, rangelist2, FALSE, 1509 consider_inheritance, pool); 1510} 1511 1512svn_error_t * 1513svn_rangelist_remove(svn_rangelist_t **output, 1514 const svn_rangelist_t *eraser, 1515 const svn_rangelist_t *whiteboard, 1516 svn_boolean_t consider_inheritance, 1517 apr_pool_t *pool) 1518{ 1519 return rangelist_intersect_or_remove(output, eraser, whiteboard, TRUE, 1520 consider_inheritance, pool); 1521} 1522 1523svn_error_t * 1524svn_rangelist_diff(svn_rangelist_t **deleted, svn_rangelist_t **added, 1525 const svn_rangelist_t *from, const svn_rangelist_t *to, 1526 svn_boolean_t consider_inheritance, 1527 apr_pool_t *pool) 1528{ 1529 /* The following diagrams illustrate some common range delta scenarios: 1530 1531 (from) deleted 1532 r0 <===========(=========)============[=========]===========> rHEAD 1533 [to] added 1534 1535 (from) deleted deleted 1536 r0 <===========(=========[============]=========)===========> rHEAD 1537 [to] 1538 1539 (from) deleted 1540 r0 <===========(=========[============)=========]===========> rHEAD 1541 [to] added 1542 1543 (from) deleted 1544 r0 <===========[=========(============]=========)===========> rHEAD 1545 [to] added 1546 1547 (from) 1548 r0 <===========[=========(============)=========]===========> rHEAD 1549 [to] added added 1550 1551 (from) d d d 1552 r0 <===(=[=)=]=[==]=[=(=)=]=[=]=[=(===|===(=)==|=|==[=(=]=)=> rHEAD 1553 [to] a a a a a a a 1554 */ 1555 1556 /* The items that are present in from, but not in to, must have been 1557 deleted. */ 1558 SVN_ERR(svn_rangelist_remove(deleted, to, from, consider_inheritance, 1559 pool)); 1560 /* The items that are present in to, but not in from, must have been 1561 added. */ 1562 return svn_rangelist_remove(added, from, to, consider_inheritance, pool); 1563} 1564 1565struct mergeinfo_diff_baton 1566{ 1567 svn_mergeinfo_t from; 1568 svn_mergeinfo_t to; 1569 svn_mergeinfo_t deleted; 1570 svn_mergeinfo_t added; 1571 svn_boolean_t consider_inheritance; 1572 apr_pool_t *pool; 1573}; 1574 1575/* This implements the 'svn_hash_diff_func_t' interface. 1576 BATON is of type 'struct mergeinfo_diff_baton *'. 1577*/ 1578static svn_error_t * 1579mergeinfo_hash_diff_cb(const void *key, apr_ssize_t klen, 1580 enum svn_hash_diff_key_status status, 1581 void *baton) 1582{ 1583 /* hash_a is FROM mergeinfo, 1584 hash_b is TO mergeinfo. */ 1585 struct mergeinfo_diff_baton *cb = baton; 1586 svn_rangelist_t *from_rangelist, *to_rangelist; 1587 const char *path = key; 1588 if (status == svn_hash_diff_key_both) 1589 { 1590 /* Record any deltas (additions or deletions). */ 1591 svn_rangelist_t *deleted_rangelist, *added_rangelist; 1592 from_rangelist = apr_hash_get(cb->from, path, klen); 1593 to_rangelist = apr_hash_get(cb->to, path, klen); 1594 SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist, 1595 from_rangelist, to_rangelist, 1596 cb->consider_inheritance, cb->pool)); 1597 if (cb->deleted && deleted_rangelist->nelts > 0) 1598 apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen), 1599 klen, deleted_rangelist); 1600 if (cb->added && added_rangelist->nelts > 0) 1601 apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen), 1602 klen, added_rangelist); 1603 } 1604 else if ((status == svn_hash_diff_key_a) && cb->deleted) 1605 { 1606 from_rangelist = apr_hash_get(cb->from, path, klen); 1607 apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen), klen, 1608 svn_rangelist_dup(from_rangelist, cb->pool)); 1609 } 1610 else if ((status == svn_hash_diff_key_b) && cb->added) 1611 { 1612 to_rangelist = apr_hash_get(cb->to, path, klen); 1613 apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen), klen, 1614 svn_rangelist_dup(to_rangelist, cb->pool)); 1615 } 1616 return SVN_NO_ERROR; 1617} 1618 1619/* Record deletions and additions of entire range lists (by path 1620 presence), and delegate to svn_rangelist_diff() for delta 1621 calculations on a specific path. */ 1622static svn_error_t * 1623walk_mergeinfo_hash_for_diff(svn_mergeinfo_t from, svn_mergeinfo_t to, 1624 svn_mergeinfo_t deleted, svn_mergeinfo_t added, 1625 svn_boolean_t consider_inheritance, 1626 apr_pool_t *result_pool, 1627 apr_pool_t *scratch_pool) 1628{ 1629 struct mergeinfo_diff_baton mdb; 1630 mdb.from = from; 1631 mdb.to = to; 1632 mdb.deleted = deleted; 1633 mdb.added = added; 1634 mdb.consider_inheritance = consider_inheritance; 1635 mdb.pool = result_pool; 1636 1637 return svn_hash_diff(from, to, mergeinfo_hash_diff_cb, &mdb, scratch_pool); 1638} 1639 1640svn_error_t * 1641svn_mergeinfo_diff2(svn_mergeinfo_t *deleted, svn_mergeinfo_t *added, 1642 svn_mergeinfo_t from, svn_mergeinfo_t to, 1643 svn_boolean_t consider_inheritance, 1644 apr_pool_t *result_pool, 1645 apr_pool_t *scratch_pool) 1646{ 1647 if (from && to == NULL) 1648 { 1649 *deleted = svn_mergeinfo_dup(from, result_pool); 1650 *added = svn_hash__make(result_pool); 1651 } 1652 else if (from == NULL && to) 1653 { 1654 *deleted = svn_hash__make(result_pool); 1655 *added = svn_mergeinfo_dup(to, result_pool); 1656 } 1657 else 1658 { 1659 *deleted = svn_hash__make(result_pool); 1660 *added = svn_hash__make(result_pool); 1661 1662 if (from && to) 1663 { 1664 SVN_ERR(walk_mergeinfo_hash_for_diff(from, to, *deleted, *added, 1665 consider_inheritance, 1666 result_pool, scratch_pool)); 1667 } 1668 } 1669 1670 return SVN_NO_ERROR; 1671} 1672 1673svn_error_t * 1674svn_mergeinfo__equals(svn_boolean_t *is_equal, 1675 svn_mergeinfo_t info1, 1676 svn_mergeinfo_t info2, 1677 svn_boolean_t consider_inheritance, 1678 apr_pool_t *pool) 1679{ 1680 apr_hash_index_t *hi; 1681 1682 *is_equal = FALSE; 1683 1684 /* special cases: at least one side has no merge info */ 1685 if (info1 == NULL && info2 == NULL) 1686 { 1687 *is_equal = TRUE; 1688 return SVN_NO_ERROR; 1689 } 1690 1691 if (info1 == NULL || info2 == NULL) 1692 return SVN_NO_ERROR; 1693 1694 /* trivial case: different number of paths -> unequal */ 1695 if (apr_hash_count(info1) != apr_hash_count(info2)) 1696 return SVN_NO_ERROR; 1697 1698 /* compare range lists for all paths */ 1699 for (hi = apr_hash_first(pool, info1); hi; hi = apr_hash_next(hi)) 1700 { 1701 const char *key; 1702 apr_ssize_t key_length; 1703 svn_rangelist_t *lhs, *rhs; 1704 int i; 1705 svn_rangelist_t *deleted, *added; 1706 1707 /* get both path lists */ 1708 apr_hash_this(hi, (const void**)&key, &key_length, (void **)&lhs); 1709 rhs = apr_hash_get(info2, key, key_length); 1710 1711 /* missing on one side? */ 1712 if (rhs == NULL) 1713 return SVN_NO_ERROR; 1714 1715 /* quick compare: the range lists will often be a perfect match */ 1716 if (lhs->nelts == rhs->nelts) 1717 { 1718 for (i = 0; i < lhs->nelts; ++i) 1719 { 1720 svn_merge_range_t *lrange 1721 = APR_ARRAY_IDX(lhs, i, svn_merge_range_t *); 1722 svn_merge_range_t *rrange 1723 = APR_ARRAY_IDX(rhs, i, svn_merge_range_t *); 1724 1725 /* range mismatch? -> needs detailed comparison */ 1726 if ( lrange->start != rrange->start 1727 || lrange->end != rrange->end) 1728 break; 1729 1730 /* inheritance mismatch? -> merge info differs */ 1731 if ( consider_inheritance 1732 && lrange->inheritable != rrange->inheritable) 1733 return SVN_NO_ERROR; 1734 } 1735 1736 /* all ranges found to match -> next path */ 1737 if (i == lhs->nelts) 1738 continue; 1739 } 1740 1741 /* range lists differ but there are many ways to sort and aggregate 1742 revisions into ranges. Do a full diff on them. */ 1743 SVN_ERR(svn_rangelist_diff(&deleted, &added, lhs, rhs, 1744 consider_inheritance, pool)); 1745 if (deleted->nelts || added->nelts) 1746 return SVN_NO_ERROR; 1747 } 1748 1749 /* no mismatch found */ 1750 *is_equal = TRUE; 1751 return SVN_NO_ERROR; 1752} 1753 1754svn_error_t * 1755svn_mergeinfo_merge2(svn_mergeinfo_t mergeinfo, 1756 svn_mergeinfo_t changes, 1757 apr_pool_t *result_pool, 1758 apr_pool_t *scratch_pool) 1759{ 1760 apr_hash_index_t *hi; 1761 apr_pool_t *iterpool; 1762 1763 if (!apr_hash_count(changes)) 1764 return SVN_NO_ERROR; 1765 1766 iterpool = svn_pool_create(scratch_pool); 1767 for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi)) 1768 { 1769 const char *key; 1770 apr_ssize_t klen; 1771 svn_rangelist_t *to_insert; 1772 svn_rangelist_t *target; 1773 1774 /* get ranges to insert and the target ranges list of that insertion */ 1775 apr_hash_this(hi, (const void**)&key, &klen, (void*)&to_insert); 1776 target = apr_hash_get(mergeinfo, key, klen); 1777 1778 /* if range list exists, just expand on it. 1779 * Otherwise, add new hash entry. */ 1780 if (target) 1781 { 1782 SVN_ERR(svn_rangelist_merge2(target, to_insert, result_pool, 1783 iterpool)); 1784 svn_pool_clear(iterpool); 1785 } 1786 else 1787 apr_hash_set(mergeinfo, key, klen, to_insert); 1788 } 1789 1790 svn_pool_destroy(iterpool); 1791 1792 return SVN_NO_ERROR; 1793} 1794 1795svn_error_t * 1796svn_mergeinfo_catalog_merge(svn_mergeinfo_catalog_t mergeinfo_cat, 1797 svn_mergeinfo_catalog_t changes_cat, 1798 apr_pool_t *result_pool, 1799 apr_pool_t *scratch_pool) 1800{ 1801 int i = 0; 1802 int j = 0; 1803 apr_array_header_t *sorted_cat = 1804 svn_sort__hash(mergeinfo_cat, svn_sort_compare_items_as_paths, 1805 scratch_pool); 1806 apr_array_header_t *sorted_changes = 1807 svn_sort__hash(changes_cat, svn_sort_compare_items_as_paths, 1808 scratch_pool); 1809 1810 while (i < sorted_cat->nelts && j < sorted_changes->nelts) 1811 { 1812 svn_sort__item_t cat_elt, change_elt; 1813 int res; 1814 1815 cat_elt = APR_ARRAY_IDX(sorted_cat, i, svn_sort__item_t); 1816 change_elt = APR_ARRAY_IDX(sorted_changes, j, svn_sort__item_t); 1817 res = svn_sort_compare_items_as_paths(&cat_elt, &change_elt); 1818 1819 if (res == 0) /* Both catalogs have mergeinfo for a given path. */ 1820 { 1821 svn_mergeinfo_t mergeinfo = cat_elt.value; 1822 svn_mergeinfo_t changes_mergeinfo = change_elt.value; 1823 1824 SVN_ERR(svn_mergeinfo_merge2(mergeinfo, changes_mergeinfo, 1825 result_pool, scratch_pool)); 1826 apr_hash_set(mergeinfo_cat, cat_elt.key, cat_elt.klen, mergeinfo); 1827 i++; 1828 j++; 1829 } 1830 else if (res < 0) /* Only MERGEINFO_CAT has mergeinfo for this path. */ 1831 { 1832 i++; 1833 } 1834 else /* Only CHANGES_CAT has mergeinfo for this path. */ 1835 { 1836 apr_hash_set(mergeinfo_cat, 1837 apr_pstrdup(result_pool, change_elt.key), 1838 change_elt.klen, 1839 svn_mergeinfo_dup(change_elt.value, result_pool)); 1840 j++; 1841 } 1842 } 1843 1844 /* Copy back any remaining elements from the CHANGES_CAT catalog. */ 1845 for (; j < sorted_changes->nelts; j++) 1846 { 1847 svn_sort__item_t elt = APR_ARRAY_IDX(sorted_changes, j, 1848 svn_sort__item_t); 1849 apr_hash_set(mergeinfo_cat, 1850 apr_pstrdup(result_pool, elt.key), 1851 elt.klen, 1852 svn_mergeinfo_dup(elt.value, result_pool)); 1853 } 1854 1855 return SVN_NO_ERROR; 1856} 1857 1858svn_error_t * 1859svn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo, 1860 svn_mergeinfo_t mergeinfo1, 1861 svn_mergeinfo_t mergeinfo2, 1862 svn_boolean_t consider_inheritance, 1863 apr_pool_t *result_pool, 1864 apr_pool_t *scratch_pool) 1865{ 1866 apr_hash_index_t *hi; 1867 apr_pool_t *iterpool; 1868 1869 *mergeinfo = apr_hash_make(result_pool); 1870 iterpool = svn_pool_create(scratch_pool); 1871 1872 /* ### TODO(reint): Do we care about the case when a path in one 1873 ### mergeinfo hash has inheritable mergeinfo, and in the other 1874 ### has non-inhertiable mergeinfo? It seems like that path 1875 ### itself should really be an intersection, while child paths 1876 ### should not be... */ 1877 for (hi = apr_hash_first(scratch_pool, mergeinfo1); 1878 hi; hi = apr_hash_next(hi)) 1879 { 1880 const char *path = svn__apr_hash_index_key(hi); 1881 svn_rangelist_t *rangelist1 = svn__apr_hash_index_val(hi); 1882 svn_rangelist_t *rangelist2; 1883 1884 svn_pool_clear(iterpool); 1885 rangelist2 = svn_hash_gets(mergeinfo2, path); 1886 if (rangelist2) 1887 { 1888 SVN_ERR(svn_rangelist_intersect(&rangelist2, rangelist1, rangelist2, 1889 consider_inheritance, iterpool)); 1890 if (rangelist2->nelts > 0) 1891 svn_hash_sets(*mergeinfo, apr_pstrdup(result_pool, path), 1892 svn_rangelist_dup(rangelist2, result_pool)); 1893 } 1894 } 1895 svn_pool_destroy(iterpool); 1896 return SVN_NO_ERROR; 1897} 1898 1899svn_error_t * 1900svn_mergeinfo_remove2(svn_mergeinfo_t *mergeinfo, 1901 svn_mergeinfo_t eraser, 1902 svn_mergeinfo_t whiteboard, 1903 svn_boolean_t consider_inheritance, 1904 apr_pool_t *result_pool, 1905 apr_pool_t *scratch_pool) 1906{ 1907 *mergeinfo = apr_hash_make(result_pool); 1908 return walk_mergeinfo_hash_for_diff(whiteboard, eraser, *mergeinfo, NULL, 1909 consider_inheritance, result_pool, 1910 scratch_pool); 1911} 1912 1913svn_error_t * 1914svn_rangelist_to_string(svn_string_t **output, 1915 const svn_rangelist_t *rangelist, 1916 apr_pool_t *pool) 1917{ 1918 svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool); 1919 1920 if (rangelist->nelts > 0) 1921 { 1922 int i; 1923 svn_merge_range_t *range; 1924 1925 /* Handle the elements that need commas at the end. */ 1926 for (i = 0; i < rangelist->nelts - 1; i++) 1927 { 1928 range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 1929 svn_stringbuf_appendcstr(buf, range_to_string(range, pool)); 1930 svn_stringbuf_appendcstr(buf, ","); 1931 } 1932 1933 /* Now handle the last element, which needs no comma. */ 1934 range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 1935 svn_stringbuf_appendcstr(buf, range_to_string(range, pool)); 1936 } 1937 1938 *output = svn_stringbuf__morph_into_string(buf); 1939 1940 return SVN_NO_ERROR; 1941} 1942 1943/* Converts a mergeinfo INPUT to an unparsed mergeinfo in OUTPUT. If PREFIX 1944 is not NULL then prepend PREFIX to each line in OUTPUT. If INPUT contains 1945 no elements, return the empty string. If INPUT contains any merge source 1946 path keys that are relative then convert these to absolute paths in 1947 *OUTPUT. 1948 */ 1949static svn_error_t * 1950mergeinfo_to_stringbuf(svn_stringbuf_t **output, 1951 svn_mergeinfo_t input, 1952 const char *prefix, 1953 apr_pool_t *pool) 1954{ 1955 *output = svn_stringbuf_create_empty(pool); 1956 1957 if (apr_hash_count(input) > 0) 1958 { 1959 apr_array_header_t *sorted = 1960 svn_sort__hash(input, svn_sort_compare_items_as_paths, pool); 1961 int i; 1962 1963 for (i = 0; i < sorted->nelts; i++) 1964 { 1965 svn_sort__item_t elt = APR_ARRAY_IDX(sorted, i, svn_sort__item_t); 1966 svn_string_t *revlist; 1967 1968 SVN_ERR(svn_rangelist_to_string(&revlist, elt.value, pool)); 1969 svn_stringbuf_appendcstr( 1970 *output, 1971 apr_psprintf(pool, "%s%s%s:%s", 1972 prefix ? prefix : "", 1973 *((const char *) elt.key) == '/' ? "" : "/", 1974 (const char *) elt.key, 1975 revlist->data)); 1976 if (i < sorted->nelts - 1) 1977 svn_stringbuf_appendcstr(*output, "\n"); 1978 } 1979 } 1980 1981 return SVN_NO_ERROR; 1982} 1983 1984svn_error_t * 1985svn_mergeinfo_to_string(svn_string_t **output, svn_mergeinfo_t input, 1986 apr_pool_t *pool) 1987{ 1988 svn_stringbuf_t *mergeinfo_buf; 1989 1990 SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_buf, input, NULL, pool)); 1991 *output = svn_stringbuf__morph_into_string(mergeinfo_buf); 1992 return SVN_NO_ERROR; 1993} 1994 1995svn_error_t * 1996svn_mergeinfo_sort(svn_mergeinfo_t input, apr_pool_t *pool) 1997{ 1998 apr_hash_index_t *hi; 1999 2000 for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi)) 2001 { 2002 apr_array_header_t *rl = svn__apr_hash_index_val(hi); 2003 2004 qsort(rl->elts, rl->nelts, rl->elt_size, svn_sort_compare_ranges); 2005 } 2006 return SVN_NO_ERROR; 2007} 2008 2009svn_error_t * 2010svn_mergeinfo__canonicalize_ranges(svn_mergeinfo_t mergeinfo, 2011 apr_pool_t *scratch_pool) 2012{ 2013 apr_hash_index_t *hi; 2014 2015 for (hi = apr_hash_first(scratch_pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2016 { 2017 apr_array_header_t *rl = svn__apr_hash_index_val(hi); 2018 2019 SVN_ERR(svn_rangelist__canonicalize(rl, scratch_pool)); 2020 } 2021 2022 return SVN_NO_ERROR; 2023} 2024 2025svn_mergeinfo_catalog_t 2026svn_mergeinfo_catalog_dup(svn_mergeinfo_catalog_t mergeinfo_catalog, 2027 apr_pool_t *pool) 2028{ 2029 svn_mergeinfo_t new_mergeinfo_catalog = apr_hash_make(pool); 2030 apr_hash_index_t *hi; 2031 2032 for (hi = apr_hash_first(pool, mergeinfo_catalog); 2033 hi; 2034 hi = apr_hash_next(hi)) 2035 { 2036 const char *key = svn__apr_hash_index_key(hi); 2037 svn_mergeinfo_t val = svn__apr_hash_index_val(hi); 2038 2039 svn_hash_sets(new_mergeinfo_catalog, apr_pstrdup(pool, key), 2040 svn_mergeinfo_dup(val, pool)); 2041 } 2042 2043 return new_mergeinfo_catalog; 2044} 2045 2046svn_mergeinfo_t 2047svn_mergeinfo_dup(svn_mergeinfo_t mergeinfo, apr_pool_t *pool) 2048{ 2049 svn_mergeinfo_t new_mergeinfo = svn_hash__make(pool); 2050 apr_hash_index_t *hi; 2051 2052 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2053 { 2054 const char *path = svn__apr_hash_index_key(hi); 2055 apr_ssize_t pathlen = svn__apr_hash_index_klen(hi); 2056 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2057 2058 apr_hash_set(new_mergeinfo, apr_pstrmemdup(pool, path, pathlen), pathlen, 2059 svn_rangelist_dup(rangelist, pool)); 2060 } 2061 2062 return new_mergeinfo; 2063} 2064 2065svn_error_t * 2066svn_mergeinfo_inheritable2(svn_mergeinfo_t *output, 2067 svn_mergeinfo_t mergeinfo, 2068 const char *path, 2069 svn_revnum_t start, 2070 svn_revnum_t end, 2071 svn_boolean_t inheritable, 2072 apr_pool_t *result_pool, 2073 apr_pool_t *scratch_pool) 2074{ 2075 apr_hash_index_t *hi; 2076 svn_mergeinfo_t inheritable_mergeinfo = apr_hash_make(result_pool); 2077 2078 for (hi = apr_hash_first(scratch_pool, mergeinfo); 2079 hi; 2080 hi = apr_hash_next(hi)) 2081 { 2082 const char *key = svn__apr_hash_index_key(hi); 2083 apr_ssize_t keylen = svn__apr_hash_index_klen(hi); 2084 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2085 svn_rangelist_t *inheritable_rangelist; 2086 2087 if (!path || svn_path_compare_paths(path, key) == 0) 2088 SVN_ERR(svn_rangelist_inheritable2(&inheritable_rangelist, rangelist, 2089 start, end, inheritable, 2090 result_pool, scratch_pool)); 2091 else 2092 inheritable_rangelist = svn_rangelist_dup(rangelist, result_pool); 2093 2094 /* Only add this rangelist if some ranges remain. A rangelist with 2095 a path mapped to an empty rangelist is not syntactically valid */ 2096 if (inheritable_rangelist->nelts) 2097 apr_hash_set(inheritable_mergeinfo, 2098 apr_pstrmemdup(result_pool, key, keylen), keylen, 2099 inheritable_rangelist); 2100 } 2101 *output = inheritable_mergeinfo; 2102 return SVN_NO_ERROR; 2103} 2104 2105 2106svn_error_t * 2107svn_rangelist_inheritable2(svn_rangelist_t **inheritable_rangelist, 2108 const svn_rangelist_t *rangelist, 2109 svn_revnum_t start, 2110 svn_revnum_t end, 2111 svn_boolean_t inheritable, 2112 apr_pool_t *result_pool, 2113 apr_pool_t *scratch_pool) 2114{ 2115 *inheritable_rangelist = apr_array_make(result_pool, 1, 2116 sizeof(svn_merge_range_t *)); 2117 if (rangelist->nelts) 2118 { 2119 if (!SVN_IS_VALID_REVNUM(start) 2120 || !SVN_IS_VALID_REVNUM(end) 2121 || end < start) 2122 { 2123 int i; 2124 /* We want all non-inheritable ranges removed. */ 2125 for (i = 0; i < rangelist->nelts; i++) 2126 { 2127 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i, 2128 svn_merge_range_t *); 2129 if (range->inheritable == inheritable) 2130 { 2131 svn_merge_range_t *inheritable_range = 2132 apr_palloc(result_pool, sizeof(*inheritable_range)); 2133 inheritable_range->start = range->start; 2134 inheritable_range->end = range->end; 2135 inheritable_range->inheritable = TRUE; 2136 APR_ARRAY_PUSH(*inheritable_rangelist, 2137 svn_merge_range_t *) = range; 2138 } 2139 } 2140 } 2141 else 2142 { 2143 /* We want only the non-inheritable ranges bound by START 2144 and END removed. */ 2145 svn_rangelist_t *ranges_inheritable = 2146 svn_rangelist__initialize(start, end, inheritable, scratch_pool); 2147 2148 if (rangelist->nelts) 2149 SVN_ERR(svn_rangelist_remove(inheritable_rangelist, 2150 ranges_inheritable, 2151 rangelist, 2152 TRUE, 2153 result_pool)); 2154 } 2155 } 2156 return SVN_NO_ERROR; 2157} 2158 2159svn_boolean_t 2160svn_mergeinfo__remove_empty_rangelists(svn_mergeinfo_t mergeinfo, 2161 apr_pool_t *pool) 2162{ 2163 apr_hash_index_t *hi; 2164 svn_boolean_t removed_some_ranges = FALSE; 2165 2166 if (mergeinfo) 2167 { 2168 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2169 { 2170 const char *path = svn__apr_hash_index_key(hi); 2171 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2172 2173 if (rangelist->nelts == 0) 2174 { 2175 svn_hash_sets(mergeinfo, path, NULL); 2176 removed_some_ranges = TRUE; 2177 } 2178 } 2179 } 2180 return removed_some_ranges; 2181} 2182 2183svn_error_t * 2184svn_mergeinfo__remove_prefix_from_catalog(svn_mergeinfo_catalog_t *out_catalog, 2185 svn_mergeinfo_catalog_t in_catalog, 2186 const char *prefix_path, 2187 apr_pool_t *pool) 2188{ 2189 apr_hash_index_t *hi; 2190 2191 SVN_ERR_ASSERT(prefix_path[0] == '/'); 2192 2193 *out_catalog = apr_hash_make(pool); 2194 2195 for (hi = apr_hash_first(pool, in_catalog); hi; hi = apr_hash_next(hi)) 2196 { 2197 const char *original_path = svn__apr_hash_index_key(hi); 2198 svn_mergeinfo_t value = svn__apr_hash_index_val(hi); 2199 const char *new_path; 2200 2201 new_path = svn_fspath__skip_ancestor(prefix_path, original_path); 2202 SVN_ERR_ASSERT(new_path); 2203 2204 svn_hash_sets(*out_catalog, new_path, value); 2205 } 2206 2207 return SVN_NO_ERROR; 2208} 2209 2210svn_error_t * 2211svn_mergeinfo__add_prefix_to_catalog(svn_mergeinfo_catalog_t *out_catalog, 2212 svn_mergeinfo_catalog_t in_catalog, 2213 const char *prefix_path, 2214 apr_pool_t *result_pool, 2215 apr_pool_t *scratch_pool) 2216{ 2217 apr_hash_index_t *hi; 2218 2219 *out_catalog = apr_hash_make(result_pool); 2220 2221 for (hi = apr_hash_first(scratch_pool, in_catalog); 2222 hi; 2223 hi = apr_hash_next(hi)) 2224 { 2225 const char *original_path = svn__apr_hash_index_key(hi); 2226 svn_mergeinfo_t value = svn__apr_hash_index_val(hi); 2227 2228 if (original_path[0] == '/') 2229 original_path++; 2230 2231 svn_hash_sets(*out_catalog, 2232 svn_dirent_join(prefix_path, original_path, result_pool), 2233 value); 2234 } 2235 2236 return SVN_NO_ERROR; 2237} 2238 2239svn_error_t * 2240svn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo, 2241 svn_mergeinfo_t mergeinfo, 2242 const char *suffix_relpath, 2243 apr_pool_t *result_pool, 2244 apr_pool_t *scratch_pool) 2245{ 2246 apr_hash_index_t *hi; 2247 2248 SVN_ERR_ASSERT(suffix_relpath && svn_relpath_is_canonical(suffix_relpath)); 2249 2250 *out_mergeinfo = apr_hash_make(result_pool); 2251 2252 for (hi = apr_hash_first(scratch_pool, mergeinfo); 2253 hi; 2254 hi = apr_hash_next(hi)) 2255 { 2256 const char *fspath = svn__apr_hash_index_key(hi); 2257 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2258 2259 svn_hash_sets(*out_mergeinfo, 2260 svn_fspath__join(fspath, suffix_relpath, result_pool), 2261 rangelist); 2262 } 2263 2264 return SVN_NO_ERROR; 2265} 2266 2267svn_rangelist_t * 2268svn_rangelist_dup(const svn_rangelist_t *rangelist, apr_pool_t *pool) 2269{ 2270 svn_rangelist_t *new_rl = apr_array_make(pool, rangelist->nelts, 2271 sizeof(svn_merge_range_t *)); 2272 2273 /* allocate target range buffer with a single operation */ 2274 svn_merge_range_t *copy = apr_palloc(pool, sizeof(*copy) * rangelist->nelts); 2275 int i; 2276 2277 /* fill it iteratively and link it into the range list */ 2278 for (i = 0; i < rangelist->nelts; i++) 2279 { 2280 memcpy(copy + i, 2281 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *), 2282 sizeof(*copy)); 2283 APR_ARRAY_PUSH(new_rl, svn_merge_range_t *) = copy + i; 2284 } 2285 2286 return new_rl; 2287} 2288 2289svn_merge_range_t * 2290svn_merge_range_dup(const svn_merge_range_t *range, apr_pool_t *pool) 2291{ 2292 svn_merge_range_t *new_range = apr_palloc(pool, sizeof(*new_range)); 2293 memcpy(new_range, range, sizeof(*new_range)); 2294 return new_range; 2295} 2296 2297svn_boolean_t 2298svn_merge_range_contains_rev(const svn_merge_range_t *range, svn_revnum_t rev) 2299{ 2300 assert(SVN_IS_VALID_REVNUM(range->start)); 2301 assert(SVN_IS_VALID_REVNUM(range->end)); 2302 assert(range->start != range->end); 2303 2304 if (range->start < range->end) 2305 return rev > range->start && rev <= range->end; 2306 else 2307 return rev > range->end && rev <= range->start; 2308} 2309 2310svn_error_t * 2311svn_mergeinfo__catalog_to_formatted_string(svn_string_t **output, 2312 svn_mergeinfo_catalog_t catalog, 2313 const char *key_prefix, 2314 const char *val_prefix, 2315 apr_pool_t *pool) 2316{ 2317 svn_stringbuf_t *output_buf = NULL; 2318 2319 if (catalog && apr_hash_count(catalog)) 2320 { 2321 int i; 2322 apr_array_header_t *sorted_catalog = 2323 svn_sort__hash(catalog, svn_sort_compare_items_as_paths, pool); 2324 2325 output_buf = svn_stringbuf_create_empty(pool); 2326 for (i = 0; i < sorted_catalog->nelts; i++) 2327 { 2328 svn_sort__item_t elt = 2329 APR_ARRAY_IDX(sorted_catalog, i, svn_sort__item_t); 2330 const char *path1; 2331 svn_mergeinfo_t mergeinfo; 2332 svn_stringbuf_t *mergeinfo_output_buf; 2333 2334 path1 = elt.key; 2335 mergeinfo = elt.value; 2336 if (key_prefix) 2337 svn_stringbuf_appendcstr(output_buf, key_prefix); 2338 svn_stringbuf_appendcstr(output_buf, path1); 2339 svn_stringbuf_appendcstr(output_buf, "\n"); 2340 SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_output_buf, mergeinfo, 2341 val_prefix ? val_prefix : "", pool)); 2342 svn_stringbuf_appendstr(output_buf, mergeinfo_output_buf); 2343 svn_stringbuf_appendcstr(output_buf, "\n"); 2344 } 2345 } 2346#if SVN_DEBUG 2347 else if (!catalog) 2348 { 2349 output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool); 2350 svn_stringbuf_appendcstr(output_buf, _("NULL mergeinfo catalog\n")); 2351 } 2352 else if (apr_hash_count(catalog) == 0) 2353 { 2354 output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool); 2355 svn_stringbuf_appendcstr(output_buf, _("empty mergeinfo catalog\n")); 2356 } 2357#endif 2358 2359 /* If we have an output_buf, convert it to an svn_string_t; 2360 otherwise, return a new string containing only a newline 2361 character. */ 2362 if (output_buf) 2363 *output = svn_stringbuf__morph_into_string(output_buf); 2364 else 2365 *output = svn_string_create("\n", pool); 2366 2367 return SVN_NO_ERROR; 2368} 2369 2370svn_error_t * 2371svn_mergeinfo__get_range_endpoints(svn_revnum_t *youngest_rev, 2372 svn_revnum_t *oldest_rev, 2373 svn_mergeinfo_t mergeinfo, 2374 apr_pool_t *pool) 2375{ 2376 *youngest_rev = *oldest_rev = SVN_INVALID_REVNUM; 2377 if (mergeinfo) 2378 { 2379 apr_hash_index_t *hi; 2380 2381 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2382 { 2383 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2384 2385 if (rangelist->nelts) 2386 { 2387 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, 2388 rangelist->nelts - 1, 2389 svn_merge_range_t *); 2390 if (!SVN_IS_VALID_REVNUM(*youngest_rev) 2391 || (range->end > *youngest_rev)) 2392 *youngest_rev = range->end; 2393 2394 range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *); 2395 if (!SVN_IS_VALID_REVNUM(*oldest_rev) 2396 || (range->start < *oldest_rev)) 2397 *oldest_rev = range->start; 2398 } 2399 } 2400 } 2401 return SVN_NO_ERROR; 2402} 2403 2404svn_error_t * 2405svn_mergeinfo__filter_catalog_by_ranges(svn_mergeinfo_catalog_t *filtered_cat, 2406 svn_mergeinfo_catalog_t catalog, 2407 svn_revnum_t youngest_rev, 2408 svn_revnum_t oldest_rev, 2409 svn_boolean_t include_range, 2410 apr_pool_t *result_pool, 2411 apr_pool_t *scratch_pool) 2412{ 2413 apr_hash_index_t *hi; 2414 2415 *filtered_cat = apr_hash_make(result_pool); 2416 for (hi = apr_hash_first(scratch_pool, catalog); 2417 hi; 2418 hi = apr_hash_next(hi)) 2419 { 2420 const char *path = svn__apr_hash_index_key(hi); 2421 svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi); 2422 svn_mergeinfo_t filtered_mergeinfo; 2423 2424 SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&filtered_mergeinfo, 2425 mergeinfo, 2426 youngest_rev, 2427 oldest_rev, 2428 include_range, 2429 result_pool, 2430 scratch_pool)); 2431 if (apr_hash_count(filtered_mergeinfo)) 2432 svn_hash_sets(*filtered_cat, 2433 apr_pstrdup(result_pool, path), filtered_mergeinfo); 2434 } 2435 2436 return SVN_NO_ERROR; 2437} 2438 2439svn_error_t * 2440svn_mergeinfo__filter_mergeinfo_by_ranges(svn_mergeinfo_t *filtered_mergeinfo, 2441 svn_mergeinfo_t mergeinfo, 2442 svn_revnum_t youngest_rev, 2443 svn_revnum_t oldest_rev, 2444 svn_boolean_t include_range, 2445 apr_pool_t *result_pool, 2446 apr_pool_t *scratch_pool) 2447{ 2448 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev)); 2449 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev)); 2450 SVN_ERR_ASSERT(oldest_rev < youngest_rev); 2451 2452 *filtered_mergeinfo = apr_hash_make(result_pool); 2453 2454 if (mergeinfo) 2455 { 2456 apr_hash_index_t *hi; 2457 svn_rangelist_t *filter_rangelist = 2458 svn_rangelist__initialize(oldest_rev, youngest_rev, TRUE, 2459 scratch_pool); 2460 2461 for (hi = apr_hash_first(scratch_pool, mergeinfo); 2462 hi; 2463 hi = apr_hash_next(hi)) 2464 { 2465 const char *path = svn__apr_hash_index_key(hi); 2466 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2467 2468 if (rangelist->nelts) 2469 { 2470 svn_rangelist_t *new_rangelist; 2471 2472 SVN_ERR(rangelist_intersect_or_remove( 2473 &new_rangelist, filter_rangelist, rangelist, 2474 ! include_range, FALSE, result_pool)); 2475 2476 if (new_rangelist->nelts) 2477 svn_hash_sets(*filtered_mergeinfo, 2478 apr_pstrdup(result_pool, path), new_rangelist); 2479 } 2480 } 2481 } 2482 return SVN_NO_ERROR; 2483} 2484 2485svn_error_t * 2486svn_mergeinfo__adjust_mergeinfo_rangelists(svn_mergeinfo_t *adjusted_mergeinfo, 2487 svn_mergeinfo_t mergeinfo, 2488 svn_revnum_t offset, 2489 apr_pool_t *result_pool, 2490 apr_pool_t *scratch_pool) 2491{ 2492 apr_hash_index_t *hi; 2493 *adjusted_mergeinfo = apr_hash_make(result_pool); 2494 2495 if (mergeinfo) 2496 { 2497 for (hi = apr_hash_first(scratch_pool, mergeinfo); 2498 hi; 2499 hi = apr_hash_next(hi)) 2500 { 2501 int i; 2502 const char *path = svn__apr_hash_index_key(hi); 2503 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2504 svn_rangelist_t *adjusted_rangelist = 2505 apr_array_make(result_pool, rangelist->nelts, 2506 sizeof(svn_merge_range_t *)); 2507 2508 for (i = 0; i < rangelist->nelts; i++) 2509 { 2510 svn_merge_range_t *range = 2511 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *); 2512 2513 if (range->start + offset > 0 && range->end + offset > 0) 2514 { 2515 if (range->start + offset < 0) 2516 range->start = 0; 2517 else 2518 range->start = range->start + offset; 2519 2520 if (range->end + offset < 0) 2521 range->end = 0; 2522 else 2523 range->end = range->end + offset; 2524 APR_ARRAY_PUSH(adjusted_rangelist, svn_merge_range_t *) = 2525 range; 2526 } 2527 } 2528 2529 if (adjusted_rangelist->nelts) 2530 svn_hash_sets(*adjusted_mergeinfo, apr_pstrdup(result_pool, path), 2531 adjusted_rangelist); 2532 } 2533 } 2534 return SVN_NO_ERROR; 2535} 2536 2537svn_boolean_t 2538svn_mergeinfo__is_noninheritable(svn_mergeinfo_t mergeinfo, 2539 apr_pool_t *scratch_pool) 2540{ 2541 if (mergeinfo) 2542 { 2543 apr_hash_index_t *hi; 2544 2545 for (hi = apr_hash_first(scratch_pool, mergeinfo); 2546 hi; 2547 hi = apr_hash_next(hi)) 2548 { 2549 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi); 2550 int i; 2551 2552 for (i = 0; i < rangelist->nelts; i++) 2553 { 2554 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i, 2555 svn_merge_range_t *); 2556 if (!range->inheritable) 2557 return TRUE; 2558 } 2559 } 2560 } 2561 return FALSE; 2562} 2563 2564svn_rangelist_t * 2565svn_rangelist__initialize(svn_revnum_t start, 2566 svn_revnum_t end, 2567 svn_boolean_t inheritable, 2568 apr_pool_t *result_pool) 2569{ 2570 svn_rangelist_t *rangelist = 2571 apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *)); 2572 svn_merge_range_t *range = apr_pcalloc(result_pool, sizeof(*range)); 2573 2574 range->start = start; 2575 range->end = end; 2576 range->inheritable = inheritable; 2577 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range; 2578 return rangelist; 2579} 2580 2581svn_error_t * 2582svn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p, 2583 const apr_array_header_t *segments, 2584 apr_pool_t *pool) 2585{ 2586 svn_mergeinfo_t mergeinfo = apr_hash_make(pool); 2587 int i; 2588 2589 /* Translate location segments into merge sources and ranges. */ 2590 for (i = 0; i < segments->nelts; i++) 2591 { 2592 svn_location_segment_t *segment = 2593 APR_ARRAY_IDX(segments, i, svn_location_segment_t *); 2594 svn_rangelist_t *path_ranges; 2595 svn_merge_range_t *range; 2596 const char *source_path; 2597 2598 /* No path segment? Skip it. */ 2599 if (! segment->path) 2600 continue; 2601 2602 /* Prepend a leading slash to our path. */ 2603 source_path = apr_pstrcat(pool, "/", segment->path, (char *)NULL); 2604 2605 /* See if we already stored ranges for this path. If not, make 2606 a new list. */ 2607 path_ranges = svn_hash_gets(mergeinfo, source_path); 2608 if (! path_ranges) 2609 path_ranges = apr_array_make(pool, 1, sizeof(range)); 2610 2611 /* A svn_location_segment_t may have legitimately describe only 2612 revision 0, but there is no corresponding representation for 2613 this in a svn_merge_range_t. */ 2614 if (segment->range_start == 0 && segment->range_end == 0) 2615 continue; 2616 2617 /* Build a merge range, push it onto the list of ranges, and for 2618 good measure, (re)store it in the hash. */ 2619 range = apr_pcalloc(pool, sizeof(*range)); 2620 range->start = MAX(segment->range_start - 1, 0); 2621 range->end = segment->range_end; 2622 range->inheritable = TRUE; 2623 APR_ARRAY_PUSH(path_ranges, svn_merge_range_t *) = range; 2624 svn_hash_sets(mergeinfo, source_path, path_ranges); 2625 } 2626 2627 *mergeinfo_p = mergeinfo; 2628 return SVN_NO_ERROR; 2629} 2630 2631svn_error_t * 2632svn_rangelist__merge_many(svn_rangelist_t *merged_rangelist, 2633 svn_mergeinfo_t merge_history, 2634 apr_pool_t *result_pool, 2635 apr_pool_t *scratch_pool) 2636{ 2637 if (apr_hash_count(merge_history)) 2638 { 2639 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2640 apr_hash_index_t *hi; 2641 2642 for (hi = apr_hash_first(scratch_pool, merge_history); 2643 hi; 2644 hi = apr_hash_next(hi)) 2645 { 2646 svn_rangelist_t *subtree_rangelist = svn__apr_hash_index_val(hi); 2647 2648 svn_pool_clear(iterpool); 2649 SVN_ERR(svn_rangelist_merge2(merged_rangelist, subtree_rangelist, 2650 result_pool, iterpool)); 2651 } 2652 svn_pool_destroy(iterpool); 2653 } 2654 return SVN_NO_ERROR; 2655} 2656 2657 2658const char * 2659svn_inheritance_to_word(svn_mergeinfo_inheritance_t inherit) 2660{ 2661 switch (inherit) 2662 { 2663 case svn_mergeinfo_inherited: 2664 return "inherited"; 2665 case svn_mergeinfo_nearest_ancestor: 2666 return "nearest-ancestor"; 2667 default: 2668 return "explicit"; 2669 } 2670} 2671 2672svn_mergeinfo_inheritance_t 2673svn_inheritance_from_word(const char *word) 2674{ 2675 if (strcmp(word, "inherited") == 0) 2676 return svn_mergeinfo_inherited; 2677 if (strcmp(word, "nearest-ancestor") == 0) 2678 return svn_mergeinfo_nearest_ancestor; 2679 return svn_mergeinfo_explicit; 2680} 2681