diff options
Diffstat (limited to 'subversion/libsvn_wc/diff_local.c')
-rw-r--r-- | subversion/libsvn_wc/diff_local.c | 784 |
1 files changed, 379 insertions, 405 deletions
diff --git a/subversion/libsvn_wc/diff_local.c b/subversion/libsvn_wc/diff_local.c index dc5bb76..22b498f 100644 --- a/subversion/libsvn_wc/diff_local.c +++ b/subversion/libsvn_wc/diff_local.c @@ -38,15 +38,40 @@ #include "svn_hash.h" #include "private/svn_wc_private.h" +#include "private/svn_diff_tree.h" #include "wc.h" #include "props.h" #include "translate.h" +#include "diff.h" #include "svn_private_config.h" /*-------------------------------------------------------------------------*/ +/* Baton containing the state of a directory + reported open via a diff processor */ +struct node_state_t +{ + struct node_state_t *parent; + + apr_pool_t *pool; + + const char *local_abspath; + const char *relpath; + void *baton; + + svn_diff_source_t *left_src; + svn_diff_source_t *right_src; + svn_diff_source_t *copy_src; + + svn_boolean_t skip; + svn_boolean_t skip_children; + + apr_hash_t *left_props; + apr_hash_t *right_props; + const apr_array_header_t *propchanges; +}; /* The diff baton */ struct diff_baton @@ -57,10 +82,9 @@ struct diff_baton /* Report editor paths relative from this directory */ const char *anchor_abspath; - /* The callbacks and callback argument that implement the file comparison - functions */ - const svn_wc_diff_callbacks4_t *callbacks; - void *callback_baton; + struct node_state_t *cur; + + const svn_diff_tree_processor_t *processor; /* Should this diff ignore node ancestry? */ svn_boolean_t ignore_ancestry; @@ -68,15 +92,6 @@ struct diff_baton /* Should this diff not compare copied files with their source? */ svn_boolean_t show_copies_as_adds; - /* Are we producing a git-style diff? */ - svn_boolean_t use_git_diff_format; - - /* Empty file used to diff adds / deletes */ - const char *empty_file; - - /* Hash whose keys are const char * changelist names. */ - apr_hash_t *changelist_hash; - /* Cancel function/baton */ svn_cancel_func_t cancel_func; void *cancel_baton; @@ -84,342 +99,93 @@ struct diff_baton apr_pool_t *pool; }; -/* Get the empty file associated with the edit baton. This is cached so - * that it can be reused, all empty files are the same. +/* Recursively opens directories on the stack in EB, until LOCAL_ABSPATH + is reached. If RECURSIVE_SKIP is TRUE, don't open LOCAL_ABSPATH itself, + but create it marked with skip+skip_children. */ static svn_error_t * -get_empty_file(struct diff_baton *eb, - const char **empty_file, - apr_pool_t *scratch_pool) -{ - /* Create the file if it does not exist */ - /* Note that we tried to use /dev/null in r857294, but - that won't work on Windows: it's impossible to stat NUL */ - if (!eb->empty_file) - { - SVN_ERR(svn_io_open_unique_file3(NULL, &eb->empty_file, NULL, - svn_io_file_del_on_pool_cleanup, - eb->pool, scratch_pool)); - } - - *empty_file = eb->empty_file; - - return SVN_NO_ERROR; -} - - -/* Return the value of the svn:mime-type property held in PROPS, or NULL - if no such property exists. */ -static const char * -get_prop_mimetype(apr_hash_t *props) -{ - return svn_prop_get_value(props, SVN_PROP_MIME_TYPE); -} - - -/* Diff the file PATH against its text base. At this - * stage we are dealing with a file that does exist in the working copy. - * - * DIR_BATON is the parent directory baton, PATH is the path to the file to - * be compared. - * - * Do all allocation in POOL. - * - * ### TODO: Need to work on replace if the new filename used to be a - * directory. - */ -static svn_error_t * -file_diff(struct diff_baton *eb, - const char *local_abspath, - const char *path, - apr_pool_t *scratch_pool) +ensure_state(struct diff_baton *eb, + const char *local_abspath, + svn_boolean_t recursive_skip, + apr_pool_t *scratch_pool) { - svn_wc__db_t *db = eb->db; - const char *empty_file; - const char *original_repos_relpath; - svn_wc__db_status_t status; - svn_wc__db_kind_t kind; - svn_revnum_t revision; - const svn_checksum_t *checksum; - svn_boolean_t op_root; - svn_boolean_t had_props, props_mod; - svn_boolean_t have_base, have_more_work; - svn_boolean_t replaced = FALSE; - svn_boolean_t base_replace = FALSE; - svn_wc__db_status_t base_status; - svn_revnum_t base_revision = SVN_INVALID_REVNUM; - const svn_checksum_t *base_checksum; - const char *pristine_abspath; - - SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, &checksum, NULL, - &original_repos_relpath, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, - &op_root, &had_props, &props_mod, - &have_base, &have_more_work, NULL, - db, local_abspath, scratch_pool, scratch_pool)); - - if ((status == svn_wc__db_status_added) && (have_base || have_more_work)) - { - SVN_ERR(svn_wc__db_node_check_replace(&replaced, &base_replace, - NULL, db, local_abspath, - scratch_pool)); - - if (replaced && base_replace /* && !have_more_work */) - { - svn_wc__db_kind_t base_kind; - SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, - &base_revision, - NULL, NULL, NULL, NULL, NULL, NULL, - NULL, &base_checksum, NULL, - NULL, NULL, NULL, - db, local_abspath, - scratch_pool, scratch_pool)); - - if (base_status != svn_wc__db_status_normal - || base_kind != kind) - { - /* We can't show a replacement here */ - replaced = FALSE; - base_replace = FALSE; - } - } - else - { - /* We can't look in this middle working layer (yet). - We just report the change itself. - - And if we could look at it, how would we report the addition - of this middle layer (and maybe different layers below that)? - - For 1.7 we just do what we did before: Ignore this layering - problem and just show how the current file got in your wc. - */ - replaced = FALSE; - base_replace = FALSE; - } - } - - /* Now refine ADDED to one of: ADDED, COPIED, MOVED_HERE. Note that only - the latter two have corresponding pristine info to diff against. */ - if (status == svn_wc__db_status_added) - SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, - &original_repos_relpath, NULL, NULL, - NULL, db, local_abspath, - scratch_pool, scratch_pool)); - - - SVN_ERR(get_empty_file(eb, &empty_file, scratch_pool)); - - /* When we show a delete, we show a diff of the original pristine against - * an empty file. - * A base-replace is treated like a delete plus an add. - * - * For this kind of diff we prefer to show the deletion of what was checked - * out over showing what was actually deleted (if that was defined by - * a higher layer). */ - if (status == svn_wc__db_status_deleted || - (base_replace && ! eb->ignore_ancestry)) - { - apr_hash_t *del_props; - const svn_checksum_t *del_checksum; - const char *del_text_abspath; - const char *del_mimetype; - - if (base_replace && ! eb->ignore_ancestry) - { - /* We show a deletion of the information in the BASE layer */ - SVN_ERR(svn_wc__db_base_get_props(&del_props, db, local_abspath, - scratch_pool, scratch_pool)); - - del_checksum = base_checksum; - } - else - { - /* We show a deletion of what was actually deleted */ - SVN_ERR_ASSERT(status == svn_wc__db_status_deleted); - - SVN_ERR(svn_wc__get_pristine_props(&del_props, db, local_abspath, - scratch_pool, scratch_pool)); - - SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL, - NULL, &del_checksum, NULL, - NULL, db, local_abspath, - scratch_pool, scratch_pool)); - } - - SVN_ERR_ASSERT(del_checksum != NULL); - - SVN_ERR(svn_wc__db_pristine_get_path(&del_text_abspath, db, - local_abspath, del_checksum, - scratch_pool, scratch_pool)); - - if (del_props == NULL) - del_props = apr_hash_make(scratch_pool); - - del_mimetype = get_prop_mimetype(del_props); - - SVN_ERR(eb->callbacks->file_deleted(NULL, NULL, path, - del_text_abspath, - empty_file, - del_mimetype, - NULL, - del_props, - eb->callback_baton, - scratch_pool)); - - if (status == svn_wc__db_status_deleted) - { - /* We're here only for showing a delete, so we're done. */ - return SVN_NO_ERROR; - } - } - - if (checksum != NULL) - SVN_ERR(svn_wc__db_pristine_get_path(&pristine_abspath, db, local_abspath, - checksum, - scratch_pool, scratch_pool)); - else if (base_replace && eb->ignore_ancestry) - SVN_ERR(svn_wc__db_pristine_get_path(&pristine_abspath, db, local_abspath, - base_checksum, - scratch_pool, scratch_pool)); - else - pristine_abspath = empty_file; - - /* Now deal with showing additions, or the add-half of replacements. - * If the item is schedule-add *with history*, then we usually want - * to see the usual working vs. text-base comparison, which will show changes - * made since the file was copied. But in case we're showing copies as adds, - * we need to compare the copied file to the empty file. If we're doing a git - * diff, and the file was copied, we need to report the file as added and - * diff it against the text base, so that a "copied" git diff header, and - * possibly a diff against the copy source, will be generated for it. */ - if ((! base_replace && status == svn_wc__db_status_added) || - (base_replace && ! eb->ignore_ancestry) || - ((status == svn_wc__db_status_copied || - status == svn_wc__db_status_moved_here) && - (eb->show_copies_as_adds || eb->use_git_diff_format))) + struct node_state_t *ns; + apr_pool_t *ns_pool; + if (!eb->cur) { - const char *translated = NULL; - apr_hash_t *pristine_props; - apr_hash_t *actual_props; - const char *actual_mimetype; - apr_array_header_t *propchanges; - - - /* Get svn:mime-type from ACTUAL props of PATH. */ - SVN_ERR(svn_wc__get_actual_props(&actual_props, db, local_abspath, - scratch_pool, scratch_pool)); - actual_mimetype = get_prop_mimetype(actual_props); - - /* Set the original properties to empty, then compute "changes" from - that. Essentially, all ACTUAL props will be "added". */ - pristine_props = apr_hash_make(scratch_pool); - SVN_ERR(svn_prop_diffs(&propchanges, actual_props, pristine_props, + const char *relpath; + + relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, local_abspath); + if (! relpath) + return SVN_NO_ERROR; + + /* Don't recurse on the anchor, as that might loop infinately because + svn_dirent_dirname("/",...) -> "/" + svn_dirent_dirname("C:/",...) -> "C:/" (Windows) */ + if (*relpath) + SVN_ERR(ensure_state(eb, + svn_dirent_dirname(local_abspath,scratch_pool), + FALSE, scratch_pool)); - - SVN_ERR(svn_wc__internal_translated_file( - &translated, local_abspath, db, local_abspath, - SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP, - eb->cancel_func, eb->cancel_baton, - scratch_pool, scratch_pool)); - - SVN_ERR(eb->callbacks->file_added(NULL, NULL, NULL, path, - (! eb->show_copies_as_adds && - eb->use_git_diff_format && - status != svn_wc__db_status_added) ? - pristine_abspath : empty_file, - translated, - 0, revision, - NULL, - actual_mimetype, - original_repos_relpath, - SVN_INVALID_REVNUM, propchanges, - pristine_props, eb->callback_baton, - scratch_pool)); } + else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL)) + SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool), + FALSE, + scratch_pool)); else - { - const char *translated = NULL; - apr_hash_t *pristine_props; - const char *pristine_mimetype; - const char *actual_mimetype; - apr_hash_t *actual_props; - apr_array_header_t *propchanges; - svn_boolean_t modified; - - /* Here we deal with showing pure modifications. */ - SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, local_abspath, - FALSE, scratch_pool)); - if (modified) - { - /* Note that this might be the _second_ time we translate - the file, as svn_wc__text_modified_internal_p() might have used a - tmp translated copy too. But what the heck, diff is - already expensive, translating twice for the sake of code - modularity is liveable. */ - SVN_ERR(svn_wc__internal_translated_file( - &translated, local_abspath, db, local_abspath, - SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP, - eb->cancel_func, eb->cancel_baton, - scratch_pool, scratch_pool)); - } + return SVN_NO_ERROR; - /* Get the properties, the svn:mime-type values, and compute the - differences between the two. */ - if (base_replace - && eb->ignore_ancestry) - { - /* We don't want the normal pristine properties (which are - from the WORKING tree). We want the pristines associated - with the BASE tree, which are saved as "revert" props. */ - SVN_ERR(svn_wc__db_base_get_props(&pristine_props, - db, local_abspath, - scratch_pool, scratch_pool)); - } - else - { - /* We can only fetch the pristine props (from BASE or WORKING) if - the node has not been replaced, or it was copied/moved here. */ - SVN_ERR_ASSERT(!replaced - || status == svn_wc__db_status_copied - || status == svn_wc__db_status_moved_here); - - SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props, db, - local_abspath, - scratch_pool, scratch_pool)); - - /* baseprops will be NULL for added nodes */ - if (!pristine_props) - pristine_props = apr_hash_make(scratch_pool); - } - pristine_mimetype = get_prop_mimetype(pristine_props); + if (eb->cur && eb->cur->skip_children) + return SVN_NO_ERROR; - SVN_ERR(svn_wc__db_read_props(&actual_props, db, local_abspath, - scratch_pool, scratch_pool)); - actual_mimetype = get_prop_mimetype(actual_props); + ns_pool = svn_pool_create(eb->cur ? eb->cur->pool : eb->pool); + ns = apr_pcalloc(ns_pool, sizeof(*ns)); - SVN_ERR(svn_prop_diffs(&propchanges, actual_props, pristine_props, - scratch_pool)); + ns->pool = ns_pool; + ns->local_abspath = apr_pstrdup(ns_pool, local_abspath); + ns->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, ns->local_abspath); + ns->parent = eb->cur; + eb->cur = ns; - if (modified || propchanges->nelts > 0) - { - SVN_ERR(eb->callbacks->file_changed(NULL, NULL, NULL, - path, - modified ? pristine_abspath - : NULL, - translated, - revision, - SVN_INVALID_REVNUM, - pristine_mimetype, - actual_mimetype, - propchanges, - pristine_props, - eb->callback_baton, - scratch_pool)); - } + if (recursive_skip) + { + ns->skip = TRUE; + ns->skip_children = TRUE; + return SVN_NO_ERROR; } + { + svn_revnum_t revision; + svn_error_t *err; + + err = svn_wc__db_base_get_info(NULL, NULL, &revision, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + eb->db, local_abspath, + scratch_pool, scratch_pool); + + if (err) + { + if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) + return svn_error_trace(err); + svn_error_clear(err); + + revision = 0; /* Use original revision? */ + } + ns->left_src = svn_diff__source_create(revision, ns->pool); + ns->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, ns->pool); + + SVN_ERR(eb->processor->dir_opened(&ns->baton, &ns->skip, + &ns->skip_children, + ns->relpath, + ns->left_src, + ns->right_src, + NULL /* copyfrom_source */, + ns->parent ? ns->parent->baton : NULL, + eb->processor, + ns->pool, scratch_pool)); + } + return SVN_NO_ERROR; } @@ -431,74 +197,224 @@ diff_status_callback(void *baton, apr_pool_t *scratch_pool) { struct diff_baton *eb = baton; - switch (status->node_status) - { - case svn_wc_status_unversioned: - case svn_wc_status_ignored: - return SVN_NO_ERROR; /* No diff */ + svn_wc__db_t *db = eb->db; - case svn_wc_status_obstructed: - case svn_wc_status_missing: - return SVN_NO_ERROR; /* ### What should we do here? */ + if (! status->versioned) + return SVN_NO_ERROR; /* unversioned (includes dir externals) */ - default: - break; /* Go check other conditions */ + if (status->node_status == svn_wc_status_conflicted + && status->text_status == svn_wc_status_none + && status->prop_status == svn_wc_status_none) + { + /* Node is an actual only node describing a tree conflict */ + return SVN_NO_ERROR; } - if (eb->changelist_hash != NULL - && (!status->changelist - || ! apr_hash_get(eb->changelist_hash, status->changelist, - APR_HASH_KEY_STRING))) - return SVN_NO_ERROR; /* Filtered via changelist */ + /* Not text/prop modified, not copied. Easy out */ + if (status->node_status == svn_wc_status_normal && !status->copied) + return SVN_NO_ERROR; - /* ### The following checks should probably be reversed as it should decide - when *not* to show a diff, because generally all changed nodes should - have a diff. */ - if (status->kind == svn_node_file) + /* Mark all directories where we are no longer inside as closed */ + while (eb->cur + && !svn_dirent_is_ancestor(eb->cur->local_abspath, local_abspath)) { - /* Show a diff when - * - The text is modified - * - Or the properties are modified - * - Or when the node has been replaced - * - Or (if in copies as adds or git mode) when a node is copied */ - if (status->text_status == svn_wc_status_modified - || status->prop_status == svn_wc_status_modified - || status->node_status == svn_wc_status_deleted - || status->node_status == svn_wc_status_replaced - || ((eb->show_copies_as_adds || eb->use_git_diff_format) - && status->copied)) - { - const char *path = svn_dirent_skip_ancestor(eb->anchor_abspath, - local_abspath); + struct node_state_t *ns = eb->cur; - SVN_ERR(file_diff(eb, local_abspath, path, scratch_pool)); + if (!ns->skip) + { + if (ns->propchanges) + SVN_ERR(eb->processor->dir_changed(ns->relpath, + ns->left_src, + ns->right_src, + ns->left_props, + ns->right_props, + ns->propchanges, + ns->baton, + eb->processor, + ns->pool)); + else + SVN_ERR(eb->processor->dir_closed(ns->relpath, + ns->left_src, + ns->right_src, + ns->baton, + eb->processor, + ns->pool)); } + eb->cur = ns->parent; + svn_pool_clear(ns->pool); } - else - { - /* ### This case should probably be extended for git-diff, but this - is what the old diff code provided */ - if (status->node_status == svn_wc_status_deleted - || status->node_status == svn_wc_status_replaced - || status->prop_status == svn_wc_status_modified) - { - apr_array_header_t *propchanges; - apr_hash_t *baseprops; - const char *path = svn_dirent_skip_ancestor(eb->anchor_abspath, - local_abspath); - + SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath, scratch_pool), + FALSE, scratch_pool)); + + if (eb->cur && eb->cur->skip_children) + return SVN_NO_ERROR; + + /* This code does about the same thing as the inner body of + walk_local_nodes_diff() in diff_editor.c, except that + it is already filtered by the status walker, doesn't have to + account for remote changes (and many tiny other details) */ + + { + svn_boolean_t repos_only; + svn_boolean_t local_only; + svn_wc__db_status_t db_status; + svn_boolean_t have_base; + svn_node_kind_t base_kind; + svn_node_kind_t db_kind = status->kind; + svn_depth_t depth_below_here = svn_depth_unknown; + + const char *child_abspath = local_abspath; + const char *child_relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, + local_abspath); + + + repos_only = FALSE; + local_only = FALSE; + + /* ### optimize away this call using status info. Should + be possible in almost every case (except conflict, missing, obst.)*/ + SVN_ERR(svn_wc__db_read_info(&db_status, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + &have_base, NULL, NULL, + eb->db, local_abspath, + scratch_pool, scratch_pool)); + if (!have_base) + { + local_only = TRUE; /* Only report additions */ + } + else if (db_status == svn_wc__db_status_normal) + { + /* Simple diff */ + base_kind = db_kind; + } + else if (db_status == svn_wc__db_status_deleted) + { + svn_wc__db_status_t base_status; + repos_only = TRUE; + SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + eb->db, local_abspath, + scratch_pool, scratch_pool)); - SVN_ERR(svn_wc__internal_propdiff(&propchanges, &baseprops, - eb->db, local_abspath, - scratch_pool, scratch_pool)); + if (base_status != svn_wc__db_status_normal) + return SVN_NO_ERROR; + } + else + { + /* working status is either added or deleted */ + svn_wc__db_status_t base_status; + + SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, + eb->db, local_abspath, + scratch_pool, scratch_pool)); - SVN_ERR(eb->callbacks->dir_props_changed(NULL, NULL, - path, FALSE /* ### ? */, - propchanges, baseprops, - eb->callback_baton, + if (base_status != svn_wc__db_status_normal) + local_only = TRUE; + else if (base_kind != db_kind || !eb->ignore_ancestry) + { + repos_only = TRUE; + local_only = TRUE; + } + } + + if (repos_only) + { + /* Report repository form deleted */ + if (base_kind == svn_node_file) + SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath, + child_relpath, + SVN_INVALID_REVNUM, + eb->processor, + eb->cur ? eb->cur->baton : NULL, + scratch_pool)); + else if (base_kind == svn_node_dir) + SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath, + child_relpath, + SVN_INVALID_REVNUM, + depth_below_here, + eb->processor, + eb->cur ? eb->cur->baton : NULL, + eb->cancel_func, + eb->cancel_baton, + scratch_pool)); + } + else if (!local_only) + { + /* Diff base against actual */ + if (db_kind == svn_node_file) + { + SVN_ERR(svn_wc__diff_base_working_diff(db, child_abspath, + child_relpath, + SVN_INVALID_REVNUM, + eb->processor, + eb->cur + ? eb->cur->baton + : NULL, + FALSE, + eb->cancel_func, + eb->cancel_baton, scratch_pool)); - } - } + } + else if (db_kind == svn_node_dir) + { + SVN_ERR(ensure_state(eb, local_abspath, FALSE, scratch_pool)); + + if (status->prop_status != svn_wc_status_none + && status->prop_status != svn_wc_status_normal) + { + apr_array_header_t *propchanges; + SVN_ERR(svn_wc__db_base_get_props(&eb->cur->left_props, + eb->db, local_abspath, + eb->cur->pool, + scratch_pool)); + SVN_ERR(svn_wc__db_read_props(&eb->cur->right_props, + eb->db, local_abspath, + eb->cur->pool, + scratch_pool)); + + SVN_ERR(svn_prop_diffs(&propchanges, + eb->cur->right_props, + eb->cur->left_props, + eb->cur->pool)); + + eb->cur->propchanges = propchanges; + } + } + } + + if (local_only && (db_status != svn_wc__db_status_deleted)) + { + if (db_kind == svn_node_file) + SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath, + child_relpath, + eb->processor, + eb->cur ? eb->cur->baton : NULL, + FALSE, + eb->cancel_func, + eb->cancel_baton, + scratch_pool)); + else if (db_kind == svn_node_dir) + SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath, + child_relpath, depth_below_here, + eb->processor, + eb->cur ? eb->cur->baton : NULL, + FALSE, + eb->cancel_func, + eb->cancel_baton, + scratch_pool)); + } + + if (db_kind == svn_node_dir && (local_only || repos_only)) + SVN_ERR(ensure_state(eb, local_abspath, TRUE /* skip */, scratch_pool)); + } + return SVN_NO_ERROR; } @@ -519,32 +435,61 @@ svn_wc_diff6(svn_wc_context_t *wc_ctx, apr_pool_t *scratch_pool) { struct diff_baton eb = { 0 }; - svn_wc__db_kind_t kind; + svn_node_kind_t kind; svn_boolean_t get_all; + const svn_diff_tree_processor_t *processor; SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); - SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath, FALSE, + SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath, + FALSE /* allow_missing */, + TRUE /* show_deleted */, + FALSE /* show_hidden */, scratch_pool)); - if (kind == svn_wc__db_kind_dir) - eb.anchor_abspath = local_abspath; + if (kind == svn_node_dir) + eb.anchor_abspath = local_abspath; else eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool); + SVN_ERR(svn_wc__wrap_diff_callbacks(&processor, + callbacks, callback_baton, TRUE, + scratch_pool, scratch_pool)); + + if (use_git_diff_format) + show_copies_as_adds = TRUE; + if (show_copies_as_adds) + ignore_ancestry = FALSE; + + + + /* + if (reverse_order) + processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool); + */ + + if (! show_copies_as_adds && !use_git_diff_format) + processor = svn_diff__tree_processor_copy_as_changed_create(processor, + scratch_pool); + + /* Apply changelist filtering to the output */ + if (changelist_filter && changelist_filter->nelts) + { + apr_hash_t *changelist_hash; + + SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter, + scratch_pool)); + processor = svn_wc__changelist_filter_tree_processor_create( + processor, wc_ctx, local_abspath, + changelist_hash, scratch_pool); + } + eb.db = wc_ctx->db; - eb.callbacks = callbacks; - eb.callback_baton = callback_baton; + eb.processor = processor; eb.ignore_ancestry = ignore_ancestry; eb.show_copies_as_adds = show_copies_as_adds; - eb.use_git_diff_format = use_git_diff_format; - eb.empty_file = NULL; eb.pool = scratch_pool; - if (changelist_filter && changelist_filter->nelts) - SVN_ERR(svn_hash_from_cstring_keys(&eb.changelist_hash, changelist_filter, - scratch_pool)); - - if (show_copies_as_adds || use_git_diff_format) + if (show_copies_as_adds || use_git_diff_format || !ignore_ancestry) get_all = TRUE; /* We need unmodified descendants of copies */ else get_all = FALSE; @@ -559,5 +504,34 @@ svn_wc_diff6(svn_wc_context_t *wc_ctx, cancel_func, cancel_baton, scratch_pool)); + /* Close the remaining open directories */ + while (eb.cur) + { + struct node_state_t *ns = eb.cur; + + if (!ns->skip) + { + if (ns->propchanges) + SVN_ERR(processor->dir_changed(ns->relpath, + ns->left_src, + ns->right_src, + ns->left_props, + ns->right_props, + ns->propchanges, + ns->baton, + processor, + ns->pool)); + else + SVN_ERR(processor->dir_closed(ns->relpath, + ns->left_src, + ns->right_src, + ns->baton, + processor, + ns->pool)); + } + eb.cur = ns->parent; + svn_pool_clear(ns->pool); + } + return SVN_NO_ERROR; } |