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