You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by gb...@apache.org on 2013/11/17 15:56:28 UTC

svn commit: r1542741 [2/3] - in /subversion/branches/invoke-diff-cmd-feature: BRANCH-README subversion/include/svn_io.h subversion/libsvn_client/diff.c subversion/svn/svn.c subversion/svnlook/svnlook.c

Modified: subversion/branches/invoke-diff-cmd-feature/BRANCH-README
URL: http://svn.apache.org/viewvc/subversion/branches/invoke-diff-cmd-feature/BRANCH-README?rev=1542741&r1=1542740&r2=1542741&view=diff
==============================================================================
--- subversion/branches/invoke-diff-cmd-feature/BRANCH-README (original)
+++ subversion/branches/invoke-diff-cmd-feature/BRANCH-README Sun Nov 17 14:56:28 2013
@@ -1,6 +1,3 @@
-TODO:  add invoke-diff-cmd help to svnlook.c, log
-change location of opt_invoke_diff_cmd in declarion svn_cl__longopt_t
-
 This branch implements the 'invoke-diff-cmd' feature and is located at 
 https://svn.apache.org/repos/asf/subversion/branches/invoke-diff-cmd-feature/
 
@@ -10,10 +7,22 @@ expected to be reintegrated back thereto
 See: http://subversion.tigris.org/issues/show_bug.cgi?id=2044 for the
 original motivation for this project.
 
+Index:
+======
+
+1.  Introduction
+2.  What --invoke-diff-cmd provides
+3.  Structure of the feature
+    API components
+    UI components   
+4.  Changes to the existing code structure
+5.  Tests
+6.  Log Messages
+7.  Diff file
 
-======================================================================
-        INVOKE-DIFF-CMD SECTION
-======================================================================
+
+1. Introduction
+================
 
 --invoke-diff-cmd allows command line selection of an external diff
 program and will be extended to cover the existing diff3 option with a
@@ -26,8 +35,8 @@ which are passed as the diff program via
 'svnlook' and the config file.
 
 
-What --invoke-diff-cmd provides
-===============================
+2. What --invoke-diff-cmd provides
+==================================
 
 Users can type 'free-style' command lines for their selected
 diff/merge program, and optionally select a diff command file that
@@ -54,8 +63,8 @@ from 'svn help diff':
         +%svn_new, %svn_new- and file=%svn_label_new+
 
 
-Structure of the feature:
-=========================
+3.  Structure of the feature:
+=============================
 
 API components 
 --------------
@@ -95,8 +104,8 @@ UI components
   invoked.
 
 
-Changes to the existing code structure:
-=======================================
+4.  Changes to the existing code structure:
+===========================================
 
 None.
 
@@ -111,8 +120,8 @@ user's input of switches, whereas --invo
 free-style-anything-goes-including-shooting-your-foot creation.
 
 
-Tests
-=====
+5.  Tests
+=========
 
 The test for the 'invoke-diff-cmd' feature is
 
@@ -123,1146 +132,1528 @@ The test for the 'invoke-diff-cmd' featu
 Log messages 
 =============
 
-* subversion/include/private/svn_io_private.h
 
-  (svn_io__create_custom_diff_cmd): New function declaration.
+* subversion/libsvn_subr/io.c
 
+  (svn_io__create_custom_diff_cmd): New function.
 
-* subversion/include/svn_client.h 
+  (svn_io_run_external_diff): New function.
 
-  (svn_client_diff7, svn_client_diff_peg7): Declare the new API.  Like
-    svn_client_diff[_peg]6 but with invoke_diff_cmd parameter.
+  (svn_io_run_diff2):  Add test location information.
 
-  (svn_client_diff6, svn_client_diff_peg_6): Deprecate. 
 
+* subversion/libsvn_subr/config_file.c
 
-* subversion/include/svn_config.h
+  (svn_config_ensure,"invoke-diff-cmd"): New entry: invoke-diff-cmd.
+    Add help info.
 
-  (SVN_CONFIG_OPTION_INVOKE_DIFF_CMD): New definition.
 
+* subversion/tests/cmdline/diff_tests.py
 
-* subversion/include/svn_error_codes.h
+  (diff_invoke_external_diffcmd): New function.
 
-  (SVN_CLIENT_DIFF_CMD): New macro.
+  (test_list): Add new entry 'diff_invoke_external_diffcmd'.
 
 
-* subversion/include/svn_io.h
+* subversion/tests/cmdline/diff_tests.py
 
-   (svn_io_run_external_diff): New function.
+  (diff_invoke_external_diffcmd): New function.
 
+  (test_list): Add new entry 'diff_invoke_external_diffcmd'.
 
-* subversion/libsvn_client/deprecated.c
 
-  (svn_client_diff6, svn_client_diff_peg6): New deprecation wrappers.
+* subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
 
+  (--invoke-diff-cmd): Add new entry to 'help' output data.
 
-* subversion/libsvn_client/diff.c
 
-  (*): Update all comments mentioning 'svn_client_diff6' to
-    'svn_client_diff7'.
+* subversion/svn/log-cmd.c
 
-  (diff_cmd_baton): New member: 'invoke_diff_cmd'. 
+  (log_receiver_baton): New struct member const char *invoke_diff_cmd.
 
-  (set_up_diff_cmd_and_options): Apply invoke-diff-cmd option
-    preferentially.  Note: fucntion has been refactored, but previous
-    behavior regards the old diff_cmd stays unchanged.
+  (display_diff): New parameter cons char *invoke_diff_cmd.  Update
+    callfrom svn_client_diff_peg6() to svn_client_peg_diff7() and pass
+    const char *invoke_diff_cmd parameter.
 
-  (diff_content_changed): Raise an error if both diff_cmd and
-    invoke-diff-cmd are set.  Add invoke-diff-cmd to if condition.
-    Add comment explaining the presence of non-canonical path in
-    function call.  Call svn_io_run_external_diff if --invoke-diff-cmd
-    option was specified, otherwise retain previous behaviour.
+  (log_entry_receiver): Pass lb->invoke_diff_cmd into display_diff().
+ 
+  (svn_cl__log): Ensure mutual exclusions between invoke_diff_cmd and
+    diff-cmd. Add 3 CR's. Require diff cmd when --invoke-diff-cmd is
+    called.  Populate log_receiver_baton member invoke_diff_cmd.
 
-  (svn_client_diff_peg_7): Rename and update from
-    svn_client_diff_peg_6.  Add new parameter: invoke_diff_cmd.
 
-  (svn_client_diff7): Rename and update from svn_client_diff6, add
-    new parameter 'invoke_diff_cmd'.  
+* subversion/svn/svn.c
 
+  (svn_cl__longopt_t): New enum opt_invoke_diff_cmd.              
+ 
+  (svn_cl__options "invoke-diff-cmd"): Add help information.  Add new
+    variable 'opt_invoke_diff_cmd'.
 
-* subversion/libsvn_subr/config_file.c
+  (svn_cl__cmd_table[]): New option: 'invoke-diff-cmd', help info.
+    Add opt_invoke_diff_cmd to 'diff'.  Add opt_invoke_diff_cmd to 'log'. 
 
-  (svn_config_ensure,"invoke-diff-cmd"): New entry: invoke-diff-cmd.
-    Add help info.
+  (sub_main): Add case opt_diff_cmd. Update comment. Prohibit
+    simultaneous usage of --invoke-diff-cmd and --internal-diff.
+    Prohibit simultaneous usage of --diff-cmd and
+    --invoke-diff-cmd. Add conditional call to svn_config_set.
 
 
-* subversion/libsvn_subr/io.c
+* subversion/svn/cl.h
 
-  (svn_io__create_custom_diff_cmd): New function.
+  (struct svn_cl__opt_state_t.diff): New member: invoke_diff_cmd.
 
-  (svn_io_run_external_diff): New function.
 
-  (svn_io_run_diff2):  Add test location information.
+* subversion/svn/diff-cmd.c
 
+  (svn_cl__diff): Update call from svn_client_diff6() to
+    svn_client_diff7().  Pass invoke_diff_cmd into svn_client_diff7().
+    Update call from svn_client_diff_peg6() to
+    svn_client_diff_peg7(). Pass invoke_diff_cmd into
+    svn_client_diff_peg7().
 
-* subversion/svn/cl.h
 
-  (struct svn_cl__opt_state_t.diff): New member: 'invoke_diff_cmd'.
+* subversion/include/private/svn_io_private.h
 
+  (svn_io__create_custom_diff_cmd): New function declaration.
 
-* subversion/svn/diff-cmd.c
 
-  (svn_cl__diff): Update call to svn_client_diff6 to svn_client_diff7,
-    add invoke_diff_cmd to parameter listing.  Update call to svn_client_diff_peg7 to
-    svn_client_diff_peg7, add invoke_diff_cmd to parameter listing.
+* subversion/include/svn_error_codes.h
 
+  (SVN_CLIENT_DIFF_CMD): New macro.
 
-* subversion/svn/log-cmd.c
 
-  (log_receiver_baton): New struct member invoke_diff_cmd.
+* subversion/include/svn_config.h
 
-  (display_diff): New parameter 'invoke_diff_cmd' . Update call to
-    svn_client_peg_diff7.  Pass invoke_diff_cmd parameter into
-    svn_client_diff_peg7().
+  (SVN_CONFIG_OPTION_INVOKE_DIFF_CMD): New definition.
 
-  (log_entry_receiver): Pass invoke_diff_cmd into display_diff().
- 
-  (svn_cl__log): Ensure mutual exclusions between invoke_diff_cmd and
-    diff-cmd. Require 'diff' option to be set when requesting
-    invoke_diff option. Populate log_receiver_baton member
-    invoke_diff_cmd.
 
+* subversion/include/svn_io.h
 
-* subversion/svnlook/svnlook.c
+   (svn_io_run_external_diff): New function.
 
-  (enum): New variable svnlook__invoke_diff_cmd.
 
-  (options_table[]): New entry 'invoke-diff-cmd'.
+* subversion/include/svn_client.h 
 
-  (cmd_tablcmd[]): Add svnlook__invoke_diff_cmd to diff cmd table
-    entry.
+  (svn_client_diff7, svn_client_diff_peg7): Declare the new API and
+    update comments.  Like svn_client_diff[_peg]6 but with
+    invoke_diff_cmd parameter.
 
-  (svnlook_opt_state): New member variable "invoke_diff_cmd".
+  (svn_client_diff6, svn_client_diff_peg_6): Deprecate. 
 
-  (svnlook_ctxt_t): New member variable "invoke_diff_cmd".
+* subversion/svnlook/svnlook.c
 
-  (print_diff_tree): Modify 'if condition' to include new
-    invoke_diff_cmd. Add conditional call to
-    /include/svn_io.c:svn_io_run_external_diff().
+  (enum (anonymous?)): New variable svnlook__invoke_diff_cmd.
 
-  (get_ctxt_baton): Assign invoke_diff_cmd data.
+  (options_table "invoke_diff_cmd"): New entry.
 
-  (main): Add case svnlook__invoke_diff_cmd. Assign opt_arg to
-    opt_state.invoke_diff_cmd.  Add exclusiveness test for
-    invoke_diff_cmd and diff_cmd.
+  (cmd_tablcmd, "diff"): Add svnlook__invoke_diff_cmd.
 
+  (svnlook_opt_state): Adjust comment. New member variable const char
+    *invoke_diff_cmd.
 
-* subversion/svn/svn.c
+  (svnlook_ctxt_t):   New member variable const char
+    *invoke_diff_cmd.
 
-  (svn_cl__longopt_t): New enum opt_invoke_diff_cmd.              
- 
-  (svn_cl__options "invoke-diff-cmd"): Add help information.  Add new
-    variable 'opt_invoke_diff_cmd'.
+  (print_diff_tree): Modify 'if condition' and add new
+    invoke_diff_cmd.  Add conditional call to
+    /include/svn_io.c:svn_io_run_external_diff().
+
+  (get_ctxt_baton): Assign invoke_diff_cmd data.
+
+  (main): Add new case svnlook__invoke_diff_cmd.  Add exclusiveness
+    test for invoke_diff_cmd and diff_cmd.
 
-  (svn_cl__cmd_table[]): New option: 'invoke-diff-cmd', help info.  Add
-    opt_invoke_diff_cmd to svn_cl__log entry.  Add opt_invoke_diff_cmd
-    to the list of valid subcommands.
+* subversion/libsvn_client/diff.c
 
+  (*): Update all comments mentioning 'svn_client_diff6' to
+    'svn_client_diff7'.
 
-  (sub_main): Add case opt_invoke_diff_cmd. Prohibit simultaneous
-    usage of --invoke-diff-cmd and --internal-diff.  Prohibit
-    simultaneous usage of --diff-cmd and --invoke-diff-cmd. Add call
-    to svn_config_set.
+  (diff_cmd_baton): New member: const char *invoke_diff_cmd.
 
+  (diff_content_changed): Raise an error if both diff_cmd and
+    invoke-diff-cmd are set and add invoke-diff-cmd to if condition.
+    Add call to svn_io_run_external_diff().  
 
-* subversion/tests/cmdline/diff_tests.py
+  (unsupported_diff_error): Update error condition user information.
 
-  (diff_invoke_external_diffcmd): New function.
+  (set_up_diff_cmd_and_options): Update comment.  Add CRs.  Update
+  conditional call, add invoke-diff-cmd config file operation.
+  Improve code redability.
 
-  (test_list): Add new entry 'diff_invoke_external_diffcmd'.
+  (svn_client_diff_peg_7): Rename and update from
+    svn_client_diff_peg_6().  Add new parameter: const char
+    *invoke_diff_cmd.
 
+  (svn_client_diff7): Rename and update from svn_client_diff6(), add
+    new parameter const char *invoke_diff_cmd.
 
-* subversion/tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
 
-  (--invoke-diff-cmd): Add new entry to 'help' output data.
+* subversion/libsvn_client/deprecated.c
 
+  (svn_client_diff6, svn_client_diff_peg6): New deprecation wrappers.
 
-Diff file for this branch
-=========================
 
-*** /home/g/trunk/subversion/include/private/svn_io_private.h	2013-07-12 22:06:51.122108458 +0100
---- /home/g/branches/invoke-diff-cmd-feature/subversion/include/private/svn_io_private.h	2013-11-06 19:06:16.397366843 +0000
-*************** svn_stream__aprfile(svn_stream_t *stream
-*** 98 ****
---- 99,143 ----
-+ /** Parse a user defined command to contain dynamically created labels
-+  *  and filenames.  This function serves both diff and diff3 parsing
-+  *  requirements.
-+  *
-+  *  When used in a diff context: (responding parse tokens in braces)
-+  *
-+  *  @a label1 (%svn_label_old) refers to the label of @a tmpfile1
-+  *  (%svn_old) which is the pristine copy.
-+  *
-+  *  @a label2 (%svn_label_new) refers to the label of @a tmpfile2
-+  *  (%svn_new) which is the altered copy.
-+  *
-+  *  When used in a diff3 context:
-+  *
-+  *  @a label1 refers to the label of @a tmpfile1 which is the 'mine'
-+  *  copy.
-+  *
-+  *  @a label2 refers to the label of @a tmpfile2 which is the 'older'
-+  *  copy.
-+  *
-+  *  @a label3 (%svn_label_base) refers to the label of @a base
-+  *  (%svn_base) which is the 'base' copy.
-+  *
-+  *  In general:
-+  *
-+  *  @a cmd is a user defined string containing 0 or more parse tokens
-+  *  which are expanded by the required labels and filenames.
-+  * 
-+  *  @a pool is used for temporary allocations.
-+  *
-+  *  @return A NULL-terminated character array.
-+  * 
-+  * @since New in 1.9.
-+  */
-+ const char **
-+ svn_io__create_custom_diff_cmd(const char *label1,
-+                                const char *label2,
-+                                const char *label3,
-+                                const char *from,
-+                                const char *to,
-+                                const char *base,
-+                                const char *cmd,
-+                                apr_pool_t *pool);
-+ 
-+ 
 
-================================================================================
+7.  Diff file
+=============
 
-*** /home/g/trunk/subversion/include/svn_client.h	2013-10-15 15:55:28.309189195 +0100
---- /home/g/branches/invoke-diff-cmd-feature/subversion/include/svn_client.h	2013-11-06 16:28:10.178327223 +0000
-*************** svn_client_blame(const char *path_or_url
-*** 3059 ****
---- 3060,3064 ----
-+  * @a invoke_diff_cmd is used to call an external diff program but may
-+  * not be @c NULL.  The command line invocation will override the
-+  * invoke-diff-cmd invocation entry(if any) in the Subversion
-+  * configuration file.
-+  *
-*************** svn_client_blame(const char *path_or_url
-*** 3089 ****
---- 3095,3123 ----
-+  * @since New in 1.9.
-+  */
-+ svn_error_t *
-+ svn_client_diff7(const apr_array_header_t *options,
-+                  const char *path_or_url1,
-+                  const svn_opt_revision_t *revision1,
-+                  const char *path_or_url2,
-+                  const svn_opt_revision_t *revision2,
-+                  const char *relative_to_dir,
-+                  svn_depth_t depth,
-+                  svn_boolean_t ignore_ancestry,
-+                  svn_boolean_t no_diff_added,
-+                  svn_boolean_t no_diff_deleted,
-+                  svn_boolean_t show_copies_as_adds,
-+                  svn_boolean_t ignore_content_type,
-+                  svn_boolean_t ignore_properties,
-+                  svn_boolean_t properties_only,
-+                  svn_boolean_t use_git_diff_format,
-+                  const char *header_encoding,
-+                  svn_stream_t *outstream,
-+                  svn_stream_t *errstream,
-+                  const apr_array_header_t *changelists,
-+                  const char *invoke_diff_cmd,
-+                  svn_client_ctx_t *ctx,
-+                  apr_pool_t *pool);
-+ 
-+ /** Similar to svn_client_diff7(), but without @a invoke_diff_cmd.
-+  *
-+  * @deprecated Provided for backward compatibility with the 1.8 API.
-*************** svn_client_blame(const char *path_or_url
-*** 3091 ****
---- 3126 ----
-+ SVN_DEPRECATED
-*************** svn_client_diff(const apr_array_header_t
-*** 3249 ****
-!  * identically to svn_client_diff6(), using @a path_or_url for both of that
---- 3284 ----
-!  * identically to svn_client_diff7(), using @a path_or_url for both of that 
-*************** svn_client_diff(const apr_array_header_t
-*** 3252 ****
-!  * All other options are handled identically to svn_client_diff6().
---- 3287 ----
-!  * All other options are handled identically to svn_client_diff7().
-*************** svn_client_diff(const apr_array_header_t
-*** 3253 ****
---- 3289,3321 ----
-+  * @since New in 1.9.
-+  */
-+ svn_error_t *
-+ svn_client_diff_peg7(const apr_array_header_t *diff_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,
-+                      const char *relative_to_dir,
-+                      svn_depth_t depth,
-+                      svn_boolean_t ignore_ancestry,
-+                      svn_boolean_t no_diff_added,
-+                      svn_boolean_t no_diff_deleted,
-+                      svn_boolean_t show_copies_as_adds,
-+                      svn_boolean_t ignore_content_type,
-+                      svn_boolean_t ignore_properties,
-+                      svn_boolean_t properties_only,
-+                      svn_boolean_t use_git_diff_format,
-+                      const char *header_encoding,
-+                      svn_stream_t *outstream,
-+                      svn_stream_t *errstream,
-+                      const apr_array_header_t *changelists,
-+                      const char *invoke_diff_cmd,
-+                      svn_client_ctx_t *ctx,
-+                      apr_pool_t *pool);
-+          
-+ 
-+ /**
-+  * Similar to svn_client_peg7(), but without @a no_diff_added set to
-+  * FALSE, @a ignore_properties set to FALSE and @a properties_only set
-+  * to FALSE.
-+  *
-+  * @deprecated Provided for backward compatibility with the 1.7 API.
-*************** svn_client_diff(const apr_array_header_t
-*** 3255 ****
---- 3324 ----
-+ SVN_DEPRECATED
-*************** svn_client_diff_peg6(const apr_array_hea
-*** 3279 ****
-! /** Similar to svn_client_diff6_peg6(), but with @a outfile and @a errfile,
---- 3348,3349 ----
-! /**
-!  * Similar to svn_client_diff_peg6(), but with @a outfile and @a errfile,
-
-================================================================================
-
-*** /home/g/trunk/subversion/include/svn_config.h	2013-08-21 14:38:52.462504050 +0100
---- /home/g/branches/invoke-diff-cmd-feature/subversion/include/svn_config.h	2013-08-21 15:13:02.172668024 +0100
-*************** typedef struct svn_config_t svn_config_t
-*** 116 ****
---- 117,118 ----
-+ /** @since New in 1.9. */
-+ #define SVN_CONFIG_OPTION_INVOKE_DIFF_CMD           "invoke-diff-cmd"
-
-================================================================================
-
-*** /home/g/trunk/subversion/include/svn_error_codes.h	2013-09-24 21:40:36.710752868 +0100
---- /home/g/branches/invoke-diff-cmd-feature/subversion/include/svn_error_codes.h	2013-09-26 13:50:41.575809672 +0100
-*************** SVN_ERROR_START
-*** 1206 ****
---- 1207,1211 ----
-+   /** @since New in 1.9 */
-+   SVN_ERRDEF(SVN_ERR_CLIENT_DIFF_CMD,
-+              SVN_ERR_CLIENT_CATEGORY_START + 24,
-+              "More than one diff command defined")
-+ 
+svn diff -x -p ^/subversion/trunk/subversion ^/subversion/branches/invoke-diff-cmd-feature/subversion@1542685
 
-================================================================================
 
-*** /home/g/trunk/subversion/include/svn_io.h	2013-10-15 15:55:28.305189175 +0100
---- /home/g/branches/invoke-diff-cmd-feature/subversion/include/svn_io.h	2013-11-06 17:13:17.923754208 +0000
-*************** svn_io_file_readline(apr_file_t *file,
-*** 2362 ****
---- 2363,2379 ----
-+ 
-+ /** Run the external diff command defined by the invoke-diff-cmd
-+  *  option.
-+  *  
-+  *  @since New in 1.9.
+Index: libsvn_subr/io.c
+===================================================================
+--- libsvn_subr/io.c	(.../trunk/subversion)	(revision 1542732)
++++ libsvn_subr/io.c	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -2993,8 +2993,166 @@ svn_io_run_cmd(const char *path,
+   return svn_io_wait_for_cmd(&cmd_proc, cmd, exitcode, exitwhy, pool);
+ }
+ 
++const char **
++svn_io__create_custom_diff_cmd(const char *label1,
++                               const char *label2,
++                               const char *label3,
++                               const char *from,
++                               const char *to,
++                               const char *base,
++                               const char *cmd,
++                               apr_pool_t *pool)
++{
++  /* 
++     This function can be tested with:
++     /subversion/tests/cmdline/diff_tests.py diff_invoke_external_diffcmd
 +  */
-+ svn_error_t *
-+ svn_io_run_external_diff(const char *dir,
-+                          const char *label1,
-+                          const char *label2,
-+                          const char *tmpfile1,
-+                          const char *tmpfile2,
-+                          int *pexitcode,
-+                          apr_file_t *outfile,
-+                          apr_file_t *errfile,
-+                          const char *external_diff_cmd,
-+                          apr_pool_t *scratch_pool);
-
-================================================================================
-
-*** /home/g/trunk/subversion/libsvn_client/deprecated.c	2013-09-24 21:40:36.722752927 +0100
---- /home/g/branches/invoke-diff-cmd-feature/subversion/libsvn_client/deprecated.c	2013-09-26 13:50:46.983836489 +0100
-*************** svn_error_t *
-*** 916 ****
---- 917,963 ----
-+ svn_client_diff6(const apr_array_header_t *options,
-+                  const char *path_or_url1,
-+                  const svn_opt_revision_t *revision1,
-+                  const char *path_or_url2,
-+                  const svn_opt_revision_t *revision2,
-+                  const char *relative_to_dir,
-+                  svn_depth_t depth,
-+                  svn_boolean_t ignore_ancestry,
-+                  svn_boolean_t no_diff_added,
-+                  svn_boolean_t no_diff_deleted,
-+                  svn_boolean_t show_copies_as_adds,
-+                  svn_boolean_t ignore_content_type,
-+                  svn_boolean_t ignore_properties,
-+                  svn_boolean_t properties_only,
-+                  svn_boolean_t use_git_diff_format,
-+                  const char *header_encoding,
-+                  svn_stream_t *outstream,
-+                  svn_stream_t *errstream,
-+                  const apr_array_header_t *changelists,
-+                  svn_client_ctx_t *ctx,
-+                  apr_pool_t *pool)
-+ {
-+   return svn_client_diff7(options,
-+                           path_or_url1,
-+                           revision1,
-+                           path_or_url2,
-+                           revision2,
-+                           relative_to_dir,
-+                           depth,
-+                           ignore_ancestry,
-+                           no_diff_added,
-+                           no_diff_deleted,
-+                           show_copies_as_adds,
-+                           ignore_content_type,
-+                           ignore_properties,
-+                           properties_only,
-+                           use_git_diff_format,
-+                           header_encoding,
-+                           outstream,
-+                           errstream,
-+                           changelists,
-+                           NULL,
-+                           ctx, 
-+                           pool);
-+ }
-+ 
-+ svn_error_t *
-*************** svn_client_diff(const apr_array_header_t
-*** 1035 ****
---- 1083,1129 ----
-+ }
-+ 
-+ svn_error_t *
-+ 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,
-+                      const char *relative_to_dir,
-+                      svn_depth_t depth,
-+                      svn_boolean_t ignore_ancestry,
-+                      svn_boolean_t no_diff_added,
-+                      svn_boolean_t no_diff_deleted,
-+                      svn_boolean_t show_copies_as_adds,
-+                      svn_boolean_t ignore_content_type,
-+                      svn_boolean_t ignore_properties,
-+                      svn_boolean_t properties_only,
-+                      svn_boolean_t use_git_diff_format,
-+                      const char *header_encoding,
-+                      svn_stream_t *outstream,
-+                      svn_stream_t *errstream,
-+                      const apr_array_header_t *changelists,
-+                      svn_client_ctx_t *ctx,
-+                      apr_pool_t *pool)
-+ {
-+   return svn_client_diff_peg7(options,
-+                               path_or_url,
-+                               peg_revision,
-+                               start_revision,
-+                               end_revision,
-+                               relative_to_dir,
-+                               depth,
-+                               ignore_ancestry,
-+                               no_diff_added,
-+                               no_diff_deleted,
-+                               show_copies_as_adds,
-+                               ignore_content_type,
-+                               ignore_properties,
-+                               properties_only,
-+                               use_git_diff_format,
-+                               header_encoding,
-+                               outstream,
-+                               errstream,
-+                               changelists,
-+                               NULL,
-+                               ctx,
-+                               pool);
-
-================================================================================
-
-*** /home/g/trunk/subversion/libsvn_client/diff.c	2013-08-07 20:56:50.881234131 +0100
---- /home/g/branches/invoke-diff-cmd-feature/subversion/libsvn_client/diff.c	2013-11-06 17:10:30.546924231 +0000
-*************** print_git_diff_header(svn_stream_t *os,
-*** 431 ****
-!    passed to svn_client_diff6(), which is probably stdout.
---- 431 ----
-!    passed to svn_client_diff7(), which is probably stdout.
-*************** struct diff_cmd_baton {
-*** 538 ****
---- 539,541 ----
-+   /* external custom diff command */
-+   const char *invoke_diff_cmd;
-+ 
-*************** struct diff_cmd_baton {
-*** 567 ****
-!      svn_client_diff6(), either may be SVN_INVALID_REVNUM.  We need these
---- 570 ----
-!      svn_client_diff7(), either may be SVN_INVALID_REVNUM.  We need these
-*************** diff_content_changed(svn_boolean_t *wrot
-*** 788 ****
---- 793,796 ----
-+   if (diff_cmd_baton->diff_cmd && diff_cmd_baton->invoke_diff_cmd)
-+       return svn_error_create(SVN_ERR_CLIENT_DIFF_CMD, NULL,
-+                               _("diff-cmd and invoke-diff-cmd are "
-+                                 "mutually exclusive."));
-*************** diff_content_changed(svn_boolean_t *wrot
-*** 790 ****
-!   if (diff_cmd_baton->diff_cmd)
---- 798 ----
-!   if (diff_cmd_baton->diff_cmd || diff_cmd_baton->invoke_diff_cmd)
-*************** diff_content_changed(svn_boolean_t *wrot
-*** 828 ****
---- 836,837 ----
-+       /* "." is a non-canonical path for the diff process's working directory. */
-+       if (diff_cmd_baton->diff_cmd) 
-*************** diff_content_changed(svn_boolean_t *wrot
-*** 835 ****
---- 845,853 ----
-+       else
-+         { 
-+           SVN_ERR(svn_io_run_external_diff(".", 
-+                                            label1, label2,
-+                                            tmpfile1, tmpfile2,
-+                                            &exitcode, outfile, errfile,
-+                                            diff_cmd_baton->invoke_diff_cmd,
-+                                            scratch_pool));
-+         }
-*************** diff_prepare_repos_repos(const char **ur
-*** 1537,1538 ****
-!    This function is really svn_client_diff6().  If you read the public
-!    API description for svn_client_diff6(), it sounds quite Grand.  It
---- 1555,1556 ----
-!    This function is really svn_client_diff7().  If you read the public
-!    API description for svn_client_diff7(), it sounds quite Grand.  It
-*************** diff_prepare_repos_repos(const char **ur
-*** 1562 ****
-!    Perhaps someday a brave soul will truly make svn_client_diff6()
---- 1580 ----
-!    Perhaps someday a brave soul will truly make svn_client_diff7()
-*************** unsupported_diff_error(svn_error_t *chil
-*** 1575 ****
-!                           _("Sorry, svn_client_diff6 was called in a way "
---- 1593 ----
-!                           _("Sorry, svn_client_diff7 was called in a way "
-*************** unsupported_diff_error(svn_error_t *chil
-*** 1584 ****
-!    All other options are the same as those passed to svn_client_diff6(). */
---- 1602 ----
-!    All other options are the same as those passed to svn_client_diff7(). */
-*************** diff_wc_wc(const char *path1,
-*** 1667 ****
-!    All other options are the same as those passed to svn_client_diff6(). */
---- 1685 ----
-!    All other options are the same as those passed to svn_client_diff7(). */
-*************** diff_repos_repos(const svn_wc_diff_callb
-*** 1812 ****
-!    All other options are the same as those passed to svn_client_diff6(). */
---- 1830 ----
-!    All other options are the same as those passed to svn_client_diff7(). */
-*************** do_diff(const svn_wc_diff_callbacks4_t *
-*** 2147 ****
-!    All other options are the same as those passed to svn_client_diff6(). */
---- 2165 ----
-!    All other options are the same as those passed to svn_client_diff7(). */
-*************** diff_summarize_repos_wc(svn_client_diff_
-*** 2191 ****
-!    All other options are the same as those passed to svn_client_diff6(). */
---- 2209 ----
-!    All other options are the same as those passed to svn_client_diff7(). */
-*************** set_up_diff_cmd_and_options(struct diff_
-*** 2466 ****
-!   /* See if there is a diff command and/or diff arguments. */
---- 2484 ----
-!   /* old style diff_cmd has precedence in config file */
-*************** set_up_diff_cmd_and_options(struct diff_
-*** 2486,2487 ****
-!     SVN_ERR(svn_path_cstring_to_utf8(&diff_cmd_baton->diff_cmd, diff_cmd,
-!                                      pool));
---- 2505,2506 ----
-!     SVN_ERR(svn_path_cstring_to_utf8(&diff_cmd_baton->diff_cmd, 
-! 				     diff_cmd, pool));
-*************** set_up_diff_cmd_and_options(struct diff_
-*** 2488 ****
---- 2508,2511 ----
-+     {
-+       if (config) /* check if there is a invoke_diff_cmd in the config file */
-+ 	{
-+ 	  svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG);
-*************** set_up_diff_cmd_and_options(struct diff_
-*** 2489 ****
---- 2513,2522 ----
-+ 	  svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
-+ 			 SVN_CONFIG_OPTION_INVOKE_DIFF_CMD, NULL);
-+ 	  if (diff_cmd) 
-+ 	    {
-+ 	      SVN_ERR(svn_path_cstring_to_utf8(&diff_cmd_baton->invoke_diff_cmd,
-+                                                diff_cmd, pool));
-+ 	      return SVN_NO_ERROR;
-+ 	    }
-+ 	}
-+     }
-*************** set_up_diff_cmd_and_options(struct diff_
-*** 2502 ****
-!                       APR_ARRAY_IDX(options, i, const char *), pool));
---- 2537,2539 ----
-!                                             APR_ARRAY_IDX(options, i, 
-!                                                           const char *), 
-!                                             pool));
-*************** svn_error_t *
-*** 2555 ****
-! svn_client_diff6(const apr_array_header_t *options,
---- 2592 ----
-! svn_client_diff7(const apr_array_header_t *options,
-*************** svn_client_diff6(const apr_array_header_
-*** 2573 ****
---- 2611 ----
-+                  const char *invoke_diff_cmd,
-*************** svn_client_diff6(const apr_array_header_
-*** 2590 ****
---- 2629 ----
-+   diff_cmd_baton.invoke_diff_cmd = invoke_diff_cmd;
-*************** svn_error_t *
-*** 2622 ****
-! svn_client_diff_peg6(const apr_array_header_t *options,
---- 2661 ----
-! svn_client_diff_peg7(const apr_array_header_t *options,
-*************** svn_client_diff_peg6(const apr_array_hea
-*** 2640 ****
---- 2680 ----
-+                      const char *invoke_diff_cmd,
-*************** svn_client_diff_peg6(const apr_array_hea
-*** 2653 ****
---- 2694 ----
-+   diff_cmd_baton.invoke_diff_cmd = invoke_diff_cmd;
-
-================================================================================
-
-*** /home/g/trunk/subversion/libsvn_subr/config_file.c	2013-11-04 17:30:37.864042065 +0000
---- /home/g/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/config_file.c	2013-11-05 12:39:49.417954357 +0000
-*************** svn_config_ensure(const char *config_dir
-*** 1189 ****
---- 1190,1194 ----
-+         "### Set invoke-diff-cmd to the absolute path of your 'diff'"        NL
-+         "### program."                                                       NL
-+         "###   This will override the compile-time default, which is to use" NL
-+         "###   Subversion's internal diff implementation."                   NL
-+         "# invoke-diff-cmd = (see svn help diff for examples)"               NL
-
-================================================================================
-
-
-================================================================================
-
-*** /home/g/trunk/subversion/libsvn_subr/io.c	2013-10-31 19:53:47.816899485 +0000
---- /home/g/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/io.c	2013-11-06 19:07:58.589873589 +0000
-*************** svn_io_run_cmd(const char *path,
-*** 2995 ****
---- 2996,3153 ----
-+ const char **
-+ svn_io__create_custom_diff_cmd(const char *label1,
-+                                const char *label2,
-+                                const char *label3,
-+                                const char *from,
-+                                const char *to,
-+                                const char *base,
-+                                const char *cmd,
-+                                apr_pool_t *pool)
-+ {
-+   /* 
-+      This function can be tested with:
-+      /subversion/tests/cmdline/diff_tests.py diff_invoke_external_diffcmd
+ 
++  apr_array_header_t *words;
++  const char ** result;
++  size_t argv, item, i, delimiters = 6;
++  apr_pool_t *scratch_pool = svn_pool_create(pool); 
++  
++  struct replace_tokens_tab
++  {
++    const char *delimiter;
++    const char *replace;
++  } tokens_tab[] = {  /* Diff terminology */
++    { "%svn_label_old",  label1 },
++    { "%svn_label_new",  label2 },
++    { "%svn_label_base", label3 },
++    { "%svn_old",  from },  
++    { "%svn_new",  to   },    
++    { "%svn_base", base },    
++    { NULL, NULL }
++  };
++ 
++  if (label3) /* Merge terminology */
++    {
++      tokens_tab[0].delimiter = "%svn_label_mine";
++      tokens_tab[1].delimiter = "%svn_label_yours";
++      tokens_tab[3].delimiter = "%svn_mine";
++      tokens_tab[4].delimiter = "%svn_yours";
++    }
++
++  words = svn_cstring_split(cmd, " ", TRUE, scratch_pool);
++
++  result = apr_palloc(pool, 
++                      (words->nelts+1) * words->elt_size * sizeof(char *) );
++
++  for (item = 0, argv = 0; item < words->nelts; argv++, item++)
++    {
++      svn_stringbuf_t *word;
++
++      word = svn_stringbuf_create_empty(scratch_pool);
++      svn_stringbuf_appendcstr(word, APR_ARRAY_IDX(words, item, char *));
++
++      if ( (word->data[0] == '"') && (word->data[word->len-1] != '"') )
++        {
++          svn_stringbuf_t * complete = svn_stringbuf_create_empty(scratch_pool);
++          int done = 0;
++
++          while( (!done) && item < words->nelts)
++            {
++              svn_stringbuf_appendcstr(complete, 
++                                       APR_ARRAY_IDX(words, item, char *)); 
++
++              if ( (complete->data[complete->len-1] == '"') 
++                   || (item == words->nelts - 1) )
++                {
++                  word->data = complete->data;
++                  done = 1;
++                }
++              else 
++                { 
++                  svn_stringbuf_appendcstr(complete, " ");
++                  item++;
++                }
++            }
++        }
++      i = 0;
++      while (i < delimiters)
++        {
++          char *found = strstr(word->data, tokens_tab[i].delimiter);
++
++          if (!found)
++            {
++              i++;
++              continue;
++            }
++            
++          svn_stringbuf_replace(word, found - word->data,
++                                strlen(tokens_tab[i].delimiter),
++                                tokens_tab[i].replace,
++                                strlen(tokens_tab[i].replace));
++        }
++      result[argv] = apr_pstrdup(pool,word->data);
++    }  
++  result[argv] = NULL;
++  svn_pool_destroy(scratch_pool);
++  return result;
++}
++
+ svn_error_t *
++svn_io_run_external_diff(const char *dir,
++                         const char *label1,
++                         const char *label2,
++                         const char *tmpfile1,
++                         const char *tmpfile2,
++                         int *pexitcode,
++                         apr_file_t *outfile,
++                         apr_file_t *errfile,
++                         const char *external_diff_cmd,
++                         apr_pool_t *pool)
++{
++  int exitcode;
++  const char ** cmd;
++
++  apr_pool_t *scratch_pool = svn_pool_create(pool); 
++
++  if (0 == strlen(external_diff_cmd)) 
++     return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, NULL);
++
++  cmd = svn_io__create_custom_diff_cmd(label1, label2, NULL, 
++                                 tmpfile1, tmpfile2, NULL, 
++                                 external_diff_cmd, scratch_pool);
++  if (pexitcode == NULL)
++     pexitcode = &exitcode;
++  
++  SVN_ERR(svn_io_run_cmd(dir, cmd[0], cmd, pexitcode, NULL, TRUE,
++                         NULL, outfile, errfile, scratch_pool));
++  
++  /* The man page for (GNU) diff describes the return value as:
++
++      "An exit status of 0 means no differences were found, 1 means
++      some differences were found, and 2 means trouble."
++     
++     A return value of 2 typically occurs when diff cannot read its input
++     or write to its output, but in any case we probably ought to return an
++     error for anything other than 0 or 1 as the output is likely to be
++     corrupt.
 +   */
-+ 
-+   apr_array_header_t *words;
-+   const char ** result;
-+   size_t argv, item, i, delimiters = 6;
-+   apr_pool_t *scratch_pool = svn_pool_create(pool); 
-+   
-+   struct replace_tokens_tab
-+   {
-+     const char *delimiter;
-+     const char *replace;
-+   } tokens_tab[] = {  /* Diff terminology */
-+     { "%svn_label_old",  label1 },
-+     { "%svn_label_new",  label2 },
-+     { "%svn_label_base", label3 },
-+     { "%svn_old",  from },  
-+     { "%svn_new",  to   },    
-+     { "%svn_base", base },    
-+     { NULL, NULL }
-+   };
++  if (*pexitcode != 0 && *pexitcode != 1)
++    {
++      int i;
++      const char *failed_command = "";
++
++      for (i = 0; cmd[i]; ++i)
++          failed_command = apr_pstrcat(pool, failed_command, 
++                                       cmd[i], " ", (char*) NULL);
++      svn_pool_destroy(scratch_pool);
++      return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
++                               _("'%s' was expanded to '%s' and returned %d"),
++                               external_diff_cmd,
++                               failed_command,
++                               *pexitcode);
++    }
++  else
++    svn_pool_destroy(scratch_pool);
++  return SVN_NO_ERROR;
++}
++
++svn_error_t *
+ svn_io_run_diff2(const char *dir,
+                  const char *const *user_args,
+                  int num_user_args,
+@@ -3008,6 +3166,11 @@ svn_io_run_diff2(const char *dir,
+                  const char *diff_cmd,
+                  apr_pool_t *pool)
+ {
++  /* 
++  This function can be tested by using the test 
++  /subversion/tests/cmdline/diff_tests.py diff_external_diffcmd
++  */
++
+   const char **args;
+   int i;
+   int exitcode;
+@@ -3082,7 +3245,6 @@ svn_io_run_diff2(const char *dir,
+   return SVN_NO_ERROR;
+ }
+ 
+-
+ svn_error_t *
+ svn_io_run_diff3_3(int *exitcode,
+                    const char *dir,
+Index: libsvn_subr/config_file.c
+===================================================================
+--- libsvn_subr/config_file.c	(.../trunk/subversion)	(revision 1542732)
++++ libsvn_subr/config_file.c	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -1200,6 +1200,11 @@ svn_config_ensure(const char *config_dir, apr_pool
+         "### Set diff3-has-program-arg to 'yes' if your 'diff3' program"     NL
+         "###   accepts the '--diff-program' option."                         NL
+         "# diff3-has-program-arg = [yes | no]"                               NL
++        "### Set invoke-diff-cmd to the absolute path of your 'diff'"        NL
++        "### program."                                                       NL
++        "###   This will override the compile-time default, which is to use" NL
++        "###   Subversion's internal diff implementation."                   NL
++        "# invoke-diff-cmd = (see svn help diff for examples)"               NL
+         "### Set merge-tool-cmd to the command used to invoke your external" NL
+         "### merging tool of choice. Subversion will pass 5 arguments to"    NL
+         "### the specified command: base theirs mine merged wcfile"          NL
+Index: tests/cmdline/diff_tests.py
+===================================================================
+--- tests/cmdline/diff_tests.py	(.../trunk/subversion)	(revision 1542732)
++++ tests/cmdline/diff_tests.py	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -3237,6 +3237,7 @@ def diff_wrong_extension_type(sbox):
+   svntest.actions.run_and_verify_svn(None, [], err.INVALID_DIFF_OPTION,
+                                      'diff', '-x', sbox.wc_dir, '-r', '1')
+ 
++#----------------------------------------------------------------------
+ # Check the order of the arguments for an external diff tool
+ def diff_external_diffcmd(sbox):
+   "svn diff --diff-cmd provides the correct arguments"
+@@ -3274,7 +3275,54 @@ def diff_external_diffcmd(sbox):
+                                      'diff', '--diff-cmd', diff_script_path,
+                                      iota_path)
+ 
++# Check the correct parsing of arguments for an external diff tool
++def diff_invoke_external_diffcmd(sbox):
++  "svn diff --invoke-diff-cmd passes correct args"
+ 
++  diff_script_path = os.path.abspath(".")+"/diff"
++
++  svntest.main.create_python_hook_script(diff_script_path, 'import sys\n'
++    'for arg in sys.argv[1:]:\n  print(arg)\n')
++
++  if sys.platform == 'win32':
++     diff_script_path = "%s.bat" % diff_script_path
++
++  sbox.build(read_only = True)
++  os.chdir(sbox.wc_dir)
++
++  iota_path = 'iota'
++  svntest.main.file_append(iota_path, "new text in iota")
++
++  expected_output = svntest.verify.ExpectedOutput([
++      "Index: iota\n",
++      "===================================================================\n",
++      # correct label %svn_label_old -> label 1
++      "iota	(revision 1)\n",   
++
++      # correct file %svn_old -> old
++      os.path.abspath(svntest.wc.text_base_path("iota")) + "\n",
++
++      # correct label %svn_label_new -> label 2
++      "iota	(working copy)\n",
++
++      # correct file %svn_new -> new
++      os.path.abspath("iota") + "\n",
++
++      # preservation of quoted string  "X Y Z"-> "X Y Z"
++      "\"X Y Z\"\n",
++
++      # correct insertion of filename into string "+%svn_new+" -> "+" + new + "+"
++      "+" + os.path.abspath("iota") + "+\n",
++
++      ])
 +  
-+   if (label3) /* Merge terminology */
-+     {
-+       tokens_tab[0].delimiter = "%svn_label_mine";
-+       tokens_tab[1].delimiter = "%svn_label_yours";
-+       tokens_tab[3].delimiter = "%svn_mine";
-+       tokens_tab[4].delimiter = "%svn_yours";
-+     }
-+ 
-+   words = svn_cstring_split(cmd, " ", TRUE, scratch_pool);
-+ 
-+   result = apr_palloc(pool, 
-+                       (words->nelts+1) * words->elt_size * sizeof(char *) );
-+ 
-+   for (item = 0, argv = 0; item < words->nelts; argv++, item++)
-+     {
-+       svn_stringbuf_t *word;
-+ 
-+       word = svn_stringbuf_create_empty(scratch_pool);
-+       svn_stringbuf_appendcstr(word, APR_ARRAY_IDX(words, item, char *));
-+ 
-+       if ( (word->data[0] == '"') && (word->data[word->len-1] != '"') )
-+         {
-+           svn_stringbuf_t * complete = svn_stringbuf_create_empty(scratch_pool);
-+           int done = 0;
-+ 
-+           while( (!done) && item < words->nelts)
-+             {
-+               svn_stringbuf_appendcstr(complete, 
-+                                        APR_ARRAY_IDX(words, item, char *)); 
-+ 
-+               if ( (complete->data[complete->len-1] == '"') 
-+                    || (item == words->nelts - 1) )
-+                 {
-+                   word->data = complete->data;
-+                   done = 1;
-+                 }
-+               else 
-+                 { 
-+                   svn_stringbuf_appendcstr(complete, " ");
-+                   item++;
-+                 }
-+             }
-+         }
-+       i = 0;
-+       while (i < delimiters)
-+         {
-+           char *found = strstr(word->data, tokens_tab[i].delimiter);
-+ 
-+           if (!found)
-+             {
-+               i++;
-+               continue;
-+             }
-+             
-+           svn_stringbuf_replace(word, found - word->data,
-+                                 strlen(tokens_tab[i].delimiter),
-+                                 tokens_tab[i].replace,
-+                                 strlen(tokens_tab[i].replace));
-+         }
-+       result[argv] = apr_pstrdup(pool,word->data);
-+     }  
-+   result[argv] = NULL;
-+   svn_pool_destroy(scratch_pool);
-+   return result;
-+ }
-+ 
-+ svn_error_t *
-+ svn_io_run_external_diff(const char *dir,
-+                          const char *label1,
-+                          const char *label2,
-+                          const char *tmpfile1,
-+                          const char *tmpfile2,
-+                          int *pexitcode,
-+                          apr_file_t *outfile,
-+                          apr_file_t *errfile,
-+                          const char *external_diff_cmd,
-+                          apr_pool_t *pool)
-+ {
-+   int exitcode;
-+   const char ** cmd;
-+ 
-+   apr_pool_t *scratch_pool = svn_pool_create(pool); 
-+ 
-+   if (0 == strlen(external_diff_cmd)) 
-+      return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, NULL);
-+ 
-+   cmd = svn_io__create_custom_diff_cmd(label1, label2, NULL, 
-+                                  tmpfile1, tmpfile2, NULL, 
-+                                  external_diff_cmd, scratch_pool);
-+   if (pexitcode == NULL)
-+      pexitcode = &exitcode;
-+   
-+   SVN_ERR(svn_io_run_cmd(dir, cmd[0], cmd, pexitcode, NULL, TRUE,
-+                          NULL, outfile, errfile, scratch_pool));
-+   
-+   /* The man page for (GNU) diff describes the return value as:
-+ 
-+       "An exit status of 0 means no differences were found, 1 means
-+       some differences were found, and 2 means trouble."
++  svntest.actions.run_and_verify_svn(None, expected_output, [],
++   'diff',
++   '--invoke-diff-cmd='+diff_script_path+
++   ' %svn_label_old %svn_old %svn_label_new %svn_new \"X Y Z\" +%svn_new+',
++  iota_path)
++
++
+ #----------------------------------------------------------------------
+ # Diffing an unrelated repository URL against working copy with
+ # local modifications (i.e. not committed). This is issue #3295 (diff
+@@ -4722,6 +4770,7 @@ test_list = [ None,
+               diff_file_depth_empty,
+               diff_wrong_extension_type,
+               diff_external_diffcmd,
++              diff_invoke_external_diffcmd,
+               diff_url_against_local_mods,
+               diff_preexisting_rev_against_local_add,
+               diff_git_format_wc_wc,
+Index: tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout
+===================================================================
+--- tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout	(.../trunk/subversion)	(revision 1542732)
++++ tests/cmdline/getopt_tests_data/svn_help_log_switch_stdout	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -111,6 +111,20 @@ Valid options:
+                                -w, --ignore-all-space: Ignore all white space
+                                --ignore-eol-style: Ignore changes in EOL style
+                                -p, --show-c-function: Show C function name
++  --invoke-diff-cmd ARG    : use ARG as format string for external diff command
++                             invocation.
++                             The following reserved keywords are replaced:
++                                 %svn_new -- new file
++                                 %svn_old -- old file
++                                 %svn_label_new -- label of the new file
++                                 %svn_label_old -- label of the old file
++                             Examples:
++                             --invoke-diff-cmd='diff -y %svn_new %svn_old'
++                             --invoke-diff-cmd="kdiff3 -auto -o /home/u/log \
++                                   %svn_new %svn_old --L1 %svn_label_new \
++                                  --L2 "Custom Label" '
++                             Reserved keywords may be embedded in strings:
++                             +%svn_new  %svn_new- and file=%svn_label_new+
+   --search ARG             : use ARG as search pattern (glob syntax)
+   --search-and ARG         : combine ARG with the previous search pattern
+ 
+Index: svn/log-cmd.c
+===================================================================
+--- svn/log-cmd.c	(.../trunk/subversion)	(revision 1542732)
++++ svn/log-cmd.c	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -67,6 +67,9 @@ struct log_receiver_baton
+   /* Diff arguments received from command line. */
+   const char *diff_extensions;
+ 
++  /* Custom diff command. */
++  const char *invoke_diff_cmd;
++
+   /* Stack which keeps track of merge revision nesting, using svn_revnum_t's */
+   apr_array_header_t *merge_stack;
+ 
+@@ -102,6 +105,7 @@ display_diff(const svn_log_entry_t *log_entry,
+              const char *diff_extensions,
+              svn_stream_t *outstream,
+              svn_stream_t *errstream,
++             const char *invoke_diff_cmd,
+              svn_client_ctx_t *ctx,
+              apr_pool_t *pool)
+ {
+@@ -122,7 +126,7 @@ display_diff(const svn_log_entry_t *log_entry,
+   end_revision.value.number = log_entry->revision;
+ 
+   SVN_ERR(svn_stream_puts(outstream, "\n"));
+-  SVN_ERR(svn_client_diff_peg6(diff_options,
++  SVN_ERR(svn_client_diff_peg7(diff_options,
+                                target_path_or_url,
+                                target_peg_revision,
+                                &start_revision, &end_revision,
+@@ -140,6 +144,7 @@ display_diff(const svn_log_entry_t *log_entry,
+                                outstream,
+                                errstream,
+                                NULL,
++                               invoke_diff_cmd,
+                                ctx, pool));
+   SVN_ERR(svn_stream_puts(outstream, _("\n")));
+   return SVN_NO_ERROR;
+@@ -466,6 +471,7 @@ log_entry_receiver(void *baton,
+                            lb->target_path_or_url, &lb->target_peg_revision,
+                            lb->depth, lb->diff_extensions,
+                            outstream, errstream,
++                           lb->invoke_diff_cmd,
+                            lb->ctx, pool));
+ 
+       SVN_ERR(svn_stream_close(outstream));
+@@ -706,25 +712,38 @@ svn_cl__log(apr_getopt_t *os,
+         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                                 _("'diff' option is not supported in "
+                                   "XML mode"));
+-    }
++    } 
+ 
++  if (opt_state->diff.diff_cmd && opt_state->diff.diff_cmd)
++    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
++                            _("'diff-cmd' and 'invoke-diff-cmd' options are "
++                              "mutually exclusive"));
++ 
+   if (opt_state->quiet && opt_state->show_diff)
+     return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                             _("'quiet' and 'diff' options are "
+                               "mutually exclusive"));
++
+   if (opt_state->diff.diff_cmd && (! opt_state->show_diff))
+     return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                             _("'diff-cmd' option requires 'diff' "
+                               "option"));
++
+   if (opt_state->diff.internal_diff && (! opt_state->show_diff))
+     return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                             _("'internal-diff' option requires "
+                               "'diff' option"));
++
+   if (opt_state->extensions && (! opt_state->show_diff))
+     return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                             _("'extensions' option requires 'diff' "
+                               "option"));
+ 
++  if (opt_state->diff.invoke_diff_cmd && (! opt_state->show_diff))
++    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
++                            _("'invoke-diff-cmd' option requires 'diff' "
++                              "option"));
++
+   if (opt_state->depth != svn_depth_unknown && (! opt_state->show_diff))
+     return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                             _("'depth' option requires 'diff' option"));
+@@ -788,6 +807,7 @@ svn_cl__log(apr_getopt_t *os,
+   lb.depth = opt_state->depth == svn_depth_unknown ? svn_depth_infinity
+                                                    : opt_state->depth;
+   lb.diff_extensions = opt_state->extensions;
++  lb.invoke_diff_cmd = opt_state->diff.invoke_diff_cmd;
+   lb.merge_stack = apr_array_make(pool, 0, sizeof(svn_revnum_t));
+   lb.search_patterns = opt_state->search_patterns;
+   lb.pool = pool;
+Index: svn/cl.h
+===================================================================
+--- svn/cl.h	(.../trunk/subversion)	(revision 1542732)
++++ svn/cl.h	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -184,6 +184,8 @@ typedef struct svn_cl__opt_state_t
+     {
+   const char *diff_cmd;              /* the external diff command to use
+                                         (not converted to UTF-8) */
++  const char *invoke_diff_cmd;       /* the format string to specify args   */
++                                     /* for the external diff cmd           */
+   svn_boolean_t internal_diff;       /* override diff_cmd in config file */
+   svn_boolean_t no_diff_added;       /* do not show diffs for deleted files */
+   svn_boolean_t no_diff_deleted;     /* do not show diffs for deleted files */
+Index: svn/svn.c
+===================================================================
+--- svn/svn.c	(.../trunk/subversion)	(revision 1542732)
++++ svn/svn.c	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -83,6 +83,7 @@ typedef enum svn_cl__longopt_t {
+   opt_ignore_properties,
+   opt_properties_only,
+   opt_patch_compatible,
++  opt_invoke_diff_cmd,
+   /* end of diff options */
+   opt_dry_run,
+   opt_editor_cmd,
+@@ -344,6 +345,34 @@ const apr_getopt_option_t svn_cl__options[] =
+   {"diff", opt_diff, 0, N_("produce diff output")}, /* maps to show_diff */
+   /* diff options */
+   {"diff-cmd",      opt_diff_cmd, 1, N_("use ARG as diff command")},
++  {"invoke-diff-cmd", opt_invoke_diff_cmd, 1, 
++                   N_("use ARG as format string for external diff command\n"
++                      "                             "
++                      "invocation.\n" 
++                      "                             "
++                      "The following reserved keywords are replaced:\n"
++                      "                             "
++                      "    %svn_new -- new file\n"
++                      "                             "
++                      "    %svn_old -- old file\n"
++                      "                             "
++                      "    %svn_label_new -- label of the new file\n"
++                      "                             "
++                      "    %svn_label_old -- label of the old file\n"
++                      "                             "
++                      "Examples:\n"
++                      "                             "
++                      "--invoke-diff-cmd=\'diff -y %svn_new %svn_old\'\n"          
++                      "                             "
++                      "--invoke-diff-cmd=\"kdiff3 -auto -o /home/u/log \\\n"
++                      "                             "
++                      "      %svn_new %svn_old --L1 %svn_label_new \\\n"
++                      "                             "
++                      "     --L2 \"Custom Label\" \'\n"
++                      "                             "
++                      "Reserved keywords may be embedded in strings:\n"
++                      "                             "
++                      "+%svn_new  %svn_new- and file=%svn_label_new+")},
+   {"internal-diff", opt_internal_diff, 0,
+                        N_("override diff-cmd specified in config file")},
+   {"no-diff-added", opt_no_diff_added, 0,
+@@ -610,7 +639,8 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table
+      opt_internal_diff, 'x', opt_no_diff_added, opt_no_diff_deleted,
+      opt_ignore_properties, opt_properties_only,
+      opt_show_copies_as_adds, opt_notice_ancestry, opt_summarize, opt_changelist,
+-     opt_force, opt_xml, opt_use_git_diff_format, opt_patch_compatible} },
++     opt_force, opt_xml, opt_use_git_diff_format, opt_patch_compatible,
++     opt_invoke_diff_cmd} },
+   { "export", svn_cl__export, {0}, N_
+     ("Create an unversioned copy of a tree.\n"
+      "usage: 1. export [-r REV] URL[@PEGREV] [PATH]\n"
+@@ -774,7 +804,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table
+     {'r', 'q', 'v', 'g', 'c', opt_targets, opt_stop_on_copy, opt_incremental,
+      opt_xml, 'l', opt_with_all_revprops, opt_with_no_revprops,
+      opt_with_revprop, opt_auto_moves, opt_depth, opt_diff, opt_diff_cmd,
+-     opt_internal_diff, 'x', opt_search, opt_search_and },
++     opt_internal_diff, 'x', opt_invoke_diff_cmd, opt_search, opt_search_and },
+     {{opt_with_revprop, N_("retrieve revision property ARG")},
+      {'c', N_("the change made in revision ARG")}} },
+ 
+@@ -2138,6 +2168,9 @@ sub_main(int argc, const char *argv[], apr_pool_t
+       case opt_diff_cmd:
+         opt_state.diff.diff_cmd = apr_pstrdup(pool, opt_arg);
+         break;
++      case opt_invoke_diff_cmd:
++        opt_state.diff.invoke_diff_cmd = apr_pstrdup(pool, opt_arg);
++        break;
+       case opt_merge_cmd:
+         opt_state.merge_cmd = apr_pstrdup(pool, opt_arg);
+         break;
+@@ -2552,7 +2585,7 @@ sub_main(int argc, const char *argv[], apr_pool_t
+       return EXIT_ERROR(err);
+     }
+ 
+-  /* Disallow simultaneous use of both --diff-cmd and
++  /* Disallow simultaneous use of --diff-cmd, --invoke-diff-cmd and
+      --internal-diff.  */
+   if (opt_state.diff.diff_cmd && opt_state.diff.internal_diff)
+     {
+@@ -2562,6 +2595,22 @@ sub_main(int argc, const char *argv[], apr_pool_t
+       return EXIT_ERROR(err);
+     }
+ 
++  if (opt_state.diff.invoke_diff_cmd && opt_state.diff.internal_diff)
++    {
++      err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
++                             _("--invoke-diff-cmd and --internal-diff "
++                               "are mutually exclusive"));
++      return EXIT_ERROR(err);
++    }
++
++  if ((opt_state.diff.diff_cmd) && (opt_state.diff.invoke_diff_cmd))
++    {
++      err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
++                             _("--invoke-diff-cmd and --diff-cmd "
++                               "are mutually exclusive"));
++      return EXIT_ERROR(err);
++    }
++
+   /* Ensure that 'revision_ranges' has at least one item, and make
+      'start_revision' and 'end_revision' match that item. */
+   if (opt_state.revision_ranges->nelts == 0)
+@@ -2774,9 +2823,12 @@ sub_main(int argc, const char *argv[], apr_pool_t
+ 
+   /* XXX: Only diff_cmd for now, overlay rest later and stop passing
+      opt_state altogether? */
+-  if (opt_state.diff.diff_cmd)
++  if (opt_state.diff.diff_cmd) 
+     svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
+                    SVN_CONFIG_OPTION_DIFF_CMD, opt_state.diff.diff_cmd);
++  if (opt_state.diff.invoke_diff_cmd)
++    svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
++                   SVN_CONFIG_OPTION_INVOKE_DIFF_CMD, opt_state.diff.invoke_diff_cmd);
+   if (opt_state.merge_cmd)
+     svn_config_set(cfg_config, SVN_CONFIG_SECTION_HELPERS,
+                    SVN_CONFIG_OPTION_DIFF3_CMD, opt_state.merge_cmd);
+Index: svn/diff-cmd.c
+===================================================================
+--- svn/diff-cmd.c	(.../trunk/subversion)	(revision 1542732)
++++ svn/diff-cmd.c	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -403,7 +403,7 @@ svn_cl__diff(apr_getopt_t *os,
+                                 ctx, iterpool));
+             }
+           else
+-            SVN_ERR(svn_client_diff6(
++            SVN_ERR(svn_client_diff7(
+                      options,
+                      target1,
+                      &(opt_state->start_revision),
+@@ -423,6 +423,7 @@ svn_cl__diff(apr_getopt_t *os,
+                      outstream,
+                      errstream,
+                      opt_state->changelists,
++                     opt_state->diff.invoke_diff_cmd,                        
+                      ctx, iterpool));
+         }
+       else
+@@ -454,7 +455,7 @@ svn_cl__diff(apr_getopt_t *os,
+                                 ctx, iterpool));
+             }
+           else
+-            SVN_ERR(svn_client_diff_peg6(
++            SVN_ERR(svn_client_diff_peg7(
+                      options,
+                      truepath,
+                      &peg_revision,
+@@ -474,6 +475,7 @@ svn_cl__diff(apr_getopt_t *os,
+                      outstream,
+                      errstream,
+                      opt_state->changelists,
++                     opt_state->diff.invoke_diff_cmd,
+                      ctx, iterpool));
+         }
+     }
+Index: include/svn_config.h
+===================================================================
+--- include/svn_config.h	(.../trunk/subversion)	(revision 1542732)
++++ include/svn_config.h	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -120,6 +120,8 @@ typedef struct svn_config_t svn_config_t;
+ #define SVN_CONFIG_OPTION_DIFF_EXTENSIONS           "diff-extensions"
+ #define SVN_CONFIG_OPTION_DIFF3_CMD                 "diff3-cmd"
+ #define SVN_CONFIG_OPTION_DIFF3_HAS_PROGRAM_ARG     "diff3-has-program-arg"
++/** @since New in 1.9. */
++#define SVN_CONFIG_OPTION_INVOKE_DIFF_CMD           "invoke-diff-cmd"
+ #define SVN_CONFIG_OPTION_MERGE_TOOL_CMD            "merge-tool-cmd"
+ #define SVN_CONFIG_SECTION_MISCELLANY           "miscellany"
+ #define SVN_CONFIG_OPTION_GLOBAL_IGNORES            "global-ignores"
+Index: include/svn_io.h
+===================================================================
+--- include/svn_io.h	(.../trunk/subversion)	(revision 1542732)
++++ include/svn_io.h	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -1835,7 +1835,7 @@ svn_io_run_cmd(const char *path,
+  * @a diff_cmd must be non-NULL.
+  *
+  * Do all allocation in @a pool.
+- * @since New in 1.6.0.
++ * @since New in 1.6.0. 
+  */
+ svn_error_t *
+ svn_io_run_diff2(const char *dir,
+@@ -2361,6 +2361,23 @@ svn_io_file_readline(apr_file_t *file,
+ 
+ /** @} */
+ 
++/** Run the external diff command defined by the invoke-diff-cmd
++ *  option.
++ *  
++ *  @since New in 1.9.
++ */
++svn_error_t *
++svn_io_run_external_diff(const char *dir,
++                         const char *label1,
++                         const char *label2,
++                         const char *tmpfile1,
++                         const char *tmpfile2,
++                         int *pexitcode,
++                         apr_file_t *outfile,
++                         apr_file_t *errfile,
++                         const char *external_diff_cmd,
++                         apr_pool_t *scratch_pool);
++
+ #ifdef __cplusplus
+ }
+ #endif /* __cplusplus */
+Index: include/svn_client.h
+===================================================================
+--- include/svn_client.h	(.../trunk/subversion)	(revision 1542732)
++++ include/svn_client.h	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -3057,6 +3057,11 @@ svn_client_blame(const char *path_or_url,
+  * The above two options are mutually exclusive. It is an error to set
+  * both to TRUE.
+  *
++ * @a invoke_diff_cmd is used to call an external diff program but may
++ * not be @c NULL.  The command line invocation will override the
++ * invoke-diff-cmd invocation entry(if any) in the Subversion
++ * configuration file.
++ *
+  * Generated headers are encoded using @a header_encoding.
+  *
+  * Diff output will not be generated for binary files, unless @a
+@@ -3087,8 +3092,38 @@ svn_client_blame(const char *path_or_url,
+  * @note @a relative_to_dir doesn't affect the path index generated by
+  * external diff programs.
+  *
++ * @since New in 1.9.
++ */
++svn_error_t *
++svn_client_diff7(const apr_array_header_t *options,
++                 const char *path_or_url1,
++                 const svn_opt_revision_t *revision1,
++                 const char *path_or_url2,
++                 const svn_opt_revision_t *revision2,
++                 const char *relative_to_dir,
++                 svn_depth_t depth,
++                 svn_boolean_t ignore_ancestry,
++                 svn_boolean_t no_diff_added,
++                 svn_boolean_t no_diff_deleted,
++                 svn_boolean_t show_copies_as_adds,
++                 svn_boolean_t ignore_content_type,
++                 svn_boolean_t ignore_properties,
++                 svn_boolean_t properties_only,
++                 svn_boolean_t use_git_diff_format,
++                 const char *header_encoding,
++                 svn_stream_t *outstream,
++                 svn_stream_t *errstream,
++                 const apr_array_header_t *changelists,
++                 const char *invoke_diff_cmd,
++                 svn_client_ctx_t *ctx,
++                 apr_pool_t *pool);
++
++/** Similar to svn_client_diff7(), but without @a invoke_diff_cmd.
++ *
++ * @deprecated Provided for backward compatibility with the 1.8 API.
+  * @since New in 1.8.
+  */
++SVN_DEPRECATED
+ svn_error_t *
+ svn_client_diff6(const apr_array_header_t *diff_options,
+                  const char *path_or_url1,
+@@ -3246,13 +3281,47 @@ svn_client_diff(const apr_array_header_t *diff_opt
+  * be either a working-copy path or URL.
+  *
+  * If @a peg_revision is #svn_opt_revision_unspecified, behave
+- * identically to svn_client_diff6(), using @a path_or_url for both of that
++ * identically to svn_client_diff7(), using @a path_or_url for both of that 
+  * function's @a path_or_url1 and @a path_or_url2 arguments.
+  *
+- * All other options are handled identically to svn_client_diff6().
++ * All other options are handled identically to svn_client_diff7().
+  *
++ * @since New in 1.9.
++ */
++svn_error_t *
++svn_client_diff_peg7(const apr_array_header_t *diff_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,
++                     const char *relative_to_dir,
++                     svn_depth_t depth,
++                     svn_boolean_t ignore_ancestry,
++                     svn_boolean_t no_diff_added,
++                     svn_boolean_t no_diff_deleted,
++                     svn_boolean_t show_copies_as_adds,
++                     svn_boolean_t ignore_content_type,
++                     svn_boolean_t ignore_properties,
++                     svn_boolean_t properties_only,
++                     svn_boolean_t use_git_diff_format,
++                     const char *header_encoding,
++                     svn_stream_t *outstream,
++                     svn_stream_t *errstream,
++                     const apr_array_header_t *changelists,
++                     const char *invoke_diff_cmd,
++                     svn_client_ctx_t *ctx,
++                     apr_pool_t *pool);
++         
++
++/**
++ * Similar to svn_client_peg7(), but without @a no_diff_added set to
++ * FALSE, @a ignore_properties set to FALSE and @a properties_only set
++ * to FALSE.
++ *
++ * @deprecated Provided for backward compatibility with the 1.7 API.
+  * @since New in 1.8.
+  */
++SVN_DEPRECATED
+ svn_error_t *
+ svn_client_diff_peg6(const apr_array_header_t *diff_options,
+                      const char *path_or_url,
+@@ -3276,7 +3345,8 @@ svn_client_diff_peg6(const apr_array_header_t *dif
+                      svn_client_ctx_t *ctx,
+                      apr_pool_t *pool);
+ 
+-/** Similar to svn_client_diff6_peg6(), but with @a outfile and @a errfile,
++/**
++ * Similar to svn_client_diff_peg6(), but with @a outfile and @a errfile,
+  * instead of @a outstream and @a errstream, and with @a
+  * no_diff_added, @a ignore_properties, and @a properties_only always
+  * passed as @c FALSE (which means that additions and property changes
+Index: include/private/svn_io_private.h
+===================================================================
+--- include/private/svn_io_private.h	(.../trunk/subversion)	(revision 1542732)
++++ include/private/svn_io_private.h	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -96,6 +96,51 @@ svn_stream__is_buffered(svn_stream_t *stream);
+ apr_file_t *
+ svn_stream__aprfile(svn_stream_t *stream);
+ 
++/** Parse a user defined command to contain dynamically created labels
++ *  and filenames.  This function serves both diff and diff3 parsing
++ *  requirements.
++ *
++ *  When used in a diff context: (responding parse tokens in braces)
++ *
++ *  @a label1 (%svn_label_old) refers to the label of @a tmpfile1
++ *  (%svn_old) which is the pristine copy.
++ *
++ *  @a label2 (%svn_label_new) refers to the label of @a tmpfile2
++ *  (%svn_new) which is the altered copy.
++ *
++ *  When used in a diff3 context:
++ *
++ *  @a label1 refers to the label of @a tmpfile1 which is the 'mine'
++ *  copy.
++ *
++ *  @a label2 refers to the label of @a tmpfile2 which is the 'older'
++ *  copy.
++ *
++ *  @a label3 (%svn_label_base) refers to the label of @a base
++ *  (%svn_base) which is the 'base' copy.
++ *
++ *  In general:
++ *
++ *  @a cmd is a user defined string containing 0 or more parse tokens
++ *  which are expanded by the required labels and filenames.
++ * 
++ *  @a pool is used for temporary allocations.
++ *
++ *  @return A NULL-terminated character array.
++ * 
++ * @since New in 1.9.
++ */
++const char **
++svn_io__create_custom_diff_cmd(const char *label1,
++                               const char *label2,
++                               const char *label3,
++                               const char *from,
++                               const char *to,
++                               const char *base,
++                               const char *cmd,
++                               apr_pool_t *pool);
++
++
+ #ifdef __cplusplus
+ }
+ #endif /* __cplusplus */
+Index: include/svn_error_codes.h
+===================================================================
+--- include/svn_error_codes.h	(.../trunk/subversion)	(revision 1542732)
++++ include/svn_error_codes.h	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -1204,6 +1204,11 @@ SVN_ERROR_START
+              SVN_ERR_CLIENT_CATEGORY_START + 23,
+              "The operation is forbidden by the server")
+ 
++  /** @since New in 1.9 */
++  SVN_ERRDEF(SVN_ERR_CLIENT_DIFF_CMD,
++             SVN_ERR_CLIENT_CATEGORY_START + 24,
++             "More than one diff command defined")
++
+   /* misc errors */
+ 
+   SVN_ERRDEF(SVN_ERR_BASE,
+Index: libsvn_client/diff.c
+===================================================================
+--- libsvn_client/diff.c	(.../trunk/subversion)	(revision 1542732)
++++ libsvn_client/diff.c	(.../branches/invoke-diff-cmd-feature/subversion)	(revision 1542685)
+@@ -428,7 +428,7 @@ print_git_diff_header(svn_stream_t *os,
+ 
+ /* A helper func that writes out verbal descriptions of property diffs
+    to OUTSTREAM.   Of course, OUTSTREAM will probably be whatever was
+-   passed to svn_client_diff6(), which is probably stdout.
++   passed to svn_client_diff7(), which is probably stdout.
+ 
+    ### FIXME needs proper docstring
+ 
+@@ -536,6 +536,9 @@ struct diff_cmd_baton {
+   /* If non-null, the external diff command to invoke. */
+   const char *diff_cmd;
+ 
++  /* external custom diff command */
++  const char *invoke_diff_cmd;
++
+   /* This is allocated in this struct's pool or a higher-up pool. */
+   union {
+     /* If 'diff_cmd' is null, then this is the parsed options to
+@@ -564,7 +567,7 @@ struct diff_cmd_baton {
+   const char *orig_path_2;
+ 
+   /* These are the numeric representations of the revisions passed to
+-     svn_client_diff6(), either may be SVN_INVALID_REVNUM.  We need these
++     svn_client_diff7(), either may be SVN_INVALID_REVNUM.  We need these
+      because some of the svn_wc_diff_callbacks4_t don't get revision
+      arguments.
+ 
+@@ -612,6 +615,7 @@ struct diff_cmd_baton {
+ 
+   /* Whether the local diff target of a repos->wc diff is a copy. */
+   svn_boolean_t repos_wc_diff_target_is_copy;
++
+ };
+ 
+ /* An helper for diff_dir_props_changed, diff_file_changed and diff_file_added
+@@ -786,8 +790,12 @@ diff_content_changed(svn_boolean_t *wrote_header,
+       return SVN_NO_ERROR;
+     }
+ 
++  if (diff_cmd_baton->diff_cmd && diff_cmd_baton->invoke_diff_cmd)
++      return svn_error_create(SVN_ERR_CLIENT_DIFF_CMD, NULL,
++                              _("diff-cmd and invoke-diff-cmd are "
++                                "mutually exclusive."));
+ 
+-  if (diff_cmd_baton->diff_cmd)
++  if (diff_cmd_baton->diff_cmd || diff_cmd_baton->invoke_diff_cmd)
+     {
+       apr_file_t *outfile;
+       apr_file_t *errfile;
+@@ -817,7 +825,6 @@ diff_content_changed(svn_boolean_t *wrote_header,
+         SVN_ERR(svn_io_open_unique_file3(&outfile, &outfilename, NULL,
+                                          svn_io_file_del_on_pool_cleanup,
+                                          scratch_pool, scratch_pool));
+-
+       errfile = svn_stream__aprfile(errstream);
+       if (errfile)
+         errfilename = NULL;
+@@ -826,13 +833,24 @@ diff_content_changed(svn_boolean_t *wrote_header,
+                                          svn_io_file_del_on_pool_cleanup,
+                                          scratch_pool, scratch_pool));
+ 
+-      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, outfile, errfile,
+-                               diff_cmd_baton->diff_cmd, scratch_pool));
++      /* "." is a non-canonical path for the diff process's working directory. */
++      if (diff_cmd_baton->diff_cmd) 
++        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, outfile, errfile,
++                                 diff_cmd_baton->diff_cmd, scratch_pool));
++      else
++        { 
++          SVN_ERR(svn_io_run_external_diff(".", 
++                                           label1, label2,
++                                           tmpfile1, tmpfile2,
++                                           &exitcode, outfile, errfile,
++                                           diff_cmd_baton->invoke_diff_cmd,
++                                           scratch_pool));
++        }
+ 
+       /* Now, open and copy our files to our output streams. */
+       if (outfilename)
+@@ -1534,8 +1552,8 @@ diff_prepare_repos_repos(const char **url1,
+ 
+ /* A Theoretical Note From Ben, regarding do_diff().
+ 
+-   This function is really svn_client_diff6().  If you read the public
+-   API description for svn_client_diff6(), it sounds quite Grand.  It
++   This function is really svn_client_diff7().  If you read the public
++   API description for svn_client_diff7(), 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.
+@@ -1559,7 +1577,7 @@ diff_prepare_repos_repos(const char **url1,
+    pigeonholed into one of these use-cases, we currently bail with a
+    friendly apology.
+ 
+-   Perhaps someday a brave soul will truly make svn_client_diff6()
++   Perhaps someday a brave soul will truly make svn_client_diff7()
+    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
+@@ -1572,7 +1590,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_diff6 was called in a way "
++                          _("Sorry, svn_client_diff7 was called in a way "
+                             "that is not yet supported"));
+ }
+ 
+@@ -1581,7 +1599,7 @@ unsupported_diff_error(svn_error_t *child_err)
+    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(). */
++   All other options are the same as those passed to svn_client_diff7(). */
+ static svn_error_t *
+ diff_wc_wc(const char *path1,
+            const svn_opt_revision_t *revision1,
+@@ -1664,7 +1682,7 @@ diff_wc_wc(const char *path1,
+    and the actual two paths compared are determined by following copy
+    history from PATH_OR_URL2.
+ 
+-   All other options are the same as those passed to svn_client_diff6(). */
++   All other options are the same as those passed to svn_client_diff7(). */
+ static svn_error_t *
+ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks,
+                  struct diff_cmd_baton *callback_baton,
+@@ -1809,7 +1827,7 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *c
+    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_diff6(). */
++   All other options are the same as those passed to svn_client_diff7(). */
+ static svn_error_t *
+ diff_repos_wc(const char *path_or_url1,
+               const svn_opt_revision_t *revision1,
+@@ -2144,7 +2162,7 @@ do_diff(const svn_wc_diff_callbacks4_t *callbacks,
+    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_diff6(). */
++   All other options are the same as those passed to svn_client_diff7(). */
+ static svn_error_t *
+ diff_summarize_repos_wc(svn_client_diff_summarize_func_t summarize_func,
+                         void *summarize_baton,
+@@ -2188,7 +2206,7 @@ diff_summarize_repos_wc(svn_client_diff_summarize_
+    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(). */
++   All other options are the same as those passed to svn_client_diff7(). */
+ static svn_error_t *
+ diff_summarize_wc_wc(svn_client_diff_summarize_func_t summarize_func,
+                      void *summarize_baton,
+@@ -2463,15 +2481,17 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton
+ {
+   const char *diff_cmd = NULL;
+ 
+-  /* See if there is a diff command and/or diff arguments. */
++  /* old style diff_cmd has precedence in config file */
+   if (config)
+     {
+       svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG);
 +      
-+      A return value of 2 typically occurs when diff cannot read its input
-+      or write to its output, but in any case we probably ought to return an
-+      error for anything other than 0 or 1 as the output is likely to be
-+      corrupt.
-+    */
-+   if (*pexitcode != 0 && *pexitcode != 1)
-+     {
-+       int i;
-+       const char *failed_command = "";
-+ 
-+       for (i = 0; cmd[i]; ++i)
-+           failed_command = apr_pstrcat(pool, failed_command, 
-+                                        cmd[i], " ", (char*) NULL);
-+       svn_pool_destroy(scratch_pool);
-+       return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
-+                                _("'%s' was expanded to '%s' and returned %d"),
-+                                external_diff_cmd,
-+                                failed_command,
-+                                *pexitcode);
-+     }
-+   else
-+     svn_pool_destroy(scratch_pool);
-+   return SVN_NO_ERROR;
-+ }
-*************** svn_io_run_diff2(const char *dir,
-*** 3010 ****
---- 3169,3173 ----
-+   /* 
-+   This function can be tested by using the test 
-+   /subversion/tests/cmdline/diff_tests.py diff_external_diffcmd
-+   */
-+ 
-
-================================================================================
-
-*** /home/g/trunk/subversion/svn/cl.h	2013-09-24 21:40:35.782748266 +0100
---- /home/g/branches/invoke-diff-cmd-feature/subversion/svn/cl.h	2013-11-06 19:53:45.955497039 +0000
-*************** typedef struct svn_cl__opt_state_t
-*** 186 ****
---- 187,188 ----
-+   const char *invoke_diff_cmd;       /* the format string to specify args   */
-+                                      /* for the external diff cmd           */
-
-================================================================================
-
-*** /home/g/trunk/subversion/svn/diff-cmd.c	2013-08-07 20:56:48.209220881 +0100
---- /home/g/branches/invoke-diff-cmd-feature/subversion/svn/diff-cmd.c	2013-08-21 15:12:59.268653617 +0100
-*************** svn_cl__diff(apr_getopt_t *os,
-*** 406 ****
-!             SVN_ERR(svn_client_diff6(
---- 406 ----
-!             SVN_ERR(svn_client_diff7(
-*************** svn_cl__diff(apr_getopt_t *os,
-*** 425 ****
---- 426 ----
-+                      opt_state->diff.invoke_diff_cmd,                        
-*************** svn_cl__diff(apr_getopt_t *os,
-*** 457 ****
-!             SVN_ERR(svn_client_diff_peg6(
---- 458 ----
-!             SVN_ERR(svn_client_diff_peg7(
-*************** svn_cl__diff(apr_getopt_t *os,
-*** 476 ****
---- 478 ----
-+                      opt_state->diff.invoke_diff_cmd,
-
-================================================================================
-
-*** /home/g/trunk/subversion/svn/log-cmd.c	2013-09-24 21:40:35.658747651 +0100
---- /home/g/branches/invoke-diff-cmd-feature/subversion/svn/log-cmd.c	2013-09-26 13:50:37.027787119 +0100
-*************** struct log_receiver_baton
-*** 69 ****
---- 70,72 ----
-+   /* Custom diff command. */
-+   const char *invoke_diff_cmd;
-+ 
-*************** display_diff(const svn_log_entry_t *log_
-*** 104 ****
---- 108 ----
-+              const char *invoke_diff_cmd,
-*************** display_diff(const svn_log_entry_t *log_
-*** 125 ****
-!   SVN_ERR(svn_client_diff_peg6(diff_options,
---- 129 ----
-!   SVN_ERR(svn_client_diff_peg7(diff_options,
-*************** display_diff(const svn_log_entry_t *log_
-*** 142 ****
---- 147 ----
-+                                invoke_diff_cmd,
-*************** log_entry_receiver(void *baton,
-*** 468 ****
---- 474 ----
-+                            lb->invoke_diff_cmd,
-*************** svn_cl__log(apr_getopt_t *os,
-*** 710 ****
---- 717,721 ----
-+   if (opt_state->diff.diff_cmd && opt_state->diff.diff_cmd)
-+     return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-+                             _("'diff-cmd' and 'invoke-diff-cmd' options are "
-+                               "mutually exclusive"));
+       svn_config_get(cfg, &diff_cmd, SVN_CONFIG_SECTION_HELPERS,
+                      SVN_CONFIG_OPTION_DIFF_CMD, NULL);
+       if (options == NULL)
+         {
+           const char *diff_extensions;
++          
+           svn_config_get(cfg, &diff_extensions, SVN_CONFIG_SECTION_HELPERS,
+                          SVN_CONFIG_OPTION_DIFF_EXTENSIONS, NULL);
+           if (diff_extensions)
+@@ -2478,15 +2498,28 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton
+             options = svn_cstring_split(diff_extensions, " \t\n\r", TRUE, pool);
+         }
+     }
+-
+   if (options == NULL)
+     options = apr_array_make(pool, 0, sizeof(const char *));
+-
 +  
-*************** svn_cl__log(apr_getopt_t *os,
-*** 727 ****
---- 742,746 ----
-+   if (opt_state->diff.invoke_diff_cmd && (! opt_state->show_diff))
-+     return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-+                             _("'invoke-diff-cmd' option requires 'diff' "
-+                               "option"));
-+ 
-*************** svn_cl__log(apr_getopt_t *os,
-*** 790 ****
---- 810 ----
-+   lb.invoke_diff_cmd = opt_state->diff.invoke_diff_cmd;
-
-================================================================================
-
-*** /home/g/trunk/subversion/svnlook/svnlook.c	2013-09-24 21:40:37.690757727 +0100
---- /home/g/branches/invoke-diff-cmd-feature/subversion/svnlook/svnlook.c	2013-10-24 16:04:38.939829801 +0100
-*************** enum
-*** 102 ****
---- 103 ----
-+     svnlook__invoke_diff_cmd,
-*************** static const apr_getopt_option_t options
-*** 137 ****
---- 139,141 ----
-+   {"invoke-diff-cmd",   svnlook__invoke_diff_cmd, 1,
-+    N_("Customizable diff command (see svn help diff)")},
-+ 
-*************** static const svn_opt_subcommand_desc2_t
-*** 227 ****
-!     svnlook__ignore_properties, svnlook__properties_only} },
---- 231,232 ----
-!     svnlook__ignore_properties, svnlook__properties_only,
-!     svnlook__invoke_diff_cmd} },
-*************** struct svnlook_opt_state
-*** 332 ****
---- 338 ----
-+   const char *invoke_diff_cmd;      /* --invoke-diff-cmd */
-*************** typedef struct svnlook_ctxt_t
-*** 355 ****
---- 362 ----
-+   const char *invoke_diff_cmd;
-*************** print_diff_tree(svn_stream_t *out_stream
-*** 947 ****
-!           if (c->diff_cmd)
---- 954 ----
-!           if (c->diff_cmd || c->invoke_diff_cmd)
-*************** print_diff_tree(svn_stream_t *out_stream
-*** 1001 ****
---- 1009 ----
-+               if (c->diff_cmd)
-*************** print_diff_tree(svn_stream_t *out_stream
-*** 1009 ****
---- 1018,1033 ----
-+               else if (c->invoke_diff_cmd)
-+                 SVN_ERR(svn_io_run_external_diff(".",
-+                                                  orig_label,
-+                                                  new_label,
-+                                                  orig_path,
-+                                                  new_path,
-+                                                  &exitcode,
-+                                                  outfile,
-+                                                  errfile,
-+                                                  c->invoke_diff_cmd,
-+                                                  pool));
-+                 
-+               SVN_ERR(svn_io_file_close(outfile, pool));
-+               SVN_ERR(svn_io_file_close(errfile, pool));
-+ 
-+ 
-*************** get_ctxt_baton(svnlook_ctxt_t **baton_p,
-*** 2093 ****
---- 2118 ----
-+   baton->invoke_diff_cmd = opt_state->invoke_diff_cmd;
-*************** main(int argc, const char *argv[])
-*** 2611 ****
---- 2636,2639 ----
-+         case svnlook__invoke_diff_cmd:
-+           opt_state.invoke_diff_cmd = opt_arg;
-+           break;
-+ 
-*************** main(int argc, const char *argv[])
-*** 2636 ****
---- 2665,2671 ----
-+ 
-+   /* The --diff-cmd and --invoke-diff-cmd options may not co-exist. */
-+   if (opt_state.diff_cmd && opt_state.invoke_diff_cmd)
-+     SVN_INT_ERR(svn_error_create
-+                 (SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS, NULL,

[... 524 lines stripped ...]