You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@subversion.apache.org by Prabhu Gnana Sundar <pr...@collab.net> on 2010/11/29 08:13:20 UTC

[PATCH] enhance "svn diff" by adding "--diff-copy-from" switch

Hi,

This patch enhances the current "svn diff" by adding a new
"--diff-copy-from" switch. I have attached the patch and the log message
along with this mail. Please review and share your comments on the same.

I am resending this patch with the extension added to the log and the
patch files.

Regards
Prabhu

Re: [PATCH] enhance "svn diff" by adding "--diff-copy-from" switch

Posted by Stefan Sperling <st...@elego.de>.
On Mon, Nov 29, 2010 at 01:43:20PM +0530, Prabhu Gnana Sundar wrote:
> Hi,
> 
> This patch enhances the current "svn diff" by adding a new
> "--diff-copy-from" switch. I have attached the patch and the log message
> along with this mail. Please review and share your comments on the same.
> 
> I am resending this patch with the extension added to the log and the
> patch files.
> 
> Regards
> Prabhu


Hi Prabhu,

very impressive patch!
I haven't tested it. But I've read over it and added a couple of remarks
below.

> Index: subversion/libsvn_ra/deprecated.c
> ===================================================================
> --- subversion/libsvn_ra/deprecated.c	(revision 1038978)
> +++ subversion/libsvn_ra/deprecated.c	(working copy)
> @@ -248,6 +248,31 @@
>                                     keep_locks, pool);
>  }
>  
> +svn_error_t *svn_ra_do_diff3(svn_ra_session_t *session,
> +                             const svn_ra_reporter3_t **reporter,
> +                             void **report_baton,
> +                             svn_revnum_t revision,
> +                             const char *diff_target,
> +                             svn_depth_t depth,
> +                             svn_boolean_t ignore_ancestry,
> +                             svn_boolean_t text_deltas,
> +                             const char *versus_url,
> +                             const svn_delta_editor_t *diff_editor,
> +                             void *diff_baton,
> +                             apr_pool_t *pool)
> +{
> +  SVN_ERR_ASSERT(svn_path_is_empty(diff_target)
> +                 || svn_path_is_single_path_component(diff_target));
> +  return session->vtable->do_diff(session,
> +                                  reporter, report_baton,
> +                                  revision, diff_target,
> +                                  depth, ignore_ancestry,
> +                                  text_deltas, FALSE /* diff copy from */,
> +                                  versus_url, diff_editor,
> +                                  diff_baton, pool);
> +}
> +
> +
>  svn_error_t *svn_ra_do_diff2(svn_ra_session_t *session,
>                               const svn_ra_reporter2_t **reporter,
>                               void **report_baton,
> @@ -270,8 +295,8 @@
>                                    &(b->reporter3), &(b->reporter3_baton),
>                                    revision, diff_target,
>                                    SVN_DEPTH_INFINITY_OR_FILES(recurse),
> -                                  ignore_ancestry, text_deltas, versus_url,
> -                                  diff_editor, diff_baton, pool);
> +                                  ignore_ancestry, text_deltas, FALSE,
> +                                  versus_url, diff_editor, diff_baton, pool);
>  }
>  
>  svn_error_t *svn_ra_do_diff(svn_ra_session_t *session,
> Index: subversion/libsvn_ra/wrapper_template.h
> ===================================================================
> --- subversion/libsvn_ra/wrapper_template.h	(revision 1038978)
> +++ subversion/libsvn_ra/wrapper_template.h	(working copy)
> @@ -361,7 +361,7 @@
>    svn_depth_t depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
>  
>    SVN_ERR(VTBL.do_diff(session_baton, &reporter3, &baton3, revision,
> -                       diff_target, depth, ignore_ancestry, TRUE,
> +                       diff_target, depth, ignore_ancestry, TRUE, FALSE,
>                         versus_url, diff_editor, diff_baton, pool));
>  
>    compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
> Index: subversion/libsvn_ra/ra_loader.c
> ===================================================================
> --- subversion/libsvn_ra/ra_loader.c	(revision 1038978)
> +++ subversion/libsvn_ra/ra_loader.c	(working copy)
> @@ -845,7 +845,7 @@
>                                      status_editor, status_baton, pool);
>  }
>  
> -svn_error_t *svn_ra_do_diff3(svn_ra_session_t *session,
> +svn_error_t *svn_ra_do_diff4(svn_ra_session_t *session,
>                               const svn_ra_reporter3_t **reporter,
>                               void **report_baton,
>                               svn_revnum_t revision,
> @@ -853,6 +853,7 @@
>                               svn_depth_t depth,
>                               svn_boolean_t ignore_ancestry,
>                               svn_boolean_t text_deltas,
> +                             svn_boolean_t diff_copy_from,
>                               const char *versus_url,
>                               const svn_delta_editor_t *diff_editor,
>                               void *diff_baton,
> @@ -864,7 +865,8 @@
>                                    reporter, report_baton,
>                                    revision, diff_target,
>                                    depth, ignore_ancestry,
> -                                  text_deltas, versus_url, diff_editor,
> +                                  text_deltas, diff_copy_from,
> +                                  versus_url, diff_editor,
>                                    diff_baton, pool);
>  }
>  
> Index: subversion/libsvn_ra/ra_loader.h
> ===================================================================
> --- subversion/libsvn_ra/ra_loader.h	(revision 1038978)
> +++ subversion/libsvn_ra/ra_loader.h	(working copy)
> @@ -159,10 +159,12 @@
>                            svn_depth_t depth,
>                            svn_boolean_t ignore_ancestry,
>                            svn_boolean_t text_deltas,
> +                          svn_boolean_t diff_copy_from,
>                            const char *versus_url,
>                            const svn_delta_editor_t *diff_editor,
>                            void *diff_baton,
>                            apr_pool_t *pool);
> +
>    svn_error_t *(*get_log)(svn_ra_session_t *session,
>                            const apr_array_header_t *paths,
>                            svn_revnum_t start,
> Index: subversion/libsvn_ra_local/ra_plugin.c
> ===================================================================
> --- subversion/libsvn_ra_local/ra_plugin.c	(revision 1038978)
> +++ subversion/libsvn_ra_local/ra_plugin.c	(working copy)
> @@ -819,6 +819,7 @@
>                        svn_depth_t depth,
>                        svn_boolean_t ignore_ancestry,
>                        svn_boolean_t text_deltas,
> +                      svn_boolean_t diff_copy_from,
>                        const char *switch_url,
>                        const svn_delta_editor_t *update_editor,
>                        void *update_baton,
> @@ -832,7 +833,7 @@
>                         switch_url,
>                         text_deltas,
>                         depth,
> -                       FALSE,
> +                       diff_copy_from ? TRUE : FALSE,
>                         ignore_ancestry,
>                         update_editor,
>                         update_baton,
> Index: subversion/libsvn_ra_svn/client.c
> ===================================================================
> --- subversion/libsvn_ra_svn/client.c	(revision 1038978)
> +++ subversion/libsvn_ra_svn/client.c	(working copy)
> @@ -1316,6 +1316,7 @@
>                                  svn_depth_t depth,
>                                  svn_boolean_t ignore_ancestry,
>                                  svn_boolean_t text_deltas,
> +                                svn_boolean_t diff_copy_from,
>                                  const char *versus_url,
>                                  const svn_delta_editor_t *diff_editor,
>                                  void *diff_baton, apr_pool_t *pool)
> @@ -1325,10 +1326,10 @@
>    svn_boolean_t recurse = DEPTH_TO_RECURSE(depth);
>  
>    /* Tell the server we want to start a diff. */
> -  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "diff", "(?r)cbbcbw", rev,
> +  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "diff", "(?r)cbbcbwb", rev,
>                                 target, recurse, ignore_ancestry,
>                                 versus_url, text_deltas,
> -                               svn_depth_to_word(depth)));
> +                               svn_depth_to_word(depth), diff_copy_from));
>    SVN_ERR(handle_auth_request(sess_baton, pool));
>  
>    /* Fetch a reporter for the caller to drive.  The reporter will drive
> Index: subversion/svn/cl.h
> ===================================================================
> --- subversion/svn/cl.h	(revision 1038978)
> +++ subversion/svn/cl.h	(working copy)
> @@ -184,6 +184,7 @@
>    svn_boolean_t no_ignore;       /* disregard default ignores & svn:ignore's */
>    svn_boolean_t no_auth_cache;   /* do not cache authentication information */
>    svn_boolean_t no_diff_deleted; /* do not show diffs for deleted files */
> +  svn_boolean_t diff_copy_from;  /* diff from the source */
>    svn_boolean_t show_copies_as_adds; /* do not diff copies with their source */
>    svn_boolean_t notice_ancestry; /* notice ancestry for diff-y operations */
>    svn_boolean_t ignore_ancestry; /* ignore ancestry for merge-y operations */
> Index: subversion/svn/diff-cmd.c
> ===================================================================
> --- subversion/svn/diff-cmd.c	(revision 1038978)
> +++ subversion/svn/diff-cmd.c	(working copy)
> @@ -347,6 +347,7 @@
>                       opt_state->depth,
>                       ! opt_state->notice_ancestry,
>                       opt_state->no_diff_deleted,
> +                     opt_state->diff_copy_from,
>                       opt_state->show_copies_as_adds,
>                       opt_state->force,
>                       opt_state->use_git_diff_format,
> @@ -392,6 +393,7 @@
>                       opt_state->depth,
>                       ! opt_state->notice_ancestry,
>                       opt_state->no_diff_deleted,
> +                     opt_state->diff_copy_from,
>                       opt_state->show_copies_as_adds,
>                       opt_state->force,
>                       opt_state->use_git_diff_format,
> Index: subversion/svn/log-cmd.c
> ===================================================================
> --- subversion/svn/log-cmd.c	(revision 1038978)
> +++ subversion/svn/log-cmd.c	(working copy)
> @@ -303,6 +303,7 @@
>                               svn_depth_infinity,
>                               FALSE, /* ignore ancestry */
>                               TRUE, /* no diff deleted */
> +                             FALSE, /* diff copy from */
>                               FALSE, /* show copies as adds */
>                               FALSE, /* ignore content type */
>                               FALSE, /* use git diff format */
> @@ -336,6 +337,7 @@
>                                           svn_depth_infinity,
>                                           FALSE, /* ignore ancestry */
>                                           TRUE, /* no diff deleted */
> +                                         FALSE, /* diff copy from */
>                                           FALSE, /* show copies as adds */
>                                           FALSE, /* ignore content type */
>                                           FALSE, /* use git diff format */
> Index: subversion/svn/main.c
> ===================================================================
> --- subversion/svn/main.c	(revision 1038978)
> +++ subversion/svn/main.c	(working copy)
> @@ -88,6 +88,7 @@
>    opt_no_auth_cache,
>    opt_no_autoprops,
>    opt_no_diff_deleted,
> +  opt_diff_copy_from,
>    opt_no_ignore,
>    opt_no_unlock,
>    opt_non_interactive,
> @@ -232,6 +233,8 @@
>                      N_("try operation but make no changes")},
>    {"no-diff-deleted", opt_no_diff_deleted, 0,
>                      N_("do not print differences for deleted files")},
> +  {"diff-copy-from", opt_diff_copy_from, 0,
> +                    N_("print copy history of files")},

Maybe say N_("diff copied files against their source") or
N_("follow copy history of files")?

>    {"notice-ancestry", opt_notice_ancestry, 0,
>                      N_("notice ancestry when calculating differences")},
>    {"ignore-ancestry", opt_ignore_ancestry, 0,
> @@ -352,6 +355,7 @@
>    {"nac",           opt_no_auth_cache, 0, NULL},
>    {"dry",           opt_dry_run, 0, NULL},
>    {"ndd",           opt_no_diff_deleted, 0, NULL},
> +  {"dcf",           opt_diff_copy_from, 0, NULL},
>    {"na",            opt_notice_ancestry, 0, NULL},
>    {"ia",            opt_ignore_ancestry, 0, NULL},
>    {"ie",            opt_ignore_externals, 0, NULL},
> @@ -543,8 +547,9 @@
>       "\n"
>       "  Use just 'svn diff' to display local modifications in a working copy.\n"),
>      {'r', 'c', opt_old_cmd, opt_new_cmd, 'N', opt_depth, opt_diff_cmd,
> -     opt_internal_diff, 'x', opt_no_diff_deleted, opt_show_copies_as_adds,
> -     opt_notice_ancestry, opt_summarize, opt_changelist, opt_force, opt_xml,
> +     opt_internal_diff, 'x', opt_no_diff_deleted, opt_diff_copy_from,
> +     opt_show_copies_as_adds, opt_notice_ancestry, opt_summarize,
> +     opt_changelist, opt_force, opt_xml,
>       opt_use_git_diff_format} },
>    { "export", svn_cl__export, {0}, N_
>      ("Create an unversioned copy of a tree.\n"
> @@ -1624,6 +1629,9 @@
>        case opt_no_diff_deleted:
>          opt_state.no_diff_deleted = TRUE;
>          break;
> +      case opt_diff_copy_from:
> +        opt_state.diff_copy_from = TRUE;
> +        break;
>        case opt_show_copies_as_adds:
>          opt_state.show_copies_as_adds = TRUE;
>          break;
> Index: subversion/include/svn_client.h
> ===================================================================
> --- subversion/include/svn_client.h	(revision 1038978)
> +++ subversion/include/svn_client.h	(working copy)
> @@ -2633,6 +2633,9 @@
>   *
>   * If @a no_diff_deleted is TRUE, then no diff output will be
>   * generated on deleted files.
> + * 
> + * If @a diff_copy_from is TRUE, then the diff output will be generated
> + * with respect to the copy-source.

Maybe say:

* If @a diff_copy_from is TRUE, diff copied files against the copyfrom
* source, instead of diffing against the history of the file's path.

>   *
>   * If @a show_copies_as_adds is TRUE, then copied files will not be diffed
>   * against their copyfrom source, and will appear in the diff output
> @@ -2684,6 +2687,7 @@
>                   svn_depth_t depth,
>                   svn_boolean_t ignore_ancestry,
>                   svn_boolean_t no_diff_deleted,
> +                 svn_boolean_t diff_copy_from,
>                   svn_boolean_t show_copies_as_adds,
>                   svn_boolean_t ignore_content_type,
>                   svn_boolean_t use_git_diff_format,
> @@ -2797,6 +2801,9 @@
>   * changed between @a start_revision and @a end_revision.  @a path can
>   * be either a working-copy path or URL.
>   *
> + * If @a diff_copy_from is TRUE, then the diff output will be generated
> + * with respect to the copy-source.
> + *

See above.

>   * If @a peg_revision is #svn_opt_revision_unspecified, behave
>   * identically to svn_client_diff5(), using @a path for both of that
>   * function's @a path1 and @a path2 argments.
> @@ -2815,6 +2822,7 @@
>                       svn_depth_t depth,
>                       svn_boolean_t ignore_ancestry,
>                       svn_boolean_t no_diff_deleted,
> +                     svn_boolean_t diff_copy_from,
>                       svn_boolean_t show_copies_as_adds,
>                       svn_boolean_t ignore_content_type,
>                       svn_boolean_t use_git_diff_format,
> Index: subversion/include/svn_ra.h
> ===================================================================
> --- subversion/include/svn_ra.h	(revision 1038978)
> +++ subversion/include/svn_ra.h	(working copy)
> @@ -1336,6 +1336,8 @@
>   * handler returned by apply_textdelta will be called once with a NULL
>   * @c svn_txdelta_window_t pointer.
>   *
> + * Pass TRUE to @a diff_copy_from to do the diff against the copy-source.
> + *
>   * Use @a pool for memory allocation.
>   *
>   * @note The reporter provided by this function does NOT supply copy-
> @@ -1345,10 +1347,10 @@
>   * needed, and sending too much data back, a pre-1.5 'recurse'
>   * directive may be sent to the server, based on @a depth.
>   *
> - * @since New in 1.5.
> + * @since New in 1.7.
>   */
>  svn_error_t *
> -svn_ra_do_diff3(svn_ra_session_t *session,
> +svn_ra_do_diff4(svn_ra_session_t *session,
>                  const svn_ra_reporter3_t **reporter,
>                  void **report_baton,
>                  svn_revnum_t revision,
> @@ -1356,12 +1358,35 @@
>                  svn_depth_t depth,
>                  svn_boolean_t ignore_ancestry,
>                  svn_boolean_t text_deltas,
> +                svn_boolean_t diff_copy_from,
>                  const char *versus_url,
>                  const svn_delta_editor_t *diff_editor,
>                  void *diff_baton,
>                  apr_pool_t *pool);
>  
>  /**
> + * Similar to svn_ra_do_diff4(), but not taking the @a diff_copy_from
> + * option, hence passing FALSE.
> + *
> + * New code should use svn_ra_do_diff4(). 
> + *
> + * @deprecated Provided for compatibility with the 1.5 API.
> + */
> +SVN_DEPRECATED
> +svn_error_t *
> +svn_ra_do_diff3(svn_ra_session_t *session,
> +                const svn_ra_reporter3_t **reporter,
> +                void **report_baton,
> +                svn_revnum_t revision,
> +                const char *diff_target,
> +                svn_depth_t depth,
> +                svn_boolean_t ignore_ancestry,
> +                svn_boolean_t text_deltas,
> +                const char *versus_url,
> +                const svn_delta_editor_t *diff_editor,
> +                void *diff_baton,
> +                apr_pool_t *pool);
> +/**
>   * Similar to svn_ra_do_diff3(), but taking @c svn_ra_reporter2_t
>   * instead of @c svn_ra_reporter3_t, and therefore only able to report
>   * @c svn_depth_infinity for depths.  Perform the diff according to
> Index: subversion/libsvn_client/deprecated.c
> ===================================================================
> --- subversion/libsvn_client/deprecated.c	(revision 1038978)
> +++ subversion/libsvn_client/deprecated.c	(working copy)
> @@ -785,7 +785,7 @@
>  {
>    return svn_client_diff5(options, path1, revision1, path2,
>                            revision2, relative_to_dir, depth,
> -                          ignore_ancestry, no_diff_deleted, FALSE,
> +                          ignore_ancestry, no_diff_deleted, FALSE, FALSE,
>                            FALSE, ignore_content_type, header_encoding,
>                            outfile, errfile, changelists, ctx, pool);
>  }
> @@ -883,6 +883,7 @@
>                                no_diff_deleted,
>                                FALSE,
>                                FALSE,
> +                              FALSE,
>                                ignore_content_type,
>                                header_encoding,
>                                outfile,
> Index: subversion/libsvn_client/repos_diff.c
> ===================================================================
> --- subversion/libsvn_client/repos_diff.c	(revision 1038978)
> +++ subversion/libsvn_client/repos_diff.c	(working copy)
> @@ -93,6 +93,9 @@
>       FALSE otherwise. */
>    svn_boolean_t walk_deleted_repos_dirs;
>  
> +  /* TRUE if diff has to be made against the copy source. */
> +  svn_boolean_t diff_copy_from;
> +
>    /* A callback used to see if the client wishes to cancel the running
>       operation. */
>    svn_cancel_func_t cancel_func;
> @@ -198,6 +201,9 @@
>    /* A cache of any property changes (svn_prop_t) received for this file. */
>    apr_array_header_t *propchanges;
>  
> +  /* The copyfrom_revision */ 

The above comment just repeats the variable name.
This comment is not terribly important because the variable name has
a clear meaning, but maybe say:

  /* The copyfrom revision. */ 

> +  svn_revnum_t copyfrom_revision;
> +
>    /* The pool passed in by add_file or open_file.
>       Also, the pool this file_baton is allocated in. */
>    apr_pool_t *pool;
> @@ -308,7 +314,9 @@
>   * the file.
>   */
>  static svn_error_t *
> -get_file_from_ra(struct file_baton *b, svn_revnum_t revision)
> +get_file_from_ra(struct file_baton *b,
> +                 const char *path,
> +                 svn_revnum_t revision)
>  {
>    svn_stream_t *fstream;
>  
> @@ -317,7 +325,7 @@
>                                   b->pool));
>  
>    SVN_ERR(svn_ra_get_file(b->edit_baton->ra_session,
> -                          b->path,
> +                          path,
>                            revision,
>                            fstream, NULL,
>                            &(b->pristine_props),
> @@ -508,7 +516,7 @@
>  
>            /* Compare a file being deleted against an empty file */
>            b = make_file_baton(path, FALSE, eb, iterpool);
> -          SVN_ERR(get_file_from_ra(b, revision));
> +          SVN_ERR(get_file_from_ra(b, path, revision));
>  
>            SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
>        
> @@ -573,7 +581,7 @@
>  
>              /* Compare a file being deleted against an empty file */
>              b = make_file_baton(path, FALSE, eb, pool);
> -            SVN_ERR(get_file_from_ra(b, eb->revision));
> +            SVN_ERR(get_file_from_ra(b, path, eb->revision));
>              SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
>  
>              get_file_mime_types(&mimetype1, &mimetype2, b);
> @@ -791,8 +799,6 @@
>    struct dir_baton *pb = parent_baton;
>    struct file_baton *b;
>  
> -  /* ### TODO: support copyfrom? */
> -
>    b = make_file_baton(path, TRUE, pb->edit_baton, pool);
>    *file_baton = b;
>  
> @@ -804,8 +810,28 @@
>        return SVN_NO_ERROR;
>      }
>  
> -  SVN_ERR(get_empty_file(b->edit_baton, &(b->path_start_revision)));
> -  b->pristine_props = pb->edit_baton->empty_hash;
> +  if(pb->edit_baton->diff_copy_from && SVN_IS_VALID_REVNUM(copyfrom_revision))
> +    {
> +      const char *preserved_url;
> +      const char *repos_root;
> +      b->copyfrom_revision = copyfrom_revision;
> +      SVN_ERR(svn_ra_get_session_url(pb->edit_baton->ra_session,
> +                                     &preserved_url,
> +                                     pool));
> +      SVN_ERR(svn_ra_get_repos_root2(pb->edit_baton->ra_session,
> +                                     &repos_root,
> +                                     pool));
> +      SVN_ERR(svn_ra_reparent(pb->edit_baton->ra_session, repos_root,
> +                              pool));

Reparenting can fail due to authz restrictions.
Please catch related errors (see subversion/svn/log-cmd.c for an example).
Maybe even add a regression test that makes sure this feature can cope with
copyfrom paths that aren't accessible due to authz restrictions.


> +      SVN_ERR(get_file_from_ra(b, copyfrom_path+1, copyfrom_revision));
> +      SVN_ERR(svn_ra_reparent(pb->edit_baton->ra_session,
> +                              preserved_url, pool));
> +    }
> +  else
> +    {
> +      SVN_ERR(get_empty_file(b->edit_baton, &(b->path_start_revision)));
> +      b->pristine_props = pb->edit_baton->empty_hash;
> +    }
>  
>    return SVN_NO_ERROR;
>  }
> @@ -831,7 +857,7 @@
>        return SVN_NO_ERROR;
>      }
>  
> -  SVN_ERR(get_file_from_ra(b, base_revision));
> +  SVN_ERR(get_file_from_ra(b, path, base_revision));
>  
>    return SVN_NO_ERROR;
>  }
> @@ -962,7 +988,7 @@
>        const char *mimetype1, *mimetype2;
>        get_file_mime_types(&mimetype1, &mimetype2, b);
>  
> -      if (b->added)
> +      if (b->added && !eb->diff_copy_from)
>          SVN_ERR(eb->diff_callbacks->file_added
>                  (local_dir_abspath, &content_state, &prop_state, &b->tree_conflicted,
>                   b->wcpath,
> @@ -981,7 +1007,7 @@
>                   &b->tree_conflicted, b->wcpath,
>                   b->path_end_revision ? b->path_start_revision : NULL,
>                   b->path_end_revision,
> -                 b->edit_baton->revision,
> +                 eb->diff_copy_from ? b->copyfrom_revision : b->edit_baton->revision,
>                   b->edit_baton->target_revision,
>                   mimetype1, mimetype2,
>                   b->propchanges, b->pristine_props,
> @@ -1297,6 +1323,7 @@
>                              svn_revnum_t revision,
>                              svn_wc_notify_func2_t notify_func,
>                              void *notify_baton,
> +                            svn_boolean_t diff_copy_from,
>                              svn_cancel_func_t cancel_func,
>                              void *cancel_baton,
>                              const svn_delta_editor_t **editor,
> @@ -1323,6 +1350,7 @@
>    eb->notify_func = notify_func;
>    eb->notify_baton = notify_baton;
>    eb->walk_deleted_repos_dirs = TRUE;
> +  eb->diff_copy_from = diff_copy_from;
>    eb->cancel_func = cancel_func;
>    eb->cancel_baton = cancel_baton;
>  
> Index: subversion/libsvn_client/client.h
> ===================================================================
> --- subversion/libsvn_client/client.h	(revision 1038978)
> +++ subversion/libsvn_client/client.h	(working copy)
> @@ -644,6 +644,7 @@
>                              svn_revnum_t revision,
>                              svn_wc_notify_func2_t notify_func,
>                              void *notify_baton,
> +                            svn_boolean_t diff_copy_from,
>                              svn_cancel_func_t cancel_func,
>                              void *cancel_baton,
>                              const svn_delta_editor_t **editor,
> Index: subversion/libsvn_client/merge.c
> ===================================================================
> --- subversion/libsvn_client/merge.c	(revision 1038978)
> +++ subversion/libsvn_client/merge.c	(working copy)
> @@ -5052,14 +5052,14 @@
>                                        merge_b->dry_run,
>                                        merge_b->ra_session2, revision1,
>                                        notification_receiver, notify_b,
> -                                      merge_b->ctx->cancel_func,
> +                                      FALSE, merge_b->ctx->cancel_func,
>                                        merge_b->ctx->cancel_baton,
>                                        &diff_editor, &diff_edit_baton,
>                                        pool));
> -  SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
> +  SVN_ERR(svn_ra_do_diff4(merge_b->ra_session1,
>                            &reporter, &report_baton, revision2,
>                            "", depth, merge_b->ignore_ancestry,
> -                          TRUE,  /* text_deltas */
> +                          TRUE /* text_deltas */, FALSE /*diff_copy_from*/,

style nit: need more spaces in comment, like this: /* diff_copy_from */

>                            url2, diff_editor, diff_edit_baton, pool));
>  
>    /* Drive the reporter. */
> Index: subversion/libsvn_client/diff.c
> ===================================================================
> --- subversion/libsvn_client/diff.c	(revision 1038978)
> +++ subversion/libsvn_client/diff.c	(working copy)
> @@ -1681,6 +1681,7 @@
>                   const svn_opt_revision_t *peg_revision,
>                   svn_depth_t depth,
>                   svn_boolean_t ignore_ancestry,
> +                 svn_boolean_t diff_copy_from,
>                   apr_pool_t *pool)
>  {
>    svn_ra_session_t *extra_ra_session;
> @@ -1733,13 +1734,13 @@
>             NULL, callbacks, callback_baton, depth,
>             FALSE /* doesn't matter for diff */, extra_ra_session, rev1,
>             NULL /* no notify_func */, NULL /* no notify_baton */,
> -           ctx->cancel_func, ctx->cancel_baton,
> +           diff_copy_from, ctx->cancel_func, ctx->cancel_baton,
>             &diff_editor, &diff_edit_baton, pool));
>  
>    /* We want to switch our txn into URL2 */
> -  SVN_ERR(svn_ra_do_diff3
> +  SVN_ERR(svn_ra_do_diff4
>            (ra_session, &reporter, &reporter_baton, rev2, target1,
> -           depth, ignore_ancestry, TRUE,
> +           depth, ignore_ancestry, TRUE, diff_copy_from,
>             url2, diff_editor, diff_edit_baton, pool));
>  
>    /* Drive the reporter; do the diff. */
> @@ -1770,6 +1771,7 @@
>                svn_boolean_t reverse,
>                svn_depth_t depth,
>                svn_boolean_t ignore_ancestry,
> +              svn_boolean_t diff_copy_from,
>                svn_boolean_t show_copies_as_adds,
>                svn_boolean_t use_git_diff_format,
>                const apr_array_header_t *changelists,
> @@ -1878,13 +1880,14 @@
>    else
>      callback_baton->revnum2 = rev;
>  
> -  SVN_ERR(svn_ra_do_diff3(ra_session,
> +  SVN_ERR(svn_ra_do_diff4(ra_session,
>                            &reporter, &reporter_baton,
>                            rev,
>                            target ? svn_path_uri_decode(target, pool) : NULL,
>                            depth,
>                            ignore_ancestry,
>                            TRUE,  /* text_deltas */
> +                          diff_copy_from,
>                            url1,
>                            diff_editor, diff_edit_baton, pool));
>  
> @@ -1915,6 +1918,7 @@
>          const svn_opt_revision_t *peg_revision,
>          svn_depth_t depth,
>          svn_boolean_t ignore_ancestry,
> +        svn_boolean_t diff_copy_from,
>          svn_boolean_t show_copies_as_adds,
>          svn_boolean_t use_git_diff_format,
>          const apr_array_header_t *changelists,
> @@ -1934,15 +1938,16 @@
>            SVN_ERR(diff_repos_repos(callbacks, callback_baton, ctx,
>                                     path1, path2, revision1, revision2,
>                                     peg_revision, depth, ignore_ancestry,
> -                                   pool));
> +                                   diff_copy_from, pool));
>          }
>        else /* path2 is a working copy path */
>          {
>            SVN_ERR(diff_repos_wc(path1, revision1, peg_revision,
>                                  path2, revision2, FALSE, depth,
> -                                ignore_ancestry, show_copies_as_adds,
> -                                use_git_diff_format, changelists,
> -                                callbacks, callback_baton, ctx, pool));
> +                                ignore_ancestry, diff_copy_from,
> +                                show_copies_as_adds, use_git_diff_format,
> +                                changelists, callbacks, callback_baton,
> +                                ctx, pool));
>          }
>      }
>    else /* path1 is a working copy path */
> @@ -1951,9 +1956,10 @@
>          {
>            SVN_ERR(diff_repos_wc(path2, revision2, peg_revision,
>                                  path1, revision1, TRUE, depth,
> -                                ignore_ancestry, show_copies_as_adds,
> -                                use_git_diff_format, changelists,
> -                                callbacks, callback_baton, ctx, pool));
> +                                ignore_ancestry, diff_copy_from,
> +                                show_copies_as_adds, use_git_diff_format,
> +                                changelists, callbacks, callback_baton,
> +                                ctx, pool));
>          }
>        else /* path2 is a working copy path */
>          {
> @@ -2020,11 +2026,11 @@
>             ctx->cancel_baton, &diff_editor, &diff_edit_baton, pool));
>  
>    /* We want to switch our txn into URL2 */
> -  SVN_ERR(svn_ra_do_diff3
> +  SVN_ERR(svn_ra_do_diff4
>            (ra_session, &reporter, &reporter_baton, rev2, target1,
>             depth, ignore_ancestry,
> -           FALSE /* do not create text delta */, url2, diff_editor,
> -           diff_edit_baton, pool));
> +           FALSE /* do not create text delta */, FALSE /* diff_copy_from */,
> +           url2, diff_editor, diff_edit_baton, pool));
>  
>    /* Drive the reporter; do the diff. */
>    SVN_ERR(reporter->set_path(reporter_baton, "", rev1,
> @@ -2177,6 +2183,7 @@
>                   svn_depth_t depth,
>                   svn_boolean_t ignore_ancestry,
>                   svn_boolean_t no_diff_deleted,
> +                 svn_boolean_t diff_copy_from,
>                   svn_boolean_t show_copies_as_adds,
>                   svn_boolean_t ignore_content_type,
>                   svn_boolean_t use_git_diff_format,
> @@ -2228,7 +2235,7 @@
>  
>    return do_diff(&diff_callbacks, &diff_cmd_baton, ctx,
>                   path1, path2, revision1, revision2, &peg_revision,
> -                 depth, ignore_ancestry, show_copies_as_adds,
> +                 depth, ignore_ancestry, diff_copy_from, show_copies_as_adds,
>                   use_git_diff_format, changelists, pool);
>  }
>  
> @@ -2242,6 +2249,7 @@
>                       svn_depth_t depth,
>                       svn_boolean_t ignore_ancestry,
>                       svn_boolean_t no_diff_deleted,
> +                     svn_boolean_t diff_copy_from,
>                       svn_boolean_t show_copies_as_adds,
>                       svn_boolean_t ignore_content_type,
>                       svn_boolean_t use_git_diff_format,
> @@ -2289,7 +2297,7 @@
>  
>    return do_diff(&diff_callbacks, &diff_cmd_baton, ctx,
>                   path, path, start_revision, end_revision, peg_revision,
> -                 depth, ignore_ancestry, show_copies_as_adds,
> +                 depth, ignore_ancestry, diff_copy_from, show_copies_as_adds,
>                   use_git_diff_format, changelists, pool);
>  }
>  
> Index: subversion/libsvn_ra_serf/update.c
> ===================================================================
> --- subversion/libsvn_ra_serf/update.c	(revision 1038978)
> +++ subversion/libsvn_ra_serf/update.c	(working copy)
> @@ -2628,6 +2628,7 @@
>                       svn_depth_t depth,
>                       svn_boolean_t ignore_ancestry,
>                       svn_boolean_t text_deltas,
> +                     svn_boolean_t diff_copy_from,
>                       const char *versus_url,
>                       const svn_delta_editor_t *diff_editor,
>                       void *diff_baton,
> @@ -2638,7 +2639,8 @@
>    return make_update_reporter(ra_session, reporter, report_baton,
>                                revision,
>                                session->repos_url.path, versus_url, diff_target,
> -                              depth, ignore_ancestry, text_deltas, FALSE,
> +                              depth, ignore_ancestry, text_deltas,
> +                              diff_copy_from ? TRUE : FALSE,
>                                diff_editor, diff_baton, pool);
>  }
>  
> Index: subversion/libsvn_ra_serf/ra_serf.h
> ===================================================================
> --- subversion/libsvn_ra_serf/ra_serf.h	(revision 1038978)
> +++ subversion/libsvn_ra_serf/ra_serf.h	(working copy)
> @@ -1245,6 +1245,7 @@
>                       svn_depth_t depth,
>                       svn_boolean_t ignore_ancestry,
>                       svn_boolean_t text_deltas,
> +                     svn_boolean_t diff_copy_from,
>                       const char *versus_url,
>                       const svn_delta_editor_t *diff_editor,
>                       void *diff_baton,
> Index: subversion/libsvn_ra_neon/ra_neon.h
> ===================================================================
> --- subversion/libsvn_ra_neon/ra_neon.h	(revision 1038978)
> +++ subversion/libsvn_ra_neon/ra_neon.h	(working copy)
> @@ -349,6 +349,7 @@
>                                     svn_depth_t depth,
>                                     svn_boolean_t ignore_ancestry,
>                                     svn_boolean_t text_deltas,
> +                                   svn_boolean_t diff_copy_from,
>                                     const char *versus_url,
>                                     const svn_delta_editor_t *wc_diff,
>                                     void *wc_diff_baton,
> Index: subversion/libsvn_ra_neon/fetch.c
> ===================================================================
> --- subversion/libsvn_ra_neon/fetch.c	(revision 1038978)
> +++ subversion/libsvn_ra_neon/fetch.c	(working copy)
> @@ -2809,6 +2809,7 @@
>                                     svn_depth_t depth,
>                                     svn_boolean_t ignore_ancestry,
>                                     svn_boolean_t text_deltas,
> +                                   svn_boolean_t diff_copy_from,
>                                     const char *versus_url,
>                                     const svn_delta_editor_t *wc_diff,
>                                     void *wc_diff_baton,
> @@ -2821,7 +2822,7 @@
>                         diff_target,
>                         versus_url,
>                         depth,
> -                       FALSE,
> +                       diff_copy_from ? TRUE : FALSE,
>                         ignore_ancestry,
>                         FALSE,
>                         wc_diff,
> Index: subversion/svnserve/serve.c
> ===================================================================
> --- subversion/svnserve/serve.c	(revision 1038978)
> +++ subversion/svnserve/serve.c	(working copy)
> @@ -1774,6 +1774,8 @@
>    /* Default to unknown.  Old clients won't send depth, but we'll
>       handle that by converting recurse if necessary. */
>    svn_depth_t depth = svn_depth_unknown;
> +  /* Default to false.  Pre-1.7 clients won't send diff-copy-from. */
> +  svn_boolean_t diff_copy_from = FALSE;
>  
>    /* Parse the arguments. */
>    if (params->nelts == 5)
> @@ -1786,10 +1788,10 @@
>      }
>    else
>      {
> -      SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "(?r)cbbcb?w",
> +      SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "(?r)cbbcb?w?b",
>                                       &rev, &target, &recurse,
>                                       &ignore_ancestry, &versus_url,
> -                                     &text_deltas, &depth_word));
> +                                     &text_deltas, &depth_word, &diff_copy_from));

style nit: needs wrap at 78 columns, like this:
                                     &text_deltas, &depth_word,
				     &diff_copy_from));

>      }
>    target = svn_uri_canonicalize(target, pool);
>    versus_url = svn_uri_canonicalize(versus_url, pool);
> @@ -1812,7 +1814,8 @@
>      svn_revnum_t from_rev;
>      SVN_ERR(accept_report(NULL, &from_rev,
>                            conn, pool, b, rev, target, versus_path,
> -                          text_deltas, depth, FALSE, ignore_ancestry));
> +                          text_deltas, depth, diff_copy_from,
> +                          ignore_ancestry));
>      SVN_ERR(log_command(b, conn, pool, "%s",
>                          svn_log__diff(full_path, from_rev, versus_path,
>                                        rev, depth, ignore_ancestry,