Merge up to r27887.

git-svn-id: https://svn.apache.org/repos/asf/subversion/branches/sqlite-node-origins@867962 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/configure.ac b/configure.ac
index 9ce2c01..daf8538 100644
--- a/configure.ac
+++ b/configure.ac
@@ -465,7 +465,7 @@
       fi
       enable_debugging=yes
       if test "$GCC" = "yes"; then
-        CFLAGS="$CFLAGS -std=c89 -Wpointer-arith -Wwrite-strings -Wshadow"
+        CFLAGS="$CFLAGS -Wpointer-arith -Wwrite-strings -Wshadow"
         CXXFLAGS="$CXXFLAGS -Wpointer-arith -Wwrite-strings -Wshadow"
       fi
     fi
diff --git a/subversion/bindings/javahl/native/SVNClient.cpp b/subversion/bindings/javahl/native/SVNClient.cpp
index 6d58560..5487930 100644
--- a/subversion/bindings/javahl/native/SVNClient.cpp
+++ b/subversion/bindings/javahl/native/SVNClient.cpp
@@ -880,9 +880,9 @@
 
 void SVNClient::diff(const char *target1, Revision &revision1,
                      const char *target2, Revision &revision2,
-                     Revision *pegRevision, const char *outfileName,
-                     svn_depth_t depth, bool ignoreAncestry,
-                     bool noDiffDelete, bool force)
+                     Revision *pegRevision, const char *relativeToDir,
+                     const char *outfileName, svn_depth_t depth,
+                     bool ignoreAncestry, bool noDiffDelete, bool force)
 {
     svn_error_t *err;
     Pool requestPool;
@@ -923,6 +923,7 @@
                                    pegRevision->revision(),
                                    revision1.revision(),
                                    revision2.revision(),
+                                   relativeToDir,
                                    depth,
                                    ignoreAncestry,
                                    noDiffDelete,
@@ -951,6 +952,7 @@
                                revision1.revision(),
                                path2.c_str(),
                                revision2.revision(),
+                               relativeToDir,
                                depth,
                                ignoreAncestry,
                                noDiffDelete,
@@ -977,20 +979,23 @@
 
 void SVNClient::diff(const char *target1, Revision &revision1,
                      const char *target2, Revision &revision2,
-                     const char *outfileName, svn_depth_t depth,
-                     bool ignoreAncestry, bool noDiffDelete, bool force)
+                     const char *relativeToDir, const char *outfileName,
+                     svn_depth_t depth, bool ignoreAncestry,
+                     bool noDiffDelete, bool force)
 {
-    diff(target1, revision1, target2, revision2, NULL, outfileName, depth,
-         ignoreAncestry, noDiffDelete, force);
+    diff(target1, revision1, target2, revision2, NULL, relativeToDir,
+         outfileName, depth, ignoreAncestry, noDiffDelete, force);
 }
 
 void SVNClient::diff(const char *target, Revision &pegRevision,
                      Revision &startRevision, Revision &endRevision,
-                     const char *outfileName, svn_depth_t depth,
-                     bool ignoreAncestry, bool noDiffDelete, bool force)
+                     const char *relativeToDir, const char *outfileName,
+                     svn_depth_t depth, bool ignoreAncestry,
+                     bool noDiffDelete, bool force)
 {
-    diff(target, startRevision, NULL, endRevision, &pegRevision, outfileName,
-         depth, ignoreAncestry, noDiffDelete, force);
+    diff(target, startRevision, NULL, endRevision, &pegRevision,
+         relativeToDir, outfileName, depth, ignoreAncestry, noDiffDelete,
+         force);
 }
 
 void
diff --git a/subversion/bindings/javahl/native/SVNClient.h b/subversion/bindings/javahl/native/SVNClient.h
index d75b2e4..bda7563 100644
--- a/subversion/bindings/javahl/native/SVNClient.h
+++ b/subversion/bindings/javahl/native/SVNClient.h
@@ -164,12 +164,14 @@
                       Revision &revision, Revision &pegRevision);
   void diff(const char *target1, Revision &revision1,
             const char *target2, Revision &revision2,
-            const char *outfileName, svn_depth_t depth, bool ignoreAncestry,
-            bool noDiffDelete, bool force);
+            const char *relativeToDir, const char *outfileName,
+            svn_depth_t depth, bool ignoreAncestry, bool noDiffDelete,
+            bool force);
   void diff(const char *target, Revision &pegevision,
             Revision &startRevision, Revision &endRevision,
-            const char *outfileName, svn_depth_t depth, bool ignoreAncestry,
-            bool noDiffDelete, bool force);
+            const char *relativeToDir, const char *outfileName,
+            svn_depth_t depth, bool ignoreAncestry, bool noDiffDelete,
+            bool force);
   void diffSummarize(const char *target1, Revision &revision1,
                      const char *target2, Revision &revision2,
                      svn_depth_t depth, bool ignoreAncestry,
@@ -203,7 +205,7 @@
    */
   void diff(const char *target1, Revision &revision1,
             const char *target2, Revision &revision2,
-            Revision *pegRevision,
+            Revision *pegRevision, const char *relativeToDir,
             const char *outfileName, svn_depth_t depth, bool ignoreAncestry,
             bool noDiffDelete, bool force);
 
diff --git a/subversion/bindings/javahl/native/org_tigris_subversion_javahl_SVNClient.cpp b/subversion/bindings/javahl/native/org_tigris_subversion_javahl_SVNClient.cpp
index 4a66b4e..07e7d23 100644
--- a/subversion/bindings/javahl/native/org_tigris_subversion_javahl_SVNClient.cpp
+++ b/subversion/bindings/javahl/native/org_tigris_subversion_javahl_SVNClient.cpp
@@ -1106,11 +1106,11 @@
 }
 
 JNIEXPORT void JNICALL
-Java_org_tigris_subversion_javahl_SVNClient_diff__Ljava_lang_String_2Lorg_tigris_subversion_javahl_Revision_2Ljava_lang_String_2Lorg_tigris_subversion_javahl_Revision_2Ljava_lang_String_2IZZZ
+Java_org_tigris_subversion_javahl_SVNClient_diff__Ljava_lang_String_2Lorg_tigris_subversion_javahl_Revision_2Ljava_lang_String_2Lorg_tigris_subversion_javahl_Revision_2Ljava_lang_String_2Ljava_lang_String_2IZZZ
 (JNIEnv *env, jobject jthis, jstring jtarget1, jobject jrevision1,
- jstring jtarget2, jobject jrevision2, jstring joutfileName,
- jint jdepth, jboolean jignoreAncestry, jboolean jnoDiffDeleted,
- jboolean jforce)
+ jstring jtarget2, jobject jrevision2, jstring jrelativeToDir,
+ jstring joutfileName, jint jdepth, jboolean jignoreAncestry,
+ jboolean jnoDiffDeleted, jboolean jforce)
 {
   JNIEntry(SVNClient, diff);
   SVNClient *cl = SVNClient::getCppObject(jthis);
@@ -1135,22 +1135,26 @@
   if (JNIUtil::isExceptionThrown())
     return;
 
+  JNIStringHolder relativeToDir(jrelativeToDir);
+  if (JNIUtil::isExceptionThrown())
+    return;
+
   JNIStringHolder outfileName(joutfileName);
   if (JNIUtil::isExceptionThrown())
     return;
 
-  cl->diff(target1, revision1, target2, revision2, outfileName,
+  cl->diff(target1, revision1, target2, revision2, relativeToDir, outfileName,
            (svn_depth_t)jdepth,
            jignoreAncestry ? true:false,
            jnoDiffDeleted ? true:false, jforce ? true:false);
 }
 
 JNIEXPORT void JNICALL
-Java_org_tigris_subversion_javahl_SVNClient_diff__Ljava_lang_String_2Lorg_tigris_subversion_javahl_Revision_2Lorg_tigris_subversion_javahl_Revision_2Lorg_tigris_subversion_javahl_Revision_2Ljava_lang_String_2IZZZ
+Java_org_tigris_subversion_javahl_SVNClient_diff__Ljava_lang_String_2Lorg_tigris_subversion_javahl_Revision_2Lorg_tigris_subversion_javahl_Revision_2Lorg_tigris_subversion_javahl_Revision_2Ljava_lang_String_2Ljava_lang_String_2IZZZ
 (JNIEnv *env, jobject jthis, jstring jtarget, jobject jpegRevision,
- jobject jstartRevision, jobject jendRevision, jstring joutfileName,
- jint jdepth, jboolean jignoreAncestry, jboolean jnoDiffDeleted,
- jboolean jforce)
+ jobject jstartRevision, jobject jendRevision, jstring jrelativeToDir,
+ jstring joutfileName, jint jdepth, jboolean jignoreAncestry,
+ jboolean jnoDiffDeleted, jboolean jforce)
 {
   JNIEntry(SVNClient, diff);
   SVNClient *cl = SVNClient::getCppObject(jthis);
@@ -1179,8 +1183,12 @@
   if (JNIUtil::isExceptionThrown())
     return;
 
-  cl->diff(target, pegRevision, startRevision, endRevision, outfileName,
-           (svn_depth_t)jdepth,
+  JNIStringHolder relativeToDir(jrelativeToDir);
+  if (JNIUtil::isExceptionThrown())
+    return;
+
+  cl->diff(target, pegRevision, startRevision, endRevision, relativeToDir,
+           outfileName, (svn_depth_t) jdepth,
            jignoreAncestry ? true:false,
            jnoDiffDeleted ? true:false, jforce ? true:false);
 }
diff --git a/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClient.java b/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClient.java
index 53313ac..8fdba43 100644
--- a/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClient.java
+++ b/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClient.java
@@ -803,7 +803,7 @@
                      boolean force)
             throws ClientException
     {
-        diff(target1, revision1, target2, revision2, outFileName,
+        diff(target1, revision1, target2, revision2, null, outFileName,
              Depth.unknownOrFiles(recurse), ignoreAncestry, noDiffDeleted,
              force);
     }
@@ -812,7 +812,8 @@
      * @since 1.5
      */
     public native void diff(String target1, Revision revision1, String target2,
-                            Revision revision2, String outFileName, int depth,
+                            Revision revision2, String relativeToDir,
+                            String outFileName, int depth,
                             boolean ignoreAncestry, boolean noDiffDeleted,
                             boolean force)
             throws ClientException;
@@ -830,9 +831,9 @@
                      boolean force)
             throws ClientException
     {
-        diff(target, pegRevision, startRevision, endRevision, outFileName,
-             Depth.unknownOrFiles(recurse), ignoreAncestry, noDiffDeleted,
-             force);
+        diff(target, pegRevision, startRevision, endRevision, null,
+             outFileName, Depth.unknownOrFiles(recurse), ignoreAncestry,
+             noDiffDeleted, force);
     }
 
     /**
@@ -840,9 +841,9 @@
      */
     public native void diff(String target, Revision pegRevision,
                             Revision startRevision, Revision endRevision,
-                            String outFileName, int depth,
-                            boolean ignoreAncestry, boolean noDiffDeleted,
-                            boolean force)
+                            String relativeToDir, String outFileName,
+                            int depth, boolean ignoreAncestry,
+                            boolean noDiffDeleted, boolean force)
             throws ClientException;
 
     /**
diff --git a/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientInterface.java b/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientInterface.java
index 21f69b2..b0549bf 100644
--- a/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientInterface.java
+++ b/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientInterface.java
@@ -1046,6 +1046,7 @@
      * @param revision1     first revision
      * @param target2       second path or url
      * @param revision2     second revision
+     * @param relativeToDir index path is relative to this path
      * @param outFileName   file name where difference are written
      * @param depth         how deep to traverse into subdirectories
      * @param ignoreAncestry ignore if files are not related
@@ -1055,8 +1056,9 @@
      * @since 1.5
      */
     void diff(String target1, Revision revision1, String target2,
-              Revision revision2, String outFileName, int depth,
-              boolean ignoreAncestry, boolean noDiffDeleted, boolean force)
+              Revision revision2, String relativeToDir, String outFileName,
+              int depth, boolean ignoreAncestry, boolean noDiffDeleted,
+              boolean force)
             throws ClientException;
 
     /**
@@ -1087,6 +1089,7 @@
      * @param pegRevision   revision tointerpret target
      * @param startRevision first Revision to compare
      * @param endRevision   second Revision to compare
+     * @param relativeToDir index path is relative to this path
      * @param outFileName   file name where difference are written
      * @param depth         how deep to traverse into subdirectories
      * @param ignoreAncestry ignore if files are not related
@@ -1096,8 +1099,9 @@
      * @since 1.5
      */
     void diff(String target, Revision pegRevision, Revision startRevision,
-              Revision endRevision, String outFileName, int depth,
-              boolean ignoreAncestry, boolean noDiffDeleted, boolean force)
+              Revision endRevision, String relativeToDir, String outFileName,
+              int depth, boolean ignoreAncestry, boolean noDiffDeleted,
+              boolean force)
             throws ClientException;
 
     /**
diff --git a/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientSynchronized.java b/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientSynchronized.java
index a94e363..fae5479 100644
--- a/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientSynchronized.java
+++ b/subversion/bindings/javahl/src/org/tigris/subversion/javahl/SVNClientSynchronized.java
@@ -1054,15 +1054,16 @@
      * @since 1.5
      */
     public void diff(String target1, Revision revision1, String target2,
-                     Revision revision2, String outFileName, int depth,
-                     boolean ignoreAncestry, boolean noDiffDeleted,
-                     boolean force)
+                     Revision revision2, String relativeToDir,
+                     String outFileName, int depth, boolean ignoreAncestry,
+                     boolean noDiffDeleted, boolean force)
             throws ClientException
     {
         synchronized (clazz)
         {
-            worker.diff(target1, revision1, target2, revision2, outFileName,
-                        depth, ignoreAncestry, noDiffDeleted, force);
+            worker.diff(target1, revision1, target2, revision2, relativeToDir,
+                        outFileName, depth, ignoreAncestry, noDiffDeleted,
+                        force);
         }
     }
 
@@ -1092,15 +1093,16 @@
      */
     public void diff(String target, Revision pegRevision,
                      Revision startRevision, Revision endRevision,
-                     String outFileName, int depth, boolean ignoreAncestry,
-                     boolean noDiffDeleted, boolean force)
+                     String relativeToDir, String outFileName, int depth,
+                     boolean ignoreAncestry, boolean noDiffDeleted,
+                     boolean force)
             throws ClientException
     {
         synchronized (clazz)
         {
             worker.diff(target, pegRevision, startRevision, endRevision,
-                        outFileName, depth, ignoreAncestry, noDiffDeleted,
-                        force);
+                        relativeToDir, outFileName, depth, ignoreAncestry,
+                        noDiffDeleted, force);
         }
     }
 
diff --git a/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/BasicTests.java b/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/BasicTests.java
index dfa1337..3d4c912 100644
--- a/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/BasicTests.java
+++ b/subversion/bindings/javahl/tests/org/tigris/subversion/javahl/BasicTests.java
@@ -19,7 +19,10 @@
 
 import org.tigris.subversion.javahl.*;
 
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
@@ -2405,6 +2408,8 @@
         final String NL = System.getProperty("line.separator");
         final String sepLine =
             "===================================================================" + NL;
+        final String underSepLine =
+            "___________________________________________________________________" + NL;
         final String expectedDiffBody =
             "@@ -1 +1 @@" + NL +
             "-This is the file 'iota'." + NL +
@@ -2450,6 +2455,78 @@
                                  "", diffOutput);
 
         diffOutput.delete();
+
+        // Test svn diff with a relative path.
+        String wcPath = fileToSVNPath(new File(thisTest.getWCPath()),
+                                        false);
+
+        expectedDiffOutput = "Index: iota" + NL + sepLine +
+            "--- iota\t(revision 1)" + NL +
+            "+++ iota\t(working copy)" + NL +
+            expectedDiffBody;
+        client.diff(iotaPath, Revision.BASE, iotaPath, Revision.WORKING,
+                    wcPath, diffOutput.getPath(), Depth.infinity, true, true,
+                    false);
+        assertFileContentsEquals("Unexpected diff output in file '" +
+                                 diffOutput.getPath() + '\'',
+                                 expectedDiffOutput, diffOutput);
+
+        // Test svn diff with a relative path and trailing slash.
+        wcPath = fileToSVNPath(new File(thisTest.getWCPath() + "/"), false);
+
+        client.diff(iotaPath, Revision.BASE, iotaPath, Revision.WORKING,
+                    wcPath, diffOutput.getPath(), Depth.infinity, true, true,
+                    false);
+        assertFileContentsEquals("Unexpected diff output in file '" +
+                                 diffOutput.getPath() + '\'',
+                                 expectedDiffOutput, diffOutput);
+
+        // Test relativeToDir fails with urls. */
+        try
+        {
+            client.diff(thisTest.getUrl() + "/iota", Revision.HEAD,
+                        thisTest.getUrl() + "/A/mu", Revision.HEAD,
+                        thisTest.getUrl(), diffOutput.getPath(),
+                        Depth.infinity, true, true, false);
+
+            fail("This test should fail becaus the relativeToDir parameter " +
+                 "does not work with URLs");
+        }
+        catch (Exception ignored)
+        {
+        }
+
+        /* Testing the expected failure when relativeToDir is not a parent
+           path of the target. */
+        try
+        {
+            client.diff(iotaPath, Revision.BASE, iotaPath, Revision.WORKING,
+                        "/non/existent/path", diffOutput.getPath(),
+                        Depth.infinity, true, true, false);
+
+            fail("This test should fail because iotaPath is not a child of " +
+                 "the relativeToDir parameter");
+        }
+        catch (Exception ignored)
+        {
+        }
+
+        // Test diff with a relative path on a directory with prop
+        // changes.
+        String aPath = fileToSVNPath(new File(thisTest.getWCPath() + "/A"),
+                                     false);
+
+        expectedDiffOutput = NL + "Property changes on: A" + NL +
+            underSepLine +
+            "Added: testprop" + NL +
+            "   + Test property value." + NL + NL;
+
+        client.propertySet(aPath, "testprop", "Test property value.", false);
+        client.diff(aPath, Revision.BASE, aPath, Revision.WORKING, wcPath,
+                    diffOutput.getPath(), Depth.infinity, true, true, false);
+        assertFileContentsEquals("Unexpected diff output in file '" +
+                                 diffOutput.getPath() + '\'',
+                                 expectedDiffOutput, diffOutput);
     }
 
     private void assertFileContentsEquals(String msg, String expected,
diff --git a/subversion/bindings/swig/core.i b/subversion/bindings/swig/core.i
index d2837db..dd7902e 100644
--- a/subversion/bindings/swig/core.i
+++ b/subversion/bindings/swig/core.i
@@ -1020,11 +1020,9 @@
 static svn_error_t *
 svn_swig_mergeinfo_merge(apr_hash_t **mergeinfo_inout,
                          apr_hash_t *changes,
-                         svn_merge_range_inheritance_t consider_inheritance,
                          apr_pool_t *pool)
 {
-  return svn_mergeinfo_merge(*mergeinfo_inout, changes, consider_inheritance,
-                             pool);
+  return svn_mergeinfo_merge(*mergeinfo_inout, changes, pool);
 }
 
 static svn_error_t *
@@ -1036,11 +1034,9 @@
 static svn_error_t *
 svn_swig_rangelist_merge(apr_array_header_t **rangelist_inout,
                          apr_array_header_t *changes,
-                         svn_merge_range_inheritance_t consider_inheritance,
                          apr_pool_t *pool)
 {
-  return svn_rangelist_merge(rangelist_inout, changes, consider_inheritance,
-                             pool);
+  return svn_rangelist_merge(rangelist_inout, changes, pool);
 }
 
 static svn_error_t *
diff --git a/subversion/bindings/swig/python/svn/core.py b/subversion/bindings/swig/python/svn/core.py
index 87b9d5f..435c405 100644
--- a/subversion/bindings/swig/python/svn/core.py
+++ b/subversion/bindings/swig/python/svn/core.py
@@ -74,22 +74,14 @@
   # determine order
   return cmp(char1, char2)
 
-def svn_mergeinfo_merge(mergeinfo, changes,
-                        consider_inheritance=svn_rangelist_equal_inheritance):
-  """CONSIDER_INHERITANCE is one of svn_rangelist_ignore_inheritance,
-  svn_rangelist_equal_inheritance, or svn_rangelist_only_inheritable."""
-  return _libsvncore.svn_swig_mergeinfo_merge(mergeinfo, changes,
-                                              consider_inheritance)
+def svn_mergeinfo_merge(mergeinfo, changes):
+  return _libsvncore.svn_swig_mergeinfo_merge(mergeinfo, changes)
 
 def svn_mergeinfo_sort(mergeinfo):
   return _libsvncore.svn_swig_mergeinfo_sort(mergeinfo)
 
-def svn_rangelist_merge(rangelist, changes,
-                        consider_inheritance=svn_rangelist_equal_inheritance):
-  """CONSIDER_INHERITANCE is one of svn_rangelist_ignore_inheritance,
-  svn_rangelist_equal_inheritance, or svn_rangelist_only_inheritable."""
-  return _libsvncore.svn_swig_rangelist_merge(rangelist, changes,
-                                              consider_inheritance)
+def svn_rangelist_merge(rangelist, changes):
+  return _libsvncore.svn_swig_rangelist_merge(rangelist, changes)
 
 def svn_rangelist_reverse(rangelist):
   return _libsvncore.svn_swig_rangelist_reverse(rangelist)
diff --git a/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c b/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c
index 2119c9a..c7eae85 100644
--- a/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c
+++ b/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c
@@ -870,25 +870,6 @@
   }
 }
 
-svn_merge_range_inheritance_t
-svn_swig_rb_to_merge_range_inheritance(VALUE value)
-{
-  if (NIL_P(value)) {
-    return svn_rangelist_ignore_inheritance;
-  } else if (RTEST(rb_obj_is_kind_of(value, rb_cString)) ||
-             RTEST(rb_obj_is_kind_of(value, rb_cSymbol))) {
-    return NUM2INT(resolve_constant(rb_svn_core(), "RANGELIST_", value));
-  } else if (RTEST(rb_obj_is_kind_of(value, rb_cInteger))) {
-    return NUM2INT(value);
-  } else {
-    rb_raise(rb_eArgError,
-       "'%s' must be RANGELIST_STRING (e.g. \"ignore_inheritance\" or"
-       " :ignore_inheritance) "
-       "or Svn::Core::RANGELIST_*",
-       r2c_inspect(value));
-  }
-}
-
 svn_mergeinfo_inheritance_t
 svn_swig_rb_to_mergeinfo_inheritance(VALUE value)
 {
diff --git a/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h b/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h
index c4d3f6b..af26058 100644
--- a/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h
+++ b/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.h
@@ -79,10 +79,6 @@
 svn_depth_t svn_swig_rb_to_depth(VALUE value);
 
 SVN_RB_SWIG_SWIGUTIL_EXPORT
-svn_merge_range_inheritance_t
-svn_swig_rb_to_merge_range_inheritance(VALUE value);
-
-SVN_RB_SWIG_SWIGUTIL_EXPORT
 svn_mergeinfo_inheritance_t svn_swig_rb_to_mergeinfo_inheritance(VALUE value);
 
 SVN_RB_SWIG_SWIGUTIL_EXPORT
diff --git a/subversion/bindings/swig/ruby/svn/core.rb b/subversion/bindings/swig/ruby/svn/core.rb
index 5504838..866f6a9 100644
--- a/subversion/bindings/swig/ruby/svn/core.rb
+++ b/subversion/bindings/swig/ruby/svn/core.rb
@@ -728,8 +728,8 @@
         end
       end
 
-      def merge(changes, consider_inheritance=nil)
-        self.class.new(Core.swig_mergeinfo_merge(self, changes, consider_inheritance))
+      def merge(changes)
+        self.class.new(Core.swig_mergeinfo_merge(self, changes))
       end
 
       def remove(eraser)
@@ -762,8 +762,8 @@
         end
       end
 
-      def merge(changes, consider_inheritance=nil)
-        self.class.new(*Core.swig_rangelist_merge(self, changes, consider_inheritance))
+      def merge(changes)
+        self.class.new(*Core.swig_rangelist_merge(self, changes))
       end
 
       def remove(eraser, consider_inheritance=nil)
diff --git a/subversion/include/private/svn_mergeinfo_private.h b/subversion/include/private/svn_mergeinfo_private.h
index e409c7c..ff83251 100644
--- a/subversion/include/private/svn_mergeinfo_private.h
+++ b/subversion/include/private/svn_mergeinfo_private.h
@@ -37,8 +37,21 @@
                          apr_pool_t *pool);
 
 /** Return whether @a info1 and @a info2 are equal in @a *is_equal.
- * @a consider_inheritance determines how to account for the inheritability
- * of the rangelists in @a info1 and @a info2 when calculating equality.
+ *
+ * @a consider_inheritance determines how the rangelists in the two
+ * hashes are compared for equality.  If @a consider_inheritance is FALSE,
+ * then the start and end revisions of the @c svn_merge_range_t's being
+ * compared are the only factors considered when determining equality.
+ * 
+ *  e.g. '/trunk: 1,3-4*,5' == '/trunk: 1,3-5'
+ *
+ * If @a consider_inheritance is TRUE, then the inheritability of the
+ * @c svn_merge_range_t's is also considered and must be the same for two
+ * otherwise identical ranges to be judged equal.
+ *
+ *  e.g. '/trunk: 1,3-4*,5' != '/trunk: 1,3-5'
+ *       '/trunk: 1,3-4*,5' == '/trunk: 1,3-4*,5'
+ *       '/trunk: 1,3-4,5'  == '/trunk: 1,3-4,5'
  *
  * Use @a pool for temporary allocations.
  *
@@ -48,7 +61,7 @@
 svn_mergeinfo__equals(svn_boolean_t *is_equal,
                       apr_hash_t *info1,
                       apr_hash_t *info2,
-                      svn_merge_range_inheritance_t consider_inheritance,
+                      svn_boolean_t consider_inheritance,
                       apr_pool_t *pool);
 
 #ifdef __cplusplus
diff --git a/subversion/include/svn_client.h b/subversion/include/svn_client.h
index 1b3a1d7..d115522 100644
--- a/subversion/include/svn_client.h
+++ b/subversion/include/svn_client.h
@@ -1994,6 +1994,14 @@
  * of the diff to @a outfile, and any errors to @a errfile.  @a path1
  * and @a path2 can be either working-copy paths or URLs.
  *
+ * If @a relative_to_dir is not @c NULL, the @a original_path and
+ * @a modified_path will have the @a relative_to_dir stripped from the
+ * front of the respective paths.  If @a relative_to_dir is @c NULL,
+ * paths will be handled the pre-1.5 way.  If @a relative_to_dir is not
+ * @c NULL but @a relative_to_dir is not a parent path of the target,
+ * an error is returned. Finally, if @a relative_to_dir is a URL, an
+ * error will be returned.
+ *
  * If either @a revision1 or @a revision2 has an `unspecified' or
  * unrecognized `kind', return @c SVN_ERR_CLIENT_BAD_REVISION.
  *
@@ -2035,6 +2043,9 @@
  * @note @a header_encoding doesn't affect headers generated by external
  * diff programs.
  *
+ * @note @a relative_to_dir doesn't affect the path index generated by
+ * external diff programs.
+ *
  * @since New in 1.5.
  */
 svn_error_t *svn_client_diff4(const apr_array_header_t *diff_options,
@@ -2042,6 +2053,7 @@
                               const svn_opt_revision_t *revision1,
                               const char *path2,
                               const svn_opt_revision_t *revision2,
+                              const char *relative_to_dir,
                               svn_depth_t depth,
                               svn_boolean_t ignore_ancestry,
                               svn_boolean_t no_diff_deleted,
@@ -2139,6 +2151,7 @@
                                   const svn_opt_revision_t *peg_revision,
                                   const svn_opt_revision_t *start_revision,
                                   const svn_opt_revision_t *end_revision,
+                                  const char *relative_to_dir,
                                   svn_depth_t depth,
                                   svn_boolean_t ignore_ancestry,
                                   svn_boolean_t no_diff_deleted,
diff --git a/subversion/include/svn_diff.h b/subversion/include/svn_diff.h
index e8b74d2..541ddd5 100644
--- a/subversion/include/svn_diff.h
+++ b/subversion/include/svn_diff.h
@@ -460,14 +460,36 @@
 /** A convenience function to produce unified diff output from the
  * diff generated by svn_diff_file_diff().
  *
- * @since New in 1.3.
+ * @since New in 1.5.
  *
  * Output a @a diff between @a original_path and @a modified_path in unified
  * context diff format to @a output_stream.  Optionally supply
  * @a original_header and/or @a modified_header to be displayed in the header
  * of the output.  If @a original_header or @a modified_header is @c NULL, a
  * default header will be displayed, consisting of path and last modified time.
- * Output all headers and markers in @a header_encoding.
+ * Output all headers and markers in @a header_encoding.  If @a relative_to_dir
+ * is not @c NULL, the @a original_path and @a modified_path will have the
+ * @a relative_to_dir stripped from the front of the respective paths.  If
+ * @a relative_to_dir is @c NULL, paths will be handled the pre-1.5 way.  If
+ * @a relative_to_dir is not @c NULL but @a relative_to_dir is not a parent
+ * path of the target, an error is returned. Finally, if @a relative_to_dir
+ * is a URL, an error will be returned.
+ */
+svn_error_t *
+svn_diff_file_output_unified3(svn_stream_t *output_stream,
+                              svn_diff_t *diff,
+                              const char *original_path,
+                              const char *modified_path,
+                              const char *original_header,
+                              const char *modified_header,
+                              const char *header_encoding,
+                              const char *relative_to_dir,
+                              apr_pool_t *pool);
+
+/** Similar to svn_diff_file_output_unified3(), but with @a relative_to_dir
+ * set to NULL.
+ *
+ * @deprecated Provided for backwards compatibility with the 1.3 API.
  */
 svn_error_t *
 svn_diff_file_output_unified2(svn_stream_t *output_stream,
diff --git a/subversion/include/svn_error_codes.h b/subversion/include/svn_error_codes.h
index e16bc06..7517e37 100644
--- a/subversion/include/svn_error_codes.h
+++ b/subversion/include/svn_error_codes.h
@@ -183,6 +183,10 @@
              SVN_ERR_BAD_CATEGORY_START + 6,
              "Version file format not correct")
 
+  SVN_ERRDEF(SVN_ERR_BAD_RELATIVE_PATH,
+             SVN_ERR_BAD_CATEGORY_START + 7,
+             "Path is not an immediate child of the specified directory")
+
   /* xml errors */
 
   SVN_ERRDEF(SVN_ERR_XML_ATTRIB_NOT_FOUND,
diff --git a/subversion/include/svn_mergeinfo.h b/subversion/include/svn_mergeinfo.h
index c837b7c..aaaba97 100644
--- a/subversion/include/svn_mergeinfo.h
+++ b/subversion/include/svn_mergeinfo.h
@@ -67,7 +67,7 @@
  *
  *   Token             Definition
  *   -----             ----------
- *   revisionrange     REVISION "-" REVISION
+ *   revisionrange     REVISION1 "-" REVISION2
  *   revisioneelement  (revisionrange | REVISION)"*"?
  *   rangelist         revisioneelement (COMMA revisioneelement)*
  *   revisionline      PATHNAME COLON rangelist
@@ -80,9 +80,10 @@
  * the source pathname.
  *
  * Rangelists must be sorted from lowest to highest revision and cannot
- * contain overlapping revisionlistelements.  Single revisions that can be
- * represented by a revisionrange are allowed (e.g. '5,6,7,8,9-12' or '5-12'
- * are both acceptable).
+ * contain overlapping revisionlistelements.  REVISION1 must be less than
+ * REVISION2.  Consecutive single revisions that can be represented by a
+ * revisionrange are allowed (e.g. '5,6,7,8,9-12' or '5-12' are both
+ * acceptable).
  */
 
 /* Suffix for SVN_PROP_MERGE_INFO revision ranges indicating a given
@@ -94,6 +95,11 @@
  * elements.  If no mergeinfo is available, return an empty hash
  * (never @c NULL).  Perform temporary allocations in @a pool.
  *
+ * If @a input is not a grammatically correct @c SVN_PROP_MERGE_INFO
+ * property, contains overlapping or unordered revision ranges, or revision
+ * ranges with a start revision greater than or equal to its end revision,
+ * then return @c SVN_ERR_MERGE_INFO_PARSE_ERROR.
+ *
  * Note: @a *mergeinfo will contain rangelists that are guaranteed to
  * be sorted (ordered by smallest revision ranges to largest).
  * @since New in 1.5.
@@ -108,22 +114,39 @@
  * added (neither output argument will ever be @c NULL), stored as the
  * usual mapping of paths to lists of @c svn_merge_range_t *'s.
  *
- * @a consider_inheritance determines how to account for the inheritability
- * of the rangelists in @a mergefrom and @a mergeto when calculating the
- * diff.
+ * @a consider_inheritance determines how the rangelists in the two
+ * hashes are compared for equality.  If @a consider_inheritance is FALSE,
+ * then the start and end revisions of the @c svn_merge_range_t's being
+ * compared are the only factors considered when determining equality.
+ * 
+ *  e.g. '/trunk: 1,3-4*,5' == '/trunk: 1,3-5'
+ *
+ * If @a consider_inheritance is TRUE, then the inheritability of the
+ * @c svn_merge_range_t's is also considered and must be the same for two
+ * otherwise identical ranges to be judged equal.
+ *
+ *  e.g. '/trunk: 1,3-4*,5' != '/trunk: 1,3-5'
+ *       '/trunk: 1,3-4*,5' == '/trunk: 1,3-4*,5'
+ *       '/trunk: 1,3-4,5'  == '/trunk: 1,3-4,5'
  *
  * @since New in 1.5.
  */
 svn_error_t *
 svn_mergeinfo_diff(apr_hash_t **deleted, apr_hash_t **added,
                    apr_hash_t *mergefrom, apr_hash_t *mergeto,
-                   svn_merge_range_inheritance_t consider_inheritance,
+                   svn_boolean_t consider_inheritance,
                    apr_pool_t *pool);
 
 /** Merge hash of mergeinfo, @a changes, into existing hash @a
- * mergeinfo.  @a consider_inheritance determines how to account for
- * the inheritability of the rangelists in @a changes and @a *mergeinfo
- * when merging.
+ * mergeinfo.
+ *
+ * When intersecting rangelists for a path are merged, the inheritability of
+ * the resulting svn_merge_range_t depends on the inheritability of the
+ * operands.  If two non-inheritable ranges are merged the result is always
+ * non-inheritable, in all other cases the resulting range is inheritable.
+ *
+ *  e.g. '/A: 1,3-4'  merged with '/A: 1,3,4*,5' --> '/A: 1,3-5'
+ *       '/A: 1,3-4*' merged with '/A: 1,3,4*,5' --> '/A: 1,3,4*,5'
  *
  * Note: @a mergeinfo and @a changes must have rangelists that are
  * sorted as said by @c svn_sort_compare_ranges().  After the merge @a
@@ -134,7 +157,6 @@
  */
 svn_error_t *
 svn_mergeinfo_merge(apr_hash_t *mergeinfo, apr_hash_t *changes,
-                    svn_merge_range_inheritance_t consider_inheritance,
                     apr_pool_t *pool);
 
 /** Removes @a eraser (the subtrahend) from @a whiteboard (the
@@ -152,22 +174,24 @@
  * output argument will ever be @c NULL).
  *
  * @a consider_inheritance determines how to account for the inheritability
- * of @a to and @a from when calculating the diff.
+ * of the two rangelist's ranges when calculating the diff,
+ * @see svn_mergeinfo_diff().
  *
  * @since New in 1.5.
  */
 svn_error_t *
 svn_rangelist_diff(apr_array_header_t **deleted, apr_array_header_t **added,
                    apr_array_header_t *from, apr_array_header_t *to,
-                   svn_merge_range_inheritance_t consider_inheritance,
+                   svn_boolean_t consider_inheritance,
                    apr_pool_t *pool);
 
 /** Merge two rangelists consisting of @c svn_merge_range_t *
  * elements, @a *rangelist and @a changes, placing the results in
  * @a *rangelist.
  *
- * @a consider_inheritance determines how to account for the inheritability
- * of @a changes and @a *rangelist when merging.
+ * When intersecting rangelists are merged, the inheritability of
+ * the resulting svn_merge_range_t depends on the inheritability of the
+ * operands, @see svn_mergeinfo_merge().
  *
  * Note: @a *rangelist and @a changes must be sorted as said by @c
  * svn_sort_compare_ranges().  @a *rangelist is guaranteed to remain
@@ -178,7 +202,6 @@
 svn_error_t *
 svn_rangelist_merge(apr_array_header_t **rangelist,
                     apr_array_header_t *changes,
-                    svn_merge_range_inheritance_t consider_inheritance,
                     apr_pool_t *pool);
 
 /** Removes @a eraser (the subtrahend) from @a whiteboard (the
@@ -188,15 +211,16 @@
  * svn_sort_compare_ranges().  @a output is guaranteed to be in sorted
  * order.
  *
- * @a consider_inheritance determines how to account for the inheritability
- * of @a whiteboard and @a *eraser when removing ranges.
+ * @a consider_inheritance determines how to account for the
+ * @c svn_merge_range_t inheritable field when comparing @a whiteboard's
+ * and @a *eraser's rangelists for equality.  @See svn_mergeinfo_diff().
  *
  * @since New in 1.5.
  */
 svn_error_t *
 svn_rangelist_remove(apr_array_header_t **output, apr_array_header_t *eraser,
                      apr_array_header_t *whiteboard,
-                     svn_merge_range_inheritance_t consider_inheritance,
+                     svn_boolean_t consider_inheritance,
                      apr_pool_t *pool);
 
 /** Find the intersection of two rangelists consisting of @c
@@ -267,27 +291,30 @@
                           svn_revnum_t end,
                           apr_pool_t *pool);
 
-/** Remove redundancies between @ *range_1 and @ *range_2.  @ *range_1 and/or
- * @ *range_2 may be additive or subtractive ranges.  The ranges should be
- * sorted such that the minimum of @ *range_1->start and @ *range_1->end is
- * less than or equal to the minimum of @ *range_2->start and
- * @ *range_2->end.
+/** Remove redundancies between @a *range_1 and @a *range_2.  @a
+ * *range_1 and/or @a *range_2 may be additive or subtractive ranges.
+ * The ranges should be sorted such that the minimum of @c
+ * *range_1->start and @c *range_1->end is less than or equal to the
+ * minimum of @c *range_2->start and @c *range_2->end.
  *
- * If either @ *range_1 or @ *range_2 is NULL, either range contains
+ * If either @a *range_1 or @a *range_2 is NULL, either range contains
  * invalid svn_revnum_t's, or the two ranges do not intersect, then do
- * nothing and return FALSE.
+ * nothing and return @c FALSE.
  *
- * If the two ranges can be reduced to one range, set @ *range_1 to represent
- * that range, set @ *range_2 to NULL, and return TRUE.
+ * If the two ranges can be reduced to one range, set @a *range_1 to
+ * represent that range, set @a *range_2 to @c NULL, and return @c
+ * TRUE.
  *
- * If the two ranges cancel each other out set both @ *range_1 and
- * @ *range_2 to NULL and return TRUE.
+ * If the two ranges cancel each other out set both @a *range_1 and @a
+ * *range_2 to @c NULL and return @c TRUE.
  *
- * If the two ranges intersect but cannot be represented by one range (because
- * one range is additive and the other subtractive) then modify @ *range_1 and
- * @ *range_2 to remove the intersecting ranges and return TRUE.
+ * If the two ranges intersect but cannot be represented by one range
+ * (because one range is additive and the other subtractive) then
+ * modify @a *range_1 and @a *range_2 to remove the intersecting
+ * ranges and return @c TRUE.
  *
- * The inheritability of @ *range_1 or @ *range_2 is not taken into account.
+ * The inheritability of @a *range_1 or @a *range_2 is not taken into
+ * account.
  *
  * @since New in 1.5.
  */
@@ -326,7 +353,7 @@
 
 /** Take a hash of mergeinfo in @a mergeinfo, and sort the rangelists
  * associated with each key (in place).
- * Note: This does not sort the hash, only the range lists in the
+ * Note: This does not sort the hash, only the rangelists in the
  * hash.
  * @since New in 1.5
  */
diff --git a/subversion/include/svn_types.h b/subversion/include/svn_types.h
index 165f73b..f6430d6 100644
--- a/subversion/include/svn_types.h
+++ b/subversion/include/svn_types.h
@@ -829,24 +829,6 @@
 } svn_merge_range_t;
 
 /**
- * The three ways to consider the inheritable member when
- * comparing @c svn_merge_range_t.
- *
- * @since New in 1.5.
- */
-typedef enum
-{
-  /* Don't take inheritability into consideration. */
-  svn_rangelist_ignore_inheritance,
-
-  /* Inheritability of both ranges must be the same. */
-  svn_rangelist_equal_inheritance,
-
-  /* Inheritability of both ranges must be @c TRUE. */
-  svn_rangelist_only_inheritable
-} svn_merge_range_inheritance_t;
-
-/**
  * Return a copy of @a range, allocated in @a pool.
  *
  * @since New in 1.5.
diff --git a/subversion/libsvn_client/copy.c b/subversion/libsvn_client/copy.c
index 14290f3..1c4b3f9 100644
--- a/subversion/libsvn_client/copy.c
+++ b/subversion/libsvn_client/copy.c
@@ -182,7 +182,7 @@
       if (src_mergeinfo)
         {
           SVN_ERR(svn_mergeinfo_merge(*target_mergeinfo, src_mergeinfo,
-                                      svn_rangelist_equal_inheritance, pool));
+                                      pool));
         }
     }
   else
@@ -209,8 +209,7 @@
 
   /* Combine the provided mergeinfo with any mergeinfo from the WC. */
   if (wc_mergeinfo)
-    SVN_ERR(svn_mergeinfo_merge(wc_mergeinfo, mergeinfo,
-                                svn_rangelist_equal_inheritance, pool));
+    SVN_ERR(svn_mergeinfo_merge(wc_mergeinfo, mergeinfo, pool));
   else
     wc_mergeinfo = mergeinfo;
 
@@ -1336,8 +1335,7 @@
                                           pair->src, FALSE, adm_access, ctx,
                                           pool));
       if (wc_mergeinfo)
-        SVN_ERR(svn_mergeinfo_merge(mergeinfo, wc_mergeinfo,
-                                    svn_rangelist_equal_inheritance, pool));
+        SVN_ERR(svn_mergeinfo_merge(mergeinfo, wc_mergeinfo, pool));
       SVN_ERR(svn_mergeinfo__to_string((svn_string_t **)
                                        &mergeinfo_prop->value,
                                        mergeinfo, pool));
diff --git a/subversion/libsvn_client/diff.c b/subversion/libsvn_client/diff.c
index c6751b5..b8cc19a 100644
--- a/subversion/libsvn_client/diff.c
+++ b/subversion/libsvn_client/diff.c
@@ -118,7 +118,7 @@
 
   SVN_ERR(svn_mergeinfo_diff(&deleted, &added, old_mergeinfo_hash,
                              new_mergeinfo_hash,
-                             svn_rangelist_equal_inheritance, pool));
+                             TRUE, pool));
 
   for (hi = apr_hash_first(pool, deleted);
        hi; hi = apr_hash_next(hi))
@@ -159,6 +159,11 @@
   return SVN_NO_ERROR;
 }
 
+#define MAKE_ERR_BAD_RELATIVE_PATH(path, relative_to_dir) \
+        svn_error_createf(SVN_ERR_BAD_RELATIVE_PATH, NULL, \
+                          _("Path '%s' must be an immediate child of " \
+                            "the directory '%s'"), path, relative_to_dir)
+
 /* A helper func that writes out verbal descriptions of property diffs
    to FILE.   Of course, the apr_file_t will probably be the 'outfile'
    passed to svn_client_diff4, which is probably stdout. */
@@ -168,10 +173,22 @@
                    const char *path,
                    const char *encoding,
                    apr_file_t *file,
+                   const char *relative_to_dir,
                    apr_pool_t *pool)
 {
   int i;
 
+  if (relative_to_dir)
+    {
+      /* Possibly adjust the path shown in the output (see issue #2723). */
+      const char *child_path = svn_path_is_child(relative_to_dir, path, pool);
+
+      if (child_path)
+        path = child_path;
+      else
+        return MAKE_ERR_BAD_RELATIVE_PATH(path, relative_to_dir);
+    }
+
   SVN_ERR(file_printf_from_utf8(file, encoding,
                                 _("%sProperty changes on: %s%s"),
                                 APR_EOL_STR,
@@ -312,6 +329,9 @@
      unconditionally, even if the diffs are empty. */
   svn_boolean_t force_empty;
 
+  /* The directory that diff target paths should be considered as
+     relative to for output generation (see issue #2723). */
+  const char *relative_to_dir;
 };
 
 
@@ -352,7 +372,9 @@
   if (props->nelts > 0)
     SVN_ERR(display_prop_diffs(props, original_props, path,
                                diff_cmd_baton->header_encoding,
-                               diff_cmd_baton->outfile, subpool));
+                               diff_cmd_baton->outfile,
+                               diff_cmd_baton->relative_to_dir,
+                               subpool));
 
   if (state)
     *state = svn_wc_notify_state_unknown;
@@ -381,6 +403,7 @@
   int nargs, exitcode;
   apr_pool_t *subpool = svn_pool_create(diff_cmd_baton->pool);
   svn_stream_t *os;
+  const char *rel_to_dir = diff_cmd_baton->relative_to_dir;
   apr_file_t *errfile = diff_cmd_baton->errfile;
   const char *label1, *label2;
   svn_boolean_t mt1_binary = FALSE, mt2_binary = FALSE;
@@ -451,6 +474,31 @@
   else
     path2 = apr_psprintf(subpool, "%s\t(.../%s)", path, path2);
 
+  if (diff_cmd_baton->relative_to_dir)
+    {
+      /* Possibly adjust the paths shown in the output (see issue #2723). */
+      const char *child_path = svn_path_is_child(rel_to_dir, path, subpool);
+
+      if (child_path)
+        path = child_path;
+      else
+        return MAKE_ERR_BAD_RELATIVE_PATH(path, rel_to_dir);
+
+      child_path = svn_path_is_child(rel_to_dir, path1, subpool);
+
+      if (child_path)
+        path1 = child_path;
+      else
+        return MAKE_ERR_BAD_RELATIVE_PATH(path1, rel_to_dir);
+
+      child_path = svn_path_is_child(rel_to_dir, path2, subpool);
+
+      if (child_path)
+        path2 = child_path;
+      else
+        return MAKE_ERR_BAD_RELATIVE_PATH(path2, rel_to_dir);
+    }
+
   label1 = diff_label(path1, rev1, subpool);
   label2 = diff_label(path2, rev2, subpool);
 
@@ -545,11 +593,10 @@
                   (os, diff_cmd_baton->header_encoding, subpool,
                    "Index: %s" APR_EOL_STR "%s" APR_EOL_STR,
                    path, equal_string));
-
           /* Output the actual diff */
-          SVN_ERR(svn_diff_file_output_unified2
+          SVN_ERR(svn_diff_file_output_unified3
                   (os, diff, tmpfile1, tmpfile2, label1, label2,
-                   diff_cmd_baton->header_encoding, subpool));
+                   diff_cmd_baton->header_encoding, rel_to_dir, subpool));
         }
     }
 
@@ -1489,6 +1536,7 @@
                  const svn_opt_revision_t *revision1,
                  const char *path2,
                  const svn_opt_revision_t *revision2,
+                 const char *relative_to_dir,
                  svn_depth_t depth,
                  svn_boolean_t ignore_ancestry,
                  svn_boolean_t no_diff_deleted,
@@ -1542,6 +1590,7 @@
   diff_cmd_baton.config = ctx->config;
   diff_cmd_baton.force_empty = FALSE;
   diff_cmd_baton.force_binary = ignore_content_type;
+  diff_cmd_baton.relative_to_dir = relative_to_dir;
 
   return do_diff(&diff_params, &diff_callbacks, &diff_cmd_baton, ctx, pool);
 }
@@ -1563,7 +1612,8 @@
                  apr_pool_t *pool)
 {
   return svn_client_diff4(options, path1, revision1, path2,
-                          revision2, SVN_DEPTH_INFINITY_OR_FILES(recurse),
+                          revision2, NULL,
+                          SVN_DEPTH_INFINITY_OR_FILES(recurse),
                           ignore_ancestry, no_diff_deleted,
                           ignore_content_type, header_encoding,
                           outfile, errfile, ctx, pool);
@@ -1615,6 +1665,7 @@
                      const svn_opt_revision_t *peg_revision,
                      const svn_opt_revision_t *start_revision,
                      const svn_opt_revision_t *end_revision,
+                     const char *relative_to_dir,
                      svn_depth_t depth,
                      svn_boolean_t ignore_ancestry,
                      svn_boolean_t no_diff_deleted,
@@ -1664,6 +1715,7 @@
   diff_cmd_baton.config = ctx->config;
   diff_cmd_baton.force_empty = FALSE;
   diff_cmd_baton.force_binary = ignore_content_type;
+  diff_cmd_baton.relative_to_dir = relative_to_dir;
 
   return do_diff(&diff_params, &diff_callbacks, &diff_cmd_baton, ctx, pool);
 }
@@ -1689,6 +1741,7 @@
                               peg_revision,
                               start_revision,
                               end_revision,
+                              NULL,
                               SVN_DEPTH_INFINITY_OR_FILES(recurse),
                               ignore_ancestry,
                               no_diff_deleted,
diff --git a/subversion/libsvn_client/merge.c b/subversion/libsvn_client/merge.c
index 4c5ed44..bfdcc72 100644
--- a/subversion/libsvn_client/merge.c
+++ b/subversion/libsvn_client/merge.c
@@ -158,14 +158,16 @@
 /*** Callbacks for 'svn merge', invoked by the repos-diff editor. ***/
 
 
-struct merge_cmd_baton {
+typedef struct merge_cmd_baton_t {
   svn_boolean_t force;
-  svn_boolean_t record_only;          /* Whether to only record mergeinfo. */
   svn_boolean_t dry_run;
+  svn_boolean_t record_only;          /* Whether to only record mergeinfo. */
   svn_boolean_t same_repos;           /* Whether the merge source repository
                                          is the same repository as the
                                          target.  Defaults to FALSE if DRY_RUN
                                          is TRUE.*/
+  svn_boolean_t ignore_ancestry;      /* Are we ignoring ancestry (and by
+                                         extension, mergeinfo)? */
   svn_boolean_t target_missing_child; /* Whether working copy target of the
                                          merge is missing any immediate
                                          children. */
@@ -214,12 +216,12 @@
   svn_boolean_t target_has_dummy_merge_range;
 
   apr_pool_t *pool;
-};
+} merge_cmd_baton_t;
 
 apr_hash_t *
 svn_client__dry_run_deletions(void *merge_cmd_baton)
 {
-  struct merge_cmd_baton *merge_b = merge_cmd_baton;
+  merge_cmd_baton_t *merge_b = merge_cmd_baton;
   return merge_b->dry_run_deletions;
 }
 
@@ -228,7 +230,7 @@
    weren't in dry_run mode (issue #2584).  Assumes that WCPATH is
    still versioned (e.g. has an associated entry). */
 static APR_INLINE svn_boolean_t
-dry_run_deleted_p(struct merge_cmd_baton *merge_b, const char *wcpath)
+dry_run_deleted_p(merge_cmd_baton_t *merge_b, const char *wcpath)
 {
   return (merge_b->dry_run &&
           apr_hash_get(merge_b->dry_run_deletions, wcpath,
@@ -238,7 +240,7 @@
 /* Return whether any WC path was put in conflict by the merge
    operation corresponding to MERGE_B. */
 static APR_INLINE svn_boolean_t
-is_path_conflicted_by_merge(struct merge_cmd_baton *merge_b)
+is_path_conflicted_by_merge(merge_cmd_baton_t *merge_b)
 {
   return (merge_b->conflicted_paths &&
           apr_hash_count(merge_b->conflicted_paths) > 0);
@@ -255,7 +257,7 @@
                     void *baton)
 {
   apr_array_header_t *props;
-  struct merge_cmd_baton *merge_b = baton;
+  merge_cmd_baton_t *merge_b = baton;
   svn_client_ctx_t *ctx = merge_b->ctx;
   apr_pool_t *subpool = svn_pool_create(merge_b->pool);
   svn_error_t *err;
@@ -371,7 +373,7 @@
                    apr_hash_t *original_props,
                    void *baton)
 {
-  struct merge_cmd_baton *merge_b = baton;
+  merge_cmd_baton_t *merge_b = baton;
   apr_pool_t *subpool = svn_pool_create(merge_b->pool);
   svn_boolean_t merge_required = TRUE;
   enum svn_wc_merge_outcome_t merge_outcome;
@@ -534,7 +536,7 @@
                  apr_hash_t *original_props,
                  void *baton)
 {
-  struct merge_cmd_baton *merge_b = baton;
+  merge_cmd_baton_t *merge_b = baton;
   apr_pool_t *subpool = svn_pool_create(merge_b->pool);
   svn_node_kind_t kind;
   const char *copyfrom_url;
@@ -695,7 +697,7 @@
                    apr_hash_t *original_props,
                    void *baton)
 {
-  struct merge_cmd_baton *merge_b = baton;
+  merge_cmd_baton_t *merge_b = baton;
   apr_pool_t *subpool = svn_pool_create(merge_b->pool);
   svn_node_kind_t kind;
   svn_wc_adm_access_t *parent_access;
@@ -763,7 +765,7 @@
                 svn_revnum_t rev,
                 void *baton)
 {
-  struct merge_cmd_baton *merge_b = baton;
+  merge_cmd_baton_t *merge_b = baton;
   apr_pool_t *subpool = svn_pool_create(merge_b->pool);
   svn_node_kind_t kind;
   const svn_wc_entry_t *entry;
@@ -923,7 +925,7 @@
                   const char *path,
                   void *baton)
 {
-  struct merge_cmd_baton *merge_b = baton;
+  merge_cmd_baton_t *merge_b = baton;
   apr_pool_t *subpool = svn_pool_create(merge_b->pool);
   svn_node_kind_t kind;
   svn_wc_adm_access_t *parent_access;
@@ -1067,7 +1069,7 @@
 
   SVN_ERR(svn_mergeinfo_diff(&deleted_mergeinfo, &added_mergeinfo,
                              start_mergeinfo, end_mergeinfo,
-                             svn_rangelist_equal_inheritance, pool));
+                             FALSE, pool));
 
   if (added_mergeinfo)
     {
@@ -1089,7 +1091,7 @@
   if (src_rangelist_for_tgt)
     SVN_ERR(svn_rangelist_remove(requested_rangelist, src_rangelist_for_tgt,
                                  *requested_rangelist,
-                                 svn_rangelist_equal_inheritance, pool));
+                                 FALSE, pool));
   return SVN_NO_ERROR;
 }
 
@@ -1203,7 +1205,7 @@
           /* Return only those revs not already represented by this WC. */
           SVN_ERR(svn_rangelist_remove(remaining_ranges, target_rangelist,
                                        requested_merge,
-                                       svn_rangelist_ignore_inheritance,
+                                       FALSE, 
                                        pool));
         }
     }
@@ -1249,7 +1251,7 @@
   int cur_ancestor_index;
 
   /* We use this to make a decision on merge begin line notifications. */
-  struct merge_cmd_baton *merge_b;
+  merge_cmd_baton_t *merge_b;
 
   /* Pool used in notification_receiver() to avoid the iteration
      sub-pool which is passed in, then subsequently destroyed. */
@@ -1398,7 +1400,7 @@
                            svn_depth_t depth,
                            svn_wc_adm_access_t *adm_access,
                            notification_receiver_baton_t *notify_b,
-                           struct merge_cmd_baton *merge_b,
+                           merge_cmd_baton_t *merge_b,
                            apr_pool_t *pool)
 {
   apr_array_header_t *rangelist;
@@ -1558,13 +1560,12 @@
           ranges = svn_rangelist_dup(ranges, subpool);
           SVN_ERR(svn_rangelist_reverse(ranges, subpool));
           SVN_ERR(svn_rangelist_remove(&rangelist, ranges, rangelist,
-                                       svn_rangelist_ignore_inheritance,
+                                       FALSE,
                                        subpool));
         }
       else
         {
           SVN_ERR(svn_rangelist_merge(&rangelist, ranges,
-                                      svn_rangelist_equal_inheritance,
                                       subpool));
         }
       /* Update the mergeinfo by adjusting the path's rangelist. */
@@ -1866,11 +1867,10 @@
                           apr_array_header_t *children_with_mergeinfo,
                           svn_boolean_t is_rollback,
                           svn_depth_t depth,
-                          svn_boolean_t ignore_ancestry,
                           notification_receiver_baton_t *notify_b,
                           svn_wc_adm_access_t *adm_access,
                           const svn_wc_diff_callbacks2_t *callbacks,
-                          struct merge_cmd_baton *merge_b,
+                          merge_cmd_baton_t *merge_b,
                           apr_pool_t *pool)
 {
   const svn_ra_reporter3_t *reporter;
@@ -1913,7 +1913,8 @@
 
   SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
                           &reporter, &report_baton, revision2,
-                          "", depth, ignore_ancestry, TRUE,  /* text_deltas */
+                          "", depth, merge_b->ignore_ancestry, 
+                          TRUE,  /* text_deltas */
                           url2, diff_editor, diff_edit_baton, pool));
 
 
@@ -1982,7 +1983,7 @@
                           svn_ra_session_t *ra_session,
                           const char *parent_merge_src_canon_path,
                           svn_wc_adm_access_t *adm_access,
-                          struct merge_cmd_baton *merge_b)
+                          merge_cmd_baton_t *merge_b)
 {
   apr_pool_t *iterpool, *persistent_pool;
   int merge_target_len = strlen(merge_b->target);
@@ -2217,7 +2218,7 @@
                                        svn_merge_range_t *range,
                                        const svn_wc_entry_t *entry,
                                        svn_wc_adm_access_t *adm_access,
-                                       struct merge_cmd_baton *merge_b,
+                                       merge_cmd_baton_t *merge_b,
                                        apr_pool_t *pool)
 {
   apr_array_header_t *rangelist;
@@ -2268,7 +2269,7 @@
                                    const char *rel_path,
                                    const char *target_wcpath,
                                    svn_wc_adm_access_t *adm_access,
-                                   struct merge_cmd_baton *merge_b,
+                                   merge_cmd_baton_t *merge_b,
                                    apr_array_header_t *children_with_mergeinfo,
                                    int target_index, apr_pool_t *pool)
 {
@@ -2311,9 +2312,7 @@
                                         FALSE, pool));
           if (!is_equal)
             {
-              SVN_ERR(svn_mergeinfo_merge(merges, inheritable_merges,
-                                          svn_rangelist_equal_inheritance,
-                                          pool));
+              SVN_ERR(svn_mergeinfo_merge(merges, inheritable_merges, pool));
               SVN_ERR(svn_client__record_wc_mergeinfo(target_wcpath, merges,
                                                       adm_access, pool));
             }
@@ -2334,7 +2333,7 @@
 record_mergeinfo_on_merged_children(svn_depth_t depth,
                                     svn_wc_adm_access_t *adm_access,
                                     notification_receiver_baton_t *notify_b,
-                                    struct merge_cmd_baton *merge_b,
+                                    merge_cmd_baton_t *merge_b,
                                     apr_pool_t *pool)
 {
   if ((depth != svn_depth_infinity) && notify_b->merged_paths)
@@ -2408,10 +2407,9 @@
          const char *target_wcpath,
          svn_wc_adm_access_t *adm_access,
          svn_depth_t depth,
-         svn_boolean_t ignore_ancestry,
          const svn_wc_diff_callbacks2_t *callbacks,
          notification_receiver_baton_t *notify_b,
-         struct merge_cmd_baton *merge_b,
+         merge_cmd_baton_t *merge_b,
          apr_array_header_t *children_with_mergeinfo,
          apr_pool_t *pool)
 {
@@ -2422,9 +2420,8 @@
 
   SVN_ERR(drive_merge_report_editor(target_wcpath,
                                     url1, revision1, url2, revision2,
-                                    children_with_mergeinfo,
-                                    is_rollback, depth,
-                                    ignore_ancestry, notify_b, adm_access,
+                                    children_with_mergeinfo, is_rollback, 
+                                    depth, notify_b, adm_access,
                                     callbacks, merge_b, pool));
 
   /* Sleep to ensure timestamp integrity. */
@@ -2658,8 +2655,7 @@
                      const char *target_wcpath,
                      svn_wc_adm_access_t *adm_access,
                      notification_receiver_baton_t *notify_b,
-                     struct merge_cmd_baton *merge_b,
-                     svn_boolean_t ignore_ancestry,
+                     merge_cmd_baton_t *merge_b,
                      apr_pool_t *pool)
 {
   svn_error_t *err = SVN_NO_ERROR;
@@ -2694,7 +2690,7 @@
   /* If we are not ignoring ancestry, then we need to check the
      relationship between the two sides of our merge.  Otherwise, just
      accept our input as-is. */
-  if (! ignore_ancestry)
+  if (! merge_b->ignore_ancestry)
     {
       const char *location_url;
       svn_opt_revision_t *location_rev;
@@ -3241,7 +3237,7 @@
 static svn_error_t *
 insert_parent_and_sibs_of_sw_absent_del_entry(
                                    apr_array_header_t *children_with_mergeinfo,
-                                   struct merge_cmd_baton *merge_cmd_baton,
+                                   merge_cmd_baton_t *merge_cmd_baton,
                                    int *curr_index,
                                    svn_client__merge_path_t *child,
                                    svn_wc_adm_access_t *adm_access,
@@ -3347,7 +3343,7 @@
    Cascade MERGE_SRC_CANON_PATH. */
 static svn_error_t *
 get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
-                    struct merge_cmd_baton *merge_cmd_baton,
+                    merge_cmd_baton_t *merge_cmd_baton,
                     const char* merge_src_canon_path,
                     const svn_wc_entry_t *entry,
                     svn_wc_adm_access_t *adm_access,
@@ -3550,7 +3546,9 @@
    NOTE: For merge tracking to work appropriately, all the changes
    that the caller is using this function to merge must have occurred
    on a single line of history that had no "changes of address"
-   (renames) between REVISION1 and REVISION2.
+   (renames) between REVISION1 and REVISION2.  (For this reason, we
+   don't need to pass IGNORE_ANCESTRY to this function -- that flag
+   only matters when our merge sources aren't related.)
 */
 static svn_error_t *
 discover_and_merge_children(const char *url1,
@@ -3560,9 +3558,8 @@
                             const svn_wc_entry_t *parent_entry,
                             svn_wc_adm_access_t *adm_access,
                             svn_depth_t depth,
-                            svn_boolean_t ignore_ancestry,
                             notification_receiver_baton_t *notify_b,
-                            struct merge_cmd_baton *merge_b,
+                            merge_cmd_baton_t *merge_b,
                             apr_pool_t *pool)
 {
   svn_error_t *err = SVN_NO_ERROR;
@@ -3646,7 +3643,7 @@
                              end_rev, pool);
       notify_b->cur_ancestor_index = -1;
       SVN_ERR(do_merge(url1, start_rev, url2, end_rev, is_rollback,
-                       merge_b->target, adm_access, depth, ignore_ancestry,
+                       merge_b->target, adm_access, depth,
                        &merge_callbacks, notify_b, merge_b,
                        children_with_mergeinfo, iterpool));
       remove_first_range_from_remaining_ranges(children_with_mergeinfo, pool);
@@ -3800,7 +3797,7 @@
    different repository from the merge target (ENTRY), to avoid later
    erroneously setting mergeinfo on the target. */
 static APR_INLINE svn_error_t *
-from_same_repos(struct merge_cmd_baton *merge_b, const svn_wc_entry_t *entry,
+from_same_repos(merge_cmd_baton_t *merge_b, const svn_wc_entry_t *entry,
                 svn_client_ctx_t *ctx, apr_pool_t *pool)
 {
   const char *src_root;
@@ -4125,7 +4122,7 @@
 {
   svn_wc_adm_access_t *adm_access;
   const svn_wc_entry_t *entry;
-  struct merge_cmd_baton merge_cmd_baton;
+  merge_cmd_baton_t merge_cmd_baton;
   const char *URL1, *URL2;
   svn_config_t *cfg;
   const char *wc_repos_root;
@@ -4177,8 +4174,9 @@
     depth = entry->depth;
 
   merge_cmd_baton.force = force;
-  merge_cmd_baton.record_only = record_only;
   merge_cmd_baton.dry_run = dry_run;
+  merge_cmd_baton.record_only = record_only;
+  merge_cmd_baton.ignore_ancestry = ignore_ancestry;
   merge_cmd_baton.target_missing_child = FALSE;
   merge_cmd_baton.merge_options = merge_options;
   merge_cmd_baton.target = target_wcpath;
@@ -4252,7 +4250,6 @@
                                    adm_access,
                                    &notify_b,
                                    &merge_cmd_baton,
-                                   ignore_ancestry,
                                    pool));
     }
   /* Otherwise, this must be a directory merge.  Do the fancy
@@ -4269,7 +4266,6 @@
                                               entry,
                                               adm_access,
                                               depth,
-                                              ignore_ancestry,
                                               &notify_b,
                                               &merge_cmd_baton,
                                               pool));
@@ -4284,7 +4280,6 @@
                            target_wcpath,
                            adm_access,
                            depth,
-                           ignore_ancestry,
                            &merge_callbacks,
                            &notify_b,
                            &merge_cmd_baton,
@@ -4359,7 +4354,7 @@
 {
   svn_wc_adm_access_t *adm_access;
   const svn_wc_entry_t *entry;
-  struct merge_cmd_baton merge_cmd_baton;
+  merge_cmd_baton_t merge_cmd_baton;
   const char *URL;
   apr_array_header_t *revision_ranges = (apr_array_header_t *)ranges_to_merge;
   apr_array_header_t *merge_sources;
@@ -4425,8 +4420,9 @@
                                   ranges_to_merge, ra_session, ctx, pool));
 
   merge_cmd_baton.force = force;
-  merge_cmd_baton.record_only = record_only;
   merge_cmd_baton.dry_run = dry_run;
+  merge_cmd_baton.record_only = record_only;
+  merge_cmd_baton.ignore_ancestry = ignore_ancestry;
   merge_cmd_baton.ctx = ctx;
   merge_cmd_baton.target_missing_child = FALSE;
   merge_cmd_baton.target = target_wcpath;
@@ -4511,7 +4507,6 @@
                                        adm_access,
                                        &notify_b,
                                        &merge_cmd_baton,
-                                       ignore_ancestry,
                                        subpool));
         }
 
@@ -4527,7 +4522,6 @@
                                               entry,
                                               adm_access,
                                               depth,
-                                              ignore_ancestry,
                                               &notify_b,
                                               &merge_cmd_baton,
                                               subpool));
diff --git a/subversion/libsvn_client/mergeinfo.c b/subversion/libsvn_client/mergeinfo.c
index 79a5330..c60021f 100644
--- a/subversion/libsvn_client/mergeinfo.c
+++ b/subversion/libsvn_client/mergeinfo.c
@@ -608,7 +608,7 @@
       SVN_ERR(svn_mergeinfo__equals(&equal_mergeinfo,
                                     parent_nonempty_mergeinfo,
                                     child_mergeinfo,
-                                    svn_rangelist_only_inheritable,
+                                    TRUE,
                                     subpool));
       if (equal_mergeinfo)
         elision_type = elision_type_full;
@@ -626,7 +626,7 @@
         is equivalent to PARENT_MERGEINFO. */
       SVN_ERR(svn_mergeinfo__equals(&equal_mergeinfo,
                                     child_nonempty_mergeinfo,
-                                    mergeinfo, svn_rangelist_only_inheritable,
+                                    mergeinfo, TRUE,
                                     subpool));
       if (equal_mergeinfo)
         elision_type = elision_type_full;
@@ -993,7 +993,7 @@
   else
     SVN_ERR(svn_rangelist_remove(rangelist, already_merged_ranges,
                                  full_range_list,
-                                 svn_rangelist_equal_inheritance, pool));
+                                 FALSE, pool));
 
   return SVN_NO_ERROR;
 }
diff --git a/subversion/libsvn_diff/diff_file.c b/subversion/libsvn_diff/diff_file.c
index af3be99..60afb2e 100644
--- a/subversion/libsvn_diff/diff_file.c
+++ b/subversion/libsvn_diff/diff_file.c
@@ -35,6 +35,7 @@
 #include "svn_pools.h"
 #include "diff.h"
 #include "svn_private_config.h"
+#include "svn_path.h"
 
 
 /* A token, i.e. a line read from a file. */
@@ -1075,13 +1076,14 @@
 };
 
 svn_error_t *
-svn_diff_file_output_unified2(svn_stream_t *output_stream,
+svn_diff_file_output_unified3(svn_stream_t *output_stream,
                               svn_diff_t *diff,
                               const char *original_path,
                               const char *modified_path,
                               const char *original_header,
                               const char *modified_header,
                               const char *header_encoding,
+                              const char *relative_to_dir,
                               apr_pool_t *pool)
 {
   svn_diff__file_output_baton_t baton;
@@ -1104,6 +1106,30 @@
       SVN_ERR(svn_utf_cstring_from_utf8_ex2(&baton.insert_str, "+",
                                             header_encoding, pool));
 
+  if (relative_to_dir)
+    {
+      /* Possibly adjust the "original" and "modified" paths shown in
+         the output (see issue #2723). */
+      const char *child_path = svn_path_is_child(relative_to_dir,
+                                                 original_path, pool);
+      if (child_path)
+        original_path = child_path;
+      else
+        return svn_error_createf(SVN_ERR_BAD_RELATIVE_PATH, NULL,
+                                 _("Path '%s' must be an immediate child of "
+                                   "the directory '%s'"),
+                                 original_path, relative_to_dir);
+
+      child_path = svn_path_is_child(relative_to_dir, modified_path, pool);
+      if (child_path)
+        modified_path = child_path;
+      else
+        return svn_error_createf(SVN_ERR_BAD_RELATIVE_PATH, NULL,
+                                 _("Path '%s' must be an immediate child of "
+                                   "the directory '%s'"),
+                                 modified_path, relative_to_dir);
+    }
+
       for (i = 0; i < 2; i++)
         {
           SVN_ERR(svn_io_file_open(&baton.file[i], baton.path[i],
@@ -1141,6 +1167,22 @@
 }
 
 svn_error_t *
+svn_diff_file_output_unified2(svn_stream_t *output_stream,
+                              svn_diff_t *diff,
+                              const char *original_path,
+                              const char *modified_path,
+                              const char *original_header,
+                              const char *modified_header,
+                              const char *header_encoding,
+                              apr_pool_t *pool)
+{
+  return svn_diff_file_output_unified3(output_stream, diff,
+                                       original_path, modified_path,
+                                       original_header, modified_header,
+                                       header_encoding, NULL, pool);
+}
+
+svn_error_t *
 svn_diff_file_output_unified(svn_stream_t *output_stream,
                              svn_diff_t *diff,
                              const char *original_path,
diff --git a/subversion/libsvn_fs_util/mergeinfo-sqlite-index.c b/subversion/libsvn_fs_util/mergeinfo-sqlite-index.c
index 9ca1b44..44b38bc 100644
--- a/subversion/libsvn_fs_util/mergeinfo-sqlite-index.c
+++ b/subversion/libsvn_fs_util/mergeinfo-sqlite-index.c
@@ -29,6 +29,7 @@
 #include "svn_fs.h"
 #include "svn_path.h"
 #include "svn_mergeinfo.h"
+#include "svn_pools.h"
 
 #include "private/svn_dep_compat.h"
 #include "private/svn_fs_sqlite.h"
@@ -137,6 +138,7 @@
 #if APR_VERSION_AT_LEAST(1, 3, 0)
               apr_array_clear(rangelist);
 #else
+              /* Use of an iterpool would be overkill here. */
               rangelist = apr_array_make(pool, 1, sizeof(&no_mergeinfo));
 #endif
               APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = &no_mergeinfo;
@@ -215,20 +217,21 @@
   svn_error_t *err;
   sqlite3 *db;
   const char *deletestring;
+  apr_pool_t *subpool = svn_pool_create(pool);
 
-  SVN_ERR(svn_fs__sqlite_open(&db, txn->fs->path, pool));
+  SVN_ERR(svn_fs__sqlite_open(&db, txn->fs->path, subpool));
   err = svn_fs__sqlite_exec(db, "BEGIN TRANSACTION;");
   MAYBE_CLEANUP;
 
   /* Cleanup the leftovers of any previous, failed transactions
    * involving NEW_REV. */
-  deletestring = apr_psprintf(pool,
+  deletestring = apr_psprintf(subpool,
                               "DELETE FROM mergeinfo_changed WHERE "
                               "revision = %ld;",
                               new_rev);
   err = svn_fs__sqlite_exec(db, deletestring);
   MAYBE_CLEANUP;
-  deletestring = apr_psprintf(pool,
+  deletestring = apr_psprintf(subpool,
                               "DELETE FROM mergeinfo WHERE revision = %ld;",
                               new_rev);
   err = svn_fs__sqlite_exec(db, deletestring);
@@ -237,7 +240,7 @@
   /* Record any mergeinfo from the current transaction. */
   if (mergeinfo_for_paths)
     {
-      err = index_txn_mergeinfo(db, new_rev, mergeinfo_for_paths, pool);
+      err = index_txn_mergeinfo(db, new_rev, mergeinfo_for_paths, subpool);
       MAYBE_CLEANUP;
     }
 
@@ -250,7 +253,9 @@
   MAYBE_CLEANUP;
 
  cleanup:
-  return svn_fs__sqlite_close(db, err);
+  err = svn_fs__sqlite_close(db, err);
+  svn_pool_destroy(subpool);
+  return err;
 }
 
 /* Helper for get_mergeinfo_for_path() that retrieves mergeinfo for
@@ -348,9 +353,9 @@
 }
 
 
-/* Helper for get_mergeinfo_for_path() that will append
-   PATH_TO_APPEND to each path that exists in the mergeinfo hash
-   INPUT, and return a new mergeinfo hash in *OUTPUT.  */
+/* Helper for get_mergeinfo_for_path() that will append PATH_TO_APPEND
+   to each path that exists in the mergeinfo hash INPUT, and return a
+   new mergeinfo hash in *OUTPUT.  Perform all allocations in POOL. */
 static svn_error_t *
 append_component_to_paths(apr_hash_t **output,
                           apr_hash_t *input,
@@ -367,7 +372,8 @@
       char *newpath;
 
       apr_hash_this(hi, &key, NULL, &val);
-      newpath = svn_path_join((const char *) key, path_to_append, pool);
+      newpath = svn_path_join((const char *) key, path_to_append,
+                              apr_hash_pool_get(*output));
       apr_hash_set(*output, newpath, APR_HASH_KEY_STRING, val);
     }
 
@@ -494,10 +500,12 @@
 }
 
 
-/* Get the mergeinfo for all of the children of PATH in REV.  Return the
-   results in PATH_MERGEINFO.  PATH_MERGEINFO should already be created
-   prior to calling this function, but it's value may change as additional
-   mergeinfos are added to it.  */
+/* Get the mergeinfo for all of the children of PATH in REV.  Return
+   the results in PATH_MERGEINFO.  PATH_MERGEINFO should already be
+   created prior to calling this function, but it's value may change
+   as additional mergeinfos are added to it.  Returned values are
+   allocated in POOL, while temporary values are allocated in a
+   sub-pool. */
 static svn_error_t *
 get_mergeinfo_for_children(sqlite3 *db,
                            const char *path,
@@ -509,6 +517,7 @@
 {
   sqlite3_stmt *stmt;
   int sqlite_result;
+  apr_pool_t *subpool = svn_pool_create(pool);
   char *like_path;
 
   /* Get all paths under us. */
@@ -519,7 +528,7 @@
                                      "GROUP BY path;",
                                      -1, &stmt, NULL), db);
 
-  like_path = apr_psprintf(pool, "%s/%%", path);
+  like_path = apr_psprintf(subpool, "%s/%%", path);
 
   SVN_FS__SQLITE_ERR(sqlite3_bind_text(stmt, 1, like_path, -1, 
                                        SQLITE_TRANSIENT), db);
@@ -531,6 +540,8 @@
       svn_revnum_t lastmerged_rev;
       const char *merged_path;
 
+      svn_pool_clear(subpool);
+
       if (sqlite_result == SQLITE_ERROR)
         return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL,
                                 sqlite3_errmsg(db));
@@ -545,28 +556,27 @@
           svn_boolean_t omit = FALSE;
 
           SVN_ERR(parse_mergeinfo_from_db(db, merged_path, lastmerged_rev,
-                                          &db_mergeinfo, pool));
+                                          &db_mergeinfo, subpool));
 
           if (filter_func)
             SVN_ERR(filter_func(filter_func_baton, &omit, merged_path,
-                                db_mergeinfo, pool));
+                                db_mergeinfo, subpool));
 
           if (!omit)
-            SVN_ERR(svn_mergeinfo_merge(path_mergeinfo, db_mergeinfo,
-                                        svn_rangelist_equal_inheritance,
-                                        pool));
+            SVN_ERR(svn_mergeinfo_merge(path_mergeinfo, db_mergeinfo, pool));
         }
 
       sqlite_result = sqlite3_step(stmt);
     }
 
   SVN_FS__SQLITE_ERR(sqlite3_finalize(stmt), db);
+  svn_pool_destroy(subpool);
 
   return SVN_NO_ERROR;
 }
 
 /* Get the mergeinfo for a set of paths, returned as a hash of mergeinfo
-   hashs keyed by each path. */
+   hashs keyed by each path.  Perform all allocations in POOL. */
 static svn_error_t *
 get_mergeinfo(sqlite3 *db,
               apr_hash_t **mergeinfo,
@@ -583,13 +593,16 @@
     {
       const char *path = APR_ARRAY_IDX(paths, i, const char *);
       SVN_ERR(get_mergeinfo_for_path(db, path, rev, *mergeinfo,
-                                     mergeinfo_cache, inherit, pool));
+                                     mergeinfo_cache, inherit,
+                                     apr_hash_pool_get(*mergeinfo)));
     }
 
   return SVN_NO_ERROR;
 }
 
-/* Get the mergeinfo for a set of paths.  */
+/* Get the mergeinfo for a set of paths.  Returned values are
+   allocated in POOL, while temporary values are allocated in a
+   sub-pool. */
 svn_error_t *
 svn_fs_mergeinfo__get_mergeinfo(apr_hash_t **mergeinfo,
                                 svn_fs_root_t *root,
@@ -601,33 +614,40 @@
   int i;
   svn_error_t *err;
   svn_revnum_t rev;
+  apr_pool_t *subpool;
 
   /* We require a revision root. */
   if (root->is_txn_root)
     return svn_error_create(SVN_ERR_FS_NOT_REVISION_ROOT, NULL, NULL);
   rev = REV_ROOT_REV(root);
 
-  SVN_ERR(svn_fs__sqlite_open(&db, root->fs->path, pool));
+  subpool = svn_pool_create(pool);
+
+  /* Retrieve a path -> mergeinfo hash mapping. */
+  SVN_ERR(svn_fs__sqlite_open(&db, root->fs->path, subpool));
   err = get_mergeinfo(db, mergeinfo, rev, paths, inherit, pool);
   SVN_ERR(svn_fs__sqlite_close(db, err));
 
+  /* Convert each mergeinfo hash value into a textual representation. */
   for (i = 0; i < paths->nelts; i++)
     {
       svn_stringbuf_t *mergeinfo_buf;
       apr_hash_t *path_mergeinfo;
       const char *path = APR_ARRAY_IDX(paths, i, const char *);
 
+      apr_pool_clear(subpool);
+
       path_mergeinfo = apr_hash_get(*mergeinfo, path, APR_HASH_KEY_STRING);
       if (path_mergeinfo)
         {
-          SVN_ERR(svn_mergeinfo_sort(path_mergeinfo, pool));
+          SVN_ERR(svn_mergeinfo_sort(path_mergeinfo, subpool));
           SVN_ERR(svn_mergeinfo_to_stringbuf(&mergeinfo_buf, path_mergeinfo,
-                                             pool));
+                                             apr_hash_pool_get(*mergeinfo)));
           apr_hash_set(*mergeinfo, path, APR_HASH_KEY_STRING,
                        mergeinfo_buf->data);
         }
     }
-
+  svn_pool_destroy(subpool);
 
   return SVN_NO_ERROR;
 }
diff --git a/subversion/libsvn_ra_svn/client.c b/subversion/libsvn_ra_svn/client.c
index dd8ee1a..dc3f746 100644
--- a/subversion/libsvn_ra_svn/client.c
+++ b/subversion/libsvn_ra_svn/client.c
@@ -666,7 +666,7 @@
   if (! err)
     {
       SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, ""));
-      sess->url = apr_pstrdup(pool, url);
+      sess->url = apr_pstrdup(sess->pool, url);
       return SVN_NO_ERROR;
     }
   else if (err->apr_err != SVN_ERR_RA_SVN_UNKNOWN_CMD)
diff --git a/subversion/libsvn_repos/log.c b/subversion/libsvn_repos/log.c
index 1cbdf21..5c8efbf 100644
--- a/subversion/libsvn_repos/log.c
+++ b/subversion/libsvn_repos/log.c
@@ -645,7 +645,7 @@
                                              copy_path, path, rev, subpool));
 
   SVN_ERR(svn_mergeinfo_diff(&deleted, &added, implied_mergeinfo,
-                             mergeinfo, svn_rangelist_ignore_inheritance,
+                             mergeinfo, FALSE,
                              subpool));
   if (apr_hash_count(deleted) == 0 && apr_hash_count(added) == 0)
     {
@@ -753,8 +753,7 @@
       apr_hash_t *path_mergeinfo;
 
       apr_hash_this(hi, NULL, NULL, (void *)&path_mergeinfo);
-      SVN_ERR(svn_mergeinfo_merge(*mergeinfo, path_mergeinfo,
-                                  svn_rangelist_equal_inheritance, pool));
+      SVN_ERR(svn_mergeinfo_merge(*mergeinfo, path_mergeinfo, pool));
     }
 
   svn_pool_destroy(subpool);
@@ -780,7 +779,7 @@
 
       apr_hash_this(hi, NULL, NULL, (void *)&path_rangelist);
       SVN_ERR(svn_rangelist_merge(rangelist, path_rangelist,
-                                  svn_rangelist_equal_inheritance, pool));
+                                  pool));
     }
 
   return SVN_NO_ERROR;
@@ -814,10 +813,9 @@
                                  subpool));
 
   SVN_ERR(svn_mergeinfo_diff(&deleted, &changed, prev_mergeinfo,
-                             curr_mergeinfo, svn_rangelist_ignore_inheritance,
+                             curr_mergeinfo, FALSE,
                              subpool));
-  SVN_ERR(svn_mergeinfo_merge(changed, deleted,
-                              svn_rangelist_equal_inheritance, subpool));
+  SVN_ERR(svn_mergeinfo_merge(changed, deleted, subpool));
 
   *mergeinfo = svn_mergeinfo_dup(changed, pool);
   svn_pool_destroy(subpool);
diff --git a/subversion/libsvn_repos/rev_hunt.c b/subversion/libsvn_repos/rev_hunt.c
index fd4b990..09f8966 100644
--- a/subversion/libsvn_repos/rev_hunt.c
+++ b/subversion/libsvn_repos/rev_hunt.c
@@ -1036,9 +1036,8 @@
                                         old_path_rev->path,
                                         old_path_rev->revnum - 1, subpool));
   SVN_ERR(svn_mergeinfo_diff(&deleted, &changed, prev_mergeinfo, curr_mergeinfo,
-                             svn_rangelist_ignore_inheritance, subpool));
-  SVN_ERR(svn_mergeinfo_merge(changed, deleted,
-                              svn_rangelist_equal_inheritance, subpool));
+                             FALSE, subpool));
+  SVN_ERR(svn_mergeinfo_merge(changed, deleted, subpool));
   if (apr_hash_count(changed) == 0)
     {
       svn_pool_destroy(subpool);
diff --git a/subversion/libsvn_subr/mergeinfo.c b/subversion/libsvn_subr/mergeinfo.c
index 247a235..53b09bb 100644
--- a/subversion/libsvn_subr/mergeinfo.c
+++ b/subversion/libsvn_subr/mergeinfo.c
@@ -30,14 +30,15 @@
 #include "private/svn_mergeinfo_private.h"
 #include "svn_private_config.h"
 
-/* Attempt to combine two ranges, IN1 and IN2, and put the result in
-   OUTPUT.  Return whether they could be combined.
+/* Attempt to combine two adjacent or overlapping ranges, IN1 and IN2, and put
+   the result in OUTPUT.  Return whether they could be combined.
 
    CONSIDER_INHERITANCE determines how to account for the inheritability
    of IN1 and IN2 when trying to combine ranges.  If ranges with different
-   inheritability are combined the result is always non-inheritable.  If both
-   ranges are inheritable the result is inheritable and if both are
-   non-inheritable the result is non-inheritable.
+   inheritability are combined (CONSIDER_INHERITANCE must be FALSE for this
+   to happen) the result is inheritable.  If both ranges are inheritable the
+   result is inheritable.  Only and if both ranges are non-inheritable is
+   the result is non-inheritable.
 
    Range overlapping detection algorithm from
    http://c2.com/cgi-bin/wiki/fullSearch?TestIfDateRangesOverlap
@@ -49,17 +50,15 @@
 {
   if (in1->start <= in2->end && in2->start <= in1->end)
     {
-      if (consider_inheritance == svn_rangelist_ignore_inheritance
-          || (consider_inheritance == svn_rangelist_equal_inheritance
+      if (!consider_inheritance
+          || (consider_inheritance
               && ((in1->inheritable ? TRUE : FALSE)
-                  == (in2->inheritable ? TRUE : FALSE)))
-          || (consider_inheritance == svn_rangelist_only_inheritable
-              && in1->inheritable && in1->inheritable))
+                   == (in2->inheritable ? TRUE : FALSE))))
         {
           (*output)->start = MIN(in1->start, in2->start);
           (*output)->end = MAX(in1->end, in2->end);
           (*output)->inheritable =
-            (in1->inheritable && in2->inheritable) ? TRUE : FALSE;
+            (in1->inheritable || in2->inheritable) ? TRUE : FALSE;
           return TRUE;
         }
     }
@@ -85,10 +84,42 @@
   return SVN_NO_ERROR;
 }
 
-/*push to revlist and set lastrange, if could not combine mrange
-  with *lastrange or *lastrange is NULL.  CONSIDER_INHERITANCE determines
-  how to account for the inheritability of MRANGE and *LASTRANGE when trying
-  to combine ranges - see combine_ranges().
+/* Helper for svn_rangelist_merge() and rangelist_intersect_or_remove().
+
+   If *LASTRANGE is not NULL it should point to the last element in REVLIST.
+   REVLIST must be sorted from lowest to highest revision and contain no
+   overlapping revision ranges.  Any changes made to REVLIST will maintain
+   this guarantee.
+
+   If *LASTRANGE is NULL then push MRANGE to REVLIST.
+
+   If *LASTRANGE and MRANGE don't intersect then push MRANGE to REVLIST.
+   If they do intersect and have the same inheritability then combine the
+   ranges, updating *LASTRANGE to reflect the new combined range.  If the
+   ranges intersect but differ in inheritability, then merge the ranges - see
+   the doc string for svn_mergeinfo_merge.  This may result in a change to
+   *LASTRANGE's end field and the pushing of up to two new ranges on REVLIST.
+
+     e.g.  *LASTRANGE: '4-10*' merged with MRANGE: '6'________
+                  |                           |               |
+             Update end field               Push       Account for trimmed 
+                  |                           |        range from *LASTRANGE.
+                  |                           |        Push it last to
+                  |                           |        maintain sort order.
+                  |                           |               |
+                  V                           V               V
+           *LASTRANGE: '4-5*'              MRANGE: '6'   NEWRANGE: '6-10*'
+
+   Upon return, if any new ranges were pushed onto REVLIST, then set
+   *LASTRANGE to the last range pushed.
+
+   CONSIDER_INHERITANCE determines how to account for the inheritability of
+   MRANGE and *LASTRANGE when determining if they intersect.  If
+   CONSIDER_INHERITANCE is TRUE, then only ranges with the same
+   inheritability can intersect and therefore be combined.
+
+   If DUP_MRANGE is TRUE then allocate a copy of MRANGE before pushing it
+   onto REVLIST.
 */
 static APR_INLINE void
 combine_with_lastrange(svn_merge_range_t** lastrange,
@@ -97,18 +128,296 @@
                        svn_boolean_t consider_inheritance,
                        apr_pool_t *pool)
 {
-  svn_merge_range_t *pushed_mrange = mrange;
-  if (!(*lastrange) || !combine_ranges(lastrange, *lastrange, mrange,
-                                       consider_inheritance))
+  svn_merge_range_t *pushed_mrange_1 = NULL;
+  svn_merge_range_t *pushed_mrange_2 = NULL;
+  svn_boolean_t ranges_intersect = FALSE;
+  svn_boolean_t ranges_have_same_inheritance = FALSE;
+  
+  if (*lastrange)
     {
+      if ((*lastrange)->start <= mrange->end
+          && mrange->start <= (*lastrange)->end)
+        ranges_intersect = TRUE;
+      if ((*lastrange)->inheritable == mrange->inheritable)
+        ranges_have_same_inheritance = TRUE;
+    }
+
+  if (!(*lastrange)
+      || (!ranges_intersect || (!ranges_have_same_inheritance
+                                && consider_inheritance)))
+
+    {
+      /* No *LASTRANGE
+           or
+         LASTRANGE and MRANGE don't intersect
+           or
+         LASTRANGE and MRANGE "intersect" but have different
+         inheritability and we are considering inheritance so
+         can't combined them...
+         
+         ...In all these cases just push MRANGE onto *LASTRANGE. */
       if (dup_mrange)
-        pushed_mrange = svn_merge_range_dup(mrange, pool);
-      APR_ARRAY_PUSH(revlist, svn_merge_range_t *) = pushed_mrange;
-      *lastrange = pushed_mrange;
+        pushed_mrange_1 = svn_merge_range_dup(mrange, pool);
+      else
+        pushed_mrange_1 = mrange;
+    }
+  else /* MRANGE and *LASTRANGE intersect */
+    {
+      if (ranges_have_same_inheritance)
+        {
+          /* Intersecting ranges have the same inheritability
+             so just combine them. */
+          (*lastrange)->start = MIN((*lastrange)->start, mrange->start);
+          (*lastrange)->end = MAX((*lastrange)->end, mrange->end);
+          (*lastrange)->inheritable =
+            ((*lastrange)->inheritable || mrange->inheritable) ? TRUE : FALSE;
+        }
+      else /* Ranges intersect but have different
+              inheritability so merge the ranges. */
+        {
+          svn_revnum_t tmp_revnum;
+
+          /* Ranges have same starting revision. */
+          if ((*lastrange)->start == mrange->start)
+            {
+              if ((*lastrange)->end == mrange->end)
+                {
+                  (*lastrange)->inheritable = TRUE;
+                }
+              else if ((*lastrange)->end > mrange->end)
+                {
+                  if (!(*lastrange)->inheritable)
+                    {
+                      tmp_revnum = (*lastrange)->end;
+                      (*lastrange)->end = mrange->end;
+                      (*lastrange)->inheritable = TRUE;
+                      if (dup_mrange)
+                        pushed_mrange_1 = svn_merge_range_dup(mrange, pool);
+                      else
+                        pushed_mrange_1 = mrange;
+                      pushed_mrange_1->start = pushed_mrange_1->start;
+                      pushed_mrange_1->end = tmp_revnum;
+                      *lastrange = pushed_mrange_1;
+                    } 
+                }
+              else /* (*lastrange)->end < mrange->end) */
+                {
+                  if (mrange->inheritable)
+                    {
+                      (*lastrange)->inheritable = TRUE;
+                      (*lastrange)->end = mrange->end;
+                    }
+                  else
+                    {
+                      if (dup_mrange)
+                        pushed_mrange_1 = svn_merge_range_dup(mrange, pool);
+                      else
+                        pushed_mrange_1 = mrange;
+                      pushed_mrange_1->start = (*lastrange)->end;
+                    }
+                }
+            }
+          /* Ranges have same ending revision. (Same starting
+             and ending revisions already handled above.) */
+          else if ((*lastrange)->end == mrange->end)
+            {
+              if ((*lastrange)->start < mrange->start)
+                {
+                  if (!(*lastrange)->inheritable)
+                    {
+                      (*lastrange)->end = mrange->start;
+                      if (dup_mrange)
+                        pushed_mrange_1 = svn_merge_range_dup(mrange, pool);
+                      else
+                        pushed_mrange_1 = mrange;
+                      *lastrange = pushed_mrange_1;
+                    }
+                }
+              else /* (*lastrange)->start > mrange->start */
+                {
+                  (*lastrange)->start = mrange->start;
+                  (*lastrange)->end = mrange->end;
+                  (*lastrange)->inheritable = mrange->inheritable;
+                  if (dup_mrange)
+                    pushed_mrange_1 = svn_merge_range_dup(mrange, pool);
+                  else
+                    pushed_mrange_1 = mrange;
+                  pushed_mrange_1->start = (*lastrange)->end;
+                  pushed_mrange_1->inheritable = TRUE;
+
+                }
+            }
+          else /* Ranges have different starting and ending revisions. */
+            {
+              if ((*lastrange)->start < mrange->start)
+                {
+                  /* If MRANGE is a proper subset of *LASTRANGE and
+                     *LASTRANGE is inheritable there is nothing more
+                     to do. */
+                  if (!((*lastrange)->end > mrange->end
+                        && (*lastrange)->inheritable))
+                    {
+                      tmp_revnum = (*lastrange)->end;
+                      if (!(*lastrange)->inheritable)
+                        (*lastrange)->end = mrange->start;
+                      else
+                        mrange->start = (*lastrange)->end;
+                      if (dup_mrange)
+                        pushed_mrange_1 = svn_merge_range_dup(mrange, pool);
+                      else
+                        pushed_mrange_1 = mrange;
+
+                      if (tmp_revnum > mrange->end)
+                        {
+                          pushed_mrange_2 =
+                            apr_palloc(pool, sizeof(*pushed_mrange_2));
+                          pushed_mrange_2->start = mrange->end;
+                          pushed_mrange_2->end = tmp_revnum;
+                          pushed_mrange_2->inheritable =
+                            (*lastrange)->inheritable;
+                        }
+                      mrange->inheritable = TRUE;
+                    }
+                }
+              else /* ((*lastrange)->start > mrange->start) */
+                {
+                  if ((*lastrange)->end < mrange->end)
+                    {
+                      pushed_mrange_2->start = (*lastrange)->end;
+                      pushed_mrange_2->end = mrange->end;
+                      pushed_mrange_2->inheritable = mrange->inheritable;
+
+                      tmp_revnum = (*lastrange)->start;
+                      (*lastrange)->start = mrange->start;
+                      (*lastrange)->end = tmp_revnum;
+                      (*lastrange)->inheritable = mrange->inheritable;
+
+                      mrange->start = tmp_revnum;
+                      mrange->end = pushed_mrange_2->start;
+                      mrange->inheritable = TRUE;
+                    }
+                  else /* (*lastrange)->end > mrange->end */
+                    {
+                      pushed_mrange_2->start = mrange->end;
+                      pushed_mrange_2->end = (*lastrange)->end;
+                      pushed_mrange_2->inheritable =
+                        (*lastrange)->inheritable;
+
+                      tmp_revnum = (*lastrange)->start;
+                      (*lastrange)->start = mrange->start;
+                      (*lastrange)->end = tmp_revnum;
+                      (*lastrange)->inheritable = mrange->inheritable;
+
+                      mrange->start = tmp_revnum;
+                      mrange->end = pushed_mrange_2->start;
+                      mrange->inheritable = TRUE;
+                    }
+                }
+            }
+        }
+    }
+  if (pushed_mrange_1)
+    {
+      APR_ARRAY_PUSH(revlist, svn_merge_range_t *) = pushed_mrange_1;
+      *lastrange = pushed_mrange_1;
+    }
+  if (pushed_mrange_2)
+    {
+      APR_ARRAY_PUSH(revlist, svn_merge_range_t *) = pushed_mrange_2;
+      *lastrange = pushed_mrange_2;
     }
 }
 
-/* revisionlist -> (revisionelement)(COMMA revisionelement)*
+/* Convert a single svn_merge_range_t * back into an svn_stringbuf_t *.  */
+static svn_error_t *
+range_to_stringbuf(svn_stringbuf_t **result, svn_merge_range_t *range,
+                       apr_pool_t *pool)
+{
+  if (range->start == range->end - 1)
+    *result = svn_stringbuf_createf(pool, "%ld%s", range->end,
+                                    range->inheritable
+                                    ? "" : SVN_MERGEINFO_NONINHERITABLE_STR);
+  else
+    *result = svn_stringbuf_createf(pool, "%ld-%ld%s", range->start + 1,
+                                    range->end, range->inheritable
+                                    ? "" : SVN_MERGEINFO_NONINHERITABLE_STR);
+  return SVN_NO_ERROR;
+}
+
+/* Helper for svn_mergeinfo_parse() via parse_revlist().
+
+  Similar to combine_with_lastrange() but enforces the some of the
+  restrictions noted in svn_mergeinfo_parse() on otherwise grammatically
+  correct rangelists, specifically the prohibitions on:
+
+    1) Overlapping revision ranges
+
+    2) Unordered revision ranges
+
+  Returns an SVN_ERR_MERGE_INFO_PARSE_ERROR error if any of these rules
+  are violated.  The restriction on revision ranges with a start revision
+  greater than or equal to its end revision is handled in parse_revlist().
+
+  Unlike combine_with_lastrange() this function *always* considers
+  inheritance, so only adjacent revision ranges with the same
+  inheritability are ever combined. */
+static svn_error_t *
+combine_with_adjacent_lastrange(svn_merge_range_t **lastrange,
+                                svn_merge_range_t *mrange,
+                                svn_boolean_t dup_mrange,
+                                apr_array_header_t *revlist,
+                                apr_pool_t *pool)
+{
+  svn_merge_range_t *pushed_mrange = mrange;
+
+  if (*lastrange)
+    {
+      svn_stringbuf_t *r1, *r2;
+
+      if ((*lastrange)->start <= mrange->end
+          && mrange->start <= (*lastrange)->end)
+        {
+          /* The ranges intersect. */
+          SVN_ERR(range_to_stringbuf(&r1, *lastrange, pool));
+          SVN_ERR(range_to_stringbuf(&r2, mrange, pool));
+
+          /* svn_mergeinfo_parse promises to combine adjacent
+             ranges, but not overlapping ranges. */
+          if (mrange->start < (*lastrange)->end)
+            {
+              return svn_error_createf(SVN_ERR_MERGE_INFO_PARSE_ERROR, NULL,
+                                       _("Parsing of overlapping revision "
+                                         "ranges '%s' and '%s' is not "
+                                         "supported"), r1->data, r2->data);
+            }
+          else if ((*lastrange)->inheritable == mrange->inheritable)
+            {
+              /* Combine adjacent ranges with the same inheritability. */
+              (*lastrange)->end = mrange->end;
+              return SVN_NO_ERROR;
+            }
+        }
+      else if ((*lastrange)->start > mrange->start)
+        {
+          SVN_ERR(range_to_stringbuf(&r1, *lastrange, pool));
+          SVN_ERR(range_to_stringbuf(&r2, mrange, pool));
+          return svn_error_createf(SVN_ERR_MERGE_INFO_PARSE_ERROR, NULL,
+                                   _("Unable to parse unordered revision "
+                                     "ranges '%s' and '%s'"),
+                                     r1->data, r2->data);
+        }
+    }
+
+  if (dup_mrange)
+    pushed_mrange = svn_merge_range_dup(mrange, pool);
+  APR_ARRAY_PUSH(revlist, svn_merge_range_t *) = pushed_mrange;
+  *lastrange = pushed_mrange;
+  return SVN_NO_ERROR;
+}
+
+/* Helper for svn_mergeinfo_parse()
+
+   revisionlist -> (revisionelement)(COMMA revisionelement)*
    revisionrange -> REVISION "-" REVISION("*")
    revisionelement -> revisionrange | REVISION("*")
 */
@@ -152,20 +461,30 @@
 
           curr++;
           SVN_ERR(svn_revnum_parse(&secondrev, curr, &curr));
+          if (firstrev > secondrev)
+            return svn_error_createf(SVN_ERR_MERGE_INFO_PARSE_ERROR, NULL,
+                                     _("Unable to parse reversed revision "
+                                       "range '%ld-%ld'"),
+                                       firstrev, secondrev);
+          else if (firstrev == secondrev)
+            return svn_error_createf(SVN_ERR_MERGE_INFO_PARSE_ERROR, NULL,
+                                     _("Unable to parse revision range "
+                                       "'%ld-%ld' with same start and end "
+                                       "revisions"), firstrev, secondrev);
           mrange->end = secondrev;
         }
 
       if (*curr == '\n' || curr == end)
         {
-          combine_with_lastrange(&lastrange, mrange, FALSE, revlist,
-                                 svn_rangelist_equal_inheritance, pool);
+          SVN_ERR(combine_with_adjacent_lastrange(&lastrange, mrange, FALSE,
+                                                  revlist, pool));
           *input = curr;
           return SVN_NO_ERROR;
         }
       else if (*curr == ',')
         {
-          combine_with_lastrange(&lastrange, mrange, FALSE, revlist,
-                                 svn_rangelist_equal_inheritance, pool);
+          SVN_ERR(combine_with_adjacent_lastrange(&lastrange, mrange, FALSE,
+                                                  revlist, pool));
           curr++;
         }
       else if (*curr == '*')
@@ -174,8 +493,8 @@
           curr++;
           if (*curr == ',' || *curr == '\n' || curr == end)
             {
-              combine_with_lastrange(&lastrange, mrange, FALSE, revlist,
-                                     svn_rangelist_equal_inheritance, pool);
+              SVN_ERR(combine_with_adjacent_lastrange(&lastrange, mrange,
+                                                      FALSE, revlist, pool));
               if (*curr == ',')
                 {
                   curr++;
@@ -270,7 +589,6 @@
 svn_error_t *
 svn_rangelist_merge(apr_array_header_t **rangelist,
                     apr_array_header_t *changes,
-                    svn_merge_range_inheritance_t consider_inheritance,
                     apr_pool_t *pool)
 {
   int i, j;
@@ -296,20 +614,20 @@
           if (elt1->inheritable || elt2->inheritable)
             elt1->inheritable = TRUE;
           combine_with_lastrange(&lastrange, elt1, TRUE, output,
-                                 consider_inheritance, pool);
+                                 FALSE, pool);
           i++;
           j++;
         }
       else if (res < 0)
         {
           combine_with_lastrange(&lastrange, elt1, TRUE, output,
-                                 consider_inheritance, pool);
+                                 FALSE, pool);
           i++;
         }
       else
         {
           combine_with_lastrange(&lastrange, elt2, TRUE, output,
-                                 consider_inheritance, pool);
+                                 FALSE, pool);
           j++;
         }
     }
@@ -323,7 +641,7 @@
       svn_merge_range_t *elt = APR_ARRAY_IDX(*rangelist, i,
                                              svn_merge_range_t *);
       combine_with_lastrange(&lastrange, elt, TRUE, output,
-                             consider_inheritance, pool);
+                             FALSE, pool);
     }
 
 
@@ -331,38 +649,21 @@
     {
       svn_merge_range_t *elt = APR_ARRAY_IDX(changes, j, svn_merge_range_t *);
       combine_with_lastrange(&lastrange, elt, TRUE, output,
-                             consider_inheritance, pool);
+                             FALSE, pool);
     }
 
   *rangelist = output;
   return SVN_NO_ERROR;
 }
 
-/* Helper for range_intersect() and range_contains().
-
-   Determine the equality of FIRST and SECOND based soley on their
-   inheritance within the parameters set by CONSIDER_INHERITANCE. */
-static svn_boolean_t
-inheritance_equal(svn_merge_range_t *first,
-                  svn_merge_range_t *second,
-                  svn_merge_range_inheritance_t consider_inheritance)
-{
-  if (consider_inheritance == svn_rangelist_ignore_inheritance)
-    return TRUE;
-  else if (consider_inheritance == svn_rangelist_only_inheritable)
-    return (first->inheritable && second->inheritable);
-  else
-    return (first->inheritable ? TRUE : FALSE
-            == second->inheritable ? TRUE : FALSE) ? TRUE : FALSE;
-}
-
 static svn_boolean_t
 range_intersect(svn_merge_range_t *first, svn_merge_range_t *second,
                 svn_boolean_t consider_inheritance)
 {
   return (first->start + 1 <= second->end)
     && (second->start + 1 <= first->end)
-    && inheritance_equal(first, second, consider_inheritance);
+    && (!consider_inheritance
+        || (!(first->inheritable) == !(second->inheritable)));
 }
 
 static svn_boolean_t
@@ -370,7 +671,8 @@
                svn_boolean_t consider_inheritance)
 {
   return (first->start <= second->start) && (second->end <= first->end)
-    && inheritance_equal(first, second, consider_inheritance);
+    && (!consider_inheritance
+        || (!(first->inheritable) == !(second->inheritable)));
 }
 
 /* Swap start and end fields of RANGE. */
@@ -577,14 +879,14 @@
                         apr_pool_t *pool)
 {
   return rangelist_intersect_or_remove(output, rangelist1, rangelist2, FALSE,
-                                       svn_rangelist_only_inheritable, pool);
+                                       TRUE, pool);
 }
 
 svn_error_t *
 svn_rangelist_remove(apr_array_header_t **output,
                      apr_array_header_t *eraser,
                      apr_array_header_t *whiteboard,
-                     svn_merge_range_inheritance_t consider_inheritance,
+                     svn_boolean_t consider_inheritance,
                      apr_pool_t *pool)
 {
   return rangelist_intersect_or_remove(output, eraser, whiteboard, TRUE,
@@ -622,7 +924,7 @@
 svn_error_t *
 svn_rangelist_diff(apr_array_header_t **deleted, apr_array_header_t **added,
                    apr_array_header_t *from, apr_array_header_t *to,
-                   svn_merge_range_inheritance_t consider_inheritance,
+                   svn_boolean_t consider_inheritance,
                    apr_pool_t *pool)
 {
   /* The items that are present in from, but not in to, must have been
@@ -685,7 +987,7 @@
 static svn_error_t *
 walk_mergeinfo_hash_for_diff(apr_hash_t *from, apr_hash_t *to,
                              apr_hash_t *deleted, apr_hash_t *added,
-                             svn_merge_range_inheritance_t consider_inheritance,
+                             svn_boolean_t consider_inheritance,
                              apr_pool_t *pool)
 {
   apr_hash_index_t *hi;
@@ -747,7 +1049,7 @@
 svn_error_t *
 svn_mergeinfo_diff(apr_hash_t **deleted, apr_hash_t **added,
                    apr_hash_t *from, apr_hash_t *to,
-                   svn_merge_range_inheritance_t consider_inheritance,
+                   svn_boolean_t consider_inheritance,
                    apr_pool_t *pool)
 {
   if (from && to == NULL)
@@ -779,7 +1081,7 @@
 svn_mergeinfo__equals(svn_boolean_t *is_equal,
                       apr_hash_t *info1,
                       apr_hash_t *info2,
-                      svn_merge_range_inheritance_t consider_inheritance,
+                      svn_boolean_t consider_inheritance,
                       apr_pool_t *pool)
 {
   if (apr_hash_count(info1) == apr_hash_count(info2))
@@ -798,7 +1100,6 @@
 
 svn_error_t *
 svn_mergeinfo_merge(apr_hash_t *mergeinfo, apr_hash_t *changes,
-                    svn_merge_range_inheritance_t consider_inheritance,
                     apr_pool_t *pool)
 {
   apr_array_header_t *sorted1, *sorted2;
@@ -826,7 +1127,6 @@
           rl2 = elt2.value;
 
           SVN_ERR(svn_rangelist_merge(&rl1, rl2,
-                                      consider_inheritance,
                                       pool));
           apr_hash_set(mergeinfo, elt1.key, elt1.klen, rl1);
           i++;
@@ -863,22 +1163,6 @@
   return SVN_NO_ERROR;
 }
 
-/* Convert a single svn_merge_range_t * back into an svn_stringbuf_t *.  */
-static svn_error_t *
-range_to_stringbuf(svn_stringbuf_t **result, svn_merge_range_t *range,
-                       apr_pool_t *pool)
-{
-  if (range->start == range->end - 1)
-    *result = svn_stringbuf_createf(pool, "%ld%s", range->end,
-                                    range->inheritable
-                                    ? "" : SVN_MERGEINFO_NONINHERITABLE_STR);
-  else
-    *result = svn_stringbuf_createf(pool, "%ld-%ld%s", range->start + 1,
-                                    range->end, range->inheritable
-                                    ? "" : SVN_MERGEINFO_NONINHERITABLE_STR);
-  return SVN_NO_ERROR;
-}
-
 svn_error_t *
 svn_rangelist_to_stringbuf(svn_stringbuf_t **output, apr_array_header_t *input,
                            apr_pool_t *pool)
@@ -1083,7 +1367,7 @@
             SVN_ERR(svn_rangelist_remove(inheritable_rangelist,
                                          ranges_inheritable,
                                          rangelist,
-                                         svn_rangelist_equal_inheritance,
+                                         TRUE,
                                          pool));
         }
     }
diff --git a/subversion/libsvn_wc/diff.c b/subversion/libsvn_wc/diff.c
index 1f7d3d1..8beacc6 100644
--- a/subversion/libsvn_wc/diff.c
+++ b/subversion/libsvn_wc/diff.c
@@ -689,7 +689,7 @@
         }
     }
 
-  if (dir_baton->depth == svn_depth_empty)
+  if (dir_baton->depth == svn_depth_empty && !in_anchor_not_target)
     return SVN_NO_ERROR;
 
   SVN_ERR(svn_wc_entries_read(&entries, adm_access, FALSE, dir_baton->pool));
diff --git a/subversion/libsvn_wc/props.c b/subversion/libsvn_wc/props.c
index 55d489d..6855b42 100644
--- a/subversion/libsvn_wc/props.c
+++ b/subversion/libsvn_wc/props.c
@@ -1010,7 +1010,7 @@
       SVN_ERR(svn_mergeinfo_parse(&from, from_prop_val->data, pool));
       SVN_ERR(svn_mergeinfo_parse(&to, to_prop_val->data, pool));
       SVN_ERR(svn_mergeinfo_diff(deleted, added, from, to,
-                                 svn_rangelist_ignore_inheritance, pool));
+                                 FALSE, pool));
     }
   return SVN_NO_ERROR;
 }
@@ -1028,8 +1028,7 @@
   apr_hash_t *mergeinfo1, *mergeinfo2;
   SVN_ERR(svn_mergeinfo_parse(&mergeinfo1, prop_val1->data, pool));
   SVN_ERR(svn_mergeinfo_parse(&mergeinfo2, prop_val2->data, pool));
-  SVN_ERR(svn_mergeinfo_merge(mergeinfo1, mergeinfo2,
-                              svn_rangelist_equal_inheritance, pool));
+  SVN_ERR(svn_mergeinfo_merge(mergeinfo1, mergeinfo2, pool));
   SVN_ERR(svn_mergeinfo__to_string((svn_string_t **) output,
                                    mergeinfo1, pool));
   return SVN_NO_ERROR;
@@ -1052,15 +1051,13 @@
                                working_prop_val, pool));
   SVN_ERR(diff_mergeinfo_props(&r_deleted, &r_added, from_prop_val,
                                to_prop_val, pool));
-  SVN_ERR(svn_mergeinfo_merge(l_deleted, r_deleted,
-                              svn_rangelist_equal_inheritance, pool));
-  SVN_ERR(svn_mergeinfo_merge(l_added, r_added,
-                              svn_rangelist_equal_inheritance, pool));
+  SVN_ERR(svn_mergeinfo_merge(l_deleted, r_deleted, pool));
+  SVN_ERR(svn_mergeinfo_merge(l_added, r_added, pool));
 
   /* Apply the combined deltas to the base. */
   SVN_ERR(svn_mergeinfo_parse(&from_mergeinfo, from_prop_val->data, pool));
-  SVN_ERR(svn_mergeinfo_merge(from_mergeinfo, l_added,
-                              svn_rangelist_equal_inheritance, pool));
+  SVN_ERR(svn_mergeinfo_merge(from_mergeinfo, l_added, pool));
+
   SVN_ERR(svn_mergeinfo_remove(&from_mergeinfo, l_deleted,
                                from_mergeinfo, pool));
 
diff --git a/subversion/svn/diff-cmd.c b/subversion/svn/diff-cmd.c
index 657c6b6..4b61884 100644
--- a/subversion/svn/diff-cmd.c
+++ b/subversion/svn/diff-cmd.c
@@ -359,6 +359,7 @@
                      &(opt_state->start_revision),
                      target2,
                      &(opt_state->end_revision),
+                     NULL,
                      opt_state->depth,
                      opt_state->notice_ancestry ? FALSE : TRUE,
                      opt_state->no_diff_deleted,
@@ -402,6 +403,7 @@
                      &peg_revision,
                      &opt_state->start_revision,
                      &opt_state->end_revision,
+                     NULL,
                      opt_state->depth,
                      opt_state->notice_ancestry ? FALSE : TRUE,
                      opt_state->no_diff_deleted,
diff --git a/subversion/svndumpfilter/main.c b/subversion/svndumpfilter/main.c
index 78ed405..5fc1bc3 100644
--- a/subversion/svndumpfilter/main.c
+++ b/subversion/svndumpfilter/main.c
@@ -24,6 +24,7 @@
 #include "svn_private_config.h"
 #include "svn_cmdline.h"
 #include "svn_error.h"
+#include "svn_string.h"
 #include "svn_opt.h"
 #include "svn_utf.h"
 #include "svn_path.h"
@@ -35,6 +36,8 @@
 #include "svn_props.h"
 #include "svn_mergeinfo.h"
 
+#include "private/svn_mergeinfo_private.h"
+
 
 /*** Code. ***/
 
@@ -637,59 +640,80 @@
 }
 
 
-/* Renumber revisions for valid merge sources in mergeinfo. */
+/* Examine the mergeinfo in INITIAL_VAL, omitting missing merge
+   sources or renumbering revisions in rangelists as appropriate, and
+   return the (possibly new) mergeinfo in *FINAL_VAL (allocated from
+   POOL). */
 static svn_error_t *
-renumber_merge_source_rev_range(const char **mergeinfo_val,
-                                const char *mergeinfo_orig,
-                                struct revision_baton_t *rb,
-                                apr_pool_t *pool)
+adjust_mergeinfo(svn_string_t **final_val, const svn_string_t *initial_val,
+                 struct revision_baton_t *rb, apr_pool_t *pool)
 {
-  apr_hash_t *modified_mergeinfo, *mergeinfo;
+  apr_hash_t *mergeinfo;
+  apr_hash_t *final_mergeinfo = apr_hash_make(pool);
   apr_hash_index_t *hi;
-  svn_stringbuf_t *merge_val;
-  const void *merge_source;
-  void *rangelist;
+  apr_pool_t *subpool = svn_pool_create(pool);
 
-  SVN_ERR(svn_mergeinfo_parse(&mergeinfo, mergeinfo_orig, pool));
-  modified_mergeinfo = apr_hash_make(pool);
+  SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool));
   for (hi = apr_hash_first(NULL, mergeinfo); hi; hi = apr_hash_next(hi))
     {
+      const char *merge_source;
+      apr_array_header_t *rangelist;
+      struct parse_baton_t *pb = rb->pb;
       int i;
-      apr_hash_this(hi, &merge_source, NULL, &rangelist);
+      const void *key;
+      void *val;
 
-      /* Look whether the merge_source is a part of the included prefix. */
-      if (skip_path(merge_source, rb->pb->prefixes, rb->pb->do_exclude))
+      apr_hash_this(hi, &key, NULL, &val);
+      merge_source = (const char *) key;
+      rangelist = (apr_array_header_t *) val;
+
+      /* Determine whether the merge_source is a part of the prefix. */
+      if (skip_path(merge_source, pb->prefixes, pb->do_exclude))
         {
-          if (rb->pb->skip_missing_merge_sources)
+          if (pb->skip_missing_merge_sources)
             continue;
           else
             return svn_error_createf(SVN_ERR_INCOMPLETE_DATA, 0,
-                                     _("Missing merge source path '%s', try "
+                                     _("Missing merge source path '%s'; try "
                                        "with --skip-missing-merge-sources"),
-                                     (const char *)merge_source);
+                                     merge_source);
         }
 
-      /* Renumber mergeinfo rangelist. */
-      for (i = 0; i < ((apr_array_header_t *)rangelist)->nelts; i++)
+      /* Possibly renumber revisions in merge source's rangelist. */
+      if (pb->do_renumber_revs)
         {
-          struct revmap_t *revmap_start;
-          struct revmap_t *revmap_end;
-          svn_merge_range_t *range;
+          for (i = 0; i < rangelist->nelts; i++)
+            {
+              struct revmap_t *revmap_start;
+              struct revmap_t *revmap_end;
+              svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
+                                                       svn_merge_range_t *);
 
-          range = APR_ARRAY_IDX((apr_array_header_t *)rangelist, i,
-                                svn_merge_range_t *);
-          revmap_start = apr_hash_get(rb->pb->renumber_history, &range->start,
-                                      sizeof(svn_revnum_t));
-          revmap_end = apr_hash_get(rb->pb->renumber_history, &range->end,
-                                    sizeof(svn_revnum_t));
-          range->start = revmap_start->rev;
-          range->end = revmap_end->rev;
+              revmap_start = apr_hash_get(pb->renumber_history,
+                                          &range->start, sizeof(svn_revnum_t));
+              if (! (revmap_start && SVN_IS_VALID_REVNUM(revmap_start->rev)))
+                return svn_error_createf
+                  (SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+                   _("No valid revision range 'start' in filtered stream"));
+
+              revmap_end = apr_hash_get(pb->renumber_history,
+                                        &range->end, sizeof(svn_revnum_t));
+              if (! (revmap_end && SVN_IS_VALID_REVNUM(revmap_end->rev)))
+                return svn_error_createf
+                  (SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+                   _("No valid revision range 'end' in filtered stream"));
+
+              range->start = revmap_start->rev;
+              range->end = revmap_end->rev;
+            }
+          apr_hash_set(final_mergeinfo, merge_source,
+                       APR_HASH_KEY_STRING, rangelist);
         }
-      apr_hash_set(modified_mergeinfo, (const char*)merge_source,
-                   APR_HASH_KEY_STRING, rangelist);
     }
-  svn_mergeinfo_to_stringbuf(&merge_val, modified_mergeinfo, pool);
-  *mergeinfo_val = merge_val->data;
+
+  SVN_ERR(svn_mergeinfo_sort(final_mergeinfo, subpool));
+  SVN_ERR(svn_mergeinfo__to_string(final_val, final_mergeinfo, pool));
+  svn_pool_destroy(subpool);
 
   return SVN_NO_ERROR;
 }
@@ -728,14 +752,10 @@
 
   if (strcmp(name, SVN_PROP_MERGE_INFO) == 0)
     {
-      apr_pool_t *subpool = svn_pool_create(apr_hash_pool_get(rb->props));
-      const char *mergeinfo_val;
-
-      /* Renumber revisions for valid merge sources in mergeinfo. */
-      SVN_ERR(renumber_merge_source_rev_range(&mergeinfo_val, value->data, rb,
-                                              subpool));
-      value = svn_string_create(mergeinfo_val, apr_hash_pool_get(rb->props));
-      svn_pool_destroy(subpool);
+      svn_string_t *filtered_mergeinfo;  /* Avoid compiler warning. */ 
+      apr_pool_t *pool = apr_hash_pool_get(rb->props);
+      SVN_ERR(adjust_mergeinfo(&filtered_mergeinfo, value, rb, pool));
+      value = filtered_mergeinfo;
     }
 
   write_prop_to_stringbuf(&(nb->props), name, value);
diff --git a/subversion/tests/cmdline/diff_tests.py b/subversion/tests/cmdline/diff_tests.py
index 16130fd..93bf499 100755
--- a/subversion/tests/cmdline/diff_tests.py
+++ b/subversion/tests/cmdline/diff_tests.py
@@ -2951,6 +2951,18 @@
   svntest.actions.run_and_verify_diff_summarize_xml(
     [], sbox.repo_url, paths, items, props, kinds, '-r1:2', sbox.repo_url)
 
+def diff_file_depth_empty(sbox):
+  "svn diff --depth=empty FILE_WITH_LOCAL_MODS"
+  # The bug was that no diff output would be generated.  Check that some is.
+  sbox.build()
+  iota_path = os.path.join(sbox.wc_dir, 'iota')
+  svntest.main.file_append(iota_path, "new text in iota")
+  out, err = svntest.main.run_svn(None, 'diff', '--depth', 'empty', iota_path)
+  if err:
+    raise svntest.Failure
+  if len(out) < 4:
+    raise svntest.Failure
+
 ########################################################################
 #Run the tests
 
@@ -3002,6 +3014,7 @@
               diff_ignore_eolstyle_empty_lines,
               diff_backward_repos_wc_copy,
               diff_summarize_xml,
+              diff_file_depth_empty,
               ]
 
 if __name__ == '__main__':
diff --git a/subversion/tests/libsvn_subr/mergeinfo-test.c b/subversion/tests/libsvn_subr/mergeinfo-test.c
index c6b7502..000146a 100644
--- a/subversion/tests/libsvn_subr/mergeinfo-test.c
+++ b/subversion/tests/libsvn_subr/mergeinfo-test.c
@@ -119,10 +119,6 @@
     "/trunk: 5,7-9,10,11,13,14",
     "/trunk: 3-10,11*,13,14",
     "/branch: 1,2-18*,33*"
-    /* ### Should svn_mergeinfo_parse() handle unordered
-       ### but otherwise valid input strings like this?
-       ### Currently this fails.
-       "/trunk: 7-9,5,10,14,13,11" */
   };
 /* Paths corresponding to mergeinfo_vals. */
 static const char * const mergeinfo_paths[NBR_MERGEINFO_VALS] =
@@ -250,14 +246,48 @@
 }
 
 
-#define NBR_BROKEN_MERGEINFO_VALS 4
+#define NBR_BROKEN_MERGEINFO_VALS 32
 /* Invalid mergeinfo values. */
 static const char * const broken_mergeinfo_vals[NBR_BROKEN_MERGEINFO_VALS] =
   {
+    /* Invalid grammar  */
     "/missing-revs",
     "/trunk: 5,7-9,10,11,13,14,",
     "/trunk 5,7-9,10,11,13,14",
-    "/trunk:5 7--9 10 11 13 14"
+    "/trunk:5 7--9 10 11 13 14",
+    /* Unordered revs   */
+    "/trunk:3-6,15,18,9,22",
+    "/trunk:5,3",
+    "/trunk:3-6*,15*,18*,9,22*",
+    "/trunk:5,3*",
+    /* Overlapping revs differing inheritability */
+    "/trunk:5-9*,9",
+    "/trunk:5,5-9*",
+    "/trunk:5-9,9*",
+    "/trunk:5*,5-9",
+    "/trunk:4,4*",
+    "/trunk:4*,4",
+    "/trunk:3-7*,4-23",
+    "/trunk:3-7,4-23*",
+    /* Overlapping revs same inheritability */
+    "/trunk:5-9*,9*",
+    "/trunk:5*,5-9*", 
+    "/trunk:5-9,9",
+    "/trunk:5,5-9",
+    "/trunk:4,4",
+    "/trunk:4*,4*",
+    "/trunk:3-7,4-23",
+    "/trunk:3-7*,4-23*",
+    /* Reversed revision ranges */
+    "/trunk:22-20",
+    "/trunk:22-20*",
+    "/trunk:3,7-12,22-20,25",
+    "/trunk:3,7,22-20*,25-30",
+    /* Range with same start and end revision */
+    "/trunk:22-22",
+    "/trunk:22-22*",
+    "/trunk:3,7-12,20-20,25",
+    "/trunk:3,7,20-20*,25-30",
   };
 
 static svn_error_t *
@@ -288,32 +318,7 @@
 }
 
 
-static const char *mergeinfo1 = "/trunk: 5,7-9,10,11,13,14,3\n/fred:8-10";
-static const char *mergeinfo2 = "/trunk: 1-4,6,3\n/fred:9-12";
-static const char *mergeinfo3 = "/trunk: 15-25, 35-45, 55-65";
-static const char *mergeinfo4 = "/trunk: 15-25, 35-45";
-static const char *mergeinfo5 = "/trunk: 10-30, 35-45, 55-65";
-static const char *mergeinfo6 = "/trunk: 15-25";
-static const char *mergeinfo7 = "/empty-rangelist:\n/with-trailing-space: ";
-
-static svn_error_t *
-test_parse_multi_line_mergeinfo(const char **msg,
-                                svn_boolean_t msg_only,
-                                svn_test_opts_t *opts,
-                                apr_pool_t *pool)
-{
-  *msg = "parse multi line mergeinfo";
-
-  if (msg_only)
-    return SVN_NO_ERROR;
-
-  SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo1, pool));
-
-  SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo7, pool));
-
-  return SVN_NO_ERROR;
-}
-
+static const char *mergeinfo1 = "/trunk: 3,5,7-9,10,11,13,14\n/fred:8-10";
 
 #define NBR_RANGELIST_DELTAS 4
 
@@ -399,7 +404,7 @@
   SVN_ERR(svn_mergeinfo_parse(&to, "/trunk: 1-6,12-16,30-32", pool));
   /* On /trunk: deleted (7, 9, 11, 33-34) and added (2, 5-6, 13-16, 30) */
   SVN_ERR(svn_mergeinfo_diff(&deleted, &added, from, to,
-                             svn_rangelist_ignore_inheritance, pool));
+                             FALSE, pool));
 
   /* Verify calculation of range list deltas. */
   SVN_ERR(verify_mergeinfo_deltas(deleted, expected_rangelist_deletions,
@@ -520,53 +525,167 @@
                      svn_test_opts_t *opts,
                      apr_pool_t *pool)
 {
-  apr_array_header_t *result;
-  svn_merge_range_t *resultrange;
-  *msg = "merging of mergeinfo hashs";
+  int i;
+  svn_stringbuf_t *output;
 
+  /* Structures and constants for test_merge_mergeinfo() */
+  /* Number of svn_mergeinfo_merge test sets */
+  #define NBR_MERGEINFO_MERGES 12
+
+  /* Maximum number of expected paths in the results
+     of the svn_mergeinfo_merge tests */
+  #define MAX_NBR_MERGEINFO_PATHS 4
+
+  /* Maximum number of expected ranges in the results
+     of the svn_mergeinfo_merge tests */
+  #define MAX_NBR_MERGEINFO_RANGES 10
+
+  /* Struct to store a path and it's expected ranges,
+     i.e. the expected result of an svn_mergeinfo_merge
+     test. */
+  struct mergeinfo_merge_path_range
+    {
+      const char *path;
+      svn_merge_range_t expected_rngs[MAX_NBR_MERGEINFO_RANGES];
+    };
+
+  /* Struct for svn_mergeinfo_merge test data.
+     If MERGEINFO1 and MERGEINFO2 are parsed to a hash with
+     svn_mergeinfo_parse() and then merged with svn_mergeinfo_merge(),
+     the resulting hash should have EXPECTED_PATHS number of paths
+     mapped to rangelists and each mapping is described by PATH_RNGS
+     where PATH_RNGS->PATH is not NULL. */
+  struct mergeinfo_merge_test_data
+    {
+      const char *mergeinfo1;
+      const char *mergeinfo2;
+      int expected_paths;
+      struct mergeinfo_merge_path_range path_rngs[MAX_NBR_MERGEINFO_PATHS];
+    };
+
+  static struct mergeinfo_merge_test_data mergeinfo[NBR_MERGEINFO_MERGES] =
+    {
+      /* One path, intersecting inheritable ranges */
+      { "/trunk: 5-10",
+        "/trunk: 6", 1,
+        { {"/trunk", { {4, 10, TRUE} } } } },
+
+      /* One path, intersecting non-inheritable ranges */
+      { "/trunk: 5-10*",
+        "/trunk: 6*", 1,
+        { {"/trunk", { {4, 10, FALSE} } } } },
+
+      /* One path, intersecting ranges with different inheritability */
+      { "/trunk: 5-10",
+        "/trunk: 6*", 1,
+        { {"/trunk", { {4, 10, TRUE} } } } },
+
+      /* One path, intersecting ranges with different inheritability */
+      { "/trunk: 5-10*",
+        "/trunk: 6", 1,
+        { {"/trunk", { {4, 5, FALSE}, {5, 6, TRUE}, {6, 10, FALSE} } } } },
+
+      /* Adjacent ranges all inheritable ranges */
+      { "/trunk: 1,3,5-11,13",
+        "/trunk: 2,4,12,14-22", 1,
+         { {"/trunk", { {0, 22, TRUE} } } } },
+
+      /* Adjacent ranges all non-inheritable ranges */
+      { "/trunk: 1*,3*,5-11*,13*",
+        "/trunk: 2*,4*,12*,14-22*", 1,
+         { {"/trunk", { {0, 22, FALSE} } } } },
+
+      /* Adjacent ranges differing inheritability */
+      { "/trunk: 1*,3*,5-11*,13*",
+        "/trunk: 2,4,12,14-22", 1,
+         { {"/trunk", { { 0,  1, FALSE}, { 1,  2, TRUE},
+                        { 2,  3, FALSE}, { 3,  4, TRUE},
+                        { 4, 11, FALSE}, {11, 12, TRUE},
+                        {12, 13, FALSE}, {13, 22, TRUE} } } } },
+
+      /* Adjacent ranges differing inheritability */
+      { "/trunk: 1,3,5-11,13",
+        "/trunk: 2*,4*,12*,14-22*", 1,
+         { {"/trunk", { { 0,  1, TRUE}, { 1,  2, FALSE},
+                        { 2,  3, TRUE}, { 3,  4, FALSE},
+                        { 4, 11, TRUE}, {11, 12, FALSE},
+                        {12, 13, TRUE}, {13, 22, FALSE} } } } },
+
+      /* Two paths all inheritable ranges */
+      { "/trunk: 3,5,7-9,10,11,13,14\n/fred:8-10",
+        "/trunk: 1-4,6\n/fred:9-12", 2,
+        { {"/trunk", { {0, 11, TRUE}, {12, 14, TRUE} } },
+          {"/fred",  { {7, 12, TRUE} } } } },
+
+      /* Two paths all non-inheritable ranges */
+      { "/trunk: 3*,5*,7-9*,10*,11*,13*,14*\n/fred:8-10*",
+        "/trunk: 1-4*,6*\n/fred:9-12*", 2,
+        { {"/trunk", { {0, 11, FALSE}, {12, 14, FALSE} } },
+          {"/fred",  { {7, 12, FALSE} } } } },
+
+      /* Two paths mixed inheritability */
+      { "/trunk: 3,5*,7-9,10,11*,13,14\n/fred:8-10",
+        "/trunk: 1-4,6\n/fred:9-12*", 2,
+        { {"/trunk", { { 0,  4, TRUE }, { 4,  5, FALSE}, {5, 10, TRUE},
+                       {10, 11, FALSE}, {12, 14, TRUE } } },
+          {"/fred",  { { 7, 10, TRUE }, {10, 12, FALSE} } } } },
+
+      /* A slew of different paths but no ranges to be merged */
+      { "/trunk: 3,5-9*\n/betty: 2-4",
+        "/fred: 1-18\n/barney: 1,3-43", 4,
+        { {"/trunk",  { {2,  3, TRUE}, {4,  9, FALSE} } },
+          {"/betty",  { {1,  4, TRUE} } },
+          {"/barney", { {0,  1, TRUE}, {2, 43, TRUE} } },
+          {"/fred",   { {0, 18, TRUE} } } } }
+    };
+
+  *msg = "merging of mergeinfo hashs";
   if (msg_only)
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo1, pool));
-  SVN_ERR(svn_mergeinfo_parse(&info2, mergeinfo2, pool));
-
-  SVN_ERR(svn_mergeinfo_merge(info1, info2, svn_rangelist_ignore_inheritance,
-                              pool));
-
-  if (apr_hash_count(info1) != 2)
-    return fail(pool, "Wrong number of paths in merged mergeinfo");
-
-  result = apr_hash_get(info1, "/fred", APR_HASH_KEY_STRING);
-  if (!result)
-    return fail(pool, "Missing path in merged mergeinfo");
-
-  /* /fred should have one merged range, 8-12. */
-  if (result->nelts != 1)
-    return fail(pool, "Merging failed to combine ranges");
-
-  resultrange = APR_ARRAY_IDX(result, 0, svn_merge_range_t *);
-
-  if (resultrange->start != 7 || resultrange->end != 12)
-    return fail(pool, "Range combining produced wrong result");
-
-  result = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING);
-  if (!result)
-    return fail(pool, "Missing path in merged mergeinfo");
-
-  /* /trunk should have two merged ranges, 1-11, and 13-14. */
-
-  if (result->nelts != 2)
-    return fail(pool, "Merging failed to combine ranges");
-
-  resultrange = APR_ARRAY_IDX(result, 0, svn_merge_range_t *);
-
-  if (resultrange->start != 0 || resultrange->end != 11)
-    return fail(pool, "Range combining produced wrong result");
-
-  resultrange = APR_ARRAY_IDX(result, 1, svn_merge_range_t *);
-
-  if (resultrange->start != 12 || resultrange->end != 14)
-    return fail(pool, "Range combining produced wrong result");
+  for (i = 0; i < NBR_MERGEINFO_MERGES; i++)
+    {
+      int j;
+      SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo[i].mergeinfo1, pool));
+      SVN_ERR(svn_mergeinfo_parse(&info2, mergeinfo[i].mergeinfo2, pool));
+      SVN_ERR(svn_mergeinfo_merge(info1, info2, pool));
+      SVN_ERR(svn_mergeinfo_to_stringbuf(&output, info1, pool));
+      if (mergeinfo[i].expected_paths != apr_hash_count(info1))
+        return fail(pool, "Wrong number of paths in merged mergeinfo");
+      for (j = 0; j < mergeinfo[i].expected_paths; j++)
+        {
+          int k;
+          apr_array_header_t *rangelist =
+            apr_hash_get(info1, mergeinfo[i].path_rngs[j].path,
+                         APR_HASH_KEY_STRING);
+          if (!rangelist)
+            return fail(pool, "Missing path '%s' in merged mergeinfo",
+                        mergeinfo[i].path_rngs->path);
+          for (k = 0; k < rangelist->nelts; k++)
+            {
+              svn_merge_range_t *ranges =
+                APR_ARRAY_IDX(rangelist, k, svn_merge_range_t *);
+              if (ranges->start
+                    != mergeinfo[i].path_rngs[j].expected_rngs[k].start
+                  || ranges->end
+                    != mergeinfo[i].path_rngs[j].expected_rngs[k].end
+                  || ranges->inheritable
+                    != mergeinfo[i].path_rngs[j].expected_rngs[k].inheritable)
+                return fail(
+                  pool,
+                  "Range'%i-%i%s' not found in merged mergeinfo",
+                  mergeinfo[i].path_rngs->expected_rngs[k].start,
+                  mergeinfo[i].path_rngs->expected_rngs[k].end,
+                  mergeinfo[i].path_rngs->expected_rngs[k].inheritable
+                  ? "" : "*");             
+            }
+          /* Were more ranges expected? */
+          if (k < MAX_NBR_MERGEINFO_RANGES
+              && mergeinfo[i].path_rngs[j].expected_rngs[k].start != 0)
+            return fail(pool,
+                        "Not all expected ranges found in merged mergeinfo");
+        }
+    }
 
   return SVN_NO_ERROR;
 }
@@ -577,66 +696,120 @@
                       svn_test_opts_t *opts,
                       apr_pool_t *pool)
 {
-  apr_array_header_t *whiteboard;
-  apr_array_header_t *eraser;
-  apr_array_header_t *result;
-  svn_stringbuf_t *outputstring;
-  svn_stringbuf_t *expected1 = svn_stringbuf_create("55-65", pool);
-  svn_stringbuf_t *expected2 = svn_stringbuf_create("10-14,26-30,55-65",
-                                                    pool);
-  svn_stringbuf_t *expected3 = svn_stringbuf_create("10-14,26-30,35-45,55-65",
-                                                    pool);
-  *msg = "remove of rangelist";
+  int i, j;
+  svn_error_t *err, *child_err;
+  apr_array_header_t *output, *eraser, *whiteboard;
 
-  if (msg_only)
-    return SVN_NO_ERROR;
+  /* Struct for svn_rangelist_remove test data.
+     Parse WHITEBOARD and ERASER to hashes and then get the rangelist for
+     path 'A' from both.
+     
+     Remove ERASER's rangelist from WHITEBOARD's twice, once while
+     considering inheritance and once while not.  In the first case the
+     resulting rangelist should have EXPECTED_RANGES_CONSIDER_INHERITANCE
+     number of ranges and these ranges should match the ranges in
+     EXPECTED_REMOVED_CONSIDER_INHERITANCE.  In the second case there
+     should be EXPECTED_RANGES_IGNORE_INHERITANCE number of ranges and
+     these should match EXPECTED_REMOVED_IGNORE_INHERITANCE */
+  struct rangelist_remove_test_data
+  {
+    const char *whiteboard;
+    const char *eraser;
+    int expected_ranges_consider_inheritance;
+    svn_merge_range_t expected_removed_consider_inheritance[10];
+    int expected_ranges_ignore_inheritance;
+    svn_merge_range_t expected_removed_ignore_inheritance[10];
+  };
 
-  SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo3, pool));
+  #define SIZE_OF_RANGE_REMOVE_TEST_ARRAY 15
 
-  whiteboard = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING);
-  if (!whiteboard)
-    return fail(pool, "Missing path in parsed mergeinfo");
+  /* The actual test data */
+  struct rangelist_remove_test_data test_data[SIZE_OF_RANGE_REMOVE_TEST_ARRAY] =
+    {
+      /* Eraser is a proper subset of whiteboard */
+      {"/A: 1-44",  "/A: 5",  2, { {0,  4, TRUE }, {5, 44, TRUE }},
+                              2, { {0,  4, TRUE }, {5, 44, TRUE }}},
+      {"/A: 1-44*", "/A: 5",  1, { {0, 44, FALSE} },
+                              2, { {0,  4, FALSE}, {5, 44, FALSE}}},
+      {"/A: 1-44",  "/A: 5*", 1, { {0, 44, TRUE } },
+                              2, { {0,  4, TRUE }, {5, 44, TRUE }}},
+      {"/A: 1-44*", "/A: 5*", 2, { {0,  4, FALSE}, {5, 44, FALSE}},
+                              2, { {0,  4, FALSE}, {5, 44, FALSE}}},
+      /* Non-intersecting ranges...nothing is removed */
+      {"/A: 2-9,14-19",   "/A: 12",  2, { {1, 9, TRUE }, {13, 19, TRUE }},
+                                     2, { {1, 9, TRUE }, {13, 19, TRUE }}},
+      {"/A: 2-9*,14-19*", "/A: 12",  2, { {1, 9, FALSE}, {13, 19, FALSE}},
+                                     2, { {1, 9, FALSE}, {13, 19, FALSE}}},
+      {"/A: 2-9,14-19",   "/A: 12*", 2, { {1, 9, TRUE }, {13, 19, TRUE }},
+                                     2, { {1, 9, TRUE }, {13, 19, TRUE }}},
+      {"/A: 2-9*,14-19*", "/A: 12*", 2, { {1, 9, FALSE}, {13, 19, FALSE}},
+                                     2, { {1, 9, FALSE}, {13, 19, FALSE}}},
+      /* Eraser overlaps whiteboard */
+      {"/A: 1,9-17",  "/A: 12-20",  2, { {0,  1, TRUE }, {8, 11, TRUE }},
+                                    2, { {0,  1, TRUE }, {8, 11, TRUE }}},
+      {"/A: 1,9-17*", "/A: 12-20",  2, { {0,  1, TRUE }, {8, 17, FALSE}},
+                                    2, { {0,  1, TRUE }, {8, 11, FALSE}}},
+      {"/A: 1,9-17",  "/A: 12-20*", 2, { {0,  1, TRUE }, {8, 17, TRUE }},
+                                    2, { {0,  1, TRUE }, {8, 11, TRUE }}},
+      {"/A: 1,9-17*", "/A: 12-20*", 2, { {0,  1, TRUE }, {8, 11, FALSE}},
+                                    2, { {0,  1, TRUE }, {8, 11, FALSE}}},
+      /* Empty ranges */
+      {"/A:",  "/A:",             0, { {0, 0, FALSE}},
+                                  0, { {0, 0, FALSE}}},
+      {"/A:",  "/A: 5-8,10-100",  0, { {0, 0, FALSE}},
+                                  0, { {0, 0, FALSE}}},
+      {"/A: 5-8,10-100",  "/A:",  2, { {4, 8, TRUE }, {9, 100, TRUE }},
+                                  2, { {4, 8, TRUE }, {9, 100, TRUE }}}
+    };
 
-  SVN_ERR(svn_mergeinfo_parse(&info2, mergeinfo4, pool));
+  err = child_err = SVN_NO_ERROR;
+  for (j = 0; j < 2; j++)
+    {
+      for (i = 0; i < SIZE_OF_RANGE_REMOVE_TEST_ARRAY; i++)
+        {
+          int expected_nbr_ranges;
+          svn_merge_range_t *expected_ranges;
+            
+          SVN_ERR(svn_mergeinfo_parse(&info1, (test_data[i]).eraser, pool));
+          SVN_ERR(svn_mergeinfo_parse(&info2, (test_data[i]).whiteboard, pool));
+          eraser = apr_hash_get(info1, "/A", APR_HASH_KEY_STRING);
+          whiteboard = apr_hash_get(info2, "/A", APR_HASH_KEY_STRING);
 
-  eraser = apr_hash_get(info2, "/trunk", APR_HASH_KEY_STRING);
-  if (!eraser)
-    return fail(pool, "Missing path in parsed mergeinfo");
+          /* First pass try removal considering inheritance, on the
+             second pass ignore it. */
+          if (j == 0)
+            {
+              expected_nbr_ranges = (test_data[i]).expected_ranges_consider_inheritance;
+              expected_ranges = (test_data[i]).expected_removed_consider_inheritance;
+       
+            }
+          else
+            {
+              expected_nbr_ranges = (test_data[i]).expected_ranges_ignore_inheritance;
+              expected_ranges = (test_data[i]).expected_removed_ignore_inheritance;
+          
+            }
+          SVN_ERR(svn_rangelist_remove(&output, eraser, whiteboard,
+                                       j == 0 ? TRUE : FALSE,
+                                       pool));
+          child_err = verify_ranges_match(output, expected_ranges,
+                                          expected_nbr_ranges,
+                                          apr_psprintf(pool,
+                                                       "svn_rangelist_remove "
+                                                       "case %i", i),
+                                          "remove", pool);
 
-  SVN_ERR(svn_rangelist_remove(&result, eraser, whiteboard, TRUE, pool));
-
-  SVN_ERR(svn_rangelist_to_stringbuf(&outputstring, result, pool));
-
-  if (svn_stringbuf_compare(expected1, outputstring) != TRUE)
-    return fail(pool, "Rangelist string not what we expected");
-
-  SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo5, pool));
-
-  whiteboard = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING);
-  if (!whiteboard)
-    return fail(pool, "Missing path in parsed mergeinfo");
-
-  SVN_ERR(svn_rangelist_remove(&result, eraser, whiteboard, TRUE, pool));
-
-  SVN_ERR(svn_rangelist_to_stringbuf(&outputstring, result, pool));
-
-  if (svn_stringbuf_compare(expected2, outputstring) != TRUE)
-    return fail(pool, "Rangelist string not what we expected");
-
-  SVN_ERR(svn_mergeinfo_parse(&info1, mergeinfo6, pool));
-
-  eraser = apr_hash_get(info1, "/trunk", APR_HASH_KEY_STRING);
-  if (!eraser)
-    return fail(pool, "Missing path in parsed mergeinfo");
-
-  SVN_ERR(svn_rangelist_remove(&result, eraser, whiteboard, TRUE, pool));
-
-  SVN_ERR(svn_rangelist_to_stringbuf(&outputstring, result, pool));
-
-  if (svn_stringbuf_compare(expected3, outputstring) != TRUE)
-    return fail(pool, "Rangelist string not what we expected");
-
-  return SVN_NO_ERROR;
+          /* Collect all the errors rather than returning on the first. */
+          if (child_err)
+            {
+              if (err)
+                svn_error_compose(err, child_err);
+              else
+                err = child_err;
+            }
+        }
+    }
+  return err;
 }
 
 /* ### Share code with test_diff_mergeinfo() and test_remove_rangelist(). */
@@ -859,6 +1032,366 @@
     }
   return SVN_NO_ERROR;
 }
+
+static svn_error_t *
+test_rangelist_merge(const char **msg,
+                     svn_boolean_t msg_only,
+                     svn_test_opts_t *opts,
+                     apr_pool_t *pool)
+{
+  int i;
+  svn_error_t *err, *child_err;
+  apr_array_header_t *rangelist1, *rangelist2;
+  
+  /* Struct for svn_rangelist_merge test data.  Similar to
+     mergeinfo_merge_test_data struct in svn_mergeinfo_merge() test. */
+  struct rangelist_merge_test_data
+  {
+    const char *mergeinfo1;
+    const char *mergeinfo2;
+    int expected_ranges;
+    svn_merge_range_t expected_merge[6];
+  };
+
+  #define SIZE_OF_RANGE_MERGE_TEST_ARRAY 52
+  /* The actual test data. */
+  struct rangelist_merge_test_data test_data[SIZE_OF_RANGE_MERGE_TEST_ARRAY] =
+    {
+      /* Non-intersecting ranges */
+      {"/A: 1-44",    "/A: 70-101",  2, {{ 0, 44, TRUE }, {69, 101, TRUE }}},
+      {"/A: 1-44*",   "/A: 70-101",  2, {{ 0, 44, FALSE}, {69, 101, TRUE }}},
+      {"/A: 1-44",    "/A: 70-101*", 2, {{ 0, 44, TRUE }, {69, 101, FALSE}}},
+      {"/A: 1-44*",   "/A: 70-101*", 2, {{ 0, 44, FALSE}, {69, 101, FALSE}}},
+      {"/A: 70-101",  "/A: 1-44",    2, {{ 0, 44, TRUE }, {69, 101, TRUE }}},
+      {"/A: 70-101*", "/A: 1-44",    2, {{ 0, 44, TRUE }, {69, 101, FALSE}}},
+      {"/A: 70-101",  "/A: 1-44*",   2, {{ 0, 44, FALSE}, {69, 101, TRUE }}},
+      {"/A: 70-101*", "/A: 1-44*",   2, {{ 0, 44, FALSE}, {69, 101, FALSE}}},
+
+      /* Intersecting ranges with same starting and ending revisions */
+      {"/A: 4-20",  "/A: 4-20",  1, {{3, 20, TRUE }}},
+      {"/A: 4-20*", "/A: 4-20",  1, {{3, 20, TRUE }}},
+      {"/A: 4-20",  "/A: 4-20*", 1, {{3, 20, TRUE }}},
+      {"/A: 4-20*", "/A: 4-20*", 1, {{3, 20, FALSE}}},
+
+      /* Intersecting ranges with same starting revision */
+      {"/A: 6-17",  "/A: 6-12",  1, {{5, 17, TRUE}}},
+      {"/A: 6-17*", "/A: 6-12",  2, {{5, 12, TRUE }, {12, 17, FALSE}}},
+      {"/A: 6-17",  "/A: 6-12*", 1, {{5, 17, TRUE }}},
+      {"/A: 6-17*", "/A: 6-12*", 1, {{5, 17, FALSE}}},
+      {"/A: 6-12",  "/A: 6-17",  1, {{5, 17, TRUE }}},
+      {"/A: 6-12*", "/A: 6-17",  1, {{5, 17, TRUE }}},
+      {"/A: 6-12",  "/A: 6-17*", 2, {{5, 12, TRUE }, {12, 17, FALSE}}},
+      {"/A: 6-12*", "/A: 6-17*", 1, {{5, 17, FALSE}}},
+
+      /* Intersecting ranges with same ending revision */
+      {"/A: 5-77",   "/A: 44-77",  1, {{4, 77, TRUE }}},
+      {"/A: 5-77*",  "/A: 44-77",  2, {{4, 43, FALSE}, {43, 77, TRUE}}},
+      {"/A: 5-77",   "/A: 44-77*", 1, {{4, 77, TRUE }}},
+      {"/A: 5-77*",  "/A: 44-77*", 1, {{4, 77, FALSE}}},
+      {"/A: 44-77",  "/A: 5-77",   1, {{4, 77, TRUE }}},
+      {"/A: 44-77*", "/A: 5-77",   1, {{4, 77, TRUE }}},
+      {"/A: 44-77",  "/A: 5-77*",  2, {{4, 43, FALSE}, {43, 77, TRUE}}},
+      {"/A: 44-77*", "/A: 5-77*",  1, {{4, 77, FALSE}}},
+
+      /* Intersecting ranges with different starting and ending revision
+         where one range is a proper subset of the other. */
+      {"/A: 12-24",  "/A: 20-23",  1, {{11, 24, TRUE }}},
+      {"/A: 12-24*", "/A: 20-23",  3, {{11, 19, FALSE}, {19, 23, TRUE },
+                                       {23, 24, FALSE}}},
+      {"/A: 12-24",  "/A: 20-23*", 1, {{11, 24, TRUE }}},
+      {"/A: 12-24*", "/A: 20-23*", 1, {{11, 24, FALSE}}},
+      {"/A: 20-23",  "/A: 12-24",  1, {{11, 24, TRUE }}},
+      {"/A: 20-23*", "/A: 12-24",  1, {{11, 24, TRUE }}},
+      {"/A: 20-23",  "/A: 12-24*", 3, {{11, 19, FALSE}, {19, 23, TRUE },
+                                       {23, 24, FALSE}}},
+      {"/A: 20-23*", "/A: 12-24*", 1, {{11, 24, FALSE}}},
+
+      /* Intersecting ranges with different starting and ending revision
+         where neither range is a proper subset of the other. */
+      {"/A: 50-73",  "/A: 60-99",  1, {{49, 99, TRUE }}},
+      {"/A: 50-73*", "/A: 60-99",  2, {{49, 59, FALSE}, {59, 99, TRUE }}},
+      {"/A: 50-73",  "/A: 60-99*", 2, {{49, 73, TRUE }, {73, 99, FALSE}}},
+      {"/A: 50-73*", "/A: 60-99*", 1, {{49, 99, FALSE}}},
+      {"/A: 60-99",  "/A: 50-73",  1, {{49, 99, TRUE }}},
+      {"/A: 60-99*", "/A: 50-73",  2, {{49, 73, TRUE }, {73, 99, FALSE}}},
+      {"/A: 60-99",  "/A: 50-73*", 2, {{49, 59, FALSE}, {59, 99, TRUE }}},
+      {"/A: 60-99*", "/A: 50-73*", 1, {{49, 99, FALSE}}},
+
+      /* Multiple ranges. */
+      {"/A: 1-5,7,12-13",    "/A: 2-17",  1, {{0,  17, TRUE }}},
+      {"/A: 1-5*,7*,12-13*", "/A: 2-17*", 1, {{0,  17, FALSE}}},
+
+      {"/A: 1-5,7,12-13",    "/A: 2-17*", 6,
+       {{0,  5, TRUE }, { 5,  6, FALSE}, { 6,  7, TRUE },
+        {7, 11, FALSE}, {11, 13, TRUE }, {13, 17, FALSE}}},
+
+      {"/A: 1-5*,7*,12-13*", "/A: 2-17", 2,
+       {{0, 1, FALSE}, {1, 17, TRUE }}},
+
+      {"/A: 2-17",  "/A: 1-5,7,12-13",    1, {{0,  17, TRUE }}},
+      {"/A: 2-17*", "/A: 1-5*,7*,12-13*", 1, {{0,  17, FALSE}}},
+
+      {"/A: 2-17*", "/A: 1-5,7,12-13", 6,
+       {{0,  5, TRUE }, { 5,  6, FALSE}, { 6,  7, TRUE },
+        {7, 11, FALSE}, {11, 13, TRUE }, {13, 17, FALSE}}},
+
+      {"/A: 2-17", "/A: 1-5*,7*,12-13*", 2,
+       {{0, 1, FALSE}, {1, 17, TRUE}}},
+    };
+  *msg = "merge of rangelists";
+  if (msg_only)
+    return SVN_NO_ERROR;
+
+  err = child_err = SVN_NO_ERROR;
+  for (i = 0; i < SIZE_OF_RANGE_MERGE_TEST_ARRAY; i++)
+    {
+      SVN_ERR(svn_mergeinfo_parse(&info1, (test_data[i]).mergeinfo1, pool));
+      SVN_ERR(svn_mergeinfo_parse(&info2, (test_data[i]).mergeinfo2, pool));
+      rangelist1 = apr_hash_get(info1, "/A", APR_HASH_KEY_STRING);
+      rangelist2 = apr_hash_get(info2, "/A", APR_HASH_KEY_STRING);
+      SVN_ERR(svn_rangelist_merge(&rangelist1, rangelist2, pool));
+      child_err = verify_ranges_match(rangelist1,
+                                     (test_data[i]).expected_merge,
+                                     (test_data[i]).expected_ranges,
+                                     apr_psprintf(pool,
+                                                  "svn_rangelist_merge "
+                                                  "case %i", i),
+                                     "merge", pool);
+
+      /* Collect all the errors rather than returning on the first. */
+      if (child_err)
+        {
+          if (err)
+            svn_error_compose(err, child_err);
+          else
+            err = child_err;
+        }
+    }
+  return err;
+}
+
+static svn_error_t *
+test_rangelist_diff(const char **msg,
+                    svn_boolean_t msg_only,
+                    svn_test_opts_t *opts,
+                    apr_pool_t *pool)
+{
+  int i;
+  svn_error_t *err, *child_err;
+  apr_array_header_t *from, *to, *added, *deleted;
+
+  /* Structure containing two ranges to diff and the expected output of the
+     diff both when considering and ignoring range inheritance. */
+  struct rangelist_diff_test_data
+  {
+    /* svn:mergeinfo string representations */
+    const char *from;
+    const char *to;
+    
+    /* Expected results for performing svn_rangelist_diff
+       while considering differences in inheritability to be real
+       differences. */
+    int expected_add_ranges;
+    svn_merge_range_t expected_adds[10];
+    int expected_del_ranges;
+    svn_merge_range_t expected_dels[10];
+
+    /* Expected results for performing svn_rangelist_diff
+       while ignoring differences in inheritability. */
+    int expected_add_ranges_ignore_inheritance;
+    svn_merge_range_t expected_adds_ignore_inheritance[10];
+    int expected_del_ranges_ignore_inheritance;
+    svn_merge_range_t expected_dels_ignore_inheritance[10];
+  };
+
+  #define SIZE_OF_RANGE_DIFF_TEST_ARRAY 16
+  /* The actual test data array.
+
+                    'from' --> {"/A: 1,5-8",  "/A: 1,6,10-12", <-- 'to' 
+      Number of adds when  -->  1, { { 9, 12, TRUE } },
+      considering inheritance
+
+      Number of dels when  -->  2, { { 4,  5, TRUE }, { 6, 8, TRUE } },
+      considering inheritance
+
+      Number of adds when  -->  1, { { 9, 12, TRUE } },
+      ignoring inheritance
+
+      Number of dels when  -->  2, { { 4,  5, TRUE }, { 6, 8, TRUE } } },
+      ignoring inheritance
+                                            ^               ^
+                                    The expected svn_merge_range_t's
+  */
+  struct rangelist_diff_test_data test_data[SIZE_OF_RANGE_DIFF_TEST_ARRAY] =
+    {
+      /* Add and Delete */
+      {"/A: 1",  "/A: 3",
+       1, { { 2, 3, TRUE } },
+       1, { { 0, 1, TRUE } },
+       1, { { 2, 3, TRUE } },
+       1, { { 0, 1, TRUE } } },
+
+      /* Add only */
+      {"/A: 1",  "/A: 1,3",
+       1, { { 2, 3, TRUE } },
+       0, { { 0, 0, FALSE } },
+       1, { { 2, 3, TRUE } },
+       0, { { 0, 0, FALSE } } },
+
+      /* Delete only */
+      {"/A: 1,3",  "/A: 1",
+       0, { { 0, 0, FALSE } },
+       1, { { 2, 3, TRUE  } },
+       0, { { 0, 0, FALSE } },
+       1, { { 2, 3, TRUE  } } },
+
+      /* No diff */
+      {"/A: 1,3",  "/A: 1,3",
+       0, { { 0, 0, FALSE } },
+       0, { { 0, 0, FALSE } },
+       0, { { 0, 0, FALSE } },
+       0, { { 0, 0, FALSE } } },
+
+      {"/A: 1,3*",  "/A: 1,3*",
+       0, { { 0, 0, FALSE } },
+       0, { { 0, 0, FALSE } },
+       0, { { 0, 0, FALSE } },
+       0, { { 0, 0, FALSE } } },
+
+      /* Adds and Deletes */
+      {"/A: 1,5-8",  "/A: 1,6,10-12",
+       1, { { 9, 12, TRUE } },
+       2, { { 4, 5, TRUE }, { 6, 8, TRUE } },
+       1, { { 9, 12, TRUE } },
+       2, { { 4, 5, TRUE }, { 6, 8, TRUE } } },
+
+      {"/A: 6*",  "/A: 6",
+       1, { { 5, 6, TRUE  } },
+       1, { { 5, 6, FALSE } },
+       0, { { 0, 0, FALSE } },
+       0, { { 0, 0, FALSE } } },
+
+      /* Intersecting range with different inheritability */
+      {"/A: 6",  "/A: 6*",
+       1, { { 5, 6, FALSE } },
+       1, { { 5, 6, TRUE  } },
+       0, { { 0, 0, FALSE } },
+       0, { { 0, 0, FALSE } } },
+
+      {"/A: 6*",  "/A: 6",
+       1, { { 5, 6, TRUE  } },
+       1, { { 5, 6, FALSE } },
+       0, { { 0, 0, FALSE } },
+       0, { { 0, 0, FALSE } } },
+
+      {"/A: 1,5-8",  "/A: 1,6*,10-12",
+       2, { { 5,  6, FALSE }, { 9, 12, TRUE } },
+       1, { { 4,  8, TRUE  } },
+       1, { { 9, 12, TRUE  } },
+       2, { { 4,  5, TRUE  }, { 6,  8, TRUE } } },
+
+     {"/A: 1,5-8*",  "/A: 1,6,10-12",
+       2, { { 5,  6, TRUE  }, { 9, 12, TRUE  } },
+       1, { { 4,  8, FALSE } },
+       1, { { 9, 12, TRUE  } },
+       2, { { 4,  5, FALSE }, { 6,  8, FALSE } } },
+
+      /* Empty range diffs */
+      {"/A: 3-9",  "/A:",
+       0, { { 0, 0, FALSE } },
+       1, { { 2, 9, TRUE  } },
+       0, { { 0, 0, FALSE } },
+       1, { { 2, 9, TRUE  } } },
+
+      {"/A: 3-9*",  "/A:",
+       0, { { 0, 0, FALSE } },
+       1, { { 2, 9, FALSE } },
+       0, { { 0, 0, FALSE } },
+       1, { { 2, 9, FALSE } } },
+
+      {"/A:",  "/A: 3-9",
+       1, { { 2, 9, TRUE  } },
+       0, { { 0, 0, FALSE } },
+       1, { { 2, 9, TRUE  } },
+       0, { { 0, 0, FALSE } } },
+
+      {"/A:",  "/A: 3-9*",
+       1, { { 2, 9, FALSE } },
+       0, { { 0, 0, FALSE } },
+       1, { { 2, 9, FALSE } },
+       0, { { 0, 0, FALSE } } },
+
+       /* Empty range no diff */
+      {"/A:",  "/A:",
+       0, { { 0, 0, FALSE } },
+       0, { { 0, 0, FALSE } },
+       0, { { 0, 0, FALSE } },
+       0, { { 0, 0, FALSE } } },
+    };
+
+  *msg = "diff of rangelists";
+  if (msg_only)
+    return SVN_NO_ERROR;
+
+  err = child_err = SVN_NO_ERROR;
+  for (i = 0; i < SIZE_OF_RANGE_DIFF_TEST_ARRAY; i++)
+    {
+      SVN_ERR(svn_mergeinfo_parse(&info1, (test_data[i]).to, pool));
+      SVN_ERR(svn_mergeinfo_parse(&info2, (test_data[i]).from, pool));
+      to = apr_hash_get(info1, "/A", APR_HASH_KEY_STRING);
+      from = apr_hash_get(info2, "/A", APR_HASH_KEY_STRING);
+
+      /* First diff the ranges while considering
+         differences in inheritance. */
+      SVN_ERR(svn_rangelist_diff(&deleted, &added, from, to, TRUE, pool));
+
+      child_err = verify_ranges_match(added,
+                                     (test_data[i]).expected_adds,
+                                     (test_data[i]).expected_add_ranges,
+                                     apr_psprintf(pool,
+                                                  "svn_rangelist_diff"
+                                                  "case %i", i),
+                                     "diff", pool);
+      if (!child_err)
+        child_err = verify_ranges_match(deleted,
+                                        (test_data[i]).expected_dels,
+                                        (test_data[i]).expected_del_ranges,
+                                        apr_psprintf(pool,
+                                                     "svn_rangelist_diff"
+                                                     "case %i", i),
+                                                     "diff", pool);
+      if (!child_err)
+        {
+          /* Now do the diff while ignoring differences in inheritance. */
+          SVN_ERR(svn_rangelist_diff(&deleted, &added, from, to, FALSE,
+                                     pool));
+          child_err = verify_ranges_match(
+            added,
+            (test_data[i]).expected_adds_ignore_inheritance,
+            (test_data[i]).expected_add_ranges_ignore_inheritance,
+            apr_psprintf(pool, "svn_rangelist_diff case %i", i),
+            "diff", pool);
+
+          if (!child_err)
+            child_err = verify_ranges_match(
+              deleted,
+              (test_data[i]).expected_dels_ignore_inheritance,
+              (test_data[i]).expected_del_ranges_ignore_inheritance,
+              apr_psprintf(pool, "svn_rangelist_diff case %i", i),
+              "diff", pool);
+        }
+
+      /* Collect all the errors rather than returning on the first. */
+      if (child_err)
+        {
+          if (err)
+            svn_error_compose(err, child_err);
+          else
+            err = child_err;
+        }
+    }
+  return err;
+}
 
 /* The test table.  */
 
@@ -869,7 +1402,6 @@
     SVN_TEST_PASS(test_mergeinfo_dup),
     SVN_TEST_PASS(test_parse_combine_rangeinfo),
     SVN_TEST_PASS(test_parse_broken_mergeinfo),
-    SVN_TEST_PASS(test_parse_multi_line_mergeinfo),
     SVN_TEST_PASS(test_remove_rangelist),
     SVN_TEST_PASS(test_remove_mergeinfo),
     SVN_TEST_PASS(test_rangelist_reverse),
@@ -881,5 +1413,7 @@
     SVN_TEST_PASS(test_rangelist_to_string),
     SVN_TEST_PASS(test_mergeinfo_to_string),
     SVN_TEST_PASS(test_range_compact),
+    SVN_TEST_PASS(test_rangelist_merge),
+    SVN_TEST_PASS(test_rangelist_diff),
     SVN_TEST_NULL
   };
diff --git a/tools/client-side/change-svn-wc-format.py b/tools/client-side/change-svn-wc-format.py
index 78f0670..939fe91 100755
--- a/tools/client-side/change-svn-wc-format.py
+++ b/tools/client-side/change-svn-wc-format.py
@@ -44,10 +44,14 @@
   stream = error_msg and sys.stderr or sys.stdout
   if error_msg:
     print >> stream, "ERROR: %s\n" % error_msg
-  print >> stream, """usage: %s WC_PATH SVN_VERSION [--verbose] [--force]
+  print >> stream, """\
+usage: %s WC_PATH SVN_VERSION [--verbose] [--force] [--skip-unknown-format]
        %s --help
 
 Change the format of a Subversion working copy to that of SVN_VERSION.
+
+  --skip-unknown-format    : skip directories with unknown working copy
+                             format and continue the update
 """ % (progname, progname)
   sys.exit(error_msg and 1 or 0)
 
@@ -61,6 +65,7 @@
 class WCFormatConverter:
   "Performs WC format conversions."
   root_path = None
+  error_on_unrecognized = True
   force = False
   verbosity = 0
 
@@ -71,7 +76,7 @@
 
     # Avoid iterating in unversioned directories.
     if not get_adm_dir() in paths:
-      paths = []
+      del paths[:]
       return
 
     for path in paths:
@@ -83,7 +88,12 @@
 
         if self.verbosity:
           print "Parsing file '%s'" % entries.path
-        entries.parse(self.verbosity)
+        try:
+          entries.parse(self.verbosity)
+        except UnrecognizedWCFormatException, e:
+          if self.error_on_unrecognized:
+            raise
+          print >>sys.stderr, "%s, skipping" % (e,)
 
         if self.verbosity:
           print "Checking whether WC format can be converted"
@@ -170,7 +180,7 @@
     except ValueError:
       format_nbr = -1
     if not format_nbr in LATEST_FORMATS.values():
-      raise NotImplementedError("Unrecognized WC format detected")
+      raise UnrecognizedWCFormatException(format_nbr, self.path)
 
     # Parse file into individual entries, to later inspect for
     # non-convertable data.
@@ -264,7 +274,12 @@
       rep += "[%s] %s\n" % (Entries.entry_fields[i], self.fields[i])
     return rep
 
-class LossyConversionException(Exception):
+
+class LocalException(Exception):
+  """Root of local exception class hierarchy."""
+  pass
+
+class LossyConversionException(LocalException):
   "Exception thrown when a lossy WC format conversion is requested."
   def __init__(self, lossy_fields, str):
     self.lossy_fields = lossy_fields
@@ -272,9 +287,19 @@
   def __str__(self):
     return self.str
 
+class UnrecognizedWCFormatException(LocalException):
+  def __init__(self, format, path):
+    self.format = format
+    self.path = path
+  def __str__(self):
+    return "Unrecognized WC format %d in '%s'" % (self.format, self.path)
+
+
 def main():
   try:
-    opts, args = my_getopt(sys.argv[1:], "vh?", ["force", "verbose", "help"])
+    opts, args = my_getopt(sys.argv[1:], "vh?",
+                           ["debug", "force", "skip-unknown-format",
+                            "verbose", "help"])
   except:
     usage_and_exit("Unable to process arguments/options")
 
@@ -288,13 +313,18 @@
     usage_and_exit()
 
   # Process options.
+  debug = False
   for opt, value in opts:
     if opt in ("--help", "-h", "-?"):
       usage_and_exit()
     elif opt == "--force":
       converter.force = True
+    elif opt == "--skip-unknown-format":
+      converter.error_on_unrecognized = False
     elif opt in ("--verbose", "-v"):
       converter.verbosity += 1
+    elif opt == "--debug":
+      debug = True
     else:
       usage_and_exit("Unknown option '%s'" % opt)
 
@@ -305,9 +335,9 @@
 
   try:
     converter.change_wc_format(new_format_nbr)
-  except NotImplementedError, e:
-    usage_and_exit(str(e))
-  except LossyConversionException, e:
+  except LocalException, e:
+    if debug:
+      raise
     print >> sys.stderr, str(e)
     sys.exit(1)