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