2 * mergeinfo.c: Mergeinfo parsing and handling
4 * ====================================================================
5 * Copyright (c) 2006-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
22 #include "svn_types.h"
23 #include "svn_ctype.h"
24 #include "svn_pools.h"
25 #include "svn_sorts.h"
26 #include "svn_error.h"
27 #include "svn_error_codes.h"
28 #include "svn_string.h"
29 #include "svn_mergeinfo.h"
30 #include "private/svn_mergeinfo_private.h"
31 #include "svn_private_config.h"
33 /* Attempt to combine two adjacent or overlapping ranges, IN1 and IN2, and put
34 the result in OUTPUT. Return whether they could be combined.
36 CONSIDER_INHERITANCE determines how to account for the inheritability
37 of IN1 and IN2 when trying to combine ranges. If ranges with different
38 inheritability are combined (CONSIDER_INHERITANCE must be FALSE for this
39 to happen) the result is inheritable. If both ranges are inheritable the
40 result is inheritable. Only and if both ranges are non-inheritable is
41 the result is non-inheritable.
43 Range overlapping detection algorithm from
44 http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap
47 combine_ranges(svn_merge_range_t
**output
, svn_merge_range_t
*in1
,
48 svn_merge_range_t
*in2
,
49 svn_boolean_t consider_inheritance
)
51 if (in1
->start
<= in2
->end
&& in2
->start
<= in1
->end
)
53 if (!consider_inheritance
54 || (consider_inheritance
55 && ((in1
->inheritable
? TRUE
: FALSE
)
56 == (in2
->inheritable
? TRUE
: FALSE
))))
58 (*output
)->start
= MIN(in1
->start
, in2
->start
);
59 (*output
)->end
= MAX(in1
->end
, in2
->end
);
60 (*output
)->inheritable
=
61 (in1
->inheritable
|| in2
->inheritable
) ? TRUE
: FALSE
;
68 /* pathname -> PATHNAME */
70 parse_pathname(const char **input
, const char *end
,
71 svn_stringbuf_t
**pathname
, apr_pool_t
*pool
)
73 const char *curr
= *input
;
74 *pathname
= svn_stringbuf_create("", pool
);
76 while (curr
< end
&& *curr
!= ':')
78 svn_stringbuf_appendbytes(*pathname
, curr
, 1);
82 if ((*pathname
)->len
== 0)
83 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
84 _("No pathname preceeding ':'"));
90 /* Helper for svn_rangelist_merge() and rangelist_intersect_or_remove().
92 If *LASTRANGE is not NULL it should point to the last element in REVLIST.
93 REVLIST must be sorted from lowest to highest revision and contain no
94 overlapping revision ranges. Any changes made to REVLIST will maintain
97 If *LASTRANGE is NULL then push MRANGE to REVLIST.
99 If *LASTRANGE and MRANGE don't intersect then push MRANGE to REVLIST.
100 If they do intersect and have the same inheritability then combine the
101 ranges, updating *LASTRANGE to reflect the new combined range. If the
102 ranges intersect but differ in inheritability, then merge the ranges - see
103 the doc string for svn_mergeinfo_merge. This may result in a change to
104 *LASTRANGE's end field and the pushing of up to two new ranges on REVLIST.
106 e.g. *LASTRANGE: '4-10*' merged with MRANGE: '6'________
108 Update end field Push Account for trimmed
109 | | range from *LASTRANGE.
111 | | maintain sort order.
114 *LASTRANGE: '4-5*' MRANGE: '6' NEWRANGE: '6-10*'
116 Upon return, if any new ranges were pushed onto REVLIST, then set
117 *LASTRANGE to the last range pushed.
119 CONSIDER_INHERITANCE determines how to account for the inheritability of
120 MRANGE and *LASTRANGE when determining if they intersect. If
121 CONSIDER_INHERITANCE is TRUE, then only ranges with the same
122 inheritability can intersect and therefore be combined.
124 If DUP_MRANGE is TRUE then allocate a copy of MRANGE before pushing it
127 static APR_INLINE
void
128 combine_with_lastrange(svn_merge_range_t
** lastrange
,
129 svn_merge_range_t
*mrange
, svn_boolean_t dup_mrange
,
130 apr_array_header_t
*revlist
,
131 svn_boolean_t consider_inheritance
,
134 svn_merge_range_t
*pushed_mrange_1
= NULL
;
135 svn_merge_range_t
*pushed_mrange_2
= NULL
;
136 svn_boolean_t ranges_intersect
= FALSE
;
137 svn_boolean_t ranges_have_same_inheritance
= FALSE
;
141 if ((*lastrange
)->start
<= mrange
->end
142 && mrange
->start
<= (*lastrange
)->end
)
143 ranges_intersect
= TRUE
;
144 if ((*lastrange
)->inheritable
== mrange
->inheritable
)
145 ranges_have_same_inheritance
= TRUE
;
149 || (!ranges_intersect
|| (!ranges_have_same_inheritance
150 && consider_inheritance
)))
155 LASTRANGE and MRANGE don't intersect
157 LASTRANGE and MRANGE "intersect" but have different
158 inheritability and we are considering inheritance so
159 can't combined them...
161 ...In all these cases just push MRANGE onto *LASTRANGE. */
163 pushed_mrange_1
= svn_merge_range_dup(mrange
, pool
);
165 pushed_mrange_1
= mrange
;
167 else /* MRANGE and *LASTRANGE intersect */
169 if (ranges_have_same_inheritance
)
171 /* Intersecting ranges have the same inheritability
172 so just combine them. */
173 (*lastrange
)->start
= MIN((*lastrange
)->start
, mrange
->start
);
174 (*lastrange
)->end
= MAX((*lastrange
)->end
, mrange
->end
);
175 (*lastrange
)->inheritable
=
176 ((*lastrange
)->inheritable
|| mrange
->inheritable
) ? TRUE
: FALSE
;
178 else /* Ranges intersect but have different
179 inheritability so merge the ranges. */
181 svn_revnum_t tmp_revnum
;
183 /* Ranges have same starting revision. */
184 if ((*lastrange
)->start
== mrange
->start
)
186 if ((*lastrange
)->end
== mrange
->end
)
188 (*lastrange
)->inheritable
= TRUE
;
190 else if ((*lastrange
)->end
> mrange
->end
)
192 if (!(*lastrange
)->inheritable
)
194 tmp_revnum
= (*lastrange
)->end
;
195 (*lastrange
)->end
= mrange
->end
;
196 (*lastrange
)->inheritable
= TRUE
;
198 pushed_mrange_1
= svn_merge_range_dup(mrange
, pool
);
200 pushed_mrange_1
= mrange
;
201 pushed_mrange_1
->start
= pushed_mrange_1
->start
;
202 pushed_mrange_1
->end
= tmp_revnum
;
203 *lastrange
= pushed_mrange_1
;
206 else /* (*lastrange)->end < mrange->end) */
208 if (mrange
->inheritable
)
210 (*lastrange
)->inheritable
= TRUE
;
211 (*lastrange
)->end
= mrange
->end
;
216 pushed_mrange_1
= svn_merge_range_dup(mrange
, pool
);
218 pushed_mrange_1
= mrange
;
219 pushed_mrange_1
->start
= (*lastrange
)->end
;
223 /* Ranges have same ending revision. (Same starting
224 and ending revisions already handled above.) */
225 else if ((*lastrange
)->end
== mrange
->end
)
227 if ((*lastrange
)->start
< mrange
->start
)
229 if (!(*lastrange
)->inheritable
)
231 (*lastrange
)->end
= mrange
->start
;
233 pushed_mrange_1
= svn_merge_range_dup(mrange
, pool
);
235 pushed_mrange_1
= mrange
;
236 *lastrange
= pushed_mrange_1
;
239 else /* (*lastrange)->start > mrange->start */
241 (*lastrange
)->start
= mrange
->start
;
242 (*lastrange
)->end
= mrange
->end
;
243 (*lastrange
)->inheritable
= mrange
->inheritable
;
245 pushed_mrange_1
= svn_merge_range_dup(mrange
, pool
);
247 pushed_mrange_1
= mrange
;
248 pushed_mrange_1
->start
= (*lastrange
)->end
;
249 pushed_mrange_1
->inheritable
= TRUE
;
253 else /* Ranges have different starting and ending revisions. */
255 if ((*lastrange
)->start
< mrange
->start
)
257 /* If MRANGE is a proper subset of *LASTRANGE and
258 *LASTRANGE is inheritable there is nothing more
260 if (!((*lastrange
)->end
> mrange
->end
261 && (*lastrange
)->inheritable
))
263 tmp_revnum
= (*lastrange
)->end
;
264 if (!(*lastrange
)->inheritable
)
265 (*lastrange
)->end
= mrange
->start
;
267 mrange
->start
= (*lastrange
)->end
;
269 pushed_mrange_1
= svn_merge_range_dup(mrange
, pool
);
271 pushed_mrange_1
= mrange
;
273 if (tmp_revnum
> mrange
->end
)
276 apr_palloc(pool
, sizeof(*pushed_mrange_2
));
277 pushed_mrange_2
->start
= mrange
->end
;
278 pushed_mrange_2
->end
= tmp_revnum
;
279 pushed_mrange_2
->inheritable
=
280 (*lastrange
)->inheritable
;
282 mrange
->inheritable
= TRUE
;
285 else /* ((*lastrange)->start > mrange->start) */
287 if ((*lastrange
)->end
< mrange
->end
)
289 pushed_mrange_2
->start
= (*lastrange
)->end
;
290 pushed_mrange_2
->end
= mrange
->end
;
291 pushed_mrange_2
->inheritable
= mrange
->inheritable
;
293 tmp_revnum
= (*lastrange
)->start
;
294 (*lastrange
)->start
= mrange
->start
;
295 (*lastrange
)->end
= tmp_revnum
;
296 (*lastrange
)->inheritable
= mrange
->inheritable
;
298 mrange
->start
= tmp_revnum
;
299 mrange
->end
= pushed_mrange_2
->start
;
300 mrange
->inheritable
= TRUE
;
302 else /* (*lastrange)->end > mrange->end */
304 pushed_mrange_2
->start
= mrange
->end
;
305 pushed_mrange_2
->end
= (*lastrange
)->end
;
306 pushed_mrange_2
->inheritable
=
307 (*lastrange
)->inheritable
;
309 tmp_revnum
= (*lastrange
)->start
;
310 (*lastrange
)->start
= mrange
->start
;
311 (*lastrange
)->end
= tmp_revnum
;
312 (*lastrange
)->inheritable
= mrange
->inheritable
;
314 mrange
->start
= tmp_revnum
;
315 mrange
->end
= pushed_mrange_2
->start
;
316 mrange
->inheritable
= TRUE
;
324 APR_ARRAY_PUSH(revlist
, svn_merge_range_t
*) = pushed_mrange_1
;
325 *lastrange
= pushed_mrange_1
;
329 APR_ARRAY_PUSH(revlist
, svn_merge_range_t
*) = pushed_mrange_2
;
330 *lastrange
= pushed_mrange_2
;
334 /* Convert a single svn_merge_range_t * back into an svn_stringbuf_t *. */
336 range_to_stringbuf(svn_stringbuf_t
**result
, svn_merge_range_t
*range
,
339 if (range
->start
== range
->end
- 1)
340 *result
= svn_stringbuf_createf(pool
, "%ld%s", range
->end
,
342 ? "" : SVN_MERGEINFO_NONINHERITABLE_STR
);
344 *result
= svn_stringbuf_createf(pool
, "%ld-%ld%s", range
->start
+ 1,
345 range
->end
, range
->inheritable
346 ? "" : SVN_MERGEINFO_NONINHERITABLE_STR
);
350 /* Helper for svn_mergeinfo_parse() via parse_revlist().
352 Similar to combine_with_lastrange() but enforces the some of the
353 restrictions noted in svn_mergeinfo_parse() on otherwise grammatically
354 correct rangelists, specifically the prohibitions on:
356 1) Overlapping revision ranges
358 2) Unordered revision ranges
360 Returns an SVN_ERR_MERGEINFO_PARSE_ERROR error if any of these rules
361 are violated. The restriction on revision ranges with a start revision
362 greater than or equal to its end revision is handled in parse_revlist().
364 Unlike combine_with_lastrange() this function *always* considers
365 inheritance, so only adjacent revision ranges with the same
366 inheritability are ever combined. */
368 combine_with_adjacent_lastrange(svn_merge_range_t
**lastrange
,
369 svn_merge_range_t
*mrange
,
370 svn_boolean_t dup_mrange
,
371 apr_array_header_t
*revlist
,
374 svn_merge_range_t
*pushed_mrange
= mrange
;
378 svn_stringbuf_t
*r1
, *r2
;
380 if ((*lastrange
)->start
<= mrange
->end
381 && mrange
->start
<= (*lastrange
)->end
)
383 /* The ranges intersect. */
384 SVN_ERR(range_to_stringbuf(&r1
, *lastrange
, pool
));
385 SVN_ERR(range_to_stringbuf(&r2
, mrange
, pool
));
387 /* svn_mergeinfo_parse promises to combine adjacent
388 ranges, but not overlapping ranges. */
389 if (mrange
->start
< (*lastrange
)->end
)
391 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
392 _("Parsing of overlapping revision "
393 "ranges '%s' and '%s' is not "
394 "supported"), r1
->data
, r2
->data
);
396 else if ((*lastrange
)->inheritable
== mrange
->inheritable
)
398 /* Combine adjacent ranges with the same inheritability. */
399 (*lastrange
)->end
= mrange
->end
;
403 else if ((*lastrange
)->start
> mrange
->start
)
405 SVN_ERR(range_to_stringbuf(&r1
, *lastrange
, pool
));
406 SVN_ERR(range_to_stringbuf(&r2
, mrange
, pool
));
407 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
408 _("Unable to parse unordered revision "
409 "ranges '%s' and '%s'"),
415 pushed_mrange
= svn_merge_range_dup(mrange
, pool
);
416 APR_ARRAY_PUSH(revlist
, svn_merge_range_t
*) = pushed_mrange
;
417 *lastrange
= pushed_mrange
;
421 /* Helper for svn_mergeinfo_parse()
423 revisionlist -> (revisionelement)(COMMA revisionelement)*
424 revisionrange -> REVISION "-" REVISION("*")
425 revisionelement -> revisionrange | REVISION("*")
427 PATHNAME is the path this revisionlist is mapped to. It is
428 used only for producing a more descriptive error message.
431 parse_revlist(const char **input
, const char *end
,
432 apr_array_header_t
*revlist
, const char *pathname
,
435 const char *curr
= *input
;
436 svn_merge_range_t
*lastrange
= NULL
;
438 /* Eat any leading horizontal white-space before the rangelist. */
439 while (curr
< end
&& *curr
!= '\n' && isspace(*curr
))
442 if (*curr
== '\n' || curr
== end
)
444 /* Empty range list. */
446 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
447 _("Mergeinfo for '%s' maps to an "
448 "empty revision range"), pathname
);
451 while (curr
< end
&& *curr
!= '\n')
453 /* Parse individual revisions or revision ranges. */
454 svn_merge_range_t
*mrange
= apr_pcalloc(pool
, sizeof(*mrange
));
455 svn_revnum_t firstrev
;
457 SVN_ERR(svn_revnum_parse(&firstrev
, curr
, &curr
));
458 if (*curr
!= '-' && *curr
!= '\n' && *curr
!= ',' && *curr
!= '*'
460 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
461 _("Invalid character '%c' found in revision "
463 mrange
->start
= firstrev
- 1;
464 mrange
->end
= firstrev
;
465 mrange
->inheritable
= TRUE
;
469 svn_revnum_t secondrev
;
472 SVN_ERR(svn_revnum_parse(&secondrev
, curr
, &curr
));
473 if (firstrev
> secondrev
)
474 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
475 _("Unable to parse reversed revision "
477 firstrev
, secondrev
);
478 else if (firstrev
== secondrev
)
479 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
480 _("Unable to parse revision range "
481 "'%ld-%ld' with same start and end "
482 "revisions"), firstrev
, secondrev
);
483 mrange
->end
= secondrev
;
486 if (*curr
== '\n' || curr
== end
)
488 SVN_ERR(combine_with_adjacent_lastrange(&lastrange
, mrange
, FALSE
,
493 else if (*curr
== ',')
495 SVN_ERR(combine_with_adjacent_lastrange(&lastrange
, mrange
, FALSE
,
499 else if (*curr
== '*')
501 mrange
->inheritable
= FALSE
;
503 if (*curr
== ',' || *curr
== '\n' || curr
== end
)
505 SVN_ERR(combine_with_adjacent_lastrange(&lastrange
, mrange
,
506 FALSE
, revlist
, pool
));
519 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
520 _("Invalid character '%c' found in "
521 "range list"), *curr
);
526 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
527 _("Invalid character '%c' found in "
528 "range list"), *curr
);
533 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
534 _("Range list parsing ended before hitting "
540 /* revisionline -> PATHNAME COLON revisionlist */
542 parse_revision_line(const char **input
, const char *end
, apr_hash_t
*hash
,
545 svn_stringbuf_t
*pathname
;
546 apr_array_header_t
*revlist
= apr_array_make(pool
, 1,
547 sizeof(svn_merge_range_t
*));
549 SVN_ERR(parse_pathname(input
, end
, &pathname
, pool
));
551 if (*(*input
) != ':')
552 return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
553 _("Pathname not terminated by ':'"));
557 SVN_ERR(parse_revlist(input
, end
, revlist
, pathname
->data
, pool
));
559 if (*input
!= end
&& *(*input
) != '\n')
560 return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR
, NULL
,
561 _("Could not find end of line in range list line "
567 qsort(revlist
->elts
, revlist
->nelts
, revlist
->elt_size
,
568 svn_sort_compare_ranges
);
569 apr_hash_set(hash
, pathname
->data
, APR_HASH_KEY_STRING
, revlist
);
574 /* top -> revisionline (NEWLINE revisionline)* */
576 parse_top(const char **input
, const char *end
, apr_hash_t
*hash
,
580 SVN_ERR(parse_revision_line(input
, end
, hash
, pool
));
585 /* Parse mergeinfo. */
587 svn_mergeinfo_parse(apr_hash_t
**mergeinfo
,
591 *mergeinfo
= apr_hash_make(pool
);
592 return parse_top(&input
, input
+ strlen(input
), *mergeinfo
, pool
);
596 /* Merge revision list RANGELIST into *MERGEINFO, doing some trivial
597 attempts to combine ranges as we go. */
599 svn_rangelist_merge(apr_array_header_t
**rangelist
,
600 apr_array_header_t
*changes
,
604 svn_merge_range_t
*lastrange
= NULL
;
605 apr_array_header_t
*output
= apr_array_make(pool
, 1,
606 sizeof(svn_merge_range_t
*));
609 while (i
< (*rangelist
)->nelts
&& j
< changes
->nelts
)
611 svn_merge_range_t
*elt1
, *elt2
;
614 elt1
= APR_ARRAY_IDX(*rangelist
, i
, svn_merge_range_t
*);
615 elt2
= APR_ARRAY_IDX(changes
, j
, svn_merge_range_t
*);
617 res
= svn_sort_compare_ranges(&elt1
, &elt2
);
620 /* Only when merging two non-inheritable ranges is the result also
621 non-inheritable. In all other cases ensure an inheritiable
623 if (elt1
->inheritable
|| elt2
->inheritable
)
624 elt1
->inheritable
= TRUE
;
625 combine_with_lastrange(&lastrange
, elt1
, TRUE
, output
,
632 combine_with_lastrange(&lastrange
, elt1
, TRUE
, output
,
638 combine_with_lastrange(&lastrange
, elt2
, TRUE
, output
,
643 /* Copy back any remaining elements.
644 Only one of these loops should end up running, if anything. */
646 assert (!(i
< (*rangelist
)->nelts
&& j
< changes
->nelts
));
648 for (; i
< (*rangelist
)->nelts
; i
++)
650 svn_merge_range_t
*elt
= APR_ARRAY_IDX(*rangelist
, i
,
651 svn_merge_range_t
*);
652 combine_with_lastrange(&lastrange
, elt
, TRUE
, output
,
657 for (; j
< changes
->nelts
; j
++)
659 svn_merge_range_t
*elt
= APR_ARRAY_IDX(changes
, j
, svn_merge_range_t
*);
660 combine_with_lastrange(&lastrange
, elt
, TRUE
, output
,
669 range_intersect(svn_merge_range_t
*first
, svn_merge_range_t
*second
,
670 svn_boolean_t consider_inheritance
)
672 return (first
->start
+ 1 <= second
->end
)
673 && (second
->start
+ 1 <= first
->end
)
674 && (!consider_inheritance
675 || (!(first
->inheritable
) == !(second
->inheritable
)));
679 range_contains(svn_merge_range_t
*first
, svn_merge_range_t
*second
,
680 svn_boolean_t consider_inheritance
)
682 return (first
->start
<= second
->start
) && (second
->end
<= first
->end
)
683 && (!consider_inheritance
684 || (!(first
->inheritable
) == !(second
->inheritable
)));
687 /* Swap start and end fields of RANGE. */
689 range_swap_endpoints(svn_merge_range_t
*range
)
691 svn_revnum_t swap
= range
->start
;
692 range
->start
= range
->end
;
697 svn_rangelist_reverse(apr_array_header_t
*rangelist
, apr_pool_t
*pool
)
700 svn_merge_range_t range
;
701 for (i
= 0; i
< rangelist
->nelts
/ 2; i
++)
703 swap_index
= rangelist
->nelts
- i
- 1;
704 range
= *APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*);
705 *APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*) =
706 *APR_ARRAY_IDX(rangelist
, swap_index
, svn_merge_range_t
*);
707 *APR_ARRAY_IDX(rangelist
, swap_index
, svn_merge_range_t
*) = range
;
708 range_swap_endpoints(APR_ARRAY_IDX(rangelist
, swap_index
,
709 svn_merge_range_t
*));
710 range_swap_endpoints(APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*));
713 /* If there's an odd number of elements, we still need to swap the
714 end points of the remaining range. */
715 if (rangelist
->nelts
% 2 == 1)
716 range_swap_endpoints(APR_ARRAY_IDX(rangelist
, rangelist
->nelts
/ 2,
717 svn_merge_range_t
*));
722 /* Either remove any overlapping ranges described by ERASER from
723 WHITEBOARD (when DO_REMOVE is TRUE), or capture the overlap, and
724 place the remaining or overlapping ranges in OUTPUT. */
725 /* ### FIXME: Some variables names and inline comments for this method
726 ### are legacy from when it was solely the remove() impl. */
728 rangelist_intersect_or_remove(apr_array_header_t
**output
,
729 apr_array_header_t
*eraser
,
730 apr_array_header_t
*whiteboard
,
731 svn_boolean_t do_remove
,
732 svn_boolean_t consider_inheritance
,
736 svn_merge_range_t
*lastrange
= NULL
;
737 svn_merge_range_t wboardelt
;
739 *output
= apr_array_make(pool
, 1, sizeof(svn_merge_range_t
*));
743 lasti
= -1; /* Initialized to a value that "i" will never be. */
745 while (i
< whiteboard
->nelts
&& j
< eraser
->nelts
)
747 svn_merge_range_t
*elt1
, *elt2
;
749 elt2
= APR_ARRAY_IDX(eraser
, j
, svn_merge_range_t
*);
751 /* Instead of making a copy of the entire array of whiteboard
752 elements, we just keep a copy of the current whiteboard element
753 that needs to be used, and modify our copy if necessary. */
756 wboardelt
= *(APR_ARRAY_IDX(whiteboard
, i
, svn_merge_range_t
*));
761 /* If the whiteboard range is contained completely in the
762 eraser, we increment the whiteboard.
763 If the ranges intersect, and match exactly, we increment both
764 eraser and whiteboard.
765 Otherwise, we have to generate a range for the left part of
766 the removal of eraser from whiteboard, and possibly change
767 the whiteboard to the remaining portion of the right part of
768 the removal, to test against. */
769 if (range_contains(elt2
, elt1
, consider_inheritance
))
772 combine_with_lastrange(&lastrange
, elt1
, TRUE
, *output
,
773 consider_inheritance
, pool
);
777 if (elt1
->start
== elt2
->start
&& elt1
->end
== elt2
->end
)
780 else if (range_intersect(elt2
, elt1
, consider_inheritance
))
782 if (elt1
->start
< elt2
->start
)
784 /* The whiteboard range starts before the eraser range. */
785 svn_merge_range_t tmp_range
;
786 tmp_range
.inheritable
= elt1
->inheritable
;
789 /* Retain the range that falls before the eraser start. */
790 tmp_range
.start
= elt1
->start
;
791 tmp_range
.end
= elt2
->start
;
795 /* Retain the range that falls between the eraser
796 start and whiteboard end. */
797 tmp_range
.start
= elt2
->start
;
798 tmp_range
.end
= elt1
->end
;
801 combine_with_lastrange(&lastrange
, &tmp_range
, TRUE
,
802 *output
, consider_inheritance
, pool
);
805 /* Set up the rest of the whiteboard range for further
807 if (elt1
->end
> elt2
->end
)
809 /* The whiteboard range ends after the eraser range. */
812 /* Partial overlap. */
813 svn_merge_range_t tmp_range
;
814 tmp_range
.start
= elt1
->start
;
815 tmp_range
.end
= elt2
->end
;
816 tmp_range
.inheritable
= elt1
->inheritable
;
817 combine_with_lastrange(&lastrange
, &tmp_range
, TRUE
,
818 *output
, consider_inheritance
, pool
);
821 wboardelt
.start
= elt2
->end
;
822 wboardelt
.end
= elt1
->end
;
827 else /* ranges don't intersect */
829 /* See which side of the whiteboard the eraser is on. If it
830 is on the left side, we need to move the eraser.
832 If it is on past the whiteboard on the right side, we
833 need to output the whiteboard and increment the
835 if (svn_sort_compare_ranges(&elt2
, &elt1
) < 0)
839 if (!lastrange
|| !combine_ranges(&lastrange
, lastrange
, elt1
,
840 consider_inheritance
))
844 lastrange
= svn_merge_range_dup(elt1
, pool
);
845 APR_ARRAY_PUSH(*output
, svn_merge_range_t
*) = lastrange
;
855 /* Copy the current whiteboard element if we didn't hit the end
856 of the whiteboard, and we still had it around. This element
857 may have been touched, so we can't just walk the whiteboard
858 array, we have to use our copy. This case only happens when
859 we ran out of eraser before whiteboard, *and* we had changed
860 the whiteboard element. */
861 if (i
== lasti
&& i
< whiteboard
->nelts
)
863 combine_with_lastrange(&lastrange
, &wboardelt
, TRUE
, *output
,
864 consider_inheritance
, pool
);
868 /* Copy any other remaining untouched whiteboard elements. */
869 for (; i
< whiteboard
->nelts
; i
++)
871 svn_merge_range_t
*elt
= APR_ARRAY_IDX(whiteboard
, i
,
872 svn_merge_range_t
*);
874 combine_with_lastrange(&lastrange
, elt
, TRUE
, *output
,
875 consider_inheritance
, pool
);
883 /* Expected to handle all the range overlap cases: non, partial, full */
885 svn_rangelist_intersect(apr_array_header_t
**output
,
886 apr_array_header_t
*rangelist1
,
887 apr_array_header_t
*rangelist2
,
890 return rangelist_intersect_or_remove(output
, rangelist1
, rangelist2
, FALSE
,
895 svn_rangelist_remove(apr_array_header_t
**output
,
896 apr_array_header_t
*eraser
,
897 apr_array_header_t
*whiteboard
,
898 svn_boolean_t consider_inheritance
,
901 return rangelist_intersect_or_remove(output
, eraser
, whiteboard
, TRUE
,
902 consider_inheritance
, pool
);
905 /* Output deltas via *DELETED and *ADDED, which will never be @c NULL.
907 The following diagrams illustrate some common range delta scenarios:
910 r0 <===========(=========)============[=========]===========> rHEAD
913 (from) deleted deleted
914 r0 <===========(=========[============]=========)===========> rHEAD
918 r0 <===========(=========[============)=========]===========> rHEAD
922 r0 <===========[=========(============]=========)===========> rHEAD
926 r0 <===========[=========(============)=========]===========> rHEAD
930 r0 <===(=[=)=]=[==]=[=(=)=]=[=]=[=(===|===(=)==|=|==[=(=]=)=> rHEAD
934 svn_rangelist_diff(apr_array_header_t
**deleted
, apr_array_header_t
**added
,
935 apr_array_header_t
*from
, apr_array_header_t
*to
,
936 svn_boolean_t consider_inheritance
,
939 /* The items that are present in from, but not in to, must have been
941 SVN_ERR(svn_rangelist_remove(deleted
, to
, from
, consider_inheritance
,
943 /* The items that are present in to, but not in from, must have been
945 SVN_ERR(svn_rangelist_remove(added
, from
, to
, consider_inheritance
, pool
));
950 svn_rangelist_count_revs(apr_array_header_t
*rangelist
)
952 apr_uint64_t nbr_revs
= 0;
955 for (i
= 0; i
< rangelist
->nelts
; i
++)
957 svn_merge_range_t
*range
= APR_ARRAY_IDX(rangelist
, i
,
958 svn_merge_range_t
*);
959 nbr_revs
+= range
->end
- range
->start
;
966 svn_rangelist_to_revs(apr_array_header_t
**revs
,
967 const apr_array_header_t
*rangelist
,
972 *revs
= apr_array_make(pool
, rangelist
->nelts
, sizeof(svn_revnum_t
));
974 for (i
= 0; i
< rangelist
->nelts
; i
++)
976 svn_merge_range_t
*range
= APR_ARRAY_IDX(rangelist
, i
,
977 svn_merge_range_t
*);
978 svn_revnum_t rev
= range
->start
+ 1;
980 while (rev
<= range
->end
)
982 APR_ARRAY_PUSH(*revs
, svn_revnum_t
) = rev
;
990 /* Record deletions and additions of entire range lists (by path
991 presence), and delegate to svn_rangelist_diff() for delta
992 calculations on a specific path. */
993 /* ### TODO: Merge implementation with
994 ### libsvn_subr/sorts.c:svn_prop_diffs(). Factor out a generic
995 ### hash diffing function for addition to APR's apr_hash.h API. */
997 walk_mergeinfo_hash_for_diff(apr_hash_t
*from
, apr_hash_t
*to
,
998 apr_hash_t
*deleted
, apr_hash_t
*added
,
999 svn_boolean_t consider_inheritance
,
1002 apr_hash_index_t
*hi
;
1006 apr_array_header_t
*from_rangelist
, *to_rangelist
;
1008 /* Handle path deletions and differences. */
1009 for (hi
= apr_hash_first(pool
, from
); hi
; hi
= apr_hash_next(hi
))
1011 apr_hash_this(hi
, &key
, NULL
, &val
);
1013 from_rangelist
= val
;
1015 /* If the path is not present at all in the "to" hash, the
1016 entire "from" rangelist is a deletion. Paths which are
1017 present in the "to" hash require closer scrutiny. */
1018 to_rangelist
= apr_hash_get(to
, path
, APR_HASH_KEY_STRING
);
1021 /* Record any deltas (additions or deletions). */
1022 apr_array_header_t
*deleted_rangelist
, *added_rangelist
;
1023 svn_rangelist_diff(&deleted_rangelist
, &added_rangelist
,
1024 from_rangelist
, to_rangelist
,
1025 consider_inheritance
, pool
);
1026 if (deleted
&& deleted_rangelist
->nelts
> 0)
1027 apr_hash_set(deleted
, apr_pstrdup(pool
, path
),
1028 APR_HASH_KEY_STRING
, deleted_rangelist
);
1029 if (added
&& added_rangelist
->nelts
> 0)
1030 apr_hash_set(added
, apr_pstrdup(pool
, path
),
1031 APR_HASH_KEY_STRING
, added_rangelist
);
1034 apr_hash_set(deleted
, apr_pstrdup(pool
, path
), APR_HASH_KEY_STRING
,
1035 svn_rangelist_dup(from_rangelist
, pool
));
1038 /* Handle path additions. */
1040 return SVN_NO_ERROR
;
1042 for (hi
= apr_hash_first(pool
, to
); hi
; hi
= apr_hash_next(hi
))
1044 apr_hash_this(hi
, &key
, NULL
, &val
);
1048 /* If the path is not present in the "from" hash, the entire
1049 "to" rangelist is an addition. */
1050 if (apr_hash_get(from
, path
, APR_HASH_KEY_STRING
) == NULL
)
1051 apr_hash_set(added
, apr_pstrdup(pool
, path
), APR_HASH_KEY_STRING
,
1052 svn_rangelist_dup(to_rangelist
, pool
));
1055 return SVN_NO_ERROR
;
1059 svn_mergeinfo_diff(apr_hash_t
**deleted
, apr_hash_t
**added
,
1060 apr_hash_t
*from
, apr_hash_t
*to
,
1061 svn_boolean_t consider_inheritance
,
1064 if (from
&& to
== NULL
)
1066 *deleted
= svn_mergeinfo_dup(from
, pool
);
1067 *added
= apr_hash_make(pool
);
1069 else if (from
== NULL
&& to
)
1071 *deleted
= apr_hash_make(pool
);
1072 *added
= svn_mergeinfo_dup(to
, pool
);
1076 *deleted
= apr_hash_make(pool
);
1077 *added
= apr_hash_make(pool
);
1081 SVN_ERR(walk_mergeinfo_hash_for_diff(from
, to
, *deleted
, *added
,
1082 consider_inheritance
, pool
));
1086 return SVN_NO_ERROR
;
1090 svn_mergeinfo__equals(svn_boolean_t
*is_equal
,
1093 svn_boolean_t consider_inheritance
,
1096 if (apr_hash_count(info1
) == apr_hash_count(info2
))
1098 apr_hash_t
*deleted
, *added
;
1099 SVN_ERR(svn_mergeinfo_diff(&deleted
, &added
, info1
, info2
,
1100 consider_inheritance
, pool
));
1101 *is_equal
= apr_hash_count(deleted
) == 0 && apr_hash_count(added
) == 0;
1107 return SVN_NO_ERROR
;
1111 svn_mergeinfo_merge(apr_hash_t
*mergeinfo
, apr_hash_t
*changes
,
1114 apr_array_header_t
*sorted1
, *sorted2
;
1117 sorted1
= svn_sort__hash(mergeinfo
, svn_sort_compare_items_as_paths
, pool
);
1118 sorted2
= svn_sort__hash(changes
, svn_sort_compare_items_as_paths
, pool
);
1122 while (i
< sorted1
->nelts
&& j
< sorted2
->nelts
)
1124 svn_sort__item_t elt1
, elt2
;
1127 elt1
= APR_ARRAY_IDX(sorted1
, i
, svn_sort__item_t
);
1128 elt2
= APR_ARRAY_IDX(sorted2
, j
, svn_sort__item_t
);
1129 res
= svn_sort_compare_items_as_paths(&elt1
, &elt2
);
1133 apr_array_header_t
*rl1
, *rl2
;
1138 SVN_ERR(svn_rangelist_merge(&rl1
, rl2
,
1140 apr_hash_set(mergeinfo
, elt1
.key
, elt1
.klen
, rl1
);
1150 apr_hash_set(mergeinfo
, elt2
.key
, elt2
.klen
, elt2
.value
);
1155 /* Copy back any remaining elements from the second hash. */
1156 for (; j
< sorted2
->nelts
; j
++)
1158 svn_sort__item_t elt
= APR_ARRAY_IDX(sorted2
, j
, svn_sort__item_t
);
1159 apr_hash_set(mergeinfo
, elt
.key
, elt
.klen
, elt
.value
);
1162 return SVN_NO_ERROR
;
1166 svn_mergeinfo_intersect(apr_hash_t
**mergeinfo
,
1167 apr_hash_t
*mergeinfo1
,
1168 apr_hash_t
*mergeinfo2
,
1171 apr_hash_index_t
*hi
;
1173 *mergeinfo
= apr_hash_make(pool
);
1175 /* ### TODO(reint): Do we care about the case when a path in one
1176 ### mergeinfo hash has inheritable mergeinfo, and in the other
1177 ### has non-inhertiable mergeinfo? It seems like that path
1178 ### itself should really be an intersection, while child paths
1179 ### should not be... */
1180 for (hi
= apr_hash_first(apr_hash_pool_get(mergeinfo1
), mergeinfo1
);
1181 hi
; hi
= apr_hash_next(hi
))
1183 apr_array_header_t
*rangelist
;
1186 apr_hash_this(hi
, &path
, NULL
, &val
);
1188 rangelist
= apr_hash_get(mergeinfo2
, path
, APR_HASH_KEY_STRING
);
1191 SVN_ERR(svn_rangelist_intersect(&rangelist
,
1192 (apr_array_header_t
*) val
,
1194 if (rangelist
->nelts
> 0)
1195 apr_hash_set(*mergeinfo
, path
, APR_HASH_KEY_STRING
, rangelist
);
1198 return SVN_NO_ERROR
;
1202 svn_mergeinfo_remove(apr_hash_t
**mergeinfo
, apr_hash_t
*eraser
,
1203 apr_hash_t
*whiteboard
, apr_pool_t
*pool
)
1205 *mergeinfo
= apr_hash_make(pool
);
1206 SVN_ERR(walk_mergeinfo_hash_for_diff(whiteboard
, eraser
, *mergeinfo
, NULL
,
1208 return SVN_NO_ERROR
;
1212 svn_rangelist_to_stringbuf(svn_stringbuf_t
**output
,
1213 const apr_array_header_t
*rangelist
,
1216 *output
= svn_stringbuf_create("", pool
);
1218 if (rangelist
->nelts
> 0)
1221 svn_merge_range_t
*range
;
1222 svn_stringbuf_t
*toappend
;
1224 /* Handle the elements that need commas at the end. */
1225 for (i
= 0; i
< rangelist
->nelts
- 1; i
++)
1227 range
= APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*);
1228 SVN_ERR(range_to_stringbuf(&toappend
, range
, pool
));
1229 svn_stringbuf_appendstr(*output
, toappend
);
1230 svn_stringbuf_appendcstr(*output
, ",");
1233 /* Now handle the last element, which needs no comma. */
1234 range
= APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*);
1235 SVN_ERR(range_to_stringbuf(&toappend
, range
, pool
));
1236 svn_stringbuf_appendstr(*output
, toappend
);
1239 return SVN_NO_ERROR
;
1243 svn_mergeinfo_to_stringbuf(svn_stringbuf_t
**output
, apr_hash_t
*input
,
1246 *output
= svn_stringbuf_create("", pool
);
1248 if (apr_hash_count(input
) > 0)
1250 apr_array_header_t
*sorted
=
1251 svn_sort__hash(input
, svn_sort_compare_items_as_paths
, pool
);
1252 svn_sort__item_t elt
;
1253 svn_stringbuf_t
*revlist
, *combined
;
1256 /* Handle the elements that need newlines at the end. */
1257 for (i
= 0; i
< sorted
->nelts
- 1; i
++)
1259 elt
= APR_ARRAY_IDX(sorted
, i
, svn_sort__item_t
);
1261 SVN_ERR(svn_rangelist_to_stringbuf(&revlist
, elt
.value
, pool
));
1262 combined
= svn_stringbuf_createf(pool
, "%s:%s\n", (char *) elt
.key
,
1264 svn_stringbuf_appendstr(*output
, combined
);
1267 /* Now handle the last element, which is not newline terminated. */
1268 elt
= APR_ARRAY_IDX(sorted
, i
, svn_sort__item_t
);
1270 SVN_ERR(svn_rangelist_to_stringbuf(&revlist
, elt
.value
, pool
));
1271 combined
= svn_stringbuf_createf(pool
, "%s:%s", (char *) elt
.key
,
1273 svn_stringbuf_appendstr(*output
, combined
);
1276 return SVN_NO_ERROR
;
1280 svn_mergeinfo__to_string(svn_string_t
**output
, apr_hash_t
*input
,
1283 if (apr_hash_count(input
) > 0)
1285 svn_stringbuf_t
*mergeinfo_buf
;
1286 SVN_ERR(svn_mergeinfo_to_stringbuf(&mergeinfo_buf
, input
, pool
));
1287 *output
= svn_string_create_from_buf(mergeinfo_buf
, pool
);
1291 *output
= svn_string_create("", pool
);
1293 return SVN_NO_ERROR
;
1296 /* Perform an in-place sort of the rangelists in a mergeinfo hash. */
1298 svn_mergeinfo_sort(apr_hash_t
*input
, apr_pool_t
*pool
)
1300 apr_hash_index_t
*hi
;
1303 for (hi
= apr_hash_first(pool
, input
); hi
; hi
= apr_hash_next(hi
))
1305 apr_array_header_t
*rl
;
1306 apr_hash_this(hi
, NULL
, NULL
, &val
);
1309 qsort(rl
->elts
, rl
->nelts
, rl
->elt_size
, svn_sort_compare_ranges
);
1311 return SVN_NO_ERROR
;
1315 svn_mergeinfo_dup(apr_hash_t
*mergeinfo
, apr_pool_t
*pool
)
1317 apr_hash_t
*new_mergeinfo
= apr_hash_make(pool
);
1318 apr_hash_index_t
*hi
;
1320 apr_ssize_t pathlen
;
1323 for (hi
= apr_hash_first(pool
, mergeinfo
); hi
; hi
= apr_hash_next(hi
))
1325 apr_hash_this(hi
, &path
, &pathlen
, &rangelist
);
1326 apr_hash_set(new_mergeinfo
, apr_pstrmemdup(pool
, path
, pathlen
), pathlen
,
1327 svn_rangelist_dup((apr_array_header_t
*) rangelist
, pool
));
1330 return new_mergeinfo
;
1334 svn_mergeinfo_inheritable(apr_hash_t
**output
,
1335 apr_hash_t
*mergeinfo
,
1341 apr_hash_index_t
*hi
;
1346 apr_hash_t
*inheritable_mergeinfo
= apr_hash_make(pool
);
1347 for (hi
= apr_hash_first(pool
, mergeinfo
); hi
; hi
= apr_hash_next(hi
))
1349 apr_array_header_t
*inheritable_rangelist
;
1350 apr_hash_this(hi
, &key
, &keylen
, &rangelist
);
1351 if (!path
|| svn_path_compare_paths(path
, (const char *)key
) == 0)
1352 SVN_ERR(svn_rangelist_inheritable(&inheritable_rangelist
,
1353 (apr_array_header_t
*) rangelist
,
1356 inheritable_rangelist
=
1357 svn_rangelist_dup((apr_array_header_t
*)rangelist
, pool
);
1358 apr_hash_set(inheritable_mergeinfo
,
1359 apr_pstrmemdup(pool
, key
, keylen
), keylen
,
1360 inheritable_rangelist
);
1362 *output
= inheritable_mergeinfo
;
1363 return SVN_NO_ERROR
;
1367 svn_rangelist_inheritable(apr_array_header_t
**inheritable_rangelist
,
1368 apr_array_header_t
*rangelist
,
1373 *inheritable_rangelist
= apr_array_make(pool
, 1,
1374 sizeof(svn_merge_range_t
*));
1375 if (rangelist
->nelts
)
1377 if (!SVN_IS_VALID_REVNUM(start
)
1378 || !SVN_IS_VALID_REVNUM(end
)
1382 /* We want all non-inheritable ranges removed. */
1383 for (i
= 0; i
< rangelist
->nelts
; i
++)
1385 svn_merge_range_t
*range
= APR_ARRAY_IDX(rangelist
, i
,
1386 svn_merge_range_t
*);
1387 if (range
->inheritable
)
1389 svn_merge_range_t
*inheritable_range
=
1390 apr_palloc(pool
, sizeof(*inheritable_range
));
1391 inheritable_range
->start
= range
->start
;
1392 inheritable_range
->end
= range
->end
;
1393 inheritable_range
->inheritable
= TRUE
;
1394 APR_ARRAY_PUSH(*inheritable_rangelist
,
1395 svn_merge_range_t
*) = range
;
1401 /* We want only the non-inheritable ranges bound by START
1403 apr_array_header_t
*ranges_inheritable
=
1404 apr_array_make(pool
, 0, sizeof(svn_merge_range_t
*));
1405 svn_merge_range_t
*range
= apr_palloc(pool
, sizeof(*range
));
1407 range
->start
= start
;
1409 range
->inheritable
= FALSE
;
1410 APR_ARRAY_PUSH(ranges_inheritable
, svn_merge_range_t
*) = range
;
1412 if (rangelist
->nelts
)
1413 SVN_ERR(svn_rangelist_remove(inheritable_rangelist
,
1420 return SVN_NO_ERROR
;
1424 svn_mergeinfo__remove_empty_rangelists(apr_hash_t
*mergeinfo
,
1427 apr_hash_index_t
*hi
;
1428 svn_boolean_t removed_some_ranges
= FALSE
;
1432 for (hi
= apr_hash_first(pool
, mergeinfo
); hi
; hi
= apr_hash_next(hi
))
1437 apr_array_header_t
*rangelist
;
1439 apr_hash_this(hi
, &key
, NULL
, &value
);
1443 if (rangelist
->nelts
== 0)
1445 apr_hash_set(mergeinfo
, path
, APR_HASH_KEY_STRING
, NULL
);
1446 removed_some_ranges
= TRUE
;
1450 return removed_some_ranges
;
1453 apr_array_header_t
*
1454 svn_rangelist_dup(apr_array_header_t
*rangelist
, apr_pool_t
*pool
)
1456 apr_array_header_t
*new_rl
= apr_array_make(pool
, rangelist
->nelts
,
1457 sizeof(svn_merge_range_t
*));
1460 for (i
= 0; i
< rangelist
->nelts
; i
++)
1462 APR_ARRAY_PUSH(new_rl
, svn_merge_range_t
*) =
1463 svn_merge_range_dup(APR_ARRAY_IDX(rangelist
, i
, svn_merge_range_t
*),
1471 svn_range_compact(svn_merge_range_t
**range_1
,
1472 svn_merge_range_t
**range_2
)
1474 svn_boolean_t is_compacted
= FALSE
;
1475 svn_boolean_t range_1_is_reversed
, range_2_is_reversed
;
1477 /* Wave the white flag if out preconditions are not met. */
1480 || !SVN_IS_VALID_REVNUM(*range_1
)
1481 || !SVN_IS_VALID_REVNUM(*range_2
))
1484 /* "Normalize" the ranges so start <= end. */
1485 range_1_is_reversed
= (*range_1
)->start
> (*range_1
)->end
? TRUE
: FALSE
;
1486 range_2_is_reversed
= (*range_2
)->start
> (*range_2
)->end
? TRUE
: FALSE
;
1487 if (range_1_is_reversed
)
1488 range_swap_endpoints(*range_1
);
1489 if (range_2_is_reversed
)
1490 range_swap_endpoints(*range_2
);
1492 /* Do the ranges overlap?
1493 Range overlapping detection algorithm from
1494 http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap */
1495 if ((*range_1
)->start
<= (*range_2
)->end
1496 && (*range_2
)->start
<= (*range_1
)->end
)
1498 /* If the ranges are both in the same direction simply combine them. */
1499 if (range_1_is_reversed
== range_2_is_reversed
)
1501 (*range_1
)->start
= MIN((*range_1
)->start
, (*range_2
)->start
);
1502 (*range_1
)->end
= MAX((*range_1
)->end
, (*range_2
)->end
);
1505 else /* *Exactly* one of the ranges is a reverse range. */
1507 svn_revnum_t range_tmp
;
1509 if ((*range_1
)->start
== (*range_2
)->start
)
1511 if ((*range_1
)->end
== (*range_2
)->end
)
1513 /* A range and its reverse just cancel each other out. */
1514 *range_1
= *range_2
= NULL
;
1518 (*range_1
)->start
= (*range_1
)->end
;
1519 (*range_1
)->end
= (*range_2
)->end
;
1521 /* RANGE_2 is a superset of RANGE_1, the intersecting
1522 portions of each range cancel each other out.
1523 The resulting compacted range is stored in RANGE_1 so
1524 it takes on the reversed property of RANGE_2. */
1525 range_1_is_reversed
= range_2_is_reversed
;
1528 else /* (*range_1)->start != (*range_2)->start) */
1530 if ((*range_1
)->end
> (*range_2
)->end
)
1532 /* RANGE_1 is a superset of RANGE_2 */
1533 if ((*range_1
)->start
< (*range_2
)->start
)
1535 range_tmp
= (*range_1
)->end
;
1536 (*range_1
)->end
= (*range_2
)->start
;
1537 (*range_2
)->start
= (*range_2
)->end
;
1538 (*range_2
)->end
= range_tmp
;
1539 range_2_is_reversed
= range_1_is_reversed
;
1543 range_tmp
= (*range_1
)->start
;
1544 (*range_1
)->start
= (*range_2
)->end
;
1545 (*range_2
)->end
= range_tmp
;
1548 else if ((*range_1
)->end
< (*range_2
)->end
)
1550 /* RANGE_1 and RANGE_2 intersect. */
1551 range_tmp
= (*range_1
)->end
;
1552 (*range_1
)->end
= (*range_2
)->start
;
1553 (*range_2
)->start
= range_tmp
;
1554 (*range_2
)->end
= (*range_2
)->end
;
1556 else /* (*range_1)->end == (*range_2)->end */
1558 /* RANGE_2 is a proper subset of RANGE_1. */
1559 (*range_1
)->end
= (*range_2
)->start
;
1564 is_compacted
= TRUE
;
1565 } /* ranges overlap */
1567 /* Return compacted ranges to their original direction. */
1568 if (*range_1
&& range_1_is_reversed
)
1569 range_swap_endpoints(*range_1
);
1570 if (*range_2
&& range_2_is_reversed
)
1571 range_swap_endpoints(*range_2
);
1572 return is_compacted
;
1576 svn_merge_range_dup(svn_merge_range_t
*range
, apr_pool_t
*pool
)
1578 svn_merge_range_t
*new_range
= apr_palloc(pool
, sizeof(*new_range
));
1579 memcpy(new_range
, range
, sizeof(*new_range
));
1584 svn_merge_range_contains_rev(svn_merge_range_t
*range
, svn_revnum_t rev
)
1586 assert(SVN_IS_VALID_REVNUM(range
->start
));
1587 assert(SVN_IS_VALID_REVNUM(range
->end
));
1588 assert(range
->start
!= range
->end
);
1590 if (range
->start
< range
->end
)
1591 return rev
> range
->start
&& rev
<= range
->end
;
1593 return rev
> range
->end
&& rev
<= range
->start
;