Here are the three patches resulting from my work so far:

merge-4840-backtrace-4.patch
merge-4840-print-empty-range.patch
merge-4840-test-1.patch

Together, anyone who wants to follow along can reproduce the errors. These are not quite in a clean enough state to commit, but I expect to commit something like them.

- Julian
For issue #4840, "Merge assertion failure in svn_sort__array_insert".


* subversion/libsvn_subr/mergeinfo.c
  (range_to_string): Print an empty range in the form "[empty-range@100*]".
--This line, and those below, will be ignored--

Index: subversion/libsvn_subr/mergeinfo.c
===================================================================
--- subversion/libsvn_subr/mergeinfo.c	(revision 1871324)
+++ subversion/libsvn_subr/mergeinfo.c	(working copy)
@@ -470,6 +470,8 @@ range_to_string(const svn_merge_range_t
     return apr_psprintf(pool, "-%ld%s", range->start, mark);
   else if (range->start < range->end)
     return apr_psprintf(pool, "%ld-%ld%s", range->start + 1, range->end, mark);
+  else if (range->start == range->end)
+    return apr_psprintf(pool, "[empty-range@%ld%s]", range->start, mark);
   else
     return apr_psprintf(pool, "%ld-%ld%s", range->start, range->end + 1, mark);
 }
Random-input testing for issue #4840, "Merge assertion failure in
svn_sort__array_insert".

### Manually edit the test to call one of
    rangelist_random_non_canonical() or
    rangelist_random_semi_canonical() or
    rangelist_random_canonical(), in two places.

Note that the number of iterations needs to be in the order of 1000 in each
loop rather than 300 in each loop, in its present formulation, to catch some
of the errors.

* subversion/tests/libsvn_subr/mergeinfo-test.c
  (...): Helper functions.
  (test_rangelist_merge_random_inputs): New test.
  (test_funcs): Run it.
--This line, and those below, will be ignored--

Index: subversion/tests/libsvn_subr/mergeinfo-test.c
===================================================================
--- subversion/tests/libsvn_subr/mergeinfo-test.c	(revision 1871324)
+++ subversion/tests/libsvn_subr/mergeinfo-test.c	(working copy)
@@ -30,12 +30,13 @@
 
 #include "svn_hash.h"
 #include "svn_pools.h"
 #include "svn_types.h"
 #include "svn_mergeinfo.h"
 #include "private/svn_mergeinfo_private.h"
+#include "private/svn_sorts_private.h"
 #include "../svn_test.h"
 
 /* A quick way to create error messages.  */
 static svn_error_t *
 fail(apr_pool_t *pool, const char *fmt, ...)
 {
@@ -1780,12 +1781,258 @@ test_rangelist_loop(apr_pool_t *pool)
           /* TODO: Verify result */
         }
       }
 
   return SVN_NO_ERROR;
 }
+
+/* A representation of svn_rangelist_t in which
+ *   root[R]    := (revision R is in the rangelist)
+ *   inherit[R] := (revision R is in the rangelist and inheritable)
+ *
+ * Assuming all forward ranges.
+ */
+typedef struct rl_array_t {
+    svn_boolean_t root[100];
+    svn_boolean_t inherit[100];
+} rl_array_t;
+
+static void
+rangelist_to_array(rl_array_t *a,
+                   const svn_rangelist_t *rl)
+{
+  int i;
+
+  memset(a, 0, sizeof(*a));
+  for (i = 0; i < rl->nelts; i++)
+    {
+      svn_merge_range_t *range = APR_ARRAY_IDX(rl, i, svn_merge_range_t *);
+      int r;
+
+      for (r = range->start + 1; r <= range->end; r++)
+        {
+          a->root[r] = TRUE;
+          a->inherit[r] = range->inheritable;
+        }
+    }
+}
+
+/* Let MA := union(BA, CA)
+ * Assuming all forward ranges.
+ */
+static void
+rangelist_array_merge(rl_array_t *ma,
+                      const rl_array_t *ba,
+                      const rl_array_t *ca)
+{
+  int r;
+
+  for (r = 0; r < 100; r++)
+    {
+      ma->root[r]    = ba->root[r]    || ca->root[r];
+      ma->inherit[r] = ba->inherit[r] || ca->inherit[r];
+    }
+}
+
+static svn_boolean_t
+rangelist_array_equal(const rl_array_t *ba,
+                      const rl_array_t *ca)
+{
+  int r;
+
+  for (r = 0; r < 100; r++)
+    {
+      if (ba->root[r]    != ca->root[r]
+       || ba->inherit[r] != ca->inherit[r])
+        {
+          return FALSE;
+        }
+    }
+  return TRUE;
+}
+
+static svn_boolean_t
+rangelist_equal(const svn_rangelist_t *bl,
+                const svn_rangelist_t *cl)
+{
+  rl_array_t ba, ca;
+
+  rangelist_to_array(&ba, bl);
+  rangelist_to_array(&ca, cl);
+  return rangelist_array_equal(&ba, &ca);
+}
+
+#define IS_VALID_FORWARD_RANGE(range) \
+  (SVN_IS_VALID_REVNUM((range)->start) && ((range)->start < (range)->end))
+
+/* Check rangelist is sorted and contains forward ranges. */
+static svn_boolean_t
+rangelist_is_sorted(const svn_rangelist_t *rangelist)
+{
+  int i;
+
+  for (i = 0; i < rangelist->nelts; i++)
+    {
+      const svn_merge_range_t *thisrange
+        = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
+
+      if (!IS_VALID_FORWARD_RANGE(thisrange))
+        return FALSE;
+    }
+  for (i = 1; i < rangelist->nelts; i++)
+    {
+      const svn_merge_range_t *lastrange
+        = APR_ARRAY_IDX(rangelist, i-1, svn_merge_range_t *);
+      const svn_merge_range_t *thisrange
+        = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
+
+      if (svn_sort_compare_ranges(&lastrange, &thisrange) > 0)
+        return FALSE;
+    }
+  return TRUE;
+}
+
+static int rand_less_than(int n)
+{
+  apr_uint32_t next = svn_test_rand(&random_rev_array_seed);
+  return ((apr_uint64_t)next * n) >> 32;
+}
+
+static void
+rangelist_random_non_canonical(svn_rangelist_t **rl,
+                               apr_pool_t *pool)
+{
+  svn_rangelist_t *r = apr_array_make(pool, 4, sizeof(svn_merge_range_t *));
+  int i;
+
+  /* Choose from 0 to 4 ranges, biased towards 2 ranges */
+  for (i = rand_less_than(3) + rand_less_than(3); i > 0; --i)
+    {
+      svn_merge_range_t *mrange = apr_pcalloc(pool, sizeof(*mrange));
+
+      mrange->start = rand_less_than(42);
+      mrange->end = rand_less_than(42);
+      mrange->inheritable = rand_less_than(2);
+      APR_ARRAY_PUSH(r, svn_merge_range_t *) = mrange;
+    }
+  *rl = r;
+}
+
+/* Return a rangelist which is not necessarily canonical
+ * but is at least sorted according to svn_sort_compare_ranges()
+ * and on which svn_rangelist__canonicalize() would succeed. */
+static void
+rangelist_random_semi_canonical(svn_rangelist_t **rl,
+                                apr_pool_t *pool)
+{
+  do
+    {
+      svn_rangelist_t *dup;
+      svn_error_t *err;
+
+      rangelist_random_non_canonical(rl, pool);
+      if (!rangelist_is_sorted(*rl))
+        continue;
+      dup = svn_rangelist_dup(*rl, pool);
+      if ((err = svn_rangelist__canonicalize(dup, pool)))
+        {
+          svn_error_clear(err);
+          continue;
+        }
+      break;
+    } while (1);
+}
+
+static void
+rangelist_random_canonical(svn_rangelist_t **rl,
+                           apr_pool_t *pool)
+{
+  do {
+    rangelist_random_non_canonical(rl, pool);
+  } while (! svn_rangelist__is_canonical(*rl));
+}
+
+static const char *
+rangelist_to_string(const svn_rangelist_t *rlx,
+                    apr_pool_t *pool)
+{
+  svn_string_t *sx;
+
+  svn_error_clear(svn_rangelist_to_string(&sx, rlx, pool));
+  return sx->data;
+}
+
+static const char *
+two_rangelists_to_string(const svn_rangelist_t *rlx,
+                         const svn_rangelist_t *rly,
+                         apr_pool_t *pool)
+{
+  svn_string_t *sx, *sy;
+
+  svn_error_clear(svn_rangelist_to_string(&sx, rlx, pool));
+  svn_error_clear(svn_rangelist_to_string(&sy, rly, pool));
+  return apr_psprintf(pool, "%s / %s", sx->data, sy->data);
+}
+
+static svn_error_t *
+test_rangelist_merge_random_inputs(apr_pool_t *pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(pool);
+  int ix, iy;
+
+  for (ix = 0; ix < 1000; ix++)
+   {
+    svn_rangelist_t *rlx;
+    rl_array_t ax;
+
+    rangelist_random_semi_canonical(&rlx, pool);
+    rangelist_to_array(&ax, rlx);
+
+    for (iy = 0; iy < 1000; iy++)
+      {
+        svn_rangelist_t *rly, *rlm;
+        rl_array_t ay, am, a;
+        svn_error_t *err;
+
+        svn_pool_clear(iterpool);
+
+        rangelist_random_semi_canonical(&rly, iterpool);
+        rangelist_to_array(&ay, rly);
+        rangelist_array_merge(&am, &ax, &ay);
+
+        rlm = svn_rangelist_dup(rlx, iterpool);
+        /*printf("testcase: %s\n", two_rangelists_to_string(rlx, rly, iterpool));*/
+        err = svn_rangelist_merge2(rlm, rly, iterpool, iterpool);
+        if (err)
+          {
+            if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
+              printf("testcase MI PARSE ERR: %s\n", two_rangelists_to_string(rlx, rly, iterpool));
+            else {
+              printf("testcase FAIL: %s\n", two_rangelists_to_string(rlx, rly, iterpool));
+              svn_handle_error(err, stdout, FALSE);
+            }
+            svn_error_clear(err);
+            continue;
+          }
+
+        /*SVN_TEST_ASSERT(svn_rangelist__is_canonical(rlm));*/
+        rangelist_to_array(&a, rlm);
+        if (!rangelist_array_equal(&a, &am))
+          {
+            printf("testcase FAIL: (c? %d / %d) %s\n",
+                   svn_rangelist__is_canonical(rlx),
+                   svn_rangelist__is_canonical(rly),
+                   two_rangelists_to_string(rlx, rly, iterpool));
+            printf("               -> %s\n", rangelist_to_string(rlm, iterpool));
+          }
+        /*SVN_TEST_ASSERT(rangelist_array_equal(&a, &am));*/
+      }
+   }
+
+  return SVN_NO_ERROR;
+}
 
 /* The test table.  */
 
 static int max_threads = 4;
 
 static struct svn_test_descriptor_t test_funcs[] =
@@ -1828,10 +2075,12 @@ static struct svn_test_descriptor_t test
     SVN_TEST_PASS2(test_remove_prefix_from_catalog,
                    "removal of prefix paths from catalog keys"),
     SVN_TEST_PASS2(test_rangelist_merge_overlap,
                    "merge of rangelists with overlaps (issue 4686)"),
     SVN_TEST_PASS2(test_rangelist_loop,
                     "test rangelist edgecases via loop"),
+    SVN_TEST_PASS2(test_rangelist_merge_random_inputs,
+                   "test rangelist merge random inputs"),
     SVN_TEST_NULL
   };
 
 SVN_TEST_MAIN
Debugging for issue #4840, "Merge assertion failure in
svn_sort__array_insert".

Avoid aborting on assertion failure; instead, raise an error.

If an error occurs in svn_rangelist_merge2(), report its inputs so we
can generate a reproducible test case.

Check inputs to svn_sort__array_insert().
Check inputs to svn_sort__array_delete() as well, since aspects of the same
issue may show up here too.  This function was previously ignoring
calls with out-of-range inputs.

* subversion/include/private/svn_sorts_private.h,
  subversion/libsvn_subr/sorts.c
  (svn_sort__array_insert2,
   svn_sort__array_delete2): New.

* subversion/libsvn_client/merge.c
  (slice_remaining_ranges,
   insert_child_to_merge): Allow returning an error.
  Everywhere: use svn_sort__array_insert2() and svn_sort__array_delete2().

* subversion/libsvn_subr/mergeinfo.c
  (adjust_remaining_ranges): Allow returning an error.
  (dual_dump): Activate this old debug helper, and let it wrap an
    existing error.
  (svn_rangelist_merge2): Extract the body of this function into a local
    'rangelist_merge2', leaving the original as an error-checking
    wrapper. If an error occurs, report its inputs.
  (rangelist_is_sorted): New.
  Everywhere: use svn_sort__array_insert2() and svn_sort__array_delete2().

* subversion/svn/svn.c
  (sub_main): Configure assertion errors to raise an error rather than abort.
    Affects SVN_ERR_ASSERT() but not assert().
--This line, and those below, will be ignored--

Index: subversion/include/private/svn_sorts_private.h
===================================================================
--- subversion/include/private/svn_sorts_private.h	(revision 1871324)
+++ subversion/include/private/svn_sorts_private.h	(working copy)
@@ -127,6 +127,16 @@
                        const void *new_element,
                        int insert_index);
 
+/* Like svn_sort__array_insert() but raise an error if @a insert_index
+ * is less than 0 or greater than the length of the array.
+ *
+ * @note Private. For use by Subversion's own code only.
+ */
+svn_error_t *
+svn_sort__array_insert2(apr_array_header_t *array,
+                        const void *new_element,
+                        int insert_index);
+
 
 /* Remove @a elements_to_delete elements starting at @a delete_index from the
  * array @a arr. If @a delete_index is not a valid element of @a arr,
@@ -141,6 +151,16 @@
                        int delete_index,
                        int elements_to_delete);
 
+/* Like svn_sort__array_delete() but raise an error if attempting
+ * to delete a range of elements that goes out of bounds of the array.
+ *
+ * @note Private. For use by Subversion's own code only.
+ */
+svn_error_t *
+svn_sort__array_delete2(apr_array_header_t *arr,
+                        int delete_index,
+                        int elements_to_delete);
+
 /* Reverse the order of elements in @a array, in place.
  *
  * @note Private. For use by Subversion's own code only.
Index: subversion/libsvn_client/merge.c
===================================================================
--- subversion/libsvn_client/merge.c	(revision 1871324)
+++ subversion/libsvn_client/merge.c	(working copy)
@@ -5975,7 +5975,7 @@
    remaining_ranges is inclusive of END_REV, Slice the first range in
    to two at END_REV. All the allocations are persistent and allocated
    from POOL. */
-static void
+static svn_error_t *
 slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
                        svn_boolean_t is_rollback, svn_revnum_t end_rev,
                        apr_pool_t *pool)
@@ -6005,10 +6005,11 @@
               split_range2->start = end_rev;
               APR_ARRAY_IDX(child->remaining_ranges, 0,
                             svn_merge_range_t *) = split_range1;
-              svn_sort__array_insert(child->remaining_ranges, &split_range2, 1);
+              SVN_ERR(svn_sort__array_insert2(child->remaining_ranges, &split_range2, 1));
             }
         }
     }
+  return SVN_NO_ERROR;
 }
 
 /* Helper for do_directory_merge().
@@ -6130,7 +6131,7 @@
        out of order and then sort afterwards. (One caller is doing a qsort
        after calling this anyway.)
  */
-static void
+static svn_error_t *
 insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
                       const svn_client__merge_path_t *insert_element,
                       apr_pool_t *pool)
@@ -6144,7 +6145,8 @@
                                   compare_merge_path_t_as_paths);
 
   new_element = svn_client__merge_path_dup(insert_element, pool);
-  svn_sort__array_insert(children_with_mergeinfo, &new_element, insert_index);
+  SVN_ERR(svn_sort__array_insert2(children_with_mergeinfo, &new_element, insert_index));
+  return SVN_NO_ERROR;
 }
 
 /* Helper for get_mergeinfo_paths().
@@ -6205,7 +6207,7 @@
       parent->missing_child = child->absent;
       parent->switched_child = child->switched;
       /* Insert PARENT into CHILDREN_WITH_MERGEINFO. */
-      insert_child_to_merge(children_with_mergeinfo, parent, pool);
+      SVN_ERR(insert_child_to_merge(children_with_mergeinfo, parent, pool));
       /* Increment for loop index so we don't process the inserted element. */
       (*curr_index)++;
     } /*(parent == NULL) */
@@ -6242,8 +6244,8 @@
 
           sibling_of_missing = svn_client__merge_path_create(child_abspath,
                                                              pool);
-          insert_child_to_merge(children_with_mergeinfo, sibling_of_missing,
-                                pool);
+          SVN_ERR(insert_child_to_merge(children_with_mergeinfo, sibling_of_missing,
+                                        pool));
         }
     }
 
@@ -6584,8 +6586,8 @@
                svn_client__merge_path_t *switched_child =
                  svn_client__merge_path_create(wc_path, result_pool);
                switched_child->switched = TRUE;
-               insert_child_to_merge(children_with_mergeinfo, switched_child,
-                                     result_pool);
+               SVN_ERR(insert_child_to_merge(children_with_mergeinfo, switched_child,
+                                             result_pool));
              }
         }
     }
@@ -6637,8 +6639,8 @@
             }
 
           if (new_shallow_child)
-            insert_child_to_merge(children_with_mergeinfo, shallow_child,
-                                  result_pool);
+            SVN_ERR(insert_child_to_merge(children_with_mergeinfo, shallow_child,
+                                          result_pool));
        }
     }
 
@@ -6667,8 +6669,8 @@
                svn_client__merge_path_t *absent_child =
                  svn_client__merge_path_create(wc_path, result_pool);
                absent_child->absent = TRUE;
-               insert_child_to_merge(children_with_mergeinfo, absent_child,
-                                     result_pool);
+               SVN_ERR(insert_child_to_merge(children_with_mergeinfo, absent_child,
+                                             result_pool));
              }
         }
     }
@@ -6681,8 +6683,8 @@
       svn_client__merge_path_t *target_child =
         svn_client__merge_path_create(target->abspath,
                                       result_pool);
-      insert_child_to_merge(children_with_mergeinfo, target_child,
-                            result_pool);
+      SVN_ERR(insert_child_to_merge(children_with_mergeinfo, target_child,
+                                    result_pool));
     }
 
   /* Case 8: Path is an immediate *directory* child of
@@ -6725,8 +6727,8 @@
                       && depth == svn_depth_immediates)
                     immediate_child->immediate_child_dir = TRUE;
 
-                  insert_child_to_merge(children_with_mergeinfo,
-                                        immediate_child, result_pool);
+                  SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
+                                                immediate_child, result_pool));
                 }
             }
         }
@@ -6818,9 +6820,9 @@
                   child_of_noninheritable =
                     svn_client__merge_path_create(child_abspath, result_pool);
                   child_of_noninheritable->child_of_noninheritable = TRUE;
-                  insert_child_to_merge(children_with_mergeinfo,
-                                        child_of_noninheritable,
-                                        result_pool);
+                  SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
+                                                child_of_noninheritable,
+                                                result_pool));
                   if (!dry_run && same_repos)
                     {
                       svn_mergeinfo_t mergeinfo;
@@ -7244,7 +7246,7 @@
                   new_segment->path = original_repos_relpath;
                   new_segment->range_start = original_revision;
                   new_segment->range_end = original_revision;
-                  svn_sort__array_insert(segments, &new_segment, 0);
+                  SVN_ERR(svn_sort__array_insert2(segments, &new_segment, 0));
                 }
             }
         }
@@ -7978,7 +7980,7 @@
               /* Set the path's remaining_ranges equal to its parent's. */
               new_child->remaining_ranges = svn_rangelist_dup(
                  parent->remaining_ranges, pool);
-              insert_child_to_merge(children_with_mergeinfo, new_child, pool);
+              SVN_ERR(insert_child_to_merge(children_with_mergeinfo, new_child, pool));
             }
         }
     }
@@ -9510,8 +9512,8 @@
 
               svn_pool_clear(iterpool);
 
-              slice_remaining_ranges(children_with_mergeinfo,
-                                     is_rollback, end_rev, scratch_pool);
+              SVN_ERR(slice_remaining_ranges(children_with_mergeinfo,
+                                             is_rollback, end_rev, scratch_pool));
 
               /* Reset variables that must be reset for every drive */
               merge_b->notify_begin.last_abspath = NULL;
Index: subversion/libsvn_subr/mergeinfo.c
===================================================================
--- subversion/libsvn_subr/mergeinfo.c	(revision 1871324)
+++ subversion/libsvn_subr/mergeinfo.c	(working copy)
@@ -807,7 +807,7 @@
    element is equal to the start rev of the younger element.
 
    Any new elements inserted into RANGELIST are allocated in  RESULT_POOL.*/
-static void
+static svn_error_t *
 adjust_remaining_ranges(svn_rangelist_t *rangelist,
                         int *range_index,
                         apr_pool_t *result_pool)
@@ -818,7 +818,7 @@
   svn_merge_range_t *modified_range;
 
   if (*range_index >= rangelist->nelts)
-    return;
+    return SVN_NO_ERROR;
 
   starting_index = *range_index + 1;
   modified_range = APR_ARRAY_IDX(rangelist, *range_index, svn_merge_range_t *);
@@ -892,10 +892,10 @@
               new_modified_range->inheritable = FALSE;
               modified_range->end = next_range->start;
               (*range_index) += 2 + elements_to_delete;
-              svn_sort__array_insert(rangelist, &new_modified_range,
-                                     *range_index);
+              SVN_ERR(svn_sort__array_insert2(rangelist, &new_modified_range,
+                                              *range_index));
               /* Recurse with the new range. */
-              adjust_remaining_ranges(rangelist, range_index, result_pool);
+              SVN_ERR(adjust_remaining_ranges(rangelist, range_index, result_pool));
               break;
             }
         }
@@ -948,31 +948,33 @@
     }
 
   if (elements_to_delete)
-    svn_sort__array_delete(rangelist, starting_index, elements_to_delete);
+    SVN_ERR(svn_sort__array_delete2(rangelist, starting_index, elements_to_delete));
+
+  return SVN_NO_ERROR;
 }
 
-#if 0 /* Temporary debug helper code */
+#if 1 /* Temporary debug helper code */
 static svn_error_t *
-dual_dump(const char *prefix,
-  const svn_rangelist_t *rangelist,
-  const svn_rangelist_t *changes,
-  apr_pool_t *scratch_pool)
+dual_dump(svn_error_t *err,
+          const char *prefix,
+          const svn_rangelist_t *rangelist,
+          const svn_rangelist_t *changes,
+          apr_pool_t *scratch_pool)
 {
   svn_string_t *rls, *chg;
 
   SVN_ERR(svn_rangelist_to_string(&rls, rangelist, scratch_pool));
   SVN_ERR(svn_rangelist_to_string(&chg, changes, scratch_pool));
 
-  SVN_DBG(("%s: %s / %s", prefix, rls->data, chg->data));
-  return SVN_NO_ERROR;
+  return svn_error_quick_wrapf(err, "%s: %s / %s", prefix, rls->data, chg->data);
 }
 #endif
 
-svn_error_t *
-svn_rangelist_merge2(svn_rangelist_t *rangelist,
-                     const svn_rangelist_t *chg,
-                     apr_pool_t *result_pool,
-                     apr_pool_t *scratch_pool)
+static svn_error_t *
+rangelist_merge2(svn_rangelist_t *rangelist,
+                 const svn_rangelist_t *chg,
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
 {
   svn_rangelist_t *changes;
   int i = 0;
@@ -1000,7 +1002,7 @@
           /* No overlap, nor adjoin, copy change to result range */
           svn_merge_range_t *chg_copy = svn_merge_range_dup(change,
                                                             result_pool);
-          svn_sort__array_insert(rangelist, &chg_copy, i++);
+          SVN_ERR(svn_sort__array_insert2(rangelist, &chg_copy, i++));
           continue;
         }
       else if ((change->start > range->end)
@@ -1030,7 +1032,7 @@
           else
             range->start = change->end;
 
-          svn_sort__array_insert(rangelist, &chg_copy, i++);
+          SVN_ERR(svn_sort__array_insert2(rangelist, &chg_copy, i++));
 
           change->start = chg_copy->end;
           if (change->start >= change->end)
@@ -1065,7 +1067,7 @@
                   /* RANGE and CHANGE have the same inheritability so
                      RANGE expands to absord CHANGE. */
                   range->end = change->end;
-                  adjust_remaining_ranges(rangelist, &i, result_pool);
+                  SVN_ERR(adjust_remaining_ranges(rangelist, &i, result_pool));
                   continue;
                 }
               else
@@ -1105,7 +1107,7 @@
                         svn_merge_range_dup(range, result_pool);
                       range_copy->end = change->start;
                       range->start = change->start;
-                      svn_sort__array_insert(rangelist, &range_copy, i++);
+                      SVN_ERR(svn_sort__array_insert2(rangelist, &range_copy, i++));
                       j--;
                       continue;
                     }
@@ -1154,7 +1156,7 @@
                       /* ...but if RANGE is expanded ensure that we don't
                          violate any rangelist invariants. */
                       range->end = change->end;
-                      adjust_remaining_ranges(rangelist, &i, result_pool);
+                      SVN_ERR(adjust_remaining_ranges(rangelist, &i, result_pool));
                     }
                   continue;
                 }
@@ -1210,7 +1212,7 @@
                           range->start = change->start;
                           range->end = change->end;
                           range->inheritable = TRUE;
-                          svn_sort__array_insert(rangelist, &range_copy, ++i);
+                          SVN_ERR(svn_sort__array_insert2(rangelist, &range_copy, ++i));
                           continue;
                         }
                     }
@@ -1228,7 +1230,7 @@
                       range_copy->end = change->end;
                       range_copy->inheritable = TRUE;
                       range->start = change->end;
-                      svn_sort__array_insert(rangelist, &range_copy, i++);
+                      SVN_ERR(svn_sort__array_insert2(rangelist, &range_copy, i++));
                       continue;
                     }
                 }
@@ -1238,12 +1242,64 @@ svn_rangelist_merge2(svn_rangelist_t *ra
     }
 
 #ifdef SVN_DEBUG
-  SVN_ERR_ASSERT(svn_rangelist__is_canonical(rangelist));
+  /*SVN_ERR_ASSERT(svn_rangelist__is_canonical(rangelist));*/
 #endif
 
   return SVN_NO_ERROR;
 }
 
+static svn_boolean_t
+rangelist_is_sorted(const svn_rangelist_t *rangelist)
+{
+  int i;
+
+  for (i = 1; i < rangelist->nelts; i++)
+    {
+      const svn_merge_range_t *lastrange
+        = APR_ARRAY_IDX(rangelist, i-1, svn_merge_range_t *);
+      const svn_merge_range_t *thisrange
+        = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
+
+      if (svn_sort_compare_ranges(&lastrange, &thisrange) > 0)
+        return FALSE;
+    }
+  return TRUE;
+}
+
+svn_error_t *
+svn_rangelist_merge2(svn_rangelist_t *rangelist,
+                     const svn_rangelist_t *chg,
+                     apr_pool_t *result_pool,
+                     apr_pool_t *scratch_pool)
+{
+  svn_rangelist_t *rangelist_orig = svn_rangelist_dup(rangelist, scratch_pool);
+  svn_error_t *err;
+
+  SVN_ERR_ASSERT(rangelist_is_sorted(rangelist));
+  SVN_ERR_ASSERT(rangelist_is_sorted(chg));
+
+  err = rangelist_merge2(rangelist, chg, result_pool, scratch_pool);
+  if (err)
+    {
+      err = dual_dump(err, "svn_rangelist_merge2: internal error",
+                      rangelist, chg, scratch_pool);
+    }
+  else if (! svn_rangelist__is_canonical(rangelist)
+           && svn_rangelist__is_canonical(rangelist_orig)
+           && svn_rangelist__is_canonical(chg))
+    {
+      svn_string_t *s;
+
+      SVN_ERR(svn_rangelist_to_string(&s, rangelist, scratch_pool));
+      err = svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
+                              "non-c result ( %s ) from canonical inputs", s->data);
+      err = dual_dump(err, "svn_rangelist_merge2: canonical inputs -> non-c result",
+                      rangelist_orig, chg, scratch_pool);
+    }
+
+  return err;
+}
+
 /* Return TRUE iff the forward revision ranges FIRST and SECOND overlap and
  * (if CONSIDER_INHERITANCE is TRUE) have the same inheritability. */
 static svn_boolean_t
Index: subversion/libsvn_subr/sorts.c
===================================================================
--- subversion/libsvn_subr/sorts.c	(revision 1871324)
+++ subversion/libsvn_subr/sorts.c	(working copy)
@@ -34,6 +34,8 @@
 #include "svn_error.h"
 #include "private/svn_sorts_private.h"
 
+#include "svn_private_config.h"
+
 
 
 /*** svn_sort__hash() ***/
@@ -324,6 +326,20 @@
   memcpy(new_position, new_element, array->elt_size);
 }
 
+svn_error_t *
+svn_sort__array_insert2(apr_array_header_t *array,
+                        const void *new_element,
+                        int insert_index)
+{
+  if (insert_index < 0 || insert_index > array->nelts)
+    return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+                             _("svn_sort__array_insert2: Attempted insert "
+                               "at index %d in array length %d"),
+                             insert_index, array->nelts);
+  svn_sort__array_insert(array, new_element, insert_index);
+  return SVN_NO_ERROR;
+}
+
 void
 svn_sort__array_delete(apr_array_header_t *arr,
                        int delete_index,
@@ -349,6 +365,23 @@
     }
 }
 
+svn_error_t *
+svn_sort__array_delete2(apr_array_header_t *arr,
+                        int delete_index,
+                        int elements_to_delete)
+{
+  if (!(delete_index >= 0
+        && delete_index < arr->nelts
+        && elements_to_delete > 0
+        && (arr->nelts - delete_index) >= elements_to_delete))
+    return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
+                             _("svn_sort__array_delete2: Attempted delete "
+                               "at index %d, %d elements, in array length %d"),
+                             delete_index, elements_to_delete, arr->nelts);
+  svn_sort__array_delete(arr, delete_index, elements_to_delete);
+  return SVN_NO_ERROR;
+}
+
 void
 svn_sort__array_reverse(apr_array_header_t *array,
                         apr_pool_t *scratch_pool)
Index: subversion/svn/svn.c
===================================================================
--- subversion/svn/svn.c	(revision 1871324)
+++ subversion/svn/svn.c	(working copy)
@@ -3459,6 +3459,8 @@
     ctx->conflict_baton2 = NULL;
   }
 
+  svn_error_set_malfunction_handler(svn_error_raise_on_malfunction);
+
   /* And now we finally run the subcommand. */
   err = (*subcommand->cmd_func)(os, &command_baton, pool);
   if (err)

Reply via email to