diff options
Diffstat (limited to 'subversion/libsvn_client/diff.c')
-rw-r--r-- | subversion/libsvn_client/diff.c | 2066 |
1 files changed, 948 insertions, 1118 deletions
diff --git a/subversion/libsvn_client/diff.c b/subversion/libsvn_client/diff.c index 26890ae..4817ffd 100644 --- a/subversion/libsvn_client/diff.c +++ b/subversion/libsvn_client/diff.c @@ -52,20 +52,22 @@ #include "private/svn_diff_private.h" #include "private/svn_subr_private.h" #include "private/svn_io_private.h" +#include "private/svn_ra_private.h" #include "svn_private_config.h" /* Utilities */ +#define DIFF_REVNUM_NONEXISTENT ((svn_revnum_t) -100) #define MAKE_ERR_BAD_RELATIVE_PATH(path, relative_to_dir) \ svn_error_createf(SVN_ERR_BAD_RELATIVE_PATH, NULL, \ _("Path '%s' must be an immediate child of " \ "the directory '%s'"), path, relative_to_dir) -/* Calculate the repository relative path of DIFF_RELPATH, using RA_SESSION - * and WC_CTX, and return the result in *REPOS_RELPATH. +/* Calculate the repository relative path of DIFF_RELPATH, using + * SESSION_RELPATH and WC_CTX, and return the result in *REPOS_RELPATH. * ORIG_TARGET is the related original target passed to the diff command, * and may be used to derive leading path components missing from PATH. * ANCHOR is the local path where the diff editor is anchored. @@ -74,16 +76,15 @@ static svn_error_t * make_repos_relpath(const char **repos_relpath, const char *diff_relpath, const char *orig_target, - svn_ra_session_t *ra_session, + const char *session_relpath, svn_wc_context_t *wc_ctx, const char *anchor, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { const char *local_abspath; - const char *orig_repos_relpath = NULL; - if (! ra_session + if (! session_relpath || (anchor && !svn_path_is_url(orig_target))) { svn_error_t *err; @@ -98,7 +99,7 @@ make_repos_relpath(const char **repos_relpath, wc_ctx, local_abspath, result_pool, scratch_pool); - if (!ra_session + if (!session_relpath || ! err || (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)) { @@ -113,23 +114,8 @@ make_repos_relpath(const char **repos_relpath, svn_error_clear(err); } - { - const char *url; - const char *repos_root_url; - - /* Would be nice if the RA layer could just provide the parent - repos_relpath of the ra session */ - SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool)); - - SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, - scratch_pool)); - - orig_repos_relpath = svn_uri_skip_ancestor(repos_root_url, url, - scratch_pool); - - *repos_relpath = svn_relpath_join(orig_repos_relpath, diff_relpath, - result_pool); - } + *repos_relpath = svn_relpath_join(session_relpath, diff_relpath, + result_pool); return SVN_NO_ERROR; } @@ -169,9 +155,6 @@ adjust_paths_for_diff_labels(const char **index_path, new_path = "."; else return MAKE_ERR_BAD_RELATIVE_PATH(new_path, relative_to_dir); - - child_path = svn_dirent_is_child(relative_to_dir, new_path1, - result_pool); } { @@ -179,7 +162,7 @@ adjust_paths_for_diff_labels(const char **index_path, svn_boolean_t is_url1; svn_boolean_t is_url2; /* ### Holy cow. Due to anchor/target weirdness, we can't - simply join diff_cmd_baton->orig_path_1 with path, ditto for + simply join dwi->orig_path_1 with path, ditto for orig_path_2. That will work when they're directory URLs, but not for file URLs. Nor can we just use anchor1 and anchor2 from do_diff(), at least not without some more logic here. @@ -230,7 +213,7 @@ adjust_paths_for_diff_labels(const char **index_path, if (new_path2[0] == '\0') new_path2 = new_path; else if (svn_path_is_url(new_path2)) - new_path1 = apr_psprintf(result_pool, "%s\t(%s)", new_path, new_path2); + new_path2 = apr_psprintf(result_pool, "%s\t(%s)", new_path, new_path2); else if (new_path2[0] == '/') new_path2 = apr_psprintf(result_pool, "%s\t(...%s)", new_path, new_path2); else @@ -254,9 +237,11 @@ diff_label(const char *path, apr_pool_t *pool) { const char *label; - if (revnum != SVN_INVALID_REVNUM) + if (revnum >= 0) label = apr_psprintf(pool, _("%s\t(revision %ld)"), path, revnum); - else + else if (revnum == DIFF_REVNUM_NONEXISTENT) + label = apr_psprintf(pool, _("%s\t(nonexistent)"), path); + else /* SVN_INVALID_REVNUM */ label = apr_psprintf(pool, _("%s\t(working copy)"), path); return label; @@ -452,7 +437,9 @@ display_prop_diffs(const apr_array_header_t *propchanges, const char *relative_to_dir, svn_boolean_t show_diff_header, svn_boolean_t use_git_diff_format, - svn_ra_session_t *ra_session, + const char *ra_session_relpath, + svn_cancel_func_t cancel_func, + void *cancel_baton, svn_wc_context_t *wc_ctx, apr_pool_t *scratch_pool) { @@ -465,10 +452,10 @@ display_prop_diffs(const apr_array_header_t *propchanges, if (use_git_diff_format) { SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath, orig_path1, - ra_session, wc_ctx, anchor, + ra_session_relpath, wc_ctx, anchor, scratch_pool, scratch_pool)); SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath, orig_path2, - ra_session, wc_ctx, anchor, + ra_session_relpath, wc_ctx, anchor, scratch_pool, scratch_pool)); } @@ -521,7 +508,9 @@ display_prop_diffs(const apr_array_header_t *propchanges, SVN_ERR(svn_diff__display_prop_diffs( outstream, encoding, propchanges, original_props, - TRUE /* pretty_print_mergeinfo */, scratch_pool)); + TRUE /* pretty_print_mergeinfo */, + -1 /* context_size */, + cancel_func, cancel_baton, scratch_pool)); return SVN_NO_ERROR; } @@ -530,9 +519,27 @@ display_prop_diffs(const apr_array_header_t *propchanges, /*** Callbacks for 'svn diff', invoked by the repos-diff editor. ***/ +/* State provided by the diff drivers; used by the diff writer */ +typedef struct diff_driver_info_t +{ + /* The anchor to prefix before wc paths */ + const char *anchor; -struct diff_cmd_baton { + /* Relative path of ra session from repos_root_url */ + const char *session_relpath; + /* The original targets passed to the diff command. We may need + these to construct distinctive diff labels when comparing the + same relative path in the same revision, under different anchors + (for example, when comparing a trunk against a branch). */ + const char *orig_path_1; + const char *orig_path_2; +} diff_driver_info_t; + + +/* Diff writer state */ +typedef struct diff_writer_info_t +{ /* If non-null, the external diff command to invoke. */ const char *diff_cmd; @@ -556,24 +563,6 @@ struct diff_cmd_baton { const char *header_encoding; - /* The original targets passed to the diff command. We may need - these to construct distinctive diff labels when comparing the - same relative path in the same revision, under different anchors - (for example, when comparing a trunk against a branch). */ - const char *orig_path_1; - 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 - because some of the svn_wc_diff_callbacks4_t don't get revision - arguments. - - ### Perhaps we should change the callback signatures and eliminate - ### these? - */ - svn_revnum_t revnum1; - svn_revnum_t revnum2; - /* Set this if you want diff output even for binary files. */ svn_boolean_t force_binary; @@ -597,22 +586,18 @@ struct diff_cmd_baton { svn_boolean_t no_diff_deleted; /* Whether to ignore copyfrom information when showing adds */ - svn_boolean_t no_copyfrom_on_add; + svn_boolean_t show_copies_as_adds; /* Empty files for creating diffs or NULL if not used yet */ const char *empty_file; svn_wc_context_t *wc_ctx; - /* The RA session used during diffs involving the repository. */ - svn_ra_session_t *ra_session; - - /* The anchor to prefix before wc paths */ - const char *anchor; + svn_cancel_func_t cancel_func; + void *cancel_baton; - /* Whether the local diff target of a repos->wc diff is a copy. */ - svn_boolean_t repos_wc_diff_target_is_copy; -}; + struct diff_driver_info_t ddi; +} diff_writer_info_t; /* An helper for diff_dir_props_changed, diff_file_changed and diff_file_added */ @@ -620,17 +605,16 @@ static svn_error_t * diff_props_changed(const char *diff_relpath, svn_revnum_t rev1, svn_revnum_t rev2, - svn_boolean_t dir_was_added, const apr_array_header_t *propchanges, apr_hash_t *original_props, svn_boolean_t show_diff_header, - struct diff_cmd_baton *diff_cmd_baton, + diff_writer_info_t *dwi, apr_pool_t *scratch_pool) { apr_array_header_t *props; /* If property differences are ignored, there's nothing to do. */ - if (diff_cmd_baton->ignore_properties) + if (dwi->ignore_properties) return SVN_NO_ERROR; SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props, @@ -638,58 +622,31 @@ diff_props_changed(const char *diff_relpath, if (props->nelts > 0) { - /* We're using the revnums from the diff_cmd_baton since there's + /* We're using the revnums from the dwi since there's * no revision argument to the svn_wc_diff_callback_t * dir_props_changed(). */ SVN_ERR(display_prop_diffs(props, original_props, diff_relpath, - diff_cmd_baton->anchor, - diff_cmd_baton->orig_path_1, - diff_cmd_baton->orig_path_2, + dwi->ddi.anchor, + dwi->ddi.orig_path_1, + dwi->ddi.orig_path_2, rev1, rev2, - diff_cmd_baton->header_encoding, - diff_cmd_baton->outstream, - diff_cmd_baton->relative_to_dir, + dwi->header_encoding, + dwi->outstream, + dwi->relative_to_dir, show_diff_header, - diff_cmd_baton->use_git_diff_format, - diff_cmd_baton->ra_session, - diff_cmd_baton->wc_ctx, + dwi->use_git_diff_format, + dwi->ddi.session_relpath, + dwi->cancel_func, + dwi->cancel_baton, + dwi->wc_ctx, scratch_pool)); } return SVN_NO_ERROR; } -/* An svn_wc_diff_callbacks4_t function. */ -static svn_error_t * -diff_dir_props_changed(svn_wc_notify_state_t *state, - svn_boolean_t *tree_conflicted, - const char *diff_relpath, - svn_boolean_t dir_was_added, - const apr_array_header_t *propchanges, - apr_hash_t *original_props, - void *diff_baton, - apr_pool_t *scratch_pool) -{ - struct diff_cmd_baton *diff_cmd_baton = diff_baton; - - return svn_error_trace(diff_props_changed(diff_relpath, - /* ### These revs be filled - * ### with per node info */ - dir_was_added - ? 0 /* Magic legacy value */ - : diff_cmd_baton->revnum1, - diff_cmd_baton->revnum2, - dir_was_added, - propchanges, - original_props, - TRUE /* show_diff_header */, - diff_cmd_baton, - scratch_pool)); -} - - /* Show differences between TMPFILE1 and TMPFILE2. DIFF_RELPATH, REV1, and REV2 are used in the headers to indicate the file and revisions. If either MIMETYPE1 or MIMETYPE2 indicate binary content, don't show a diff, @@ -711,26 +668,24 @@ diff_content_changed(svn_boolean_t *wrote_header, svn_boolean_t force_diff, const char *copyfrom_path, svn_revnum_t copyfrom_rev, - struct diff_cmd_baton *diff_cmd_baton, + diff_writer_info_t *dwi, apr_pool_t *scratch_pool) { - int exitcode; - const char *rel_to_dir = diff_cmd_baton->relative_to_dir; - svn_stream_t *errstream = diff_cmd_baton->errstream; - svn_stream_t *outstream = diff_cmd_baton->outstream; + const char *rel_to_dir = dwi->relative_to_dir; + svn_stream_t *outstream = dwi->outstream; const char *label1, *label2; svn_boolean_t mt1_binary = FALSE, mt2_binary = FALSE; const char *index_path = diff_relpath; - const char *path1 = diff_cmd_baton->orig_path_1; - const char *path2 = diff_cmd_baton->orig_path_2; + const char *path1 = dwi->ddi.orig_path_1; + const char *path2 = dwi->ddi.orig_path_2; /* If only property differences are shown, there's nothing to do. */ - if (diff_cmd_baton->properties_only) + if (dwi->properties_only) return SVN_NO_ERROR; /* Generate the diff headers. */ SVN_ERR(adjust_paths_for_diff_labels(&index_path, &path1, &path2, - rel_to_dir, diff_cmd_baton->anchor, + rel_to_dir, dwi->ddi.anchor, scratch_pool, scratch_pool)); label1 = diff_label(path1, rev1, scratch_pool); @@ -744,42 +699,82 @@ diff_content_changed(svn_boolean_t *wrote_header, if (mimetype2) mt2_binary = svn_mime_type_is_binary(mimetype2); - if (! diff_cmd_baton->force_binary && (mt1_binary || mt2_binary)) + if (! dwi->force_binary && (mt1_binary || mt2_binary)) { /* Print out the diff header. */ SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, + dwi->header_encoding, scratch_pool, "Index: %s" APR_EOL_STR SVN_DIFF__EQUAL_STRING APR_EOL_STR, index_path)); /* ### Print git diff headers. */ - SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, - _("Cannot display: file marked as a binary type.%s"), - APR_EOL_STR)); - - if (mt1_binary && !mt2_binary) - SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, - "svn:mime-type = %s" APR_EOL_STR, mimetype1)); - else if (mt2_binary && !mt1_binary) - SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, - "svn:mime-type = %s" APR_EOL_STR, mimetype2)); - else if (mt1_binary && mt2_binary) + if (dwi->use_git_diff_format) + { + svn_stream_t *left_stream; + svn_stream_t *right_stream; + const char *repos_relpath1; + const char *repos_relpath2; + + SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath, + dwi->ddi.orig_path_1, + dwi->ddi.session_relpath, + dwi->wc_ctx, + dwi->ddi.anchor, + scratch_pool, scratch_pool)); + SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath, + dwi->ddi.orig_path_2, + dwi->ddi.session_relpath, + dwi->wc_ctx, + dwi->ddi.anchor, + scratch_pool, scratch_pool)); + SVN_ERR(print_git_diff_header(outstream, &label1, &label2, + operation, + repos_relpath1, repos_relpath2, + rev1, rev2, + copyfrom_path, + copyfrom_rev, + dwi->header_encoding, + scratch_pool)); + + SVN_ERR(svn_stream_open_readonly(&left_stream, tmpfile1, + scratch_pool, scratch_pool)); + SVN_ERR(svn_stream_open_readonly(&right_stream, tmpfile2, + scratch_pool, scratch_pool)); + SVN_ERR(svn_diff_output_binary(outstream, + left_stream, right_stream, + dwi->cancel_func, dwi->cancel_baton, + scratch_pool)); + } + else { - if (strcmp(mimetype1, mimetype2) == 0) + SVN_ERR(svn_stream_printf_from_utf8(outstream, + dwi->header_encoding, scratch_pool, + _("Cannot display: file marked as a binary type.%s"), + APR_EOL_STR)); + + if (mt1_binary && !mt2_binary) SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, - "svn:mime-type = %s" APR_EOL_STR, - mimetype1)); - else + dwi->header_encoding, scratch_pool, + "svn:mime-type = %s" APR_EOL_STR, mimetype1)); + else if (mt2_binary && !mt1_binary) SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, - "svn:mime-type = (%s, %s)" APR_EOL_STR, - mimetype1, mimetype2)); + dwi->header_encoding, scratch_pool, + "svn:mime-type = %s" APR_EOL_STR, mimetype2)); + else if (mt1_binary && mt2_binary) + { + if (strcmp(mimetype1, mimetype2) == 0) + SVN_ERR(svn_stream_printf_from_utf8(outstream, + dwi->header_encoding, scratch_pool, + "svn:mime-type = %s" APR_EOL_STR, + mimetype1)); + else + SVN_ERR(svn_stream_printf_from_utf8(outstream, + dwi->header_encoding, scratch_pool, + "svn:mime-type = (%s, %s)" APR_EOL_STR, + mimetype1, mimetype2)); + } } /* Exit early. */ @@ -787,17 +782,19 @@ diff_content_changed(svn_boolean_t *wrote_header, } - if (diff_cmd_baton->diff_cmd) + if (dwi->diff_cmd) { + svn_stream_t *errstream = dwi->errstream; apr_file_t *outfile; apr_file_t *errfile; const char *outfilename; const char *errfilename; svn_stream_t *stream; + int exitcode; /* Print out the diff header. */ SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, + dwi->header_encoding, scratch_pool, "Index: %s" APR_EOL_STR SVN_DIFF__EQUAL_STRING APR_EOL_STR, index_path)); @@ -827,12 +824,12 @@ diff_content_changed(svn_boolean_t *wrote_header, scratch_pool, scratch_pool)); SVN_ERR(svn_io_run_diff2(".", - diff_cmd_baton->options.for_external.argv, - diff_cmd_baton->options.for_external.argc, + dwi->options.for_external.argv, + dwi->options.for_external.argc, label1, label2, tmpfile1, tmpfile2, &exitcode, outfile, errfile, - diff_cmd_baton->diff_cmd, scratch_pool)); + dwi->diff_cmd, scratch_pool)); /* Now, open and copy our files to our output streams. */ if (outfilename) @@ -854,43 +851,44 @@ diff_content_changed(svn_boolean_t *wrote_header, NULL, NULL, scratch_pool)); } - /* We have a printed a diff for this path, mark it as visited. */ - *wrote_header = TRUE; + /* If we have printed a diff for this path, mark it as visited. */ + if (exitcode == 1) + *wrote_header = TRUE; } else /* use libsvn_diff to generate the diff */ { svn_diff_t *diff; SVN_ERR(svn_diff_file_diff_2(&diff, tmpfile1, tmpfile2, - diff_cmd_baton->options.for_internal, + dwi->options.for_internal, scratch_pool)); if (force_diff - || diff_cmd_baton->use_git_diff_format + || dwi->use_git_diff_format || svn_diff_contains_diffs(diff)) { /* Print out the diff header. */ SVN_ERR(svn_stream_printf_from_utf8(outstream, - diff_cmd_baton->header_encoding, scratch_pool, + dwi->header_encoding, scratch_pool, "Index: %s" APR_EOL_STR SVN_DIFF__EQUAL_STRING APR_EOL_STR, index_path)); - if (diff_cmd_baton->use_git_diff_format) + if (dwi->use_git_diff_format) { const char *repos_relpath1; const char *repos_relpath2; SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath, - diff_cmd_baton->orig_path_1, - diff_cmd_baton->ra_session, - diff_cmd_baton->wc_ctx, - diff_cmd_baton->anchor, + dwi->ddi.orig_path_1, + dwi->ddi.session_relpath, + dwi->wc_ctx, + dwi->ddi.anchor, scratch_pool, scratch_pool)); SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath, - diff_cmd_baton->orig_path_2, - diff_cmd_baton->ra_session, - diff_cmd_baton->wc_ctx, - diff_cmd_baton->anchor, + dwi->ddi.orig_path_2, + dwi->ddi.session_relpath, + dwi->wc_ctx, + dwi->ddi.anchor, scratch_pool, scratch_pool)); SVN_ERR(print_git_diff_header(outstream, &label1, &label2, operation, @@ -898,20 +896,23 @@ diff_content_changed(svn_boolean_t *wrote_header, rev1, rev2, copyfrom_path, copyfrom_rev, - diff_cmd_baton->header_encoding, + dwi->header_encoding, scratch_pool)); } /* Output the actual diff */ if (force_diff || svn_diff_contains_diffs(diff)) - SVN_ERR(svn_diff_file_output_unified3(outstream, diff, + SVN_ERR(svn_diff_file_output_unified4(outstream, diff, tmpfile1, tmpfile2, label1, label2, - diff_cmd_baton->header_encoding, rel_to_dir, - diff_cmd_baton->options.for_internal->show_c_function, + dwi->header_encoding, rel_to_dir, + dwi->options.for_internal->show_c_function, + dwi->options.for_internal->context_size, + dwi->cancel_func, dwi->cancel_baton, scratch_pool)); - /* We have a printed a diff for this path, mark it as visited. */ - *wrote_header = TRUE; + /* If we have printed a diff for this path, mark it as visited. */ + if (dwi->use_git_diff_format || svn_diff_contains_diffs(diff)) + *wrote_header = TRUE; } } @@ -922,62 +923,43 @@ diff_content_changed(svn_boolean_t *wrote_header, return SVN_NO_ERROR; } +/* An svn_diff_tree_processor_t callback. */ static svn_error_t * -diff_file_opened(svn_boolean_t *tree_conflicted, - svn_boolean_t *skip, - const char *diff_relpath, - svn_revnum_t rev, - void *diff_baton, - apr_pool_t *scratch_pool) -{ - return SVN_NO_ERROR; -} - -/* An svn_wc_diff_callbacks4_t function. */ -static svn_error_t * -diff_file_changed(svn_wc_notify_state_t *content_state, - svn_wc_notify_state_t *prop_state, - svn_boolean_t *tree_conflicted, - const char *diff_relpath, - const char *tmpfile1, - const char *tmpfile2, - svn_revnum_t rev1, - svn_revnum_t rev2, - const char *mimetype1, - const char *mimetype2, +diff_file_changed(const char *relpath, + const svn_diff_source_t *left_source, + const svn_diff_source_t *right_source, + const char *left_file, + const char *right_file, + /*const*/ apr_hash_t *left_props, + /*const*/ apr_hash_t *right_props, + svn_boolean_t file_modified, const apr_array_header_t *prop_changes, - apr_hash_t *original_props, - void *diff_baton, + void *file_baton, + const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - struct diff_cmd_baton *diff_cmd_baton = diff_baton; + diff_writer_info_t *dwi = processor->baton; svn_boolean_t wrote_header = FALSE; - /* During repos->wc diff of a copy revision numbers obtained - * from the working copy are always SVN_INVALID_REVNUM. */ - if (diff_cmd_baton->repos_wc_diff_target_is_copy) - { - if (rev1 == SVN_INVALID_REVNUM && - diff_cmd_baton->revnum1 != SVN_INVALID_REVNUM) - rev1 = diff_cmd_baton->revnum1; - - if (rev2 == SVN_INVALID_REVNUM && - diff_cmd_baton->revnum2 != SVN_INVALID_REVNUM) - rev2 = diff_cmd_baton->revnum2; - } - - if (tmpfile1) - SVN_ERR(diff_content_changed(&wrote_header, diff_relpath, - tmpfile1, tmpfile2, rev1, rev2, - mimetype1, mimetype2, + if (file_modified) + SVN_ERR(diff_content_changed(&wrote_header, relpath, + left_file, right_file, + left_source->revision, + right_source->revision, + svn_prop_get_value(left_props, + SVN_PROP_MIME_TYPE), + svn_prop_get_value(right_props, + SVN_PROP_MIME_TYPE), svn_diff_op_modified, FALSE, NULL, - SVN_INVALID_REVNUM, diff_cmd_baton, + SVN_INVALID_REVNUM, dwi, scratch_pool)); if (prop_changes->nelts > 0) - SVN_ERR(diff_props_changed(diff_relpath, rev1, rev2, FALSE, prop_changes, - original_props, !wrote_header, - diff_cmd_baton, scratch_pool)); + SVN_ERR(diff_props_changed(relpath, + left_source->revision, + right_source->revision, prop_changes, + left_props, !wrote_header, + dwi, scratch_pool)); return SVN_NO_ERROR; } @@ -985,132 +967,125 @@ diff_file_changed(svn_wc_notify_state_t *content_state, each of these next two functions, they can be dumb wrappers around the main workhorse routine. */ -/* An svn_wc_diff_callbacks4_t function. */ +/* An svn_diff_tree_processor_t callback. */ static svn_error_t * -diff_file_added(svn_wc_notify_state_t *content_state, - svn_wc_notify_state_t *prop_state, - svn_boolean_t *tree_conflicted, - const char *diff_relpath, - const char *tmpfile1, - const char *tmpfile2, - svn_revnum_t rev1, - svn_revnum_t rev2, - const char *mimetype1, - const char *mimetype2, - const char *copyfrom_path, - svn_revnum_t copyfrom_revision, - const apr_array_header_t *prop_changes, - apr_hash_t *original_props, - void *diff_baton, +diff_file_added(const char *relpath, + const svn_diff_source_t *copyfrom_source, + const svn_diff_source_t *right_source, + const char *copyfrom_file, + const char *right_file, + /*const*/ apr_hash_t *copyfrom_props, + /*const*/ apr_hash_t *right_props, + void *file_baton, + const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - struct diff_cmd_baton *diff_cmd_baton = diff_baton; + diff_writer_info_t *dwi = processor->baton; svn_boolean_t wrote_header = FALSE; + const char *left_file; + apr_hash_t *left_props; + apr_array_header_t *prop_changes; /* During repos->wc diff of a copy revision numbers obtained * from the working copy are always SVN_INVALID_REVNUM. */ - if (diff_cmd_baton->repos_wc_diff_target_is_copy) + if (copyfrom_source && !dwi->show_copies_as_adds) { - if (rev1 == SVN_INVALID_REVNUM && - diff_cmd_baton->revnum1 != SVN_INVALID_REVNUM) - rev1 = diff_cmd_baton->revnum1; - - if (rev2 == SVN_INVALID_REVNUM && - diff_cmd_baton->revnum2 != SVN_INVALID_REVNUM) - rev2 = diff_cmd_baton->revnum2; + left_file = copyfrom_file; + left_props = copyfrom_props ? copyfrom_props : apr_hash_make(scratch_pool); } - - if (diff_cmd_baton->no_copyfrom_on_add - && (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_revision))) + else { - apr_hash_t *empty_hash = apr_hash_make(scratch_pool); - apr_array_header_t *new_changes; - - /* Rebase changes on having no left source. */ - if (!diff_cmd_baton->empty_file) - SVN_ERR(svn_io_open_unique_file3(NULL, &diff_cmd_baton->empty_file, + if (!dwi->empty_file) + SVN_ERR(svn_io_open_unique_file3(NULL, &dwi->empty_file, NULL, svn_io_file_del_on_pool_cleanup, - diff_cmd_baton->pool, scratch_pool)); + dwi->pool, scratch_pool)); - SVN_ERR(svn_prop_diffs(&new_changes, - svn_prop__patch(original_props, prop_changes, - scratch_pool), - empty_hash, - scratch_pool)); + left_file = dwi->empty_file; + left_props = apr_hash_make(scratch_pool); - tmpfile1 = diff_cmd_baton->empty_file; - prop_changes = new_changes; - original_props = empty_hash; - copyfrom_revision = SVN_INVALID_REVNUM; + copyfrom_source = NULL; + copyfrom_file = NULL; } - if (diff_cmd_baton->no_diff_added) + SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props, scratch_pool)); + + if (dwi->no_diff_added) { - const char *index_path = diff_relpath; + const char *index_path = relpath; - if (diff_cmd_baton->anchor) - index_path = svn_dirent_join(diff_cmd_baton->anchor, diff_relpath, + if (dwi->ddi.anchor) + index_path = svn_dirent_join(dwi->ddi.anchor, relpath, scratch_pool); - SVN_ERR(svn_stream_printf_from_utf8(diff_cmd_baton->outstream, - diff_cmd_baton->header_encoding, scratch_pool, + SVN_ERR(svn_stream_printf_from_utf8(dwi->outstream, + dwi->header_encoding, scratch_pool, "Index: %s (added)" APR_EOL_STR SVN_DIFF__EQUAL_STRING APR_EOL_STR, index_path)); wrote_header = TRUE; } - else if (tmpfile1 && copyfrom_path) - SVN_ERR(diff_content_changed(&wrote_header, diff_relpath, - tmpfile1, tmpfile2, rev1, rev2, - mimetype1, mimetype2, + else if (copyfrom_source && right_file) + SVN_ERR(diff_content_changed(&wrote_header, relpath, + left_file, right_file, + copyfrom_source->revision, + right_source->revision, + svn_prop_get_value(left_props, + SVN_PROP_MIME_TYPE), + svn_prop_get_value(right_props, + SVN_PROP_MIME_TYPE), svn_diff_op_copied, TRUE /* force diff output */, - copyfrom_path, - copyfrom_revision, diff_cmd_baton, - scratch_pool)); - else if (tmpfile1) - SVN_ERR(diff_content_changed(&wrote_header, diff_relpath, - tmpfile1, tmpfile2, rev1, rev2, - mimetype1, mimetype2, + copyfrom_source->repos_relpath, + copyfrom_source->revision, + dwi, scratch_pool)); + else if (right_file) + SVN_ERR(diff_content_changed(&wrote_header, relpath, + left_file, right_file, + DIFF_REVNUM_NONEXISTENT, + right_source->revision, + svn_prop_get_value(left_props, + SVN_PROP_MIME_TYPE), + svn_prop_get_value(right_props, + SVN_PROP_MIME_TYPE), svn_diff_op_added, TRUE /* force diff output */, NULL, SVN_INVALID_REVNUM, - diff_cmd_baton, scratch_pool)); + dwi, scratch_pool)); if (prop_changes->nelts > 0) - SVN_ERR(diff_props_changed(diff_relpath, rev1, rev2, - FALSE, prop_changes, - original_props, ! wrote_header, - diff_cmd_baton, scratch_pool)); + SVN_ERR(diff_props_changed(relpath, + copyfrom_source ? copyfrom_source->revision + : DIFF_REVNUM_NONEXISTENT, + right_source->revision, + prop_changes, + left_props, ! wrote_header, + dwi, scratch_pool)); return SVN_NO_ERROR; } -/* An svn_wc_diff_callbacks4_t function. */ +/* An svn_diff_tree_processor_t callback. */ static svn_error_t * -diff_file_deleted(svn_wc_notify_state_t *state, - svn_boolean_t *tree_conflicted, - const char *diff_relpath, - const char *tmpfile1, - const char *tmpfile2, - const char *mimetype1, - const char *mimetype2, - apr_hash_t *original_props, - void *diff_baton, +diff_file_deleted(const char *relpath, + const svn_diff_source_t *left_source, + const char *left_file, + /*const*/ apr_hash_t *left_props, + void *file_baton, + const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - struct diff_cmd_baton *diff_cmd_baton = diff_baton; + diff_writer_info_t *dwi = processor->baton; - if (diff_cmd_baton->no_diff_deleted) + if (dwi->no_diff_deleted) { - const char *index_path = diff_relpath; + const char *index_path = relpath; - if (diff_cmd_baton->anchor) - index_path = svn_dirent_join(diff_cmd_baton->anchor, diff_relpath, + if (dwi->ddi.anchor) + index_path = svn_dirent_join(dwi->ddi.anchor, relpath, scratch_pool); - SVN_ERR(svn_stream_printf_from_utf8(diff_cmd_baton->outstream, - diff_cmd_baton->header_encoding, scratch_pool, + SVN_ERR(svn_stream_printf_from_utf8(dwi->outstream, + dwi->header_encoding, scratch_pool, "Index: %s (deleted)" APR_EOL_STR SVN_DIFF__EQUAL_STRING APR_EOL_STR, index_path)); @@ -1118,99 +1093,144 @@ diff_file_deleted(svn_wc_notify_state_t *state, else { svn_boolean_t wrote_header = FALSE; - if (tmpfile1) - SVN_ERR(diff_content_changed(&wrote_header, diff_relpath, - tmpfile1, tmpfile2, - diff_cmd_baton->revnum1, - diff_cmd_baton->revnum2, - mimetype1, mimetype2, + + if (!dwi->empty_file) + SVN_ERR(svn_io_open_unique_file3(NULL, &dwi->empty_file, + NULL, svn_io_file_del_on_pool_cleanup, + dwi->pool, scratch_pool)); + + if (left_file) + SVN_ERR(diff_content_changed(&wrote_header, relpath, + left_file, dwi->empty_file, + left_source->revision, + DIFF_REVNUM_NONEXISTENT, + svn_prop_get_value(left_props, + SVN_PROP_MIME_TYPE), + NULL, svn_diff_op_deleted, FALSE, NULL, SVN_INVALID_REVNUM, - diff_cmd_baton, + dwi, scratch_pool)); - /* Should we also report the properties as deleted? */ - } + if (left_props && apr_hash_count(left_props)) + { + apr_array_header_t *prop_changes; - /* We don't list all the deleted properties. */ + SVN_ERR(svn_prop_diffs(&prop_changes, apr_hash_make(scratch_pool), + left_props, scratch_pool)); - return SVN_NO_ERROR; -} - -/* An svn_wc_diff_callbacks4_t function. */ -static svn_error_t * -diff_dir_added(svn_wc_notify_state_t *state, - svn_boolean_t *tree_conflicted, - svn_boolean_t *skip, - svn_boolean_t *skip_children, - const char *diff_relpath, - svn_revnum_t rev, - const char *copyfrom_path, - svn_revnum_t copyfrom_revision, - void *diff_baton, - apr_pool_t *scratch_pool) -{ - /* Do nothing. */ + SVN_ERR(diff_props_changed(relpath, + left_source->revision, + DIFF_REVNUM_NONEXISTENT, + prop_changes, + left_props, ! wrote_header, + dwi, scratch_pool)); + } + } return SVN_NO_ERROR; } /* An svn_wc_diff_callbacks4_t function. */ static svn_error_t * -diff_dir_deleted(svn_wc_notify_state_t *state, - svn_boolean_t *tree_conflicted, - const char *diff_relpath, - void *diff_baton, +diff_dir_changed(const char *relpath, + const svn_diff_source_t *left_source, + const svn_diff_source_t *right_source, + /*const*/ apr_hash_t *left_props, + /*const*/ apr_hash_t *right_props, + const apr_array_header_t *prop_changes, + void *dir_baton, + const struct svn_diff_tree_processor_t *processor, apr_pool_t *scratch_pool) { - /* Do nothing. */ + diff_writer_info_t *dwi = processor->baton; + + SVN_ERR(diff_props_changed(relpath, + left_source->revision, + right_source->revision, + prop_changes, + left_props, + TRUE /* show_diff_header */, + dwi, + scratch_pool)); return SVN_NO_ERROR; } -/* An svn_wc_diff_callbacks4_t function. */ +/* An svn_diff_tree_processor_t callback. */ static svn_error_t * -diff_dir_opened(svn_boolean_t *tree_conflicted, - svn_boolean_t *skip, - svn_boolean_t *skip_children, - const char *diff_relpath, - svn_revnum_t rev, - void *diff_baton, - apr_pool_t *scratch_pool) +diff_dir_added(const char *relpath, + const svn_diff_source_t *copyfrom_source, + const svn_diff_source_t *right_source, + /*const*/ apr_hash_t *copyfrom_props, + /*const*/ apr_hash_t *right_props, + void *dir_baton, + const struct svn_diff_tree_processor_t *processor, + apr_pool_t *scratch_pool) { - /* Do nothing. */ + diff_writer_info_t *dwi = processor->baton; + apr_hash_t *left_props; + apr_array_header_t *prop_changes; - return SVN_NO_ERROR; + if (dwi->no_diff_added) + return SVN_NO_ERROR; + + if (copyfrom_source && !dwi->show_copies_as_adds) + { + left_props = copyfrom_props ? copyfrom_props + : apr_hash_make(scratch_pool); + } + else + { + left_props = apr_hash_make(scratch_pool); + copyfrom_source = NULL; + } + + SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props, + scratch_pool)); + + return svn_error_trace(diff_props_changed(relpath, + copyfrom_source ? copyfrom_source->revision + : DIFF_REVNUM_NONEXISTENT, + right_source->revision, + prop_changes, + left_props, + TRUE /* show_diff_header */, + dwi, + scratch_pool)); } -/* An svn_wc_diff_callbacks4_t function. */ +/* An svn_diff_tree_processor_t callback. */ static svn_error_t * -diff_dir_closed(svn_wc_notify_state_t *contentstate, - svn_wc_notify_state_t *propstate, - svn_boolean_t *tree_conflicted, - const char *diff_relpath, - svn_boolean_t dir_was_added, - void *diff_baton, - apr_pool_t *scratch_pool) +diff_dir_deleted(const char *relpath, + const svn_diff_source_t *left_source, + /*const*/ apr_hash_t *left_props, + void *dir_baton, + const struct svn_diff_tree_processor_t *processor, + apr_pool_t *scratch_pool) { - /* Do nothing. */ + diff_writer_info_t *dwi = processor->baton; + apr_array_header_t *prop_changes; + + if (dwi->no_diff_deleted) + return SVN_NO_ERROR; + + + SVN_ERR(svn_prop_diffs(&prop_changes, apr_hash_make(scratch_pool), + left_props, scratch_pool)); + + SVN_ERR(diff_props_changed(relpath, + left_source->revision, + DIFF_REVNUM_NONEXISTENT, + prop_changes, + left_props, + TRUE /* show_diff_header */, + dwi, + scratch_pool)); return SVN_NO_ERROR; } -static const svn_wc_diff_callbacks4_t diff_callbacks = -{ - diff_file_opened, - diff_file_changed, - diff_file_added, - diff_file_deleted, - diff_dir_deleted, - diff_dir_opened, - diff_dir_added, - diff_dir_props_changed, - diff_dir_closed -}; - /*-----------------------------------------------------------------*/ /** The logic behind 'svn diff' and 'svn merge'. */ @@ -1329,45 +1349,6 @@ check_diff_target_exists(const char *url, return SVN_NO_ERROR; } - -/* Return in *RESOLVED_URL the URL which PATH_OR_URL@PEG_REVISION has in - * REVISION. If the object has no location in REVISION, set *RESOLVED_URL - * to NULL. */ -static svn_error_t * -resolve_pegged_diff_target_url(const char **resolved_url, - svn_ra_session_t *ra_session, - const char *path_or_url, - const svn_opt_revision_t *peg_revision, - const svn_opt_revision_t *revision, - svn_client_ctx_t *ctx, - apr_pool_t *scratch_pool) -{ - svn_error_t *err; - - /* Check if the PATH_OR_URL exists at REVISION. */ - err = svn_client__repos_locations(resolved_url, NULL, - NULL, NULL, - ra_session, - path_or_url, - peg_revision, - revision, - NULL, - ctx, scratch_pool); - if (err) - { - if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES || - err->apr_err == SVN_ERR_FS_NOT_FOUND) - { - svn_error_clear(err); - *resolved_url = NULL; - } - else - return svn_error_trace(err); - } - - return SVN_NO_ERROR; -} - /** Prepare a repos repos diff between PATH_OR_URL1 and * PATH_OR_URL2@PEG_REVISION, in the revision range REVISION1:REVISION2. * Return URLs and peg revisions in *URL1, *REV1 and in *URL2, *REV2. @@ -1375,13 +1356,10 @@ resolve_pegged_diff_target_url(const char **resolved_url, * *TARGET1 and *TARGET2, based on *URL1 and *URL2. * Indicate the corresponding node kinds in *KIND1 and *KIND2, and verify * that at least one of the diff targets exists. - * Set *BASE_PATH corresponding to the URL opened in the new *RA_SESSION - * which is pointing at *ANCHOR1. * Use client context CTX. Do all allocations in POOL. */ static svn_error_t * diff_prepare_repos_repos(const char **url1, const char **url2, - const char **base_path, svn_revnum_t *rev1, svn_revnum_t *rev2, const char **anchor1, @@ -1399,92 +1377,135 @@ diff_prepare_repos_repos(const char **url1, const svn_opt_revision_t *peg_revision, apr_pool_t *pool) { - const char *abspath_or_url2; - const char *abspath_or_url1; + const char *local_abspath1 = NULL; + const char *local_abspath2 = NULL; const char *repos_root_url; const char *wri_abspath = NULL; + svn_client__pathrev_t *resolved1; + svn_client__pathrev_t *resolved2 = NULL; + enum svn_opt_revision_kind peg_kind = peg_revision->kind; if (!svn_path_is_url(path_or_url2)) { - SVN_ERR(svn_dirent_get_absolute(&abspath_or_url2, path_or_url2, pool)); - SVN_ERR(svn_wc__node_get_url(url2, ctx->wc_ctx, abspath_or_url2, + SVN_ERR(svn_dirent_get_absolute(&local_abspath2, path_or_url2, pool)); + SVN_ERR(svn_wc__node_get_url(url2, ctx->wc_ctx, local_abspath2, pool, pool)); - wri_abspath = abspath_or_url2; + wri_abspath = local_abspath2; } else - *url2 = abspath_or_url2 = apr_pstrdup(pool, path_or_url2); + *url2 = apr_pstrdup(pool, path_or_url2); if (!svn_path_is_url(path_or_url1)) { - SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, pool)); - SVN_ERR(svn_wc__node_get_url(url1, ctx->wc_ctx, abspath_or_url1, - pool, pool)); - wri_abspath = abspath_or_url1; + SVN_ERR(svn_dirent_get_absolute(&local_abspath1, path_or_url1, pool)); + wri_abspath = local_abspath1; } - else - *url1 = abspath_or_url1 = apr_pstrdup(pool, path_or_url1); - - /* We need exactly one BASE_PATH, so we'll let the BASE_PATH - calculated for PATH_OR_URL2 override the one for PATH_OR_URL1 - (since the diff will be "applied" to URL2 anyway). */ - *base_path = NULL; - if (strcmp(*url1, path_or_url1) != 0) - *base_path = path_or_url1; - if (strcmp(*url2, path_or_url2) != 0) - *base_path = path_or_url2; SVN_ERR(svn_client_open_ra_session2(ra_session, *url2, wri_abspath, ctx, pool, pool)); /* If we are performing a pegged diff, we need to find out what our actual URLs will be. */ - if (peg_revision->kind != svn_opt_revision_unspecified) + if (peg_kind != svn_opt_revision_unspecified + || path_or_url1 == path_or_url2 + || local_abspath2) { - const char *resolved_url1; - const char *resolved_url2; - - SVN_ERR(resolve_pegged_diff_target_url(&resolved_url2, *ra_session, - path_or_url2, peg_revision, - revision2, ctx, pool)); - - SVN_ERR(svn_ra_reparent(*ra_session, *url1, pool)); - SVN_ERR(resolve_pegged_diff_target_url(&resolved_url1, *ra_session, - path_or_url1, peg_revision, - revision1, ctx, pool)); - - /* Either or both URLs might have changed as a result of resolving - * the PATH_OR_URL@PEG_REVISION's history. If only one of the URLs - * could be resolved, use the same URL for URL1 and URL2, so we can - * show a diff that adds or removes the object (see issue #4153). */ - if (resolved_url2) + svn_error_t *err; + + err = svn_client__resolve_rev_and_url(&resolved2, + *ra_session, path_or_url2, + peg_revision, revision2, + ctx, pool); + if (err) { - *url2 = resolved_url2; - if (!resolved_url1) - *url1 = resolved_url2; + if (err->apr_err != SVN_ERR_CLIENT_UNRELATED_RESOURCES + && err->apr_err != SVN_ERR_FS_NOT_FOUND) + return svn_error_trace(err); + + svn_error_clear(err); + resolved2 = NULL; } - if (resolved_url1) + } + else + resolved2 = NULL; + + if (peg_kind != svn_opt_revision_unspecified + || path_or_url1 == path_or_url2 + || local_abspath1) + { + svn_error_t *err; + + err = svn_client__resolve_rev_and_url(&resolved1, + *ra_session, path_or_url1, + peg_revision, revision1, + ctx, pool); + if (err) { - *url1 = resolved_url1; - if (!resolved_url2) - *url2 = resolved_url1; + if (err->apr_err != SVN_ERR_CLIENT_UNRELATED_RESOURCES + && err->apr_err != SVN_ERR_FS_NOT_FOUND) + return svn_error_trace(err); + + svn_error_clear(err); + resolved1 = NULL; } + } + else + resolved1 = NULL; + + if (resolved1) + { + *url1 = resolved1->url; + *rev1 = resolved1->rev; + } + else + { + /* It would be nice if we could just return an error when resolving a + location fails... But in many such cases we prefer diffing against + an not existing location to show adds od removes (see issue #4153) */ + + if (resolved2 + && (peg_kind != svn_opt_revision_unspecified + || path_or_url1 == path_or_url2)) + *url1 = resolved2->url; + else if (! local_abspath1) + *url1 = path_or_url1; + else + SVN_ERR(svn_wc__node_get_url(url1, ctx->wc_ctx, local_abspath1, + pool, pool)); + + SVN_ERR(svn_client__get_revision_number(rev1, NULL, ctx->wc_ctx, + local_abspath1 /* may be NULL */, + *ra_session, revision1, pool)); + } - /* Reparent the session, since *URL2 might have changed as a result - the above call. */ - SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool)); + if (resolved2) + { + *url2 = resolved2->url; + *rev2 = resolved2->rev; + } + else + { + /* It would be nice if we could just return an error when resolving a + location fails... But in many such cases we prefer diffing against + an not existing location to show adds od removes (see issue #4153) */ + + if (resolved1 + && (peg_kind != svn_opt_revision_unspecified + || path_or_url1 == path_or_url2)) + *url2 = resolved1->url; + /* else keep url2 */ + + SVN_ERR(svn_client__get_revision_number(rev2, NULL, ctx->wc_ctx, + local_abspath2 /* may be NULL */, + *ra_session, revision2, pool)); } /* Resolve revision and get path kind for the second target. */ - SVN_ERR(svn_client__get_revision_number(rev2, NULL, ctx->wc_ctx, - (path_or_url2 == *url2) ? NULL : abspath_or_url2, - *ra_session, revision2, pool)); + SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool)); SVN_ERR(svn_ra_check_path(*ra_session, "", *rev2, kind2, pool)); /* Do the same for the first target. */ SVN_ERR(svn_ra_reparent(*ra_session, *url1, pool)); - SVN_ERR(svn_client__get_revision_number(rev1, NULL, ctx->wc_ctx, - (strcmp(path_or_url1, *url1) == 0) ? NULL : abspath_or_url1, - *ra_session, revision1, pool)); SVN_ERR(svn_ra_check_path(*ra_session, "", *rev1, kind1, pool)); /* Either both URLs must exist at their respective revisions, @@ -1521,12 +1542,37 @@ diff_prepare_repos_repos(const char **url1, if (strcmp(*url1, repos_root_url) != 0 && strcmp(*url2, repos_root_url) != 0) { + svn_node_kind_t ignored_kind; + svn_error_t *err; + svn_uri_split(anchor1, target1, *url1, pool); svn_uri_split(anchor2, target2, *url2, pool); - if (*base_path - && (*kind1 == svn_node_file || *kind2 == svn_node_file)) - *base_path = svn_dirent_dirname(*base_path, pool); + SVN_ERR(svn_ra_reparent(*ra_session, *anchor1, pool)); + + /* We might not have the necessary rights to read the root now. + (It is ok to pass a revision here where the node doesn't exist) */ + err = svn_ra_check_path(*ra_session, "", *rev1, &ignored_kind, pool); + + if (err && (err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN + || err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED)) + { + svn_error_clear(err); + + /* Ok, lets undo the reparent... + + We can't report replacements this way, but at least we can + report changes on the descendants */ + + *anchor1 = svn_path_url_add_component2(*anchor1, *target1, pool); + *anchor2 = svn_path_url_add_component2(*anchor2, *target2, pool); + *target1 = ""; + *target2 = ""; + + SVN_ERR(svn_ra_reparent(*ra_session, *anchor1, pool)); + } + else + SVN_ERR(err); } return SVN_NO_ERROR; @@ -1553,7 +1599,7 @@ diff_prepare_repos_repos(const char **url1, Since Subversion 1.8 we also have a variant of svn_wc_diff called svn_client__arbitrary_nodes_diff, that allows handling WORKING-WORKING - comparisions between nodes in the working copy. + comparisons between nodes in the working copy. So the truth of the matter is, if the caller's arguments can't be pigeonholed into one of these use-cases, we currently bail with a @@ -1583,28 +1629,27 @@ unsupported_diff_error(svn_error_t *child_err) All other options are the same as those passed to svn_client_diff6(). */ static svn_error_t * -diff_wc_wc(const char *path1, +diff_wc_wc(const char **root_relpath, + svn_boolean_t *root_is_dir, + struct diff_driver_info_t *ddi, + const char *path1, const svn_opt_revision_t *revision1, const char *path2, const svn_opt_revision_t *revision2, svn_depth_t depth, svn_boolean_t ignore_ancestry, - svn_boolean_t show_copies_as_adds, - svn_boolean_t use_git_diff_format, const apr_array_header_t *changelists, - const svn_wc_diff_callbacks4_t *callbacks, - struct diff_cmd_baton *callback_baton, + const svn_diff_tree_processor_t *diff_processor, svn_client_ctx_t *ctx, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { const char *abspath1; - svn_error_t *err; - svn_node_kind_t kind; SVN_ERR_ASSERT(! svn_path_is_url(path1)); SVN_ERR_ASSERT(! svn_path_is_url(path2)); - SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, pool)); + SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, scratch_pool)); /* Currently we support only the case where path1 and path2 are the same path. */ @@ -1617,42 +1662,25 @@ diff_wc_wc(const char *path1, "and its working files are supported at this time" ))); - - /* Resolve named revisions to real numbers. */ - err = svn_client__get_revision_number(&callback_baton->revnum1, NULL, - ctx->wc_ctx, abspath1, NULL, - revision1, pool); - - /* In case of an added node, we have no base rev, and we show a revision - * number of 0. Note that this code is currently always asking for - * svn_opt_revision_base. - * ### TODO: get rid of this 0 for added nodes. */ - if (err && (err->apr_err == SVN_ERR_CLIENT_BAD_REVISION)) + if (ddi) { - svn_error_clear(err); - callback_baton->revnum1 = 0; - } - else - SVN_ERR(err); + svn_node_kind_t kind; - callback_baton->revnum2 = SVN_INVALID_REVNUM; /* WC */ + SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath1, + TRUE, FALSE, scratch_pool)); - SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath1, - TRUE, FALSE, pool)); + if (kind != svn_node_dir) + ddi->anchor = svn_dirent_dirname(path1, scratch_pool); + else + ddi->anchor = path1; + } - if (kind != svn_node_dir) - callback_baton->anchor = svn_dirent_dirname(path1, pool); - else - callback_baton->anchor = path1; - - SVN_ERR(svn_wc_diff6(ctx->wc_ctx, - abspath1, - callbacks, callback_baton, - depth, - ignore_ancestry, show_copies_as_adds, - use_git_diff_format, changelists, - ctx->cancel_func, ctx->cancel_baton, - pool)); + SVN_ERR(svn_wc__diff7(root_relpath, root_is_dir, + ctx->wc_ctx, abspath1, depth, + ignore_ancestry, changelists, + diff_processor, + ctx->cancel_func, ctx->cancel_baton, + result_pool, scratch_pool)); return SVN_NO_ERROR; } @@ -1666,9 +1694,9 @@ diff_wc_wc(const char *path1, All other options are the same as those passed to svn_client_diff6(). */ static svn_error_t * -diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks, - struct diff_cmd_baton *callback_baton, - svn_client_ctx_t *ctx, +diff_repos_repos(const char **root_relpath, + svn_boolean_t *root_is_dir, + struct diff_driver_info_t *ddi, const char *path_or_url1, const char *path_or_url2, const svn_opt_revision_t *revision1, @@ -1676,7 +1704,11 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks, const svn_opt_revision_t *peg_revision, svn_depth_t depth, svn_boolean_t ignore_ancestry, - apr_pool_t *pool) + svn_boolean_t text_deltas, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_ra_session_t *extra_ra_session; @@ -1686,11 +1718,8 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks, const svn_delta_editor_t *diff_editor; void *diff_edit_baton; - const svn_diff_tree_processor_t *diff_processor; - const char *url1; const char *url2; - const char *base_path; svn_revnum_t rev1; svn_revnum_t rev2; svn_node_kind_t kind1; @@ -1700,40 +1729,40 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks, const char *target1; const char *target2; svn_ra_session_t *ra_session; - const char *wri_abspath = NULL; /* Prepare info for the repos repos diff. */ - SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &base_path, &rev1, &rev2, + SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &rev1, &rev2, &anchor1, &anchor2, &target1, &target2, &kind1, &kind2, &ra_session, ctx, path_or_url1, path_or_url2, revision1, revision2, peg_revision, - pool)); - - /* Find a WC path for the ra session */ - if (!svn_path_is_url(path_or_url1)) - SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url1, pool)); - else if (!svn_path_is_url(path_or_url2)) - SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url2, pool)); + scratch_pool)); /* Set up the repos_diff editor on BASE_PATH, if available. Otherwise, we just use "". */ - SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor, - callbacks, callback_baton, - TRUE /* walk_deleted_dirs */, - pool, pool)); - - /* Get actual URLs. */ - callback_baton->orig_path_1 = url1; - callback_baton->orig_path_2 = url2; - - /* Get numeric revisions. */ - callback_baton->revnum1 = rev1; - callback_baton->revnum2 = rev2; + if (ddi) + { + /* Get actual URLs. */ + ddi->orig_path_1 = url1; + ddi->orig_path_2 = url2; + + /* This should be moved to the diff writer + - path_or_url are provided by the caller + - target1 is available as *root_relpath + - (kind1 != svn_node_dir || kind2 != svn_node_dir) = !*root_is_dir */ + + if (!svn_path_is_url(path_or_url2)) + ddi->anchor = path_or_url2; + else if (!svn_path_is_url(path_or_url1)) + ddi->anchor = path_or_url1; + else + ddi->anchor = NULL; - callback_baton->ra_session = ra_session; - callback_baton->anchor = base_path; + if (*target1 && ddi->anchor + && (kind1 != svn_node_dir || kind2 != svn_node_dir)) + ddi->anchor = svn_dirent_dirname(ddi->anchor, result_pool); + } /* The repository can bring in a new working copy, but not delete everything. Luckily our new diff handler can just be reversed. */ @@ -1759,45 +1788,65 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks, target1 = str_tmp; diff_processor = svn_diff__tree_processor_reverse_create(diff_processor, - NULL, pool); + NULL, + scratch_pool); } /* Filter the first path component using a filter processor, until we fixed the diff processing to handle this directly */ - if ((kind1 != svn_node_file && kind2 != svn_node_file) && target1[0] != '\0') - { - diff_processor = svn_diff__tree_processor_filter_create(diff_processor, - target1, pool); - } + if (root_relpath) + *root_relpath = apr_pstrdup(result_pool, target1); + else if ((kind1 != svn_node_file && kind2 != svn_node_file) + && target1[0] != '\0') + { + diff_processor = svn_diff__tree_processor_filter_create( + diff_processor, target1, scratch_pool); + } /* Now, we open an extra RA session to the correct anchor location for URL1. This is used during the editor calls to fetch file contents. */ - SVN_ERR(svn_client_open_ra_session2(&extra_ra_session, anchor1, wri_abspath, - ctx, pool, pool)); + SVN_ERR(svn_ra__dup_session(&extra_ra_session, ra_session, anchor1, + scratch_pool, scratch_pool)); + + if (ddi) + { + const char *repos_root_url; + const char *session_url; + + SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, + scratch_pool)); + SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, + scratch_pool)); + + ddi->session_relpath = svn_uri_skip_ancestor(repos_root_url, + session_url, + result_pool); + } SVN_ERR(svn_client__get_diff_editor2( &diff_editor, &diff_edit_baton, extra_ra_session, depth, rev1, - TRUE /* text_deltas */, + text_deltas, diff_processor, ctx->cancel_func, ctx->cancel_baton, - pool)); + scratch_pool)); /* We want to switch our txn into URL2 */ SVN_ERR(svn_ra_do_diff3(ra_session, &reporter, &reporter_baton, rev2, target1, - depth, ignore_ancestry, TRUE /* text_deltas */, - url2, diff_editor, diff_edit_baton, pool)); + depth, ignore_ancestry, text_deltas, + url2, diff_editor, diff_edit_baton, scratch_pool)); /* Drive the reporter; do the diff. */ SVN_ERR(reporter->set_path(reporter_baton, "", rev1, svn_depth_infinity, FALSE, NULL, - pool)); + scratch_pool)); - return svn_error_trace(reporter->finish_report(reporter_baton, pool)); + return svn_error_trace( + reporter->finish_report(reporter_baton, scratch_pool)); } /* Perform a diff between a repository path and a working-copy path. @@ -1811,7 +1860,10 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks, All other options are the same as those passed to svn_client_diff6(). */ static svn_error_t * -diff_repos_wc(const char *path_or_url1, +diff_repos_wc(const char **root_relpath, + svn_boolean_t *root_is_dir, + struct diff_driver_info_t *ddi, + const char *path_or_url1, const svn_opt_revision_t *revision1, const svn_opt_revision_t *peg_revision, const char *path2, @@ -1819,18 +1871,13 @@ diff_repos_wc(const char *path_or_url1, svn_boolean_t reverse, svn_depth_t depth, svn_boolean_t ignore_ancestry, - svn_boolean_t show_copies_as_adds, - svn_boolean_t use_git_diff_format, const apr_array_header_t *changelists, - const svn_wc_diff_callbacks4_t *callbacks, - void *callback_baton, - struct diff_cmd_baton *cmd_baton, + const svn_diff_tree_processor_t *diff_processor, svn_client_ctx_t *ctx, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - apr_pool_t *pool = scratch_pool; - const char *url1, *anchor, *anchor_url, *target; - svn_revnum_t rev; + const char *anchor, *anchor_url, *target; svn_ra_session_t *ra_session; svn_depth_t diff_depth; const svn_ra_reporter3_t *reporter; @@ -1842,197 +1889,236 @@ diff_repos_wc(const char *path_or_url1, const char *abspath_or_url1; const char *abspath2; const char *anchor_abspath; - svn_node_kind_t kind1; - svn_node_kind_t kind2; svn_boolean_t is_copy; svn_revnum_t cf_revision; const char *cf_repos_relpath; const char *cf_repos_root_url; + svn_depth_t cf_depth; + const char *copy_root_abspath; + const char *target_url; + svn_client__pathrev_t *loc1; SVN_ERR_ASSERT(! svn_path_is_url(path2)); if (!svn_path_is_url(path_or_url1)) { - SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, pool)); - SVN_ERR(svn_wc__node_get_url(&url1, ctx->wc_ctx, abspath_or_url1, - pool, pool)); + SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, + scratch_pool)); } else { - url1 = path_or_url1; abspath_or_url1 = path_or_url1; } - SVN_ERR(svn_dirent_get_absolute(&abspath2, path2, pool)); + SVN_ERR(svn_dirent_get_absolute(&abspath2, path2, scratch_pool)); - /* Convert path_or_url1 to a URL to feed to do_diff. */ - SVN_ERR(svn_wc_get_actual_target2(&anchor, &target, - ctx->wc_ctx, path2, - pool, pool)); + /* Check if our diff target is a copied node. */ + SVN_ERR(svn_wc__node_get_origin(&is_copy, + &cf_revision, + &cf_repos_relpath, + &cf_repos_root_url, + NULL, &cf_depth, + ©_root_abspath, + ctx->wc_ctx, abspath2, + FALSE, scratch_pool, scratch_pool)); - /* Fetch the URL of the anchor directory. */ - SVN_ERR(svn_dirent_get_absolute(&anchor_abspath, anchor, pool)); - SVN_ERR(svn_wc__node_get_url(&anchor_url, ctx->wc_ctx, anchor_abspath, - pool, pool)); - SVN_ERR_ASSERT(anchor_url != NULL); + SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc1, + path_or_url1, abspath2, + peg_revision, revision1, + ctx, scratch_pool)); - /* If we are performing a pegged diff, we need to find out what our - actual URLs will be. */ - if (peg_revision->kind != svn_opt_revision_unspecified) + if (revision2->kind == svn_opt_revision_base || !is_copy) { - SVN_ERR(svn_client__repos_locations(&url1, NULL, NULL, NULL, - NULL, - path_or_url1, - peg_revision, - revision1, NULL, - ctx, pool)); - if (!reverse) + /* Convert path_or_url1 to a URL to feed to do_diff. */ + SVN_ERR(svn_wc_get_actual_target2(&anchor, &target, ctx->wc_ctx, path2, + scratch_pool, scratch_pool)); + + /* Handle the ugly case where target is ".." */ + if (*target && !svn_path_is_single_path_component(target)) + { + anchor = svn_dirent_join(anchor, target, scratch_pool); + target = ""; + } + + if (root_relpath) + *root_relpath = apr_pstrdup(result_pool, target); + if (root_is_dir) + *root_is_dir = (*target == '\0'); + + /* Fetch the URL of the anchor directory. */ + SVN_ERR(svn_dirent_get_absolute(&anchor_abspath, anchor, scratch_pool)); + SVN_ERR(svn_wc__node_get_url(&anchor_url, ctx->wc_ctx, anchor_abspath, + scratch_pool, scratch_pool)); + SVN_ERR_ASSERT(anchor_url != NULL); + + target_url = NULL; + } + else /* is_copy && revision2->kind == svn_opt_revision_base */ + { +#if 0 + svn_node_kind_t kind; +#endif + /* ### Ugly hack ahead ### + * + * We're diffing a locally copied/moved node. + * Describe the copy source to the reporter instead of the copy itself. + * Doing the latter would generate a single add_directory() call to the + * diff editor which results in an unexpected diff (the copy would + * be shown as deleted). + * + * ### But if we will receive any real changes from the repositor we + * will most likely fail to apply them as the wc diff editor assumes + * that we have the data to which the change applies in BASE... + */ + + target_url = svn_path_url_add_component2(cf_repos_root_url, + cf_repos_relpath, + scratch_pool); + +#if 0 + /*SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath2, FALSE, FALSE, + scratch_pool)); + + if (kind != svn_node_dir + || strcmp(copy_root_abspath, abspath2) != 0) */ +#endif { - cmd_baton->orig_path_1 = url1; - cmd_baton->orig_path_2 = - svn_path_url_add_component2(anchor_url, target, pool); + /* We are looking at a subdirectory of the repository, + We can describe the parent directory as the anchor.. + + ### This 'appears to work', but that is really dumb luck + ### for the simple cases in the test suite */ + anchor_abspath = svn_dirent_dirname(abspath2, scratch_pool); + anchor_url = svn_path_url_add_component2(cf_repos_root_url, + svn_relpath_dirname( + cf_repos_relpath, + scratch_pool), + scratch_pool); + target = svn_dirent_basename(abspath2, NULL); + anchor = svn_dirent_dirname(path2, scratch_pool); } +#if 0 else { - cmd_baton->orig_path_1 = - svn_path_url_add_component2(anchor_url, target, pool); - cmd_baton->orig_path_2 = url1; + /* This code, while ok can't be enabled without causing test + * failures. The repository will send some changes against + * BASE for nodes that don't have BASE... + */ + anchor_abspath = abspath2; + anchor_url = svn_path_url_add_component2(cf_repos_root_url, + cf_repos_relpath, + scratch_pool); + anchor = path2; + target = ""; } +#endif } - /* Open an RA session to URL1 to figure out its node kind. */ - SVN_ERR(svn_client_open_ra_session2(&ra_session, url1, abspath2, - ctx, pool, pool)); - /* Resolve the revision to use for URL1. */ - SVN_ERR(svn_client__get_revision_number(&rev, NULL, ctx->wc_ctx, - (strcmp(path_or_url1, url1) == 0) - ? NULL : abspath_or_url1, - ra_session, revision1, pool)); - SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind1, pool)); - - /* Figure out the node kind of the local target. */ - SVN_ERR(svn_wc_read_kind2(&kind2, ctx->wc_ctx, abspath2, - TRUE, FALSE, pool)); - - cmd_baton->ra_session = ra_session; - cmd_baton->anchor = anchor; - - if (!reverse) - cmd_baton->revnum1 = rev; - else - cmd_baton->revnum2 = rev; + SVN_ERR(svn_ra_reparent(ra_session, anchor_url, scratch_pool)); - /* Check if our diff target is a copied node. */ - SVN_ERR(svn_wc__node_get_origin(&is_copy, - &cf_revision, - &cf_repos_relpath, - &cf_repos_root_url, - NULL, NULL, - ctx->wc_ctx, abspath2, - FALSE, pool, pool)); + if (ddi) + { + const char *repos_root_url; + + ddi->anchor = anchor; + + if (!reverse) + { + ddi->orig_path_1 = apr_pstrdup(result_pool, loc1->url); + ddi->orig_path_2 = + svn_path_url_add_component2(anchor_url, target, result_pool); + } + else + { + ddi->orig_path_1 = + svn_path_url_add_component2(anchor_url, target, result_pool); + ddi->orig_path_2 = apr_pstrdup(result_pool, loc1->url); + } + + SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, + scratch_pool)); + + ddi->session_relpath = svn_uri_skip_ancestor(repos_root_url, + anchor_url, + result_pool); + } + + if (reverse) + diff_processor = svn_diff__tree_processor_reverse_create( + diff_processor, NULL, scratch_pool); /* Use the diff editor to generate the diff. */ SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth, - SVN_RA_CAPABILITY_DEPTH, pool)); + SVN_RA_CAPABILITY_DEPTH, scratch_pool)); SVN_ERR(svn_wc__get_diff_editor(&diff_editor, &diff_edit_baton, ctx->wc_ctx, anchor_abspath, target, depth, - ignore_ancestry || is_copy, - show_copies_as_adds, - use_git_diff_format, + ignore_ancestry, rev2_is_base, reverse, server_supports_depth, changelists, - callbacks, callback_baton, + diff_processor, ctx->cancel_func, ctx->cancel_baton, - pool, pool)); - SVN_ERR(svn_ra_reparent(ra_session, anchor_url, pool)); + scratch_pool, scratch_pool)); if (depth != svn_depth_infinity) diff_depth = depth; else diff_depth = svn_depth_unknown; - if (is_copy) - { - const char *copyfrom_parent_url; - const char *copyfrom_basename; - svn_depth_t copy_depth; - - cmd_baton->repos_wc_diff_target_is_copy = TRUE; - - /* We're diffing a locally copied/moved node. - * Describe the copy source to the reporter instead of the copy itself. - * Doing the latter would generate a single add_directory() call to the - * diff editor which results in an unexpected diff (the copy would - * be shown as deleted). */ - - if (cf_repos_relpath[0] == '\0') - { - copyfrom_parent_url = cf_repos_root_url; - copyfrom_basename = ""; - } - else - { - const char *parent_relpath; - svn_relpath_split(&parent_relpath, ©from_basename, - cf_repos_relpath, scratch_pool); - copyfrom_parent_url = svn_path_url_add_component2(cf_repos_root_url, - parent_relpath, - scratch_pool); - } - SVN_ERR(svn_ra_reparent(ra_session, copyfrom_parent_url, pool)); + if (is_copy && revision2->kind != svn_opt_revision_base) + { /* Tell the RA layer we want a delta to change our txn to URL1 */ SVN_ERR(svn_ra_do_diff3(ra_session, &reporter, &reporter_baton, - rev, + loc1->rev, target, diff_depth, ignore_ancestry, TRUE, /* text_deltas */ - url1, - diff_editor, diff_edit_baton, pool)); + loc1->url, + diff_editor, diff_edit_baton, + scratch_pool)); /* Report the copy source. */ - SVN_ERR(svn_wc__node_get_depth(©_depth, ctx->wc_ctx, abspath2, - pool)); + if (cf_depth == svn_depth_unknown) + cf_depth = svn_depth_infinity; - if (copy_depth == svn_depth_unknown) - copy_depth = svn_depth_infinity; + /* Reporting the in-wc revision as r0, makes the repository send + everything as added, which avoids using BASE for pristine information, + which is not there (or unrelated) for a copy */ SVN_ERR(reporter->set_path(reporter_baton, "", - cf_revision, - copy_depth, FALSE, NULL, scratch_pool)); + ignore_ancestry ? 0 : cf_revision, + cf_depth, FALSE, NULL, scratch_pool)); - if (strcmp(target, copyfrom_basename) != 0) + if (*target) SVN_ERR(reporter->link_path(reporter_baton, target, - svn_path_url_add_component2( - cf_repos_root_url, - cf_repos_relpath, - scratch_pool), - cf_revision, - copy_depth, FALSE, NULL, scratch_pool)); + target_url, + ignore_ancestry ? 0 : cf_revision, + cf_depth, FALSE, NULL, scratch_pool)); /* Finish the report to generate the diff. */ - SVN_ERR(reporter->finish_report(reporter_baton, pool)); + SVN_ERR(reporter->finish_report(reporter_baton, scratch_pool)); } else { /* Tell the RA layer we want a delta to change our txn to URL1 */ SVN_ERR(svn_ra_do_diff3(ra_session, &reporter, &reporter_baton, - rev, + loc1->rev, target, diff_depth, ignore_ancestry, TRUE, /* text_deltas */ - url1, - diff_editor, diff_edit_baton, pool)); + loc1->url, + diff_editor, diff_edit_baton, + scratch_pool)); /* Create a txn mirror of path2; the diff editor will print diffs in reverse. :-) */ @@ -2043,18 +2129,18 @@ diff_repos_wc(const char *path_or_url1, FALSE, ctx->cancel_func, ctx->cancel_baton, NULL, NULL, /* notification is N/A */ - pool)); + scratch_pool)); } return SVN_NO_ERROR; } -/* This is basically just the guts of svn_client_diff[_peg]6(). */ +/* This is basically just the guts of svn_client_diff[_summarize][_peg]6(). */ static svn_error_t * -do_diff(const svn_wc_diff_callbacks4_t *callbacks, - struct diff_cmd_baton *callback_baton, - svn_client_ctx_t *ctx, +do_diff(const char **root_relpath, + svn_boolean_t *root_is_dir, + diff_driver_info_t *ddi, const char *path_or_url1, const char *path_or_url2, const svn_opt_revision_t *revision1, @@ -2062,10 +2148,12 @@ do_diff(const svn_wc_diff_callbacks4_t *callbacks, const svn_opt_revision_t *peg_revision, svn_depth_t depth, svn_boolean_t ignore_ancestry, - svn_boolean_t show_copies_as_adds, - svn_boolean_t use_git_diff_format, const apr_array_header_t *changelists, - apr_pool_t *pool) + svn_boolean_t text_deltas, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_boolean_t is_repos1; svn_boolean_t is_repos2; @@ -2079,32 +2167,35 @@ do_diff(const svn_wc_diff_callbacks4_t *callbacks, if (is_repos2) { /* ### Ignores 'show_copies_as_adds'. */ - SVN_ERR(diff_repos_repos(callbacks, callback_baton, ctx, + SVN_ERR(diff_repos_repos(root_relpath, root_is_dir, + ddi, path_or_url1, path_or_url2, revision1, revision2, peg_revision, depth, ignore_ancestry, - pool)); + text_deltas, + diff_processor, ctx, + result_pool, scratch_pool)); } else /* path_or_url2 is a working copy path */ { - SVN_ERR(diff_repos_wc(path_or_url1, revision1, peg_revision, + SVN_ERR(diff_repos_wc(root_relpath, root_is_dir, ddi, + path_or_url1, revision1, peg_revision, path_or_url2, revision2, FALSE, depth, - ignore_ancestry, show_copies_as_adds, - use_git_diff_format, changelists, - callbacks, callback_baton, callback_baton, - ctx, pool)); + ignore_ancestry, changelists, + diff_processor, ctx, + result_pool, scratch_pool)); } } else /* path_or_url1 is a working copy path */ { if (is_repos2) { - SVN_ERR(diff_repos_wc(path_or_url2, revision2, peg_revision, + SVN_ERR(diff_repos_wc(root_relpath, root_is_dir, ddi, + path_or_url2, revision2, peg_revision, path_or_url1, revision1, TRUE, depth, - ignore_ancestry, show_copies_as_adds, - use_git_diff_format, changelists, - callbacks, callback_baton, callback_baton, - ctx, pool)); + ignore_ancestry, changelists, + diff_processor, ctx, + result_pool, scratch_pool)); } else /* path_or_url2 is a working copy path */ { @@ -2114,352 +2205,44 @@ do_diff(const svn_wc_diff_callbacks4_t *callbacks, const char *abspath1; const char *abspath2; - SVN_ERR(svn_dirent_get_absolute(&abspath1, path_or_url1, pool)); - SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2, pool)); + SVN_ERR(svn_dirent_get_absolute(&abspath1, path_or_url1, + scratch_pool)); + SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2, + scratch_pool)); - SVN_ERR(svn_client__arbitrary_nodes_diff(abspath1, abspath2, + /* ### What about ddi? */ + + SVN_ERR(svn_client__arbitrary_nodes_diff(root_relpath, root_is_dir, + abspath1, abspath2, depth, - callbacks, - callback_baton, - ctx, pool)); + diff_processor, + ctx, + result_pool, scratch_pool)); } else - SVN_ERR(diff_wc_wc(path_or_url1, revision1, - path_or_url2, revision2, - depth, ignore_ancestry, show_copies_as_adds, - use_git_diff_format, changelists, - callbacks, callback_baton, ctx, pool)); + { + SVN_ERR(diff_wc_wc(root_relpath, root_is_dir, ddi, + path_or_url1, revision1, + path_or_url2, revision2, + depth, ignore_ancestry, changelists, + diff_processor, ctx, + result_pool, scratch_pool)); + } } } return SVN_NO_ERROR; } -/* Perform a diff between a repository path and a working-copy path. - - PATH_OR_URL1 may be either a URL or a working copy path. PATH2 is a - working copy path. REVISION1 and REVISION2 are their respective - revisions. If REVERSE is TRUE, the diff will be done in reverse. - If PEG_REVISION is specified, then PATH_OR_URL1 is the path in the peg - 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(). */ -static svn_error_t * -diff_summarize_repos_wc(svn_client_diff_summarize_func_t summarize_func, - void *summarize_baton, - const char *path_or_url1, - const svn_opt_revision_t *revision1, - const svn_opt_revision_t *peg_revision, - const char *path2, - const svn_opt_revision_t *revision2, - svn_boolean_t reverse, - svn_depth_t depth, - svn_boolean_t ignore_ancestry, - const apr_array_header_t *changelists, - svn_client_ctx_t *ctx, - apr_pool_t *pool) -{ - const char *anchor, *target; - svn_wc_diff_callbacks4_t *callbacks; - void *callback_baton; - struct diff_cmd_baton cmd_baton; - - SVN_ERR_ASSERT(! svn_path_is_url(path2)); - - SVN_ERR(svn_wc_get_actual_target2(&anchor, &target, - ctx->wc_ctx, path2, - pool, pool)); - - SVN_ERR(svn_client__get_diff_summarize_callbacks( - &callbacks, &callback_baton, target, reverse, - summarize_func, summarize_baton, pool)); - - SVN_ERR(diff_repos_wc(path_or_url1, revision1, peg_revision, - path2, revision2, reverse, - depth, FALSE, TRUE, FALSE, changelists, - callbacks, callback_baton, &cmd_baton, - ctx, pool)); - return SVN_NO_ERROR; -} - -/* Perform a summary diff between two working-copy paths. - - 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(). */ -static svn_error_t * -diff_summarize_wc_wc(svn_client_diff_summarize_func_t summarize_func, - void *summarize_baton, - const char *path1, - const svn_opt_revision_t *revision1, - const char *path2, - const svn_opt_revision_t *revision2, - svn_depth_t depth, - svn_boolean_t ignore_ancestry, - const apr_array_header_t *changelists, - svn_client_ctx_t *ctx, - apr_pool_t *pool) -{ - svn_wc_diff_callbacks4_t *callbacks; - void *callback_baton; - const char *abspath1, *target1; - svn_node_kind_t kind; - - SVN_ERR_ASSERT(! svn_path_is_url(path1)); - SVN_ERR_ASSERT(! svn_path_is_url(path2)); - - /* Currently we support only the case where path1 and path2 are the - same path. */ - if ((strcmp(path1, path2) != 0) - || (! ((revision1->kind == svn_opt_revision_base) - && (revision2->kind == svn_opt_revision_working)))) - return unsupported_diff_error - (svn_error_create - (SVN_ERR_INCORRECT_PARAMS, NULL, - _("Summarized diffs are only supported between a path's text-base " - "and its working files at this time"))); - - /* Find the node kind of PATH1 so that we know whether the diff drive will - be anchored at PATH1 or its parent dir. */ - SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, pool)); - SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath1, - TRUE, FALSE, pool)); - target1 = (kind == svn_node_dir) ? "" : svn_dirent_basename(path1, pool); - SVN_ERR(svn_client__get_diff_summarize_callbacks( - &callbacks, &callback_baton, target1, FALSE, - summarize_func, summarize_baton, pool)); - - SVN_ERR(svn_wc_diff6(ctx->wc_ctx, - abspath1, - callbacks, callback_baton, - depth, - ignore_ancestry, FALSE /* show_copies_as_adds */, - FALSE /* use_git_diff_format */, changelists, - ctx->cancel_func, ctx->cancel_baton, - pool)); - return SVN_NO_ERROR; -} - -/* Perform a diff summary between two repository paths. */ -static svn_error_t * -diff_summarize_repos_repos(svn_client_diff_summarize_func_t summarize_func, - void *summarize_baton, - svn_client_ctx_t *ctx, - const char *path_or_url1, - const char *path_or_url2, - const svn_opt_revision_t *revision1, - const svn_opt_revision_t *revision2, - const svn_opt_revision_t *peg_revision, - svn_depth_t depth, - svn_boolean_t ignore_ancestry, - apr_pool_t *pool) -{ - svn_ra_session_t *extra_ra_session; - - const svn_ra_reporter3_t *reporter; - void *reporter_baton; - - const svn_delta_editor_t *diff_editor; - void *diff_edit_baton; - - const svn_diff_tree_processor_t *diff_processor; - - const char *url1; - const char *url2; - const char *base_path; - svn_revnum_t rev1; - svn_revnum_t rev2; - svn_node_kind_t kind1; - svn_node_kind_t kind2; - const char *anchor1; - const char *anchor2; - const char *target1; - const char *target2; - svn_ra_session_t *ra_session; - svn_wc_diff_callbacks4_t *callbacks; - void *callback_baton; - - /* Prepare info for the repos repos diff. */ - SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &base_path, &rev1, &rev2, - &anchor1, &anchor2, &target1, &target2, - &kind1, &kind2, &ra_session, - ctx, path_or_url1, path_or_url2, - revision1, revision2, - peg_revision, pool)); - - /* Set up the repos_diff editor. */ - SVN_ERR(svn_client__get_diff_summarize_callbacks( - &callbacks, &callback_baton, - target1, FALSE, summarize_func, summarize_baton, pool)); - - SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor, - callbacks, callback_baton, - TRUE /* walk_deleted_dirs */, - pool, pool)); - - - /* The repository can bring in a new working copy, but not delete - everything. Luckily our new diff handler can just be reversed. */ - if (kind2 == svn_node_none) - { - const char *str_tmp; - svn_revnum_t rev_tmp; - - str_tmp = url2; - url2 = url1; - url1 = str_tmp; - - rev_tmp = rev2; - rev2 = rev1; - rev1 = rev_tmp; - - str_tmp = anchor2; - anchor2 = anchor1; - anchor1 = str_tmp; - - str_tmp = target2; - target2 = target1; - target1 = str_tmp; - - diff_processor = svn_diff__tree_processor_reverse_create(diff_processor, - NULL, pool); - } - - /* Now, we open an extra RA session to the correct anchor - location for URL1. This is used to get deleted path information. */ - SVN_ERR(svn_client_open_ra_session2(&extra_ra_session, anchor1, NULL, - ctx, pool, pool)); - - SVN_ERR(svn_client__get_diff_editor2(&diff_editor, &diff_edit_baton, - extra_ra_session, - depth, - rev1, - FALSE /* text_deltas */, - diff_processor, - ctx->cancel_func, ctx->cancel_baton, - pool)); - - /* We want to switch our txn into URL2 */ - SVN_ERR(svn_ra_do_diff3 - (ra_session, &reporter, &reporter_baton, rev2, target1, - depth, ignore_ancestry, - FALSE /* do not create text delta */, url2, diff_editor, - diff_edit_baton, pool)); - - /* Drive the reporter; do the diff. */ - SVN_ERR(reporter->set_path(reporter_baton, "", rev1, - svn_depth_infinity, - FALSE, NULL, pool)); - return svn_error_trace(reporter->finish_report(reporter_baton, pool)); -} - -/* This is basically just the guts of svn_client_diff_summarize[_peg]2(). */ -static svn_error_t * -do_diff_summarize(svn_client_diff_summarize_func_t summarize_func, - void *summarize_baton, - svn_client_ctx_t *ctx, - const char *path_or_url1, - const char *path_or_url2, - const svn_opt_revision_t *revision1, - const svn_opt_revision_t *revision2, - const svn_opt_revision_t *peg_revision, - svn_depth_t depth, - svn_boolean_t ignore_ancestry, - const apr_array_header_t *changelists, - apr_pool_t *pool) -{ - svn_boolean_t is_repos1; - svn_boolean_t is_repos2; - - /* Check if paths/revisions are urls/local. */ - SVN_ERR(check_paths(&is_repos1, &is_repos2, path_or_url1, path_or_url2, - revision1, revision2, peg_revision)); - - if (is_repos1) - { - if (is_repos2) - SVN_ERR(diff_summarize_repos_repos(summarize_func, summarize_baton, ctx, - path_or_url1, path_or_url2, - revision1, revision2, - peg_revision, depth, ignore_ancestry, - pool)); - else - SVN_ERR(diff_summarize_repos_wc(summarize_func, summarize_baton, - path_or_url1, revision1, - peg_revision, - path_or_url2, revision2, - FALSE, depth, - ignore_ancestry, - changelists, - ctx, pool)); - } - else /* ! is_repos1 */ - { - if (is_repos2) - SVN_ERR(diff_summarize_repos_wc(summarize_func, summarize_baton, - path_or_url2, revision2, - peg_revision, - path_or_url1, revision1, - TRUE, depth, - ignore_ancestry, - changelists, - ctx, pool)); - else - { - if (revision1->kind == svn_opt_revision_working - && revision2->kind == svn_opt_revision_working) - { - const char *abspath1; - const char *abspath2; - svn_wc_diff_callbacks4_t *callbacks; - void *callback_baton; - const char *target; - svn_node_kind_t kind; - - SVN_ERR(svn_dirent_get_absolute(&abspath1, path_or_url1, pool)); - SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2, pool)); - - SVN_ERR(svn_io_check_resolved_path(abspath1, &kind, pool)); - - if (kind == svn_node_dir) - target = ""; - else - target = svn_dirent_basename(path_or_url1, NULL); - - SVN_ERR(svn_client__get_diff_summarize_callbacks( - &callbacks, &callback_baton, target, FALSE, - summarize_func, summarize_baton, pool)); - - SVN_ERR(svn_client__arbitrary_nodes_diff(abspath1, abspath2, - depth, - callbacks, - callback_baton, - ctx, pool)); - } - else - SVN_ERR(diff_summarize_wc_wc(summarize_func, summarize_baton, - path_or_url1, revision1, - path_or_url2, revision2, - depth, ignore_ancestry, - changelists, ctx, pool)); - } - } - - return SVN_NO_ERROR; -} - - -/* Initialize DIFF_CMD_BATON.diff_cmd and DIFF_CMD_BATON.options, +/* Initialize DWI.diff_cmd and DWI.options, * according to OPTIONS and CONFIG. CONFIG and OPTIONS may be null. - * Allocate the fields in POOL, which should be at least as long-lived - * as the pool DIFF_CMD_BATON itself is allocated in. + * Allocate the fields in RESULT_POOL, which should be at least as long-lived + * as the pool DWI itself is allocated in. */ static svn_error_t * -set_up_diff_cmd_and_options(struct diff_cmd_baton *diff_cmd_baton, - const apr_array_header_t *options, - apr_hash_t *config, apr_pool_t *pool) +create_diff_writer_info(diff_writer_info_t *dwi, + const apr_array_header_t *options, + apr_hash_t *config, apr_pool_t *result_pool) { const char *diff_cmd = NULL; @@ -2475,41 +2258,41 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton *diff_cmd_baton, 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); + options = svn_cstring_split(diff_extensions, " \t\n\r", TRUE, + result_pool); } } if (options == NULL) - options = apr_array_make(pool, 0, sizeof(const char *)); + options = apr_array_make(result_pool, 0, sizeof(const char *)); if (diff_cmd) - SVN_ERR(svn_path_cstring_to_utf8(&diff_cmd_baton->diff_cmd, diff_cmd, - pool)); + SVN_ERR(svn_path_cstring_to_utf8(&dwi->diff_cmd, diff_cmd, + result_pool)); else - diff_cmd_baton->diff_cmd = NULL; + dwi->diff_cmd = NULL; /* If there was a command, arrange options to pass to it. */ - if (diff_cmd_baton->diff_cmd) + if (dwi->diff_cmd) { const char **argv = NULL; int argc = options->nelts; if (argc) { int i; - argv = apr_palloc(pool, argc * sizeof(char *)); + argv = apr_palloc(result_pool, argc * sizeof(char *)); for (i = 0; i < argc; i++) SVN_ERR(svn_utf_cstring_to_utf8(&argv[i], - APR_ARRAY_IDX(options, i, const char *), pool)); + APR_ARRAY_IDX(options, i, const char *), result_pool)); } - diff_cmd_baton->options.for_external.argv = argv; - diff_cmd_baton->options.for_external.argc = argc; + dwi->options.for_external.argv = argv; + dwi->options.for_external.argc = argc; } else /* No command, so arrange options for internal invocation instead. */ { - diff_cmd_baton->options.for_internal - = svn_diff_file_options_create(pool); - SVN_ERR(svn_diff_file_options_parse - (diff_cmd_baton->options.for_internal, options, pool)); + dwi->options.for_internal = svn_diff_file_options_create(result_pool); + SVN_ERR(svn_diff_file_options_parse(dwi->options.for_internal, + options, result_pool)); } return SVN_NO_ERROR; @@ -2574,8 +2357,10 @@ svn_client_diff6(const apr_array_header_t *options, svn_client_ctx_t *ctx, apr_pool_t *pool) { - struct diff_cmd_baton diff_cmd_baton = { 0 }; + diff_writer_info_t dwi = { 0 }; svn_opt_revision_t peg_revision; + const svn_diff_tree_processor_t *diff_processor; + svn_diff_tree_processor_t *processor; if (ignore_properties && properties_only) return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, @@ -2586,36 +2371,54 @@ svn_client_diff6(const apr_array_header_t *options, peg_revision.kind = svn_opt_revision_unspecified; /* setup callback and baton */ - diff_cmd_baton.orig_path_1 = path_or_url1; - diff_cmd_baton.orig_path_2 = path_or_url2; - - SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options, - ctx->config, pool)); - diff_cmd_baton.pool = pool; - diff_cmd_baton.outstream = outstream; - diff_cmd_baton.errstream = errstream; - diff_cmd_baton.header_encoding = header_encoding; - diff_cmd_baton.revnum1 = SVN_INVALID_REVNUM; - diff_cmd_baton.revnum2 = SVN_INVALID_REVNUM; - - diff_cmd_baton.force_binary = ignore_content_type; - diff_cmd_baton.ignore_properties = ignore_properties; - diff_cmd_baton.properties_only = properties_only; - diff_cmd_baton.relative_to_dir = relative_to_dir; - diff_cmd_baton.use_git_diff_format = use_git_diff_format; - diff_cmd_baton.no_diff_added = no_diff_added; - diff_cmd_baton.no_diff_deleted = no_diff_deleted; - diff_cmd_baton.no_copyfrom_on_add = show_copies_as_adds; - - diff_cmd_baton.wc_ctx = ctx->wc_ctx; - diff_cmd_baton.ra_session = NULL; - diff_cmd_baton.anchor = NULL; - - return do_diff(&diff_callbacks, &diff_cmd_baton, ctx, - path_or_url1, path_or_url2, revision1, revision2, - &peg_revision, - depth, ignore_ancestry, show_copies_as_adds, - use_git_diff_format, changelists, pool); + dwi.ddi.orig_path_1 = path_or_url1; + dwi.ddi.orig_path_2 = path_or_url2; + + SVN_ERR(create_diff_writer_info(&dwi, options, + ctx->config, pool)); + dwi.pool = pool; + dwi.outstream = outstream; + dwi.errstream = errstream; + dwi.header_encoding = header_encoding; + + dwi.force_binary = ignore_content_type; + dwi.ignore_properties = ignore_properties; + dwi.properties_only = properties_only; + dwi.relative_to_dir = relative_to_dir; + dwi.use_git_diff_format = use_git_diff_format; + dwi.no_diff_added = no_diff_added; + dwi.no_diff_deleted = no_diff_deleted; + dwi.show_copies_as_adds = show_copies_as_adds; + + dwi.cancel_func = ctx->cancel_func; + dwi.cancel_baton = ctx->cancel_baton; + + dwi.wc_ctx = ctx->wc_ctx; + dwi.ddi.session_relpath = NULL; + dwi.ddi.anchor = NULL; + + processor = svn_diff__tree_processor_create(&dwi, pool); + + processor->dir_added = diff_dir_added; + processor->dir_changed = diff_dir_changed; + processor->dir_deleted = diff_dir_deleted; + + processor->file_added = diff_file_added; + processor->file_changed = diff_file_changed; + processor->file_deleted = diff_file_deleted; + + diff_processor = processor; + + /* --show-copies-as-adds and --git imply --notice-ancestry */ + if (show_copies_as_adds || use_git_diff_format) + ignore_ancestry = FALSE; + + return svn_error_trace(do_diff(NULL, NULL, &dwi.ddi, + path_or_url1, path_or_url2, + revision1, revision2, &peg_revision, + depth, ignore_ancestry, changelists, + TRUE /* text_deltas */, + diff_processor, ctx, pool, pool)); } svn_error_t * @@ -2641,7 +2444,9 @@ svn_client_diff_peg6(const apr_array_header_t *options, svn_client_ctx_t *ctx, apr_pool_t *pool) { - struct diff_cmd_baton diff_cmd_baton = { 0 }; + diff_writer_info_t dwi = { 0 }; + const svn_diff_tree_processor_t *diff_processor; + svn_diff_tree_processor_t *processor; if (ignore_properties && properties_only) return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, @@ -2649,36 +2454,54 @@ svn_client_diff_peg6(const apr_array_header_t *options, "properties at the same time")); /* setup callback and baton */ - diff_cmd_baton.orig_path_1 = path_or_url; - diff_cmd_baton.orig_path_2 = path_or_url; - - SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options, - ctx->config, pool)); - diff_cmd_baton.pool = pool; - diff_cmd_baton.outstream = outstream; - diff_cmd_baton.errstream = errstream; - diff_cmd_baton.header_encoding = header_encoding; - diff_cmd_baton.revnum1 = SVN_INVALID_REVNUM; - diff_cmd_baton.revnum2 = SVN_INVALID_REVNUM; - - diff_cmd_baton.force_binary = ignore_content_type; - diff_cmd_baton.ignore_properties = ignore_properties; - diff_cmd_baton.properties_only = properties_only; - diff_cmd_baton.relative_to_dir = relative_to_dir; - diff_cmd_baton.use_git_diff_format = use_git_diff_format; - diff_cmd_baton.no_diff_added = no_diff_added; - diff_cmd_baton.no_diff_deleted = no_diff_deleted; - diff_cmd_baton.no_copyfrom_on_add = show_copies_as_adds; - - diff_cmd_baton.wc_ctx = ctx->wc_ctx; - diff_cmd_baton.ra_session = NULL; - diff_cmd_baton.anchor = NULL; - - return do_diff(&diff_callbacks, &diff_cmd_baton, ctx, - path_or_url, path_or_url, start_revision, end_revision, - peg_revision, - depth, ignore_ancestry, show_copies_as_adds, - use_git_diff_format, changelists, pool); + dwi.ddi.orig_path_1 = path_or_url; + dwi.ddi.orig_path_2 = path_or_url; + + SVN_ERR(create_diff_writer_info(&dwi, options, + ctx->config, pool)); + dwi.pool = pool; + dwi.outstream = outstream; + dwi.errstream = errstream; + dwi.header_encoding = header_encoding; + + dwi.force_binary = ignore_content_type; + dwi.ignore_properties = ignore_properties; + dwi.properties_only = properties_only; + dwi.relative_to_dir = relative_to_dir; + dwi.use_git_diff_format = use_git_diff_format; + dwi.no_diff_added = no_diff_added; + dwi.no_diff_deleted = no_diff_deleted; + dwi.show_copies_as_adds = show_copies_as_adds; + + dwi.cancel_func = ctx->cancel_func; + dwi.cancel_baton = ctx->cancel_baton; + + dwi.wc_ctx = ctx->wc_ctx; + dwi.ddi.session_relpath = NULL; + dwi.ddi.anchor = NULL; + + processor = svn_diff__tree_processor_create(&dwi, pool); + + processor->dir_added = diff_dir_added; + processor->dir_changed = diff_dir_changed; + processor->dir_deleted = diff_dir_deleted; + + processor->file_added = diff_file_added; + processor->file_changed = diff_file_changed; + processor->file_deleted = diff_file_deleted; + + diff_processor = processor; + + /* --show-copies-as-adds and --git imply --notice-ancestry */ + if (show_copies_as_adds || use_git_diff_format) + ignore_ancestry = FALSE; + + return svn_error_trace(do_diff(NULL, NULL, &dwi.ddi, + path_or_url, path_or_url, + start_revision, end_revision, peg_revision, + depth, ignore_ancestry, changelists, + TRUE /* text_deltas */, + diff_processor, ctx, pool, pool)); } svn_error_t * @@ -2694,14 +2517,24 @@ svn_client_diff_summarize2(const char *path_or_url1, svn_client_ctx_t *ctx, apr_pool_t *pool) { - /* We will never do a pegged diff from here. */ + const svn_diff_tree_processor_t *diff_processor; svn_opt_revision_t peg_revision; + const char **p_root_relpath; + + /* We will never do a pegged diff from here. */ peg_revision.kind = svn_opt_revision_unspecified; - return do_diff_summarize(summarize_func, summarize_baton, ctx, - path_or_url1, path_or_url2, revision1, revision2, - &peg_revision, - depth, ignore_ancestry, changelists, pool); + SVN_ERR(svn_client__get_diff_summarize_callbacks( + &diff_processor, &p_root_relpath, + summarize_func, summarize_baton, + path_or_url1, pool, pool)); + + return svn_error_trace(do_diff(p_root_relpath, NULL, NULL, + path_or_url1, path_or_url2, + revision1, revision2, &peg_revision, + depth, ignore_ancestry, changelists, + FALSE /* text_deltas */, + diff_processor, ctx, pool, pool)); } svn_error_t * @@ -2717,22 +2550,19 @@ svn_client_diff_summarize_peg2(const char *path_or_url, svn_client_ctx_t *ctx, apr_pool_t *pool) { - return do_diff_summarize(summarize_func, summarize_baton, ctx, - path_or_url, path_or_url, - start_revision, end_revision, peg_revision, - depth, ignore_ancestry, changelists, pool); -} - -svn_client_diff_summarize_t * -svn_client_diff_summarize_dup(const svn_client_diff_summarize_t *diff, - apr_pool_t *pool) -{ - svn_client_diff_summarize_t *dup_diff = apr_palloc(pool, sizeof(*dup_diff)); - - *dup_diff = *diff; - - if (diff->path) - dup_diff->path = apr_pstrdup(pool, diff->path); + const svn_diff_tree_processor_t *diff_processor; + const char **p_root_relpath; - return dup_diff; + SVN_ERR(svn_client__get_diff_summarize_callbacks( + &diff_processor, &p_root_relpath, + summarize_func, summarize_baton, + path_or_url, pool, pool)); + + return svn_error_trace(do_diff(p_root_relpath, NULL, NULL, + path_or_url, path_or_url, + start_revision, end_revision, peg_revision, + depth, ignore_ancestry, changelists, + FALSE /* text_deltas */, + diff_processor, ctx, pool, pool)); } + |