Provide (experimental) clang-format file
[TortoiseGit.git] / src / TortoiseMerge / libsvn_diff / mergeinfo.c
blobf12ef83b6dd2c16ec42f9b3310064802a86defed
1 /*
2 * mergeinfo.c: Mergeinfo parsing and handling
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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 * ====================================================================
23 #include <assert.h>
24 #include <ctype.h>
26 #include "svn_path.h"
27 #include "svn_types.h"
28 #include "svn_ctype.h"
29 #include "svn_pools.h"
30 #include "svn_sorts.h"
31 #include "svn_error.h"
32 #include "svn_error_codes.h"
33 #include "svn_string.h"
34 #include "svn_mergeinfo.h"
35 #include "private/svn_fspath.h"
36 #include "private/svn_mergeinfo_private.h"
37 #include "private/svn_string_private.h"
38 #include "private/svn_subr_private.h"
39 #include "svn_private_config.h"
40 #include "svn_hash.h"
41 #include "private/svn_dep_compat.h"
43 /* Attempt to combine two ranges, IN1 and IN2. If they are adjacent or
44 overlapping, and their inheritability allows them to be combined, put
45 the result in OUTPUT and return TRUE, otherwise return FALSE.
47 CONSIDER_INHERITANCE determines how to account for the inheritability
48 of IN1 and IN2 when trying to combine ranges. If ranges with different
49 inheritability are combined (CONSIDER_INHERITANCE must be FALSE for this
50 to happen) the result is inheritable. If both ranges are inheritable the
51 result is inheritable. Only and if both ranges are non-inheritable is
52 the result is non-inheritable.
54 Range overlapping detection algorithm from
55 http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap
57 static svn_boolean_t
58 combine_ranges(svn_merge_range_t *output,
59 const svn_merge_range_t *in1,
60 const svn_merge_range_t *in2,
61 svn_boolean_t consider_inheritance)
63 if (in1->start <= in2->end && in2->start <= in1->end)
65 if (!consider_inheritance
66 || (consider_inheritance
67 && (in1->inheritable == in2->inheritable)))
69 output->start = MIN(in1->start, in2->start);
70 output->end = MAX(in1->end, in2->end);
71 output->inheritable = (in1->inheritable || in2->inheritable);
72 return TRUE;
75 return FALSE;
78 /* pathname -> PATHNAME */
79 static svn_error_t *
80 parse_pathname(const char **input,
81 const char *end,
82 const char **pathname,
83 apr_pool_t *pool)
85 const char *curr = *input;
86 const char *last_colon = NULL;
88 /* A pathname may contain colons, so find the last colon before END
89 or newline. We'll consider this the divider between the pathname
90 and the revisionlist. */
91 while (curr < end && *curr != '\n')
93 if (*curr == ':')
94 last_colon = curr;
95 curr++;
98 if (!last_colon)
99 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
100 _("Pathname not terminated by ':'"));
101 if (last_colon == *input)
102 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
103 _("No pathname preceding ':'"));
105 /* Tolerate relative repository paths, but convert them to absolute.
106 ### Efficiency? 1 string duplication here, 2 in canonicalize. */
107 *pathname = svn_fspath__canonicalize(apr_pstrndup(pool, *input,
108 last_colon - *input),
109 pool);
111 *input = last_colon;
113 return SVN_NO_ERROR;
116 /* Return TRUE iff (svn_merge_range_t *) RANGE describes a valid, forward
117 * revision range.
119 * Note: The smallest valid value of RANGE->start is 0 because it is an
120 * exclusive endpoint, being one less than the revision number of the first
121 * change described by the range, and the oldest possible change is "r1" as
122 * there cannot be a change "r0". */
123 #define IS_VALID_FORWARD_RANGE(range) \
124 (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end))
126 /* Ways in which two svn_merge_range_t can intersect or adjoin, if at all. */
127 typedef enum intersection_type_t
129 /* Ranges don't intersect and don't adjoin. */
130 svn__no_intersection,
132 /* Ranges are equal. */
133 svn__equal_intersection,
135 /* Ranges adjoin but don't overlap. */
136 svn__adjoining_intersection,
138 /* Ranges overlap but neither is a subset of the other. */
139 svn__overlapping_intersection,
141 /* One range is a proper subset of the other. */
142 svn__proper_subset_intersection
143 } intersection_type_t;
145 /* Given ranges R1 and R2, both of which must be forward merge ranges,
146 set *INTERSECTION_TYPE to describe how the ranges intersect, if they
147 do at all. The inheritance type of the ranges is not considered. */
148 static svn_error_t *
149 get_type_of_intersection(const svn_merge_range_t *r1,
150 const svn_merge_range_t *r2,
151 intersection_type_t *intersection_type)
153 SVN_ERR_ASSERT(r1);
154 SVN_ERR_ASSERT(r2);
155 SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r1));
156 SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r2));
158 if (!(r1->start <= r2->end && r2->start <= r1->end))
159 *intersection_type = svn__no_intersection;
160 else if (r1->start == r2->start && r1->end == r2->end)
161 *intersection_type = svn__equal_intersection;
162 else if (r1->end == r2->start || r2->end == r1->start)
163 *intersection_type = svn__adjoining_intersection;
164 else if (r1->start <= r2->start && r1->end >= r2->end)
165 *intersection_type = svn__proper_subset_intersection;
166 else if (r2->start <= r1->start && r2->end >= r1->end)
167 *intersection_type = svn__proper_subset_intersection;
168 else
169 *intersection_type = svn__overlapping_intersection;
171 return SVN_NO_ERROR;
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.
177 OVERVIEW
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.
187 ([*] Due to the simplifying assumption below, only the last range in
188 RANGELIST is considered.)
190 DETAILS
192 If RANGELIST is not empty assume NEW_RANGE does not intersect with any
193 range before the last one in RANGELIST.
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.
199 If NEW_RANGE intersects with the last range in RANGELIST then combine
200 these two ranges as described below:
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:
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.:
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*
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.:
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
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.:
236 Last range in NEW_RANGE RESULTING RANGES
237 RANGELIST
238 ------------- --------- ----------------
239 4-10 6* 4-10 (Not 4-5, 6, 7-10)
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.
245 static svn_error_t *
246 combine_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)
251 svn_merge_range_t *lastrange;
252 svn_merge_range_t combined_range;
254 /* We don't accept a NULL RANGELIST. */
255 SVN_ERR_ASSERT(rangelist);
257 if (rangelist->nelts > 0)
258 lastrange = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1, svn_merge_range_t *);
259 else
260 lastrange = NULL;
262 if (!lastrange)
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);
268 else if (!consider_inheritance)
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))
275 *lastrange = combined_range;
277 else
279 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
280 svn_merge_range_dup(new_range, result_pool);
283 else /* Considering inheritance */
285 if (combine_ranges(&combined_range, lastrange, new_range, TRUE))
287 /* Even when considering inheritance two intersection ranges
288 of the same inheritability can simply be combined. */
289 *lastrange = combined_range;
291 else
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;
299 SVN_ERR(get_type_of_intersection(new_range, lastrange,
300 &intersection_type));
302 switch (intersection_type)
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;
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;
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;
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.*/
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);
340 /* Pop off *LASTRANGE to make our manipulations
341 easier. */
342 apr_array_pop(rangelist);
344 /* Ensure R1 is the older range. */
345 if (r2->start < r1->start)
347 /* Swap R1 and R2. */
348 *r2 = *r1;
349 *r1 = *new_range;
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;
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;
369 default: /* svn__proper_subset_intersection */
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;
378 /* Pop off *LASTRANGE to make our manipulations
379 easier. */
380 apr_array_pop(rangelist);
382 /* Ensure R1 is the superset. */
383 if (r2->start < r1->start || r2->end > r1->end)
385 /* Swap R1 and R2. */
386 *r2 = *r1;
387 *r1 = *new_range;
390 if (r1->inheritable)
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;
398 else if (r1->start == r2->start)
400 svn_revnum_t tmp_revnum;
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;
410 else if (r1->end == r2->end)
412 /* *LASTRANGE and NEW_RANGE share an end point. */
413 r1->end = r2->start;
414 r2->inheritable = TRUE;
416 else
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;
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)
433 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
434 if (sorted)
435 sorted = (svn_sort_compare_ranges(&r1, &r2) < 0);
437 if (r3)
439 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r3;
440 if (sorted)
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);
450 break;
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);
462 return SVN_NO_ERROR;
465 /* Convert a single svn_merge_range_t *RANGE back into a string. */
466 static char *
467 range_to_string(const svn_merge_range_t *range,
468 apr_pool_t *pool)
470 const char *mark
471 = range->inheritable ? "" : SVN_MERGEINFO_NONINHERITABLE_STR;
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);
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.
489 revisionlist -> (revisionelement)(COMMA revisionelement)*
490 revisionrange -> REVISION "-" REVISION("*")
491 revisionelement -> revisionrange | REVISION("*")
493 static svn_error_t *
494 parse_rangelist(const char **input, const char *end,
495 svn_rangelist_t *rangelist,
496 apr_pool_t *pool)
498 const char *curr = *input;
500 /* Eat any leading horizontal white-space before the rangelist. */
501 while (curr < end && *curr != '\n' && isspace(*curr))
502 curr++;
504 if (*curr == '\n' || curr == end)
506 /* Empty range list. */
507 *input = curr;
508 return SVN_NO_ERROR;
511 while (curr < end && *curr != '\n')
513 /* Parse individual revisions or revision ranges. */
514 svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
515 svn_revnum_t firstrev;
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;
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"));
532 if (*curr == '-')
534 svn_revnum_t secondrev;
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;
551 if (*curr == '\n' || curr == end)
553 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
554 *input = curr;
555 return SVN_NO_ERROR;
557 else if (*curr == ',')
559 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
560 curr++;
562 else if (*curr == '*')
564 mrange->inheritable = FALSE;
565 curr++;
566 if (*curr == ',' || *curr == '\n' || curr == end)
568 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = mrange;
569 if (*curr == ',')
571 curr++;
573 else
575 *input = curr;
576 return SVN_NO_ERROR;
579 else
581 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
582 _("Invalid character '%c' found in "
583 "range list"), *curr);
586 else
588 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
589 _("Invalid character '%c' found in "
590 "range list"), *curr);
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;
602 svn_error_t *
603 svn_rangelist__parse(svn_rangelist_t **rangelist,
604 const char *str,
605 apr_pool_t *result_pool)
607 const char *s = str;
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;
614 svn_error_t *
615 svn_rangelist__combine_adjacent_ranges(svn_rangelist_t *rangelist,
616 apr_pool_t *scratch_pool)
618 int i;
619 svn_merge_range_t *range, *lastrange;
621 lastrange = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
623 for (i = 1; i < rangelist->nelts; i++)
625 range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
626 if (lastrange->start <= range->end
627 && range->start <= lastrange->end)
629 /* The ranges are adjacent or intersect. */
631 /* svn_mergeinfo_parse promises to combine overlapping
632 ranges as long as their inheritability is the same. */
633 if (range->start < lastrange->end
634 && range->inheritable != lastrange->inheritable)
636 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
637 _("Unable to parse overlapping "
638 "revision ranges '%s' and '%s' "
639 "with different inheritance "
640 "types"),
641 range_to_string(lastrange,
642 scratch_pool),
643 range_to_string(range,
644 scratch_pool));
647 /* Combine overlapping or adjacent ranges with the
648 same inheritability. */
649 if (lastrange->inheritable == range->inheritable)
651 lastrange->end = MAX(range->end, lastrange->end);
652 svn_sort__array_delete(rangelist, i, 1);
653 i--;
656 lastrange = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
659 return SVN_NO_ERROR;
662 /* revisionline -> PATHNAME COLON revisionlist */
663 static svn_error_t *
664 parse_revision_line(const char **input, const char *end, svn_mergeinfo_t hash,
665 apr_pool_t *scratch_pool)
667 const char *pathname = "";
668 apr_ssize_t klen;
669 svn_rangelist_t *existing_rangelist;
670 svn_rangelist_t *rangelist = apr_array_make(scratch_pool, 1,
671 sizeof(svn_merge_range_t *));
673 SVN_ERR(parse_pathname(input, end, &pathname, scratch_pool));
675 if (*(*input) != ':')
676 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
677 _("Pathname not terminated by ':'"));
679 *input = *input + 1;
681 SVN_ERR(parse_rangelist(input, end, rangelist, scratch_pool));
683 if (rangelist->nelts == 0)
684 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
685 _("Mergeinfo for '%s' maps to an "
686 "empty revision range"), pathname);
687 if (*input != end && *(*input) != '\n')
688 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
689 _("Could not find end of line in range list line "
690 "in '%s'"), *input);
692 if (*input != end)
693 *input = *input + 1;
695 /* Sort the rangelist, combine adjacent ranges into single ranges,
696 and make sure there are no overlapping ranges. */
697 if (rangelist->nelts > 1)
699 qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size,
700 svn_sort_compare_ranges);
702 SVN_ERR(svn_rangelist__combine_adjacent_ranges(rangelist, scratch_pool));
705 /* Handle any funky mergeinfo with relative merge source paths that
706 might exist due to issue #3547. It's possible that this issue allowed
707 the creation of mergeinfo with path keys that differ only by a
708 leading slash, e.g. "trunk:4033\n/trunk:4039-4995". In the event
709 we encounter this we merge the rangelists together under a single
710 absolute path key. */
711 klen = strlen(pathname);
712 existing_rangelist = apr_hash_get(hash, pathname, klen);
713 if (existing_rangelist)
714 SVN_ERR(svn_rangelist_merge2(rangelist, existing_rangelist,
715 scratch_pool, scratch_pool));
717 apr_hash_set(hash, apr_pstrmemdup(apr_hash_pool_get(hash), pathname, klen),
718 klen, svn_rangelist_dup(rangelist, apr_hash_pool_get(hash)));
720 return SVN_NO_ERROR;
723 /* top -> revisionline (NEWLINE revisionline)* */
724 static svn_error_t *
725 parse_top(const char **input, const char *end, svn_mergeinfo_t hash,
726 apr_pool_t *pool)
728 apr_pool_t *iterpool = svn_pool_create(pool);
730 while (*input < end)
732 svn_pool_clear(iterpool);
733 SVN_ERR(parse_revision_line(input, end, hash, iterpool));
735 svn_pool_destroy(iterpool);
737 return SVN_NO_ERROR;
740 svn_error_t *
741 svn_mergeinfo_parse(svn_mergeinfo_t *mergeinfo,
742 const char *input,
743 apr_pool_t *pool)
745 svn_error_t *err;
747 *mergeinfo = svn_hash__make(pool);
748 err = parse_top(&input, input + strlen(input), *mergeinfo, pool);
750 /* Always return SVN_ERR_MERGEINFO_PARSE_ERROR as the topmost error. */
751 if (err && err->apr_err != SVN_ERR_MERGEINFO_PARSE_ERROR)
752 err = svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, err,
753 _("Could not parse mergeinfo string '%s'"),
754 input);
755 return err;
758 /* Cleanup after svn_rangelist_merge2 when it modifies the ending range of
759 a single rangelist element in-place.
761 If *RANGE_INDEX is not a valid element in RANGELIST do nothing. Otherwise
762 ensure that RANGELIST[*RANGE_INDEX]->END does not adjoin or overlap any
763 subsequent ranges in RANGELIST.
765 If overlap is found, then remove, modify, and/or add elements to RANGELIST
766 as per the invariants for rangelists documented in svn_mergeinfo.h. If
767 RANGELIST[*RANGE_INDEX]->END adjoins a subsequent element then combine the
768 elements if their inheritability permits -- The inheritance of intersecting
769 and adjoining ranges is handled as per svn_mergeinfo_merge2. Upon return
770 set *RANGE_INDEX to the index of the youngest element modified, added, or
771 adjoined to RANGELIST[*RANGE_INDEX].
773 Note: Adjoining rangelist elements are those where the end rev of the older
774 element is equal to the start rev of the younger element.
776 Any new elements inserted into RANGELIST are allocated in RESULT_POOL.*/
777 static void
778 adjust_remaining_ranges(svn_rangelist_t *rangelist,
779 int *range_index,
780 apr_pool_t *result_pool)
782 int i;
783 int starting_index;
784 int elements_to_delete = 0;
785 svn_merge_range_t *modified_range;
787 if (*range_index >= rangelist->nelts)
788 return;
790 starting_index = *range_index + 1;
791 modified_range = APR_ARRAY_IDX(rangelist, *range_index, svn_merge_range_t *);
793 for (i = *range_index + 1; i < rangelist->nelts; i++)
795 svn_merge_range_t *next_range = APR_ARRAY_IDX(rangelist, i,
796 svn_merge_range_t *);
798 /* If MODIFIED_RANGE doesn't adjoin or overlap the next range in
799 RANGELIST then we are finished. */
800 if (modified_range->end < next_range->start)
801 break;
803 /* Does MODIFIED_RANGE adjoin NEXT_RANGE? */
804 if (modified_range->end == next_range->start)
806 if (modified_range->inheritable == next_range->inheritable)
808 /* Combine adjoining ranges with the same inheritability. */
809 modified_range->end = next_range->end;
810 elements_to_delete++;
812 else
814 /* Cannot join because inheritance differs. */
815 (*range_index)++;
817 break;
820 /* Alright, we know MODIFIED_RANGE overlaps NEXT_RANGE, but how? */
821 if (modified_range->end > next_range->end)
823 /* NEXT_RANGE is a proper subset of MODIFIED_RANGE and the two
824 don't share the same end range. */
825 if (modified_range->inheritable
826 || (modified_range->inheritable == next_range->inheritable))
828 /* MODIFIED_RANGE absorbs NEXT_RANGE. */
829 elements_to_delete++;
831 else
833 /* NEXT_RANGE is a proper subset MODIFIED_RANGE but
834 MODIFIED_RANGE is non-inheritable and NEXT_RANGE is
835 inheritable. This means MODIFIED_RANGE is truncated,
836 NEXT_RANGE remains, and the portion of MODIFIED_RANGE
837 younger than NEXT_RANGE is added as a separate range:
838 ______________________________________________
840 M MODIFIED_RANGE N
841 | (!inhertiable) |
842 |______________________________________________|
844 O NEXT_RANGE P
845 | (inheritable)|
846 |______________|
849 _______________________________________________
850 | | | |
851 M MODIFIED_RANGE O NEXT_RANGE P NEW_RANGE N
852 | (!inhertiable) | (inheritable)| (!inheritable)|
853 |________________|______________|_______________|
855 svn_merge_range_t *new_modified_range =
856 apr_palloc(result_pool, sizeof(*new_modified_range));
857 new_modified_range->start = next_range->end;
858 new_modified_range->end = modified_range->end;
859 new_modified_range->inheritable = FALSE;
860 modified_range->end = next_range->start;
861 (*range_index)+=2;
862 svn_sort__array_insert(&new_modified_range, rangelist,
863 *range_index);
864 /* Recurse with the new range. */
865 adjust_remaining_ranges(rangelist, range_index, result_pool);
866 break;
869 else if (modified_range->end == next_range->end)
871 /* NEXT_RANGE is a proper subset MODIFIED_RANGE and share
872 the same end range. */
873 if (modified_range->inheritable
874 || (modified_range->inheritable == next_range->inheritable))
876 /* MODIFIED_RANGE absorbs NEXT_RANGE. */
877 elements_to_delete++;
879 else
881 /* The intersection between MODIFIED_RANGE and NEXT_RANGE is
882 absorbed by the latter. */
883 modified_range->end = next_range->start;
884 (*range_index)++;
886 break;
888 else
890 /* NEXT_RANGE and MODIFIED_RANGE intersect but NEXT_RANGE is not
891 a proper subset of MODIFIED_RANGE, nor do the two share the
892 same end revision, i.e. they overlap. */
893 if (modified_range->inheritable == next_range->inheritable)
895 /* Combine overlapping ranges with the same inheritability. */
896 modified_range->end = next_range->end;
897 elements_to_delete++;
899 else if (modified_range->inheritable)
901 /* MODIFIED_RANGE absorbs the portion of NEXT_RANGE it overlaps
902 and NEXT_RANGE is truncated. */
903 next_range->start = modified_range->end;
904 (*range_index)++;
906 else
908 /* NEXT_RANGE absorbs the portion of MODIFIED_RANGE it overlaps
909 and MODIFIED_RANGE is truncated. */
910 modified_range->end = next_range->start;
911 (*range_index)++;
913 break;
917 if (elements_to_delete)
918 svn_sort__array_delete(rangelist, starting_index, elements_to_delete);
921 svn_error_t *
922 svn_rangelist_merge2(svn_rangelist_t *rangelist,
923 const svn_rangelist_t *changes,
924 apr_pool_t *result_pool,
925 apr_pool_t *scratch_pool)
927 int i = 0;
928 int j = 0;
930 /* We may modify CHANGES, so make a copy in SCRATCH_POOL. */
931 changes = svn_rangelist_dup(changes, scratch_pool);
933 while (i < rangelist->nelts && j < changes->nelts)
935 svn_merge_range_t *range =
936 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
937 svn_merge_range_t *change =
938 APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
939 int res = svn_sort_compare_ranges(&range, &change);
941 if (res == 0)
943 /* Only when merging two non-inheritable ranges is the result also
944 non-inheritable. In all other cases ensure an inheritiable
945 result. */
946 if (range->inheritable || change->inheritable)
947 range->inheritable = TRUE;
948 i++;
949 j++;
951 else if (res < 0) /* CHANGE is younger than RANGE */
953 if (range->end < change->start)
955 /* RANGE is older than CHANGE and the two do not
956 adjoin or overlap */
957 i++;
959 else if (range->end == change->start)
961 /* RANGE and CHANGE adjoin */
962 if (range->inheritable == change->inheritable)
964 /* RANGE and CHANGE have the same inheritability so
965 RANGE expands to absord CHANGE. */
966 range->end = change->end;
967 adjust_remaining_ranges(rangelist, &i, result_pool);
968 j++;
970 else
972 /* RANGE and CHANGE adjoin, but have different
973 inheritability. Since RANGE is older, just
974 move on to the next RANGE. */
975 i++;
978 else
980 /* RANGE and CHANGE overlap, but how? */
981 if ((range->inheritable == change->inheritable)
982 || range->inheritable)
984 /* If CHANGE is a proper subset of RANGE, it absorbs RANGE
985 with no adjustment otherwise only the intersection is
986 absorbed and CHANGE is truncated. */
987 if (range->end >= change->end)
988 j++;
989 else
990 change->start = range->end;
992 else
994 /* RANGE is non-inheritable and CHANGE is inheritable. */
995 if (range->start < change->start)
997 /* CHANGE absorbs intersection with RANGE and RANGE
998 is truncated. */
999 svn_merge_range_t *range_copy =
1000 svn_merge_range_dup(range, result_pool);
1001 range_copy->end = change->start;
1002 range->start = change->start;
1003 svn_sort__array_insert(&range_copy, rangelist, i++);
1005 else
1007 /* CHANGE and RANGE share the same start rev, but
1008 RANGE is considered older because its end rev
1009 is older. */
1010 range->inheritable = TRUE;
1011 change->start = range->end;
1016 else /* res > 0, CHANGE is older than RANGE */
1018 if (change->end < range->start)
1020 /* CHANGE is older than RANGE and the two do not
1021 adjoin or overlap, so insert a copy of CHANGE
1022 into RANGELIST. */
1023 svn_merge_range_t *change_copy =
1024 svn_merge_range_dup(change, result_pool);
1025 svn_sort__array_insert(&change_copy, rangelist, i++);
1026 j++;
1028 else if (change->end == range->start)
1030 /* RANGE and CHANGE adjoin */
1031 if (range->inheritable == change->inheritable)
1033 /* RANGE and CHANGE have the same inheritability so we
1034 can simply combine the two in place. */
1035 range->start = change->start;
1036 j++;
1038 else
1040 /* RANGE and CHANGE have different inheritability so insert
1041 a copy of CHANGE into RANGELIST. */
1042 svn_merge_range_t *change_copy =
1043 svn_merge_range_dup(change, result_pool);
1044 svn_sort__array_insert(&change_copy, rangelist, i);
1045 j++;
1048 else
1050 /* RANGE and CHANGE overlap. */
1051 if (range->inheritable == change->inheritable)
1053 /* RANGE and CHANGE have the same inheritability so we
1054 can simply combine the two in place... */
1055 range->start = change->start;
1056 if (range->end < change->end)
1058 /* ...but if RANGE is expanded ensure that we don't
1059 violate any rangelist invariants. */
1060 range->end = change->end;
1061 adjust_remaining_ranges(rangelist, &i, result_pool);
1063 j++;
1065 else if (range->inheritable)
1067 if (change->start < range->start)
1069 /* RANGE is inheritable so absorbs any part of CHANGE
1070 it overlaps. CHANGE is truncated and the remainder
1071 inserted into RANGELIST. */
1072 svn_merge_range_t *change_copy =
1073 svn_merge_range_dup(change, result_pool);
1074 change_copy->end = range->start;
1075 change->start = range->start;
1076 svn_sort__array_insert(&change_copy, rangelist, i++);
1078 else
1080 /* CHANGE and RANGE share the same start rev, but
1081 CHANGE is considered older because CHANGE->END is
1082 older than RANGE->END. */
1083 j++;
1086 else
1088 /* RANGE is non-inheritable and CHANGE is inheritable. */
1089 if (change->start < range->start)
1091 if (change->end == range->end)
1093 /* RANGE is a proper subset of CHANGE and share the
1094 same end revision, so set RANGE equal to CHANGE. */
1095 range->start = change->start;
1096 range->inheritable = TRUE;
1097 j++;
1099 else if (change->end > range->end)
1101 /* RANGE is a proper subset of CHANGE and CHANGE has
1102 a younger end revision, so set RANGE equal to its
1103 intersection with CHANGE and truncate CHANGE. */
1104 range->start = change->start;
1105 range->inheritable = TRUE;
1106 change->start = range->end;
1108 else
1110 /* CHANGE and RANGE overlap. Set RANGE equal to its
1111 intersection with CHANGE and take the remainder
1112 of RANGE and insert it into RANGELIST. */
1113 svn_merge_range_t *range_copy =
1114 svn_merge_range_dup(range, result_pool);
1115 range_copy->start = change->end;
1116 range->start = change->start;
1117 range->end = change->end;
1118 range->inheritable = TRUE;
1119 svn_sort__array_insert(&range_copy, rangelist, ++i);
1120 j++;
1123 else
1125 /* CHANGE and RANGE share the same start rev, but
1126 CHANGE is considered older because its end rev
1127 is older.
1129 Insert the intersection of RANGE and CHANGE into
1130 RANGELIST and then set RANGE to the non-intersecting
1131 portion of RANGE. */
1132 svn_merge_range_t *range_copy =
1133 svn_merge_range_dup(range, result_pool);
1134 range_copy->end = change->end;
1135 range_copy->inheritable = TRUE;
1136 range->start = change->end;
1137 svn_sort__array_insert(&range_copy, rangelist, i++);
1138 j++;
1145 /* Copy any remaining elements in CHANGES into RANGELIST. */
1146 for (; j < (changes)->nelts; j++)
1148 svn_merge_range_t *change =
1149 APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
1150 svn_merge_range_t *change_copy = svn_merge_range_dup(change,
1151 result_pool);
1152 svn_sort__array_insert(&change_copy, rangelist, rangelist->nelts);
1155 return SVN_NO_ERROR;
1158 /* Return TRUE iff the forward revision ranges FIRST and SECOND overlap and
1159 * (if CONSIDER_INHERITANCE is TRUE) have the same inheritability. */
1160 static svn_boolean_t
1161 range_intersect(const svn_merge_range_t *first, const svn_merge_range_t *second,
1162 svn_boolean_t consider_inheritance)
1164 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first));
1165 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second));
1167 return (first->start + 1 <= second->end)
1168 && (second->start + 1 <= first->end)
1169 && (!consider_inheritance
1170 || (!(first->inheritable) == !(second->inheritable)));
1173 /* Return TRUE iff the forward revision range FIRST wholly contains the
1174 * forward revision range SECOND and (if CONSIDER_INHERITANCE is TRUE) has
1175 * the same inheritability. */
1176 static svn_boolean_t
1177 range_contains(const svn_merge_range_t *first, const svn_merge_range_t *second,
1178 svn_boolean_t consider_inheritance)
1180 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first));
1181 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second));
1183 return (first->start <= second->start) && (second->end <= first->end)
1184 && (!consider_inheritance
1185 || (!(first->inheritable) == !(second->inheritable)));
1188 /* Swap start and end fields of RANGE. */
1189 static void
1190 range_swap_endpoints(svn_merge_range_t *range)
1192 svn_revnum_t swap = range->start;
1193 range->start = range->end;
1194 range->end = swap;
1197 svn_error_t *
1198 svn_rangelist_reverse(svn_rangelist_t *rangelist, apr_pool_t *pool)
1200 int i;
1202 svn_sort__array_reverse(rangelist, pool);
1204 for (i = 0; i < rangelist->nelts; i++)
1206 range_swap_endpoints(APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *));
1209 return SVN_NO_ERROR;
1212 void
1213 svn_rangelist__set_inheritance(svn_rangelist_t *rangelist,
1214 svn_boolean_t inheritable)
1216 if (rangelist)
1218 int i;
1219 svn_merge_range_t *range;
1221 for (i = 0; i < rangelist->nelts; i++)
1223 range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1224 range->inheritable = inheritable;
1227 return;
1230 void
1231 svn_mergeinfo__set_inheritance(svn_mergeinfo_t mergeinfo,
1232 svn_boolean_t inheritable,
1233 apr_pool_t *scratch_pool)
1235 if (mergeinfo)
1237 apr_hash_index_t *hi;
1239 for (hi = apr_hash_first(scratch_pool, mergeinfo);
1241 hi = apr_hash_next(hi))
1243 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
1245 if (rangelist)
1246 svn_rangelist__set_inheritance(rangelist, inheritable);
1249 return;
1252 /* If DO_REMOVE is true, then remove any overlapping ranges described by
1253 RANGELIST1 from RANGELIST2 and place the results in *OUTPUT. When
1254 DO_REMOVE is true, RANGELIST1 is effectively the "eraser" and RANGELIST2
1255 the "whiteboard".
1257 If DO_REMOVE is false, then capture the intersection between RANGELIST1
1258 and RANGELIST2 and place the results in *OUTPUT. The ordering of
1259 RANGELIST1 and RANGELIST2 doesn't matter when DO_REMOVE is false.
1261 If CONSIDER_INHERITANCE is true, then take the inheritance of the
1262 ranges in RANGELIST1 and RANGELIST2 into account when comparing them
1263 for intersection, see the doc string for svn_rangelist_intersect().
1265 If CONSIDER_INHERITANCE is false, then ranges with differing inheritance
1266 may intersect, but the resulting intersection is non-inheritable only
1267 if both ranges were non-inheritable, e.g.:
1269 RANGELIST1 RANGELIST2 CONSIDER DO_REMOVE *OUTPUT
1270 INHERITANCE
1271 ---------- ------ ----------- --------- -------
1273 90-420* 1-100 TRUE FALSE Empty Rangelist
1274 90-420 1-100* TRUE FALSE Empty Rangelist
1275 90-420 1-100 TRUE FALSE 90-100
1276 90-420* 1-100* TRUE FALSE 90-100*
1278 90-420* 1-100 FALSE FALSE 90-100
1279 90-420 1-100* FALSE FALSE 90-100
1280 90-420 1-100 FALSE FALSE 90-100
1281 90-420* 1-100* FALSE FALSE 90-100*
1283 Allocate the contents of *OUTPUT in POOL. */
1284 static svn_error_t *
1285 rangelist_intersect_or_remove(svn_rangelist_t **output,
1286 const svn_rangelist_t *rangelist1,
1287 const svn_rangelist_t *rangelist2,
1288 svn_boolean_t do_remove,
1289 svn_boolean_t consider_inheritance,
1290 apr_pool_t *pool)
1292 int i1, i2, lasti2;
1293 svn_merge_range_t working_elt2;
1295 *output = apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
1297 i1 = 0;
1298 i2 = 0;
1299 lasti2 = -1; /* Initialized to a value that "i2" will never be. */
1301 while (i1 < rangelist1->nelts && i2 < rangelist2->nelts)
1303 svn_merge_range_t *elt1, *elt2;
1305 elt1 = APR_ARRAY_IDX(rangelist1, i1, svn_merge_range_t *);
1307 /* Instead of making a copy of the entire array of rangelist2
1308 elements, we just keep a copy of the current rangelist2 element
1309 that needs to be used, and modify our copy if necessary. */
1310 if (i2 != lasti2)
1312 working_elt2 =
1313 *(APR_ARRAY_IDX(rangelist2, i2, svn_merge_range_t *));
1314 lasti2 = i2;
1317 elt2 = &working_elt2;
1319 /* If the rangelist2 range is contained completely in the
1320 rangelist1, we increment the rangelist2.
1321 If the ranges intersect, and match exactly, we increment both
1322 rangelist1 and rangelist2.
1323 Otherwise, we have to generate a range for the left part of
1324 the removal of rangelist1 from rangelist2, and possibly change
1325 the rangelist2 to the remaining portion of the right part of
1326 the removal, to test against. */
1327 if (range_contains(elt1, elt2, consider_inheritance))
1329 if (!do_remove)
1331 svn_merge_range_t tmp_range;
1332 tmp_range.start = elt2->start;
1333 tmp_range.end = elt2->end;
1334 /* The intersection of two ranges is non-inheritable only
1335 if both ranges are non-inheritable. */
1336 tmp_range.inheritable =
1337 (elt2->inheritable || elt1->inheritable);
1338 SVN_ERR(combine_with_lastrange(&tmp_range, *output,
1339 consider_inheritance,
1340 pool));
1343 i2++;
1345 if (elt2->start == elt1->start && elt2->end == elt1->end)
1346 i1++;
1348 else if (range_intersect(elt1, elt2, consider_inheritance))
1350 if (elt2->start < elt1->start)
1352 /* The rangelist2 range starts before the rangelist1 range. */
1353 svn_merge_range_t tmp_range;
1354 if (do_remove)
1356 /* Retain the range that falls before the rangelist1
1357 start. */
1358 tmp_range.start = elt2->start;
1359 tmp_range.end = elt1->start;
1360 tmp_range.inheritable = elt2->inheritable;
1362 else
1364 /* Retain the range that falls between the rangelist1
1365 start and rangelist2 end. */
1366 tmp_range.start = elt1->start;
1367 tmp_range.end = MIN(elt2->end, elt1->end);
1368 /* The intersection of two ranges is non-inheritable only
1369 if both ranges are non-inheritable. */
1370 tmp_range.inheritable =
1371 (elt2->inheritable || elt1->inheritable);
1374 SVN_ERR(combine_with_lastrange(&tmp_range,
1375 *output, consider_inheritance,
1376 pool));
1379 /* Set up the rest of the rangelist2 range for further
1380 processing. */
1381 if (elt2->end > elt1->end)
1383 /* The rangelist2 range ends after the rangelist1 range. */
1384 if (!do_remove)
1386 /* Partial overlap. */
1387 svn_merge_range_t tmp_range;
1388 tmp_range.start = MAX(elt2->start, elt1->start);
1389 tmp_range.end = elt1->end;
1390 /* The intersection of two ranges is non-inheritable only
1391 if both ranges are non-inheritable. */
1392 tmp_range.inheritable =
1393 (elt2->inheritable || elt1->inheritable);
1394 SVN_ERR(combine_with_lastrange(&tmp_range,
1395 *output,
1396 consider_inheritance,
1397 pool));
1400 working_elt2.start = elt1->end;
1401 working_elt2.end = elt2->end;
1403 else
1404 i2++;
1406 else /* ranges don't intersect */
1408 /* See which side of the rangelist2 the rangelist1 is on. If it
1409 is on the left side, we need to move the rangelist1.
1411 If it is on past the rangelist2 on the right side, we
1412 need to output the rangelist2 and increment the
1413 rangelist2. */
1414 if (svn_sort_compare_ranges(&elt1, &elt2) < 0)
1415 i1++;
1416 else
1418 svn_merge_range_t *lastrange;
1420 if ((*output)->nelts > 0)
1421 lastrange = APR_ARRAY_IDX(*output, (*output)->nelts - 1,
1422 svn_merge_range_t *);
1423 else
1424 lastrange = NULL;
1426 if (do_remove && !(lastrange &&
1427 combine_ranges(lastrange, lastrange, elt2,
1428 consider_inheritance)))
1430 lastrange = svn_merge_range_dup(elt2, pool);
1431 APR_ARRAY_PUSH(*output, svn_merge_range_t *) = lastrange;
1433 i2++;
1438 if (do_remove)
1440 /* Copy the current rangelist2 element if we didn't hit the end
1441 of the rangelist2, and we still had it around. This element
1442 may have been touched, so we can't just walk the rangelist2
1443 array, we have to use our copy. This case only happens when
1444 we ran out of rangelist1 before rangelist2, *and* we had changed
1445 the rangelist2 element. */
1446 if (i2 == lasti2 && i2 < rangelist2->nelts)
1448 SVN_ERR(combine_with_lastrange(&working_elt2, *output,
1449 consider_inheritance, pool));
1450 i2++;
1453 /* Copy any other remaining untouched rangelist2 elements. */
1454 for (; i2 < rangelist2->nelts; i2++)
1456 svn_merge_range_t *elt = APR_ARRAY_IDX(rangelist2, i2,
1457 svn_merge_range_t *);
1459 SVN_ERR(combine_with_lastrange(elt, *output,
1460 consider_inheritance, pool));
1464 return SVN_NO_ERROR;
1468 svn_error_t *
1469 svn_rangelist_intersect(svn_rangelist_t **output,
1470 const svn_rangelist_t *rangelist1,
1471 const svn_rangelist_t *rangelist2,
1472 svn_boolean_t consider_inheritance,
1473 apr_pool_t *pool)
1475 return rangelist_intersect_or_remove(output, rangelist1, rangelist2, FALSE,
1476 consider_inheritance, pool);
1479 svn_error_t *
1480 svn_rangelist_remove(svn_rangelist_t **output,
1481 const svn_rangelist_t *eraser,
1482 const svn_rangelist_t *whiteboard,
1483 svn_boolean_t consider_inheritance,
1484 apr_pool_t *pool)
1486 return rangelist_intersect_or_remove(output, eraser, whiteboard, TRUE,
1487 consider_inheritance, pool);
1490 svn_error_t *
1491 svn_rangelist_diff(svn_rangelist_t **deleted, svn_rangelist_t **added,
1492 const svn_rangelist_t *from, const svn_rangelist_t *to,
1493 svn_boolean_t consider_inheritance,
1494 apr_pool_t *pool)
1496 /* The following diagrams illustrate some common range delta scenarios:
1498 (from) deleted
1499 r0 <===========(=========)============[=========]===========> rHEAD
1500 [to] added
1502 (from) deleted deleted
1503 r0 <===========(=========[============]=========)===========> rHEAD
1504 [to]
1506 (from) deleted
1507 r0 <===========(=========[============)=========]===========> rHEAD
1508 [to] added
1510 (from) deleted
1511 r0 <===========[=========(============]=========)===========> rHEAD
1512 [to] added
1514 (from)
1515 r0 <===========[=========(============)=========]===========> rHEAD
1516 [to] added added
1518 (from) d d d
1519 r0 <===(=[=)=]=[==]=[=(=)=]=[=]=[=(===|===(=)==|=|==[=(=]=)=> rHEAD
1520 [to] a a a a a a a
1523 /* The items that are present in from, but not in to, must have been
1524 deleted. */
1525 SVN_ERR(svn_rangelist_remove(deleted, to, from, consider_inheritance,
1526 pool));
1527 /* The items that are present in to, but not in from, must have been
1528 added. */
1529 return svn_rangelist_remove(added, from, to, consider_inheritance, pool);
1532 struct mergeinfo_diff_baton
1534 svn_mergeinfo_t from;
1535 svn_mergeinfo_t to;
1536 svn_mergeinfo_t deleted;
1537 svn_mergeinfo_t added;
1538 svn_boolean_t consider_inheritance;
1539 apr_pool_t *pool;
1542 /* This implements the 'svn_hash_diff_func_t' interface.
1543 BATON is of type 'struct mergeinfo_diff_baton *'.
1545 static svn_error_t *
1546 mergeinfo_hash_diff_cb(const void *key, apr_ssize_t klen,
1547 enum svn_hash_diff_key_status status,
1548 void *baton)
1550 /* hash_a is FROM mergeinfo,
1551 hash_b is TO mergeinfo. */
1552 struct mergeinfo_diff_baton *cb = baton;
1553 svn_rangelist_t *from_rangelist, *to_rangelist;
1554 const char *path = key;
1555 if (status == svn_hash_diff_key_both)
1557 /* Record any deltas (additions or deletions). */
1558 svn_rangelist_t *deleted_rangelist, *added_rangelist;
1559 from_rangelist = apr_hash_get(cb->from, path, klen);
1560 to_rangelist = apr_hash_get(cb->to, path, klen);
1561 SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
1562 from_rangelist, to_rangelist,
1563 cb->consider_inheritance, cb->pool));
1564 if (cb->deleted && deleted_rangelist->nelts > 0)
1565 apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen),
1566 klen, deleted_rangelist);
1567 if (cb->added && added_rangelist->nelts > 0)
1568 apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen),
1569 klen, added_rangelist);
1571 else if ((status == svn_hash_diff_key_a) && cb->deleted)
1573 from_rangelist = apr_hash_get(cb->from, path, klen);
1574 apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen), klen,
1575 svn_rangelist_dup(from_rangelist, cb->pool));
1577 else if ((status == svn_hash_diff_key_b) && cb->added)
1579 to_rangelist = apr_hash_get(cb->to, path, klen);
1580 apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen), klen,
1581 svn_rangelist_dup(to_rangelist, cb->pool));
1583 return SVN_NO_ERROR;
1586 /* Record deletions and additions of entire range lists (by path
1587 presence), and delegate to svn_rangelist_diff() for delta
1588 calculations on a specific path. */
1589 static svn_error_t *
1590 walk_mergeinfo_hash_for_diff(svn_mergeinfo_t from, svn_mergeinfo_t to,
1591 svn_mergeinfo_t deleted, svn_mergeinfo_t added,
1592 svn_boolean_t consider_inheritance,
1593 apr_pool_t *result_pool,
1594 apr_pool_t *scratch_pool)
1596 struct mergeinfo_diff_baton mdb;
1597 mdb.from = from;
1598 mdb.to = to;
1599 mdb.deleted = deleted;
1600 mdb.added = added;
1601 mdb.consider_inheritance = consider_inheritance;
1602 mdb.pool = result_pool;
1604 return svn_hash_diff(from, to, mergeinfo_hash_diff_cb, &mdb, scratch_pool);
1607 svn_error_t *
1608 svn_mergeinfo_diff2(svn_mergeinfo_t *deleted, svn_mergeinfo_t *added,
1609 svn_mergeinfo_t from, svn_mergeinfo_t to,
1610 svn_boolean_t consider_inheritance,
1611 apr_pool_t *result_pool,
1612 apr_pool_t *scratch_pool)
1614 if (from && to == NULL)
1616 *deleted = svn_mergeinfo_dup(from, result_pool);
1617 *added = svn_hash__make(result_pool);
1619 else if (from == NULL && to)
1621 *deleted = svn_hash__make(result_pool);
1622 *added = svn_mergeinfo_dup(to, result_pool);
1624 else
1626 *deleted = svn_hash__make(result_pool);
1627 *added = svn_hash__make(result_pool);
1629 if (from && to)
1631 SVN_ERR(walk_mergeinfo_hash_for_diff(from, to, *deleted, *added,
1632 consider_inheritance,
1633 result_pool, scratch_pool));
1637 return SVN_NO_ERROR;
1640 svn_error_t *
1641 svn_mergeinfo__equals(svn_boolean_t *is_equal,
1642 svn_mergeinfo_t info1,
1643 svn_mergeinfo_t info2,
1644 svn_boolean_t consider_inheritance,
1645 apr_pool_t *pool)
1647 apr_hash_index_t *hi;
1649 *is_equal = FALSE;
1651 /* special cases: at least one side has no merge info */
1652 if (info1 == NULL && info2 == NULL)
1654 *is_equal = TRUE;
1655 return SVN_NO_ERROR;
1658 if (info1 == NULL || info2 == NULL)
1659 return SVN_NO_ERROR;
1661 /* trivial case: different number of paths -> unequal */
1662 if (apr_hash_count(info1) != apr_hash_count(info2))
1663 return SVN_NO_ERROR;
1665 /* compare range lists for all paths */
1666 for (hi = apr_hash_first(pool, info1); hi; hi = apr_hash_next(hi))
1668 const char *key;
1669 apr_ssize_t key_length;
1670 svn_rangelist_t *lhs, *rhs;
1671 int i;
1672 svn_rangelist_t *deleted, *added;
1674 /* get both path lists */
1675 apr_hash_this(hi, (const void**)&key, &key_length, (void **)&lhs);
1676 rhs = apr_hash_get(info2, key, key_length);
1678 /* missing on one side? */
1679 if (rhs == NULL)
1680 return SVN_NO_ERROR;
1682 /* quick compare: the range lists will often be a perfect match */
1683 if (lhs->nelts == rhs->nelts)
1685 for (i = 0; i < lhs->nelts; ++i)
1687 svn_merge_range_t *lrange
1688 = APR_ARRAY_IDX(lhs, i, svn_merge_range_t *);
1689 svn_merge_range_t *rrange
1690 = APR_ARRAY_IDX(rhs, i, svn_merge_range_t *);
1692 /* range mismatch? -> needs detailed comparison */
1693 if ( lrange->start != rrange->start
1694 || lrange->end != rrange->end)
1695 break;
1697 /* inheritance mismatch? -> merge info differs */
1698 if ( consider_inheritance
1699 && lrange->inheritable != rrange->inheritable)
1700 return SVN_NO_ERROR;
1703 /* all ranges found to match -> next path */
1704 if (i == lhs->nelts)
1705 continue;
1708 /* range lists differ but there are many ways to sort and aggregate
1709 revisions into ranges. Do a full diff on them. */
1710 SVN_ERR(svn_rangelist_diff(&deleted, &added, lhs, rhs,
1711 consider_inheritance, pool));
1712 if (deleted->nelts || added->nelts)
1713 return SVN_NO_ERROR;
1716 /* no mismatch found */
1717 *is_equal = TRUE;
1718 return SVN_NO_ERROR;
1721 svn_error_t *
1722 svn_mergeinfo_merge2(svn_mergeinfo_t mergeinfo,
1723 svn_mergeinfo_t changes,
1724 apr_pool_t *result_pool,
1725 apr_pool_t *scratch_pool)
1727 apr_hash_index_t *hi;
1728 apr_pool_t *iterpool;
1730 if (!apr_hash_count(changes))
1731 return SVN_NO_ERROR;
1733 iterpool = svn_pool_create(scratch_pool);
1734 for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi))
1736 const char *key;
1737 apr_ssize_t klen;
1738 svn_rangelist_t *to_insert;
1739 svn_rangelist_t *target;
1741 /* get ranges to insert and the target ranges list of that insertion */
1742 apr_hash_this(hi, (const void**)&key, &klen, (void*)&to_insert);
1743 target = apr_hash_get(mergeinfo, key, klen);
1745 /* if range list exists, just expand on it.
1746 * Otherwise, add new hash entry. */
1747 if (target)
1749 SVN_ERR(svn_rangelist_merge2(target, to_insert, result_pool,
1750 iterpool));
1751 svn_pool_clear(iterpool);
1753 else
1754 apr_hash_set(mergeinfo, key, klen, to_insert);
1757 svn_pool_destroy(iterpool);
1759 return SVN_NO_ERROR;
1762 svn_error_t *
1763 svn_mergeinfo_catalog_merge(svn_mergeinfo_catalog_t mergeinfo_cat,
1764 svn_mergeinfo_catalog_t changes_cat,
1765 apr_pool_t *result_pool,
1766 apr_pool_t *scratch_pool)
1768 int i = 0;
1769 int j = 0;
1770 apr_array_header_t *sorted_cat =
1771 svn_sort__hash(mergeinfo_cat, svn_sort_compare_items_as_paths,
1772 scratch_pool);
1773 apr_array_header_t *sorted_changes =
1774 svn_sort__hash(changes_cat, svn_sort_compare_items_as_paths,
1775 scratch_pool);
1777 while (i < sorted_cat->nelts && j < sorted_changes->nelts)
1779 svn_sort__item_t cat_elt, change_elt;
1780 int res;
1782 cat_elt = APR_ARRAY_IDX(sorted_cat, i, svn_sort__item_t);
1783 change_elt = APR_ARRAY_IDX(sorted_changes, j, svn_sort__item_t);
1784 res = svn_sort_compare_items_as_paths(&cat_elt, &change_elt);
1786 if (res == 0) /* Both catalogs have mergeinfo for a given path. */
1788 svn_mergeinfo_t mergeinfo = cat_elt.value;
1789 svn_mergeinfo_t changes_mergeinfo = change_elt.value;
1791 SVN_ERR(svn_mergeinfo_merge2(mergeinfo, changes_mergeinfo,
1792 result_pool, scratch_pool));
1793 apr_hash_set(mergeinfo_cat, cat_elt.key, cat_elt.klen, mergeinfo);
1794 i++;
1795 j++;
1797 else if (res < 0) /* Only MERGEINFO_CAT has mergeinfo for this path. */
1799 i++;
1801 else /* Only CHANGES_CAT has mergeinfo for this path. */
1803 apr_hash_set(mergeinfo_cat,
1804 apr_pstrdup(result_pool, change_elt.key),
1805 change_elt.klen,
1806 svn_mergeinfo_dup(change_elt.value, result_pool));
1807 j++;
1811 /* Copy back any remaining elements from the CHANGES_CAT catalog. */
1812 for (; j < sorted_changes->nelts; j++)
1814 svn_sort__item_t elt = APR_ARRAY_IDX(sorted_changes, j,
1815 svn_sort__item_t);
1816 apr_hash_set(mergeinfo_cat,
1817 apr_pstrdup(result_pool, elt.key),
1818 elt.klen,
1819 svn_mergeinfo_dup(elt.value, result_pool));
1822 return SVN_NO_ERROR;
1825 svn_error_t *
1826 svn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo,
1827 svn_mergeinfo_t mergeinfo1,
1828 svn_mergeinfo_t mergeinfo2,
1829 svn_boolean_t consider_inheritance,
1830 apr_pool_t *result_pool,
1831 apr_pool_t *scratch_pool)
1833 apr_hash_index_t *hi;
1834 apr_pool_t *iterpool;
1836 *mergeinfo = apr_hash_make(result_pool);
1837 iterpool = svn_pool_create(scratch_pool);
1839 /* ### TODO(reint): Do we care about the case when a path in one
1840 ### mergeinfo hash has inheritable mergeinfo, and in the other
1841 ### has non-inhertiable mergeinfo? It seems like that path
1842 ### itself should really be an intersection, while child paths
1843 ### should not be... */
1844 for (hi = apr_hash_first(scratch_pool, mergeinfo1);
1845 hi; hi = apr_hash_next(hi))
1847 const char *path = svn__apr_hash_index_key(hi);
1848 svn_rangelist_t *rangelist1 = svn__apr_hash_index_val(hi);
1849 svn_rangelist_t *rangelist2;
1851 svn_pool_clear(iterpool);
1852 rangelist2 = svn_hash_gets(mergeinfo2, path);
1853 if (rangelist2)
1855 SVN_ERR(svn_rangelist_intersect(&rangelist2, rangelist1, rangelist2,
1856 consider_inheritance, iterpool));
1857 if (rangelist2->nelts > 0)
1858 svn_hash_sets(*mergeinfo, apr_pstrdup(result_pool, path),
1859 svn_rangelist_dup(rangelist2, result_pool));
1862 svn_pool_destroy(iterpool);
1863 return SVN_NO_ERROR;
1866 svn_error_t *
1867 svn_mergeinfo_remove2(svn_mergeinfo_t *mergeinfo,
1868 svn_mergeinfo_t eraser,
1869 svn_mergeinfo_t whiteboard,
1870 svn_boolean_t consider_inheritance,
1871 apr_pool_t *result_pool,
1872 apr_pool_t *scratch_pool)
1874 *mergeinfo = apr_hash_make(result_pool);
1875 return walk_mergeinfo_hash_for_diff(whiteboard, eraser, *mergeinfo, NULL,
1876 consider_inheritance, result_pool,
1877 scratch_pool);
1880 svn_error_t *
1881 svn_rangelist_to_string(svn_string_t **output,
1882 const svn_rangelist_t *rangelist,
1883 apr_pool_t *pool)
1885 svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool);
1887 if (rangelist->nelts > 0)
1889 int i;
1890 svn_merge_range_t *range;
1892 /* Handle the elements that need commas at the end. */
1893 for (i = 0; i < rangelist->nelts - 1; i++)
1895 range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1896 svn_stringbuf_appendcstr(buf, range_to_string(range, pool));
1897 svn_stringbuf_appendcstr(buf, ",");
1900 /* Now handle the last element, which needs no comma. */
1901 range = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
1902 svn_stringbuf_appendcstr(buf, range_to_string(range, pool));
1905 *output = svn_stringbuf__morph_into_string(buf);
1907 return SVN_NO_ERROR;
1910 /* Converts a mergeinfo INPUT to an unparsed mergeinfo in OUTPUT. If PREFIX
1911 is not NULL then prepend PREFIX to each line in OUTPUT. If INPUT contains
1912 no elements, return the empty string. If INPUT contains any merge source
1913 path keys that are relative then convert these to absolute paths in
1914 *OUTPUT.
1916 static svn_error_t *
1917 mergeinfo_to_stringbuf(svn_stringbuf_t **output,
1918 svn_mergeinfo_t input,
1919 const char *prefix,
1920 apr_pool_t *pool)
1922 *output = svn_stringbuf_create_empty(pool);
1924 if (apr_hash_count(input) > 0)
1926 apr_array_header_t *sorted =
1927 svn_sort__hash(input, svn_sort_compare_items_as_paths, pool);
1928 int i;
1930 for (i = 0; i < sorted->nelts; i++)
1932 svn_sort__item_t elt = APR_ARRAY_IDX(sorted, i, svn_sort__item_t);
1933 svn_string_t *revlist;
1935 SVN_ERR(svn_rangelist_to_string(&revlist, elt.value, pool));
1936 svn_stringbuf_appendcstr(
1937 *output,
1938 apr_psprintf(pool, "%s%s%s:%s",
1939 prefix ? prefix : "",
1940 *((const char *) elt.key) == '/' ? "" : "/",
1941 (const char *) elt.key,
1942 revlist->data));
1943 if (i < sorted->nelts - 1)
1944 svn_stringbuf_appendcstr(*output, "\n");
1948 return SVN_NO_ERROR;
1951 svn_error_t *
1952 svn_mergeinfo_to_string(svn_string_t **output, svn_mergeinfo_t input,
1953 apr_pool_t *pool)
1955 svn_stringbuf_t *mergeinfo_buf;
1957 SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_buf, input, NULL, pool));
1958 *output = svn_stringbuf__morph_into_string(mergeinfo_buf);
1959 return SVN_NO_ERROR;
1962 svn_error_t *
1963 svn_mergeinfo_sort(svn_mergeinfo_t input, apr_pool_t *pool)
1965 apr_hash_index_t *hi;
1967 for (hi = apr_hash_first(pool, input); hi; hi = apr_hash_next(hi))
1969 apr_array_header_t *rl = svn__apr_hash_index_val(hi);
1971 qsort(rl->elts, rl->nelts, rl->elt_size, svn_sort_compare_ranges);
1973 return SVN_NO_ERROR;
1976 svn_mergeinfo_catalog_t
1977 svn_mergeinfo_catalog_dup(svn_mergeinfo_catalog_t mergeinfo_catalog,
1978 apr_pool_t *pool)
1980 svn_mergeinfo_t new_mergeinfo_catalog = apr_hash_make(pool);
1981 apr_hash_index_t *hi;
1983 for (hi = apr_hash_first(pool, mergeinfo_catalog);
1985 hi = apr_hash_next(hi))
1987 const char *key = svn__apr_hash_index_key(hi);
1988 svn_mergeinfo_t val = svn__apr_hash_index_val(hi);
1990 svn_hash_sets(new_mergeinfo_catalog, apr_pstrdup(pool, key),
1991 svn_mergeinfo_dup(val, pool));
1994 return new_mergeinfo_catalog;
1997 svn_mergeinfo_t
1998 svn_mergeinfo_dup(svn_mergeinfo_t mergeinfo, apr_pool_t *pool)
2000 svn_mergeinfo_t new_mergeinfo = svn_hash__make(pool);
2001 apr_hash_index_t *hi;
2003 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2005 const char *path = svn__apr_hash_index_key(hi);
2006 apr_ssize_t pathlen = svn__apr_hash_index_klen(hi);
2007 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2009 apr_hash_set(new_mergeinfo, apr_pstrmemdup(pool, path, pathlen), pathlen,
2010 svn_rangelist_dup(rangelist, pool));
2013 return new_mergeinfo;
2016 svn_error_t *
2017 svn_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)
2026 apr_hash_index_t *hi;
2027 svn_mergeinfo_t inheritable_mergeinfo = apr_hash_make(result_pool);
2029 for (hi = apr_hash_first(scratch_pool, mergeinfo);
2031 hi = apr_hash_next(hi))
2033 const char *key = svn__apr_hash_index_key(hi);
2034 apr_ssize_t keylen = svn__apr_hash_index_klen(hi);
2035 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2036 svn_rangelist_t *inheritable_rangelist;
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);
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);
2052 *output = inheritable_mergeinfo;
2053 return SVN_NO_ERROR;
2057 svn_error_t *
2058 svn_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)
2066 *inheritable_rangelist = apr_array_make(result_pool, 1,
2067 sizeof(svn_merge_range_t *));
2068 if (rangelist->nelts)
2070 if (!SVN_IS_VALID_REVNUM(start)
2071 || !SVN_IS_VALID_REVNUM(end)
2072 || end < start)
2074 int i;
2075 /* We want all non-inheritable ranges removed. */
2076 for (i = 0; i < rangelist->nelts; i++)
2078 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
2079 svn_merge_range_t *);
2080 if (range->inheritable == inheritable)
2082 svn_merge_range_t *inheritable_range =
2083 apr_palloc(result_pool, sizeof(*inheritable_range));
2084 inheritable_range->start = range->start;
2085 inheritable_range->end = range->end;
2086 inheritable_range->inheritable = TRUE;
2087 APR_ARRAY_PUSH(*inheritable_rangelist,
2088 svn_merge_range_t *) = range;
2092 else
2094 /* We want only the non-inheritable ranges bound by START
2095 and END removed. */
2096 svn_rangelist_t *ranges_inheritable =
2097 svn_rangelist__initialize(start, end, inheritable, scratch_pool);
2099 if (rangelist->nelts)
2100 SVN_ERR(svn_rangelist_remove(inheritable_rangelist,
2101 ranges_inheritable,
2102 rangelist,
2103 TRUE,
2104 result_pool));
2107 return SVN_NO_ERROR;
2110 svn_boolean_t
2111 svn_mergeinfo__remove_empty_rangelists(svn_mergeinfo_t mergeinfo,
2112 apr_pool_t *pool)
2114 apr_hash_index_t *hi;
2115 svn_boolean_t removed_some_ranges = FALSE;
2117 if (mergeinfo)
2119 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2121 const char *path = svn__apr_hash_index_key(hi);
2122 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2124 if (rangelist->nelts == 0)
2126 svn_hash_sets(mergeinfo, path, NULL);
2127 removed_some_ranges = TRUE;
2131 return removed_some_ranges;
2134 svn_error_t *
2135 svn_mergeinfo__remove_prefix_from_catalog(svn_mergeinfo_catalog_t *out_catalog,
2136 svn_mergeinfo_catalog_t in_catalog,
2137 const char *prefix_path,
2138 apr_pool_t *pool)
2140 apr_hash_index_t *hi;
2142 SVN_ERR_ASSERT(prefix_path[0] == '/');
2144 *out_catalog = apr_hash_make(pool);
2146 for (hi = apr_hash_first(pool, in_catalog); hi; hi = apr_hash_next(hi))
2148 const char *original_path = svn__apr_hash_index_key(hi);
2149 svn_mergeinfo_t value = svn__apr_hash_index_val(hi);
2150 const char *new_path;
2152 new_path = svn_fspath__skip_ancestor(prefix_path, original_path);
2153 SVN_ERR_ASSERT(new_path);
2155 svn_hash_sets(*out_catalog, new_path, value);
2158 return SVN_NO_ERROR;
2161 svn_error_t *
2162 svn_mergeinfo__add_prefix_to_catalog(svn_mergeinfo_catalog_t *out_catalog,
2163 svn_mergeinfo_catalog_t in_catalog,
2164 const char *prefix_path,
2165 apr_pool_t *result_pool,
2166 apr_pool_t *scratch_pool)
2168 apr_hash_index_t *hi;
2170 *out_catalog = apr_hash_make(result_pool);
2172 for (hi = apr_hash_first(scratch_pool, in_catalog);
2174 hi = apr_hash_next(hi))
2176 const char *original_path = svn__apr_hash_index_key(hi);
2177 svn_mergeinfo_t value = svn__apr_hash_index_val(hi);
2179 if (original_path[0] == '/')
2180 original_path++;
2182 svn_hash_sets(*out_catalog,
2183 svn_dirent_join(prefix_path, original_path, result_pool),
2184 value);
2187 return SVN_NO_ERROR;
2190 svn_error_t *
2191 svn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo,
2192 svn_mergeinfo_t mergeinfo,
2193 const char *suffix_relpath,
2194 apr_pool_t *result_pool,
2195 apr_pool_t *scratch_pool)
2197 apr_hash_index_t *hi;
2199 SVN_ERR_ASSERT(suffix_relpath && svn_relpath_is_canonical(suffix_relpath));
2201 *out_mergeinfo = apr_hash_make(result_pool);
2203 for (hi = apr_hash_first(scratch_pool, mergeinfo);
2205 hi = apr_hash_next(hi))
2207 const char *fspath = svn__apr_hash_index_key(hi);
2208 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2210 svn_hash_sets(*out_mergeinfo,
2211 svn_fspath__join(fspath, suffix_relpath, result_pool),
2212 rangelist);
2215 return SVN_NO_ERROR;
2218 svn_rangelist_t *
2219 svn_rangelist_dup(const svn_rangelist_t *rangelist, apr_pool_t *pool)
2221 svn_rangelist_t *new_rl = apr_array_make(pool, rangelist->nelts,
2222 sizeof(svn_merge_range_t *));
2224 /* allocate target range buffer with a single operation */
2225 svn_merge_range_t *copy = apr_palloc(pool, sizeof(*copy) * rangelist->nelts);
2226 int i;
2228 /* fill it iteratively and link it into the range list */
2229 for (i = 0; i < rangelist->nelts; i++)
2231 memcpy(copy + i,
2232 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *),
2233 sizeof(*copy));
2234 APR_ARRAY_PUSH(new_rl, svn_merge_range_t *) = copy + i;
2237 return new_rl;
2240 svn_merge_range_t *
2241 svn_merge_range_dup(const svn_merge_range_t *range, apr_pool_t *pool)
2243 svn_merge_range_t *new_range = apr_palloc(pool, sizeof(*new_range));
2244 memcpy(new_range, range, sizeof(*new_range));
2245 return new_range;
2248 svn_boolean_t
2249 svn_merge_range_contains_rev(const svn_merge_range_t *range, svn_revnum_t rev)
2251 assert(SVN_IS_VALID_REVNUM(range->start));
2252 assert(SVN_IS_VALID_REVNUM(range->end));
2253 assert(range->start != range->end);
2255 if (range->start < range->end)
2256 return rev > range->start && rev <= range->end;
2257 else
2258 return rev > range->end && rev <= range->start;
2261 svn_error_t *
2262 svn_mergeinfo__catalog_to_formatted_string(svn_string_t **output,
2263 svn_mergeinfo_catalog_t catalog,
2264 const char *key_prefix,
2265 const char *val_prefix,
2266 apr_pool_t *pool)
2268 svn_stringbuf_t *output_buf = NULL;
2270 if (catalog && apr_hash_count(catalog))
2272 int i;
2273 apr_array_header_t *sorted_catalog =
2274 svn_sort__hash(catalog, svn_sort_compare_items_as_paths, pool);
2276 output_buf = svn_stringbuf_create_empty(pool);
2277 for (i = 0; i < sorted_catalog->nelts; i++)
2279 svn_sort__item_t elt =
2280 APR_ARRAY_IDX(sorted_catalog, i, svn_sort__item_t);
2281 const char *path1;
2282 svn_mergeinfo_t mergeinfo;
2283 svn_stringbuf_t *mergeinfo_output_buf;
2285 path1 = elt.key;
2286 mergeinfo = elt.value;
2287 if (key_prefix)
2288 svn_stringbuf_appendcstr(output_buf, key_prefix);
2289 svn_stringbuf_appendcstr(output_buf, path1);
2290 svn_stringbuf_appendcstr(output_buf, "\n");
2291 SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_output_buf, mergeinfo,
2292 val_prefix ? val_prefix : "", pool));
2293 svn_stringbuf_appendstr(output_buf, mergeinfo_output_buf);
2294 svn_stringbuf_appendcstr(output_buf, "\n");
2297 #if SVN_DEBUG
2298 else if (!catalog)
2300 output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool);
2301 svn_stringbuf_appendcstr(output_buf, _("NULL mergeinfo catalog\n"));
2303 else if (apr_hash_count(catalog) == 0)
2305 output_buf = svn_stringbuf_create(key_prefix ? key_prefix : "", pool);
2306 svn_stringbuf_appendcstr(output_buf, _("empty mergeinfo catalog\n"));
2308 #endif
2310 /* If we have an output_buf, convert it to an svn_string_t;
2311 otherwise, return a new string containing only a newline
2312 character. */
2313 if (output_buf)
2314 *output = svn_stringbuf__morph_into_string(output_buf);
2315 else
2316 *output = svn_string_create("\n", pool);
2318 return SVN_NO_ERROR;
2321 svn_error_t *
2322 svn_mergeinfo__get_range_endpoints(svn_revnum_t *youngest_rev,
2323 svn_revnum_t *oldest_rev,
2324 svn_mergeinfo_t mergeinfo,
2325 apr_pool_t *pool)
2327 *youngest_rev = *oldest_rev = SVN_INVALID_REVNUM;
2328 if (mergeinfo)
2330 apr_hash_index_t *hi;
2332 for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
2334 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2336 if (rangelist->nelts)
2338 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist,
2339 rangelist->nelts - 1,
2340 svn_merge_range_t *);
2341 if (!SVN_IS_VALID_REVNUM(*youngest_rev)
2342 || (range->end > *youngest_rev))
2343 *youngest_rev = range->end;
2345 range = APR_ARRAY_IDX(rangelist, 0, svn_merge_range_t *);
2346 if (!SVN_IS_VALID_REVNUM(*oldest_rev)
2347 || (range->start < *oldest_rev))
2348 *oldest_rev = range->start;
2352 return SVN_NO_ERROR;
2355 svn_error_t *
2356 svn_mergeinfo__filter_catalog_by_ranges(svn_mergeinfo_catalog_t *filtered_cat,
2357 svn_mergeinfo_catalog_t catalog,
2358 svn_revnum_t youngest_rev,
2359 svn_revnum_t oldest_rev,
2360 svn_boolean_t include_range,
2361 apr_pool_t *result_pool,
2362 apr_pool_t *scratch_pool)
2364 apr_hash_index_t *hi;
2366 *filtered_cat = apr_hash_make(result_pool);
2367 for (hi = apr_hash_first(scratch_pool, catalog);
2369 hi = apr_hash_next(hi))
2371 const char *path = svn__apr_hash_index_key(hi);
2372 svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi);
2373 svn_mergeinfo_t filtered_mergeinfo;
2375 SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&filtered_mergeinfo,
2376 mergeinfo,
2377 youngest_rev,
2378 oldest_rev,
2379 include_range,
2380 result_pool,
2381 scratch_pool));
2382 if (apr_hash_count(filtered_mergeinfo))
2383 svn_hash_sets(*filtered_cat,
2384 apr_pstrdup(result_pool, path), filtered_mergeinfo);
2387 return SVN_NO_ERROR;
2390 svn_error_t *
2391 svn_mergeinfo__filter_mergeinfo_by_ranges(svn_mergeinfo_t *filtered_mergeinfo,
2392 svn_mergeinfo_t mergeinfo,
2393 svn_revnum_t youngest_rev,
2394 svn_revnum_t oldest_rev,
2395 svn_boolean_t include_range,
2396 apr_pool_t *result_pool,
2397 apr_pool_t *scratch_pool)
2399 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
2400 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
2401 SVN_ERR_ASSERT(oldest_rev < youngest_rev);
2403 *filtered_mergeinfo = apr_hash_make(result_pool);
2405 if (mergeinfo)
2407 apr_hash_index_t *hi;
2408 svn_rangelist_t *filter_rangelist =
2409 svn_rangelist__initialize(oldest_rev, youngest_rev, TRUE,
2410 scratch_pool);
2412 for (hi = apr_hash_first(scratch_pool, mergeinfo);
2414 hi = apr_hash_next(hi))
2416 const char *path = svn__apr_hash_index_key(hi);
2417 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2419 if (rangelist->nelts)
2421 svn_rangelist_t *new_rangelist;
2423 SVN_ERR(rangelist_intersect_or_remove(
2424 &new_rangelist, filter_rangelist, rangelist,
2425 ! include_range, FALSE, result_pool));
2427 if (new_rangelist->nelts)
2428 svn_hash_sets(*filtered_mergeinfo,
2429 apr_pstrdup(result_pool, path), new_rangelist);
2433 return SVN_NO_ERROR;
2436 svn_error_t *
2437 svn_mergeinfo__adjust_mergeinfo_rangelists(svn_mergeinfo_t *adjusted_mergeinfo,
2438 svn_mergeinfo_t mergeinfo,
2439 svn_revnum_t offset,
2440 apr_pool_t *result_pool,
2441 apr_pool_t *scratch_pool)
2443 apr_hash_index_t *hi;
2444 *adjusted_mergeinfo = apr_hash_make(result_pool);
2446 if (mergeinfo)
2448 for (hi = apr_hash_first(scratch_pool, mergeinfo);
2450 hi = apr_hash_next(hi))
2452 int i;
2453 const char *path = svn__apr_hash_index_key(hi);
2454 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2455 svn_rangelist_t *adjusted_rangelist =
2456 apr_array_make(result_pool, rangelist->nelts,
2457 sizeof(svn_merge_range_t *));
2459 for (i = 0; i < rangelist->nelts; i++)
2461 svn_merge_range_t *range =
2462 APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
2464 if (range->start + offset > 0 && range->end + offset > 0)
2466 if (range->start + offset < 0)
2467 range->start = 0;
2468 else
2469 range->start = range->start + offset;
2471 if (range->end + offset < 0)
2472 range->end = 0;
2473 else
2474 range->end = range->end + offset;
2475 APR_ARRAY_PUSH(adjusted_rangelist, svn_merge_range_t *) =
2476 range;
2480 if (adjusted_rangelist->nelts)
2481 svn_hash_sets(*adjusted_mergeinfo, apr_pstrdup(result_pool, path),
2482 adjusted_rangelist);
2485 return SVN_NO_ERROR;
2488 svn_boolean_t
2489 svn_mergeinfo__is_noninheritable(svn_mergeinfo_t mergeinfo,
2490 apr_pool_t *scratch_pool)
2492 if (mergeinfo)
2494 apr_hash_index_t *hi;
2496 for (hi = apr_hash_first(scratch_pool, mergeinfo);
2498 hi = apr_hash_next(hi))
2500 svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
2501 int i;
2503 for (i = 0; i < rangelist->nelts; i++)
2505 svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
2506 svn_merge_range_t *);
2507 if (!range->inheritable)
2508 return TRUE;
2512 return FALSE;
2515 svn_rangelist_t *
2516 svn_rangelist__initialize(svn_revnum_t start,
2517 svn_revnum_t end,
2518 svn_boolean_t inheritable,
2519 apr_pool_t *result_pool)
2521 svn_rangelist_t *rangelist =
2522 apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *));
2523 svn_merge_range_t *range = apr_pcalloc(result_pool, sizeof(*range));
2525 range->start = start;
2526 range->end = end;
2527 range->inheritable = inheritable;
2528 APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = range;
2529 return rangelist;
2532 svn_error_t *
2533 svn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t *mergeinfo_p,
2534 const apr_array_header_t *segments,
2535 apr_pool_t *pool)
2537 svn_mergeinfo_t mergeinfo = apr_hash_make(pool);
2538 int i;
2540 /* Translate location segments into merge sources and ranges. */
2541 for (i = 0; i < segments->nelts; i++)
2543 svn_location_segment_t *segment =
2544 APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
2545 svn_rangelist_t *path_ranges;
2546 svn_merge_range_t *range;
2547 const char *source_path;
2549 /* No path segment? Skip it. */
2550 if (! segment->path)
2551 continue;
2553 /* Prepend a leading slash to our path. */
2554 source_path = apr_pstrcat(pool, "/", segment->path, (char *)NULL);
2556 /* See if we already stored ranges for this path. If not, make
2557 a new list. */
2558 path_ranges = svn_hash_gets(mergeinfo, source_path);
2559 if (! path_ranges)
2560 path_ranges = apr_array_make(pool, 1, sizeof(range));
2562 /* A svn_location_segment_t may have legitimately describe only
2563 revision 0, but there is no corresponding representation for
2564 this in a svn_merge_range_t. */
2565 if (segment->range_start == 0 && segment->range_end == 0)
2566 continue;
2568 /* Build a merge range, push it onto the list of ranges, and for
2569 good measure, (re)store it in the hash. */
2570 range = apr_pcalloc(pool, sizeof(*range));
2571 range->start = MAX(segment->range_start - 1, 0);
2572 range->end = segment->range_end;
2573 range->inheritable = TRUE;
2574 APR_ARRAY_PUSH(path_ranges, svn_merge_range_t *) = range;
2575 svn_hash_sets(mergeinfo, source_path, path_ranges);
2578 *mergeinfo_p = mergeinfo;
2579 return SVN_NO_ERROR;
2582 svn_error_t *
2583 svn_rangelist__merge_many(svn_rangelist_t *merged_rangelist,
2584 svn_mergeinfo_t merge_history,
2585 apr_pool_t *result_pool,
2586 apr_pool_t *scratch_pool)
2588 if (apr_hash_count(merge_history))
2590 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2591 apr_hash_index_t *hi;
2593 for (hi = apr_hash_first(scratch_pool, merge_history);
2595 hi = apr_hash_next(hi))
2597 svn_rangelist_t *subtree_rangelist = svn__apr_hash_index_val(hi);
2599 svn_pool_clear(iterpool);
2600 SVN_ERR(svn_rangelist_merge2(merged_rangelist, subtree_rangelist,
2601 result_pool, iterpool));
2603 svn_pool_destroy(iterpool);
2605 return SVN_NO_ERROR;
2609 const char *
2610 svn_inheritance_to_word(svn_mergeinfo_inheritance_t inherit)
2612 switch (inherit)
2614 case svn_mergeinfo_inherited:
2615 return "inherited";
2616 case svn_mergeinfo_nearest_ancestor:
2617 return "nearest-ancestor";
2618 default:
2619 return "explicit";
2623 svn_mergeinfo_inheritance_t
2624 svn_inheritance_from_word(const char *word)
2626 if (strcmp(word, "inherited") == 0)
2627 return svn_mergeinfo_inherited;
2628 if (strcmp(word, "nearest-ancestor") == 0)
2629 return svn_mergeinfo_nearest_ancestor;
2630 return svn_mergeinfo_explicit;