Hi, my branch has grown into a veritable forest, and so, I thought that it would be convenient to present the net code changes in a file called 'entire-branch-code' (attached).
This is generated like so: 1) Merge trunk to branch 2) run this bash command: svn log --stop-on-copy | grep '^*\ subversion' |\ awk '{gsub(/^ +| +$/,"")} {print $0}' | cut -d '*' -f 2 |\ sort --unique | while read line; do echo $line | if [ -a $line ];\ then echo ""; echo \ "================================================================================";\ echo "";\ diff -p --context=0 -b -B -w "$HOME/trunk/$line" "$PWD/$line"; fi;\ done > entire-branch-code Is this useful? Can this be improved? thanks, Gabriela
================================================================================ *** /home/g/trunk/subversion/include/svn_client.h 2013-09-24 21:40:40.306770699 +0100 --- /home/g/branches/invoke-diff-cmd-feature/subversion/include/svn_client.h 2013-09-26 13:50:43.967821533 +0100 *************** svn_client_blame(const char *path_or_url *** 3023 **** --- 3024,3028 ---- + * @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 *** 3053 **** --- 3059,3087 ---- + * @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 with @a invoke_diff_cmd. + * + * @deprecated Provided for backward compatibility with the 1.8 API. *************** svn_client_blame(const char *path_or_url *** 3055 **** --- 3090 ---- + SVN_DEPRECATED *************** svn_client_diff(const apr_array_header_t *** 3213 **** ! * identically to svn_client_diff6(), using @a path_or_url for both of that --- 3248 ---- ! * identically to svn_client_diff7(), using @a path_or_url for both of that *************** svn_client_diff(const apr_array_header_t *** 3216 **** ! * All other options are handled identically to svn_client_diff6(). --- 3251 ---- ! * All other options are handled identically to svn_client_diff7(). *************** svn_client_diff(const apr_array_header_t *** 3217 **** --- 3253,3285 ---- + * @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 with @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 *** 3219 **** --- 3288 ---- + SVN_DEPRECATED *************** svn_client_diff_peg6(const apr_array_hea *** 3243 **** ! /** Similar to svn_client_diff6_peg6(), but with @a outfile and @a errfile, --- 3312,3313 ---- ! /** ! * Similar to svn_client_diff6_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") + ================================================================================ *** /home/g/trunk/subversion/include/svn_io.h 2013-08-07 20:56:48.269221179 +0100 --- /home/g/branches/invoke-diff-cmd-feature/subversion/include/svn_io.h 2013-10-12 14:15:01.434000142 +0100 *************** svn_io_run_cmd(const char *path, *** 1824,1825 **** ! * @since New in 1.6.0. ! */ --- 1824 ---- ! * @since New in 1.6.0. */ *************** svn_io_file_readline(apr_file_t *file, *** 2348 **** --- 2348,2412 ---- + + /** 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 (;l1) refers to the label of @a tmpfile1 (;f1) which is + * the pristine copy. + * + * @a label2 (;l2) refers to the label of @a tmpfile2 (;f2) 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 (;l3) refers to the label of @a base (;f3) which is + * the 'base' copy. + * + * A parse token can be escaped by prefixing a ';'. Any other + * strings containing ';' are not affected. + * + * 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 ** + __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); + + + /** 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); ================================================================================ *** /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-08-21 15:18:31.626301687 +0100 *************** 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 { *** 567 **** ! svn_client_diff6(), either may be SVN_INVALID_REVNUM. We need these --- 567 ---- ! svn_client_diff7(), either may be SVN_INVALID_REVNUM. We need these *************** struct diff_cmd_baton { *** 614 **** --- 615,618 ---- + + /* external custom diff command */ + const char *invoke_diff_cmd; + *************** 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_ *** 2469 **** ! svn_config_t *cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG); --- 2487,2490 ---- ! svn_config_t *cfg; ! ! cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG); ! *************** 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)); --- 2507,2508 ---- ! SVN_ERR(svn_path_cstring_to_utf8(&diff_cmd_baton->diff_cmd, ! diff_cmd, pool)); *************** set_up_diff_cmd_and_options(struct diff_ *** 2488 **** --- 2510,2515 ---- + { + if (config) /* check if there is a invoke_diff_cmd in the config file */ + { + svn_config_t *cfg; + + cfg = svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG); *************** set_up_diff_cmd_and_options(struct diff_ *** 2489 **** --- 2517,2526 ---- + 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_ *** 2494,2495 **** ! const char **argv = NULL; ! int argc = options->nelts; --- 2531,2535 ---- ! const char **argv; ! int argc; ! ! argc = options->nelts; ! argv = NULL; *************** set_up_diff_cmd_and_options(struct diff_ *** 2502 **** ! APR_ARRAY_IDX(options, i, const char *), pool)); --- 2543,2545 ---- ! APR_ARRAY_IDX(options, i, ! const char *), ! pool)); *************** svn_error_t * *** 2555 **** ! svn_client_diff6(const apr_array_header_t *options, --- 2598 ---- ! svn_client_diff7(const apr_array_header_t *options, *************** svn_client_diff6(const apr_array_header_ *** 2573 **** --- 2617 ---- + const char *invoke_diff_cmd, *************** svn_client_diff6(const apr_array_header_ *** 2590 **** --- 2635 ---- + diff_cmd_baton.invoke_diff_cmd = invoke_diff_cmd; *************** svn_error_t * *** 2622 **** ! svn_client_diff_peg6(const apr_array_header_t *options, --- 2667 ---- ! svn_client_diff_peg7(const apr_array_header_t *options, *************** svn_client_diff_peg6(const apr_array_hea *** 2640 **** --- 2686 ---- + const char *invoke_diff_cmd, *************** svn_client_diff_peg6(const apr_array_hea *** 2653 **** --- 2700 ---- + diff_cmd_baton.invoke_diff_cmd = invoke_diff_cmd; ================================================================================ *** /home/g/trunk/subversion/libsvn_subr/config_file.c 2013-07-12 22:06:43.862072458 +0100 --- /home/g/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/config_file.c 2013-08-21 15:12:45.828586973 +0100 *************** svn_config_ensure(const char *config_dir *** 1120 **** --- 1121,1125 ---- + "### 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 = \"diff -y --label ;l1 ;f1 --label ;l2 ;f2\"" NL ================================================================================ ================================================================================ *** /home/g/trunk/subversion/libsvn_subr/io.c 2013-10-12 21:38:58.478086111 +0100 --- /home/g/branches/invoke-diff-cmd-feature/subversion/libsvn_subr/io.c 2013-10-12 16:52:34.768876699 +0100 *************** svn_io_run_cmd(const char *path, *** 3029,3034 **** ! ! svn_error_t * ! svn_io_run_diff2(const char *dir, ! const char *const *user_args, ! int num_user_args, ! const char *label1, --- 3029,3030 ---- ! const char ** ! __create_custom_diff_cmd(const char *label1, *************** svn_io_run_diff2(const char *dir, *** 3035 **** --- 3032 ---- + const char *label3, *************** svn_io_run_diff2(const char *dir, *** 3038,3041 **** ! int *pexitcode, ! apr_file_t *outfile, ! apr_file_t *errfile, ! const char *diff_cmd, --- 3035,3036 ---- ! const char *base, ! const char *cmd, *************** svn_io_run_diff2(const char *dir, *** 3044,3048 **** ! const char **args; ! int i; ! int exitcode; ! int nargs = 4; /* the diff command itself, two paths, plus a trailing NULL */ ! apr_pool_t *subpool = svn_pool_create(pool); --- 3039,3042 ---- ! /* ! This function can be tested with: ! /subversion/tests/cmdline/diff_tests.py diff_invoke_external_diffcmd ! */ *************** svn_io_run_diff2(const char *dir, *** 3050,3051 **** ! if (pexitcode == NULL) ! pexitcode = &exitcode; --- 3044,3047 ---- ! apr_array_header_t *words; ! const char ** result; ! size_t argv, item, i, delimiters = 6; ! apr_pool_t *scratch_pool = svn_pool_create(pool); *************** svn_io_run_diff2(const char *dir, *** 3053,3056 **** ! if (user_args != NULL) ! nargs += num_user_args; ! else ! nargs += 1; /* -u */ --- 3049,3061 ---- ! struct replace_tokens_tab ! { ! const char *delimiter; ! const char *replace; ! } tokens_tab[] = { ! { ";l1", label1 }, ! { ";l2", label2 }, ! { ";l3", label3 }, ! { ";f1", from }, ! { ";f2", to }, ! { ";f3", base }, ! { NULL, NULL } ! }; *************** svn_io_run_diff2(const char *dir, *** 3058,3061 **** ! if (label1 != NULL) ! nargs += 2; /* the -L and the label itself */ ! if (label2 != NULL) ! nargs += 2; /* the -L and the label itself */ --- 3063 ---- ! words = svn_cstring_split(cmd, " ", TRUE, scratch_pool); *************** svn_io_run_diff2(const char *dir, *** 3063 **** ! args = apr_palloc(subpool, nargs * sizeof(char *)); --- 3065,3066 ---- ! result = apr_palloc(pool, ! (words->nelts+1) * words->elt_size * sizeof(char *) ); *************** svn_io_run_diff2(const char *dir, *** 3065,3066 **** ! i = 0; ! args[i++] = diff_cmd; --- 3068,3070 ---- ! for (item = 0, argv = 0; item < words->nelts; argv++, item++) ! { ! svn_stringbuf_t *word; *************** svn_io_run_diff2(const char *dir, *** 3068 **** ! if (user_args != NULL) --- 3072,3076 ---- ! 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_io_run_diff2(const char *dir, *** 3070,3072 **** ! int j; ! for (j = 0; j < num_user_args; ++j) ! args[i++] = user_args[j]; --- 3078,3090 ---- ! 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; *************** svn_io_run_diff2(const char *dir, *** 3075,3077 **** - args[i++] = "-u"; /* assume -u if the user didn't give us any args */ - - if (label1 != NULL) --- 3092 ---- *************** svn_io_run_diff2(const char *dir, *** 3079,3080 **** ! args[i++] = "-L"; ! args[i++] = label1; --- 3094,3095 ---- ! svn_stringbuf_appendcstr(complete, " "); ! item++; *************** svn_io_run_diff2(const char *dir, *** 3082 **** ! if (label2 != NULL) --- 3097,3114 ---- ! } ! } ! ! for (i = 0; i < delimiters; i++) ! { ! char *found = strstr(word->data, tokens_tab[i].delimiter); ! int len; ! ! if (!found) ! continue; ! ! len = word->len - strlen(found) - 1; ! ! /* if we find a protective semi-colon in front of this, consume it */ ! if ( (len >= 0) ! && (word->data[len] == ';') ) ! svn_stringbuf_remove(word, len, 1); ! else *************** svn_io_run_diff2(const char *dir, *** 3084,3085 **** ! args[i++] = "-L"; ! args[i++] = label2; --- 3116,3120 ---- ! svn_stringbuf_replace(word, found - word->data, ! strlen(tokens_tab[i].delimiter), ! tokens_tab[i].replace, ! strlen(tokens_tab[i].replace)); ! i = delimiters; *************** svn_io_run_diff2(const char *dir, *** 3086 **** --- 3122,3125 ---- + } + result[argv] = word->data; + } + result[argv] = NULL; *************** svn_io_run_diff2(const char *dir, *** 3088,3090 **** ! args[i++] = svn_dirent_local_style(from, subpool); ! args[i++] = svn_dirent_local_style(to, subpool); ! args[i++] = NULL; --- 3127,3129 ---- ! svn_pool_destroy(scratch_pool); ! return result; ! } *************** svn_io_run_diff2(const char *dir, *** 3092 **** ! SVN_ERR_ASSERT(i == nargs); --- 3131,3144 ---- ! 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; *************** svn_io_run_diff2(const char *dir, *** 3094,3095 **** ! SVN_ERR(svn_io_run_cmd(dir, diff_cmd, args, pexitcode, NULL, TRUE, ! NULL, outfile, errfile, subpool)); --- 3146,3147 ---- ! if (0 == strlen(external_diff_cmd)) ! return svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, NULL); *************** svn_io_run_diff2(const char *dir, *** 3097 **** ! /* The man page for (GNU) diff describes the return value as: --- 3149,3153 ---- ! cmd = __create_custom_diff_cmd(label1, label2, NULL, ! tmpfile1, tmpfile2, NULL, ! external_diff_cmd, pool); ! if (pexitcode == NULL) ! pexitcode = &exitcode; *************** svn_io_run_diff2(const char *dir, *** 3099,3100 **** ! "An exit status of 0 means no differences were found, 1 means ! some differences were found, and 2 means trouble." --- 3155,3156 ---- ! SVN_ERR(svn_io_run_cmd(dir, cmd[0], cmd, pexitcode, NULL, TRUE, ! NULL, outfile, errfile, pool)); *************** svn_io_run_diff2(const char *dir, *** 3102,3106 **** - 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. - */ --- 3157 ---- *************** svn_io_run_diff2(const char *dir, *** 3107 **** --- 3159,3166 ---- + { + int i; + const char *failed_command = ""; + + for (i = 0; cmd[i]; ++i) + failed_command = apr_pstrcat(pool, failed_command, + cmd[i], " ", (char*) NULL); + *************** svn_io_run_diff2(const char *dir, *** 3109,3110 **** ! _("'%s' returned %d"), ! svn_dirent_local_style(diff_cmd, pool), --- 3168,3170 ---- ! _("'%s' was expanded to '%s' and returned %d"), ! external_diff_cmd, ! failed_command, *************** svn_io_run_diff2(const char *dir, *** 3111 **** --- 3172,3174 ---- + } + return SVN_NO_ERROR; + } *************** svn_io_run_diff2(const char *dir, *** 3113 **** ! svn_pool_destroy(subpool); --- 3176,3197 ---- ! svn_error_t * ! svn_io_run_diff2(const char *dir, ! const char *const *user_args, ! int num_user_args, ! const char *label1, ! const char *label2, ! const char *from, ! const char *to, ! int *pexitcode, ! apr_file_t *outfile, ! apr_file_t *errfile, ! 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 ! */ ! svn_stringbuf_t *com; ! com = svn_stringbuf_create_empty(pool); ! svn_stringbuf_appendcstr(com, diff_cmd); ! svn_stringbuf_appendcstr(com, " "); *************** svn_io_run_diff2(const char *dir, *** 3115 **** ! return SVN_NO_ERROR; --- 3199,3206 ---- ! if (user_args != NULL) ! { ! int j; ! for (j = 0; j < num_user_args; ++j) ! { ! svn_stringbuf_appendcstr(com, user_args[j]); ! svn_stringbuf_appendcstr(com, " "); ! } *************** svn_io_run_diff2(const char *dir, *** 3116 **** --- 3208,3209 ---- + else /* assume -u if the user didn't give us any args */ + svn_stringbuf_appendcstr(com, "-u "); *************** svn_io_run_diff2(const char *dir, *** 3117 **** --- 3211,3229 ---- + if (label1 != NULL) + svn_stringbuf_appendcstr(com,"-L ;l1 "); + + if (label2 != NULL) + svn_stringbuf_appendcstr(com,"-L ;l2 "); + + svn_stringbuf_appendcstr(com,";f1 ;f2 "); + + return svn_io_run_external_diff(dir, + label1, + label2, + from, + to, + pexitcode, + outfile, + errfile, + com->data, + pool); + } ================================================================================ *** /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-09-26 13:50:36.815786068 +0100 *************** 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_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-09-26 13:50:47.043836786 +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, + _("Cannot use the '--diff-cmd' option with the " + "'--invoke-diff-cmd' option"))); ================================================================================ *** /home/g/trunk/subversion/svn/svn.c 2013-09-24 21:40:35.782748266 +0100 --- /home/g/branches/invoke-diff-cmd-feature/subversion/svn/svn.c 2013-10-12 14:55:38.646085624 +0100 *************** typedef enum svn_cl__longopt_t { *** 85 **** --- 86 ---- + opt_invoke_diff_cmd, *************** const apr_getopt_option_t svn_cl__option *** 346 **** --- 348,374 ---- + {"invoke-diff-cmd", opt_invoke_diff_cmd, 1, + N_("use ARG as format string for external diff command\n" + " " + "invocation. \n \n" + " " + "Substitutions: ;f1 original file \n" + " " + " ;f2 changed file \n" + " " + " ;l1 label of the original file \n" + " " + " ;l2 label of the changed file \n" + " " + "Examples: --invoke-diff-cmd=\"diff -y ;f1 ;f2\" \n" + " " + " --invoke-diff-cmd=\"kdiff3 -auto -o /home/u/log \\ \n" + " " + " +;f1 ;l2 --L1 ;l1 --L2 \"Custom Label\" \" \n" + " " + "The delimiter ';' can be escaped by adding a ';', which\n" + " " + "will be consumed in the process. The delimiter can \n" + " " + "appear anywhere in the string, ie, file=;f1 will expand\n" + " " + "as expected and file=;;f1 will be rendered as file=;f1.\n" + )}, *************** const svn_opt_subcommand_desc2_t svn_cl_ *** 613 **** ! opt_force, opt_xml, opt_use_git_diff_format, opt_patch_compatible} }, --- 641,642 ---- ! opt_force, opt_xml, opt_use_git_diff_format, opt_patch_compatible, ! opt_invoke_diff_cmd} }, *************** sub_main(int argc, const char *argv[], a *** 2140 **** --- 2170,2172 ---- + case opt_invoke_diff_cmd: + opt_state.diff.invoke_diff_cmd = apr_pstrdup(pool, opt_arg); + break; *************** sub_main(int argc, const char *argv[], a *** 2555,2556 **** ! /* Disallow simultaneous use of both --diff-cmd and ! --internal-diff. */ --- 2587,2588 ---- ! /* Disallow simultaneous use of both --diff-cmd, --invoke-diff-cmd ! and --internal-diff. */ *************** sub_main(int argc, const char *argv[], a *** 2564 **** --- 2597,2612 ---- + 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); + } + *************** sub_main(int argc, const char *argv[], a *** 2779 **** --- 2828,2830 ---- + 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); ================================================================================ *** /home/g/trunk/subversion/tests/cmdline/diff_tests.py 2013-08-21 14:38:53.038506909 +0100 --- /home/g/branches/invoke-diff-cmd-feature/subversion/tests/cmdline/diff_tests.py 2013-10-12 14:34:04.175666692 +0100 *************** def diff_wrong_extension_type(sbox): *** 3225 **** --- 3226 ---- + #---------------------------------------------------------------------- *************** def diff_external_diffcmd(sbox): *** 3262 **** --- 3264,3312 ---- + # 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 ;l1 -> label 1 + "iota (revision 1)\n", + + # correct file ;f1 -> file1 + os.path.abspath(svntest.wc.text_base_path("iota")) + "\n", + + # correct label ;l2 -> label 2 + "iota (working copy)\n", + + # correct file ;f2 -> file2 + 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 "+;f2+" -> "+" + file2 + "+" + "+" + os.path.abspath("iota") + "+\n", + + # removal of protective ';' ";;f1" -> ";f1" + ";f1\n", + ]) + + svntest.actions.run_and_verify_svn(None, expected_output, [], + 'diff', + '--invoke-diff-cmd='+diff_script_path+ + ' ;l1 ;f1 ;l2 ;f2 \"X Y Z\" +;f2+ ;;f1', + iota_path) + *************** test_list = [ None, *** 4690 **** --- 4741 ---- + diff_invoke_external_diffcmd,