You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ar...@apache.org on 2011/11/21 08:08:07 UTC

svn commit: r1204374 [15/29] - in /subversion/branches/svn-bisect: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/ contrib/client-side/emacs/ notes/ notes/wc-ng/ subversion/bindings/javahl/native...

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/dirent_uri.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/dirent_uri.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/dirent_uri.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/dirent_uri.c Mon Nov 21 07:07:56 2011
@@ -884,10 +884,10 @@ svn_dirent_local_style(const char *diren
 }
 
 const char *
-svn_relpath__internal_style(const char *dirent,
-                           apr_pool_t *pool)
+svn_relpath__internal_style(const char *relpath,
+                            apr_pool_t *pool)
 {
-  return svn_relpath_canonicalize(internal_style(dirent, pool), pool);
+  return svn_relpath_canonicalize(internal_style(relpath, pool), pool);
 }
 
 
@@ -1403,33 +1403,6 @@ svn_dirent_is_child(const char *parent_d
 }
 
 const char *
-svn_relpath__is_child(const char *parent_relpath,
-                      const char *child_relpath,
-                      apr_pool_t *pool)
-{
-  /* assert(relpath_is_canonical(parent_relpath)); */
-  /* assert(relpath_is_canonical(child_relpath)); */
-
-  return is_child(type_relpath, parent_relpath, child_relpath, pool);
-}
-
-const char *
-svn_uri__is_child(const char *parent_uri,
-                  const char *child_uri,
-                  apr_pool_t *pool)
-{
-  const char *relpath;
-
-  assert(svn_uri_is_canonical(parent_uri, NULL));
-  assert(svn_uri_is_canonical(child_uri, NULL));
-
-  relpath = is_child(type_uri, parent_uri, child_uri, pool);
-  if (relpath)
-    relpath = svn_path_uri_decode(relpath, pool);
-  return relpath;
-}
-
-const char *
 svn_dirent_skip_ancestor(const char *parent_dirent,
                          const char *child_dirent)
 {
@@ -1546,12 +1519,6 @@ svn_dirent_is_ancestor(const char *paren
 }
 
 svn_boolean_t
-svn_relpath__is_ancestor(const char *parent_relpath, const char *child_relpath)
-{
-  return svn_relpath_skip_ancestor(parent_relpath, child_relpath) != NULL;
-}
-
-svn_boolean_t
 svn_uri__is_ancestor(const char *parent_uri, const char *child_uri)
 {
   return uri_skip_ancestor(parent_uri, child_uri) != NULL;
@@ -1652,7 +1619,7 @@ svn_dirent_canonicalize(const char *dire
 }
 
 svn_boolean_t
-svn_dirent_is_canonical(const char *dirent, apr_pool_t *pool)
+svn_dirent_is_canonical(const char *dirent, apr_pool_t *scratch_pool)
 {
   const char *ptr = dirent;
   if (*ptr == '/')
@@ -1665,7 +1632,8 @@ svn_dirent_is_canonical(const char *dire
           /* TODO: Scan hostname and sharename and fall back to part code */
 
           /* ### Fall back to old implementation */
-          return (strcmp(dirent, svn_dirent_canonicalize(dirent, pool)) == 0);
+          return (strcmp(dirent, svn_dirent_canonicalize(dirent, scratch_pool))
+                  == 0);
         }
 #endif /* SVN_USE_DOS_PATHS */
     }
@@ -1739,7 +1707,7 @@ svn_relpath_is_canonical(const char *rel
 }
 
 svn_boolean_t
-svn_uri_is_canonical(const char *uri, apr_pool_t *pool)
+svn_uri_is_canonical(const char *uri, apr_pool_t *scratch_pool)
 {
   const char *ptr = uri, *seg = uri;
   const char *schema_data = NULL;
@@ -2458,21 +2426,6 @@ svn_fspath__is_root(const char *fspath, 
 
 
 const char *
-svn_fspath__is_child(const char *parent_fspath,
-                     const char *child_fspath,
-                     apr_pool_t *pool)
-{
-  const char *result;
-  assert(svn_fspath__is_canonical(parent_fspath));
-  assert(svn_fspath__is_canonical(child_fspath));
-
-  result = svn_relpath__is_child(parent_fspath + 1, child_fspath + 1, pool);
-
-  assert(result == NULL || svn_relpath_is_canonical(result));
-  return result;
-}
-
-const char *
 svn_fspath__skip_ancestor(const char *parent_fspath,
                           const char *child_fspath)
 {
@@ -2482,16 +2435,6 @@ svn_fspath__skip_ancestor(const char *pa
   return svn_relpath_skip_ancestor(parent_fspath + 1, child_fspath + 1);
 }
 
-svn_boolean_t
-svn_fspath__is_ancestor(const char *parent_fspath,
-                        const char *child_fspath)
-{
-  assert(svn_fspath__is_canonical(parent_fspath));
-  assert(svn_fspath__is_canonical(child_fspath));
-
-  return svn_relpath__is_ancestor(parent_fspath + 1, child_fspath + 1);
-}
-
 
 const char *
 svn_fspath__dirname(const char *fspath,

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/dso.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/dso.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/dso.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/dso.c Mon Nov 21 07:07:56 2011
@@ -26,10 +26,10 @@
 #include "svn_pools.h"
 #include "svn_private_config.h"
 
+#include "private/svn_mutex.h"
+
 /* A mutex to protect our global pool and cache. */
-#if APR_HAS_THREADS
-static apr_thread_mutex_t *dso_mutex;
-#endif
+static svn_mutex__t *dso_mutex = NULL;
 
 /* Global pool to allocate DSOs in. */
 static apr_pool_t *dso_pool;
@@ -49,40 +49,21 @@ static int not_there_sentinel;
 svn_error_t *
 svn_dso_initialize2(void)
 {
-#if APR_HAS_THREADS
-  apr_status_t status;
-#endif
   if (dso_pool)
     return SVN_NO_ERROR;
 
   dso_pool = svn_pool_create(NULL);
 
-#if APR_HAS_THREADS
-  status = apr_thread_mutex_create(&dso_mutex,
-                                   APR_THREAD_MUTEX_DEFAULT, dso_pool);
-  if (status)
-    return svn_error_wrap_apr(status, _("Can't create DSO mutex"));
-#endif
+  SVN_ERR(svn_mutex__init(&dso_mutex, TRUE, dso_pool));
 
   dso_cache = apr_hash_make(dso_pool);
   return SVN_NO_ERROR;
 }
 
 #if APR_HAS_DSO
-svn_error_t *
-svn_dso_load(apr_dso_handle_t **dso, const char *fname)
+static svn_error_t *
+svn_dso_load_internal(apr_dso_handle_t **dso, const char *fname)
 {
-  apr_status_t status;
-
-  if (! dso_pool)
-    SVN_ERR(svn_dso_initialize2());
-
-#if APR_HAS_THREADS
-  status = apr_thread_mutex_lock(dso_mutex);
-  if (status)
-    return svn_error_wrap_apr(status, _("Can't grab DSO mutex"));
-#endif
-
   *dso = apr_hash_get(dso_cache, fname, APR_HASH_KEY_STRING);
 
   /* First check to see if we've been through this before...  We do this
@@ -91,18 +72,13 @@ svn_dso_load(apr_dso_handle_t **dso, con
   if (*dso == NOT_THERE)
     {
       *dso = NULL;
-#if APR_HAS_THREADS
-      status = apr_thread_mutex_unlock(dso_mutex);
-      if (status)
-        return svn_error_wrap_apr(status, _("Can't ungrab DSO mutex"));
-#endif
       return SVN_NO_ERROR;
     }
 
   /* If we got nothing back from the cache, try and load the library. */
   if (! *dso)
     {
-      status = apr_dso_load(dso, fname, dso_pool);
+      apr_status_t status = apr_dso_load(dso, fname, dso_pool);
       if (status)
         {
 #ifdef SVN_DEBUG_DSO
@@ -120,11 +96,6 @@ svn_dso_load(apr_dso_handle_t **dso, con
                        APR_HASH_KEY_STRING,
                        NOT_THERE);
 
-#if APR_HAS_THREADS
-          status = apr_thread_mutex_unlock(dso_mutex);
-          if (status)
-            return svn_error_wrap_apr(status, _("Can't ungrab DSO mutex"));
-#endif
           return SVN_NO_ERROR;
         }
 
@@ -135,11 +106,16 @@ svn_dso_load(apr_dso_handle_t **dso, con
                    *dso);
     }
 
-#if APR_HAS_THREADS
-  status = apr_thread_mutex_unlock(dso_mutex);
-  if (status)
-    return svn_error_wrap_apr(status, _("Can't ungrab DSO mutex"));
-#endif
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_dso_load(apr_dso_handle_t **dso, const char *fname)
+{
+  if (! dso_pool)
+    SVN_ERR(svn_dso_initialize2());
+
+  SVN_MUTEX__WITH_LOCK(dso_mutex, svn_dso_load_internal(dso, fname));
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/error.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/error.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/error.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/error.c Mon Nov 21 07:07:56 2011
@@ -64,8 +64,9 @@ static const char SVN_FILE_LINE_UNDEFINE
 #undef svn_error_quick_wrap
 #undef svn_error_wrap_apr
 
-/* Note: This function was historically in the public API, so we need
- * to define it even when !SVN_DEBUG. */
+/* Note: Although this is a "__" function, it was historically in the
+ * public ABI, so we can never change it or remove its signature, even
+ * though it is now only used in SVN_DEBUG mode. */
 void
 svn_error__locate(const char *file, long line)
 {
@@ -663,6 +664,8 @@ svn_error_set_malfunction_handler(svn_er
   return old_malfunction_handler;
 }
 
+/* Note: Although this is a "__" function, it is in the public ABI, so
+ * we can never remove it or change its signature. */
 svn_error_t *
 svn_error__malfunction(svn_boolean_t can_return,
                        const char *file, int line,

Propchange: subversion/branches/svn-bisect/subversion/libsvn_subr/hash.c
            ('svn:mergeinfo' removed)

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/io.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/io.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/io.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/io.c Mon Nov 21 07:07:56 2011
@@ -2158,8 +2158,11 @@ svn_io_remove_file2(const char *path,
     }
 #endif
 
-  return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
-                            svn_dirent_local_style(path, scratch_pool));
+  if (apr_err)
+    return svn_error_wrap_apr(apr_err, _("Can't remove file '%s'"),
+                              svn_dirent_local_style(path, scratch_pool));
+
+  return SVN_NO_ERROR;
 }
 
 
@@ -2934,9 +2937,11 @@ svn_io_detect_mimetype2(const char **mim
      heuristic. */
   if (mimetype_map)
     {
-      const char *type_from_map, *path_ext;
-      svn_path_splitext(NULL, &path_ext, file, pool);
-      fileext_tolower((char *)path_ext);
+      const char *type_from_map;
+      char *path_ext; /* Can point to physical const memory but only when
+                         svn_path_splitext sets it to "". */
+      svn_path_splitext(NULL, (const char **)&path_ext, file, pool);
+      fileext_tolower(path_ext);
       if ((type_from_map = apr_hash_get(mimetype_map, path_ext,
                                         APR_HASH_KEY_STRING)))
         {
@@ -2975,6 +2980,13 @@ svn_io_is_binary_data(const void *data, 
 {
   const unsigned char *buf = data;
 
+  if (len == 3 && buf[0] == 0xEF && buf[1] == 0xBB && buf[2] == 0xBF)
+    {
+      /* This is an empty UTF-8 file which only contains the UTF-8 BOM.
+       * Treat it as plain text. */
+      return FALSE;
+    }
+
   /* Right now, this function is going to be really stupid.  It's
      going to examine the block of data, and make sure that 15%
      of the bytes are such that their value is in the ranges 0x07-0x0D

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/iter.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/iter.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/iter.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/iter.c Mon Nov 21 07:07:56 2011
@@ -176,6 +176,8 @@ svn_iter_apr_array(svn_boolean_t *comple
   return err;
 }
 
+/* Note: Although this is a "__" function, it is in the public ABI, so
+ * we can never remove it or change its signature. */
 svn_error_t *
 svn_iter__break(void)
 {

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/log.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/log.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/log.c Mon Nov 21 07:07:56 2011
@@ -121,7 +121,7 @@ svn_log__get_mergeinfo(const apr_array_h
 {
   int i;
   apr_pool_t *iterpool = svn_pool_create(pool);
-  svn_stringbuf_t *space_separated_paths = svn_stringbuf_create("", pool);
+  svn_stringbuf_t *space_separated_paths = svn_stringbuf_create_empty(pool);
 
   for (i = 0; i < paths->nelts; i++)
     {
@@ -210,8 +210,8 @@ svn_log__log(const apr_array_header_t *p
 {
   int i;
   apr_pool_t *iterpool = svn_pool_create(pool);
-  svn_stringbuf_t *space_separated_paths = svn_stringbuf_create("", pool);
-  svn_stringbuf_t *options = svn_stringbuf_create("", pool);
+  svn_stringbuf_t *space_separated_paths = svn_stringbuf_create_empty(pool);
+  svn_stringbuf_t *options = svn_stringbuf_create_empty(pool);
 
   for (i = 0; i < paths->nelts; i++)
     {
@@ -264,7 +264,7 @@ svn_log__get_locations(const char *path,
 {
   const svn_revnum_t *revision_ptr, *revision_ptr_start, *revision_ptr_end;
   apr_pool_t *iterpool = svn_pool_create(pool);
-  svn_stringbuf_t *space_separated_revnums = svn_stringbuf_create("", pool);
+  svn_stringbuf_t *space_separated_revnums = svn_stringbuf_create_empty(pool);
 
   revision_ptr_start = (const svn_revnum_t *)location_revisions->elts;
   revision_ptr = revision_ptr_start;
@@ -311,7 +311,7 @@ svn_log__lock(const apr_array_header_t *
 {
   int i;
   apr_pool_t *iterpool = svn_pool_create(pool);
-  svn_stringbuf_t *space_separated_paths = svn_stringbuf_create("", pool);
+  svn_stringbuf_t *space_separated_paths = svn_stringbuf_create_empty(pool);
 
   for (i = 0; i < paths->nelts; i++)
     {
@@ -334,7 +334,7 @@ svn_log__unlock(const apr_array_header_t
 {
   int i;
   apr_pool_t *iterpool = svn_pool_create(pool);
-  svn_stringbuf_t *space_separated_paths = svn_stringbuf_create("", pool);
+  svn_stringbuf_t *space_separated_paths = svn_stringbuf_create_empty(pool);
 
   for (i = 0; i < paths->nelts; i++)
     {

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/magic.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/magic.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/magic.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/magic.c Mon Nov 21 07:07:56 2011
@@ -45,8 +45,9 @@
 struct svn_magic__cookie_t {
 #ifdef SVN_HAVE_LIBMAGIC
   magic_t magic;
-#endif
+#else
   char dummy;
+#endif
 };
 
 #ifdef SVN_HAVE_LIBMAGIC

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/mergeinfo.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/mergeinfo.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/mergeinfo.c Mon Nov 21 07:07:56 2011
@@ -78,7 +78,7 @@ combine_ranges(svn_merge_range_t *output
 static svn_error_t *
 parse_pathname(const char **input,
                const char *end,
-               svn_stringbuf_t **pathname,
+               const char **pathname,
                apr_pool_t *pool)
 {
   const char *curr = *input;
@@ -101,17 +101,11 @@ parse_pathname(const char **input,
     return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
                             _("No pathname preceding ':'"));
 
-  /* Tolerate relative repository paths, but convert them to absolute. */
-  if (**input == '/')
-    {
-      *pathname = svn_stringbuf_ncreate(*input, last_colon - *input, pool);
-    }
-  else
-    {
-      const char *repos_rel_path = apr_pstrndup(pool, *input,
-                                                last_colon - *input);
-      *pathname = svn_stringbuf_createf(pool, "/%s",  repos_rel_path);
-    }
+  /* Tolerate relative repository paths, but convert them to absolute.
+     ### Efficiency?  1 string duplication here, 2 in canonicalize. */
+  *pathname = svn_fspath__canonicalize(apr_pstrndup(pool, *input,
+                                                    last_colon - *input),
+                                       pool);
 
   *input = last_colon;
 
@@ -245,15 +239,13 @@ get_type_of_intersection(const svn_merge
 
    When replacing the last range in RANGELIST, either allocate a new range in
    RESULT_POOL or modify the existing range in place.  Any new ranges added
-   to RANGELIST are allocated in RESULT_POOL.  SCRATCH_POOL is used for any
-   temporary allocations.
+   to RANGELIST are allocated in RESULT_POOL.
 */
 static svn_error_t *
 combine_with_lastrange(const svn_merge_range_t *new_range,
                        apr_array_header_t *rangelist,
                        svn_boolean_t consider_inheritance,
-                       apr_pool_t *result_pool,
-                       apr_pool_t *scratch_pool)
+                       apr_pool_t *result_pool)
 {
   svn_merge_range_t *lastrange;
   svn_merge_range_t combined_range;
@@ -301,143 +293,168 @@ combine_with_lastrange(const svn_merge_r
              intersect but have differing inheritability.  Check for the
              first case as that is easy to handle. */
           intersection_type_t intersection_type;
+          svn_boolean_t sorted = FALSE;
 
           SVN_ERR(get_type_of_intersection(new_range, lastrange,
                                            &intersection_type));
 
-              switch (intersection_type)
+          switch (intersection_type)
+            {
+              case svn__no_intersection:
+                /* NEW_RANGE and *LASTRANGE *really* don't intersect so
+                   just push NEW_RANGE only RANGELIST. */
+                APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
+                  svn_merge_range_dup(new_range, result_pool);
+                sorted = (svn_sort_compare_ranges(&lastrange,
+                                                  &new_range) < 0);
+                break;
+
+              case svn__equal_intersection:
+                /* They range are equal so all we do is force the
+                   inheritability of lastrange to true. */
+                lastrange->inheritable = TRUE;
+                sorted = TRUE;
+                break;
+
+              case svn__adjoining_intersection:
+                /* They adjoin but don't overlap so just push NEW_RANGE
+                   onto RANGELIST. */
+                APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
+                  svn_merge_range_dup(new_range, result_pool);
+                sorted = (svn_sort_compare_ranges(&lastrange,
+                                                  &new_range) < 0);
+                break;
+
+              case svn__overlapping_intersection:
+                /* They ranges overlap but neither is a proper subset of
+                   the other.  We'll end up pusing two new ranges onto
+                   RANGELIST, the intersecting part and the part unique to
+                   NEW_RANGE.*/
                 {
-                  case svn__no_intersection:
-                    /* NEW_RANGE and *LASTRANGE *really* don't intersect so
-                       just push NEW_RANGE only RANGELIST. */
-                    APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
-                      svn_merge_range_dup(new_range, result_pool);
-                    break;
-
-                  case svn__equal_intersection:
-                    /* They range are equal so all we do is force the
-                       inheritability of lastrange to true. */
-                    lastrange->inheritable = TRUE;
-                    break;
-
-                  case svn__adjoining_intersection:
-                    /* They adjoin but don't overlap so just push NEW_RANGE
-                       onto RANGELIST. */
-                    APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) =
-                      svn_merge_range_dup(new_range, result_pool);
-                    break;
-
-                  case svn__overlapping_intersection:
-                    /* They ranges overlap but neither is a proper subset of
-                       the other.  We'll end up pusing two new ranges onto
-                       RANGELIST, the intersecting part and the part unique to
-                       NEW_RANGE.*/
+                  svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
+                                                              result_pool);
+                  svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
+                                                              result_pool);
+
+                  /* Pop off *LASTRANGE to make our manipulations
+                     easier. */
+                  apr_array_pop(rangelist);
+
+                  /* Ensure R1 is the older range. */
+                  if (r2->start < r1->start)
                     {
-                      svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
-                                                                  result_pool);
-                      svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
-                                                                  result_pool);
-
-                      /* Pop off *LASTRANGE to make our manipulations
-                         easier. */
-                      apr_array_pop(rangelist);
+                      /* Swap R1 and R2. */
+                      *r2 = *r1;
+                      *r1 = *new_range;
+                    }
 
-                      /* Ensure R1 is the older range. */
-                      if (r2->start < r1->start)
-                        {
-                          /* Swap R1 and R2. */
-                          *r2 = *r1;
-                          *r1 = *new_range;
-                        }
+                  /* Absorb the intersecting ranges into the
+                     inheritable range. */
+                  if (r1->inheritable)
+                    r2->start = r1->end;
+                  else
+                    r1->end = r2->start;
 
-                      /* Absorb the intersecting ranges into the
-                         inheritable range. */
-                      if (r1->inheritable)
-                        r2->start = r1->end;
-                      else
-                        r1->end = r2->start;
+                  /* Push everything back onto RANGELIST. */
+                  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
+                  sorted = (svn_sort_compare_ranges(&lastrange,
+                                                    &r1) < 0);
+                  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
+                  if (sorted)
+                    sorted = (svn_sort_compare_ranges(&r1, &r2) < 0);
+                  break;
+                }
 
-                      /* Push everything back onto RANGELIST. */
-                      APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
-                      APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
+              default: /* svn__proper_subset_intersection */
+                {
+                  /* One range is a proper subset of the other. */
+                  svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
+                                                              result_pool);
+                  svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
+                                                              result_pool);
+                  svn_merge_range_t *r3 = NULL;
+
+                  /* Pop off *LASTRANGE to make our manipulations
+                     easier. */
+                  apr_array_pop(rangelist);
 
-                      break;
+                  /* Ensure R1 is the superset. */
+                  if (r2->start < r1->start || r2->end > r1->end)
+                    {
+                      /* Swap R1 and R2. */
+                      *r2 = *r1;
+                      *r1 = *new_range;
                     }
 
-                  default: /* svn__proper_subset_intersection */
+                  if (r1->inheritable)
                     {
-                      /* One range is a proper subset of the other. */
-                      svn_merge_range_t *r1 = svn_merge_range_dup(lastrange,
-                                                                  result_pool);
-                      svn_merge_range_t *r2 = svn_merge_range_dup(new_range,
-                                                                  result_pool);
-                      svn_merge_range_t *r3 = NULL;
-
-                      /* Pop off *LASTRANGE to make our manipulations
-                         easier. */
-                      apr_array_pop(rangelist);
-
-                      /* Ensure R1 is the superset. */
-                      if (r2->start < r1->start || r2->end > r1->end)
-                        {
-                          /* Swap R1 and R2. */
-                          *r2 = *r1;
-                          *r1 = *new_range;
-                        }
+                      /* The simple case: The superset is inheritable, so
+                         just combine r1 and r2. */
+                      r1->start = MIN(r1->start, r2->start);
+                      r1->end = MAX(r1->end, r2->end);
+                      r2 = NULL;
+                    }
+                  else if (r1->start == r2->start)
+                    {
+                      svn_revnum_t tmp_revnum;
 
-                      if (r1->inheritable)
-                        {
-                          /* The simple case: The superset is inheritable, so
-                             just combine r1 and r2. */
-                          r1->start = MIN(r1->start, r2->start);
-                          r1->end = MAX(r1->end, r2->end);
-                          r2 = NULL;
-                        }
-                      else if (r1->start == r2->start)
-                        {
-                          svn_revnum_t tmp_revnum;
+                      /* *LASTRANGE and NEW_RANGE share an end point. */
+                      tmp_revnum = r1->end;
+                      r1->end = r2->end;
+                      r2->inheritable = r1->inheritable;
+                      r1->inheritable = TRUE;
+                      r2->start = r1->end;
+                      r2->end = tmp_revnum;
+                    }
+                  else if (r1->end == r2->end)
+                    {
+                      /* *LASTRANGE and NEW_RANGE share an end point. */
+                      r1->end = r2->start;
+                      r2->inheritable = TRUE;
+                    }
+                  else
+                    {
+                      /* NEW_RANGE and *LASTRANGE share neither start
+                         nor end points. */
+                      r3 = apr_pcalloc(result_pool, sizeof(*r3));
+                      r3->start = r2->end;
+                      r3->end = r1->end;
+                      r3->inheritable = r1->inheritable;
+                      r2->inheritable = TRUE;
+                      r1->end = r2->start;
+                    }
 
-                          /* *LASTRANGE and NEW_RANGE share an end point. */
-                          tmp_revnum = r1->end;
-                          r1->end = r2->end;
-                          r2->inheritable = r1->inheritable;
-                          r1->inheritable = TRUE;
-                          r2->start = r1->end;
-                          r2->end = tmp_revnum;
-                        }
-                      else if (r1->end == r2->end)
-                        {
-                          /* *LASTRANGE and NEW_RANGE share an end point. */
-                          r1->end = r2->start;
-                          r2->inheritable = TRUE;
-                        }
-                      else
+                  /* Push everything back onto RANGELIST. */
+                  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
+                  sorted = (svn_sort_compare_ranges(&lastrange, &r1) < 0);
+                  if (r2)
+                    {
+                      APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
+                      if (sorted)
+                        sorted = (svn_sort_compare_ranges(&r1, &r2) < 0);
+                    }
+                  if (r3)
+                    {
+                      APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r3;
+                      if (sorted)
                         {
-                          /* NEW_RANGE and *LASTRANGE share neither start
-                             nor end points. */
-                          r3 = apr_pcalloc(result_pool, sizeof(*r3));
-                          r3->start = r2->end;
-                          r3->end = r1->end;
-                          r3->inheritable = r1->inheritable;
-                          r2->inheritable = TRUE;
-                          r1->end = r2->start;
+                          if (r2)
+                            sorted = (svn_sort_compare_ranges(&r2,
+                                                              &r3) < 0);
+                          else
+                            sorted = (svn_sort_compare_ranges(&r1,
+                                                              &r3) < 0);
                         }
-
-                      /* Push everything back onto RANGELIST. */
-                      APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r1;
-                      if (r2)
-                        APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r2;
-                      if (r3)
-                        APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = r3;
-
-                      break;
                     }
+                  break;
                 }
+            }
 
-              /* Some of the above cases might have put *RANGELIST out of
-                 order, so re-sort.*/
-              qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size,
-                    svn_sort_compare_ranges);
+          /* Some of the above cases might have put *RANGELIST out of
+             order, so re-sort.*/
+          if (!sorted)
+            qsort(rangelist->elts, rangelist->nelts, rangelist->elt_size,
+                  svn_sort_compare_ranges);
         }
     }
 
@@ -471,13 +488,10 @@ range_to_string(const svn_merge_range_t 
    revisionlist -> (revisionelement)(COMMA revisionelement)*
    revisionrange -> REVISION "-" REVISION("*")
    revisionelement -> revisionrange | REVISION("*")
-
-   PATHNAME is the path this revisionlist is mapped to.  It is
-   used only for producing a more descriptive error message.
 */
 static svn_error_t *
 parse_rangelist(const char **input, const char *end,
-                apr_array_header_t *rangelist, const char *pathname,
+                apr_array_header_t *rangelist,
                 apr_pool_t *pool)
 {
   const char *curr = *input;
@@ -490,9 +504,7 @@ parse_rangelist(const char **input, cons
     {
       /* Empty range list. */
       *input = curr;
-      return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
-                               _("Mergeinfo for '%s' maps to an "
-                                 "empty revision range"), pathname);
+      return SVN_NO_ERROR;
     }
 
   while (curr < end && *curr != '\n')
@@ -586,17 +598,29 @@ parse_rangelist(const char **input, cons
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_rangelist__parse(apr_array_header_t **rangelist,
+                     const char *str,
+                     apr_pool_t *result_pool)
+{
+  const char *s = str;
+
+  *rangelist = apr_array_make(result_pool, 1, sizeof(svn_merge_range_t *));
+  SVN_ERR(parse_rangelist(&s, s + strlen(s), *rangelist, result_pool));
+  return SVN_NO_ERROR;
+}
+
 /* revisionline -> PATHNAME COLON revisionlist */
 static svn_error_t *
 parse_revision_line(const char **input, const char *end, svn_mergeinfo_t hash,
-                    apr_pool_t *pool)
+                    apr_pool_t *scratch_pool)
 {
-  svn_stringbuf_t *pathname;
+  const char *pathname;
   apr_array_header_t *existing_rangelist;
-  apr_array_header_t *rangelist = apr_array_make(pool, 1,
+  apr_array_header_t *rangelist = apr_array_make(scratch_pool, 1,
                                                  sizeof(svn_merge_range_t *));
 
-  SVN_ERR(parse_pathname(input, end, &pathname, pool));
+  SVN_ERR(parse_pathname(input, end, &pathname, scratch_pool));
 
   if (*(*input) != ':')
     return svn_error_create(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
@@ -604,8 +628,12 @@ parse_revision_line(const char **input, 
 
   *input = *input + 1;
 
-  SVN_ERR(parse_rangelist(input, end, rangelist, pathname->data, pool));
+  SVN_ERR(parse_rangelist(input, end, rangelist, scratch_pool));
 
+  if (rangelist->nelts == 0)
+      return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
+                               _("Mergeinfo for '%s' maps to an "
+                                 "empty revision range"), pathname);
   if (*input != end && *(*input) != '\n')
     return svn_error_createf(SVN_ERR_MERGEINFO_PARSE_ERROR, NULL,
                              _("Could not find end of line in range list line "
@@ -643,8 +671,10 @@ parse_revision_line(const char **input, 
                                              "revision ranges '%s' and '%s' "
                                              "with different inheritance "
                                              "types"),
-                                           range_to_string(lastrange, pool),
-                                           range_to_string(range, pool));
+                                           range_to_string(lastrange,
+                                                           scratch_pool),
+                                           range_to_string(range,
+                                                           scratch_pool));
                 }
 
               /* Combine overlapping or adjacent ranges with the
@@ -670,11 +700,14 @@ parse_revision_line(const char **input, 
      leading slash, e.g. "trunk:4033\n/trunk:4039-4995".  In the event
      we encounter this we merge the rangelists together under a single
      absolute path key. */
-  existing_rangelist = apr_hash_get(hash, pathname->data, APR_HASH_KEY_STRING);
+  existing_rangelist = apr_hash_get(hash, pathname, APR_HASH_KEY_STRING);
   if (existing_rangelist)
-    SVN_ERR(svn_rangelist_merge2(rangelist, existing_rangelist, pool, pool));
+    SVN_ERR(svn_rangelist_merge2(rangelist, existing_rangelist,
+                                 scratch_pool, scratch_pool));
 
-  apr_hash_set(hash, pathname->data, APR_HASH_KEY_STRING, rangelist);
+  apr_hash_set(hash, apr_pstrdup(apr_hash_pool_get(hash), pathname),
+               APR_HASH_KEY_STRING,
+               svn_rangelist_dup(rangelist, apr_hash_pool_get(hash)));
 
   return SVN_NO_ERROR;
 }
@@ -684,8 +717,14 @@ static svn_error_t *
 parse_top(const char **input, const char *end, svn_mergeinfo_t hash,
           apr_pool_t *pool)
 {
+  apr_pool_t *iterpool = svn_pool_create(pool);
+
   while (*input < end)
-    SVN_ERR(parse_revision_line(input, end, hash, pool));
+    {
+      svn_pool_clear(iterpool);
+      SVN_ERR(parse_revision_line(input, end, hash, iterpool));
+    }
+  svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
@@ -708,73 +747,401 @@ svn_mergeinfo_parse(svn_mergeinfo_t *mer
   return err;
 }
 
+/* Cleanup after svn_rangelist_merge2 when it modifies the ending range of
+   a single rangelist element in-place.
+
+   If *RANGE_INDEX is not a valid element in RANGELIST do nothing.  Otherwise
+   ensure that RANGELIST[*RANGE_INDEX]->END does not adjoin or overlap any
+   subsequent ranges in RANGELIST.
+
+   If overlap is found, then remove, modify, and/or add elements to RANGELIST
+   as per the invariants for rangelists documented in svn_mergeinfo.h.  If
+   RANGELIST[*RANGE_INDEX]->END adjoins a subsequent element then combine the
+   elements if their inheritability permits -- The inheritance of intersecting
+   and adjoining ranges is handled as per svn_mergeinfo_merge2.  Upon return
+   set *RANGE_INDEX to the index of the youngest element modified, added, or
+   adjoined to RANGELIST[*RANGE_INDEX].
+
+   Note: Adjoining rangelist elements are those where the end rev of the older
+   element is equal to the start rev of the younger element.
+
+   Any new elements inserted into RANGELIST are allocated in  RESULT_POOL.*/
+static void
+adjust_remaining_ranges(apr_array_header_t *rangelist,
+                        int *range_index,
+                        apr_pool_t *result_pool)
+{
+  int i;
+  int starting_index;
+  int elements_to_delete = 0;
+  svn_merge_range_t *modified_range;
+
+  if (*range_index >= rangelist->nelts)
+    return;
+
+  starting_index = *range_index + 1;
+  modified_range = APR_ARRAY_IDX(rangelist, *range_index, svn_merge_range_t *);
+
+  for (i = *range_index + 1; i < rangelist->nelts; i++)
+    {
+      svn_merge_range_t *next_range = APR_ARRAY_IDX(rangelist, i,
+                                                    svn_merge_range_t *);
+
+      /* If MODIFIED_RANGE doesn't adjoin or overlap the next range in
+         RANGELIST then we are finished. */
+      if (modified_range->end < next_range->start)
+        break;
+
+      /* Does MODIFIED_RANGE adjoin NEXT_RANGE? */
+      if (modified_range->end == next_range->start)
+        {
+          if (modified_range->inheritable == next_range->inheritable)
+            {
+              /* Combine adjoining ranges with the same inheritability. */
+              modified_range->end = next_range->end;
+              elements_to_delete++;
+            }
+          else
+            {
+              /* Cannot join because inheritance differs. */
+              (*range_index)++;
+            }
+          break;
+        }
+
+      /* Alright, we know MODIFIED_RANGE overlaps NEXT_RANGE, but how? */
+      if (modified_range->end > next_range->end)
+        {
+          /* NEXT_RANGE is a proper subset of MODIFIED_RANGE and the two
+             don't share the same end range. */
+          if (modified_range->inheritable
+              || (modified_range->inheritable == next_range->inheritable))
+            {
+              /* MODIFIED_RANGE absorbs NEXT_RANGE. */
+              elements_to_delete++;
+            }
+          else
+            {
+              /* NEXT_RANGE is a proper subset MODIFIED_RANGE but
+                 MODIFIED_RANGE is non-inheritable and NEXT_RANGE is
+                 inheritable.  This means MODIFIED_RANGE is truncated,
+                 NEXT_RANGE remains, and the portion of MODIFIED_RANGE
+                 younger than NEXT_RANGE is added as a separate range:
+                  ______________________________________________
+                 |                                              |
+                 M                 MODIFIED_RANGE               N
+                 |                 (!inhertiable)               |
+                 |______________________________________________|
+                                  |              |
+                                  O  NEXT_RANGE  P
+                                  | (inheritable)|
+                                  |______________|
+                                         |
+                                         V
+                  _______________________________________________
+                 |                |              |               |
+                 M MODIFIED_RANGE O  NEXT_RANGE  P   NEW_RANGE   N
+                 | (!inhertiable) | (inheritable)| (!inheritable)|
+                 |________________|______________|_______________|
+              */
+              svn_merge_range_t *new_modified_range =
+                apr_palloc(result_pool, sizeof(*new_modified_range));
+              new_modified_range->start = next_range->end;
+              new_modified_range->end = modified_range->end;
+              new_modified_range->inheritable = FALSE;
+              modified_range->end = next_range->start;
+              (*range_index)+=2;
+              svn_sort__array_insert(&new_modified_range, rangelist,
+                                     *range_index);
+              /* Recurse with the new range. */
+              adjust_remaining_ranges(rangelist, range_index, result_pool);
+              break;
+            }
+        }
+      else if (modified_range->end == next_range->end)
+        {
+          /* NEXT_RANGE is a proper subset MODIFIED_RANGE and share
+             the same end range. */
+          if (modified_range->inheritable
+              || (modified_range->inheritable == next_range->inheritable))
+            {
+              /* MODIFIED_RANGE absorbs NEXT_RANGE. */
+              elements_to_delete++;
+            }
+          else
+            {
+              /* The intersection between MODIFIED_RANGE and NEXT_RANGE is
+                 absorbed by the latter. */
+              modified_range->end = next_range->start;
+              (*range_index)++;
+            }
+          break;
+        }
+      else
+        {
+          /* NEXT_RANGE and MODIFIED_RANGE intersect but NEXT_RANGE is not
+             a proper subset of MODIFIED_RANGE, nor do the two share the
+             same end revision, i.e. they overlap. */
+          if (modified_range->inheritable == next_range->inheritable)
+            {
+              /* Combine overlapping ranges with the same inheritability. */
+              modified_range->end = next_range->end;
+              elements_to_delete++;
+            }
+          else if (modified_range->inheritable)
+            {
+              /* MODIFIED_RANGE absorbs the portion of NEXT_RANGE it overlaps
+                 and NEXT_RANGE is truncated. */
+              next_range->start = modified_range->end;
+              (*range_index)++;
+            }
+          else
+            {
+              /* NEXT_RANGE absorbs the portion of MODIFIED_RANGE it overlaps
+                 and MODIFIED_RANGE is truncated. */
+              modified_range->end = next_range->start;
+              (*range_index)++;
+            }
+          break;
+        }
+    }
+
+  if (elements_to_delete)
+    svn_sort__array_delete(rangelist, starting_index, elements_to_delete);
+}
+
 svn_error_t *
 svn_rangelist_merge2(apr_array_header_t *rangelist,
                      const apr_array_header_t *changes,
                      apr_pool_t *result_pool,
                      apr_pool_t *scratch_pool)
 {
-  int i, j;
-  apr_array_header_t *original_rangelist;
-  
-  original_rangelist = apr_array_copy(scratch_pool, rangelist);
-  apr_array_clear(rangelist);
+  int i = 0;
+  int j = 0;
 
-  i = 0;
-  j = 0;
-  while (i < (original_rangelist)->nelts && j < changes->nelts)
-    {
-      svn_merge_range_t *elt1, *elt2;
-      int res;
+  /* We may modify CHANGES, so make a copy in SCRATCH_POOL. */
+  changes = svn_rangelist_dup(changes, scratch_pool);
 
-      elt1 = APR_ARRAY_IDX(original_rangelist, i, svn_merge_range_t *);
-      elt2 = APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
+  while (i < rangelist->nelts && j < changes->nelts)
+    {
+      svn_merge_range_t *range =
+        APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
+      svn_merge_range_t *change =
+        APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
+      int res = svn_sort_compare_ranges(&range, &change);
 
-      res = svn_sort_compare_ranges(&elt1, &elt2);
       if (res == 0)
         {
           /* Only when merging two non-inheritable ranges is the result also
              non-inheritable.  In all other cases ensure an inheritiable
              result. */
-          if (elt1->inheritable || elt2->inheritable)
-            elt1->inheritable = TRUE;
-          SVN_ERR(combine_with_lastrange(elt1, rangelist, TRUE, result_pool,
-                                         scratch_pool));
+          if (range->inheritable || change->inheritable)
+            range->inheritable = TRUE;
           i++;
           j++;
         }
-      else if (res < 0)
+      else if (res < 0) /* CHANGE is younger than RANGE */
         {
-          SVN_ERR(combine_with_lastrange(elt1, rangelist, TRUE, result_pool,
-                                         scratch_pool));
-          i++;
+          if (range->end < change->start)
+            {
+              /* RANGE is older than CHANGE and the two do not
+                 adjoin or overlap */
+              i++;
+            }
+          else if (range->end == change->start)
+            {
+              /* RANGE and CHANGE adjoin */
+              if (range->inheritable == change->inheritable)
+                {
+                  /* RANGE and CHANGE have the same inheritability so
+                     RANGE expands to absord CHANGE. */
+                  range->end = change->end;
+                  adjust_remaining_ranges(rangelist, &i, result_pool);
+                  j++;
+                }
+              else
+                {
+                  /* RANGE and CHANGE adjoin, but have different
+                     inheritability.  Since RANGE is older, just
+                     move on to the next RANGE. */
+                  i++;
+                }
+            }
+          else
+            {
+              /* RANGE and CHANGE overlap, but how? */
+              if ((range->inheritable == change->inheritable)
+                  || range->inheritable)
+                {
+                  /* If CHANGE is a proper subset of RANGE, it absorbs RANGE
+                      with no adjustment otherwise only the intersection is
+                      absorbed and CHANGE is truncated. */
+                  if (range->end >= change->end)
+                    j++;
+                  else
+                    change->start = range->end;
+                }
+              else
+                {
+                  /* RANGE is non-inheritable and CHANGE is inheritable. */
+                  if (range->start < change->start)
+                    {
+                      /* CHANGE absorbs intersection with RANGE and RANGE
+                         is truncated. */
+                      svn_merge_range_t *range_copy =
+                        svn_merge_range_dup(range, result_pool);
+                      range_copy->end = change->start;
+                      range->start = change->start;
+                      svn_sort__array_insert(&range_copy, rangelist, i++);
+                    }
+                  else
+                    {
+                      /* CHANGE and RANGE share the same start rev, but
+                         RANGE is considered older because its end rev
+                         is older. */
+                      range->inheritable = TRUE;
+                      change->start = range->end;
+                    }
+                }
+            }
         }
-      else
+      else /* res > 0, CHANGE is older than RANGE */
         {
-          SVN_ERR(combine_with_lastrange(elt2, rangelist, TRUE, result_pool,
-                                         scratch_pool));
-          j++;
+          if (change->end < range->start)
+            {
+              /* CHANGE is older than RANGE and the two do not
+                 adjoin or overlap, so insert a copy of CHANGE
+                 into RANGELIST. */
+              svn_merge_range_t *change_copy =
+                svn_merge_range_dup(change, result_pool);
+              svn_sort__array_insert(&change_copy, rangelist, i++);
+              j++;
+            }
+          else if (change->end == range->start)
+            {
+              /* RANGE and CHANGE adjoin */
+              if (range->inheritable == change->inheritable)
+                {
+                  /* RANGE and CHANGE have the same inheritability so we
+                     can simply combine the two in place. */
+                  range->start = change->start;
+                  j++;
+                }
+              else
+                {
+                  /* RANGE and CHANGE have different inheritability so insert
+                     a copy of CHANGE into RANGELIST. */
+                  svn_merge_range_t *change_copy =
+                    svn_merge_range_dup(change, result_pool);
+                  svn_sort__array_insert(&change_copy, rangelist, i);
+                  j++;
+                }
+            }
+          else
+            {
+              /* RANGE and CHANGE overlap. */
+              if (range->inheritable == change->inheritable)
+                {
+                  /* RANGE and CHANGE have the same inheritability so we
+                     can simply combine the two in place... */
+                  range->start = change->start;
+                  if (range->end < change->end)
+                    {
+                      /* ...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);
+                    }
+                  j++;
+                }
+              else if (range->inheritable)
+                {
+                  if (change->start < range->start)
+                    {
+                      /* RANGE is inheritable so absorbs any part of CHANGE
+                         it overlaps.  CHANGE is truncated and the remainder
+                         inserted into RANGELIST. */
+                      svn_merge_range_t *change_copy =
+                        svn_merge_range_dup(change, result_pool);
+                      change_copy->end = range->start;
+                      change->start = range->start;
+                      svn_sort__array_insert(&change_copy, rangelist, i++);
+                    }
+                  else
+                    {
+                      /* CHANGE and RANGE share the same start rev, but
+                         CHANGE is considered older because CHANGE->END is
+                         older than RANGE->END. */
+                      j++;
+                    }
+                }
+              else
+                {
+                  /* RANGE is non-inheritable and CHANGE is inheritable. */
+                  if (change->start < range->start)
+                    {
+                      if (change->end == range->end)
+                        {
+                          /* RANGE is a proper subset of CHANGE and share the
+                             same end revision, so set RANGE equal to CHANGE. */
+                          range->start = change->start;
+                          range->inheritable = TRUE;
+                          j++;
+                        }
+                      else if (change->end > range->end)
+                        {
+                          /* RANGE is a proper subset of CHANGE and CHANGE has
+                             a younger end revision, so set RANGE equal to its
+                             intersection with CHANGE and truncate CHANGE. */
+                          range->start = change->start;
+                          range->inheritable = TRUE;
+                          change->start = range->end;
+                        }
+                      else
+                        {
+                          /* CHANGE and RANGE overlap. Set RANGE equal to its
+                             intersection with CHANGE and take the remainder
+                             of RANGE and insert it into RANGELIST. */
+                          svn_merge_range_t *range_copy =
+                            svn_merge_range_dup(range, result_pool);
+                          range_copy->start = change->end;
+                          range->start = change->start;
+                          range->end = change->end;
+                          range->inheritable = TRUE;
+                          svn_sort__array_insert(&range_copy, rangelist, ++i);
+                          j++;
+                        }
+                    }
+                  else 
+                    {
+                      /* CHANGE and RANGE share the same start rev, but
+                         CHANGE is considered older because its end rev
+                         is older.
+                         
+                         Insert the intersection of RANGE and CHANGE into
+                         RANGELIST and then set RANGE to the non-intersecting
+                         portion of RANGE. */
+                      svn_merge_range_t *range_copy =
+                        svn_merge_range_dup(range, result_pool);
+                      range_copy->end = change->end;
+                      range_copy->inheritable = TRUE;
+                      range->start = change->end;
+                      svn_sort__array_insert(&range_copy, rangelist, i++);
+                      j++;
+                    }
+                }
+            }
         }
     }
-  /* Copy back any remaining elements.
-     Only one of these loops should end up running, if anything. */
-
-  SVN_ERR_ASSERT(!(i < (original_rangelist)->nelts && j < changes->nelts));
-
-  for (; i < (original_rangelist)->nelts; i++)
-    {
-      svn_merge_range_t *elt = APR_ARRAY_IDX(original_rangelist, i,
-                                             svn_merge_range_t *);
-      SVN_ERR(combine_with_lastrange(elt, rangelist, TRUE, result_pool,
-                                     scratch_pool));
-    }
 
-
-  for (; j < changes->nelts; j++)
+  /* Copy any remaining elements in CHANGES into RANGELIST. */
+  for (; j < (changes)->nelts; j++)
     {
-      svn_merge_range_t *elt = APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
-      SVN_ERR(combine_with_lastrange(elt, rangelist,
-                                     TRUE, result_pool, scratch_pool));
+      svn_merge_range_t *change =
+        APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
+      svn_merge_range_t *change_copy = svn_merge_range_dup(change,
+                                                           result_pool);
+      svn_sort__array_insert(&change_copy, rangelist, rangelist->nelts);
     }
 
   return SVN_NO_ERROR;
@@ -972,7 +1339,7 @@ rangelist_intersect_or_remove(apr_array_
               tmp_range.inheritable =
                 (elt2->inheritable || elt1->inheritable);
               SVN_ERR(combine_with_lastrange(&tmp_range, *output,
-                                             consider_inheritance, pool,
+                                             consider_inheritance,
                                              pool));
             }
 
@@ -1009,7 +1376,7 @@ rangelist_intersect_or_remove(apr_array_
 
               SVN_ERR(combine_with_lastrange(&tmp_range,
                                              *output, consider_inheritance,
-                                             pool, pool));
+                                             pool));
             }
 
           /* Set up the rest of the rangelist2 range for further
@@ -1030,7 +1397,7 @@ rangelist_intersect_or_remove(apr_array_
                   SVN_ERR(combine_with_lastrange(&tmp_range,
                                                  *output,
                                                  consider_inheritance,
-                                                 pool, pool));
+                                                 pool));
                 }
 
               working_elt2.start = elt1->end;
@@ -1082,7 +1449,7 @@ rangelist_intersect_or_remove(apr_array_
       if (i2 == lasti2 && i2 < rangelist2->nelts)
         {
           SVN_ERR(combine_with_lastrange(&working_elt2, *output,
-                                         consider_inheritance, pool, pool));
+                                         consider_inheritance, pool));
           i2++;
         }
 
@@ -1093,7 +1460,7 @@ rangelist_intersect_or_remove(apr_array_
                                                  svn_merge_range_t *);
 
           SVN_ERR(combine_with_lastrange(elt, *output,
-                                         consider_inheritance, pool, pool));
+                                         consider_inheritance, pool));
         }
     }
 
@@ -1242,31 +1609,32 @@ walk_mergeinfo_hash_for_diff(svn_mergein
 }
 
 svn_error_t *
-svn_mergeinfo_diff(svn_mergeinfo_t *deleted, svn_mergeinfo_t *added,
-                   svn_mergeinfo_t from, svn_mergeinfo_t to,
-                   svn_boolean_t consider_inheritance,
-                   apr_pool_t *pool)
+svn_mergeinfo_diff2(svn_mergeinfo_t *deleted, svn_mergeinfo_t *added,
+                    svn_mergeinfo_t from, svn_mergeinfo_t to,
+                    svn_boolean_t consider_inheritance,
+                    apr_pool_t *result_pool,
+                    apr_pool_t *scratch_pool)
 {
   if (from && to == NULL)
     {
-      *deleted = svn_mergeinfo_dup(from, pool);
-      *added = apr_hash_make(pool);
+      *deleted = svn_mergeinfo_dup(from, result_pool);
+      *added = apr_hash_make(result_pool);
     }
   else if (from == NULL && to)
     {
-      *deleted = apr_hash_make(pool);
-      *added = svn_mergeinfo_dup(to, pool);
+      *deleted = apr_hash_make(result_pool);
+      *added = svn_mergeinfo_dup(to, result_pool);
     }
   else
     {
-      *deleted = apr_hash_make(pool);
-      *added = apr_hash_make(pool);
+      *deleted = apr_hash_make(result_pool);
+      *added = apr_hash_make(result_pool);
 
       if (from && to)
         {
           SVN_ERR(walk_mergeinfo_hash_for_diff(from, to, *deleted, *added,
-                                               consider_inheritance, pool,
-                                               pool));
+                                               consider_inheritance,
+                                               result_pool, scratch_pool));
         }
     }
 
@@ -1283,8 +1651,8 @@ svn_mergeinfo__equals(svn_boolean_t *is_
   if (apr_hash_count(info1) == apr_hash_count(info2))
     {
       svn_mergeinfo_t deleted, added;
-      SVN_ERR(svn_mergeinfo_diff(&deleted, &added, info1, info2,
-                                 consider_inheritance, pool));
+      SVN_ERR(svn_mergeinfo_diff2(&deleted, &added, info1, info2,
+                                  consider_inheritance, pool, pool));
       *is_equal = apr_hash_count(deleted) == 0 && apr_hash_count(added) == 0;
     }
   else
@@ -1295,8 +1663,10 @@ svn_mergeinfo__equals(svn_boolean_t *is_
 }
 
 svn_error_t *
-svn_mergeinfo_merge(svn_mergeinfo_t mergeinfo, svn_mergeinfo_t changes,
-                    apr_pool_t *pool)
+svn_mergeinfo_merge2(svn_mergeinfo_t mergeinfo,
+                     svn_mergeinfo_t changes,
+                     apr_pool_t *result_pool,
+                     apr_pool_t *scratch_pool)
 {
   apr_array_header_t *sorted1, *sorted2;
   int i, j;
@@ -1305,12 +1675,14 @@ svn_mergeinfo_merge(svn_mergeinfo_t merg
   if (!apr_hash_count(changes))
     return SVN_NO_ERROR;
 
-  sorted1 = svn_sort__hash(mergeinfo, svn_sort_compare_items_as_paths, pool);
-  sorted2 = svn_sort__hash(changes, svn_sort_compare_items_as_paths, pool);
+  sorted1 = svn_sort__hash(mergeinfo, svn_sort_compare_items_as_paths,
+                           scratch_pool);
+  sorted2 = svn_sort__hash(changes, svn_sort_compare_items_as_paths,
+                           scratch_pool);
 
   i = 0;
   j = 0;
-  iterpool = svn_pool_create(pool);
+  iterpool = svn_pool_create(scratch_pool);
   while (i < sorted1->nelts && j < sorted2->nelts)
     {
       svn_sort__item_t elt1, elt2;
@@ -1329,7 +1701,7 @@ svn_mergeinfo_merge(svn_mergeinfo_t merg
           rl1 = elt1.value;
           rl2 = elt2.value;
 
-          SVN_ERR(svn_rangelist_merge2(rl1, rl2, pool, iterpool));
+          SVN_ERR(svn_rangelist_merge2(rl1, rl2, result_pool, iterpool));
           apr_hash_set(mergeinfo, elt1.key, elt1.klen, rl1);
           i++;
           j++;
@@ -1385,8 +1757,8 @@ svn_mergeinfo_catalog_merge(svn_mergeinf
           svn_mergeinfo_t mergeinfo = cat_elt.value;
           svn_mergeinfo_t changes_mergeinfo = change_elt.value;
 
-          SVN_ERR(svn_mergeinfo_merge(mergeinfo, changes_mergeinfo,
-                                      result_pool));
+          SVN_ERR(svn_mergeinfo_merge2(mergeinfo, changes_mergeinfo,
+                                       result_pool, scratch_pool));
           apr_hash_set(mergeinfo_cat, cat_elt.key, cat_elt.klen, mergeinfo);
           i++;
           j++;
@@ -1420,16 +1792,6 @@ svn_mergeinfo_catalog_merge(svn_mergeinf
 }
 
 svn_error_t *
-svn_mergeinfo_intersect(svn_mergeinfo_t *mergeinfo,
-                        svn_mergeinfo_t mergeinfo1,
-                        svn_mergeinfo_t mergeinfo2,
-                        apr_pool_t *pool)
-{
-  return svn_mergeinfo_intersect2(mergeinfo, mergeinfo1, mergeinfo2,
-                                  TRUE, pool, pool);
-}
-
-svn_error_t *
 svn_mergeinfo_intersect2(svn_mergeinfo_t *mergeinfo,
                          svn_mergeinfo_t mergeinfo1,
                          svn_mergeinfo_t mergeinfo2,
@@ -1438,26 +1800,29 @@ svn_mergeinfo_intersect2(svn_mergeinfo_t
                          apr_pool_t *scratch_pool)
 {
   apr_hash_index_t *hi;
+  apr_pool_t *iterpool;
 
   *mergeinfo = apr_hash_make(result_pool);
+  iterpool = svn_pool_create(scratch_pool);
 
   /* ### TODO(reint): Do we care about the case when a path in one
      ### mergeinfo hash has inheritable mergeinfo, and in the other
      ### has non-inhertiable mergeinfo?  It seems like that path
      ### itself should really be an intersection, while child paths
      ### should not be... */
-  for (hi = apr_hash_first(apr_hash_pool_get(mergeinfo1), mergeinfo1);
+  for (hi = apr_hash_first(scratch_pool, mergeinfo1);
        hi; hi = apr_hash_next(hi))
     {
       const char *path = svn__apr_hash_index_key(hi);
       apr_array_header_t *rangelist1 = svn__apr_hash_index_val(hi);
       apr_array_header_t *rangelist2;
 
+      svn_pool_clear(iterpool);
       rangelist2 = apr_hash_get(mergeinfo2, path, APR_HASH_KEY_STRING);
       if (rangelist2)
         {
           SVN_ERR(svn_rangelist_intersect(&rangelist2, rangelist1, rangelist2,
-                                          consider_inheritance, scratch_pool));
+                                          consider_inheritance, iterpool));
           if (rangelist2->nelts > 0)
             apr_hash_set(*mergeinfo,
                          apr_pstrdup(result_pool, path),
@@ -1465,18 +1830,11 @@ svn_mergeinfo_intersect2(svn_mergeinfo_t
                          svn_rangelist_dup(rangelist2, result_pool));
         }
     }
+  svn_pool_destroy(iterpool);
   return SVN_NO_ERROR;
 }
 
 svn_error_t *
-svn_mergeinfo_remove(svn_mergeinfo_t *mergeinfo, svn_mergeinfo_t eraser,
-                     svn_mergeinfo_t whiteboard, apr_pool_t *pool)
-{
-  return svn_mergeinfo_remove2(mergeinfo, eraser, whiteboard, TRUE, pool,
-                               pool);
-}
-
-svn_error_t *
 svn_mergeinfo_remove2(svn_mergeinfo_t *mergeinfo,
                       svn_mergeinfo_t eraser,
                       svn_mergeinfo_t whiteboard,
@@ -1495,7 +1853,7 @@ svn_rangelist_to_string(svn_string_t **o
                         const apr_array_header_t *rangelist,
                         apr_pool_t *pool)
 {
-  svn_stringbuf_t *buf = svn_stringbuf_create("", pool);
+  svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool);
 
   if (rangelist->nelts > 0)
     {
@@ -1532,7 +1890,7 @@ mergeinfo_to_stringbuf(svn_stringbuf_t *
                        const char *prefix,
                        apr_pool_t *pool)
 {
-  *output = svn_stringbuf_create("", pool);
+  *output = svn_stringbuf_create_empty(pool);
 
   if (apr_hash_count(input) > 0)
     {
@@ -1565,16 +1923,10 @@ svn_error_t *
 svn_mergeinfo_to_string(svn_string_t **output, svn_mergeinfo_t input,
                         apr_pool_t *pool)
 {
-  if (apr_hash_count(input) > 0)
-    {
-      svn_stringbuf_t *mergeinfo_buf;
-      SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_buf, input, NULL, pool));
-      *output = svn_stringbuf__morph_into_string(mergeinfo_buf);
-    }
-  else
-    {
-      *output = svn_string_create("", pool);
-    }
+  svn_stringbuf_t *mergeinfo_buf;
+
+  SVN_ERR(mergeinfo_to_stringbuf(&mergeinfo_buf, input, NULL, pool));
+  *output = svn_stringbuf__morph_into_string(mergeinfo_buf);
   return SVN_NO_ERROR;
 }
 
@@ -1773,7 +2125,7 @@ svn_mergeinfo__remove_prefix_from_catalo
       apr_ssize_t padding = 0;
 
       SVN_ERR_ASSERT(klen >= prefix_len);
-      SVN_ERR_ASSERT(svn_fspath__is_ancestor(prefix_path, original_path));
+      SVN_ERR_ASSERT(svn_fspath__skip_ancestor(prefix_path, original_path));
 
       /* If the ORIGINAL_PATH doesn't match the PREFIX_PATH exactly
          and we're not simply removing a single leading slash (such as
@@ -1820,6 +2172,36 @@ svn_mergeinfo__add_prefix_to_catalog(svn
 }
 
 svn_error_t *
+svn_mergeinfo__relpaths_to_urls(apr_hash_t **out_mergeinfo,
+                                svn_mergeinfo_t mergeinfo,
+                                const char *repos_root_url,
+                                apr_pool_t *result_pool,
+                                apr_pool_t *scratch_pool)
+{
+  *out_mergeinfo = NULL;
+  if (mergeinfo)
+    {
+      apr_hash_index_t *hi;
+      apr_hash_t *full_path_mergeinfo = apr_hash_make(result_pool);
+
+      for (hi = apr_hash_first(scratch_pool, mergeinfo);
+           hi; hi = apr_hash_next(hi))
+        {
+          const char *key = svn__apr_hash_index_key(hi);
+          void *val = svn__apr_hash_index_val(hi);
+
+          apr_hash_set(full_path_mergeinfo,
+                       svn_path_url_add_component2(repos_root_url, key + 1,
+                                                   result_pool),
+                       APR_HASH_KEY_STRING, val);
+        }
+      *out_mergeinfo = full_path_mergeinfo;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo,
                                        svn_mergeinfo_t mergeinfo,
                                        const char *suffix_relpath,
@@ -1836,13 +2218,13 @@ svn_mergeinfo__add_suffix_to_mergeinfo(s
        hi;
        hi = apr_hash_next(hi))
     {
-      const char *path = svn__apr_hash_index_key(hi);
+      const char *fspath = svn__apr_hash_index_key(hi);
       apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
 
       apr_hash_set(*out_mergeinfo,
-                   svn_dirent_join(path, suffix_relpath, result_pool),
+                   svn_fspath__join(fspath, suffix_relpath, result_pool),
                    APR_HASH_KEY_STRING,
-                   svn_rangelist_dup(rangelist, result_pool));
+                   rangelist);
     }
 
   return SVN_NO_ERROR;
@@ -1901,7 +2283,7 @@ svn_mergeinfo__catalog_to_formatted_stri
       apr_array_header_t *sorted_catalog =
         svn_sort__hash(catalog, svn_sort_compare_items_as_paths, pool);
 
-      output_buf = svn_stringbuf_create("", pool);
+      output_buf = svn_stringbuf_create_empty(pool);
       for (i = 0; i < sorted_catalog->nelts; i++)
         {
           svn_sort__item_t elt =
@@ -1974,7 +2356,7 @@ svn_mergeinfo__to_formatted_string(svn_s
 #endif
 
   *output = output_buf ? svn_stringbuf__morph_into_string(output_buf)
-                       : svn_string_create("", pool);
+                       : svn_string_create_empty(pool);
   return SVN_NO_ERROR;
 }
 
@@ -2176,25 +2558,6 @@ svn_mergeinfo__is_noninheritable(svn_mer
   return FALSE;
 }
 
-svn_error_t *
-svn_mergeinfo__string_has_noninheritable(svn_boolean_t *is_noninheritable,
-                                         const char *mergeinfo_str,
-                                         apr_pool_t *scratch_pool)
-{
-  *is_noninheritable = FALSE;
-
-  if (mergeinfo_str)
-    {
-      svn_mergeinfo_t mergeinfo;
-
-      SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_str, scratch_pool));
-      *is_noninheritable = svn_mergeinfo__is_noninheritable(mergeinfo,
-                                                            scratch_pool);
-    }
-
-  return SVN_NO_ERROR;
-}
-
 apr_array_header_t *
 svn_rangelist__initialize(svn_revnum_t start,
                           svn_revnum_t end,
@@ -2261,3 +2624,54 @@ svn_mergeinfo__mergeinfo_from_segments(s
   *mergeinfo_p = mergeinfo;
   return SVN_NO_ERROR;
 }
+
+svn_error_t *
+svn_rangelist__merge_many(apr_array_header_t *merged_rangelist,
+                          svn_mergeinfo_t merge_history,
+                          apr_pool_t *result_pool,
+                          apr_pool_t *scratch_pool)
+{
+  if (apr_hash_count(merge_history))
+    {
+      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+      apr_hash_index_t *hi;
+
+      for (hi = apr_hash_first(scratch_pool, merge_history);
+           hi;
+           hi = apr_hash_next(hi))
+        {
+          apr_array_header_t *subtree_rangelist = svn__apr_hash_index_val(hi);
+
+          svn_pool_clear(iterpool);
+          SVN_ERR(svn_rangelist_merge2(merged_rangelist, subtree_rangelist,
+                                       result_pool, iterpool));
+        }
+      svn_pool_destroy(iterpool);
+    }
+  return SVN_NO_ERROR;
+}
+
+
+const char *
+svn_inheritance_to_word(svn_mergeinfo_inheritance_t inherit)
+{
+  switch (inherit)
+    {
+    case svn_mergeinfo_inherited:
+      return "inherited";
+    case svn_mergeinfo_nearest_ancestor:
+      return "nearest-ancestor";
+    default:
+      return "explicit";
+    }
+}
+
+svn_mergeinfo_inheritance_t
+svn_inheritance_from_word(const char *word)
+{
+  if (strcmp(word, "inherited") == 0)
+    return svn_mergeinfo_inherited;
+  if (strcmp(word, "nearest-ancestor") == 0)
+    return svn_mergeinfo_nearest_ancestor;
+  return svn_mergeinfo_explicit;
+}

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/opt.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/opt.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/opt.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/opt.c Mon Nov 21 07:07:56 2011
@@ -644,6 +644,32 @@ svn_opt__revision_to_string(const svn_op
     }
 }
 
+svn_opt_revision_range_t *
+svn_opt__revision_range_create(const svn_opt_revision_t *start_revision,
+                               const svn_opt_revision_t *end_revision,
+                               apr_pool_t *result_pool)
+{
+  svn_opt_revision_range_t *range = apr_palloc(result_pool, sizeof(*range));
+
+  range->start = *start_revision;
+  range->end = *end_revision;
+  return range;
+}
+
+svn_opt_revision_range_t *
+svn_opt__revision_range_from_revnums(svn_revnum_t start_revnum,
+                                     svn_revnum_t end_revnum,
+                                     apr_pool_t *result_pool)
+{
+  svn_opt_revision_range_t *range = apr_palloc(result_pool, sizeof(*range));
+
+  range->start.kind = svn_opt_revision_number;
+  range->start.value.number = start_revnum;
+  range->end.kind = svn_opt_revision_number;
+  range->end.value.number = end_revnum;
+  return range;
+}
+
 
 
 /*** Parsing arguments. ***/
@@ -944,7 +970,7 @@ svn_opt_parse_revprop(apr_hash_t **revpr
   else
     {
       SVN_ERR(svn_utf_cstring_to_utf8(&propname, revprop_spec, pool));
-      propval = svn_string_create("", pool);
+      propval = svn_string_create_empty(pool);
     }
 
   if (!svn_prop_name_is_valid(propname))

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/prompt.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/prompt.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/prompt.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/prompt.c Mon Nov 21 07:07:56 2011
@@ -95,7 +95,7 @@ prompt(const char **result,
   apr_file_t *fp;
   char c;
 
-  svn_stringbuf_t *strbuf = svn_stringbuf_create("", pool);
+  svn_stringbuf_t *strbuf = svn_stringbuf_create_empty(pool);
 
   status = apr_file_open_stdin(&fp, pool);
   if (status)

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/properties.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/properties.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/properties.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/properties.c Mon Nov 21 07:07:56 2011
@@ -221,6 +221,85 @@ svn_prop_diffs(apr_array_header_t **prop
   return SVN_NO_ERROR;
 }
 
+/**
+ * Reallocate the members of PROP using POOL.
+ */
+static void
+svn_prop__members_dup(svn_prop_t *prop, apr_pool_t *pool)
+{
+  if (prop->name)
+    prop->name = apr_pstrdup(pool, prop->name);
+  if (prop->value)
+    prop->value = svn_string_dup(prop->value, pool);
+}
+
+svn_prop_t *
+svn_prop_dup(const svn_prop_t *prop, apr_pool_t *pool)
+{
+  svn_prop_t *new_prop = apr_palloc(pool, sizeof(*new_prop));
+
+  *new_prop = *prop;
+
+  svn_prop__members_dup(new_prop, pool);
+
+  return new_prop;
+}
+
+apr_array_header_t *
+svn_prop_array_dup(const apr_array_header_t *array, apr_pool_t *pool)
+{
+  int i;
+  apr_array_header_t *new_array = apr_array_copy(pool, array);
+  for (i = 0; i < new_array->nelts; ++i)
+    {
+      svn_prop_t *elt = &APR_ARRAY_IDX(new_array, i, svn_prop_t);
+      svn_prop__members_dup(elt, pool);
+    }
+  return new_array;
+}
+
+apr_array_header_t *
+svn_prop_hash_to_array(apr_hash_t *hash, apr_pool_t *pool)
+{
+  apr_hash_index_t *hi;
+  apr_array_header_t *array = apr_array_make(pool, apr_hash_count(hash),
+                                             sizeof(svn_prop_t));
+
+  for (hi = apr_hash_first(pool, hash); hi; hi = apr_hash_next(hi))
+    {
+      const void *key;
+      void *val;
+      svn_prop_t prop;
+
+      apr_hash_this(hi, &key, NULL, &val);
+      prop.name = key;
+      prop.value = val;
+      APR_ARRAY_PUSH(array, svn_prop_t) = prop;
+    }
+
+  return array;
+}
+
+apr_hash_t *
+svn_prop_hash_dup(apr_hash_t *hash,
+                  apr_pool_t *pool)
+{
+  apr_hash_index_t *hi;
+  apr_hash_t *new_hash = apr_hash_make(pool);
+
+  for (hi = apr_hash_first(pool, hash); hi; hi = apr_hash_next(hi))
+    {
+      const void *key;
+      apr_ssize_t klen;
+      void *prop;
+
+      apr_hash_this(hi, &key, &klen, &prop);
+      apr_hash_set(new_hash, apr_pstrdup(pool, key), klen,
+                   svn_string_dup(prop, pool));
+    }
+  return new_hash;
+}
+
 apr_hash_t *
 svn_prop_array_to_hash(const apr_array_header_t *properties,
                        apr_pool_t *pool)

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/quoprint.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/quoprint.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/quoprint.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/quoprint.c Mon Nov 21 07:07:56 2011
@@ -120,7 +120,7 @@ encode_data(void *baton, const char *dat
 {
   struct encode_baton *eb = baton;
   apr_pool_t *subpool = svn_pool_create(eb->pool);
-  svn_stringbuf_t *encoded = svn_stringbuf_create("", subpool);
+  svn_stringbuf_t *encoded = svn_stringbuf_create_empty(subpool);
   apr_size_t enclen;
   svn_error_t *err = SVN_NO_ERROR;
 
@@ -177,7 +177,7 @@ svn_quoprint_encode(svn_stream_t *output
 svn_stringbuf_t *
 svn_quoprint_encode_string(const svn_stringbuf_t *str, apr_pool_t *pool)
 {
-  svn_stringbuf_t *encoded = svn_stringbuf_create("", pool);
+  svn_stringbuf_t *encoded = svn_stringbuf_create_empty(pool);
   int linelen = 0;
 
   encode_bytes(encoded, str->data, str->len, &linelen);
@@ -254,7 +254,7 @@ decode_data(void *baton, const char *dat
 
   /* Decode this block of data.  */
   subpool = svn_pool_create(db->pool);
-  decoded = svn_stringbuf_create("", subpool);
+  decoded = svn_stringbuf_create_empty(subpool);
   decode_bytes(decoded, data, *len, db->buf, &db->buflen);
 
   /* Write the output, clean up, go home.  */
@@ -300,7 +300,7 @@ svn_quoprint_decode(svn_stream_t *output
 svn_stringbuf_t *
 svn_quoprint_decode_string(const svn_stringbuf_t *str, apr_pool_t *pool)
 {
-  svn_stringbuf_t *decoded = svn_stringbuf_create("", pool);
+  svn_stringbuf_t *decoded = svn_stringbuf_create_empty(pool);
   char ingroup[4];
   int ingrouplen = 0;
 

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/sorts.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/sorts.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/sorts.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/sorts.c Mon Nov 21 07:07:56 2011
@@ -140,21 +140,38 @@ svn_sort__hash(apr_hash_t *ht,
 {
   apr_hash_index_t *hi;
   apr_array_header_t *ary;
+  svn_boolean_t sorted;
+  svn_sort__item_t *prev_item;
 
   /* allocate an array with enough elements to hold all the keys. */
   ary = apr_array_make(pool, apr_hash_count(ht), sizeof(svn_sort__item_t));
 
   /* loop over hash table and push all keys into the array */
+  sorted = TRUE;
+  prev_item = NULL;
   for (hi = apr_hash_first(pool, ht); hi; hi = apr_hash_next(hi))
     {
       svn_sort__item_t *item = apr_array_push(ary);
 
       apr_hash_this(hi, &item->key, &item->klen, &item->value);
+
+      if (prev_item == NULL)
+        {
+          prev_item = item;
+          continue;
+        }
+
+      if (sorted)
+        {
+          sorted = (comparison_func(prev_item, item) < 0);
+          prev_item = item;
+        }
     }
 
-  /* now quicksort the array.  */
-  qsort(ary->elts, ary->nelts, ary->elt_size,
-        (int (*)(const void *, const void *))comparison_func);
+  /* quicksort the array if it isn't already sorted.  */
+  if (!sorted)
+    qsort(ary->elts, ary->nelts, ary->elt_size,
+          (int (*)(const void *, const void *))comparison_func);
 
   return ary;
 }
@@ -228,3 +245,28 @@ svn_sort__array_insert(const void *new_e
   /* Copy in the new element */
   memcpy(new_position, new_element, array->elt_size);
 }
+
+void
+svn_sort__array_delete(apr_array_header_t *arr,
+                       int delete_index,
+                       int elements_to_delete)
+{
+  /* Do we have a valid index and are there enough elements? */
+  if (delete_index >= 0
+      && delete_index < arr->nelts
+      && elements_to_delete > 0
+      && (elements_to_delete + delete_index) <= arr->nelts)
+    {
+      /* If we are not deleting a block of elements that extends to the end
+         of the array, then we need to move the remaining elements to keep
+         the array contiguous. */
+      if ((elements_to_delete + delete_index) < arr->nelts)
+        memmove(
+          arr->elts + arr->elt_size * delete_index,
+          arr->elts + (arr->elt_size * (delete_index + elements_to_delete)),
+          arr->elt_size * (arr->nelts - elements_to_delete - delete_index));
+
+      /* Delete the last ELEMENTS_TO_DELETE elements. */
+      arr->nelts -= elements_to_delete;
+    }
+}

Modified: subversion/branches/svn-bisect/subversion/libsvn_subr/sqlite.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_subr/sqlite.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_subr/sqlite.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_subr/sqlite.c Mon Nov 21 07:07:56 2011
@@ -133,6 +133,10 @@ struct svn_sqlite__value_t
 } while (0)
 
 
+/* Time (in milliseconds) to wait for sqlite locks before giving up. */
+#define BUSY_TIMEOUT 10000
+
+
 /* Convenience wrapper around exec_sql2(). */
 #define exec_sql(db, sql) exec_sql2((db), (sql), SQLITE_OK)
 
@@ -579,108 +583,6 @@ svn_sqlite__reset(svn_sqlite__stmt_t *st
 
 
 svn_error_t *
-svn_sqlite__set_schema_version(svn_sqlite__db_t *db,
-                               int version,
-                               apr_pool_t *scratch_pool)
-{
-  const char *pragma_cmd = apr_psprintf(scratch_pool,
-                                        "PRAGMA user_version = %d;",
-                                        version);
-
-  return svn_error_trace(exec_sql(db, pragma_cmd));
-}
-
-
-/* Time (in milliseconds) to wait for sqlite locks before giving up. */
-#define BUSY_TIMEOUT 10000
-
-
-#if 0
-/*
- * EXAMPLE
- *
- * The following provide an example for a series of SQL statements to
- * create/upgrade a SQLite schema across multiple "formats". This array
- * and integer would be passed into svn_sqlite__open().
- */
-static const char *schema_create_sql[] = {
-  NULL, /* An empty database is format 0 */
-
-  /* USER_VERSION 1 */
-  "PRAGMA auto_vacuum = 1;"
-  APR_EOL_STR
-  "CREATE TABLE mergeinfo (revision INTEGER NOT NULL, mergedfrom TEXT NOT "
-  "NULL, mergedto TEXT NOT NULL, mergedrevstart INTEGER NOT NULL, "
-  "mergedrevend INTEGER NOT NULL, inheritable INTEGER NOT NULL);"
-  APR_EOL_STR
-  "CREATE INDEX mi_mergedfrom_idx ON mergeinfo (mergedfrom);"
-  APR_EOL_STR
-  "CREATE INDEX mi_mergedto_idx ON mergeinfo (mergedto);"
-  APR_EOL_STR
-  "CREATE INDEX mi_revision_idx ON mergeinfo (revision);"
-  APR_EOL_STR
-  "CREATE TABLE mergeinfo_changed (revision INTEGER NOT NULL, path TEXT "
-  "NOT NULL);"
-  APR_EOL_STR
-  "CREATE UNIQUE INDEX mi_c_revpath_idx ON mergeinfo_changed (revision, path);"
-  APR_EOL_STR
-  "CREATE INDEX mi_c_path_idx ON mergeinfo_changed (path);"
-  APR_EOL_STR
-  "CREATE INDEX mi_c_revision_idx ON mergeinfo_changed (revision);"
-  APR_EOL_STR,
-
-  /* USER_VERSION 2 */
-  "CREATE TABLE node_origins (node_id TEXT NOT NULL, node_rev_id TEXT NOT "
-  "NULL);"
-  APR_EOL_STR
-  "CREATE UNIQUE INDEX no_ni_idx ON node_origins (node_id);"
-  APR_EOL_STR
-};
-
-static const int latest_schema_format =
-  sizeof(schema_create_sql)/sizeof(schema_create_sql[0]) - 1;
-
-#endif
-
-struct upgrade_baton
-{
-  int current_schema;
-  int latest_schema;
-  const char * const *upgrade_sql;
-};
-
-
-/* This implements svn_sqlite__transaction_callback_t */
-static svn_error_t *
-upgrade_format(void *baton,
-               svn_sqlite__db_t *db,
-               apr_pool_t *scratch_pool)
-{
-  struct upgrade_baton *ub = baton;
-  int current_schema = ub->current_schema;
-  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-
-  while (current_schema < ub->latest_schema)
-    {
-      svn_pool_clear(iterpool);
-
-      /* Go to the next schema */
-      current_schema++;
-
-      /* Run the upgrade SQL */
-      if (ub->upgrade_sql[current_schema])
-        SVN_ERR(exec_sql(db, ub->upgrade_sql[current_schema]));
-
-      /* Update the user version pragma */
-      SVN_ERR(svn_sqlite__set_schema_version(db, current_schema, iterpool));
-    }
-
-  svn_pool_destroy(iterpool);
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
 svn_sqlite__read_schema_version(int *version,
                                 svn_sqlite__db_t *db,
                                 apr_pool_t *scratch_pool)
@@ -696,41 +598,6 @@ svn_sqlite__read_schema_version(int *ver
 }
 
 
-/* Check the schema format of the database, upgrading it if necessary.
-   Return SVN_ERR_SQLITE_UNSUPPORTED_SCHEMA if the schema format is too new,
-   or SVN_ERR_SQLITE_ERROR if an sqlite error occurs during validation.
-   Return SVN_NO_ERROR if everything is okay. */
-static svn_error_t *
-check_format(svn_sqlite__db_t *db,
-             int latest_schema,
-             const char * const *upgrade_sql,
-             apr_pool_t *scratch_pool)
-{
-  int current_schema;
-
-  /* Validate that the schema exists as expected. */
-  SVN_ERR(svn_sqlite__read_schema_version(&current_schema, db, scratch_pool));
-
-  if (current_schema == latest_schema)
-    return SVN_NO_ERROR;
-
-  if (current_schema < latest_schema)
-    {
-      struct upgrade_baton ub;
-
-      ub.current_schema = current_schema;
-      ub.latest_schema = latest_schema;
-      ub.upgrade_sql = upgrade_sql;
-
-      return svn_error_trace(svn_sqlite__with_transaction(
-                               db, upgrade_format, &ub, scratch_pool));
-    }
-
-  return svn_error_createf(SVN_ERR_SQLITE_UNSUPPORTED_SCHEMA, NULL,
-                           _("Schema format %d not recognized"),
-                           current_schema);
-}
-
 static volatile svn_atomic_t sqlite_init_state = 0;
 
 /* If possible, verify that SQLite was compiled in a thread-safe
@@ -739,12 +606,12 @@ static volatile svn_atomic_t sqlite_init
 static svn_error_t *
 init_sqlite(void *baton, apr_pool_t *pool)
 {
-  if (sqlite3_libversion_number() < SQLITE_VERSION_NUMBER)
+  if (sqlite3_libversion_number() < SVN_SQLITE_MIN_VERSION_NUMBER)
     {
       return svn_error_createf(
                     SVN_ERR_SQLITE_ERROR, NULL,
                     _("SQLite compiled for %s, but running with %s"),
-                    SQLITE_VERSION, sqlite3_libversion());
+                    SVN_SQLITE_MIN_VERSION, sqlite3_libversion());
     }
 
 #if APR_HAS_THREADS
@@ -770,12 +637,6 @@ init_sqlite(void *baton, apr_pool_t *poo
 
 #endif /* APR_HAS_THRADS */
 
-  /* SQLite 3.5 allows sharing cache instances, even in a multithreaded
-     environment. This allows sharing cached data when we open a database
-     more than once (Very common in the current pre-single-database state) */
-  SQLITE_ERR_MSG(sqlite3_enable_shared_cache(TRUE),
-                 _("Could not initialize SQLite shared cache"));
-
   return SVN_NO_ERROR;
 }
 
@@ -836,22 +697,6 @@ internal_open(sqlite3 **db3, const char 
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_sqlite__get_schema_version(int *version,
-                               const char *path,
-                               apr_pool_t *scratch_pool)
-{
-  svn_sqlite__db_t db;
-
-  SVN_ERR(svn_atomic__init_once(&sqlite_init_state,
-                                init_sqlite, NULL, scratch_pool));
-  SVN_ERR(internal_open(&db.db3, path, svn_sqlite__mode_readonly,
-                        scratch_pool));
-  SVN_ERR(svn_sqlite__read_schema_version(version, &db, scratch_pool));
-  SQLITE_ERR(sqlite3_close(db.db3), &db);
-
-  return SVN_NO_ERROR;
-}
 
 /* APR cleanup function used to close the database when its pool is destroyed.
    DATA should be the svn_sqlite__db_t handle for the database. */
@@ -897,7 +742,7 @@ close_apr(void *data)
 svn_error_t *
 svn_sqlite__open(svn_sqlite__db_t **db, const char *path,
                  svn_sqlite__mode_t mode, const char * const statements[],
-                 int latest_schema, const char * const *upgrade_sql,
+                 int unused1, const char * const *unused2,
                  apr_pool_t *result_pool, apr_pool_t *scratch_pool)
 {
   SVN_ERR(svn_atomic__init_once(&sqlite_init_state,
@@ -966,10 +811,6 @@ svn_sqlite__open(svn_sqlite__db_t **db, 
      setting SQLITE_TEMP_STORE to 0 (always to disk) */
   svn_error_clear(exec_sql(*db, "PRAGMA temp_store = MEMORY;"));
 
-  /* Validate the schema, upgrading if necessary. */
-  if (upgrade_sql != NULL)
-    SVN_ERR(check_format(*db, latest_schema, upgrade_sql, scratch_pool));
-
   /* Store the provided statements. */
   if (statements)
     {