Modified: subversion/branches/1.7.x-issue4340-repos/build/generator/gen_win.py URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/build/generator/gen_win.py?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/build/generator/gen_win.py (original) +++ subversion/branches/1.7.x-issue4340-repos/build/generator/gen_win.py Sun Dec 14 11:44:03 2014 @@ -382,14 +382,21 @@ class WinGeneratorBase(GeneratorBase): install_targets = [x for x in install_targets if not (isinstance(x, gen_base.TargetExe) and x.install == 'bdb-test')] + # Don't build serf when we don't have it or for 1.3+ + if not self.serf_lib or (self.serf_ver_maj, self.serf_ver_min) >= (1, 3): + install_targets = [x for x in install_targets if x.name != 'serf'] + # Drop the serf target if we don't have both serf and openssl if not self.serf_lib: - install_targets = [x for x in install_targets if x.name != 'serf'] install_targets = [x for x in install_targets if x.name != 'libsvn_ra_serf'] if self.without_neon: install_targets = [x for x in install_targets if x.name != 'neon'] install_targets = [x for x in install_targets if x.name != 'libsvn_ra_neon'] + # Don't build zlib if we have an already compiled serf + if self.serf_lib and (self.serf_ver_maj, self.serf_ver_min) >= (1, 3): + install_targets = [x for x in install_targets if x.name != 'zlib'] + # Drop the swig targets if we don't have swig if not self.swig_path and not self.swig_libdir: install_targets = [x for x in install_targets @@ -712,7 +719,9 @@ class WinGeneratorBase(GeneratorBase): # Build ZLib as a dependency of Neon or Serf if we have it if self.zlib_path and (name == 'neon' or name == 'serf'): - depends.extend(self.sections['zlib'].get_targets()) + # Don't add a dependency to zlib, when we don't build zlib. + if not self.serf_lib or (self.serf_ver_maj, self.serf_ver_min) < (1, 3): + depends.extend(self.sections['zlib'].get_targets()) # To set the correct build order of the JavaHL targets, the javahl-javah # and libsvnjavahl targets are defined with extra dependencies in build.conf @@ -942,6 +951,9 @@ class WinGeneratorBase(GeneratorBase): if self.serf_lib: fakeincludes.append(self.apath(self.serf_path)) + + if self.openssl_path and self.openssl_inc_dir: + fakeincludes.append(self.apath(self.openssl_inc_dir)) if self.swig_libdir \ and (isinstance(target, gen_base.TargetSWIG) @@ -993,7 +1005,13 @@ class WinGeneratorBase(GeneratorBase): if self.sasl_path: fakelibdirs.append(self.apath(self.sasl_path, "lib")) if self.serf_lib: - fakelibdirs.append(self.apath(msvc_path_join(self.serf_path, cfg))) + if (self.serf_ver_maj, self.serf_ver_min) >= (1, 3): + fakelibdirs.append(self.apath(self.serf_path)) + + if self.openssl_path and self.openssl_lib_dir: + fakelibdirs.append(self.apath(self.openssl_lib_dir)) + else: + fakelibdirs.append(self.apath(msvc_path_join(self.serf_path, cfg))) fakelibdirs.append(self.apath(self.apr_path, cfg)) fakelibdirs.append(self.apath(self.apr_util_path, cfg)) @@ -1033,7 +1051,14 @@ class WinGeneratorBase(GeneratorBase): else: serflib = 'serf.lib' - zlib = (cfg == 'Debug' and 'zlibstatD.lib' or 'zlibstat.lib') + if self.serf_lib and (self.serf_ver_maj, self.serf_ver_min) >= (1, 3): + # We don't build zlib ourselves, so use the standard name + # (zdll.lib would link to zlib.dll) + zlib = 'zlib.lib' + else: + # We compile zlib ourselves to these explicit (non-standard) names + zlib = (cfg == 'Debug' and 'zlibstatD.lib' or 'zlibstat.lib') + sasllib = None if self.sasl_path: sasllib = 'libsasl.lib' @@ -1080,6 +1105,9 @@ class WinGeneratorBase(GeneratorBase): if self.serf_lib and dep.external_lib == '$(SVN_SERF_LIBS)': nondeplibs.append(serflib) + if (self.serf_ver_maj, self.serf_ver_min) >= (1, 3): + nondeplibs.append('ssleay32.lib') + nondeplibs.append('libeay32.lib') if dep.external_lib == '$(SVN_SASL_LIBS)': nondeplibs.append(sasllib) @@ -1487,6 +1515,21 @@ class WinGeneratorBase(GeneratorBase): "Check if serf and its dependencies are available" minimal_serf_version = (0, 3, 0) + + if self.openssl_path and os.path.exists(self.openssl_path): + version_path = os.path.join(self.openssl_path, 'inc32/openssl/opensslv.h') + if os.path.isfile(version_path): + # We have an OpenSSL Source location (legacy handling) + self.openssl_inc_dir = os.path.join(self.openssl_path, 'inc32') + self.openssl_lib_dir = os.path.join(self.openssl_path, 'out32dll') + elif os.path.isfile(os.path.join(self.openssl_path, + 'include/openssl/opensslv.h')): + self.openssl_inc_dir = os.path.join(self.openssl_path, 'include') + self.openssl_lib_dir = os.path.join(self.openssl_path, 'lib') + else: + print('WARNING: \'opensslv.h\' not found') + self.openssl_path = None + self.serf_lib = None if self.serf_path and os.path.exists(self.serf_path): if self.openssl_path and os.path.exists(self.openssl_path):
Modified: subversion/branches/1.7.x-issue4340-repos/build/generator/templates/build_zlib.ezt URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/build/generator/templates/build_zlib.ezt?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/build/generator/templates/build_zlib.ezt (original) +++ subversion/branches/1.7.x-issue4340-repos/build/generator/templates/build_zlib.ezt Sun Dec 14 11:44:03 2014 @@ -44,25 +44,6 @@ if /i "%2" == "x64" goto PX64 goto pIIerr :checkrebuild - -[if-any use_ml] -@rem ************************************************************************** -@rem Compile ASM sources with ML -set ASFLAGS=-nologo -Zi -coff -set LOC=-DASMV -DASMINF -[is zlib_version "1.2.4"] -set OBJA=contrib\masmx86\gvmat32c.obj contrib\masmx86\gvmat32.obj contrib\masmx86\inffas32.obj -set ASM_OPTS=ASFLAGS="%ASFLAGS%" LOC="%LOC%" OBJA="%OBJA%" -[else] -if /i "%2" == "Win32" ( - set ASM_OPTS=LOC="-DASMV -DASMINF" OBJA="inffas32.obj match686.obj" -) else if /i "%2" == "x64" ( - set ASM_OPTS=LOC="-DASMV -DASMINF" OBJA="inffasx64.obj gvmat64.obj inffas8664.obj" AS=ml64 -) -[end] -[end] - - if /i "%3" == "rebuild" goto rebuild if /i "%3" == "clean" goto clean if not "%3" == "" goto pIIIerr Modified: subversion/branches/1.7.x-issue4340-repos/build/generator/templates/vcnet_vcproj.ezt URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/build/generator/templates/vcnet_vcproj.ezt?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/build/generator/templates/vcnet_vcproj.ezt (original) +++ subversion/branches/1.7.x-issue4340-repos/build/generator/templates/vcnet_vcproj.ezt Sun Dec 14 11:44:03 2014 @@ -32,7 +32,7 @@ Name="[configs.name]|[platforms]" OutputDirectory="..\..\..\[configs.name]\[target.output_dir]" BuildLogFile="$(IntDir)\BuildLog_$(ProjectName).htm" - IntermediateDirectory="..\..\..\[configs.name]\[target.intermediate_dir]" + IntermediateDirectory="..\..\..\[configs.name]\[target.intermediate_dir]\[target.proj_name]" ConfigurationType="[target_type]"[is configs.name "Release"] WholeProgramOptimization="FALSE"[end]> <Tool Modified: subversion/branches/1.7.x-issue4340-repos/configure.ac URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/configure.ac?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/configure.ac (original) +++ subversion/branches/1.7.x-issue4340-repos/configure.ac Sun Dec 14 11:44:03 2014 @@ -118,7 +118,7 @@ AC_PATH_PROG(PKG_CONFIG, pkg-config) # Either a space-separated list of allowable Neon versions, or "any" to # mean allow anything. -NEON_ALLOWED_LIST="0\.25 0\.26 0\.27\.2 0\.28 0\.29" +NEON_ALLOWED_LIST="0\.25 0\.26 0\.27\.2 0\.28 0\.29 0\.30" NEON_RECOMMENDED_VER="0.29.6" NEON_URL="http://www.webdav.org/neon/neon-${NEON_RECOMMENDED_VER}.tar.gz" dnl You can skip the neon version check only if you know what you are doing @@ -721,7 +721,7 @@ AC_ARG_WITH(libmagic,AS_HELP_STRING([--w CPPFLAGS="$CPPFLAGS -I$libmagic_prefix/include" AC_CHECK_HEADERS(magic.h,[ save_ldflags="$LDFLAGS" - LDFLAGS="-L$libmagic_prefix/lib" + LDFLAGS="-L$libmagic_prefix/lib $LDFLAGS" AC_CHECK_LIB(magic, magic_open, [libmagic_found="yes"]) LDFLAGS="$save_ldflags" ]) Modified: subversion/branches/1.7.x-issue4340-repos/contrib/hook-scripts/check-mime-type.pl URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/contrib/hook-scripts/check-mime-type.pl?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/contrib/hook-scripts/check-mime-type.pl (original) +++ subversion/branches/1.7.x-issue4340-repos/contrib/hook-scripts/check-mime-type.pl Sun Dec 14 11:44:03 2014 @@ -120,7 +120,7 @@ foreach my $path ( @files_added ) # Parse the complete list of property values of the file $path to extract # the mime-type and eol-style foreach my $prop (&read_from_process($svnlook, 'proplist', $repos, '-t', - $txn, '--verbose', $path)) + $txn, '--verbose', '--', $path)) { if ($prop =~ /^\s*svn:mime-type : (\S+)/) { @@ -187,7 +187,7 @@ sub safe_read_from_pipe croak "$0: safe_read_from_pipe passed no arguments.\n"; } print "Running @_\n"; - my $pid = open(SAFE_READ, '-|'); + my $pid = open(SAFE_READ, '-|', @_); unless (defined $pid) { die "$0: cannot fork: $!\n"; Modified: subversion/branches/1.7.x-issue4340-repos/contrib/hook-scripts/svn-keyword-check.pl URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/contrib/hook-scripts/svn-keyword-check.pl?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/contrib/hook-scripts/svn-keyword-check.pl (original) +++ subversion/branches/1.7.x-issue4340-repos/contrib/hook-scripts/svn-keyword-check.pl Sun Dec 14 11:44:03 2014 @@ -141,7 +141,7 @@ sub check { return 1; } else { my @keywords = get_svnkeywords($file); - my $fh = _pipe("$svnlook cat $flag $value $repos $file"); + my $fh = _pipe($svnlook, qw/cat/, $flag, $value, $repos, '--', $file); while (my $line = <$fh>) { foreach my $keyword (@keywords) { if ($line =~ m/$keyword/) { @@ -168,7 +168,7 @@ sub file_is_binary { return 0; } if (has_svn_property($file, "svn:mime-type")) { - my ($mimetype) = read_from_process("$svnlook propget $flag $value $repos svn:mime-type $file"); + my ($mimetype) = read_from_process($svnlook, qw/propget/, $flag, $value, $repos, 'svn:mime-type', '--', $file); chomp($mimetype); $mimetype =~ s/^\s*(.*)/$1/; if ($mimetype =~ m/^text\//) { @@ -186,7 +186,7 @@ sub file_is_binary { # Return a list of svn:keywords on a file sub get_svnkeywords { my $file = shift; - my @lines = read_from_process("$svnlook propget $flag $value $repos svn:keywords $file"); + my @lines = read_from_process($svnlook, qw/propget/, $flag, $value, $repos, 'svn:keywords', '--', $file); my @returnlines; foreach my $line (@lines) { $line =~ s/\s+/ /; @@ -199,7 +199,7 @@ sub get_svnkeywords { sub has_svn_property { my $file = shift; my $keyword = shift; - my @proplist = read_from_process("$svnlook proplist $flag $value $repos $file"); + my @proplist = read_from_process($svnlook, qw/proplist/, $flag, $value, $repos, '--', $file); foreach my $prop (@proplist) { chomp($prop); if ($prop =~ m/\b$keyword\b/) { @@ -241,7 +241,7 @@ sub safe_read_from_pipe { # Return the filehandle as a glob so we can loop over it elsewhere. sub _pipe { local *SAFE_READ; - my $pid = open(SAFE_READ, '-|'); + my $pid = open(SAFE_READ, '-|', @_); unless (defined $pid) { die "$0: cannot fork: $!\n"; } Modified: subversion/branches/1.7.x-issue4340-repos/get-deps.sh URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/get-deps.sh?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/get-deps.sh (original) +++ subversion/branches/1.7.x-issue4340-repos/get-deps.sh Sun Dec 14 11:44:03 2014 @@ -27,12 +27,13 @@ APR=apr-1.4.5 APR_UTIL=apr-util-1.3.12 NEON=neon-0.29.6 SERF=serf-0.7.2 -ZLIB=zlib-1.2.7 +ZLIB=zlib-1.2.8 SQLITE_VERSION=3.7.6.3 -SQLITE=sqlite-amalgamation-$(printf %d%02d%02d%02d $(echo $SQLITE_VERSION | sed -e 's/\./ /g')) +SQLITE_VERSION_LIST=`echo $SQLITE_VERSION | sed -e 's/\./ /g'` +SQLITE=sqlite-amalgamation-`printf %d%02d%02d%02d $SQLITE_VERSION_LIST` -HTTPD=httpd-2.2.19 +HTTPD=httpd-2.2.25 APR_ICONV=apr-iconv-1.2.1 BASEDIR=`pwd` @@ -90,10 +91,10 @@ get_serf() { get_zlib() { cd $TEMPDIR - $HTTP_FETCH http://www.zlib.net/$ZLIB.tar.bz2 + $HTTP_FETCH http://www.zlib.net/$ZLIB.tar.gz cd $BASEDIR - bzip2 -dc $TEMPDIR/$ZLIB.tar.bz2 | tar -xf - + gzip -dc $TEMPDIR/$ZLIB.tar.gz | tar -xf - mv $ZLIB zlib } @@ -120,8 +121,12 @@ get_deps() { done if [ $# -gt 0 ]; then - for target; do - get_$target || usage + for target in "$@"; do + if [ "$target" != "deps" ]; then + get_$target || usage + else + usage + fi done else get_apr Modified: subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/ClientContext.cpp URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/ClientContext.cpp?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/ClientContext.cpp (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/ClientContext.cpp Sun Dec 14 11:44:03 2014 @@ -422,6 +422,7 @@ ClientContext::resolve(svn_wc_conflict_r { jobject jctx = (jobject) baton; JNIEnv *env = JNIUtil::getEnv(); + *result = NULL; // Create a local frame for our references env->PushLocalFrame(LOCAL_FRAME_SIZE); Modified: subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/JNIUtil.cpp URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/JNIUtil.cpp?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/JNIUtil.cpp (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/JNIUtil.cpp Sun Dec 14 11:44:03 2014 @@ -376,7 +376,7 @@ JNIUtil::putErrorsInTrace(svn_error_t *e return; char *tmp_path; - char *path = svn_relpath_dirname(err->file, err->pool); + char *path = svn_dirent_dirname(err->file, err->pool); while (tmp_path = strchr(path, '/')) *tmp_path = '.'; @@ -384,7 +384,7 @@ JNIUtil::putErrorsInTrace(svn_error_t *e if (isJavaExceptionThrown()) return; - jstring jfileName = makeJString(svn_relpath_basename(err->file, err->pool)); + jstring jfileName = makeJString(svn_dirent_basename(err->file, err->pool)); if (isJavaExceptionThrown()) return; @@ -399,7 +399,7 @@ JNIUtil::putErrorsInTrace(svn_error_t *e env->DeleteLocalRef(jfileName); } -void JNIUtil::handleSVNError(svn_error_t *err) +void JNIUtil::wrappedHandleSVNError(svn_error_t *err) { std::string msg; assembleErrorMessage(svn_error_purge_tracing(err), 0, APR_SUCCESS, msg); @@ -521,7 +521,16 @@ void JNIUtil::handleSVNError(svn_error_t #endif env->Throw(static_cast<jthrowable>(env->PopLocalFrame(nativeException))); +} +void JNIUtil::handleSVNError(svn_error_t *err) +{ + try { + wrappedHandleSVNError(err); + } catch (...) { + svn_error_clear(err); + throw; + } svn_error_clear(err); } @@ -621,28 +630,42 @@ bool JNIUtil::isJavaExceptionThrown() const char * JNIUtil::thrownExceptionToCString(SVN::Pool &in_pool) { - const char *msg; + const char *msg = NULL; JNIEnv *env = getEnv(); + apr_pool_t *pool = in_pool.getPool(); if (env->ExceptionCheck()) { jthrowable t = env->ExceptionOccurred(); - static jmethodID getMessage = 0; - if (getMessage == 0) + jclass cls = env->GetObjectClass(t); + + jstring jclass_name; + { + jmethodID mid = env->GetMethodID(cls, "getClass", "()Ljava/lang/Class;"); + jobject clsobj = env->CallObjectMethod(t, mid); + jclass basecls = env->GetObjectClass(clsobj); + mid = env->GetMethodID(basecls, "getName", "()Ljava/lang/String;"); + jclass_name = (jstring) env->CallObjectMethod(clsobj, mid); + } + + jstring jmessage; + { + jmethodID mid = env->GetMethodID(cls, "getMessage", + "()Ljava/lang/String;"); + jmessage = (jstring) env->CallObjectMethod(t, mid); + } + + JNIStringHolder class_name(jclass_name); + if (jmessage) { - jclass clazz = env->FindClass("java/lang/Throwable"); - getMessage = env->GetMethodID(clazz, "getMessage", - "(V)Ljava/lang/String;"); - env->DeleteLocalRef(clazz); + JNIStringHolder message(jmessage); + msg = apr_pstrcat(pool, + static_cast<const char*>(class_name), ": ", + static_cast<const char*>(message), NULL); } - jstring jmsg = (jstring) env->CallObjectMethod(t, getMessage); - JNIStringHolder tmp(jmsg); - msg = tmp.pstrdup(in_pool.getPool()); + else + msg = class_name.pstrdup(pool); // ### Conditionally add t.printStackTrace() to msg? } - else - { - msg = NULL; - } return msg; } Modified: subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/JNIUtil.h URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/JNIUtil.h?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/JNIUtil.h (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/JNIUtil.h Sun Dec 14 11:44:03 2014 @@ -138,6 +138,7 @@ class JNIUtil enum { noLog, errorLog, exceptionLog, entryLog } LogLevel; private: + static void wrappedHandleSVNError(svn_error_t *err); static void assembleErrorMessage(svn_error_t *err, int depth, apr_status_t parent_apr_err, std::string &buffer); Modified: subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/SVNClient.cpp URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/SVNClient.cpp?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/SVNClient.cpp (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/native/SVNClient.cpp Sun Dec 14 11:44:03 2014 @@ -1138,7 +1138,7 @@ void SVNClient::streamFileContent(const return; SVN_JNI_ERR(svn_client_cat2(outputStream.getStream(subPool), - path, pegRevision.revision(), + intPath.c_str(), pegRevision.revision(), revision.revision(), ctx, subPool.getPool()), ); } Modified: subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/LogDate.java URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/LogDate.java?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/LogDate.java (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/LogDate.java Sun Dec 14 11:44:03 2014 @@ -51,7 +51,11 @@ public class LogDate implements java.io. { throw new ParseException("String is not a valid Subversion date", 0); } - Date date = formatter.parse(datestr.substring(0, 23) + " UTC"); + Date date; + synchronized(formatter) + { + date = formatter.parse(datestr.substring(0, 23) + " UTC"); + } this.cachedString = datestr; cachedDate = Calendar.getInstance(UTC); cachedDate.setTime(date); Modified: subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/src/org/tigris/subversion/javahl/LogDate.java URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/src/org/tigris/subversion/javahl/LogDate.java?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/src/org/tigris/subversion/javahl/LogDate.java (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/bindings/javahl/src/org/tigris/subversion/javahl/LogDate.java Sun Dec 14 11:44:03 2014 @@ -53,7 +53,11 @@ public class LogDate implements java.io. { throw new ParseException("String is not a valid Subversion date", 0); } - Date date = formatter.parse(datestr.substring(0, 23) + " UTC"); + Date date; + synchronized(formatter) + { + date = formatter.parse(datestr.substring(0, 23) + " UTC"); + } this.cachedString = datestr; cachedDate = Calendar.getInstance(UTC); cachedDate.setTime(date); Modified: subversion/branches/1.7.x-issue4340-repos/subversion/bindings/swig/include/svn_types.swg URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/bindings/swig/include/svn_types.swg?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/bindings/swig/include/svn_types.swg (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/bindings/swig/include/svn_types.swg Sun Dec 14 11:44:03 2014 @@ -901,6 +901,12 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE se $2 = (void *)baton; } #endif +#ifdef SWIGPERL +%ignore svn_fs_set_warning_func; +#endif +#ifdef SWIGPYTHON +%ignore svn_fs_set_warning_func; +#endif /* ----------------------------------------------------------------------- svn_stream_t interoperability with language native io handles Modified: subversion/branches/1.7.x-issue4340-repos/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/bindings/swig/perl/libsvn_swig_perl/swigutil_pl.c Sun Dec 14 11:44:03 2014 @@ -114,9 +114,9 @@ static apr_hash_t *svn_swig_pl_to_hash(S h = (HV *)SvRV(source); cnt = hv_iterinit(h); while (cnt--) { - SV* item = hv_iternextsv(h, &key, &retlen); - void *val = cv(item, ctx, pool); - apr_hash_set(hash, key, APR_HASH_KEY_STRING, val); + SV* item = hv_iternextsv(h, &key, &retlen); + void *val = cv(item, ctx, pool); + apr_hash_set(hash, apr_pstrmemdup(pool, key, retlen), retlen, val); } return hash; Propchange: subversion/branches/1.7.x-issue4340-repos/subversion/include/private/svn_adler32.h ------------------------------------------------------------------------------ --- svn:mergeinfo (original) +++ svn:mergeinfo Sun Dec 14 11:44:03 2014 @@ -1,5 +1,7 @@ /subversion/branches/1.5.x-r30215/subversion/libsvn_diff/diff.h:870312 +/subversion/branches/1.7.x/subversion/include/private/svn_adler32.h:1480943-1645438 /subversion/branches/1.7.x-issue4087/subversion/include/private/svn_adler32.h:1243707-1336073 +/subversion/branches/1.7.x-issue4153/subversion/include/private/svn_adler32.h:1309894-1539233 /subversion/branches/1.7.x-issue4169/subversion/include/private/svn_adler32.h:1330537-1336116 /subversion/branches/1.7.x-r1210147/subversion/include/private/svn_adler32.h:1213310-1293110 /subversion/branches/1.7.x-r1423646/subversion/include/private/svn_adler32.h:1423647-1424282 Propchange: subversion/branches/1.7.x-issue4340-repos/subversion/include/private/svn_string_private.h ------------------------------------------------------------------------------ --- svn:mergeinfo (original) +++ svn:mergeinfo Sun Dec 14 11:44:03 2014 @@ -1,5 +1,7 @@ /subversion/branches/1.5.x-r30215/subversion/include/svn_string.h:870312 +/subversion/branches/1.7.x/subversion/include/private/svn_string_private.h:1480943-1645438 /subversion/branches/1.7.x-issue4087/subversion/include/private/svn_string_private.h:1243707-1336073 +/subversion/branches/1.7.x-issue4153/subversion/include/private/svn_string_private.h:1309894-1539233 /subversion/branches/1.7.x-issue4169/subversion/include/private/svn_string_private.h:1330537-1336116 /subversion/branches/1.7.x-r1210147/subversion/include/private/svn_string_private.h:1213310-1293110 /subversion/branches/1.7.x-r1423646/subversion/include/private/svn_string_private.h:1423647-1424282 Propchange: subversion/branches/1.7.x-issue4340-repos/subversion/include/private/svn_temp_serializer.h ------------------------------------------------------------------------------ --- svn:mergeinfo (original) +++ svn:mergeinfo Sun Dec 14 11:44:03 2014 @@ -1,4 +1,6 @@ +/subversion/branches/1.7.x/subversion/include/private/svn_temp_serializer.h:1480943-1645438 /subversion/branches/1.7.x-issue4087/subversion/include/private/svn_temp_serializer.h:1243707-1336073 +/subversion/branches/1.7.x-issue4153/subversion/include/private/svn_temp_serializer.h:1309894-1539233 /subversion/branches/1.7.x-issue4169/subversion/include/private/svn_temp_serializer.h:1330537-1336116 /subversion/branches/1.7.x-r1210147/subversion/include/private/svn_temp_serializer.h:1213310-1293110 /subversion/branches/1.7.x-r1423646/subversion/include/private/svn_temp_serializer.h:1423647-1424282 Modified: subversion/branches/1.7.x-issue4340-repos/subversion/include/svn_io.h URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/include/svn_io.h?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/include/svn_io.h (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/include/svn_io.h Sun Dec 14 11:44:03 2014 @@ -1064,6 +1064,8 @@ svn_stream_read(svn_stream_t *stream, * of reads or a simple seek operation. If the stream implementation has * not provided a skip function, this will read from the stream and * discard the data. + * + * @since New in 1.7. */ svn_error_t * svn_stream_skip(svn_stream_t *stream, Modified: subversion/branches/1.7.x-issue4340-repos/subversion/include/svn_types.h URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/include/svn_types.h?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/include/svn_types.h (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/include/svn_types.h Sun Dec 14 11:44:03 2014 @@ -186,6 +186,16 @@ svn__apr_hash_index_val(const apr_hash_i || ((s) == APR_OS_START_SYSERR + ERROR_INVALID_NAME)) #endif +/** On Windows, APR_STATUS_IS_EPIPE does not include ERROR_NO_DATA error. + * So we include it.*/ +/* ### These fixes should go into APR. */ +#ifndef WIN32 +#define SVN__APR_STATUS_IS_EPIPE(s) APR_STATUS_IS_EPIPE(s) +#else +#define SVN__APR_STATUS_IS_EPIPE(s) (APR_STATUS_IS_EPIPE(s) \ + || ((s) == APR_OS_START_SYSERR + ERROR_NO_DATA)) +#endif + /** @} */ /** The various types of nodes in the Subversion filesystem. */ Modified: subversion/branches/1.7.x-issue4340-repos/subversion/include/svn_version.h URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/include/svn_version.h?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/include/svn_version.h (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/include/svn_version.h Sun Dec 14 11:44:03 2014 @@ -71,7 +71,7 @@ extern "C" { * * @since New in 1.1. */ -#define SVN_VER_PATCH 10 +#define SVN_VER_PATCH 20 /** @deprecated Provided for backward compatibility with the 1.0 API. */ Modified: subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_auth_gnome_keyring/gnome_keyring.c Sun Dec 14 11:44:03 2014 @@ -28,6 +28,7 @@ /*** Includes. ***/ #include <apr_pools.h> +#include <apr_strings.h> #include "svn_auth.h" #include "svn_config.h" #include "svn_error.h" @@ -46,120 +47,19 @@ /*-----------------------------------------------------------------------*/ -struct gnome_keyring_baton -{ - const char *keyring_name; - GnomeKeyringInfo *info; - GMainLoop *loop; -}; - - -/* Callback function to destroy gnome_keyring_baton. */ -static void -callback_destroy_data_keyring(void *data) -{ - struct gnome_keyring_baton *key_info = data; - - if (data == NULL) - return; - - free((void*)key_info->keyring_name); - key_info->keyring_name = NULL; - - if (key_info->info) - { - gnome_keyring_info_free(key_info->info); - key_info->info = NULL; - } - - return; -} - - -/* Callback function to complete the keyring operation. */ -static void -callback_done(GnomeKeyringResult result, - gpointer data) -{ - struct gnome_keyring_baton *key_info = data; - - g_main_loop_quit(key_info->loop); - return; -} - - -/* Callback function to get the keyring info. */ -static void -callback_get_info_keyring(GnomeKeyringResult result, - GnomeKeyringInfo *info, - void *data) -{ - struct gnome_keyring_baton *key_info = data; - - if (result == GNOME_KEYRING_RESULT_OK && info != NULL) - { - key_info->info = gnome_keyring_info_copy(info); - } - else - { - if (key_info->info != NULL) - gnome_keyring_info_free(key_info->info); - - key_info->info = NULL; - } - - g_main_loop_quit(key_info->loop); - - return; -} - - -/* Callback function to get the default keyring string name. */ -static void -callback_default_keyring(GnomeKeyringResult result, - const char *string, - void *data) -{ - struct gnome_keyring_baton *key_info = data; - - if (result == GNOME_KEYRING_RESULT_OK && string != NULL) - { - key_info->keyring_name = strdup(string); - } - else - { - free((void*)key_info->keyring_name); - key_info->keyring_name = NULL; - } - - g_main_loop_quit(key_info->loop); - - return; -} - -/* Returns the default keyring name. */ +/* Returns the default keyring name, allocated in RESULT_POOL. */ static char* -get_default_keyring_name(apr_pool_t *pool) +get_default_keyring_name(apr_pool_t *result_pool) { - char *def = NULL; - struct gnome_keyring_baton key_info; - - key_info.info = NULL; - key_info.keyring_name = NULL; + char *name, *def; + GnomeKeyringResult gkr; - /* Finds default keyring. */ - key_info.loop = g_main_loop_new(NULL, FALSE); - gnome_keyring_get_default_keyring(callback_default_keyring, &key_info, NULL); - g_main_loop_run(key_info.loop); - - if (key_info.keyring_name == NULL) - { - callback_destroy_data_keyring(&key_info); - return NULL; - } + gkr = gnome_keyring_get_default_keyring_sync(&name); + if (gkr != GNOME_KEYRING_RESULT_OK) + return NULL; - def = strdup(key_info.keyring_name); - callback_destroy_data_keyring(&key_info); + def = apr_pstrdup(result_pool, name); + g_free(name); return def; } @@ -168,28 +68,22 @@ get_default_keyring_name(apr_pool_t *poo static svn_boolean_t check_keyring_is_locked(const char *keyring_name) { - struct gnome_keyring_baton key_info; + GnomeKeyringInfo *info; + svn_boolean_t locked; + GnomeKeyringResult gkr; - key_info.info = NULL; - key_info.keyring_name = NULL; + gkr = gnome_keyring_get_info_sync(keyring_name, &info); + if (gkr != GNOME_KEYRING_RESULT_OK) + return FALSE; - /* Get details about the default keyring. */ - key_info.loop = g_main_loop_new(NULL, FALSE); - gnome_keyring_get_info(keyring_name, callback_get_info_keyring, &key_info, - NULL); - g_main_loop_run(key_info.loop); - - if (key_info.info == NULL) - { - callback_destroy_data_keyring(&key_info); - return FALSE; - } - - /* Check if keyring is locked. */ - if (gnome_keyring_info_get_is_locked(key_info.info)) - return TRUE; + if (gnome_keyring_info_get_is_locked(info)) + locked = TRUE; else - return FALSE; + locked = FALSE; + + gnome_keyring_info_free(info); + + return locked; } /* Unlock the KEYRING_NAME with the KEYRING_PASSWORD. If KEYRING was @@ -199,34 +93,19 @@ unlock_gnome_keyring(const char *keyring const char *keyring_password, apr_pool_t *pool) { - struct gnome_keyring_baton key_info; + GnomeKeyringInfo *info; + GnomeKeyringResult gkr; - key_info.info = NULL; - key_info.keyring_name = NULL; + gkr = gnome_keyring_get_info_sync(keyring_name, &info); + if (gkr != GNOME_KEYRING_RESULT_OK) + return FALSE; - /* Get details about the default keyring. */ - key_info.loop = g_main_loop_new(NULL, FALSE); - gnome_keyring_get_info(keyring_name, callback_get_info_keyring, - &key_info, NULL); - g_main_loop_run(key_info.loop); - - if (key_info.info == NULL) - { - callback_destroy_data_keyring(&key_info); - return FALSE; - } - else - { - key_info.loop = g_main_loop_new(NULL, FALSE); - gnome_keyring_unlock(keyring_name, keyring_password, - callback_done, &key_info, NULL); - g_main_loop_run(key_info.loop); - } - callback_destroy_data_keyring(&key_info); - if (check_keyring_is_locked(keyring_name)) + gkr = gnome_keyring_unlock_sync(keyring_name, keyring_password); + gnome_keyring_info_free(info); + if (gkr != GNOME_KEYRING_RESULT_OK) return FALSE; - return TRUE; + return check_keyring_is_locked(keyring_name); } Modified: subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/blame.c URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/blame.c?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/blame.c (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/blame.c Sun Dec 14 11:44:03 2014 @@ -675,28 +675,41 @@ svn_client_blame5(const char *target, SVN_ERR(svn_wc_status3(&status, ctx->wc_ctx, target_abspath_or_url, pool, pool)); - if (status->text_status != svn_wc_status_normal) + if (status->text_status != svn_wc_status_normal + || (status->prop_status != svn_wc_status_normal + && status->prop_status != svn_wc_status_none)) { - apr_hash_t *props; svn_stream_t *wcfile; - svn_string_t *keywords; svn_stream_t *tempfile; + svn_opt_revision_t rev; + svn_boolean_t normalize_eols = FALSE; const char *temppath; - apr_hash_t *kw = NULL; - SVN_ERR(svn_wc_prop_list2(&props, ctx->wc_ctx, target_abspath_or_url, - pool, pool)); - SVN_ERR(svn_stream_open_readonly(&wcfile, target, pool, pool)); - - keywords = apr_hash_get(props, SVN_PROP_KEYWORDS, - APR_HASH_KEY_STRING); - - if (keywords) - SVN_ERR(svn_subst_build_keywords2(&kw, keywords->data, NULL, NULL, - 0, NULL, pool)); - - wcfile = svn_subst_stream_translated(wcfile, "\n", TRUE, kw, FALSE, - pool); + if (status->prop_status != svn_wc_status_none) + { + const svn_string_t *eol_style; + SVN_ERR(svn_wc_prop_get2(&eol_style, ctx->wc_ctx, + target_abspath_or_url, + SVN_PROP_EOL_STYLE, + pool, pool)); + + if (eol_style) + { + svn_subst_eol_style_t style; + const char *eol; + svn_subst_eol_style_from_value(&style, &eol, eol_style->data); + + normalize_eols = (style == svn_subst_eol_style_native); + } + } + + rev.kind = svn_opt_revision_working; + SVN_ERR(svn_client__get_normalized_stream(&wcfile, ctx->wc_ctx, + target_abspath_or_url, &rev, + FALSE, normalize_eols, + ctx->cancel_func, + ctx->cancel_baton, + pool, pool)); SVN_ERR(svn_stream_open_unique(&tempfile, &temppath, NULL, svn_io_file_del_on_pool_cleanup, Modified: subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/client.h URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/client.h?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/client.h (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/client.h Sun Dec 14 11:44:03 2014 @@ -609,6 +609,24 @@ svn_client__get_diff_summarize_editor(co void **edit_baton, apr_pool_t *pool); +/* Set *CALLBACKS and *CALLBACK_BATON to a set of diff callbacks that will + report a diff summary, i.e. only providing information about the changed + items without the text deltas. + + TARGET is the target path, relative to the anchor, of the diff. + + SUMMARIZE_FUNC is called with SUMMARIZE_BATON as parameter by the + created callbacks for each changed item. +*/ +svn_error_t * +svn_client__get_diff_summarize_callbacks( + svn_wc_diff_callbacks4_t **callbacks, + void **callback_baton, + const char *target, + svn_client_diff_summarize_func_t summarize_func, + void *summarize_baton, + apr_pool_t *pool); + /* ---------------------------------------------------------------- */ /*** Copy Stuff ***/ @@ -929,7 +947,11 @@ svn_client__export_externals(apr_hash_t /* Perform status operations on each external in EXTERNAL_MAP, a const char * local_abspath of all externals mapping to the const char* defining_abspath. - All other options are the same as those passed to svn_client_status(). */ + All other options are the same as those passed to svn_client_status(). + + If ANCHOR_ABSPATH and ANCHOR-RELPATH are not null, use them to provide + properly formatted relative paths + */ svn_error_t * svn_client__do_external_status(svn_client_ctx_t *ctx, apr_hash_t *external_map, @@ -937,9 +959,11 @@ svn_client__do_external_status(svn_clien svn_boolean_t get_all, svn_boolean_t update, svn_boolean_t no_ignore, + const char *anchor_abspath, + const char *anchor_relpath, svn_client_status_func_t status_func, void *status_baton, - apr_pool_t *pool); + apr_pool_t *scratch_pool); /* Baton type for svn_wc__external_info_gatherer(). */ typedef struct svn_client__external_func_baton_t Modified: subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/copy.c URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/copy.c?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/copy.c (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/copy.c Sun Dec 14 11:44:03 2014 @@ -374,6 +374,8 @@ do_wc_to_wc_moves(const apr_array_header { const char *src_parent_abspath; struct do_wc_to_wc_moves_with_locks_baton baton; + const char *src_wcroot_abspath; + const char *dst_wcroot_abspath; svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i, svn_client__copy_pair_t *); @@ -386,6 +388,13 @@ do_wc_to_wc_moves(const apr_array_header src_parent_abspath = svn_dirent_dirname(pair->src_abspath_or_url, iterpool); + SVN_ERR(svn_wc__get_wc_root(&src_wcroot_abspath, + ctx->wc_ctx, src_parent_abspath, + iterpool, iterpool)); + SVN_ERR(svn_wc__get_wc_root(&dst_wcroot_abspath, + ctx->wc_ctx, pair->dst_parent_abspath, + iterpool, iterpool)); + /* We now need to lock the right combination of batons. Four cases: 1) src_parent == dst_parent @@ -394,14 +403,18 @@ do_wc_to_wc_moves(const apr_array_header 4) src_parent and dst_parent are disjoint We can handle 1) as either 2) or 3) */ if (strcmp(src_parent_abspath, pair->dst_parent_abspath) == 0 - || svn_dirent_is_child(src_parent_abspath, pair->dst_parent_abspath, - iterpool)) + || (svn_dirent_is_child(src_parent_abspath, pair->dst_parent_abspath, + NULL) + && !svn_dirent_is_child(src_parent_abspath, dst_wcroot_abspath, + NULL))) { baton.lock_src = TRUE; baton.lock_dst = FALSE; } - else if (svn_dirent_is_child(pair->dst_parent_abspath, src_parent_abspath, - iterpool)) + else if (svn_dirent_is_child(pair->dst_parent_abspath, + src_parent_abspath, NULL) + && !svn_dirent_is_child(pair->dst_parent_abspath, + src_wcroot_abspath, NULL)) { baton.lock_src = FALSE; baton.lock_dst = TRUE; Modified: subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/delete.c URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/delete.c?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/delete.c (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/delete.c Sun Dec 14 11:44:03 2014 @@ -143,7 +143,7 @@ path_driver_cb_func(void **dir_baton, static svn_error_t * single_repos_delete(svn_ra_session_t *ra_session, - const char *repos_root, + const char *base_uri, const apr_array_header_t *relpaths, const apr_hash_t *revprop_table, svn_commit_callback2_t commit_callback, @@ -171,7 +171,7 @@ single_repos_delete(svn_ra_session_t *ra const char *relpath = APR_ARRAY_IDX(relpaths, i, const char *); item = svn_client_commit_item3_create(pool); - item->url = svn_path_url_add_component2(repos_root, relpath, pool); + item->url = svn_path_url_add_component2(base_uri, relpath, pool); item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE; APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; } @@ -311,7 +311,6 @@ delete_urls_multi_repos(const apr_array_ iterpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, deletables); hi; hi = apr_hash_next(hi)) { - const char *repos_root = svn__apr_hash_index_key(hi); struct repos_deletables_t *repos_deletables = svn__apr_hash_index_val(hi); const char *base_uri; apr_array_header_t *target_relpaths; @@ -348,7 +347,7 @@ delete_urls_multi_repos(const apr_array_ } SVN_ERR(svn_ra_reparent(repos_deletables->ra_session, base_uri, pool)); - SVN_ERR(single_repos_delete(repos_deletables->ra_session, repos_root, + SVN_ERR(single_repos_delete(repos_deletables->ra_session, base_uri, target_relpaths, revprop_table, commit_callback, commit_baton, ctx, iterpool)); Modified: subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/diff.c URL: http://svn.apache.org/viewvc/subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/diff.c?rev=1645439&r1=1645438&r2=1645439&view=diff ============================================================================== --- subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/diff.c (original) +++ subversion/branches/1.7.x-issue4340-repos/subversion/libsvn_client/diff.c Sun Dec 14 11:44:03 2014 @@ -1494,11 +1494,55 @@ check_diff_target_exists(const char *url return SVN_NO_ERROR; } +/* Resolve PATH_OR_URL@PEG_REVISION to a possibly different *RESOLVED_URL + * which the corresponding object has in REVISION. If the object has no + * location in REVISION, set *RESOLVED_URL to NULL. */ +static svn_error_t * +resolve_pegged_diff_target_url(const char **resolved_url, + svn_ra_session_t *ra_session, + const char *path_or_url, + const svn_opt_revision_t *peg_revision, + const svn_opt_revision_t *revision, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + svn_opt_revision_t *start_rev_ignore, *end_rev_ignore; + const char *end_url_ignore; + static const svn_opt_revision_t unspecified_rev = + { svn_opt_revision_unspecified, { 0 } }; + svn_error_t *err; + + /* Check if the PATH_OR_URL exists at REVISION. */ + err = svn_client__repos_locations(resolved_url, &start_rev_ignore, + &end_url_ignore, &end_rev_ignore, + ra_session, + path_or_url, + peg_revision, + revision, + &unspecified_rev, + ctx, scratch_pool); + if (err) + { + if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES || + err->apr_err == SVN_ERR_FS_NOT_FOUND) + { + svn_error_clear(err); + *resolved_url = NULL; + } + else + return svn_error_trace(err); + } + + return SVN_NO_ERROR; +} + /** Prepare a repos repos diff between PATH1 and PATH2@PEG_REVISION, * in the revision range REVISION1:REVISION2. * Return URLs and peg revisions in *URL1, *REV1 and in *URL2, *REV2. * Return suitable anchors in *ANCHOR1 and *ANCHOR2, and targets in * *TARGET1 and *TARGET2, based on *URL1 and *URL2. + * Indicate the corresponding node kinds in *KIND1 and *KIND2, and verify + * that at least one of the diff targets exists. * Set *BASE_PATH corresponding to the URL opened in the new *RA_SESSION * which is pointing at *ANCHOR1. * Use client context CTX. Do all allocations in POOL. */ @@ -1512,6 +1556,8 @@ diff_prepare_repos_repos(const char **ur const char **anchor2, const char **target1, const char **target2, + svn_node_kind_t *kind1, + svn_node_kind_t *kind2, svn_ra_session_t **ra_session, svn_client_ctx_t *ctx, const char *path1, @@ -1521,7 +1567,6 @@ diff_prepare_repos_repos(const char **ur const svn_opt_revision_t *peg_revision, apr_pool_t *pool) { - svn_node_kind_t kind1, kind2; const char *path2_abspath; const char *path1_abspath; @@ -1560,53 +1605,56 @@ diff_prepare_repos_repos(const char **ur actual URLs will be. */ if (peg_revision->kind != svn_opt_revision_unspecified) { - svn_opt_revision_t *start_ignore, *end_ignore; - svn_error_t *err; + const char *resolved_url1; + const char *resolved_url2; - err = svn_client__repos_locations(url1, &start_ignore, - url2, &end_ignore, - *ra_session, - path2, - peg_revision, - revision1, - revision2, - ctx, pool); - if (err) - { - if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES) - { - /* Don't give up just yet. A missing path might translate - * into an addition in the diff. Below, we verify that each - * URL exists on at least one side of the diff. */ - svn_error_clear(err); - } - else - return svn_error_trace(err); + SVN_ERR(resolve_pegged_diff_target_url(&resolved_url2, *ra_session, + path2, peg_revision, + revision2, ctx, pool)); + + SVN_ERR(svn_ra_reparent(*ra_session, *url1, pool)); + SVN_ERR(resolve_pegged_diff_target_url(&resolved_url1, *ra_session, + path1, peg_revision, + revision1, ctx, pool)); + + /* Either or both URLs might have changed as a result of resolving + * the PATH_OR_URL@PEG_REVISION's history. If only one of the URLs + * could be resolved, use the same URL for URL1 and URL2, so we can + * show diff that 'adds' the object (see issue #4153). */ + if (resolved_url2) + { + *url2 = resolved_url2; + if (!resolved_url1) + *url1 = resolved_url2; } - else + if (resolved_url1) { - /* Reparent the session, since *URL2 might have changed as a result - the above call. */ - SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool)); + *url1 = resolved_url1; + if (!resolved_url2) + *url2 = resolved_url1; } + + /* Reparent the session, since *URL2 might have changed as a result + the above call. */ + SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool)); } /* Resolve revision and get path kind for the second target. */ SVN_ERR(svn_client__get_revision_number(rev2, NULL, ctx->wc_ctx, (path2 == *url2) ? NULL : path2_abspath, *ra_session, revision2, pool)); - SVN_ERR(svn_ra_check_path(*ra_session, "", *rev2, &kind2, pool)); + SVN_ERR(svn_ra_check_path(*ra_session, "", *rev2, kind2, pool)); /* Do the same for the first target. */ SVN_ERR(svn_ra_reparent(*ra_session, *url1, pool)); SVN_ERR(svn_client__get_revision_number(rev1, NULL, ctx->wc_ctx, (strcmp(path1, *url1) == 0) ? NULL : path1_abspath, *ra_session, revision1, pool)); - SVN_ERR(svn_ra_check_path(*ra_session, "", *rev1, &kind1, pool)); + SVN_ERR(svn_ra_check_path(*ra_session, "", *rev1, kind1, pool)); /* Either both URLs must exist at their respective revisions, * or one of them may be missing from one side of the diff. */ - if (kind1 == svn_node_none && kind2 == svn_node_none) + if (*kind1 == svn_node_none && *kind2 == svn_node_none) { if (strcmp(*url1, *url2) == 0) return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, @@ -1620,9 +1668,9 @@ diff_prepare_repos_repos(const char **ur "'%ld'"), *url1, *url2, *rev1, *rev2); } - else if (kind1 == svn_node_none) + else if (*kind1 == svn_node_none) SVN_ERR(check_diff_target_exists(*url1, *rev2, *rev1, *ra_session, pool)); - else if (kind2 == svn_node_none) + else if (*kind2 == svn_node_none) SVN_ERR(check_diff_target_exists(*url2, *rev1, *rev2, *ra_session, pool)); /* Choose useful anchors and targets for our two URLs. */ @@ -1630,57 +1678,9 @@ diff_prepare_repos_repos(const char **ur *anchor2 = *url2; *target1 = ""; *target2 = ""; - if ((kind1 == svn_node_none) || (kind2 == svn_node_none)) - { - svn_node_kind_t kind; - const char *repos_root; - const char *new_anchor; - svn_revnum_t rev; - - /* The diff target does not exist on one side of the diff. - * This can happen if the target was added or deleted within the - * revision range being diffed. - * However, we don't know how deep within a added/deleted subtree the - * diff target is. Find a common parent that exists on both sides of - * the diff and use it as anchor for the diff operation. - * - * ### This can fail due to authz restrictions (like in issue #3242). - * ### But it is the only option we have right now to try to get - * ### a usable diff in this situation. */ - - SVN_ERR(svn_ra_get_repos_root2(*ra_session, &repos_root, pool)); - - /* Since we already know that one of the URLs does exist, - * look for an existing parent of the URL which doesn't exist. */ - new_anchor = (kind1 == svn_node_none ? *anchor1 : *anchor2); - rev = (kind1 == svn_node_none ? *rev1 : *rev2); - do - { - if (strcmp(new_anchor, repos_root) != 0) - { - new_anchor = svn_path_uri_decode(svn_uri_dirname(new_anchor, - pool), - pool); - if (*base_path) - *base_path = svn_dirent_dirname(*base_path, pool); - } - - SVN_ERR(svn_ra_reparent(*ra_session, new_anchor, pool)); - SVN_ERR(svn_ra_check_path(*ra_session, "", rev, &kind, pool)); - - } - while (kind != svn_node_dir); - *anchor1 = *anchor2 = new_anchor; - /* Diff targets must be relative to the new anchor. */ - *target1 = svn_uri_skip_ancestor(new_anchor, *url1, pool); - *target2 = svn_uri_skip_ancestor(new_anchor, *url2, pool); - SVN_ERR_ASSERT(*target1 && *target2); - if (kind1 == svn_node_none) - kind1 = svn_node_dir; - else - kind2 = svn_node_dir; - } - else if ((kind1 == svn_node_file) || (kind2 == svn_node_file)) + + /* If one of the targets is a file, use the parent directory as anchor. */ + if (*kind1 == svn_node_file || *kind2 == svn_node_file) { svn_uri_split(anchor1, target1, *url1, pool); svn_uri_split(anchor2, target2, *url2, pool); @@ -1811,6 +1811,299 @@ diff_wc_wc(const char *path1, return SVN_NO_ERROR; } +/* Create an array of regular properties in PROP_HASH, filtering entry-props + * and wc-props. Allocate the returned array in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations. */ +static apr_array_header_t * +make_regular_props_array(apr_hash_t *prop_hash, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_array_header_t *regular_props; + apr_hash_index_t *hi; + + regular_props = apr_array_make(result_pool, 0, sizeof(svn_prop_t)); + for (hi = apr_hash_first(scratch_pool, prop_hash); hi; + hi = apr_hash_next(hi)) + { + const char *name = svn__apr_hash_index_key(hi); + svn_string_t *value = svn__apr_hash_index_val(hi); + svn_prop_kind_t prop_kind = svn_property_kind(NULL, name); + + if (prop_kind == svn_prop_regular_kind) + { + svn_prop_t *prop = apr_palloc(scratch_pool, sizeof(svn_prop_t)); + + prop->name = name; + prop->value = value; + APR_ARRAY_PUSH(regular_props, svn_prop_t) = *prop; + } + } + + return regular_props; +} + +/* Create a hash of regular properties from PROP_HASH, filtering entry-props + * and wc-props. Allocate the returned hash in RESULT_POOL. + * Use SCRATCH_POOL for temporary allocations. */ +static apr_hash_t * +make_regular_props_hash(apr_hash_t *prop_hash, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_hash_t *regular_props; + apr_hash_index_t *hi; + + regular_props = apr_hash_make(result_pool); + for (hi = apr_hash_first(scratch_pool, prop_hash); hi; + hi = apr_hash_next(hi)) + { + const char *name = svn__apr_hash_index_key(hi); + svn_string_t *value = svn__apr_hash_index_val(hi); + svn_prop_kind_t prop_kind = svn_property_kind(NULL, name); + + if (prop_kind == svn_prop_regular_kind) + apr_hash_set(regular_props, name, APR_HASH_KEY_STRING, value); + } + + return regular_props; +} + +/* Handle an added or deleted diff target file for a repos<->repos diff. + * + * Using the provided diff CALLBACKS and the CALLBACK_BATON, show the file + * TARGET@PEG_REVISION as added or deleted, depending on SHOW_DELETION. + * TARGET is a path relative to RA_SESSION's URL. + * REV1 and REV2 are the revisions being compared. + * Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +diff_repos_repos_added_or_deleted_file(const char *target, + svn_revnum_t peg_revision, + svn_revnum_t rev1, + svn_revnum_t rev2, + svn_boolean_t show_deletion, + const char *empty_file, + const svn_wc_diff_callbacks4_t + *callbacks, + struct diff_cmd_baton *callback_baton, + svn_ra_session_t *ra_session, + apr_pool_t *scratch_pool) +{ + const char *file_abspath; + svn_stream_t *content; + apr_hash_t *prop_hash; + svn_string_t *mimetype; + + SVN_ERR(svn_stream_open_unique(&content, &file_abspath, NULL, + svn_io_file_del_on_pool_cleanup, + scratch_pool, scratch_pool)); + SVN_ERR(svn_ra_get_file(ra_session, target, peg_revision, content, NULL, + &prop_hash, scratch_pool)); + SVN_ERR(svn_stream_close(content)); + + mimetype = apr_hash_get(prop_hash, SVN_PROP_MIME_TYPE, APR_HASH_KEY_STRING); + + if (show_deletion) + { + SVN_ERR(callbacks->file_deleted(NULL, NULL, + target, file_abspath, empty_file, + mimetype ? mimetype->data : NULL, + NULL, + make_regular_props_hash( + prop_hash, scratch_pool, scratch_pool), + callback_baton, scratch_pool)); + } + else + { + SVN_ERR(callbacks->file_added(NULL, NULL, NULL, + target, empty_file, file_abspath, + rev1, rev2, NULL, + mimetype ? mimetype->data : NULL, + NULL, SVN_INVALID_REVNUM, + make_regular_props_array(prop_hash, + scratch_pool, + scratch_pool), + NULL, callback_baton, scratch_pool)); + } + + return SVN_NO_ERROR; +} + +/* Handle an added or deleted diff target directory for a repos<->repos diff. + * + * Using the provided diff CALLBACKS and the CALLBACK_BATON, show the + * directory TARGET@PEG_REVISION, and all of its children, as added or deleted, + * depending on SHOW_DELETION. TARGET is a path relative to RA_SESSION's URL. + * REV1 and REV2 are the revisions being compared. + * Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +diff_repos_repos_added_or_deleted_dir(const char *target, + svn_revnum_t revision, + svn_revnum_t rev1, + svn_revnum_t rev2, + svn_boolean_t show_deletion, + const char *empty_file, + const svn_wc_diff_callbacks4_t + *callbacks, + struct diff_cmd_baton *callback_baton, + svn_ra_session_t *ra_session, + apr_pool_t *scratch_pool) +{ + apr_hash_t *dirents; + apr_hash_t *props; + apr_pool_t *iterpool; + apr_hash_index_t *hi; + + SVN_ERR(svn_ra_get_dir2(ra_session, &dirents, NULL, &props, + target, revision, SVN_DIRENT_KIND, + scratch_pool)); + + if (show_deletion) + SVN_ERR(callbacks->dir_deleted(NULL, NULL, target, callback_baton, + scratch_pool)); + else + SVN_ERR(callbacks->dir_added(NULL, NULL, NULL, NULL, + target, revision, + NULL, SVN_INVALID_REVNUM, + callback_baton, scratch_pool)); + if (props) + { + if (show_deletion) + SVN_ERR(callbacks->dir_props_changed(NULL, NULL, target, FALSE, + apr_array_make(scratch_pool, 0, + sizeof(svn_prop_t)), + make_regular_props_hash( + props, scratch_pool, + scratch_pool), + callback_baton, scratch_pool)); + else + SVN_ERR(callbacks->dir_props_changed(NULL, NULL, target, TRUE, + make_regular_props_array( + props, scratch_pool, + scratch_pool), + NULL, + callback_baton, scratch_pool)); + } + + iterpool = svn_pool_create(scratch_pool); + for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi)) + { + const char *name = svn__apr_hash_index_key(hi); + svn_dirent_t *dirent = svn__apr_hash_index_val(hi); + const char *child_target; + + svn_pool_clear(iterpool); + + child_target = svn_relpath_join(target, name, iterpool); + + if (dirent->kind == svn_node_dir) + SVN_ERR(diff_repos_repos_added_or_deleted_dir(child_target, + revision, rev1, rev2, + show_deletion, + empty_file, + callbacks, + callback_baton, + ra_session, + iterpool)); + else if (dirent->kind == svn_node_file) + SVN_ERR(diff_repos_repos_added_or_deleted_file(child_target, + revision, rev1, rev2, + show_deletion, + empty_file, + callbacks, + callback_baton, + ra_session, + iterpool)); + } + svn_pool_destroy(iterpool); + + if (!show_deletion) + SVN_ERR(callbacks->dir_closed(NULL, NULL, NULL, target, TRUE, + callback_baton, scratch_pool)); + + return SVN_NO_ERROR; +} + + +/* Handle an added or deleted diff target for a repos<->repos diff. + * + * Using the provided diff CALLBACKS and the CALLBACK_BATON, show + * TARGET@PEG_REVISION, and all of its children, if any, as added or deleted. + * TARGET is a path relative to RA_SESSION's URL. + * REV1 and REV2 are the revisions being compared. + * Use SCRATCH_POOL for temporary allocations. */ +static svn_error_t * +diff_repos_repos_added_or_deleted_target(const char *target1, + const char *target2, + svn_revnum_t rev1, + svn_revnum_t rev2, + svn_node_kind_t kind1, + svn_node_kind_t kind2, + const svn_wc_diff_callbacks4_t + *callbacks, + struct diff_cmd_baton *callback_baton, + svn_ra_session_t *ra_session, + apr_pool_t *scratch_pool) +{ + const char *existing_target; + svn_revnum_t existing_rev; + svn_node_kind_t existing_kind; + svn_boolean_t show_deletion; + const char *empty_file; + + SVN_ERR_ASSERT(kind1 == svn_node_none || kind2 == svn_node_none); + + /* Are we showing an addition or deletion? */ + show_deletion = (kind2 == svn_node_none); + + /* Which target is being added/deleted? Is it a file or a directory? */ + if (show_deletion) + { + existing_target = target1; + existing_rev = rev1; + existing_kind = kind1; + } + else + { + existing_target = target2; + existing_rev = rev2; + existing_kind = kind2; + } + + /* All file content will be diffed against the empty file. */ + SVN_ERR(svn_io_open_unique_file3(NULL, &empty_file, NULL, + svn_io_file_del_on_pool_cleanup, + scratch_pool, scratch_pool)); + + if (existing_kind == svn_node_file) + { + /* Get file content and show a diff against the empty file. */ + SVN_ERR(diff_repos_repos_added_or_deleted_file(existing_target, + existing_rev, + rev1, rev2, + show_deletion, + empty_file, + callbacks, + callback_baton, + ra_session, + scratch_pool)); + } + else + { + /* Walk the added/deleted tree and show a diff for each child. */ + SVN_ERR(diff_repos_repos_added_or_deleted_dir(existing_target, + existing_rev, + rev1, rev2, + show_deletion, + empty_file, + callbacks, + callback_baton, + ra_session, + scratch_pool)); + } + + return SVN_NO_ERROR; +} /* Perform a diff between two repository paths. @@ -1847,6 +2140,8 @@ diff_repos_repos(const svn_wc_diff_callb const char *base_path; svn_revnum_t rev1; svn_revnum_t rev2; + svn_node_kind_t kind1; + svn_node_kind_t kind2; const char *anchor1; const char *anchor2; const char *target1; @@ -1856,7 +2151,8 @@ diff_repos_repos(const svn_wc_diff_callb /* Prepare info for the repos repos diff. */ SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &base_path, &rev1, &rev2, &anchor1, &anchor2, &target1, &target2, - &ra_session, ctx, path1, path2, + &kind1, &kind2, &ra_session, + ctx, path1, path2, revision1, revision2, peg_revision, pool)); @@ -1871,6 +2167,21 @@ diff_repos_repos(const svn_wc_diff_callb callback_baton->ra_session = ra_session; callback_baton->anchor = base_path; + if (kind1 == svn_node_none || kind2 == svn_node_none) + { + /* One side of the diff does not exist. + * Walk the tree that does exist, showing a series of additions + * or deletions. */ + SVN_ERR(diff_repos_repos_added_or_deleted_target(target1, target2, + rev1, rev2, + kind1, kind2, + callbacks, + callback_baton, + ra_session, + pool)); + return SVN_NO_ERROR; + } + /* Now, we open an extra RA session to the correct anchor location for URL1. This is used during the editor calls to fetch file contents. */ @@ -1904,6 +2215,204 @@ diff_repos_repos(const svn_wc_diff_callb } +/* Using CALLBACKS, show a REPOS->WC diff for a file TARGET, which in the + * working copy is at FILE2_ABSPATH. KIND1 is the node kind of the repository + * target (either svn_node_file or svn_node_none). REV is the revision the + * working file is diffed against. RA_SESSION points at the URL of the file + * in the repository and is used to get the file's repository-version content, + * if necessary. If DIFF_WITH_BASE is set, diff against the BASE version of + * the local file instead of WORKING. + * The other parameters are as in diff_repos_wc(). */ +static svn_error_t * +diff_repos_wc_file_target(const char *target, + const char *file2_abspath, + svn_node_kind_t kind1, + svn_revnum_t rev, + svn_boolean_t reverse, + svn_boolean_t show_copies_as_adds, + svn_boolean_t diff_with_base, + const svn_wc_diff_callbacks4_t *callbacks, + void *callback_baton, + svn_ra_session_t *ra_session, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) +{ + const char *file1_abspath; + svn_stream_t *file1_content; + svn_stream_t *file2_content; + apr_hash_t *file1_props = NULL; + apr_hash_t *file2_props; + svn_boolean_t is_copy = FALSE; + svn_string_t *mimetype1, *mimetype2; + + /* Get content and props of file 1 (the remote file). */ + SVN_ERR(svn_stream_open_unique(&file1_content, &file1_abspath, NULL, + svn_io_file_del_on_pool_cleanup, + scratch_pool, scratch_pool)); + if (kind1 == svn_node_file) + { + if (show_copies_as_adds) + SVN_ERR(svn_wc__node_get_origin(&is_copy, + NULL, NULL, NULL, NULL, NULL, + ctx->wc_ctx, file2_abspath, + FALSE, scratch_pool, scratch_pool)); + /* If showing copies as adds, diff against the empty file. */ + if (!(show_copies_as_adds && is_copy)) + SVN_ERR(svn_ra_get_file(ra_session, "", rev, file1_content, + NULL, &file1_props, scratch_pool)); + } + + SVN_ERR(svn_stream_close(file1_content)); + + /* Get content and props of file 2 (the local file). */ + if (diff_with_base) + { + svn_stream_t *pristine_content; + + SVN_ERR(svn_wc_get_pristine_props(&file2_props, ctx->wc_ctx, + file2_abspath, scratch_pool, + scratch_pool)); + + /* ### We need a filename, but this API returns an opaque stream. + * ### This requires us to copy to a temporary file. Maybe libsvn_wc + * ### should also provide an API that returns a path to a file that + * ### contains pristine content, possibly temporary? */ + SVN_ERR(svn_wc_get_pristine_contents2(&pristine_content, + ctx->wc_ctx, + file2_abspath, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_stream_open_unique(&file2_content, &file2_abspath, NULL, + svn_io_file_del_on_pool_cleanup, + scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_copy3(pristine_content, file2_content, + ctx->cancel_func, ctx->cancel_baton, + scratch_pool)); + } + else + { + apr_hash_t *keywords = NULL; + svn_string_t *keywords_prop; + svn_string_t *eol_prop; + svn_subst_eol_style_t eol_style; + const char *eol_str; + + SVN_ERR(svn_wc_prop_list2(&file2_props, ctx->wc_ctx, file2_abspath, + scratch_pool, scratch_pool)); + + /* We might have to create a normalised version of the working file. */ + eol_prop = apr_hash_get(file2_props, SVN_PROP_EOL_STYLE, + APR_HASH_KEY_STRING); + svn_subst_eol_style_from_value(&eol_style, &eol_str, + eol_prop ? eol_prop->data : NULL); + keywords_prop = apr_hash_get(file2_props, SVN_PROP_KEYWORDS, + APR_HASH_KEY_STRING); + if (keywords_prop) + SVN_ERR(svn_subst_build_keywords2(&keywords, keywords_prop->data, + NULL, NULL, 0, NULL, + scratch_pool)); + if (svn_subst_translation_required(eol_style, eol_str, + keywords, FALSE, TRUE)) + { + svn_stream_t *working_content; + svn_stream_t *normalized_content; + + if (eol_style == svn_subst_eol_style_native) + eol_str = SVN_SUBST_NATIVE_EOL_STR; + else if (! (eol_style == svn_subst_eol_style_fixed + || eol_style == svn_subst_eol_style_none)) + return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL); + + SVN_ERR(svn_stream_open_readonly(&working_content, file2_abspath, + scratch_pool, scratch_pool)); + + /* Create a temporary file and copy normalised data into it. */ + SVN_ERR(svn_stream_open_unique(&file2_content, &file2_abspath, NULL, + svn_io_file_del_on_pool_cleanup, + scratch_pool, scratch_pool)); + normalized_content = svn_subst_stream_translated( + file2_content, eol_str, + eol_style == svn_subst_eol_style_fixed, + keywords, FALSE, scratch_pool); + SVN_ERR(svn_stream_copy3(working_content, normalized_content, + ctx->cancel_func, ctx->cancel_baton, + scratch_pool)); + } + } + + mimetype1 = file1_props ? apr_hash_get(file1_props, SVN_PROP_MIME_TYPE, + APR_HASH_KEY_STRING) + : NULL; + mimetype2 = apr_hash_get(file2_props, SVN_PROP_MIME_TYPE, + APR_HASH_KEY_STRING); + + if (kind1 == svn_node_file && !(show_copies_as_adds && is_copy)) + { + apr_array_header_t *propchanges; + + SVN_ERR(callbacks->file_opened(NULL, NULL, target, + reverse ? SVN_INVALID_REVNUM : rev, + callback_baton, scratch_pool)); + + if (reverse) + { + SVN_ERR(svn_prop_diffs(&propchanges, file1_props, file2_props, + scratch_pool)); + + SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, target, + file2_abspath, file1_abspath, + SVN_INVALID_REVNUM, rev, + mimetype2 ? mimetype2->data : NULL, + mimetype1 ? mimetype1->data : NULL, + propchanges, file2_props, + callback_baton, scratch_pool)); + } + else + { + SVN_ERR(svn_prop_diffs(&propchanges, file2_props, file1_props, + scratch_pool)); + + SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, target, + file1_abspath, file2_abspath, + rev, SVN_INVALID_REVNUM, + mimetype1 ? mimetype1->data : NULL, + mimetype2 ? mimetype2->data : NULL, + propchanges, file1_props, + callback_baton, scratch_pool)); + } + } + else + { + if (reverse) + { + SVN_ERR(callbacks->file_deleted(NULL, NULL, + target, file2_abspath, file1_abspath, + mimetype2 ? mimetype2->data : NULL, + NULL, + make_regular_props_hash( + file2_props, scratch_pool, + scratch_pool), + callback_baton, scratch_pool)); + } + else + { + SVN_ERR(callbacks->file_added(NULL, NULL, NULL, target, + file1_abspath, file2_abspath, + rev, SVN_INVALID_REVNUM, + NULL, + mimetype2 ? mimetype2->data : NULL, + NULL, SVN_INVALID_REVNUM, + make_regular_props_array( + file2_props, scratch_pool, + scratch_pool), + NULL, + callback_baton, scratch_pool)); + } + } + + return SVN_NO_ERROR; +} + /* Perform a diff between a repository path and a working-copy path. PATH1 may be either a URL or a working copy path. PATH2 is a @@ -1944,6 +2453,8 @@ diff_repos_wc(const char *path1, const char *abspath1; const char *abspath2; const char *anchor_abspath; + svn_node_kind_t kind1; + svn_node_kind_t kind2; SVN_ERR_ASSERT(! svn_path_is_url(path2)); @@ -2000,22 +2511,53 @@ diff_repos_wc(const char *path1, } } - /* Establish RA session to path2's anchor */ - SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, anchor_url, - NULL, NULL, FALSE, TRUE, - ctx, pool)); - callback_baton->ra_session = ra_session; if (use_git_diff_format) { SVN_ERR(svn_wc__get_wc_root(&callback_baton->wc_root_abspath, ctx->wc_ctx, anchor_abspath, pool, pool)); } + + /* Open an RA session to URL1 to figure out its node kind. */ + SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, url1, + NULL, NULL, FALSE, TRUE, + ctx, pool)); + /* Resolve the revision to use for URL1. */ + SVN_ERR(svn_client__get_revision_number(&rev, NULL, ctx->wc_ctx, + (strcmp(path1, url1) == 0) + ? NULL : abspath1, + ra_session, revision1, pool)); + SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind1, pool)); + + /* Figure out the node kind of the local target. */ + SVN_ERR(svn_io_check_resolved_path(abspath2, &kind2, pool)); + + callback_baton->ra_session = ra_session; callback_baton->anchor = anchor; + if (!reverse) + callback_baton->revnum1 = rev; + else + callback_baton->revnum2 = rev; + + /* If both diff targets can be diffed as files, fetch the file from the + * repository and generate a diff against the local version of the file. */ + if ((kind1 == svn_node_file || kind1 == svn_node_none) + && kind2 == svn_node_file) + { + SVN_ERR(diff_repos_wc_file_target(target, abspath2, kind1, rev, + reverse, show_copies_as_adds, + rev2_is_base, + callbacks, callback_baton, + ra_session, ctx, pool)); + + return SVN_NO_ERROR; + } + + /* Else, use the diff editor to generate the diff. */ + SVN_ERR(svn_ra_reparent(ra_session, anchor_url, pool)); SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, SVN_RA_CAPABILITY_DEPTH, pool)); - SVN_ERR(svn_wc_get_diff_editor6(&diff_editor, &diff_edit_baton, ctx->wc_ctx, anchor_abspath, @@ -2032,22 +2574,12 @@ diff_repos_wc(const char *path1, ctx->cancel_func, ctx->cancel_baton, pool, pool)); - /* Tell the RA layer we want a delta to change our txn to URL1 */ - SVN_ERR(svn_client__get_revision_number(&rev, NULL, ctx->wc_ctx, - (strcmp(path1, url1) == 0) - ? NULL : abspath1, - ra_session, revision1, pool)); - - if (!reverse) - callback_baton->revnum1 = rev; - else - callback_baton->revnum2 = rev; - if (depth != svn_depth_infinity) diff_depth = depth; else diff_depth = svn_depth_unknown; + /* Tell the RA layer we want a delta to change our txn to URL1 */ SVN_ERR(svn_ra_do_diff3(ra_session, &reporter, &reporter_baton, rev, @@ -2136,6 +2668,7 @@ do_diff(const svn_wc_diff_callbacks4_t * return SVN_NO_ERROR; } + /* Perform a diff summary between two repository paths. */ static svn_error_t * diff_summarize_repos_repos(svn_client_diff_summarize_func_t summarize_func, @@ -2163,6 +2696,8 @@ diff_summarize_repos_repos(svn_client_di const char *base_path; svn_revnum_t rev1; svn_revnum_t rev2; + svn_node_kind_t kind1; + svn_node_kind_t kind2; const char *anchor1; const char *anchor2; const char *target1; @@ -2172,10 +2707,32 @@ diff_summarize_repos_repos(svn_client_di /* Prepare info for the repos repos diff. */ SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &base_path, &rev1, &rev2, &anchor1, &anchor2, &target1, &target2, - &ra_session, ctx, - path1, path2, revision1, revision2, + &kind1, &kind2, &ra_session, + ctx, path1, path2, + revision1, revision2, peg_revision, pool)); + if (kind1 == svn_node_none || kind2 == svn_node_none) + { + svn_wc_diff_callbacks4_t *callbacks; + void *callback_baton; + + /* One side of the diff does not exist. + * Walk the tree that does exist, showing a series of additions + * or deletions. */ + SVN_ERR(svn_client__get_diff_summarize_callbacks( + &callbacks, &callback_baton, target1, + summarize_func, summarize_baton, pool)); + SVN_ERR(diff_repos_repos_added_or_deleted_target(target1, target2, + rev1, rev2, + kind1, kind2, + callbacks, + callback_baton, + ra_session, + pool)); + return SVN_NO_ERROR; + } + /* Now, we open an extra RA session to the correct anchor location for URL1. This is used to get the kind of deleted paths. */ SVN_ERR(svn_client__open_ra_session_internal(&extra_ra_session, NULL,
