subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ar...@apache.org
Subject svn commit: r1204374 [6/29] - in /subversion/branches/svn-bisect: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/ contrib/client-side/emacs/ notes/ notes/wc-ng/ subversion/bindings/javahl/native/...
Date Mon, 21 Nov 2011 07:08:07 GMT
Modified: subversion/branches/svn-bisect/subversion/libsvn_client/diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_client/diff.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_client/diff.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_client/diff.c Mon Nov 21 07:07:56 2011
@@ -69,44 +69,20 @@ static const char under_string[] =
 
 /* Utilities */
 
-/* Wrapper for apr_file_printf(), which see.  FORMAT is a utf8-encoded
-   string after it is formatted, so this function can convert it to
-   ENCODING before printing. */
-static svn_error_t *
-file_printf_from_utf8(apr_file_t *fptr, const char *encoding,
-                      const char *format, ...)
-  __attribute__ ((format(printf, 3, 4)));
-static svn_error_t *
-file_printf_from_utf8(apr_file_t *fptr, const char *encoding,
-                      const char *format, ...)
-{
-  va_list ap;
-  const char *buf, *buf_apr;
-
-  va_start(ap, format);
-  buf = apr_pvsprintf(apr_file_pool_get(fptr), format, ap);
-  va_end(ap);
-
-  SVN_ERR(svn_utf_cstring_from_utf8_ex2(&buf_apr, buf, encoding,
-                                        apr_file_pool_get(fptr)));
-
-  return svn_io_file_write_full(fptr, buf_apr, strlen(buf_apr),
-                                NULL, apr_file_pool_get(fptr));
-}
-
 
 /* A helper function for display_prop_diffs.  Output the differences between
    the mergeinfo stored in ORIG_MERGEINFO_VAL and NEW_MERGEINFO_VAL in a
-   human-readable form to FILE, using ENCODING.  Use POOL for temporary
+   human-readable form to OUTSTREAM, using ENCODING.  Use POOL for temporary
    allocations. */
 static svn_error_t *
 display_mergeinfo_diff(const char *old_mergeinfo_val,
                        const char *new_mergeinfo_val,
                        const char *encoding,
-                       apr_file_t *file,
+                       svn_stream_t *outstream,
                        apr_pool_t *pool)
 {
   apr_hash_t *old_mergeinfo_hash, *new_mergeinfo_hash, *added, *deleted;
+  apr_pool_t *iterpool = svn_pool_create(pool);
   apr_hash_index_t *hi;
 
   if (old_mergeinfo_val)
@@ -119,9 +95,9 @@ display_mergeinfo_diff(const char *old_m
   else
     new_mergeinfo_hash = NULL;
 
-  SVN_ERR(svn_mergeinfo_diff(&deleted, &added, old_mergeinfo_hash,
-                             new_mergeinfo_hash,
-                             TRUE, pool));
+  SVN_ERR(svn_mergeinfo_diff2(&deleted, &added, old_mergeinfo_hash,
+                              new_mergeinfo_hash,
+                              TRUE, pool, pool));
 
   for (hi = apr_hash_first(pool, deleted);
        hi; hi = apr_hash_next(hi))
@@ -130,12 +106,14 @@ display_mergeinfo_diff(const char *old_m
       apr_array_header_t *merge_revarray = svn__apr_hash_index_val(hi);
       svn_string_t *merge_revstr;
 
-      SVN_ERR(svn_rangelist_to_string(&merge_revstr, merge_revarray, pool));
-
-      SVN_ERR(file_printf_from_utf8(file, encoding,
-                                    _("   Reverse-merged %s:r%s%s"),
-                                    from_path, merge_revstr->data,
-                                    APR_EOL_STR));
+      svn_pool_clear(iterpool);
+      SVN_ERR(svn_rangelist_to_string(&merge_revstr, merge_revarray,
+                                      iterpool));
+
+      SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, iterpool,
+                                          _("   Reverse-merged %s:r%s%s"),
+                                          from_path, merge_revstr->data,
+                                          APR_EOL_STR));
     }
 
   for (hi = apr_hash_first(pool, added);
@@ -145,14 +123,17 @@ display_mergeinfo_diff(const char *old_m
       apr_array_header_t *merge_revarray = svn__apr_hash_index_val(hi);
       svn_string_t *merge_revstr;
 
-      SVN_ERR(svn_rangelist_to_string(&merge_revstr, merge_revarray, pool));
+      svn_pool_clear(iterpool);
+      SVN_ERR(svn_rangelist_to_string(&merge_revstr, merge_revarray,
+                                      iterpool));
 
-      SVN_ERR(file_printf_from_utf8(file, encoding,
-                                    _("   Merged %s:r%s%s"),
-                                    from_path, merge_revstr->data,
-                                    APR_EOL_STR));
+      SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, iterpool,
+                                          _("   Merged %s:r%s%s"),
+                                          from_path, merge_revstr->data,
+                                          APR_EOL_STR));
     }
 
+  svn_pool_destroy(iterpool);
   return SVN_NO_ERROR;
 }
 
@@ -254,7 +235,7 @@ adjust_relative_to_repos_root(const char
   return SVN_NO_ERROR;
 }
 
-/* Adjust PATH, ORIG_PATH_1 and ORIG_PATH_2, representing the changed file
+/* Adjust *PATH, *ORIG_PATH_1 and *ORIG_PATH_2, representing the changed file
  * and the two original targets passed to the diff command, to handle the
  * case when we're dealing with different anchors. RELATIVE_TO_DIR is the
  * directory the diff target should be considered relative to. All
@@ -459,10 +440,6 @@ print_git_diff_header_modified(svn_strea
  * are the paths passed to the original diff command. REV1 and REV2 are
  * revisions being diffed. COPYFROM_PATH and COPYFROM_REV indicate where the
  * diffed item was copied from.
- * RA_SESSION and WC_CTX are used to adjust paths in the headers to be
- * relative to the repository root.
- * WC_ROOT_ABSPATH is the absolute path to the root directory of a working
- * copy involved in a repos-wc diff, and may be NULL.
  * Use SCRATCH_POOL for temporary allocations. */
 static svn_error_t *
 print_git_diff_header(svn_stream_t *os,
@@ -475,9 +452,6 @@ print_git_diff_header(svn_stream_t *os,
                       const char *copyfrom_path,
                       svn_revnum_t copyfrom_rev,
                       const char *header_encoding,
-                      svn_ra_session_t *ra_session,
-                      svn_wc_context_t *wc_ctx,
-                      const char *wc_root_abspath,
                       apr_pool_t *scratch_pool)
 {
   if (operation == svn_diff_op_deleted)
@@ -535,8 +509,8 @@ print_git_diff_header(svn_stream_t *os,
 }
 
 /* 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_diff5, which is probably stdout.
+   to OUTSTREAM.   Of course, OUTSTREAM will probably be whatever was
+   passed to svn_client_diff6(), which is probably stdout.
 
    ### FIXME needs proper docstring
 
@@ -556,7 +530,7 @@ display_prop_diffs(const apr_array_heade
                    svn_revnum_t rev1,
                    svn_revnum_t rev2,
                    const char *encoding,
-                   apr_file_t *file,
+                   svn_stream_t *outstream,
                    const char *relative_to_dir,
                    svn_boolean_t show_diff_header,
                    svn_boolean_t use_git_diff_format,
@@ -566,8 +540,9 @@ display_prop_diffs(const apr_array_heade
                    apr_pool_t *pool)
 {
   int i;
-  const char *path1 = apr_pstrdup(pool, orig_path1);
-  const char *path2 = apr_pstrdup(pool, orig_path2);
+  const char *path1 = orig_path1;
+  const char *path2 = orig_path2;
+  apr_pool_t *iterpool;
 
   if (use_git_diff_format)
     {
@@ -589,8 +564,8 @@ display_prop_diffs(const apr_array_heade
     {
       const char *label1;
       const char *label2;
-      const char *adjusted_path1 = apr_pstrdup(pool, path1);
-      const char *adjusted_path2 = apr_pstrdup(pool, path2);
+      const char *adjusted_path1 = path1;
+      const char *adjusted_path2 = path2;
 
       SVN_ERR(adjust_paths_for_diff_labels(&path, &adjusted_path1,
                                            &adjusted_path2,
@@ -602,41 +577,35 @@ display_prop_diffs(const apr_array_heade
       /* ### Should we show the paths in platform specific format,
        * ### diff_content_changed() does not! */
 
-      SVN_ERR(file_printf_from_utf8(file, encoding,
-                                    "Index: %s" APR_EOL_STR
-                                    "%s" APR_EOL_STR,
-                                    path, equal_string));
+      SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, pool,
+                                          "Index: %s" APR_EOL_STR
+                                          "%s" APR_EOL_STR,
+                                          path, equal_string));
 
       if (use_git_diff_format)
-        {
-          svn_stream_t *os;
-
-          os = svn_stream_from_aprfile2(file, TRUE, pool);
-          SVN_ERR(print_git_diff_header(os, &label1, &label2,
-                                        svn_diff_op_modified,
-                                        path1, path2, rev1, rev2, NULL,
-                                        SVN_INVALID_REVNUM,
-                                        encoding, ra_session, wc_ctx,
-                                        wc_root_abspath, pool));
-          SVN_ERR(svn_stream_close(os));
-        }
+        SVN_ERR(print_git_diff_header(outstream, &label1, &label2,
+                                      svn_diff_op_modified,
+                                      path1, path2, rev1, rev2, NULL,
+                                      SVN_INVALID_REVNUM,
+                                      encoding, pool));
 
-      SVN_ERR(file_printf_from_utf8(file, encoding,
+      SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, pool,
                                           "--- %s" APR_EOL_STR
                                           "+++ %s" APR_EOL_STR,
                                           label1,
                                           label2));
     }
 
-  SVN_ERR(file_printf_from_utf8(file, encoding,
-                                _("%sProperty changes on: %s%s"),
-                                APR_EOL_STR,
-                                use_git_diff_format ? path1 : path,
-                                APR_EOL_STR));
+  SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, pool,
+                                      _("%sProperty changes on: %s%s"),
+                                      APR_EOL_STR,
+                                      use_git_diff_format ? path1 : path,
+                                      APR_EOL_STR));
 
-  SVN_ERR(file_printf_from_utf8(file, encoding, "%s" APR_EOL_STR,
-                                under_string));
+  SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, pool,
+                                      "%s" APR_EOL_STR, under_string));
 
+  iterpool = svn_pool_create(pool);
   for (i = 0; i < propchanges->nelts; i++)
     {
       const char *action;
@@ -657,21 +626,24 @@ display_prop_diffs(const apr_array_heade
               && svn_string_compare(original_value, propchange->value)))
         continue;
 
+      svn_pool_clear(iterpool);
+
       if (! original_value)
         action = "Added";
       else if (! propchange->value)
         action = "Deleted";
       else
         action = "Modified";
-      SVN_ERR(file_printf_from_utf8(file, encoding, "%s: %s%s", action,
-                                    propchange->name, APR_EOL_STR));
+      SVN_ERR(svn_stream_printf_from_utf8(outstream, encoding, iterpool,
+                                          "%s: %s%s", action,
+                                          propchange->name, APR_EOL_STR));
 
       if (strcmp(propchange->name, SVN_PROP_MERGEINFO) == 0)
         {
           const char *orig = original_value ? original_value->data : NULL;
           const char *val = propchange->value ? propchange->value->data : NULL;
           svn_error_t *err = display_mergeinfo_diff(orig, val, encoding,
-                                                    file, pool);
+                                                    outstream, iterpool);
 
           /* Issue #3896: If we can't pretty-print mergeinfo differences
              because invalid mergeinfo is present, then don't let the diff
@@ -688,7 +660,6 @@ display_prop_diffs(const apr_array_heade
         }
 
       {
-        svn_stream_t *os = svn_stream_from_aprfile2(file, TRUE, pool);
         svn_diff_t *diff;
         svn_diff_file_options_t options = { 0 };
         const svn_string_t *tmp;
@@ -699,14 +670,16 @@ display_prop_diffs(const apr_array_heade
            Since the diff is not useful anyway for patching properties an
            eol character is appended when needed to remove those pescious
            ' \ No newline at end of file' lines. */
-        tmp = original_value ? original_value : svn_string_create("", pool);
-        orig = maybe_append_eol(tmp, pool);
+        tmp = original_value ? original_value 
+                             : svn_string_create_empty(iterpool);
+        orig = maybe_append_eol(tmp, iterpool);
 
         tmp = propchange->value ? propchange->value :
-                                  svn_string_create("", pool);
-        val = maybe_append_eol(tmp, pool);
+                                  svn_string_create_empty(iterpool);
+        val = maybe_append_eol(tmp, iterpool);
 
-        SVN_ERR(svn_diff_mem_string_diff(&diff, orig, val, &options, pool));
+        SVN_ERR(svn_diff_mem_string_diff(&diff, orig, val, &options,
+                                         iterpool));
 
         /* UNIX patch will try to apply a diff even if the diff header
          * is missing. It tries to be helpful by asking the user for a
@@ -715,14 +688,17 @@ display_prop_diffs(const apr_array_heade
          * UNIX patch could apply the property diff to, so we use "##"
          * instead of "@@" as the default hunk delimiter for property diffs.
          * We also supress the diff header. */
-        SVN_ERR(svn_diff_mem_string_output_unified2(os, diff, FALSE, "##",
-                                           svn_dirent_local_style(path, pool),
-                                           svn_dirent_local_style(path, pool),
-                                           encoding, orig, val, pool));
-        SVN_ERR(svn_stream_close(os));
+        SVN_ERR(svn_diff_mem_string_output_unified2(outstream, diff, FALSE,
+                                                    "##",
+                                           svn_dirent_local_style(path,
+                                                                  iterpool),
+                                           svn_dirent_local_style(path,
+                                                                  iterpool),
+                                           encoding, orig, val, iterpool));
 
       }
     }
+  svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
@@ -752,8 +728,8 @@ struct diff_cmd_baton {
   } options;
 
   apr_pool_t *pool;
-  apr_file_t *outfile;
-  apr_file_t *errfile;
+  svn_stream_t *outstream;
+  svn_stream_t *errstream;
 
   const char *header_encoding;
 
@@ -765,7 +741,7 @@ struct diff_cmd_baton {
   const char *orig_path_2;
 
   /* These are the numeric representations of the revisions passed to
-     svn_client_diff5, either may be SVN_INVALID_REVNUM.  We need these
+     svn_client_diff6(), either may be SVN_INVALID_REVNUM.  We need these
      because some of the svn_wc_diff_callbacks4_t don't get revision
      arguments.
 
@@ -820,10 +796,9 @@ diff_props_changed(svn_wc_notify_state_t
                    svn_boolean_t dir_was_added,
                    const apr_array_header_t *propchanges,
                    apr_hash_t *original_props,
-                   void *diff_baton,
+                   struct diff_cmd_baton *diff_cmd_baton,
                    apr_pool_t *scratch_pool)
 {
-  struct diff_cmd_baton *diff_cmd_baton = diff_baton;
   apr_array_header_t *props;
   svn_boolean_t show_diff_header;
 
@@ -846,7 +821,7 @@ diff_props_changed(svn_wc_notify_state_t
                                  diff_cmd_baton->revnum1,
                                  diff_cmd_baton->revnum2,
                                  diff_cmd_baton->header_encoding,
-                                 diff_cmd_baton->outfile,
+                                 diff_cmd_baton->outstream,
                                  diff_cmd_baton->relative_to_dir,
                                  show_diff_header,
                                  diff_cmd_baton->use_git_diff_format,
@@ -891,7 +866,7 @@ diff_dir_props_changed(svn_wc_notify_sta
                                             dir_was_added,
                                             propchanges,
                                             original_props,
-                                            diff_baton,
+                                            diff_cmd_baton,
                                             scratch_pool));
 }
 
@@ -911,26 +886,19 @@ diff_content_changed(const char *path,
                      svn_diff_operation_kind_t operation,
                      const char *copyfrom_path,
                      svn_revnum_t copyfrom_rev,
-                     void *diff_baton)
+                     struct diff_cmd_baton *diff_cmd_baton)
 {
-  struct diff_cmd_baton *diff_cmd_baton = diff_baton;
   int 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;
+  svn_stream_t *errstream = diff_cmd_baton->errstream;
+  svn_stream_t *outstream = diff_cmd_baton->outstream;
   const char *label1, *label2;
   svn_boolean_t mt1_binary = FALSE, mt2_binary = FALSE;
-  const char *path1, *path2;
-
-  /* Get a stream from our output file. */
-  os = svn_stream_from_aprfile2(diff_cmd_baton->outfile, TRUE, subpool);
+  const char *path1 = diff_cmd_baton->orig_path_1;
+  const char *path2 = diff_cmd_baton->orig_path_2;
 
   /* Generate the diff headers. */
-
-  path1 = apr_pstrdup(subpool, diff_cmd_baton->orig_path_1);
-  path2 = apr_pstrdup(subpool, diff_cmd_baton->orig_path_2);
-
   SVN_ERR(adjust_paths_for_diff_labels(&path, &path1, &path2,
                                        rel_to_dir, subpool));
 
@@ -948,35 +916,35 @@ diff_content_changed(const char *path,
   if (! diff_cmd_baton->force_binary && (mt1_binary || mt2_binary))
     {
       /* Print out the diff header. */
-      SVN_ERR(svn_stream_printf_from_utf8
-              (os, diff_cmd_baton->header_encoding, subpool,
+      SVN_ERR(svn_stream_printf_from_utf8(outstream,
+               diff_cmd_baton->header_encoding, subpool,
                "Index: %s" APR_EOL_STR "%s" APR_EOL_STR, path, equal_string));
 
       /* ### Print git diff headers. */
 
-      SVN_ERR(svn_stream_printf_from_utf8
-              (os, diff_cmd_baton->header_encoding, subpool,
+      SVN_ERR(svn_stream_printf_from_utf8(outstream,
+               diff_cmd_baton->header_encoding, subpool,
                _("Cannot display: file marked as a binary type.%s"),
                APR_EOL_STR));
 
       if (mt1_binary && !mt2_binary)
-        SVN_ERR(svn_stream_printf_from_utf8
-                (os, diff_cmd_baton->header_encoding, subpool,
+        SVN_ERR(svn_stream_printf_from_utf8(outstream,
+                 diff_cmd_baton->header_encoding, subpool,
                  "svn:mime-type = %s" APR_EOL_STR, mimetype1));
       else if (mt2_binary && !mt1_binary)
-        SVN_ERR(svn_stream_printf_from_utf8
-                (os, diff_cmd_baton->header_encoding, subpool,
+        SVN_ERR(svn_stream_printf_from_utf8(outstream,
+                 diff_cmd_baton->header_encoding, subpool,
                  "svn:mime-type = %s" APR_EOL_STR, mimetype2));
       else if (mt1_binary && mt2_binary)
         {
           if (strcmp(mimetype1, mimetype2) == 0)
-            SVN_ERR(svn_stream_printf_from_utf8
-                    (os, diff_cmd_baton->header_encoding, subpool,
+            SVN_ERR(svn_stream_printf_from_utf8(outstream,
+                     diff_cmd_baton->header_encoding, subpool,
                      "svn:mime-type = %s" APR_EOL_STR,
                      mimetype1));
           else
-            SVN_ERR(svn_stream_printf_from_utf8
-                    (os, diff_cmd_baton->header_encoding, subpool,
+            SVN_ERR(svn_stream_printf_from_utf8(outstream,
+                     diff_cmd_baton->header_encoding, subpool,
                      "svn:mime-type = (%s, %s)" APR_EOL_STR,
                      mimetype1, mimetype2));
         }
@@ -989,25 +957,52 @@ diff_content_changed(const char *path,
 
   if (diff_cmd_baton->diff_cmd)
     {
+      apr_file_t *outfile;
+      apr_file_t *errfile;
+      const char *outfilename;
+      const char *errfilename;
+      svn_stream_t *stream;
+
       /* Print out the diff header. */
-      SVN_ERR(svn_stream_printf_from_utf8
-              (os, diff_cmd_baton->header_encoding, subpool,
+      SVN_ERR(svn_stream_printf_from_utf8(outstream,
+               diff_cmd_baton->header_encoding, subpool,
                "Index: %s" APR_EOL_STR "%s" APR_EOL_STR, path, equal_string));
-      /* Close the stream (flush) */
-      SVN_ERR(svn_stream_close(os));
 
       /* ### Do we want to add git diff headers here too? I'd say no. The
        * ### 'Index' and '===' line is something subversion has added. The rest
        * ### is up to the external diff application. We may be dealing with
        * ### a non-git compatible diff application.*/
 
+      /* We deal in streams, but svn_io_run_diff2() deals in file handles,
+         unfortunately, so we need to make these temporary files, and then
+         copy the contents to our stream. */
+      SVN_ERR(svn_io_open_unique_file3(&outfile, &outfilename, NULL,
+                                       svn_io_file_del_on_pool_cleanup,
+                                       subpool, subpool));
+      SVN_ERR(svn_io_open_unique_file3(&errfile, &errfilename, NULL,
+                                       svn_io_file_del_on_pool_cleanup,
+                                       subpool, subpool));
+
       SVN_ERR(svn_io_run_diff2(".",
                                diff_cmd_baton->options.for_external.argv,
                                diff_cmd_baton->options.for_external.argc,
                                label1, label2,
                                tmpfile1, tmpfile2,
-                               &exitcode, diff_cmd_baton->outfile, errfile,
+                               &exitcode, outfile, errfile,
                                diff_cmd_baton->diff_cmd, subpool));
+
+      SVN_ERR(svn_io_file_close(outfile, subpool));
+      SVN_ERR(svn_io_file_close(errfile, subpool));
+
+      /* Now, open and copy our files to our output streams. */
+      SVN_ERR(svn_stream_open_readonly(&stream, outfilename,
+                                       subpool, subpool));
+      SVN_ERR(svn_stream_copy3(stream, svn_stream_disown(outstream, subpool),
+                               NULL, NULL, subpool));
+      SVN_ERR(svn_stream_open_readonly(&stream, errfilename,
+                                       subpool, subpool));
+      SVN_ERR(svn_stream_copy3(stream, svn_stream_disown(errstream, subpool),
+                               NULL, NULL, subpool));
     }
   else   /* use libsvn_diff to generate the diff  */
     {
@@ -1021,8 +1016,8 @@ diff_content_changed(const char *path,
           diff_cmd_baton->use_git_diff_format)
         {
           /* Print out the diff header. */
-          SVN_ERR(svn_stream_printf_from_utf8
-                  (os, diff_cmd_baton->header_encoding, subpool,
+          SVN_ERR(svn_stream_printf_from_utf8(outstream,
+                   diff_cmd_baton->header_encoding, subpool,
                    "Index: %s" APR_EOL_STR "%s" APR_EOL_STR,
                    path, equal_string));
 
@@ -1037,21 +1032,19 @@ diff_content_changed(const char *path,
                          &tmp_path2, path, diff_cmd_baton->orig_path_2,
                          diff_cmd_baton->ra_session, diff_cmd_baton->wc_ctx,
                          diff_cmd_baton->wc_root_abspath, subpool));
-              SVN_ERR(print_git_diff_header(os, &label1, &label2, operation,
+              SVN_ERR(print_git_diff_header(outstream, &label1, &label2,
+                                            operation,
                                             tmp_path1, tmp_path2, rev1, rev2,
                                             copyfrom_path,
                                             copyfrom_rev,
                                             diff_cmd_baton->header_encoding,
-                                            diff_cmd_baton->ra_session,
-                                            diff_cmd_baton->wc_ctx,
-                                            diff_cmd_baton->wc_root_abspath,
                                             subpool));
             }
 
           /* Output the actual diff */
           if (svn_diff_contains_diffs(diff) || diff_cmd_baton->force_empty)
-            SVN_ERR(svn_diff_file_output_unified3
-                    (os, diff, tmpfile1, tmpfile2, label1, label2,
+            SVN_ERR(svn_diff_file_output_unified3(outstream, diff,
+                     tmpfile1, tmpfile2, label1, label2,
                      diff_cmd_baton->header_encoding, rel_to_dir,
                      diff_cmd_baton->options.for_internal->show_c_function,
                      subpool));
@@ -1061,9 +1054,6 @@ diff_content_changed(const char *path,
                        APR_HASH_KEY_STRING, path);
 
         }
-
-      /* Close the stream (flush) */
-      SVN_ERR(svn_stream_close(os));
     }
 
   /* ### todo: someday we'll need to worry about whether we're going
@@ -1105,6 +1095,7 @@ diff_file_changed(svn_wc_notify_state_t 
                   apr_pool_t *scratch_pool)
 {
   struct diff_cmd_baton *diff_cmd_baton = diff_baton;
+
   if (diff_cmd_baton->anchor)
     path = svn_dirent_join(diff_cmd_baton->anchor, path, scratch_pool);
   if (tmpfile1)
@@ -1112,11 +1103,11 @@ diff_file_changed(svn_wc_notify_state_t 
                                  tmpfile1, tmpfile2, rev1, rev2,
                                  mimetype1, mimetype2,
                                  svn_diff_op_modified, NULL,
-                                 SVN_INVALID_REVNUM, diff_baton));
+                                 SVN_INVALID_REVNUM, diff_cmd_baton));
   if (prop_changes->nelts > 0)
     SVN_ERR(diff_props_changed(prop_state, tree_conflicted,
                                path, FALSE, prop_changes,
-                               original_props, diff_baton, scratch_pool));
+                               original_props, diff_cmd_baton, scratch_pool));
   if (content_state)
     *content_state = svn_wc_notify_state_unknown;
   if (prop_state)
@@ -1166,17 +1157,17 @@ diff_file_added(svn_wc_notify_state_t *c
                                  tmpfile1, tmpfile2, rev1, rev2,
                                  mimetype1, mimetype2,
                                  svn_diff_op_copied, copyfrom_path,
-                                 copyfrom_revision, diff_baton));
+                                 copyfrom_revision, diff_cmd_baton));
   else if (tmpfile1)
     SVN_ERR(diff_content_changed(path,
                                  tmpfile1, tmpfile2, rev1, rev2,
                                  mimetype1, mimetype2,
                                  svn_diff_op_added, NULL, SVN_INVALID_REVNUM,
-                                 diff_baton));
+                                 diff_cmd_baton));
   if (prop_changes->nelts > 0)
     SVN_ERR(diff_props_changed(prop_state, tree_conflicted,
                                path, FALSE, prop_changes,
-                               original_props, diff_baton, scratch_pool));
+                               original_props, diff_cmd_baton, scratch_pool));
   if (content_state)
     *content_state = svn_wc_notify_state_unknown;
   if (prop_state)
@@ -1209,9 +1200,8 @@ diff_file_deleted(svn_wc_notify_state_t 
 
   if (diff_cmd_baton->no_diff_deleted)
     {
-      SVN_ERR(file_printf_from_utf8(
-                diff_cmd_baton->outfile,
-                diff_cmd_baton->header_encoding,
+      SVN_ERR(svn_stream_printf_from_utf8(diff_cmd_baton->outstream,
+                diff_cmd_baton->header_encoding, scratch_pool,
                 "Index: %s (deleted)" APR_EOL_STR "%s" APR_EOL_STR,
                 path, equal_string));
     }
@@ -1224,7 +1214,7 @@ diff_file_deleted(svn_wc_notify_state_t 
                                      diff_cmd_baton->revnum2,
                                      mimetype1, mimetype2,
                                      svn_diff_op_deleted, NULL,
-                                     SVN_INVALID_REVNUM, diff_baton));
+                                     SVN_INVALID_REVNUM, diff_cmd_baton));
     }
 
   /* We don't list all the deleted properties. */
@@ -1337,14 +1327,14 @@ static const svn_wc_diff_callbacks4_t di
    this knowledge has been grokked yet.
 
    There are five cases:
-      1. path is not an URL and start_revision != end_revision
-      2. path is not an URL and start_revision == end_revision
-      3. path is an URL and start_revision != end_revision
-      4. path is an URL and start_revision == end_revision
-      5. path is not an URL and no revisions given
+      1. path is not a URL and start_revision != end_revision
+      2. path is not a URL and start_revision == end_revision
+      3. path is a URL and start_revision != end_revision
+      4. path is a URL and start_revision == end_revision
+      5. path is not a URL and no revisions given
 
    With only one distinct revision the working copy provides the
-   other.  When path is an URL there is no working copy. Thus
+   other.  When path is a URL there is no working copy. Thus
 
      1: compare repository versions for URL coresponding to working copy
      2: compare working copy against repository version
@@ -1387,16 +1377,17 @@ convert_to_url(const char **url,
   return SVN_NO_ERROR;
 }
 
-/** Check if paths PATH1 and PATH2 are urls and if the revisions REVISION1
- *  and REVISION2 are local. If PEG_REVISION is not unspecified, ensure that
- *  at least one of the two revisions is non-local.
- *  If PATH1 can only be found in the repository, set *IS_REPOS1 to TRUE.
- *  If PATH2 can only be found in the repository, set *IS_REPOS2 to TRUE. */
+/** Check if paths PATH_OR_URL1 and PATH_OR_URL2 are urls and if the
+ * revisions REVISION1 and REVISION2 are local. If PEG_REVISION is not
+ * unspecified, ensure that at least one of the two revisions is non-local.
+ * If PATH_OR_URL1 can only be found in the repository, set *IS_REPOS1
+ * to TRUE. If PATH_OR_URL2 can only be found in the repository, set
+ * *IS_REPOS2 to TRUE. */
 static svn_error_t *
 check_paths(svn_boolean_t *is_repos1,
             svn_boolean_t *is_repos2,
-            const char *path1,
-            const char *path2,
+            const char *path_or_url1,
+            const char *path_or_url2,
             const svn_opt_revision_t *revision1,
             const svn_opt_revision_t *revision2,
             const svn_opt_revision_t *peg_revision)
@@ -1434,15 +1425,55 @@ check_paths(svn_boolean_t *is_repos1,
          URLs.  We don't do that here, though.  We simply record that it
          needs to be done, which is information that helps us choose our
          diff helper function.  */
-      *is_repos1 = ! is_local_rev1 || svn_path_is_url(path1);
-      *is_repos2 = ! is_local_rev2 || svn_path_is_url(path2);
+      *is_repos1 = ! is_local_rev1 || svn_path_is_url(path_or_url1);
+      *is_repos2 = ! is_local_rev2 || svn_path_is_url(path_or_url2);
     }
 
   return SVN_NO_ERROR;
 }
 
-/** Prepare a repos repos diff between PATH1 and PATH2@PEG_REVISION,
- * in the revision range REVISION1:REVISION2.
+/* Raise an error if the diff target URL does not exist at REVISION.
+ * If REVISION does not equal OTHER_REVISION, mention both revisions in
+ * the error message. Use RA_SESSION to contact the repository.
+ * Use POOL for temporary allocations. */
+static svn_error_t *
+check_diff_target_exists(const char *url,
+                         svn_revnum_t revision,
+                         svn_revnum_t other_revision,
+                         svn_ra_session_t *ra_session,
+                         apr_pool_t *pool)
+{
+  svn_node_kind_t kind;
+  const char *session_url;
+
+  SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool));
+
+  if (strcmp(url, session_url) != 0)
+    SVN_ERR(svn_ra_reparent(ra_session, url, pool));
+
+  SVN_ERR(svn_ra_check_path(ra_session, "", revision, &kind, pool));
+  if (kind == svn_node_none)
+    {
+      if (revision == other_revision)
+        return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+                                 _("Diff target '%s' was not found in the "
+                                   "repository at revision '%ld'"),
+                                 url, revision);
+      else
+        return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+                                 _("Diff target '%s' was not found in the "
+                                   "repository at revision '%ld' or '%ld'"),
+                                 url, revision, other_revision);
+     }
+
+  if (strcmp(url, session_url) != 0)
+    SVN_ERR(svn_ra_reparent(ra_session, session_url, pool));
+
+  return SVN_NO_ERROR;
+}
+
+/** Prepare a repos repos diff between PATH_OR_URL1 and
+ * PATH_OR_URL2@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.
@@ -1461,43 +1492,43 @@ diff_prepare_repos_repos(const char **ur
                          const char **target2,
                          svn_ra_session_t **ra_session,
                          svn_client_ctx_t *ctx,
-                         const char *path1,
-                         const char *path2,
+                         const char *path_or_url1,
+                         const char *path_or_url2,
                          const svn_opt_revision_t *revision1,
                          const svn_opt_revision_t *revision2,
                          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;
+  const char *abspath_or_url2;
+  const char *abspath_or_url1;
 
-  if (!svn_path_is_url(path2))
-    SVN_ERR(svn_dirent_get_absolute(&path2_abspath, path2,
+  if (!svn_path_is_url(path_or_url2))
+    SVN_ERR(svn_dirent_get_absolute(&abspath_or_url2, path_or_url2,
                                     pool));
   else
-    path2_abspath = path2;
+    abspath_or_url2 = path_or_url2;
 
-  if (!svn_path_is_url(path1))
-    SVN_ERR(svn_dirent_get_absolute(&path1_abspath, path1,
+  if (!svn_path_is_url(path_or_url1))
+    SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1,
                                     pool));
   else
-    path1_abspath = path1;
+    abspath_or_url1 = path_or_url1;
 
   /* Figure out URL1 and URL2. */
-  SVN_ERR(convert_to_url(url1, ctx->wc_ctx, path1_abspath,
+  SVN_ERR(convert_to_url(url1, ctx->wc_ctx, abspath_or_url1,
                          pool, pool));
-  SVN_ERR(convert_to_url(url2, ctx->wc_ctx, path2_abspath,
+  SVN_ERR(convert_to_url(url2, ctx->wc_ctx, abspath_or_url2,
                          pool, pool));
 
   /* We need exactly one BASE_PATH, so we'll let the BASE_PATH
-     calculated for PATH2 override the one for PATH1 (since the diff
-     will be "applied" to URL2 anyway). */
+     calculated for PATH_OR_URL2 override the one for PATH_OR_URL1
+     (since the diff will be "applied" to URL2 anyway). */
   *base_path = NULL;
-  if (strcmp(*url1, path1) != 0)
-    *base_path = path1;
-  if (strcmp(*url2, path2) != 0)
-    *base_path = path2;
+  if (strcmp(*url1, path_or_url1) != 0)
+    *base_path = path_or_url1;
+  if (strcmp(*url2, path_or_url2) != 0)
+    *base_path = path_or_url2;
 
   SVN_ERR(svn_client__open_ra_session_internal(ra_session, NULL, *url2,
                                                NULL, NULL, FALSE,
@@ -1507,50 +1538,126 @@ 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;
 
-      SVN_ERR(svn_client__repos_locations(url1, &start_ignore,
-                                          url2, &end_ignore,
-                                          *ra_session,
-                                          path2,
-                                          peg_revision,
-                                          revision1,
-                                          revision2,
-                                          ctx, pool));
-      /* Reparent the session, since *URL2 might have changed as a result
-         the above call. */
-      SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool));
+      err = svn_client__repos_locations(url1, NULL,
+                                        url2, NULL,
+                                        *ra_session,
+                                        path_or_url2,
+                                        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);
+        }
+      else
+        {
+          /* 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,
+           (path_or_url2 == *url2) ? NULL : abspath_or_url2,
            *ra_session, revision2, pool));
   SVN_ERR(svn_ra_check_path(*ra_session, "", *rev2, &kind2, pool));
-  if (kind2 == svn_node_none)
-    return svn_error_createf
-      (SVN_ERR_FS_NOT_FOUND, NULL,
-       _("'%s' was not found in the repository at revision %ld"),
-       *url2, *rev2);
 
   /* 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,
+           (strcmp(path_or_url1, *url1) == 0) ? NULL : abspath_or_url1,
            *ra_session, revision1, pool));
   SVN_ERR(svn_ra_check_path(*ra_session, "", *rev1, &kind1, pool));
-  if (kind1 == svn_node_none)
-    return svn_error_createf
-      (SVN_ERR_FS_NOT_FOUND, NULL,
-       _("'%s' was not found in the repository at revision %ld"),
-       *url1, *rev1);
+
+  /* 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 (strcmp(*url1, *url2) == 0)
+        return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+                                 _("Diff target '%s' was not found in the "
+                                   "repository at revisions '%ld' and '%ld'"),
+                                 *url1, *rev1, *rev2);
+      else
+        return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+                                 _("Diff targets '%s and '%s' were not found "
+                                   "in the repository at revisions '%ld' and "
+                                   "'%ld'"),
+                                 *url1, *url2, *rev1, *rev2);
+    }
+  else if (kind1 == svn_node_none)
+    SVN_ERR(check_diff_target_exists(*url1, *rev2, *rev1, *ra_session, pool));
+  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. */
   *anchor1 = *url1;
   *anchor2 = *url2;
   *target1 = "";
   *target2 = "";
-  if ((kind1 == svn_node_file) || (kind2 == svn_node_file))
+  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))
     {
       svn_uri_split(anchor1, target1, *url1, pool);
       svn_uri_split(anchor2, target2, *url2, pool);
@@ -1564,8 +1671,8 @@ diff_prepare_repos_repos(const char **ur
 
 /* A Theoretical Note From Ben, regarding do_diff().
 
-   This function is really svn_client_diff5().  If you read the public
-   API description for svn_client_diff5(), it sounds quite Grand.  It
+   This function is really svn_client_diff6().  If you read the public
+   API description for svn_client_diff6(), it sounds quite Grand.  It
    sounds really generalized and abstract and beautiful: that it will
    diff any two paths, be they working-copy paths or URLs, at any two
    revisions.
@@ -1585,7 +1692,7 @@ diff_prepare_repos_repos(const char **ur
    pigeonholed into one of these three use-cases, we currently bail
    with a friendly apology.
 
-   Perhaps someday a brave soul will truly make svn_client_diff5
+   Perhaps someday a brave soul will truly make svn_client_diff6()
    perfectly general.  For now, we live with the 90% case.  Certainly,
    the commandline client only calls this function in legal ways.
    When there are other users of svn_client.h, maybe this will become
@@ -1598,7 +1705,7 @@ static svn_error_t *
 unsupported_diff_error(svn_error_t *child_err)
 {
   return svn_error_create(SVN_ERR_INCORRECT_PARAMS, child_err,
-                          _("Sorry, svn_client_diff5 was called in a way "
+                          _("Sorry, svn_client_diff6 was called in a way "
                             "that is not yet supported"));
 }
 
@@ -1608,7 +1715,7 @@ unsupported_diff_error(svn_error_t *chil
    PATH1 and PATH2 are both working copy paths.  REVISION1 and
    REVISION2 are their respective revisions.
 
-   All other options are the same as those passed to svn_client_diff5(). */
+   All other options are the same as those passed to svn_client_diff6(). */
 static svn_error_t *
 diff_wc_wc(const char *path1,
            const svn_opt_revision_t *revision1,
@@ -1684,19 +1791,19 @@ diff_wc_wc(const char *path1,
 
 /* Perform a diff between two repository paths.
 
-   PATH1 and PATH2 may be either URLs or the working copy paths.
+   PATH_OR_URL1 and PATH_OR_URL2 may be either URLs or the working copy paths.
    REVISION1 and REVISION2 are their respective revisions.
-   If PEG_REVISION is specified, PATH2 is the path at the peg revision,
+   If PEG_REVISION is specified, PATH_OR_URL2 is the path at the peg revision,
    and the actual two paths compared are determined by following copy
-   history from PATH2.
+   history from PATH_OR_URL2.
 
-   All other options are the same as those passed to svn_client_diff5(). */
+   All other options are the same as those passed to svn_client_diff6(). */
 static svn_error_t *
 diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks,
                  struct diff_cmd_baton *callback_baton,
                  svn_client_ctx_t *ctx,
-                 const char *path1,
-                 const char *path2,
+                 const char *path_or_url1,
+                 const char *path_or_url2,
                  const svn_opt_revision_t *revision1,
                  const svn_opt_revision_t *revision2,
                  const svn_opt_revision_t *peg_revision,
@@ -1726,7 +1833,7 @@ 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,
+                                   &ra_session, ctx, path_or_url1, path_or_url2,
                                    revision1, revision2, peg_revision,
                                    pool));
 
@@ -1752,8 +1859,9 @@ diff_repos_repos(const svn_wc_diff_callb
      Otherwise, we just use "". */
   SVN_ERR(svn_client__get_diff_editor(
                 &diff_editor, &diff_edit_baton,
-                NULL, "", depth,
-                extra_ra_session, rev1, TRUE,
+                depth,
+                extra_ra_session, rev1, TRUE /* walk_deleted_dirs */,
+                TRUE /* text_deltas */,
                 callbacks, callback_baton,
                 ctx->cancel_func, ctx->cancel_baton,
                 NULL /* no notify_func */, NULL /* no notify_baton */,
@@ -1762,7 +1870,7 @@ diff_repos_repos(const svn_wc_diff_callb
   /* We want to switch our txn into URL2 */
   SVN_ERR(svn_ra_do_diff3
           (ra_session, &reporter, &reporter_baton, rev2, target1,
-           depth, ignore_ancestry, TRUE,
+           depth, ignore_ancestry, TRUE /* text_deltas */,
            url2, diff_editor, diff_edit_baton, pool));
 
   /* Drive the reporter; do the diff. */
@@ -1776,16 +1884,16 @@ diff_repos_repos(const svn_wc_diff_callb
 
 /* 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
+   PATH_OR_URL1 may be either a URL or a working copy path.  PATH2 is a
    working copy path.  REVISION1 and REVISION2 are their respective
    revisions.  If REVERSE is TRUE, the diff will be done in reverse.
-   If PEG_REVISION is specified, then PATH1 is the path in the peg
+   If PEG_REVISION is specified, then PATH_OR_URL1 is the path in the peg
    revision, and the actual repository path to be compared is
    determined by following copy history.
 
-   All other options are the same as those passed to svn_client_diff5(). */
+   All other options are the same as those passed to svn_client_diff6(). */
 static svn_error_t *
-diff_repos_wc(const char *path1,
+diff_repos_wc(const char *path_or_url1,
               const svn_opt_revision_t *revision1,
               const svn_opt_revision_t *peg_revision,
               const char *path2,
@@ -1811,21 +1919,21 @@ diff_repos_wc(const char *path1,
   void *diff_edit_baton;
   svn_boolean_t rev2_is_base = (revision2->kind == svn_opt_revision_base);
   svn_boolean_t server_supports_depth;
-  const char *abspath1;
+  const char *abspath_or_url1;
   const char *abspath2;
   const char *anchor_abspath;
 
   SVN_ERR_ASSERT(! svn_path_is_url(path2));
 
-  if (!svn_path_is_url(path1))
-    SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, pool));
+  if (!svn_path_is_url(path_or_url1))
+    SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, pool));
   else
-    abspath1 = path1;
+    abspath_or_url1 = path_or_url1;
 
   SVN_ERR(svn_dirent_get_absolute(&abspath2, path2, pool));
 
-  /* Convert path1 to a URL to feed to do_diff. */
-  SVN_ERR(convert_to_url(&url1, ctx->wc_ctx, abspath1, pool, pool));
+  /* Convert path_or_url1 to a URL to feed to do_diff. */
+  SVN_ERR(convert_to_url(&url1, ctx->wc_ctx, abspath_or_url1, pool, pool));
 
   SVN_ERR(svn_wc_get_actual_target2(&anchor, &target,
                                     ctx->wc_ctx, path2,
@@ -1844,17 +1952,11 @@ diff_repos_wc(const char *path1,
      actual URLs will be. */
   if (peg_revision->kind != svn_opt_revision_unspecified)
     {
-      svn_opt_revision_t *start_ignore, *end_ignore, end;
-      const char *url_ignore;
-
-      end.kind = svn_opt_revision_unspecified;
-
-      SVN_ERR(svn_client__repos_locations(&url1, &start_ignore,
-                                          &url_ignore, &end_ignore,
+      SVN_ERR(svn_client__repos_locations(&url1, NULL, NULL, NULL,
                                           NULL,
-                                          path1,
+                                          path_or_url1,
                                           peg_revision,
-                                          revision1, &end,
+                                          revision1, NULL,
                                           ctx, pool));
       if (!reverse)
         {
@@ -1904,8 +2006,8 @@ diff_repos_wc(const char *path1,
 
   /* 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,
+                                          (strcmp(path_or_url1, url1) == 0)
+                                                    ? NULL : abspath_or_url1,
                                           ra_session, revision1, pool));
 
   if (!reverse)
@@ -1942,13 +2044,13 @@ diff_repos_wc(const char *path1,
 }
 
 
-/* This is basically just the guts of svn_client_diff[_peg]5(). */
+/* This is basically just the guts of svn_client_diff[_peg]6(). */
 static svn_error_t *
 do_diff(const svn_wc_diff_callbacks4_t *callbacks,
         struct diff_cmd_baton *callback_baton,
         svn_client_ctx_t *ctx,
-        const char *path1,
-        const char *path2,
+        const char *path_or_url1,
+        const char *path_or_url2,
         const svn_opt_revision_t *revision1,
         const svn_opt_revision_t *revision2,
         const svn_opt_revision_t *peg_revision,
@@ -1963,40 +2065,42 @@ do_diff(const svn_wc_diff_callbacks4_t *
   svn_boolean_t is_repos2;
 
   /* Check if paths/revisions are urls/local. */
-  SVN_ERR(check_paths(&is_repos1, &is_repos2, path1, path2,
+  SVN_ERR(check_paths(&is_repos1, &is_repos2, path_or_url1, path_or_url2,
                       revision1, revision2, peg_revision));
 
   if (is_repos1)
     {
       if (is_repos2)
         {
+          /* ### Ignores 'show_copies_as_adds'. */
           SVN_ERR(diff_repos_repos(callbacks, callback_baton, ctx,
-                                   path1, path2, revision1, revision2,
+                                   path_or_url1, path_or_url2,
+                                   revision1, revision2,
                                    peg_revision, depth, ignore_ancestry,
                                    pool));
         }
-      else /* path2 is a working copy path */
+      else /* path_or_url2 is a working copy path */
         {
-          SVN_ERR(diff_repos_wc(path1, revision1, peg_revision,
-                                path2, revision2, FALSE, depth,
+          SVN_ERR(diff_repos_wc(path_or_url1, revision1, peg_revision,
+                                path_or_url2, revision2, FALSE, depth,
                                 ignore_ancestry, show_copies_as_adds,
                                 use_git_diff_format, changelists,
                                 callbacks, callback_baton, ctx, pool));
         }
     }
-  else /* path1 is a working copy path */
+  else /* path_or_url1 is a working copy path */
     {
       if (is_repos2)
         {
-          SVN_ERR(diff_repos_wc(path2, revision2, peg_revision,
-                                path1, revision1, TRUE, depth,
+          SVN_ERR(diff_repos_wc(path_or_url2, revision2, peg_revision,
+                                path_or_url1, revision1, TRUE, depth,
                                 ignore_ancestry, show_copies_as_adds,
                                 use_git_diff_format, changelists,
                                 callbacks, callback_baton, ctx, pool));
         }
-      else /* path2 is a working copy path */
+      else /* path_or_url2 is a working copy path */
         {
-          SVN_ERR(diff_wc_wc(path1, revision1, path2, revision2,
+          SVN_ERR(diff_wc_wc(path_or_url1, revision1, path_or_url2, revision2,
                              depth, ignore_ancestry, show_copies_as_adds,
                              use_git_diff_format, changelists,
                              callbacks, callback_baton, ctx, pool));
@@ -2006,13 +2110,71 @@ do_diff(const svn_wc_diff_callbacks4_t *
   return SVN_NO_ERROR;
 }
 
+/* Perform a summary diff between two working-copy paths.
+
+   PATH1 and PATH2 are both working copy paths.  REVISION1 and
+   REVISION2 are their respective revisions.
+
+   All other options are the same as those passed to svn_client_diff6(). */
+static svn_error_t *
+diff_summarize_wc_wc(svn_client_diff_summarize_func_t summarize_func,
+                     void *summarize_baton,
+                     const char *path1,
+                     const svn_opt_revision_t *revision1,
+                     const char *path2,
+                     const svn_opt_revision_t *revision2,
+                     svn_depth_t depth,
+                     svn_boolean_t ignore_ancestry,
+                     const apr_array_header_t *changelists,
+                     svn_client_ctx_t *ctx,
+                     apr_pool_t *pool)
+{
+  svn_wc_diff_callbacks4_t *callbacks;
+  void *callback_baton;
+  const char *abspath1, *target1;
+  svn_node_kind_t kind;
+
+  SVN_ERR_ASSERT(! svn_path_is_url(path1));
+  SVN_ERR_ASSERT(! svn_path_is_url(path2));
+
+  /* Currently we support only the case where path1 and path2 are the
+     same path. */
+  if ((strcmp(path1, path2) != 0)
+      || (! ((revision1->kind == svn_opt_revision_base)
+             && (revision2->kind == svn_opt_revision_working))))
+    return unsupported_diff_error
+      (svn_error_create
+       (SVN_ERR_INCORRECT_PARAMS, NULL,
+        _("Only diffs between a path's text-base "
+          "and its working files are supported at this time")));
+
+  /* Find the node kind of PATH1 so that we know whether the diff drive will
+     be anchored at PATH1 or its parent dir. */
+  SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, pool));
+  SVN_ERR(svn_wc_read_kind(&kind, ctx->wc_ctx, abspath1, FALSE, pool));
+  target1 = (kind == svn_node_dir) ? "" : svn_dirent_basename(path1, pool);
+  SVN_ERR(svn_client__get_diff_summarize_callbacks(
+            &callbacks, &callback_baton, target1,
+            summarize_func, summarize_baton, pool));
+
+  SVN_ERR(svn_wc_diff6(ctx->wc_ctx,
+                       abspath1,
+                       callbacks, callback_baton,
+                       depth,
+                       ignore_ancestry, FALSE /* show_copies_as_adds */,
+                       FALSE /* use_git_diff_format */, changelists,
+                       ctx->cancel_func, ctx->cancel_baton,
+                       pool));
+  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,
                            void *summarize_baton,
                            svn_client_ctx_t *ctx,
-                           const char *path1,
-                           const char *path2,
+                           const char *path_or_url1,
+                           const char *path_or_url2,
                            const svn_opt_revision_t *revision1,
                            const svn_opt_revision_t *revision2,
                            const svn_opt_revision_t *peg_revision,
@@ -2038,14 +2200,20 @@ diff_summarize_repos_repos(svn_client_di
   const char *target1;
   const char *target2;
   svn_ra_session_t *ra_session;
+  svn_wc_diff_callbacks4_t *callbacks;
+  void *callback_baton;
 
   /* 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,
+                                   path_or_url1, path_or_url2, revision1, revision2,
                                    peg_revision, pool));
 
+  SVN_ERR(svn_client__get_diff_summarize_callbacks(
+            &callbacks, &callback_baton,
+            target1, summarize_func, summarize_baton, pool));
+
   /* 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,
@@ -2053,10 +2221,13 @@ diff_summarize_repos_repos(svn_client_di
                                                TRUE, ctx, pool));
 
   /* Set up the repos_diff editor. */
-  SVN_ERR(svn_client__get_diff_summarize_editor
-          (target2, summarize_func,
-           summarize_baton, extra_ra_session, rev1, ctx->cancel_func,
-           ctx->cancel_baton, &diff_editor, &diff_edit_baton, pool));
+  SVN_ERR(svn_client__get_diff_editor(&diff_editor, &diff_edit_baton,
+            depth,
+            extra_ra_session, rev1, TRUE /* walk_deleted_dirs */,
+            FALSE /* text_deltas */,
+            callbacks, callback_baton,
+            ctx->cancel_func, ctx->cancel_baton,
+            NULL /* notify_func */, NULL /* notify_baton */, pool));
 
   /* We want to switch our txn into URL2 */
   SVN_ERR(svn_ra_do_diff3
@@ -2077,31 +2248,40 @@ static svn_error_t *
 do_diff_summarize(svn_client_diff_summarize_func_t summarize_func,
                   void *summarize_baton,
                   svn_client_ctx_t *ctx,
-                  const char *path1,
-                  const char *path2,
+                  const char *path_or_url1,
+                  const char *path_or_url2,
                   const svn_opt_revision_t *revision1,
                   const svn_opt_revision_t *revision2,
                   const svn_opt_revision_t *peg_revision,
                   svn_depth_t depth,
                   svn_boolean_t ignore_ancestry,
+                  const apr_array_header_t *changelists,
                   apr_pool_t *pool)
 {
   svn_boolean_t is_repos1;
   svn_boolean_t is_repos2;
 
   /* Check if paths/revisions are urls/local. */
-  SVN_ERR(check_paths(&is_repos1, &is_repos2, path1, path2,
+  SVN_ERR(check_paths(&is_repos1, &is_repos2, path_or_url1, path_or_url2,
                       revision1, revision2, peg_revision));
 
   if (is_repos1 && is_repos2)
     return diff_summarize_repos_repos(summarize_func, summarize_baton, ctx,
-                                      path1, path2, revision1, revision2,
+                                      path_or_url1, path_or_url2,
+                                      revision1, revision2,
                                       peg_revision, depth, ignore_ancestry,
                                       pool);
+  else if (! is_repos1 && ! is_repos2)
+    return diff_summarize_wc_wc(summarize_func, summarize_baton,
+                                path_or_url1, revision1,
+                                path_or_url2, revision2,
+                                depth, ignore_ancestry,
+                                changelists, ctx, pool);
   else
-    return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
-                            _("Summarizing diff can only compare repository "
-                              "to repository"));
+   return unsupported_diff_error(
+            svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+                             _("Summarizing diff cannot compare repository "
+                               "to WC")));
 }
 
 
@@ -2207,10 +2387,10 @@ set_up_diff_cmd_and_options(struct diff_
       * These cases require server communication.
 */
 svn_error_t *
-svn_client_diff5(const apr_array_header_t *options,
-                 const char *path1,
+svn_client_diff6(const apr_array_header_t *options,
+                 const char *path_or_url1,
                  const svn_opt_revision_t *revision1,
-                 const char *path2,
+                 const char *path_or_url2,
                  const svn_opt_revision_t *revision2,
                  const char *relative_to_dir,
                  svn_depth_t depth,
@@ -2220,8 +2400,8 @@ svn_client_diff5(const apr_array_header_
                  svn_boolean_t ignore_content_type,
                  svn_boolean_t use_git_diff_format,
                  const char *header_encoding,
-                 apr_file_t *outfile,
-                 apr_file_t *errfile,
+                 svn_stream_t *outstream,
+                 svn_stream_t *errstream,
                  const apr_array_header_t *changelists,
                  svn_client_ctx_t *ctx,
                  apr_pool_t *pool)
@@ -2233,14 +2413,14 @@ svn_client_diff5(const apr_array_header_
   peg_revision.kind = svn_opt_revision_unspecified;
 
   /* setup callback and baton */
-  diff_cmd_baton.orig_path_1 = path1;
-  diff_cmd_baton.orig_path_2 = path2;
+  diff_cmd_baton.orig_path_1 = path_or_url1;
+  diff_cmd_baton.orig_path_2 = path_or_url2;
 
   SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options,
                                       ctx->config, pool));
   diff_cmd_baton.pool = pool;
-  diff_cmd_baton.outfile = outfile;
-  diff_cmd_baton.errfile = errfile;
+  diff_cmd_baton.outstream = outstream;
+  diff_cmd_baton.errstream = errstream;
   diff_cmd_baton.header_encoding = header_encoding;
   diff_cmd_baton.revnum1 = SVN_INVALID_REVNUM;
   diff_cmd_baton.revnum2 = SVN_INVALID_REVNUM;
@@ -2257,14 +2437,15 @@ svn_client_diff5(const apr_array_header_
   diff_cmd_baton.anchor = NULL;
 
   return do_diff(&diff_callbacks, &diff_cmd_baton, ctx,
-                 path1, path2, revision1, revision2, &peg_revision,
+                 path_or_url1, path_or_url2, revision1, revision2,
+                 &peg_revision,
                  depth, ignore_ancestry, show_copies_as_adds,
                  use_git_diff_format, changelists, pool);
 }
 
 svn_error_t *
-svn_client_diff_peg5(const apr_array_header_t *options,
-                     const char *path,
+svn_client_diff_peg6(const apr_array_header_t *options,
+                     const char *path_or_url,
                      const svn_opt_revision_t *peg_revision,
                      const svn_opt_revision_t *start_revision,
                      const svn_opt_revision_t *end_revision,
@@ -2276,8 +2457,8 @@ svn_client_diff_peg5(const apr_array_hea
                      svn_boolean_t ignore_content_type,
                      svn_boolean_t use_git_diff_format,
                      const char *header_encoding,
-                     apr_file_t *outfile,
-                     apr_file_t *errfile,
+                     svn_stream_t *outstream,
+                     svn_stream_t *errstream,
                      const apr_array_header_t *changelists,
                      svn_client_ctx_t *ctx,
                      apr_pool_t *pool)
@@ -2285,14 +2466,14 @@ svn_client_diff_peg5(const apr_array_hea
   struct diff_cmd_baton diff_cmd_baton = { 0 };
 
   /* setup callback and baton */
-  diff_cmd_baton.orig_path_1 = path;
-  diff_cmd_baton.orig_path_2 = path;
+  diff_cmd_baton.orig_path_1 = path_or_url;
+  diff_cmd_baton.orig_path_2 = path_or_url;
 
   SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options,
                                       ctx->config, pool));
   diff_cmd_baton.pool = pool;
-  diff_cmd_baton.outfile = outfile;
-  diff_cmd_baton.errfile = errfile;
+  diff_cmd_baton.outstream = outstream;
+  diff_cmd_baton.errstream = errstream;
   diff_cmd_baton.header_encoding = header_encoding;
   diff_cmd_baton.revnum1 = SVN_INVALID_REVNUM;
   diff_cmd_baton.revnum2 = SVN_INVALID_REVNUM;
@@ -2309,15 +2490,16 @@ svn_client_diff_peg5(const apr_array_hea
   diff_cmd_baton.anchor = NULL;
 
   return do_diff(&diff_callbacks, &diff_cmd_baton, ctx,
-                 path, path, start_revision, end_revision, peg_revision,
+                 path_or_url, path_or_url, start_revision, end_revision,
+                 peg_revision,
                  depth, ignore_ancestry, show_copies_as_adds,
                  use_git_diff_format, changelists, pool);
 }
 
 svn_error_t *
-svn_client_diff_summarize2(const char *path1,
+svn_client_diff_summarize2(const char *path_or_url1,
                            const svn_opt_revision_t *revision1,
-                           const char *path2,
+                           const char *path_or_url2,
                            const svn_opt_revision_t *revision2,
                            svn_depth_t depth,
                            svn_boolean_t ignore_ancestry,
@@ -2331,14 +2513,14 @@ svn_client_diff_summarize2(const char *p
   svn_opt_revision_t peg_revision;
   peg_revision.kind = svn_opt_revision_unspecified;
 
-  /* ### CHANGELISTS parameter isn't used */
   return do_diff_summarize(summarize_func, summarize_baton, ctx,
-                           path1, path2, revision1, revision2, &peg_revision,
-                           depth, ignore_ancestry, pool);
+                           path_or_url1, path_or_url2, revision1, revision2,
+                           &peg_revision,
+                           depth, ignore_ancestry, changelists, pool);
 }
 
 svn_error_t *
-svn_client_diff_summarize_peg2(const char *path,
+svn_client_diff_summarize_peg2(const char *path_or_url,
                                const svn_opt_revision_t *peg_revision,
                                const svn_opt_revision_t *start_revision,
                                const svn_opt_revision_t *end_revision,
@@ -2350,10 +2532,10 @@ svn_client_diff_summarize_peg2(const cha
                                svn_client_ctx_t *ctx,
                                apr_pool_t *pool)
 {
-  /* ### CHANGELISTS parameter isn't used */
   return do_diff_summarize(summarize_func, summarize_baton, ctx,
-                           path, path, start_revision, end_revision,
-                           peg_revision, depth, ignore_ancestry, pool);
+                           path_or_url, path_or_url,
+                           start_revision, end_revision, peg_revision,
+                           depth, ignore_ancestry, changelists, pool);
 }
 
 svn_client_diff_summarize_t *

Modified: subversion/branches/svn-bisect/subversion/libsvn_client/export.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-bisect/subversion/libsvn_client/export.c?rev=1204374&r1=1204373&r2=1204374&view=diff
==============================================================================
--- subversion/branches/svn-bisect/subversion/libsvn_client/export.c (original)
+++ subversion/branches/svn-bisect/subversion/libsvn_client/export.c Mon Nov 21 07:07:56 2011
@@ -1039,7 +1039,6 @@ svn_client_export5(svn_revnum_t *result_
                    apr_pool_t *pool)
 {
   svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
-  const char *url;
   svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url);
 
   SVN_ERR_ASSERT(peg_revision != NULL);
@@ -1056,10 +1055,10 @@ svn_client_export5(svn_revnum_t *result_
   if (from_is_url || ! SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind))
     {
       svn_revnum_t revnum;
+      const char *url;
       svn_ra_session_t *ra_session;
       svn_node_kind_t kind;
       struct edit_baton *eb = apr_pcalloc(pool, sizeof(*eb));
-      const char *repos_root_url;
 
       /* Get the RA connection. */
       SVN_ERR(svn_client__ra_session_from_path(&ra_session, &revnum,
@@ -1067,9 +1066,6 @@ svn_client_export5(svn_revnum_t *result_
                                                peg_revision,
                                                revision, ctx, pool));
 
-      /* Get the repository root. */
-      SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
-
       eb->root_path = to_path;
       eb->root_url = url;
       eb->force = overwrite;
@@ -1164,6 +1160,8 @@ svn_client_export5(svn_revnum_t *result_
           void *report_baton;
           svn_delta_editor_t *editor = svn_delta_default_editor(pool);
           svn_boolean_t use_sleep = FALSE;
+          svn_delta_shim_callbacks_t *shim_callbacks =
+                                    svn_delta_shim_callbacks_default(pool);
 
           editor->set_target_revision = set_target_revision;
           editor->open_root = open_root;
@@ -1182,6 +1180,9 @@ svn_client_export5(svn_revnum_t *result_
                                                     &edit_baton,
                                                     pool));
 
+          SVN_ERR(svn_editor__insert_shims(&export_editor, &edit_baton,
+                                           export_editor, edit_baton,
+                                           shim_callbacks, pool, pool));
 
           /* Manufacture a basic 'report' to the update reporter. */
           SVN_ERR(svn_ra_do_update2(ra_session,
@@ -1219,8 +1220,10 @@ svn_client_export5(svn_revnum_t *result_
 
           if (! ignore_externals && depth == svn_depth_infinity)
             {
+              const char *repos_root_url;
               const char *to_abspath;
 
+              SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
               SVN_ERR(svn_dirent_get_absolute(&to_abspath, to_path, pool));
               SVN_ERR(svn_client__export_externals(eb->externals,
                                                    from_path_or_url,



Mime
View raw message