On 13/05/13 22:53, Gabriela Gibson wrote:
Hi,
thanks for all the comments and help, here is the next attempt.
Gabriela
Sorry I had one mistake in the patch that has now been fixed.
Please ignore the patch in the previous post, the attached patch is the
working one.
Gabriela
Index: subversion/include/svn_client.h
===================================================================
--- subversion/include/svn_client.h (revision 1480974)
+++ subversion/include/svn_client.h (working copy)
@@ -2986,6 +2986,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
@@ -3016,8 +3021,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 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 +3210,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.8.
+ * @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_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.9.
+ */
+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: subversion/include/svn_config.h
===================================================================
--- subversion/include/svn_config.h (revision 1480974)
+++ subversion/include/svn_config.h (working copy)
@@ -112,6 +112,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: subversion/include/svn_io.h
===================================================================
--- subversion/include/svn_io.h (revision 1480974)
+++ subversion/include/svn_io.h (working copy)
@@ -2278,6 +2278,38 @@ svn_io_file_readline(apr_file_t *file,
/** @} */
+/** Parse a user defined command to contain dynamically created labels
+ * and filenames.
+ *
+ * @since New in 1.9.
+ */
+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.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: subversion/libsvn_client/deprecated.c
===================================================================
--- subversion/libsvn_client/deprecated.c (revision 1480974)
+++ subversion/libsvn_client/deprecated.c (working copy)
@@ -914,6 +914,53 @@ 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,
+ NULL,
+ ctx,
+ pool);
+}
+
+svn_error_t *
svn_client_diff5(const apr_array_header_t *diff_options,
const char *path1,
const svn_opt_revision_t *revision1,
@@ -1036,6 +1083,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,
+ NULL,
+ ctx,
+ pool);
+}
+
+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: subversion/libsvn_client/diff.c
===================================================================
--- subversion/libsvn_client/diff.c (revision 1480974)
+++ subversion/libsvn_client/diff.c (working copy)
@@ -427,7 +427,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
@@ -563,7 +563,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.
@@ -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));
@@ -1519,8 +1533,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.
@@ -1544,7 +1558,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
@@ -1557,7 +1571,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"));
}
@@ -1566,7 +1580,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,
@@ -1649,7 +1663,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,
@@ -1794,7 +1808,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,
@@ -2129,7 +2143,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,
@@ -2173,7 +2187,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,
@@ -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,
@@ -2556,6 +2583,7 @@ svn_error_t *
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)
{
@@ -2573,7 +2601,8 @@ 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));
diff_cmd_baton.pool = pool;
@@ -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,
@@ -2623,6 +2652,7 @@ svn_error_t *
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)
{
@@ -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));
Index: subversion/libsvn_subr/config_file.c
===================================================================
--- subversion/libsvn_subr/config_file.c (revision 1480974)
+++ subversion/libsvn_subr/config_file.c (working copy)
@@ -1084,6 +1084,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 = \"diff -y --label %l1% %f1% --label %l2% %f2%\""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: subversion/libsvn_subr/io.c
===================================================================
--- subversion/libsvn_subr/io.c (revision 1480974)
+++ subversion/libsvn_subr/io.c (working copy)
@@ -2921,8 +2921,153 @@ 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;
+ static const char *token_list[] =
+ {"%f1%","%f2%", "%f3%", "%l1%", "%l2%","%l3%" };
+ const char *delimiters[] = {"#","^","&","!","~","?","$","*","|"};
+ svn_stringbuf_t *com;
+ apr_array_header_t *tmp;
+ const char ** ret;
+ int i, delimiter;
+ char * found;
+ subpool = svn_pool_create(pool);
+ delimiter = 0;
+
+ /* find a delimiter that does not occur in the user string. */
+ while ( ((found = strstr(cmd, delimiters[delimiter++]))) &&
+ (delimiter < 9 /* sizeof(delimiters) */ ) )
+ { ; } delimiter--;
+
+ tmp = svn_cstring_split(cmd," ",TRUE, subpool);
+ com = svn_stringbuf_create_empty(subpool);
+
+ /* ensure that the command is split into the correct argv parcels
+ * and leave a marker in place because the correct order is lost in
+ * the subsequent in-place replacements due to labels having
+ * spaces and ending up as 2 entries in ret[] */
+ for (i = 0; i < tmp->nelts ; i++)
+ {
+ svn_stringbuf_appendcstr(com, APR_ARRAY_IDX(tmp, i, char *));
+ svn_stringbuf_appendcstr(com, delimiters[delimiter]);
+ }
+
+ for (i = 0; i < 6 /* sizeof(token_list) */; i++)
+ {
+ svn_stringbuf_t *token;
+ int len;
+
+ token = svn_stringbuf_create_empty(subpool);
+ svn_stringbuf_appendcstr(token, token_list[i]);
+ 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 */
+ 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() */
+ tmp = svn_cstring_split(com->data,delimiters[delimiter],TRUE, 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;
+
+ if (0 == strlen(external_diff_cmd))
+ return svn_error_createf(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+ _("The --invoke-diff-cmd string was empty.\n"));
+
+ 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 i, size;
+ char * failed_command;
+
+ for (i = 0, size = 0; cmd[i]; i++)
+ size += strlen(cmd[i]) + 1;
+
+ failed_command = apr_pcalloc(pool, size * sizeof(char *));
+
+ for (i = 0; cmd[i]; i++)
+ {
+ strcat(failed_command, cmd[i]);
+ strcat(failed_command, " ");
+ }
+
+ return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, NULL,
+ _("'%s' was expanded to '%s' and returned %d\n"),
+ 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: subversion/svn/cl.h
===================================================================
--- subversion/svn/cl.h (revision 1480974)
+++ subversion/svn/cl.h (working copy)
@@ -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: subversion/svn/diff-cmd.c
===================================================================
--- subversion/svn/diff-cmd.c (revision 1480974)
+++ subversion/svn/diff-cmd.c (working copy)
@@ -404,7 +404,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),
@@ -424,6 +424,7 @@ svn_cl__diff(apr_getopt_t *os,
outstream,
errstream,
opt_state->changelists,
+ opt_state->diff.invoke_diff_cmd,
ctx, iterpool));
}
else
@@ -455,7 +456,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,
@@ -475,6 +476,7 @@ svn_cl__diff(apr_getopt_t *os,
outstream,
errstream,
opt_state->changelists,
+ opt_state->diff.invoke_diff_cmd,
ctx, iterpool));
}
}
Index: subversion/svn/svn.c
===================================================================
--- subversion/svn/svn.c (revision 1480974)
+++ subversion/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"
@@ -2093,6 +2114,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;
@@ -2502,6 +2526,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)
@@ -2714,9 +2746,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: subversion/tests/cmdline/diff_tests.py
===================================================================
--- subversion/tests/cmdline/diff_tests.py (revision 1480974)
+++ subversion/tests/cmdline/diff_tests.py (working copy)
@@ -3260,7 +3260,30 @@ 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 --diff-invoke-cmd passes correct args"
+ 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",
+ "Dumping @ARGV...\n",
+ "Arg 0 is >iota (revision 1)<\n",
+ "Arg 1 is >"+os.path.abspath(svntest.wc.text_base_path("iota")) + "<\n",
+ "Arg 2 is >iota (working copy)<\n",
+ "Arg 3 is >"+os.path.abspath("iota") + "<\n"])
+
+ svntest.actions.run_and_verify_svn(None, expected_output, [],
+ 'diff',
+ '--invoke-diff-cmd=/home/g/trunk_ziff/tools/hook-scripts/argv_dump.pl %l1% %f1% %l2% %f2%',
+ iota_path)
+
#----------------------------------------------------------------------
# Diffing an unrelated repository URL against working copy with
# local modifications (i.e. not committed). This is issue #3295 (diff
@@ -4560,6 +4583,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: tools/hook-scripts/argv_dump.pl
===================================================================
--- tools/hook-scripts/argv_dump.pl (revision 0)
+++ tools/hook-scripts/argv_dump.pl (working copy)
@@ -0,0 +1,33 @@
+#!/usr/bin/perl -w
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+#
+# Use this script to test svn output. For example:
+#
+# svn diff /
+# '--invoke-diff-cmd=/home/g/trunk_ziff/tools/hook_scripts/argv_dump.pl %f1% %f2%'
+#
+use strict;
+use warnings;
+
+print "Dumping \@ARGV...\n";
+for (my $i = 0; $i < @ARGV; $i++) {
+ print "Arg $i is >$ARGV[$i]<\n";
+}
+exit 0;
Property changes on: tools/hook-scripts/argv_dump.pl
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property