2 * mergeinfo.c: Mergeinfo parsing and handling
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
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"
42 #include "private/svn_dep_compat.h"
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. */
48 range_contains(const svn_merge_range_t
*first
, const svn_merge_range_t
*second
,
49 svn_boolean_t consider_inheritance
);
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.
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.
63 Range overlapping detection algorithm from
64 http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap
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
)
72 if (in1
->start
<= in2
->end
&& in2
->start
<= in1
->end
)
74 if (!consider_inheritance
75 || (consider_inheritance
76 && (in1
->inheritable
== in2
->inheritable
)))
78 output
->start
= MIN(in1
->start
, in2
->start
);
79 output
->end
= MAX(in1
->end
, in2
->end
);
80 output
->inheritable
= (in1
->inheritable
|| in2
->inheritable
);
87 /* pathname -> PATHNAME */
89 parse_pathname(const char **input
,
91 const char **pathname
,
94 const char *curr
= *input
;
95 const char *last_colon
= NULL
;
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')
108 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
109 _("Pathname not terminated by ':'"));
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
),
122 /* Return TRUE iff (svn_merge_range_t *) RANGE describes a valid, forward
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))
132 /* Ways in which two svn_merge_range_t can intersect or adjoin, if at all. */
133 typedef enum intersection_type_t
135 /* Ranges don't intersect and don't adjoin. */
136 svn__no_intersection
,
138 /* Ranges are equal. */
139 svn__equal_intersection
,
141 /* Ranges adjoin but don't overlap. */
142 svn__adjoining_intersection
,
144 /* Ranges overlap but neither is a subset of the other. */
145 svn__overlapping_intersection
,
147 /* One range is a proper subset of the other. */
148 svn__proper_subset_intersection
149 } intersection_type_t
;
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. */
155 get_type_of_intersection(const svn_merge_range_t
*r1
,
156 const svn_merge_range_t
*r2
,
157 intersection_type_t
*intersection_type
)
161 SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r1
));
162 SVN_ERR_ASSERT(IS_VALID_FORWARD_RANGE(r2
));
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
;
175 *intersection_type
= svn__overlapping_intersection
;
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.
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
193 ([*] Due to the simplifying assumption below, only the last range in
194 RANGELIST is considered.)
198 If RANGELIST is not empty assume NEW_RANGE does not intersect with any
199 range before the last one in RANGELIST.
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,
205 If NEW_RANGE intersects with the last range in RANGELIST then combine
206 these two ranges as described below:
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:
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.:
218 Last range in NEW_RANGE RESULTING RANGES
220 ------------- --------- ----------------
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
231 Last range in NEW_RANGE RESULTING RANGES
233 ------------- --------- ----------------
234 4-10* 6 4-5*, 6, 7-10*
235 4-10* 6-12 4-5*, 6-12
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
242 Last range in NEW_RANGE RESULTING RANGES
244 ------------- --------- ----------------
245 4-10 6* 4-10 (Not 4-5, 6, 7-10)
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.
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
)
257 svn_merge_range_t
*lastrange
;
258 svn_merge_range_t combined_range
;
260 /* We don't accept a NULL RANGELIST. */
261 SVN_ERR_ASSERT(rangelist
);
263 if (rangelist
->nelts
> 0)
264 lastrange
= APR_ARRAY_IDX(rangelist
, rangelist
->nelts
- 1, svn_merge_range_t
*);
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
);
274 else if (combine_ranges(&combined_range
, lastrange
, new_range
,
275 consider_inheritance
))
277 *lastrange
= combined_range
;
279 else if (!consider_inheritance
)
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
);
287 else /* Considering inheritance */
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
;
295 SVN_ERR(get_type_of_intersection(new_range
, lastrange
,
296 &intersection_type
));
298 switch (intersection_type
)
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
,
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
;
316 case svn__adjoining_intersection
:
317 /* They adjoin but don't overlap so just push NEW_RANGE
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
,
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
331 svn_merge_range_t
*r1
= svn_merge_range_dup(lastrange
,
333 svn_merge_range_t
*r2
= svn_merge_range_dup(new_range
,
336 /* Pop off *LASTRANGE to make our manipulations
338 apr_array_pop(rangelist
);
340 /* Ensure R1 is the older range. */
341 if (r2
->start
< r1
->start
)
343 /* Swap R1 and R2. */
348 /* Absorb the intersecting ranges into the
349 inheritable range. */
355 /* Push everything back onto RANGELIST. */
356 APR_ARRAY_PUSH(rangelist
, svn_merge_range_t
*) = r1
;
357 sorted
= (svn_sort_compare_ranges(&lastrange
,
359 APR_ARRAY_PUSH(rangelist
, svn_merge_range_t
*) = r2
;
361 sorted
= (svn_sort_compare_ranges(&r1
, &r2
) < 0);
365 default: /* svn__proper_subset_intersection */
367 /* One range is a proper subset of the other. */
368 svn_merge_range_t
*r1
= svn_merge_range_dup(lastrange
,
370 svn_merge_range_t
*r2
= svn_merge_range_dup(new_range
,
372 svn_merge_range_t
*r3
= NULL
;
374 /* Pop off *LASTRANGE to make our manipulations
376 apr_array_pop(rangelist
);
378 /* Ensure R1 is the superset. */
379 if (r2
->start
< r1
->start
|| r2
->end
> r1
->end
)
381 /* Swap R1 and R2. */
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
);
394 else if (r1
->start
== r2
->start
)
396 svn_revnum_t tmp_revnum
;
398 /* *LASTRANGE and NEW_RANGE share an end point. */
399 tmp_revnum
= r1
->end
;
401 r2
->inheritable
= r1
->inheritable
;
402 r1
->inheritable
= TRUE
;
404 r2
->end
= tmp_revnum
;
406 else if (r1
->end
== r2
->end
)
408 /* *LASTRANGE and NEW_RANGE share an end point. */
410 r2
->inheritable
= TRUE
;
414 /* NEW_RANGE and *LASTRANGE share neither start
416 r3
= apr_pcalloc(result_pool
, sizeof(*r3
));
419 r3
->inheritable
= r1
->inheritable
;
420 r2
->inheritable
= TRUE
;
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);
429 APR_ARRAY_PUSH(rangelist
, svn_merge_range_t
*) = r2
;
431 sorted
= (svn_sort_compare_ranges(&r1
, &r2
) < 0);
435 APR_ARRAY_PUSH(rangelist
, svn_merge_range_t
*) = r3
;
439 sorted
= (svn_sort_compare_ranges(&r2
,
442 sorted
= (svn_sort_compare_ranges(&r1
,
450 /* Some of the above cases might have put *RANGELIST out of
453 svn_sort__array(rangelist
, svn_sort_compare_ranges
);
459 /* Convert a single svn_merge_range_t *RANGE back into a string. */
461 range_to_string(const svn_merge_range_t
*range
,
465 = range
->inheritable
? "" : SVN_MERGEINFO_NONINHERITABLE_STR
;
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
);
474 return apr_psprintf(pool
, "%ld-%ld%s", range
->start
, range
->end
+ 1, mark
);
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.
483 revisionlist -> (revisionelement)(COMMA revisionelement)*
484 revisionrange -> REVISION "-" REVISION("*")
485 revisionelement -> revisionrange | REVISION("*")
488 parse_rangelist(const char **input
, const char *end
,
489 svn_rangelist_t
*rangelist
,
492 const char *curr
= *input
;
494 /* Eat any leading horizontal white-space before the rangelist. */
495 while (curr
< end
&& *curr
!= '\n' && isspace(*curr
))
498 if (*curr
== '\n' || curr
== end
)
500 /* Empty range list. */
505 while (curr
< end
&& *curr
!= '\n')
507 /* Parse individual revisions or revision ranges. */
508 svn_merge_range_t
*mrange
= apr_pcalloc(pool
, sizeof(*mrange
));
509 svn_revnum_t firstrev
;
511 SVN_ERR(svn_revnum_parse(&firstrev
, curr
, &curr
));
512 if (*curr
!= '-' && *curr
!= '\n' && *curr
!= ',' && *curr
!= '*'
514 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
515 _("Invalid character '%c' found in revision "
517 mrange
->start
= firstrev
- 1;
518 mrange
->end
= firstrev
;
519 mrange
->inheritable
= TRUE
;
522 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
523 _("Invalid revision number '0' found in "
528 svn_revnum_t secondrev
;
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 "
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
;
545 if (*curr
== '\n' || curr
== end
)
547 APR_ARRAY_PUSH(rangelist
, svn_merge_range_t
*) = mrange
;
551 else if (*curr
== ',')
553 APR_ARRAY_PUSH(rangelist
, svn_merge_range_t
*) = mrange
;
556 else if (*curr
== '*')
558 mrange
->inheritable
= FALSE
;
560 if (*curr
== ',' || *curr
== '\n' || curr
== end
)
562 APR_ARRAY_PUSH(rangelist
, svn_merge_range_t
*) = mrange
;
575 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
576 _("Invalid character '%c' found in "
577 "range list"), *curr
);
582 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
583 _("Invalid character '%c' found in "
584 "range list"), *curr
);
589 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
590 _("Range list parsing ended before hitting "
597 svn_rangelist__parse(svn_rangelist_t
**rangelist
,
599 apr_pool_t
*result_pool
)
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
));
609 svn_rangelist__is_canonical(const svn_rangelist_t
*rangelist
)
612 svn_merge_range_t
**ranges
= (svn_merge_range_t
**)rangelist
->elts
;
614 /* Check for reversed and empty ranges */
615 for (i
= 0; i
< rangelist
->nelts
; ++i
)
617 if (ranges
[i
]->start
>= ranges
[i
]->end
)
621 /* Check for overlapping ranges */
622 for (i
= 0; i
< rangelist
->nelts
- 1; ++i
)
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
)
629 return FALSE
; /* Ranges should have been combined */
636 /* In-place combines adjacent ranges in a rangelist.
637 SCRATCH_POOL is just used for providing error messages. */
639 svn_rangelist__canonicalize(svn_rangelist_t
*rangelist
,
640 apr_pool_t
*scratch_pool
)
643 svn_merge_range_t
*range
, *lastrange
;
645 if (svn_rangelist__is_canonical(rangelist
))
646 return SVN_NO_ERROR
; /* Nothing to do */
648 svn_sort__array(rangelist
, svn_sort_compare_ranges
);
650 lastrange
= APR_ARRAY_IDX(rangelist
, 0, svn_merge_range_t
*);
652 for (i
= 1; i
< rangelist
->nelts
; i
++)
654 range
= APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*);
655 if (lastrange
->start
<= range
->end
656 && range
->start
<= lastrange
->end
)
658 /* The ranges are adjacent or intersect. */
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
)
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 "
670 range_to_string(lastrange
,
672 range_to_string(range
,
676 /* Combine overlapping or adjacent ranges with the
677 same inheritability. */
678 if (lastrange
->inheritable
== range
->inheritable
)
680 lastrange
->end
= MAX(range
->end
, lastrange
->end
);
681 svn_sort__array_delete(rangelist
, i
, 1);
685 lastrange
= APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*);
691 /* revisionline -> PATHNAME COLON revisionlist
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.
697 parse_revision_line(const char **input
, const char *end
, svn_mergeinfo_t hash
,
698 apr_pool_t
*scratch_pool
)
700 const char *pathname
= "";
702 svn_rangelist_t
*existing_rangelist
;
703 svn_rangelist_t
*rangelist
= apr_array_make(scratch_pool
, 1,
704 sizeof(svn_merge_range_t
*));
706 SVN_ERR(parse_pathname(input
, end
, &pathname
, scratch_pool
));
708 if (*(*input
) != ':')
709 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
710 _("Pathname not terminated by ':'"));
714 SVN_ERR(parse_rangelist(input
, end
, rangelist
, scratch_pool
));
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 "
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.
732 SVN_ERR(svn_rangelist__canonicalize(rangelist
, scratch_pool
));
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
));
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
)));
752 /* top -> revisionline (NEWLINE revisionline)*
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.
758 parse_top(const char **input
, const char *end
, svn_mergeinfo_t hash
,
759 apr_pool_t
*scratch_pool
)
761 apr_pool_t
*iterpool
= svn_pool_create(scratch_pool
);
765 svn_pool_clear(iterpool
);
766 SVN_ERR(parse_revision_line(input
, end
, hash
, iterpool
));
768 svn_pool_destroy(iterpool
);
774 svn_mergeinfo_parse(svn_mergeinfo_t
*mergeinfo
,
780 *mergeinfo
= svn_hash__make(pool
);
781 err
= parse_top(&input
, input
+ strlen(input
), *mergeinfo
, pool
);
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'"),
791 /* Cleanup after svn_rangelist_merge2 when it modifies the ending range of
792 a single rangelist element in-place.
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.
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].
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.
809 Any new elements inserted into RANGELIST are allocated in RESULT_POOL.*/
811 adjust_remaining_ranges(svn_rangelist_t
*rangelist
,
813 apr_pool_t
*result_pool
)
817 int elements_to_delete
= 0;
818 svn_merge_range_t
*modified_range
;
820 if (*range_index
>= rangelist
->nelts
)
823 starting_index
= *range_index
+ 1;
824 modified_range
= APR_ARRAY_IDX(rangelist
, *range_index
, svn_merge_range_t
*);
826 for (i
= *range_index
+ 1; i
< rangelist
->nelts
; i
++)
828 svn_merge_range_t
*next_range
= APR_ARRAY_IDX(rangelist
, i
,
829 svn_merge_range_t
*);
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
)
836 /* Does MODIFIED_RANGE adjoin NEXT_RANGE? */
837 if (modified_range
->end
== next_range
->start
)
839 if (modified_range
->inheritable
== next_range
->inheritable
)
841 /* Combine adjoining ranges with the same inheritability. */
842 modified_range
->end
= next_range
->end
;
843 elements_to_delete
++;
847 /* Cannot join because inheritance differs. */
853 /* Alright, we know MODIFIED_RANGE overlaps NEXT_RANGE, but how? */
854 if (modified_range
->end
> next_range
->end
)
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
))
861 /* MODIFIED_RANGE absorbs NEXT_RANGE. */
862 elements_to_delete
++;
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 ______________________________________________
875 |______________________________________________|
882 _______________________________________________
884 M MODIFIED_RANGE O NEXT_RANGE P NEW_RANGE N
885 | (!inheritable) | (inheritable)| (!inheritable)|
886 |________________|______________|_______________|
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
,
897 /* Recurse with the new range. */
898 adjust_remaining_ranges(rangelist
, range_index
, result_pool
);
902 else if (modified_range
->end
== next_range
->end
)
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
))
909 /* MODIFIED_RANGE absorbs NEXT_RANGE. */
910 elements_to_delete
++;
914 /* The intersection between MODIFIED_RANGE and NEXT_RANGE is
915 absorbed by the latter. */
916 modified_range
->end
= next_range
->start
;
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
)
928 /* Combine overlapping ranges with the same inheritability. */
929 modified_range
->end
= next_range
->end
;
930 elements_to_delete
++;
932 else if (modified_range
->inheritable
)
934 /* MODIFIED_RANGE absorbs the portion of NEXT_RANGE it overlaps
935 and NEXT_RANGE is truncated. */
936 next_range
->start
= modified_range
->end
;
941 /* NEXT_RANGE absorbs the portion of MODIFIED_RANGE it overlaps
942 and MODIFIED_RANGE is truncated. */
943 modified_range
->end
= next_range
->start
;
950 if (elements_to_delete
)
951 svn_sort__array_delete(rangelist
, starting_index
, elements_to_delete
);
954 #if 0 /* Temporary debug helper code */
956 dual_dump(const char *prefix
,
957 const svn_rangelist_t
*rangelist
,
958 const svn_rangelist_t
*changes
,
959 apr_pool_t
*scratch_pool
)
961 svn_string_t
*rls
, *chg
;
963 SVN_ERR(svn_rangelist_to_string(&rls
, rangelist
, scratch_pool
));
964 SVN_ERR(svn_rangelist_to_string(&chg
, changes
, scratch_pool
));
966 SVN_DBG(("%s: %s / %s", prefix
, rls
->data
, chg
->data
));
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
)
977 svn_rangelist_t
*changes
;
981 SVN_ERR(svn_rangelist__canonicalize(rangelist
, scratch_pool
));
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
));
987 for (j
= 0; j
< changes
->nelts
; j
++)
989 svn_merge_range_t
*range
;
990 svn_merge_range_t
*change
=
991 APR_ARRAY_IDX(changes
, j
, svn_merge_range_t
*);
994 range
= (i
< rangelist
->nelts
)
995 ? APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*)
998 if (!range
|| change
->end
< range
->start
)
1000 /* No overlap, nor adjoin, copy change to result range */
1001 svn_merge_range_t
*chg_copy
= svn_merge_range_dup(change
,
1003 svn_sort__array_insert(rangelist
, &chg_copy
, i
++);
1006 else if ((change
->start
> range
->end
)
1007 || (change
->start
== range
->end
1008 && change
->inheritable
!= range
->inheritable
))
1010 /* No overlap, nor adjoin. Check next range item against change */
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
)))
1021 /* Can't fold change into existing range.
1022 Insert new range before range */
1024 svn_merge_range_t
*chg_copy
= svn_merge_range_dup(change
,
1027 chg_copy
->start
= MIN(change
->start
, range
->start
);
1028 if (! change
->inheritable
)
1029 chg_copy
->end
= range
->start
;
1031 range
->start
= change
->end
;
1033 svn_sort__array_insert(rangelist
, &chg_copy
, i
++);
1035 change
->start
= chg_copy
->end
;
1036 if (change
->start
>= change
->end
)
1037 continue; /* No overlap with range left */
1041 range
->start
= MIN(range
->start
, change
->start
);
1044 SVN_ERR_ASSERT(change
->start
>= range
->start
);
1046 res
= svn_sort_compare_ranges(&range
, &change
);
1050 /* Only when merging two non-inheritable ranges is the result also
1051 non-inheritable. In all other cases ensure an inheritable
1053 if (range
->inheritable
|| change
->inheritable
)
1054 range
->inheritable
= TRUE
;
1058 else if (res
< 0) /* CHANGE is younger than RANGE */
1060 if (range
->end
== change
->start
)
1062 /* RANGE and CHANGE adjoin */
1063 if (range
->inheritable
== change
->inheritable
)
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
);
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();
1081 /* RANGE and CHANGE overlap, but how? */
1082 if ((range
->inheritable
== change
->inheritable
)
1083 || range
->inheritable
)
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
)
1092 change
->start
= range
->end
;
1099 /* RANGE is non-inheritable and CHANGE is inheritable. */
1100 if (range
->start
< change
->start
)
1102 /* CHANGE absorbs intersection with RANGE and RANGE
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
++);
1114 /* CHANGE and RANGE share the same start rev, but
1115 RANGE is considered older because its end rev
1117 range
->inheritable
= TRUE
;
1118 change
->start
= range
->end
;
1125 else /* res > 0, CHANGE is older than RANGE */
1127 if (change
->end
== range
->start
)
1129 /* RANGE and CHANGE adjoin */
1130 if (range
->inheritable
== change
->inheritable
)
1132 /* RANGE and CHANGE have the same inheritability so we
1133 can simply combine the two in place. */
1134 range
->start
= change
->start
;
1139 /* RANGE and CHANGE have different inheritability so insert
1140 a copy of CHANGE into RANGELIST. */
1141 SVN_ERR_MALFUNCTION(); /* Already handled */
1146 /* RANGE and CHANGE overlap. */
1147 if (range
->inheritable
== change
->inheritable
)
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
)
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
);
1161 else if (range
->inheritable
)
1163 if (change
->start
< range
->start
)
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 */
1172 /* CHANGE and RANGE share the same start rev, but
1173 CHANGE is considered older because CHANGE->END is
1174 older than RANGE->END. */
1180 /* RANGE is non-inheritable and CHANGE is inheritable. */
1181 if (change
->start
< range
->start
)
1183 if (change
->end
== range
->end
)
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
;
1191 else if (change
->end
> range
->end
)
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
;
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
);
1219 /* CHANGE and RANGE share the same start rev, but
1220 CHANGE is considered older because its end rev
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
++);
1237 SVN_ERR_MALFUNCTION(); /* Unreachable */
1241 SVN_ERR_ASSERT(svn_rangelist__is_canonical(rangelist
));
1244 return SVN_NO_ERROR
;
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
)
1253 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first
));
1254 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second
));
1256 return (first
->start
+ 1 <= second
->end
)
1257 && (second
->start
+ 1 <= first
->end
)
1258 && (!consider_inheritance
1259 || (!(first
->inheritable
) == !(second
->inheritable
)));
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
)
1269 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(first
));
1270 SVN_ERR_ASSERT_NO_RETURN(IS_VALID_FORWARD_RANGE(second
));
1272 return (first
->start
<= second
->start
) && (second
->end
<= first
->end
)
1273 && (!consider_inheritance
1274 || (!(first
->inheritable
) == !(second
->inheritable
)));
1277 /* Swap start and end fields of RANGE. */
1279 range_swap_endpoints(svn_merge_range_t
*range
)
1281 svn_revnum_t swap
= range
->start
;
1282 range
->start
= range
->end
;
1287 svn_rangelist_reverse(svn_rangelist_t
*rangelist
, apr_pool_t
*pool
)
1291 svn_sort__array_reverse(rangelist
, pool
);
1293 for (i
= 0; i
< rangelist
->nelts
; i
++)
1295 range_swap_endpoints(APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*));
1298 return SVN_NO_ERROR
;
1302 svn_rangelist__set_inheritance(svn_rangelist_t
*rangelist
,
1303 svn_boolean_t inheritable
)
1308 svn_merge_range_t
*range
;
1310 for (i
= 0; i
< rangelist
->nelts
; i
++)
1312 range
= APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*);
1313 range
->inheritable
= inheritable
;
1320 svn_mergeinfo__set_inheritance(svn_mergeinfo_t mergeinfo
,
1321 svn_boolean_t inheritable
,
1322 apr_pool_t
*scratch_pool
)
1326 apr_hash_index_t
*hi
;
1328 for (hi
= apr_hash_first(scratch_pool
, mergeinfo
);
1330 hi
= apr_hash_next(hi
))
1332 svn_rangelist_t
*rangelist
= apr_hash_this_val(hi
);
1335 svn_rangelist__set_inheritance(rangelist
, inheritable
);
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
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.
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().
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.:
1358 RANGELIST1 RANGELIST2 CONSIDER DO_REMOVE *OUTPUT
1360 ---------- ------ ----------- --------- -------
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*
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*
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
,
1382 svn_merge_range_t working_elt2
;
1384 *output
= apr_array_make(pool
, 1, sizeof(svn_merge_range_t
*));
1388 lasti2
= -1; /* Initialized to a value that "i2" will never be. */
1390 while (i1
< rangelist1
->nelts
&& i2
< rangelist2
->nelts
)
1392 svn_merge_range_t
*elt1
, *elt2
;
1394 elt1
= APR_ARRAY_IDX(rangelist1
, i1
, svn_merge_range_t
*);
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. */
1402 *(APR_ARRAY_IDX(rangelist2
, i2
, svn_merge_range_t
*));
1406 elt2
= &working_elt2
;
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
))
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
,
1434 if (elt2
->start
== elt1
->start
&& elt2
->end
== elt1
->end
)
1437 else if (range_intersect(elt1
, elt2
, consider_inheritance
))
1439 if (elt2
->start
< elt1
->start
)
1441 /* The rangelist2 range starts before the rangelist1 range. */
1442 svn_merge_range_t tmp_range
;
1445 /* Retain the range that falls before the rangelist1
1447 tmp_range
.start
= elt2
->start
;
1448 tmp_range
.end
= elt1
->start
;
1449 tmp_range
.inheritable
= elt2
->inheritable
;
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
);
1463 SVN_ERR(combine_with_lastrange(&tmp_range
,
1464 *output
, consider_inheritance
,
1468 /* Set up the rest of the rangelist2 range for further
1470 if (elt2
->end
> elt1
->end
)
1472 /* The rangelist2 range ends after the rangelist1 range. */
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
,
1485 consider_inheritance
,
1489 working_elt2
.start
= elt1
->end
;
1490 working_elt2
.end
= elt2
->end
;
1495 else /* ranges don't intersect */
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.
1500 If it is on past the rangelist2 on the right side, we
1501 need to output the rangelist2 and increment the
1503 if (svn_sort_compare_ranges(&elt1
, &elt2
) < 0)
1507 svn_merge_range_t
*lastrange
;
1509 if ((*output
)->nelts
> 0)
1510 lastrange
= APR_ARRAY_IDX(*output
, (*output
)->nelts
- 1,
1511 svn_merge_range_t
*);
1515 if (do_remove
&& !(lastrange
&&
1516 combine_ranges(lastrange
, lastrange
, elt2
,
1517 consider_inheritance
)))
1519 lastrange
= svn_merge_range_dup(elt2
, pool
);
1520 APR_ARRAY_PUSH(*output
, svn_merge_range_t
*) = lastrange
;
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
)
1537 SVN_ERR(combine_with_lastrange(&working_elt2
, *output
,
1538 consider_inheritance
, pool
));
1542 /* Copy any other remaining untouched rangelist2 elements. */
1543 for (; i2
< rangelist2
->nelts
; i2
++)
1545 svn_merge_range_t
*elt
= APR_ARRAY_IDX(rangelist2
, i2
,
1546 svn_merge_range_t
*);
1548 SVN_ERR(combine_with_lastrange(elt
, *output
,
1549 consider_inheritance
, pool
));
1553 return SVN_NO_ERROR
;
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
,
1564 return rangelist_intersect_or_remove(output
, rangelist1
, rangelist2
, FALSE
,
1565 consider_inheritance
, pool
);
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
,
1575 return rangelist_intersect_or_remove(output
, eraser
, whiteboard
, TRUE
,
1576 consider_inheritance
, pool
);
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
,
1585 /* The following diagrams illustrate some common range delta scenarios:
1588 r0 <===========(=========)============[=========]===========> rHEAD
1591 (from) deleted deleted
1592 r0 <===========(=========[============]=========)===========> rHEAD
1596 r0 <===========(=========[============)=========]===========> rHEAD
1600 r0 <===========[=========(============]=========)===========> rHEAD
1604 r0 <===========[=========(============)=========]===========> rHEAD
1608 r0 <===(=[=)=]=[==]=[=(=)=]=[=]=[=(===|===(=)==|=|==[=(=]=)=> rHEAD
1612 /* The items that are present in from, but not in to, must have been
1614 SVN_ERR(svn_rangelist_remove(deleted
, to
, from
, consider_inheritance
,
1616 /* The items that are present in to, but not in from, must have been
1618 return svn_rangelist_remove(added
, from
, to
, consider_inheritance
, pool
);
1621 struct mergeinfo_diff_baton
1623 svn_mergeinfo_t from
;
1625 svn_mergeinfo_t deleted
;
1626 svn_mergeinfo_t added
;
1627 svn_boolean_t consider_inheritance
;
1631 /* This implements the 'svn_hash_diff_func_t' interface.
1632 BATON is of type 'struct mergeinfo_diff_baton *'.
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
,
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
)
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
);
1660 else if ((status
== svn_hash_diff_key_a
) && cb
->deleted
)
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
));
1666 else if ((status
== svn_hash_diff_key_b
) && cb
->added
)
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
));
1672 return SVN_NO_ERROR
;
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
)
1685 struct mergeinfo_diff_baton mdb
;
1688 mdb
.deleted
= deleted
;
1690 mdb
.consider_inheritance
= consider_inheritance
;
1691 mdb
.pool
= result_pool
;
1693 return svn_hash_diff(from
, to
, mergeinfo_hash_diff_cb
, &mdb
, scratch_pool
);
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
)
1703 if (from
&& to
== NULL
)
1705 *deleted
= svn_mergeinfo_dup(from
, result_pool
);
1706 *added
= svn_hash__make(result_pool
);
1708 else if (from
== NULL
&& to
)
1710 *deleted
= svn_hash__make(result_pool
);
1711 *added
= svn_mergeinfo_dup(to
, result_pool
);
1715 *deleted
= svn_hash__make(result_pool
);
1716 *added
= svn_hash__make(result_pool
);
1720 SVN_ERR(walk_mergeinfo_hash_for_diff(from
, to
, *deleted
, *added
,
1721 consider_inheritance
,
1722 result_pool
, scratch_pool
));
1726 return SVN_NO_ERROR
;
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
,
1736 apr_hash_index_t
*hi
;
1740 /* special cases: at least one side has no merge info */
1741 if (info1
== NULL
&& info2
== NULL
)
1744 return SVN_NO_ERROR
;
1747 if (info1
== NULL
|| info2
== NULL
)
1748 return SVN_NO_ERROR
;
1750 /* trivial case: different number of paths -> unequal */
1751 if (apr_hash_count(info1
) != apr_hash_count(info2
))
1752 return SVN_NO_ERROR
;
1754 /* compare range lists for all paths */
1755 for (hi
= apr_hash_first(pool
, info1
); hi
; hi
= apr_hash_next(hi
))
1758 apr_ssize_t key_length
;
1759 svn_rangelist_t
*lhs
, *rhs
;
1761 svn_rangelist_t
*deleted
, *added
;
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
);
1767 /* missing on one side? */
1769 return SVN_NO_ERROR
;
1771 /* quick compare: the range lists will often be a perfect match */
1772 if (lhs
->nelts
== rhs
->nelts
)
1774 for (i
= 0; i
< lhs
->nelts
; ++i
)
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
*);
1781 /* range mismatch? -> needs detailed comparison */
1782 if ( lrange
->start
!= rrange
->start
1783 || lrange
->end
!= rrange
->end
)
1786 /* inheritance mismatch? -> merge info differs */
1787 if ( consider_inheritance
1788 && lrange
->inheritable
!= rrange
->inheritable
)
1789 return SVN_NO_ERROR
;
1792 /* all ranges found to match -> next path */
1793 if (i
== lhs
->nelts
)
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
;
1805 /* no mismatch found */
1807 return SVN_NO_ERROR
;
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
)
1816 apr_hash_index_t
*hi
;
1817 apr_pool_t
*iterpool
;
1819 if (!apr_hash_count(changes
))
1820 return SVN_NO_ERROR
;
1822 iterpool
= svn_pool_create(scratch_pool
);
1823 for (hi
= apr_hash_first(scratch_pool
, changes
); hi
; hi
= apr_hash_next(hi
))
1827 svn_rangelist_t
*to_insert
;
1828 svn_rangelist_t
*target
;
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
);
1834 /* if range list exists, just expand on it.
1835 * Otherwise, add new hash entry. */
1838 SVN_ERR(svn_rangelist_merge2(target
, to_insert
, result_pool
,
1840 svn_pool_clear(iterpool
);
1843 apr_hash_set(mergeinfo
, key
, klen
, to_insert
);
1846 svn_pool_destroy(iterpool
);
1848 return SVN_NO_ERROR
;
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
)
1859 apr_array_header_t
*sorted_cat
=
1860 svn_sort__hash(mergeinfo_cat
, svn_sort_compare_items_as_paths
,
1862 apr_array_header_t
*sorted_changes
=
1863 svn_sort__hash(changes_cat
, svn_sort_compare_items_as_paths
,
1866 while (i
< sorted_cat
->nelts
&& j
< sorted_changes
->nelts
)
1868 svn_sort__item_t cat_elt
, change_elt
;
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
);
1875 if (res
== 0) /* Both catalogs have mergeinfo for a given path. */
1877 svn_mergeinfo_t mergeinfo
= cat_elt
.value
;
1878 svn_mergeinfo_t changes_mergeinfo
= change_elt
.value
;
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
);
1886 else if (res
< 0) /* Only MERGEINFO_CAT has mergeinfo for this path. */
1890 else /* Only CHANGES_CAT has mergeinfo for this path. */
1892 apr_hash_set(mergeinfo_cat
,
1893 apr_pstrdup(result_pool
, change_elt
.key
),
1895 svn_mergeinfo_dup(change_elt
.value
, result_pool
));
1900 /* Copy back any remaining elements from the CHANGES_CAT catalog. */
1901 for (; j
< sorted_changes
->nelts
; j
++)
1903 svn_sort__item_t elt
= APR_ARRAY_IDX(sorted_changes
, j
,
1905 apr_hash_set(mergeinfo_cat
,
1906 apr_pstrdup(result_pool
, elt
.key
),
1908 svn_mergeinfo_dup(elt
.value
, result_pool
));
1911 return SVN_NO_ERROR
;
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
)
1922 apr_hash_index_t
*hi
;
1923 apr_pool_t
*iterpool
;
1925 *mergeinfo
= apr_hash_make(result_pool
);
1926 iterpool
= svn_pool_create(scratch_pool
);
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
))
1936 const char *path
= apr_hash_this_key(hi
);
1937 svn_rangelist_t
*rangelist1
= apr_hash_this_val(hi
);
1938 svn_rangelist_t
*rangelist2
;
1940 svn_pool_clear(iterpool
);
1941 rangelist2
= svn_hash_gets(mergeinfo2
, path
);
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
));
1951 svn_pool_destroy(iterpool
);
1952 return SVN_NO_ERROR
;
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
)
1963 *mergeinfo
= apr_hash_make(result_pool
);
1964 return walk_mergeinfo_hash_for_diff(whiteboard
, eraser
, *mergeinfo
, NULL
,
1965 consider_inheritance
, result_pool
,
1970 svn_rangelist_to_string(svn_string_t
**output
,
1971 const svn_rangelist_t
*rangelist
,
1974 svn_stringbuf_t
*buf
= svn_stringbuf_create_empty(pool
);
1976 if (rangelist
->nelts
> 0)
1979 svn_merge_range_t
*range
;
1981 /* Handle the elements that need commas at the end. */
1982 for (i
= 0; i
< rangelist
->nelts
- 1; i
++)
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
, ",");
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
));
1994 *output
= svn_stringbuf__morph_into_string(buf
);
1996 return SVN_NO_ERROR
;
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
2005 static svn_error_t
*
2006 mergeinfo_to_stringbuf(svn_stringbuf_t
**output
,
2007 svn_mergeinfo_t input
,
2011 *output
= svn_stringbuf_create_empty(pool
);
2013 if (apr_hash_count(input
) > 0)
2015 apr_array_header_t
*sorted
=
2016 svn_sort__hash(input
, svn_sort_compare_items_as_paths
, pool
);
2019 for (i
= 0; i
< sorted
->nelts
; i
++)
2021 svn_sort__item_t elt
= APR_ARRAY_IDX(sorted
, i
, svn_sort__item_t
);
2022 svn_string_t
*revlist
;
2024 SVN_ERR(svn_rangelist_to_string(&revlist
, elt
.value
, pool
));
2025 svn_stringbuf_appendcstr(
2027 apr_psprintf(pool
, "%s%s%s:%s",
2028 prefix
? prefix
: "",
2029 *((const char *) elt
.key
) == '/' ? "" : "/",
2030 (const char *) elt
.key
,
2032 if (i
< sorted
->nelts
- 1)
2033 svn_stringbuf_appendcstr(*output
, "\n");
2037 return SVN_NO_ERROR
;
2041 svn_mergeinfo_to_string(svn_string_t
**output
, svn_mergeinfo_t input
,
2044 svn_stringbuf_t
*mergeinfo_buf
;
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
;
2052 svn_mergeinfo_sort(svn_mergeinfo_t input
, apr_pool_t
*pool
)
2054 apr_hash_index_t
*hi
;
2056 for (hi
= apr_hash_first(pool
, input
); hi
; hi
= apr_hash_next(hi
))
2058 apr_array_header_t
*rl
= apr_hash_this_val(hi
);
2060 svn_sort__array(rl
, svn_sort_compare_ranges
);
2062 return SVN_NO_ERROR
;
2066 svn_mergeinfo__canonicalize_ranges(svn_mergeinfo_t mergeinfo
,
2067 apr_pool_t
*scratch_pool
)
2069 apr_hash_index_t
*hi
;
2071 for (hi
= apr_hash_first(scratch_pool
, mergeinfo
); hi
; hi
= apr_hash_next(hi
))
2073 apr_array_header_t
*rl
= apr_hash_this_val(hi
);
2075 SVN_ERR(svn_rangelist__canonicalize(rl
, scratch_pool
));
2078 return SVN_NO_ERROR
;
2081 svn_mergeinfo_catalog_t
2082 svn_mergeinfo_catalog_dup(svn_mergeinfo_catalog_t mergeinfo_catalog
,
2085 svn_mergeinfo_t new_mergeinfo_catalog
= apr_hash_make(pool
);
2086 apr_hash_index_t
*hi
;
2088 for (hi
= apr_hash_first(pool
, mergeinfo_catalog
);
2090 hi
= apr_hash_next(hi
))
2092 const char *key
= apr_hash_this_key(hi
);
2093 svn_mergeinfo_t val
= apr_hash_this_val(hi
);
2095 svn_hash_sets(new_mergeinfo_catalog
, apr_pstrdup(pool
, key
),
2096 svn_mergeinfo_dup(val
, pool
));
2099 return new_mergeinfo_catalog
;
2103 svn_mergeinfo_dup(svn_mergeinfo_t mergeinfo
, apr_pool_t
*pool
)
2105 svn_mergeinfo_t new_mergeinfo
= svn_hash__make(pool
);
2106 apr_hash_index_t
*hi
;
2108 for (hi
= apr_hash_first(pool
, mergeinfo
); hi
; hi
= apr_hash_next(hi
))
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
);
2114 apr_hash_set(new_mergeinfo
, apr_pstrmemdup(pool
, path
, pathlen
), pathlen
,
2115 svn_rangelist_dup(rangelist
, pool
));
2118 return new_mergeinfo
;
2122 svn_mergeinfo_inheritable2(svn_mergeinfo_t
*output
,
2123 svn_mergeinfo_t mergeinfo
,
2127 svn_boolean_t inheritable
,
2128 apr_pool_t
*result_pool
,
2129 apr_pool_t
*scratch_pool
)
2131 apr_hash_index_t
*hi
;
2132 svn_mergeinfo_t inheritable_mergeinfo
= apr_hash_make(result_pool
);
2134 for (hi
= apr_hash_first(scratch_pool
, mergeinfo
);
2136 hi
= apr_hash_next(hi
))
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
;
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
));
2148 inheritable_rangelist
= svn_rangelist_dup(rangelist
, result_pool
);
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
);
2157 *output
= inheritable_mergeinfo
;
2158 return SVN_NO_ERROR
;
2163 svn_rangelist_inheritable2(svn_rangelist_t
**inheritable_rangelist
,
2164 const svn_rangelist_t
*rangelist
,
2167 svn_boolean_t inheritable
,
2168 apr_pool_t
*result_pool
,
2169 apr_pool_t
*scratch_pool
)
2171 *inheritable_rangelist
= apr_array_make(result_pool
, 1,
2172 sizeof(svn_merge_range_t
*));
2173 if (rangelist
->nelts
)
2175 if (!SVN_IS_VALID_REVNUM(start
)
2176 || !SVN_IS_VALID_REVNUM(end
)
2179 /* We want all (non-inheritable or inheritable) ranges removed. */
2182 for (i
= 0; i
< rangelist
->nelts
; i
++)
2184 svn_merge_range_t
*range
= APR_ARRAY_IDX(rangelist
, i
,
2185 svn_merge_range_t
*);
2186 if (range
->inheritable
== inheritable
)
2188 APR_ARRAY_PUSH(*inheritable_rangelist
, svn_merge_range_t
*)
2189 = svn_merge_range_dup(range
, result_pool
);
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
);
2200 if (rangelist
->nelts
)
2201 SVN_ERR(svn_rangelist_remove(inheritable_rangelist
,
2208 return SVN_NO_ERROR
;
2212 svn_mergeinfo__remove_empty_rangelists(svn_mergeinfo_t mergeinfo
,
2213 apr_pool_t
*scratch_pool
)
2215 apr_hash_index_t
*hi
;
2216 svn_boolean_t removed_some_ranges
= FALSE
;
2220 for (hi
= apr_hash_first(scratch_pool
, mergeinfo
); hi
;
2221 hi
= apr_hash_next(hi
))
2223 const char *path
= apr_hash_this_key(hi
);
2224 svn_rangelist_t
*rangelist
= apr_hash_this_val(hi
);
2226 if (rangelist
->nelts
== 0)
2228 svn_hash_sets(mergeinfo
, path
, NULL
);
2229 removed_some_ranges
= TRUE
;
2233 return removed_some_ranges
;
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
,
2242 apr_hash_index_t
*hi
;
2244 SVN_ERR_ASSERT(prefix_path
[0] == '/');
2246 *out_catalog
= apr_hash_make(pool
);
2248 for (hi
= apr_hash_first(pool
, in_catalog
); hi
; hi
= apr_hash_next(hi
))
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
;
2254 new_path
= svn_fspath__skip_ancestor(prefix_path
, original_path
);
2255 SVN_ERR_ASSERT(new_path
);
2257 svn_hash_sets(*out_catalog
, new_path
, value
);
2260 return SVN_NO_ERROR
;
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
)
2270 apr_hash_index_t
*hi
;
2272 *out_catalog
= apr_hash_make(result_pool
);
2274 for (hi
= apr_hash_first(scratch_pool
, in_catalog
);
2276 hi
= apr_hash_next(hi
))
2278 const char *original_path
= apr_hash_this_key(hi
);
2279 svn_mergeinfo_t value
= apr_hash_this_val(hi
);
2281 if (original_path
[0] == '/')
2284 svn_hash_sets(*out_catalog
,
2285 svn_dirent_join(prefix_path
, original_path
, result_pool
),
2289 return SVN_NO_ERROR
;
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
)
2299 apr_hash_index_t
*hi
;
2301 SVN_ERR_ASSERT(suffix_relpath
&& svn_relpath_is_canonical(suffix_relpath
));
2303 *out_mergeinfo
= apr_hash_make(result_pool
);
2305 for (hi
= apr_hash_first(scratch_pool
, mergeinfo
);
2307 hi
= apr_hash_next(hi
))
2309 const char *fspath
= apr_hash_this_key(hi
);
2310 svn_rangelist_t
*rangelist
= apr_hash_this_val(hi
);
2312 svn_hash_sets(*out_mergeinfo
,
2313 svn_fspath__join(fspath
, suffix_relpath
, result_pool
),
2317 return SVN_NO_ERROR
;
2320 /* Deep-copy an array of pointers to simple objects.
2322 * Return a duplicate in POOL of the array ARRAY of pointers to objects
2323 * of size OBJECT_SIZE bytes. Duplicate each object bytewise.
2325 static apr_array_header_t
*
2326 ptr_array_dup(const apr_array_header_t
*array
,
2330 apr_array_header_t
*new_array
= apr_array_make(pool
, array
->nelts
,
2333 /* allocate target range buffer with a single operation */
2334 char *copy
= apr_palloc(pool
, object_size
* array
->nelts
);
2336 /* for efficiency, directly address source and target reference buffers */
2337 void **source
= (void **)(array
->elts
);
2338 void **target
= (void **)(new_array
->elts
);
2341 /* copy ranges iteratively and link them into the target range list */
2342 for (i
= 0; i
< array
->nelts
; i
++)
2344 target
[i
] = ©
[i
* object_size
];
2345 memcpy(target
[i
], source
[i
], object_size
);
2347 new_array
->nelts
= array
->nelts
;
2353 svn_rangelist_dup(const svn_rangelist_t
*rangelist
, apr_pool_t
*pool
)
2355 return ptr_array_dup(rangelist
, sizeof(svn_merge_range_t
), pool
);
2359 svn_merge_range_dup(const svn_merge_range_t
*range
, apr_pool_t
*pool
)
2361 svn_merge_range_t
*new_range
= apr_pmemdup(pool
, range
, sizeof(*new_range
));
2366 svn_merge_range_contains_rev(const svn_merge_range_t
*range
, svn_revnum_t rev
)
2368 assert(SVN_IS_VALID_REVNUM(range
->start
));
2369 assert(SVN_IS_VALID_REVNUM(range
->end
));
2370 assert(range
->start
!= range
->end
);
2372 if (range
->start
< range
->end
)
2373 return rev
> range
->start
&& rev
<= range
->end
;
2375 return rev
> range
->end
&& rev
<= range
->start
;
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
,
2385 svn_stringbuf_t
*output_buf
= NULL
;
2387 if (catalog
&& apr_hash_count(catalog
))
2390 apr_array_header_t
*sorted_catalog
=
2391 svn_sort__hash(catalog
, svn_sort_compare_items_as_paths
, pool
);
2393 output_buf
= svn_stringbuf_create_empty(pool
);
2394 for (i
= 0; i
< sorted_catalog
->nelts
; i
++)
2396 svn_sort__item_t elt
=
2397 APR_ARRAY_IDX(sorted_catalog
, i
, svn_sort__item_t
);
2399 svn_mergeinfo_t mergeinfo
;
2400 svn_stringbuf_t
*mergeinfo_output_buf
;
2403 mergeinfo
= elt
.value
;
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");
2417 output_buf
= svn_stringbuf_create(key_prefix
? key_prefix
: "", pool
);
2418 svn_stringbuf_appendcstr(output_buf
, _("NULL mergeinfo catalog\n"));
2420 else if (apr_hash_count(catalog
) == 0)
2422 output_buf
= svn_stringbuf_create(key_prefix
? key_prefix
: "", pool
);
2423 svn_stringbuf_appendcstr(output_buf
, _("empty mergeinfo catalog\n"));
2427 /* If we have an output_buf, convert it to an svn_string_t;
2428 otherwise, return a new string containing only a newline
2431 *output
= svn_stringbuf__morph_into_string(output_buf
);
2433 *output
= svn_string_create("\n", pool
);
2435 return SVN_NO_ERROR
;
2439 svn_mergeinfo__get_range_endpoints(svn_revnum_t
*youngest_rev
,
2440 svn_revnum_t
*oldest_rev
,
2441 svn_mergeinfo_t mergeinfo
,
2444 *youngest_rev
= *oldest_rev
= SVN_INVALID_REVNUM
;
2447 apr_hash_index_t
*hi
;
2449 for (hi
= apr_hash_first(pool
, mergeinfo
); hi
; hi
= apr_hash_next(hi
))
2451 svn_rangelist_t
*rangelist
= apr_hash_this_val(hi
);
2453 if (rangelist
->nelts
)
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
;
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
;
2469 return SVN_NO_ERROR
;
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
)
2481 apr_hash_index_t
*hi
;
2483 *filtered_cat
= apr_hash_make(result_pool
);
2484 for (hi
= apr_hash_first(scratch_pool
, catalog
);
2486 hi
= apr_hash_next(hi
))
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
;
2492 SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(&filtered_mergeinfo
,
2499 if (apr_hash_count(filtered_mergeinfo
))
2500 svn_hash_sets(*filtered_cat
,
2501 apr_pstrdup(result_pool
, path
), filtered_mergeinfo
);
2504 return SVN_NO_ERROR
;
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
)
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
);
2520 *filtered_mergeinfo
= apr_hash_make(result_pool
);
2524 apr_hash_index_t
*hi
;
2525 svn_rangelist_t
*filter_rangelist
=
2526 svn_rangelist__initialize(oldest_rev
, youngest_rev
, TRUE
,
2529 for (hi
= apr_hash_first(scratch_pool
, mergeinfo
);
2531 hi
= apr_hash_next(hi
))
2533 const char *path
= apr_hash_this_key(hi
);
2534 svn_rangelist_t
*rangelist
= apr_hash_this_val(hi
);
2536 if (rangelist
->nelts
)
2538 svn_rangelist_t
*new_rangelist
;
2540 SVN_ERR(rangelist_intersect_or_remove(
2541 &new_rangelist
, filter_rangelist
, rangelist
,
2542 ! include_range
, FALSE
, result_pool
));
2544 if (new_rangelist
->nelts
)
2545 svn_hash_sets(*filtered_mergeinfo
,
2546 apr_pstrdup(result_pool
, path
), new_rangelist
);
2550 return SVN_NO_ERROR
;
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
)
2560 apr_hash_index_t
*hi
;
2561 *adjusted_mergeinfo
= apr_hash_make(result_pool
);
2565 for (hi
= apr_hash_first(scratch_pool
, mergeinfo
);
2567 hi
= apr_hash_next(hi
))
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
*));
2576 for (i
= 0; i
< rangelist
->nelts
; i
++)
2578 svn_merge_range_t
*range
=
2579 APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*);
2581 if (range
->start
+ offset
> 0 && range
->end
+ offset
> 0)
2583 range
->start
= range
->start
+ offset
;
2584 range
->end
= range
->end
+ offset
;
2585 APR_ARRAY_PUSH(adjusted_rangelist
, svn_merge_range_t
*) =
2590 if (adjusted_rangelist
->nelts
)
2591 svn_hash_sets(*adjusted_mergeinfo
, apr_pstrdup(result_pool
, path
),
2592 adjusted_rangelist
);
2595 return SVN_NO_ERROR
;
2599 svn_mergeinfo__is_noninheritable(svn_mergeinfo_t mergeinfo
,
2600 apr_pool_t
*scratch_pool
)
2604 apr_hash_index_t
*hi
;
2606 for (hi
= apr_hash_first(scratch_pool
, mergeinfo
);
2608 hi
= apr_hash_next(hi
))
2610 svn_rangelist_t
*rangelist
= apr_hash_this_val(hi
);
2613 for (i
= 0; i
< rangelist
->nelts
; i
++)
2615 svn_merge_range_t
*range
= APR_ARRAY_IDX(rangelist
, i
,
2616 svn_merge_range_t
*);
2617 if (!range
->inheritable
)
2626 svn_rangelist__initialize(svn_revnum_t start
,
2628 svn_boolean_t inheritable
,
2629 apr_pool_t
*result_pool
)
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
));
2635 range
->start
= start
;
2637 range
->inheritable
= inheritable
;
2638 APR_ARRAY_PUSH(rangelist
, svn_merge_range_t
*) = range
;
2643 svn_mergeinfo__mergeinfo_from_segments(svn_mergeinfo_t
*mergeinfo_p
,
2644 const apr_array_header_t
*segments
,
2647 svn_mergeinfo_t mergeinfo
= apr_hash_make(pool
);
2650 /* Translate location segments into merge sources and ranges. */
2651 for (i
= 0; i
< segments
->nelts
; i
++)
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
;
2659 /* No path segment? Skip it. */
2660 if (! segment
->path
)
2663 /* Prepend a leading slash to our path. */
2664 source_path
= apr_pstrcat(pool
, "/", segment
->path
, SVN_VA_NULL
);
2666 /* See if we already stored ranges for this path. If not, make
2668 path_ranges
= svn_hash_gets(mergeinfo
, source_path
);
2670 path_ranges
= apr_array_make(pool
, 1, sizeof(range
));
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)
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
);
2688 *mergeinfo_p
= mergeinfo
;
2689 return SVN_NO_ERROR
;
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
)
2698 if (apr_hash_count(merge_history
))
2700 apr_pool_t
*iterpool
= svn_pool_create(scratch_pool
);
2701 apr_hash_index_t
*hi
;
2703 for (hi
= apr_hash_first(scratch_pool
, merge_history
);
2705 hi
= apr_hash_next(hi
))
2707 svn_rangelist_t
*subtree_rangelist
= apr_hash_this_val(hi
);
2709 svn_pool_clear(iterpool
);
2710 SVN_ERR(svn_rangelist_merge2(merged_rangelist
, subtree_rangelist
,
2711 result_pool
, iterpool
));
2713 svn_pool_destroy(iterpool
);
2715 return SVN_NO_ERROR
;
2720 svn_inheritance_to_word(svn_mergeinfo_inheritance_t inherit
)
2724 case svn_mergeinfo_inherited
:
2726 case svn_mergeinfo_nearest_ancestor
:
2727 return "nearest-ancestor";
2733 svn_mergeinfo_inheritance_t
2734 svn_inheritance_from_word(const char *word
)
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
;