Hi,
All my manual tests for the current --invoke-diff-cmd work fine, but I
cannot get the test suite to work.
I added the following new test (nr 49) to diff_tests.py:
[[[
# Check the order of the arguments for an external diff tool
def diff_invoke_external_diffcmd(sbox):
"svn diff --diff-invoke-cmd has correct arguments"
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",
"--- l1\n",
"+++ l2\n",
"@@ -1 +1,2 @@\n",
"This is the file 'iota'.\n",
"+new text in iota\n",
"\ No newline at end of file\n",
# os.path.abspath(svntest.wc.text_base_path("iota")) + "\n",
#os.path.abspath("iota") + "\n
])
# Check that the output of diff corresponds with the expected arguments,
# in the correct order.
svntest.actions.run_and_verify_svn(None, expected_output, [],
'diff', '--invoke-diff-cmd="diff
-u --label l1 %f1% --label l2 %f2%', "",
iota_path)
]]]
************************************************************************
If I now run this test, svn diff correctly expands the command (as
expected, see the line starting with "W: svn: E200012:"), but svn
crashes (the test fails too, but that is I think a separate problem)
g@musashi:~/trunk_ziff/subversion/tests/cmdline$ ./diff_tests.py 49
W: subversion/svn/diff-cmd.c:412,
W: subversion/libsvn_client/diff.c:2130,
W: subversion/libsvn_client/diff.c:1654,
W: subversion/libsvn_wc/diff_local.c:500,
W: subversion/libsvn_wc/status.c:2758,
W: subversion/libsvn_wc/status.c:1460,
W: subversion/libsvn_wc/status.c:1212,
W: subversion/libsvn_wc/status.c:931,
W: subversion/libsvn_wc/diff_local.c:364,
W: subversion/libsvn_wc/diff_editor.c:568,
W: subversion/libsvn_diff/diff_tree.c:1208,
W: subversion/libsvn_wc/diff_editor.c:2708,
W: subversion/libsvn_client/diff.c:975,
W: subversion/libsvn_client/diff.c:839,
W: subversion/libsvn_subr/io.c:3040: (apr_err=SVN_ERR_EXTERNAL_PROGRAM)
W: svn: E200012: '"diff -u --label l1 %f1% --label l2 %f2%' was expanded
to "diff -u --label l1
/home/g/trunk_ziff/subversion/tests/cmdline/svn-test-work/working_copies/diff_tests-49/.svn/pristine/2c/2c0aa9014a0cd07f01795a333d82485ef6d083e2.svn-base
--label l2
/home/g/trunk_ziff/subversion/tests/cmdline/svn-test-work/working_copies/diff_tests-49/iota
and returned 255
W: CWD:
/home/g/trunk_ziff/subversion/tests/cmdline/svn-test-work/working_copies/diff_tests-49
W: EXCEPTION: Failure: Command failed:
"/home/g/trunk_ziff/subversion/svn/svn diff --invoke-diff-cmd="diff -u
--label l1 %f1% --label l2 %f2% ..."; exit code 1
Traceback (most recent call last):
File "/home/g/trunk_ziff/subversion/tests/cmdline/svntest/main.py",
line 1550, in run
rc = self.pred.run(sandbox)
File
"/home/g/trunk_ziff/subversion/tests/cmdline/svntest/testcase.py", line
176, in run
return self.func(sandbox)
File "./diff_tests.py", line 3304, in diff_invoke_external_diffcmd
iota_path)
File
"/home/g/trunk_ziff/subversion/tests/cmdline/svntest/actions.py", line
282, in run_and_verify_svn
expected_exit, *varargs)
File
"/home/g/trunk_ziff/subversion/tests/cmdline/svntest/actions.py", line
320, in run_and_verify_svn2
exit_code, out, err = main.run_svn(want_err, *varargs)
File "/home/g/trunk_ziff/subversion/tests/cmdline/svntest/main.py",
line 682, in run_svn
*(_with_auth(_with_config_dir(varargs))))
File "/home/g/trunk_ziff/subversion/tests/cmdline/svntest/main.py",
line 365, in run_command
None, *varargs)
File "/home/g/trunk_ziff/subversion/tests/cmdline/svntest/main.py",
line 557, in run_command_stdin
'"; exit code ' + str(exit_code))
Failure: Command failed: "/home/g/trunk_ziff/subversion/svn/svn diff
--invoke-diff-cmd="diff -u --label l1 %f1% --label l2 %f2% ..."; exit
code 1
FAIL: diff_tests.py 49: svn diff --diff-invoke-cmd has correct arguments
g@musashi:~/trunk_ziff/subversion/tests/cmdline$
************************************************************************
Running the rejected output without svn works:
g@musashi:~/trunk_ziff/subversion/tests/cmdline$ diff -u --label l1
/home/g/trunk_ziff/subversion/tests/cmdline/svn-test-work/working_copies/diff_tests-49/.svn/pristine/2c/2c0aa9014a0cd07f01795a333d82485ef6d083e2.svn-base
--label l2
/home/g/trunk_ziff/subversion/tests/cmdline/svn-test-work/working_copies/diff_tests-49/iota
--- l1
+++ l2
@@ -1 +1,2 @@
This is the file 'iota'.
+new text in iota
\ No newline at end of file
g@musashi:~/trunk_ziff/subversion/tests/cmdline$
************************************************************************
So does running svn with the rejected output without the test suite:
g@musashi:~/trunk_ziff/subversion/tests/cmdline$
/home/g/trunk_ziff/subversion/svn/svn diff --invoke-diff-cmd="diff -u
--label l1 %f1% --label l2 %f2%"
Index: diff_tests.py
===================================================================
--- l1
+++ l2
@@ -3274,6 +3274,34 @@
... normal diff ensues ...
************************************************************************
So does changing into the directory I think it's normally operating in
and using the rejected output:
g@musashi:~/trunk_ziff/subversion/tests/cmdline$ cd
svn-test-work/working_copies/diff_tests-49/
g@musashi:~/trunk_ziff/subversion/tests/cmdline/svn-test-work/working_copies/diff_tests-49$
/home/g/trunk_ziff/subversion/svn/svn diff --invoke-diff-cmd="diff -u
--label l1 %f1% --label l2 %f2%"
Index: iota
===================================================================
--- l1
+++ l2
@@ -1 +1,2 @@
This is the file 'iota'.
+new text in iota
\ No newline at end of file
g@musashi:~/trunk_ziff/subversion/tests/cmdline/svn-test-work/working_copies/diff_tests-49$
************************************************************************
many thanks for any hints you might have for me,
Gabriela
Index: include/svn_client.h
===================================================================
--- include/svn_client.h (revision 1475745)
+++ include/svn_client.h (working copy)
@@ -2986,6 +2986,9 @@ svn_client_blame(const char *path_or_url,
* The above two options are mutually exclusive. It is an error to set
* both to TRUE.
*
+ * If @a invoke_diff_cmd is non-null, invoke external diff command
+ * with the string it contains.
+ *
* Generated headers are encoded using @a header_encoding.
*
* Diff output will not be generated for binary files, unless @a
@@ -3015,9 +3018,39 @@ 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.8.
+ */
+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,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool,
+ const char *invoke_diff_cmd);
+
+/** Similar to svn_client_diff7(), but with @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,
@@ -3175,14 +3208,46 @@ 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.8.
*/
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,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool,
+ const char *invoke_diff_cmd);
+
+/** Similar to svn_client_peg5(), 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.
+ * @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,
const svn_opt_revision_t *peg_revision,
Index: include/svn_config.h
===================================================================
--- include/svn_config.h (revision 1475745)
+++ include/svn_config.h (working copy)
@@ -112,6 +112,7 @@ 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"
+#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 (revision 1475745)
+++ include/svn_io.h (working copy)
@@ -2273,8 +2273,39 @@ svn_io_file_readline(apr_file_t *file,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
+/* Parse a user defined command to contain dynamically
+ * created labels and filenames.
+ *
+ ** @since New in 1.8. (?)
+ */
/** @} */
+const char **
+svn_io_create_custom_diff_cmd(const char *label1,
+ const char *label2,
+ const char *label3,
+ const char *tmpfile1,
+ const char *tmpfile2,
+ const char *base,
+ const char *cmd,
+ apr_pool_t *scratch_pool);
+/* Run the external diff command defined by the invoke-diff-cmd
+ * option.
+ *
+ ** @since New in 1.8.
+ */
+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: libsvn_client/deprecated.c
===================================================================
--- libsvn_client/deprecated.c (revision 1475745)
+++ libsvn_client/deprecated.c (working copy)
@@ -914,6 +914,54 @@ svn_client_delete(svn_client_commit_info_t **commi
/*** From diff.c ***/
svn_error_t *
+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,
+ ctx,
+ pool,
+ NULL);
+}
+
+svn_error_t *
svn_client_diff5(const apr_array_header_t *diff_options,
const char *path1,
const svn_opt_revision_t *revision1,
@@ -1036,6 +1084,53 @@ svn_client_diff(const apr_array_header_t *options,
}
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,
+ ctx,
+ pool,
+ NULL);
+}
+
+svn_error_t *
svn_client_diff_peg5(const apr_array_header_t *diff_options,
const char *path,
const svn_opt_revision_t *peg_revision,
Index: libsvn_client/diff.c
===================================================================
--- libsvn_client/diff.c (revision 1475745)
+++ libsvn_client/diff.c (working copy)
@@ -611,6 +611,10 @@ 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;
+
+ /* external custom diff command */
+ const char *invoke_diff_cmd;
+
};
/* An helper for diff_dir_props_changed, diff_file_changed and diff_file_added
@@ -786,7 +790,7 @@ diff_content_changed(svn_boolean_t *wrote_header,
}
- 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;
@@ -815,15 +819,25 @@ diff_content_changed(svn_boolean_t *wrote_header,
SVN_ERR(svn_io_open_unique_file3(&errfile, &errfilename, NULL,
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));
-
+
+ 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));
+ }
SVN_ERR(svn_io_file_close(outfile, scratch_pool));
SVN_ERR(svn_io_file_close(errfile, scratch_pool));
@@ -2448,7 +2462,7 @@ 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);
@@ -2455,15 +2469,14 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton
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)
- options = svn_cstring_split(diff_extensions, " \t\n\r", TRUE, pool);
- }
+ {
+ const char *diff_extensions;
+ svn_config_get(cfg, &diff_extensions, SVN_CONFIG_SECTION_HELPERS,
+ SVN_CONFIG_OPTION_DIFF_EXTENSIONS, NULL);
+ if (diff_extensions)
+ options = svn_cstring_split(diff_extensions, " \t\n\r", TRUE, pool);
+ }
}
-
if (options == NULL)
options = apr_array_make(pool, 0, sizeof(const char *));
@@ -2470,8 +2483,22 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton
if (diff_cmd)
SVN_ERR(svn_path_cstring_to_utf8(&diff_cmd_baton->diff_cmd, diff_cmd,
pool));
- else
- diff_cmd_baton->diff_cmd = NULL;
+ else {
+ 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);
+ diff_cmd_baton->diff_cmd = NULL;
+ 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;
+ }
+ }
+ }
/* If there was a command, arrange options to pass to it. */
if (diff_cmd_baton->diff_cmd)
@@ -2537,7 +2564,7 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton
* These cases require server communication.
*/
svn_error_t *
-svn_client_diff6(const apr_array_header_t *options,
+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,
@@ -2557,7 +2584,8 @@ svn_error_t *
svn_stream_t *errstream,
const apr_array_header_t *changelists,
svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+ apr_pool_t *pool,
+ const char *invoke_diff_cmd)
{
struct diff_cmd_baton diff_cmd_baton = { 0 };
svn_opt_revision_t peg_revision;
@@ -2573,9 +2601,10 @@ svn_error_t *
/* setup callback and baton */
diff_cmd_baton.orig_path_1 = path_or_url1;
diff_cmd_baton.orig_path_2 = path_or_url2;
-
+ diff_cmd_baton.invoke_diff_cmd = invoke_diff_cmd;
+
SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options,
- ctx->config, pool));
+ ctx->config, pool));
diff_cmd_baton.pool = pool;
diff_cmd_baton.outstream = outstream;
diff_cmd_baton.errstream = errstream;
@@ -2604,7 +2633,7 @@ svn_error_t *
}
svn_error_t *
-svn_client_diff_peg6(const apr_array_header_t *options,
+svn_client_diff_peg7(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,
@@ -2624,7 +2653,8 @@ svn_error_t *
svn_stream_t *errstream,
const apr_array_header_t *changelists,
svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+ apr_pool_t *pool,
+ const char *invoke_diff_cmd)
{
struct diff_cmd_baton diff_cmd_baton = { 0 };
@@ -2636,6 +2666,7 @@ svn_error_t *
/* setup callback and baton */
diff_cmd_baton.orig_path_1 = path_or_url;
diff_cmd_baton.orig_path_2 = path_or_url;
+ diff_cmd_baton.invoke_diff_cmd = invoke_diff_cmd;
SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options,
ctx->config, pool));
@@ -2721,3 +2752,4 @@ svn_client_diff_summarize_dup(const svn_client_dif
return dup_diff;
}
+
Index: libsvn_subr/config_file.c
===================================================================
--- libsvn_subr/config_file.c (revision 1475745)
+++ libsvn_subr/config_file.c (working copy)
@@ -1084,6 +1084,12 @@ 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 = " NL
+ "### diff_program (diff, gdiff, etc.) <options> <substitutions>" 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: libsvn_subr/io.c
===================================================================
--- libsvn_subr/io.c (revision 1475745)
+++ libsvn_subr/io.c (working copy)
@@ -2921,8 +2921,132 @@ svn_io_run_diff2(const char *dir,
return SVN_NO_ERROR;
}
+const char **
+svn_io_create_custom_diff_cmd(const char *label1,
+ const char *label2,
+ const char *label3,
+ const char *tmpfile1,
+ const char *tmpfile2,
+ const char *base,
+ const char *cmd,
+ apr_pool_t *pool)
+{
+ apr_pool_t *subpool;
+ apr_array_header_t *token_list;
+ svn_stringbuf_t *com;
+ const char ** ret;
+ int i;
+ subpool = svn_pool_create(pool);
+ token_list = svn_cstring_split("%f1% %f2% %f3% %l1% %l2% %l3%", " ", TRUE, subpool);
+ com = svn_stringbuf_create_empty(pool);
+ svn_stringbuf_appendcstr(com, cmd);
+
+ for (i = 0; i < token_list->nelts; i++)
+ {
+ svn_stringbuf_t *token;
+ char * found;
+ int len;
+
+ token = svn_stringbuf_create_empty(subpool);
+ svn_stringbuf_appendcstr(token, APR_ARRAY_IDX(token_list,i,char *));
+ len = 0;
+ while ( (found = strstr(com->data, token->data)) &&
+ (strlen(found) > len) )
+ {
+ len = strlen(found);
+
+ /* if we find a % in front of this, consume it and pass */
+ if (com->data[com->len - strlen(found)-1] == '%')
+ svn_stringbuf_remove(com, strlen(found)-1, 1);
+ else if (i == 0) /* %f1 */
+ svn_stringbuf_replace(com, com->len - strlen(found), token->len,
+ tmpfile1, strlen(tmpfile1));
+ else if (i == 1) /* %f2 */
+ svn_stringbuf_replace(com, com->len - strlen(found), token->len,
+ tmpfile2, strlen(tmpfile2));
+ else if (i == 2) /* %f3 */
+ svn_stringbuf_replace(com, com->len - strlen(found), token->len,
+ base, strlen(base));
+ else if (i == 3) /* %l1 */
+ svn_stringbuf_replace(com, com->len - strlen(found), token->len,
+ label1, strlen(label1));
+ else if (i == 4) /* %l2 */
+ svn_stringbuf_replace(com, com->len - strlen(found), token->len,
+ label2, strlen(label2));
+ else if (i == 5) /* %l3 */
+ svn_stringbuf_replace(com, com->len - strlen(found), token->len,
+ label3, strlen(label3));
+ }
+ }
+
+ { /* Produce a null-terminated argv[] required by apr_proc_create() */
+ apr_array_header_t *tmp;
+ tmp = svn_cstring_split(com->data," ",FALSE, pool);
+
+ ret = apr_pcalloc(pool,
+ tmp->nelts *
+ tmp->elt_size*sizeof(char *));
+
+ for (i = 0; i < tmp->nelts ; i++)
+ ret[i] = APR_ARRAY_IDX(tmp, i, char *);
+ ret[i] = NULL;
+ }
+
+ svn_pool_destroy(subpool);
+ return ret;
+}
+
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;
+
+ cmd = svn_io_create_custom_diff_cmd(label1, label2, NULL, tmpfile1, tmpfile2,
+ NULL, external_diff_cmd, pool);
+ if (pexitcode == NULL)
+ pexitcode = &exitcode;
+
+ SVN_ERR(svn_io_run_cmd(dir, cmd[0], cmd, pexitcode, NULL, TRUE,
+ NULL, outfile, errfile, pool));
+
+ if (*pexitcode != 0 && *pexitcode != 1)
+ {
+ int zip, size;
+ char * failed_command;
+
+ size = 0;
+
+ for (zip = 0; cmd[zip]; zip++)
+ size += strlen(cmd[zip]) + 1;
+
+ failed_command = apr_pcalloc(pool, size * sizeof(char *));
+
+ for (zip = 0; cmd[zip]; zip++)
+ {
+ strcat(failed_command, cmd[zip]);
+ strcat(failed_command, " ");
+ }
+ return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ _("'%s' was expanded to %s and returned %d"),
+ external_diff_cmd,
+ svn_dirent_local_style(failed_command, pool),
+ *pexitcode);
+ }
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
svn_io_run_diff3_3(int *exitcode,
const char *dir,
const char *mine,
Index: svn/cl.h
===================================================================
--- svn/cl.h (revision 1475745)
+++ svn/cl.h (working copy)
@@ -181,6 +181,8 @@ typedef struct svn_cl__opt_state_t
struct
{
const char *diff_cmd; /* the external diff command to use */
+ 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 */
@@ -213,6 +215,7 @@ typedef struct svn_cl__opt_state_t
const char *changelist; /* operate on this changelist
THIS IS TEMPORARY (LAST OF CHANGELISTS) */
svn_boolean_t keep_changelists;/* don't remove changelists after commit */
+ const char *invoke_diff_cmd; /* external customizable diff command */
svn_boolean_t keep_local; /* delete path only from repository */
svn_boolean_t all_revprops; /* retrieve all revprops */
svn_boolean_t no_revprops; /* retrieve no revprops */
Index: svn/diff-cmd.c
===================================================================
--- svn/diff-cmd.c (revision 1475745)
+++ svn/diff-cmd.c (working copy)
@@ -387,7 +387,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),
@@ -407,7 +407,9 @@ svn_cl__diff(apr_getopt_t *os,
outstream,
errstream,
opt_state->changelists,
- ctx, iterpool));
+ ctx,
+ iterpool,
+ opt_state->diff.invoke_diff_cmd));
}
else
{
@@ -438,7 +440,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,
@@ -458,7 +460,9 @@ svn_cl__diff(apr_getopt_t *os,
outstream,
errstream,
opt_state->changelists,
- ctx, iterpool));
+ ctx,
+ iterpool,
+ opt_state->diff.invoke_diff_cmd));
}
}
Index: svn/svn.c
===================================================================
--- svn/svn.c (revision 1475745)
+++ svn/svn.c (working copy)
@@ -84,6 +84,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,
@@ -336,6 +337,25 @@ 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 \n"
+ " "
+ "Substitutions: %f1 %f2 files to compare \n"
+ " "
+ " %l1 %l2 user defined labels \n"
+ " "
+ "Examples: --invoke-diff-cmd=\"diff -y %f1 %f2 \n"
+ " "
+ " --invoke-diff-cmd=\"kdiff3 -auto -o /home/u/log \\ \n"
+ " "
+ " %f1 %f2 --L1 %l1 --L2 \"Custom Label\" \" \n"
+ " "
+ "The switch symbol '%' can be escaped in the usual way \n"
+ " "
+ "using the '%' character: %%f1 will be passed as %f1. \n"
+ )},
{"internal-diff", opt_internal_diff, 0,
N_("override diff-cmd specified in config file")},
{"no-diff-added", opt_no_diff_added, 0,
@@ -575,7 +595,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"
@@ -2092,6 +2113,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;
@@ -2497,6 +2521,14 @@ 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);
+ }
+
/* 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)
@@ -2709,9 +2741,17 @@ 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);
+ }
+ else
+ {
+ 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: tests/cmdline/diff_tests.py
===================================================================
--- tests/cmdline/diff_tests.py (revision 1475745)
+++ tests/cmdline/diff_tests.py (working copy)
@@ -3274,7 +3274,35 @@ def diff_external_diffcmd(sbox):
'diff', '--diff-cmd', diff_script_path,
iota_path)
+# Check the order of the arguments for an external diff tool
+def diff_invoke_external_diffcmd(sbox):
+ "svn diff --diff-invoke-cmd has correct arguments"
+ 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",
+ "--- l1\n",
+ "+++ l2\n",
+ "@@ -1 +1,2 @@\n",
+ "This is the file 'iota'.\n",
+ "+new text in iota\n",
+ "\ No newline at end of file\n",
+ # os.path.abspath(svntest.wc.text_base_path("iota")) + "\n",
+ #os.path.abspath("iota") + "\n
+ ])
+
+ # Check that the output of diff corresponds with the expected arguments,
+ # in the correct order.
+ svntest.actions.run_and_verify_svn(None, expected_output, [],
+ 'diff', '--invoke-diff-cmd="diff -u --label l1 %f1% --label l2 %f2%', "",
+ iota_path)
+
#----------------------------------------------------------------------
# Diffing an unrelated repository URL against working copy with
# local modifications (i.e. not committed). This is issue #3295 (diff
@@ -4574,6 +4602,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,