Sending again with a non-null attachment MIME-type...
Steve Quoting Stephen Butler <sbut...@elego.de>:
Hi folks, here's a work in progress for fixing a long-standing diff bug. "'svn diff URL1 URL2' not reverse of 'svn diff URL2 URL1'" http://subversion.tigris.org/issues/show_bug.cgi?id=2333 The basic approach was suggested by cmpilato in a comment to the issue. In repos_diff.c:delete_entry(), I call svn_client_list2(), handing it a callback that prints diffs for all files in the "old" tree. It's a bit awkward to call a client API function in repos_diff, whose edit_baton was never yet sullied by an svn_client_ctx_t. The ugliest hack (so far) is in diff_deleted_dir_cb(), the callback. I need to hand get_file_from_ra() a path relative to the RA session's URL. TODOs include: Find out why diff_tests.py 28 still fails. Investigate other failing diff tests. Include dir diffs in the list2-callback. Handle authz failures gracefully. Anything else? Comments welcome. Steve
-- Stephen Butler | Software Developer elego Software Solutions GmbH Gustav-Meyer-Allee 25 | 13355 Berlin | Germany fon: +49 30 2345 8696 | mobile: +49 163 25 45 015 fax: +49 30 2345 8695 | http://www.elegosoft.com Geschäftsführer: Olaf Wagner | Sitz der Gesellschaft: Berlin Amtsgericht Charlottenburg HRB 77719 | USt-IdNr: DE163214194
Index: subversion/tests/cmdline/diff_tests.py =================================================================== --- subversion/tests/cmdline/diff_tests.py (revision 981661) +++ subversion/tests/cmdline/diff_tests.py (working copy) @@ -3533,7 +3533,7 @@ test_list = [ None, diff_keywords, diff_force, diff_schedule_delete, - XFail(diff_renamed_dir), + diff_renamed_dir, diff_property_changes_to_base, diff_mime_type_changes, diff_prop_change_local_propmod, Index: subversion/libsvn_client/repos_diff.c =================================================================== --- subversion/libsvn_client/repos_diff.c (revision 981661) +++ subversion/libsvn_client/repos_diff.c (working copy) @@ -54,6 +54,10 @@ struct edit_baton { repository operation. */ svn_wc_context_t *wc_ctx; + /* A client context passed to client APIs when fetching missing data + during an editor drive. May be NULL. */ + svn_client_ctx_t *ctx; + /* The callback and calback argument that implement the file comparison function */ const svn_wc_diff_callbacks4_t *diff_callbacks; @@ -89,6 +93,14 @@ struct edit_baton { svn_wc_notify_func2_t notify_func; void *notify_baton; + /* TRUE if the operation needs to walk deleted dirs on the "old" side. + FALSE otherwise. */ + svn_boolean_t walk_deleted_dirs; + + /* If WALK_DELETED_DIRS is TRUE, the walk callback will need the repos + root URL. Otherwise NULL. */ + const char *root_url; + apr_pool_t *pool; }; @@ -443,6 +455,59 @@ open_root(void *edit_baton, return SVN_NO_ERROR; } +/* This implements the svn_client_list_func_t API. To be called during + URL-URL diffs only. Prints diff output for each file in a deleted dir + in the "old" URL. */ +static svn_error_t * +diff_deleted_tree_cb(void *baton, + const char *path, + const svn_dirent_t *dirent, + const svn_lock_t *lock, + const char *abs_path, + apr_pool_t *pool) +{ + struct edit_baton *eb = baton; + + if (dirent->kind == svn_node_file) + { + const char *file_relpath, *file_path, *url; + struct file_baton *b; + const char *mimetype1, *mimetype2; + svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable; + svn_boolean_t tree_conflicted = FALSE; + + /* Compare a file being deleted against an empty file */ + + /* ### Hack! get_file_from_ra() expects the file's repository path + relative to the RA session's URL. */ + file_path = svn_dirent_join(abs_path, path, pool); + while (*file_path && *file_path == '/') + file_path++; + url = svn_uri_join(eb->root_url, file_path, pool); + svn_ra_get_path_relative_to_session(eb->ra_session, + &file_relpath, + url, + pool); + b = make_file_baton(file_relpath, FALSE, eb, pool); + SVN_ERR(get_file_from_ra(b, eb->revision)); + + SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision))); + + get_file_mime_types(&mimetype1, &mimetype2, b); + + SVN_ERR(eb->diff_callbacks->file_deleted + (NULL, &state, &tree_conflicted, b->wcpath, + b->path_start_revision, + b->path_end_revision, + mimetype1, mimetype2, + b->pristine_props, + b->edit_baton->diff_cmd_baton, + pool)); + } + + return SVN_NO_ERROR; +} + /* An editor function. */ static svn_error_t * delete_entry(const char *path, @@ -500,6 +565,34 @@ delete_entry(const char *path, (local_dir_abspath, &state, &tree_conflicted, svn_dirent_join(eb->target, path, pool), eb->diff_cmd_baton, pool)); + + /* A URL-URL diff's editor will skip the content of a deleted + dir, so we'll walk the directory's "old" URL to display all + file content as deleted. */ + if (eb->walk_deleted_dirs) + { + const char *dir_url, *session_url; + svn_opt_revision_t revision; + + SVN_ERR(svn_ra_get_session_url(eb->ra_session, + &session_url, + pool)); + dir_url = svn_uri_join(session_url, path, pool); + + revision.kind = svn_opt_revision_number; + revision.value.number = eb->revision; + + SVN_ERR(svn_client_list2(dir_url, + &revision, + &revision, + svn_depth_infinity, + SVN_DIRENT_KIND, + FALSE, + diff_deleted_tree_cb, + eb, + eb->ctx, + pool)); + } break; } default: @@ -1180,6 +1273,7 @@ absent_file(const char *path, svn_error_t * svn_client__get_diff_editor(const char *target, svn_wc_context_t *wc_ctx, + svn_client_ctx_t *ctx, const svn_wc_diff_callbacks4_t *diff_callbacks, void *diff_cmd_baton, svn_depth_t depth, @@ -1198,10 +1292,13 @@ svn_client__get_diff_editor(const char *target, svn_delta_editor_t *tree_editor = svn_delta_default_editor(subpool); struct edit_baton *eb = apr_palloc(subpool, sizeof(*eb)); const char *target_abspath; + const char *root_url; SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, pool)); + SVN_ERR(svn_client_root_url_from_path(&root_url, target, ctx, pool)); eb->target = target; eb->wc_ctx = wc_ctx; + eb->ctx = ctx; eb->diff_callbacks = diff_callbacks; eb->diff_cmd_baton = diff_cmd_baton; eb->dry_run = dry_run; @@ -1213,6 +1310,8 @@ svn_client__get_diff_editor(const char *target, eb->pool = subpool; eb->notify_func = notify_func; eb->notify_baton = notify_baton; + eb->walk_deleted_dirs = TRUE; + eb->root_url = root_url; tree_editor->set_target_revision = set_target_revision; tree_editor->open_root = open_root; Index: subversion/libsvn_client/client.h =================================================================== --- subversion/libsvn_client/client.h (revision 981661) +++ subversion/libsvn_client/client.h (working copy) @@ -631,6 +631,9 @@ svn_client__switch_internal(svn_revnum_t *result_r WC_CTX is a context for the working copy and should be NULL for operations that do not involve a working copy. + CTX is a client context passed to client APIs when fetching missing + data during an editor drive. May be NULL. + DIFF_CMD/DIFF_CMD_BATON represent the callback and callback argument that implement the file comparison function @@ -650,6 +653,7 @@ svn_client__switch_internal(svn_revnum_t *result_r svn_error_t * svn_client__get_diff_editor(const char *target, svn_wc_context_t *wc_ctx, + svn_client_ctx_t *ctx, const svn_wc_diff_callbacks4_t *diff_cmd, void *diff_cmd_baton, svn_depth_t depth, Index: subversion/libsvn_client/merge.c =================================================================== --- subversion/libsvn_client/merge.c (revision 981661) +++ subversion/libsvn_client/merge.c (working copy) @@ -4881,6 +4881,7 @@ drive_merge_report_editor(const char *target_abspa /* Get the diff editor and a reporter with which to, ultimately, drive it. */ SVN_ERR(svn_client__get_diff_editor(target_abspath, merge_b->ctx->wc_ctx, + NULL, &merge_callbacks, merge_b, depth, merge_b->dry_run, merge_b->ra_session2, revision1, Index: subversion/libsvn_client/diff.c =================================================================== --- subversion/libsvn_client/diff.c (revision 981661) +++ subversion/libsvn_client/diff.c (working copy) @@ -1601,7 +1601,7 @@ diff_repos_repos(const struct diff_parameters *dif Otherwise, we just use "". */ SVN_ERR(svn_client__get_diff_editor (drr.base_path ? drr.base_path : "", - NULL, callbacks, callback_baton, diff_param->depth, + NULL, ctx, callbacks, callback_baton, diff_param->depth, FALSE /* doesn't matter for diff */, extra_ra_session, drr.rev1, NULL /* no notify_func */, NULL /* no notify_baton */, ctx->cancel_func, ctx->cancel_baton,