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,
¬ify_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,
¬ify_b,
&merge_cmd_baton,
pool));
@@ -4284,7 +4280,6 @@
target_wcpath,
adm_access,
depth,
- ignore_ancestry,
&merge_callbacks,
¬ify_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,
¬ify_b,
&merge_cmd_baton,
- ignore_ancestry,
subpool));
}
@@ -4527,7 +4522,6 @@
entry,
adm_access,
depth,
- ignore_ancestry,
¬ify_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)