diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/libsvn_client/diff_local.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/libsvn_client/diff_local.c')
-rw-r--r-- | subversion/libsvn_client/diff_local.c | 1077 |
1 files changed, 619 insertions, 458 deletions
diff --git a/subversion/libsvn_client/diff_local.c b/subversion/libsvn_client/diff_local.c index 2dd8a1b..df6bc0a 100644 --- a/subversion/libsvn_client/diff_local.c +++ b/subversion/libsvn_client/diff_local.c @@ -45,7 +45,9 @@ #include "svn_subst.h" #include "client.h" +#include "private/svn_sorts_private.h" #include "private/svn_wc_private.h" +#include "private/svn_diff_tree.h" #include "svn_private_config.h" @@ -81,559 +83,718 @@ get_props(apr_hash_t **props, return SVN_NO_ERROR; } -/* Produce a diff between two arbitrary files at LOCAL_ABSPATH1 and - * LOCAL_ABSPATH2, using the diff callbacks from CALLBACKS. - * Use PATH as the name passed to diff callbacks. - * FILE1_IS_EMPTY and FILE2_IS_EMPTY are used as hints which diff callback - * function to use to compare the files (added/deleted/changed). +/* Forward declaration */ +static svn_error_t * +do_file_diff(const char *left_abspath, + const char *right_abspath, + const char *left_root_abspath, + const char *right_root_abspath, + svn_boolean_t left_only, + svn_boolean_t right_only, + void *parent_baton, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool); + +/* Forward declaration */ +static svn_error_t * +do_dir_diff(const char *left_abspath, + const char *right_abspath, + const char *left_root_abspath, + const char *right_root_abspath, + svn_boolean_t left_only, + svn_boolean_t right_only, + svn_boolean_t left_before_right, + svn_depth_t depth, + void *parent_baton, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool); + +/* Produce a diff of depth DEPTH between two arbitrary directories at + * LEFT_ABSPATH1 and RIGHT_ABSPATH2, using the provided diff callbacks + * to show file changes and, for versioned nodes, property changes. + * + * Report paths as relative from LEFT_ROOT_ABSPATH/RIGHT_ROOT_ABSPATH. * - * If ORIGINAL_PROPS_OVERRIDE is not NULL, use it as original properties - * instead of reading properties from LOCAL_ABSPATH1. This is required when - * a file replaces a directory, where LOCAL_ABSPATH1 is an empty file that - * file content must be diffed against, but properties to diff against come - * from the replaced directory. */ + * If LEFT_ONLY is TRUE, only the left source exists (= everything will + * be reported as deleted). If RIGHT_ONLY is TRUE, only the right source + * exists (= everything will be reported as added). + * + * If LEFT_BEFORE_RIGHT is TRUE and left and right are unrelated, left is + * reported first. If false, right is reported first. (This is to allow + * producing a proper inverse diff). + * + * Walk the sources according to depth, and report with parent baton + * PARENT_BATON. */ static svn_error_t * -do_arbitrary_files_diff(const char *local_abspath1, - const char *local_abspath2, - const char *path, - svn_boolean_t file1_is_empty, - svn_boolean_t file2_is_empty, - apr_hash_t *original_props_override, - const svn_wc_diff_callbacks4_t *callbacks, - void *diff_baton, - svn_client_ctx_t *ctx, - apr_pool_t *scratch_pool) +inner_dir_diff(const char *left_abspath, + const char *right_abspath, + const char *left_root_abspath, + const char *right_root_abspath, + svn_boolean_t left_only, + svn_boolean_t right_only, + svn_boolean_t left_before_right, + svn_depth_t depth, + void *parent_baton, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) { - apr_hash_t *original_props; - apr_hash_t *modified_props; - apr_array_header_t *prop_changes; - svn_string_t *original_mime_type = NULL; - svn_string_t *modified_mime_type = NULL; - - if (ctx->cancel_func) - SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); - - /* Try to get properties from either file. It's OK if the files do not - * have properties, or if they are unversioned. */ - if (original_props_override) - original_props = original_props_override; - else - SVN_ERR(get_props(&original_props, local_abspath1, ctx->wc_ctx, - scratch_pool, scratch_pool)); - SVN_ERR(get_props(&modified_props, local_abspath2, ctx->wc_ctx, - scratch_pool, scratch_pool)); + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + apr_hash_t *left_dirents; + apr_hash_t *right_dirents; + apr_array_header_t *sorted_dirents; + svn_error_t *err; + svn_depth_t depth_below_here; + int i; - SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props, - scratch_pool)); + SVN_ERR_ASSERT(depth >= svn_depth_files && depth <= svn_depth_infinity); - /* Try to determine the mime-type of each file. */ - original_mime_type = svn_hash_gets(original_props, SVN_PROP_MIME_TYPE); - if (!file1_is_empty && !original_mime_type) + if (!right_only) { - const char *mime_type; - SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1, - ctx->mimetypes_map, scratch_pool)); + err = svn_io_get_dirents3(&left_dirents, left_abspath, FALSE, + scratch_pool, iterpool); - if (mime_type) - original_mime_type = svn_string_create(mime_type, scratch_pool); + if (err && (APR_STATUS_IS_ENOENT(err->apr_err) + || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) + { + svn_error_clear(err); + left_dirents = apr_hash_make(scratch_pool); + right_only = TRUE; + } + else + SVN_ERR(err); } + else + left_dirents = apr_hash_make(scratch_pool); - modified_mime_type = svn_hash_gets(modified_props, SVN_PROP_MIME_TYPE); - if (!file2_is_empty && !modified_mime_type) + if (!left_only) { - const char *mime_type; - SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1, - ctx->mimetypes_map, scratch_pool)); + err = svn_io_get_dirents3(&right_dirents, right_abspath, FALSE, + scratch_pool, iterpool); - if (mime_type) - modified_mime_type = svn_string_create(mime_type, scratch_pool); + if (err && (APR_STATUS_IS_ENOENT(err->apr_err) + || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) + { + svn_error_clear(err); + right_dirents = apr_hash_make(scratch_pool); + right_only = TRUE; + } + else + SVN_ERR(err); } + else + right_dirents = apr_hash_make(scratch_pool); + + if (left_only && right_only) + return SVN_NO_ERROR; /* Somebody deleted the directory?? */ - /* Produce the diff. */ - if (file1_is_empty && !file2_is_empty) - SVN_ERR(callbacks->file_added(NULL, NULL, NULL, path, - local_abspath1, local_abspath2, - /* ### TODO get real revision info - * for versioned files? */ - SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, - original_mime_type ? - original_mime_type->data : NULL, - modified_mime_type ? - modified_mime_type->data : NULL, - /* ### TODO get copyfrom? */ - NULL, SVN_INVALID_REVNUM, - prop_changes, original_props, - diff_baton, scratch_pool)); - else if (!file1_is_empty && file2_is_empty) - SVN_ERR(callbacks->file_deleted(NULL, NULL, path, - local_abspath1, local_abspath2, - original_mime_type ? - original_mime_type->data : NULL, - modified_mime_type ? - modified_mime_type->data : NULL, - original_props, - diff_baton, scratch_pool)); + if (depth != svn_depth_infinity) + depth_below_here = svn_depth_empty; else + depth_below_here = svn_depth_infinity; + + sorted_dirents = svn_sort__hash(apr_hash_merge(iterpool, left_dirents, + right_dirents, NULL, NULL), + svn_sort_compare_items_as_paths, + scratch_pool); + + for (i = 0; i < sorted_dirents->nelts; i++) { - svn_stream_t *file1; - svn_stream_t *file2; - svn_boolean_t same; - svn_string_t *val; - /* We have two files, which may or may not be the same. + svn_sort__item_t* elt = &APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t); + svn_io_dirent2_t *left_dirent; + svn_io_dirent2_t *right_dirent; + const char *child_left_abspath; + const char *child_right_abspath; - ### Our caller assumes that we should ignore symlinks here and - handle them as normal paths. Perhaps that should change? - */ - SVN_ERR(svn_stream_open_readonly(&file1, local_abspath1, scratch_pool, - scratch_pool)); + svn_pool_clear(iterpool); - SVN_ERR(svn_stream_open_readonly(&file2, local_abspath2, scratch_pool, - scratch_pool)); + if (ctx->cancel_func) + SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); - /* Wrap with normalization, etc. if necessary */ - if (original_props) - { - val = svn_hash_gets(original_props, SVN_PROP_EOL_STYLE); + if (svn_wc_is_adm_dir(elt->key, iterpool)) + continue; - if (val) + left_dirent = right_only ? NULL : svn_hash_gets(left_dirents, elt->key); + right_dirent = left_only ? NULL : svn_hash_gets(right_dirents, elt->key); + + child_left_abspath = svn_dirent_join(left_abspath, elt->key, iterpool); + child_right_abspath = svn_dirent_join(right_abspath, elt->key, iterpool); + + if (((left_dirent == NULL) != (right_dirent == NULL)) + || (left_dirent->kind != right_dirent->kind)) + { + /* Report delete and/or add */ + if (left_dirent && left_before_right) { - svn_subst_eol_style_t style; - const char *eol; - svn_subst_eol_style_from_value(&style, &eol, val->data); - - /* ### Ignoring keywords */ - if (eol) - file1 = svn_subst_stream_translated(file1, eol, TRUE, - NULL, FALSE, - scratch_pool); + if (left_dirent->kind == svn_node_file) + SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, parent_baton, + diff_processor, ctx, iterpool)); + else if (depth >= svn_depth_immediates) + SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, left_before_right, + depth_below_here, parent_baton, + diff_processor, ctx, iterpool)); } - } - if (modified_props) - { - val = svn_hash_gets(modified_props, SVN_PROP_EOL_STYLE); + if (right_dirent) + { + if (right_dirent->kind == svn_node_file) + SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + FALSE, TRUE, parent_baton, + diff_processor, ctx, iterpool)); + else if (depth >= svn_depth_immediates) + SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + FALSE, TRUE, left_before_right, + depth_below_here, parent_baton, + diff_processor, ctx, iterpool)); + } - if (val) + if (left_dirent && !left_before_right) { - svn_subst_eol_style_t style; - const char *eol; - svn_subst_eol_style_from_value(&style, &eol, val->data); - - /* ### Ignoring keywords */ - if (eol) - file2 = svn_subst_stream_translated(file2, eol, TRUE, - NULL, FALSE, - scratch_pool); + if (left_dirent->kind == svn_node_file) + SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, parent_baton, + diff_processor, ctx, iterpool)); + else if (depth >= svn_depth_immediates) + SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, left_before_right, + depth_below_here, parent_baton, + diff_processor, ctx, iterpool)); } } - - SVN_ERR(svn_stream_contents_same2(&same, file1, file2, scratch_pool)); - - if (! same || prop_changes->nelts > 0) + else if (left_dirent->kind == svn_node_file) { - /* ### We should probably pass the normalized data we created using - the subst streams as that is what diff users expect */ - SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, path, - same ? NULL : local_abspath1, - same ? NULL : local_abspath2, - /* ### TODO get real revision info - * for versioned files? */ - SVN_INVALID_REVNUM /* rev1 */, - SVN_INVALID_REVNUM /* rev2 */, - original_mime_type ? - original_mime_type->data : NULL, - modified_mime_type ? - modified_mime_type->data : NULL, - prop_changes, original_props, - diff_baton, scratch_pool)); + /* Perform file-file diff */ + SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + FALSE, FALSE, parent_baton, + diff_processor, ctx, iterpool)); + } + else if (depth >= svn_depth_immediates) + { + /* Perform dir-dir diff */ + SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath, + left_root_abspath, right_root_abspath, + FALSE, FALSE, left_before_right, + depth_below_here, parent_baton, + diff_processor, ctx, iterpool)); } } return SVN_NO_ERROR; } -struct arbitrary_diff_walker_baton { - /* The root directories of the trees being compared. */ - const char *root1_abspath; - const char *root2_abspath; - - /* TRUE if recursing within an added subtree of root2_abspath that - * does not exist in root1_abspath. */ - svn_boolean_t recursing_within_added_subtree; +/* Translates *LEFT_ABSPATH to a temporary file if PROPS specify that the + file needs translation. *LEFT_ABSPATH is updated to point to a file that + lives at least as long as RESULT_POOL when translation is necessary. + Otherwise the value is not updated */ +static svn_error_t * +translate_if_necessary(const char **local_abspath, + apr_hash_t *props, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + const svn_string_t *eol_style_val; + const svn_string_t *keywords_val; + svn_subst_eol_style_t eol_style; + const char *eol; + apr_hash_t *keywords; + svn_stream_t *contents; + svn_stream_t *dst; + + /* if (svn_hash_gets(props, SVN_PROP_SPECIAL)) + ### TODO: Implement */ + + eol_style_val = svn_hash_gets(props, SVN_PROP_EOL_STYLE); + keywords_val = svn_hash_gets(props, SVN_PROP_KEYWORDS); + + if (eol_style_val) + svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data); + else + { + eol = NULL; + eol_style = svn_subst_eol_style_none; + } - /* TRUE if recursing within an administrative (.i.e. .svn) directory. */ - svn_boolean_t recursing_within_adm_dir; + if (keywords_val) + SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data, + APR_STRINGIFY(SVN_INVALID_REVNUM), + "", "", 0, "", scratch_pool)); + else + keywords = NULL; - /* The absolute path of the adm dir if RECURSING_WITHIN_ADM_DIR is TRUE. - * Else this is NULL.*/ - const char *adm_dir_abspath; + if (!svn_subst_translation_required(eol_style, eol, keywords, FALSE, FALSE)) + return SVN_NO_ERROR; - /* A path to an empty file used for diffs that add/delete files. */ - const char *empty_file_abspath; + SVN_ERR(svn_stream_open_readonly(&contents, *local_abspath, + scratch_pool, scratch_pool)); - const svn_wc_diff_callbacks4_t *callbacks; - void *diff_baton; - svn_client_ctx_t *ctx; - apr_pool_t *pool; -} arbitrary_diff_walker_baton; + SVN_ERR(svn_stream_open_unique(&dst, local_abspath, NULL, + svn_io_file_del_on_pool_cleanup, + result_pool, scratch_pool)); -/* Forward declaration needed because this function has a cyclic - * dependency with do_arbitrary_dirs_diff(). */ -static svn_error_t * -arbitrary_diff_walker(void *baton, const char *local_abspath, - const apr_finfo_t *finfo, - apr_pool_t *scratch_pool); + dst = svn_subst_stream_translated(dst, eol, TRUE /* repair */, + keywords, FALSE /* expand */, + scratch_pool); -/* Another forward declaration. */ -static svn_error_t * -arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b, - const char *local_abspath, - svn_depth_t depth, - apr_pool_t *scratch_pool); + SVN_ERR(svn_stream_copy3(contents, dst, cancel_func, cancel_baton, + scratch_pool)); -/* Produce a diff of depth DEPTH between two arbitrary directories at - * LOCAL_ABSPATH1 and LOCAL_ABSPATH2, using the provided diff callbacks - * to show file changes and, for versioned nodes, property changes. - * - * If ROOT_ABSPATH1 and ROOT_ABSPATH2 are not NULL, show paths in diffs - * relative to these roots, rather than relative to LOCAL_ABSPATH1 and - * LOCAL_ABSPATH2. This is needed when crawling a subtree that exists - * only within LOCAL_ABSPATH2. */ -static svn_error_t * -do_arbitrary_dirs_diff(const char *local_abspath1, - const char *local_abspath2, - const char *root_abspath1, - const char *root_abspath2, - svn_depth_t depth, - const svn_wc_diff_callbacks4_t *callbacks, - void *diff_baton, - svn_client_ctx_t *ctx, - apr_pool_t *scratch_pool) -{ - apr_file_t *empty_file; - svn_node_kind_t kind1; - - struct arbitrary_diff_walker_baton b; - - /* If LOCAL_ABSPATH1 is not a directory, crawl LOCAL_ABSPATH2 instead - * and compare it to LOCAL_ABSPATH1, showing only additions. - * This case can only happen during recursion from arbitrary_diff_walker(), - * because do_arbitrary_nodes_diff() prevents this from happening at - * the root of the comparison. */ - SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool)); - b.recursing_within_added_subtree = (kind1 != svn_node_dir); - - b.root1_abspath = root_abspath1 ? root_abspath1 : local_abspath1; - b.root2_abspath = root_abspath2 ? root_abspath2 : local_abspath2; - b.recursing_within_adm_dir = FALSE; - b.adm_dir_abspath = NULL; - b.callbacks = callbacks; - b.diff_baton = diff_baton; - b.ctx = ctx; - b.pool = scratch_pool; - - SVN_ERR(svn_io_open_unique_file3(&empty_file, &b.empty_file_abspath, - NULL, svn_io_file_del_on_pool_cleanup, - scratch_pool, scratch_pool)); - - if (depth <= svn_depth_immediates) - SVN_ERR(arbitrary_diff_this_dir(&b, local_abspath1, depth, scratch_pool)); - else if (depth == svn_depth_infinity) - SVN_ERR(svn_io_dir_walk2(b.recursing_within_added_subtree ? local_abspath2 - : local_abspath1, - 0, arbitrary_diff_walker, &b, scratch_pool)); return SVN_NO_ERROR; } -/* Produce a diff of depth DEPTH for the directory at LOCAL_ABSPATH, - * using information from the arbitrary_diff_walker_baton B. - * LOCAL_ABSPATH is the path being crawled and can be on either side - * of the diff depending on baton->recursing_within_added_subtree. */ +/* Handles reporting of a file for inner_dir_diff */ static svn_error_t * -arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b, - const char *local_abspath, - svn_depth_t depth, - apr_pool_t *scratch_pool) +do_file_diff(const char *left_abspath, + const char *right_abspath, + const char *left_root_abspath, + const char *right_root_abspath, + svn_boolean_t left_only, + svn_boolean_t right_only, + void *parent_baton, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) { - const char *local_abspath1; - const char *local_abspath2; - svn_node_kind_t kind1; - svn_node_kind_t kind2; - const char *child_relpath; - apr_hash_t *dirents1; - apr_hash_t *dirents2; - apr_hash_t *merged_dirents; - apr_array_header_t *sorted_dirents; - int i; - apr_pool_t *iterpool; - - if (b->recursing_within_adm_dir) - { - if (svn_dirent_skip_ancestor(b->adm_dir_abspath, local_abspath)) - return SVN_NO_ERROR; - else - { - b->recursing_within_adm_dir = FALSE; - b->adm_dir_abspath = NULL; - } - } - else if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL), - scratch_pool)) - { - b->recursing_within_adm_dir = TRUE; - b->adm_dir_abspath = apr_pstrdup(b->pool, local_abspath); - return SVN_NO_ERROR; - } + const char *relpath; + svn_diff_source_t *left_source; + svn_diff_source_t *right_source; + svn_boolean_t skip = FALSE; + apr_hash_t *left_props; + apr_hash_t *right_props; + void *file_baton; + + relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath); + + if (! right_only) + left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); + else + left_source = NULL; - if (b->recursing_within_added_subtree) - child_relpath = svn_dirent_skip_ancestor(b->root2_abspath, local_abspath); + if (! left_only) + right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); else - child_relpath = svn_dirent_skip_ancestor(b->root1_abspath, local_abspath); - if (!child_relpath) + right_source = NULL; + + SVN_ERR(diff_processor->file_opened(&file_baton, &skip, + relpath, + left_source, + right_source, + NULL /* copyfrom_source */, + parent_baton, + diff_processor, + scratch_pool, + scratch_pool)); + + if (skip) return SVN_NO_ERROR; - local_abspath1 = svn_dirent_join(b->root1_abspath, child_relpath, - scratch_pool); - SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool)); + if (! right_only) + { + SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx, + scratch_pool, scratch_pool)); - local_abspath2 = svn_dirent_join(b->root2_abspath, child_relpath, - scratch_pool); - SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool)); + /* We perform a mimetype detection to avoid diffing binary files + for textual changes.*/ + if (! svn_hash_gets(left_props, SVN_PROP_MIME_TYPE)) + { + const char *mime_type; - if (depth > svn_depth_empty) - { - if (kind1 == svn_node_dir) - SVN_ERR(svn_io_get_dirents3(&dirents1, local_abspath1, - TRUE, /* only_check_type */ - scratch_pool, scratch_pool)); - else - dirents1 = apr_hash_make(scratch_pool); + /* ### Use libmagic magic? */ + SVN_ERR(svn_io_detect_mimetype2(&mime_type, left_abspath, + ctx->mimetypes_map, scratch_pool)); + + if (mime_type) + svn_hash_sets(left_props, SVN_PROP_MIME_TYPE, + svn_string_create(mime_type, scratch_pool)); + } + + SVN_ERR(translate_if_necessary(&left_abspath, left_props, + ctx->cancel_func, ctx->cancel_baton, + scratch_pool, scratch_pool)); } + else + left_props = NULL; - if (kind2 == svn_node_dir) + if (! left_only) { - apr_hash_t *original_props; - apr_hash_t *modified_props; - apr_array_header_t *prop_changes; - - /* Show any property changes for this directory. */ - SVN_ERR(get_props(&original_props, local_abspath1, b->ctx->wc_ctx, - scratch_pool, scratch_pool)); - SVN_ERR(get_props(&modified_props, local_abspath2, b->ctx->wc_ctx, + SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx, scratch_pool, scratch_pool)); - SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props, - scratch_pool)); - if (prop_changes->nelts > 0) - SVN_ERR(b->callbacks->dir_props_changed(NULL, NULL, child_relpath, - FALSE /* was_added */, - prop_changes, original_props, - b->diff_baton, - scratch_pool)); - - if (depth > svn_depth_empty) - { - /* Read directory entries. */ - SVN_ERR(svn_io_get_dirents3(&dirents2, local_abspath2, - TRUE, /* only_check_type */ - scratch_pool, scratch_pool)); - } - } - else if (depth > svn_depth_empty) - dirents2 = apr_hash_make(scratch_pool); - if (depth <= svn_depth_empty) - return SVN_NO_ERROR; + /* We perform a mimetype detection to avoid diffing binary files + for textual changes.*/ + if (! svn_hash_gets(right_props, SVN_PROP_MIME_TYPE)) + { + const char *mime_type; - /* Compare dirents1 to dirents2 and show added/deleted/changed files. */ - merged_dirents = apr_hash_merge(scratch_pool, dirents1, dirents2, - NULL, NULL); - sorted_dirents = svn_sort__hash(merged_dirents, - svn_sort_compare_items_as_paths, - scratch_pool); - iterpool = svn_pool_create(scratch_pool); - for (i = 0; i < sorted_dirents->nelts; i++) - { - svn_sort__item_t elt = APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t); - const char *name = elt.key; - svn_io_dirent2_t *dirent1; - svn_io_dirent2_t *dirent2; - const char *child1_abspath; - const char *child2_abspath; + /* ### Use libmagic magic? */ + SVN_ERR(svn_io_detect_mimetype2(&mime_type, right_abspath, + ctx->mimetypes_map, scratch_pool)); - svn_pool_clear(iterpool); + if (mime_type) + svn_hash_sets(right_props, SVN_PROP_MIME_TYPE, + svn_string_create(mime_type, scratch_pool)); + } - if (b->ctx->cancel_func) - SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton)); + SVN_ERR(translate_if_necessary(&right_abspath, right_props, + ctx->cancel_func, ctx->cancel_baton, + scratch_pool, scratch_pool)); - if (strcmp(name, SVN_WC_ADM_DIR_NAME) == 0) - continue; + } + else + right_props = NULL; - dirent1 = svn_hash_gets(dirents1, name); - if (!dirent1) - { - dirent1 = svn_io_dirent2_create(iterpool); - dirent1->kind = svn_node_none; - } - dirent2 = svn_hash_gets(dirents2, name); - if (!dirent2) - { - dirent2 = svn_io_dirent2_create(iterpool); - dirent2->kind = svn_node_none; - } + if (left_only) + { + SVN_ERR(diff_processor->file_deleted(relpath, + left_source, + left_abspath, + left_props, + file_baton, + diff_processor, + scratch_pool)); + } + else if (right_only) + { + SVN_ERR(diff_processor->file_added(relpath, + NULL /* copyfrom_source */, + right_source, + NULL /* copyfrom_file */, + right_abspath, + NULL /* copyfrom_props */, + right_props, + file_baton, + diff_processor, + scratch_pool)); + } + else + { + /* ### Perform diff -> close/changed */ + svn_boolean_t same; + apr_array_header_t *prop_changes; - child1_abspath = svn_dirent_join(local_abspath1, name, iterpool); - child2_abspath = svn_dirent_join(local_abspath2, name, iterpool); + SVN_ERR(svn_io_files_contents_same_p(&same, left_abspath, right_abspath, + scratch_pool)); - if (dirent1->special) - SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent1->kind, - iterpool)); - if (dirent2->special) - SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent2->kind, - iterpool)); + SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props, + scratch_pool)); - if (dirent1->kind == svn_node_dir && - dirent2->kind == svn_node_dir) + if (!same || prop_changes->nelts > 0) { - if (depth == svn_depth_immediates) - { - /* Not using the walker, so show property diffs on these dirs. */ - SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath, - b->root1_abspath, b->root2_abspath, - svn_depth_empty, - b->callbacks, b->diff_baton, - b->ctx, iterpool)); - } - else - { - /* Either the walker will visit these directories (with - * depth=infinity) and they will be processed as 'this dir' - * later, or we're showing file children only (depth=files). */ - continue; - } - + SVN_ERR(diff_processor->file_changed(relpath, + left_source, + right_source, + same ? NULL : left_abspath, + same ? NULL : right_abspath, + left_props, + right_props, + !same, + prop_changes, + file_baton, + diff_processor, + scratch_pool)); } - - /* Files that exist only in dirents1. */ - if (dirent1->kind == svn_node_file && - (dirent2->kind == svn_node_dir || dirent2->kind == svn_node_none)) - SVN_ERR(do_arbitrary_files_diff(child1_abspath, b->empty_file_abspath, - svn_relpath_join(child_relpath, name, - iterpool), - FALSE, TRUE, NULL, - b->callbacks, b->diff_baton, - b->ctx, iterpool)); - - /* Files that exist only in dirents2. */ - if (dirent2->kind == svn_node_file && - (dirent1->kind == svn_node_dir || dirent1->kind == svn_node_none)) + else { - apr_hash_t *original_props; - - SVN_ERR(get_props(&original_props, child1_abspath, b->ctx->wc_ctx, - scratch_pool, scratch_pool)); - SVN_ERR(do_arbitrary_files_diff(b->empty_file_abspath, child2_abspath, - svn_relpath_join(child_relpath, name, - iterpool), - TRUE, FALSE, original_props, - b->callbacks, b->diff_baton, - b->ctx, iterpool)); + SVN_ERR(diff_processor->file_closed(relpath, + left_source, + right_source, + file_baton, + diff_processor, + scratch_pool)); } - - /* Files that exist in dirents1 and dirents2. */ - if (dirent1->kind == svn_node_file && dirent2->kind == svn_node_file) - SVN_ERR(do_arbitrary_files_diff(child1_abspath, child2_abspath, - svn_relpath_join(child_relpath, name, - iterpool), - FALSE, FALSE, NULL, - b->callbacks, b->diff_baton, - b->ctx, scratch_pool)); - - /* Directories that only exist in dirents2. These aren't crawled - * by this walker so we have to crawl them separately. */ - if (depth > svn_depth_files && - dirent2->kind == svn_node_dir && - (dirent1->kind == svn_node_file || dirent1->kind == svn_node_none)) - SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath, - b->root1_abspath, b->root2_abspath, - depth <= svn_depth_immediates - ? svn_depth_empty - : svn_depth_infinity , - b->callbacks, b->diff_baton, - b->ctx, iterpool)); } - - svn_pool_destroy(iterpool); - return SVN_NO_ERROR; } -/* An implementation of svn_io_walk_func_t. - * Note: LOCAL_ABSPATH is the path being crawled and can be on either side - * of the diff depending on baton->recursing_within_added_subtree. */ + +/* Handles reporting of a directory and its children for inner_dir_diff */ static svn_error_t * -arbitrary_diff_walker(void *baton, const char *local_abspath, - const apr_finfo_t *finfo, - apr_pool_t *scratch_pool) +do_dir_diff(const char *left_abspath, + const char *right_abspath, + const char *left_root_abspath, + const char *right_root_abspath, + svn_boolean_t left_only, + svn_boolean_t right_only, + svn_boolean_t left_before_right, + svn_depth_t depth, + void *parent_baton, + const svn_diff_tree_processor_t *diff_processor, + svn_client_ctx_t *ctx, + apr_pool_t *scratch_pool) { - struct arbitrary_diff_walker_baton *b = baton; + const char *relpath; + svn_diff_source_t *left_source; + svn_diff_source_t *right_source; + svn_boolean_t skip = FALSE; + svn_boolean_t skip_children = FALSE; + void *dir_baton; + apr_hash_t *left_props; + apr_hash_t *right_props; + + relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath); + + if (! right_only) + { + left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); + SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx, + scratch_pool, scratch_pool)); + } + else + { + left_source = NULL; + left_props = NULL; + } + + if (! left_only) + { + right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); + SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx, + scratch_pool, scratch_pool)); + } + else + { + right_source = NULL; + right_props = NULL; + } - if (b->ctx->cancel_func) - SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton)); + SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children, + relpath, + left_source, + right_source, + NULL /* copyfrom_source */, + parent_baton, + diff_processor, + scratch_pool, scratch_pool)); - if (finfo->filetype != APR_DIR) + if (!skip_children) + { + if (depth >= svn_depth_files) + SVN_ERR(inner_dir_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + left_only, right_only, + left_before_right, depth, + dir_baton, + diff_processor, ctx, scratch_pool)); + } + else if (skip) return SVN_NO_ERROR; - SVN_ERR(arbitrary_diff_this_dir(b, local_abspath, svn_depth_infinity, - scratch_pool)); + if (left_props && right_props) + { + apr_array_header_t *prop_diffs; + + SVN_ERR(svn_prop_diffs(&prop_diffs, right_props, left_props, + scratch_pool)); + + if (prop_diffs->nelts) + { + SVN_ERR(diff_processor->dir_changed(relpath, + left_source, + right_source, + left_props, + right_props, + prop_diffs, + dir_baton, + diff_processor, + scratch_pool)); + return SVN_NO_ERROR; + } + } + + if (left_source && right_source) + { + SVN_ERR(diff_processor->dir_closed(relpath, + left_source, + right_source, + dir_baton, + diff_processor, + scratch_pool)); + } + else if (left_source) + { + SVN_ERR(diff_processor->dir_deleted(relpath, + left_source, + left_props, + dir_baton, + diff_processor, + scratch_pool)); + } + else + { + SVN_ERR(diff_processor->dir_added(relpath, + NULL /* copyfrom_source */, + right_source, + NULL /* copyfrom_props */, + right_props, + dir_baton, + diff_processor, + scratch_pool)); + } return SVN_NO_ERROR; } svn_error_t * -svn_client__arbitrary_nodes_diff(const char *local_abspath1, - const char *local_abspath2, +svn_client__arbitrary_nodes_diff(const char **root_relpath, + svn_boolean_t *root_is_dir, + const char *left_abspath, + const char *right_abspath, svn_depth_t depth, - const svn_wc_diff_callbacks4_t *callbacks, - void *diff_baton, + const svn_diff_tree_processor_t *diff_processor, svn_client_ctx_t *ctx, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) { - svn_node_kind_t kind1; - svn_node_kind_t kind2; + svn_node_kind_t left_kind; + svn_node_kind_t right_kind; + const char *left_root_abspath; + const char *right_root_abspath; + svn_boolean_t left_before_right = TRUE; /* Future argument? */ - SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool)); - SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool)); + if (depth == svn_depth_unknown) + depth = svn_depth_infinity; - if (kind1 != kind2) - return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, - _("'%s' is not the same node kind as '%s'"), - svn_dirent_local_style(local_abspath1, - scratch_pool), - svn_dirent_local_style(local_abspath2, - scratch_pool)); + SVN_ERR(svn_io_check_resolved_path(left_abspath, &left_kind, scratch_pool)); + SVN_ERR(svn_io_check_resolved_path(right_abspath, &right_kind, scratch_pool)); if (depth == svn_depth_unknown) depth = svn_depth_infinity; - if (kind1 == svn_node_file) - SVN_ERR(do_arbitrary_files_diff(local_abspath1, local_abspath2, - svn_dirent_basename(local_abspath1, - scratch_pool), - FALSE, FALSE, NULL, - callbacks, diff_baton, - ctx, scratch_pool)); - else if (kind1 == svn_node_dir) - SVN_ERR(do_arbitrary_dirs_diff(local_abspath1, local_abspath2, - NULL, NULL, depth, - callbacks, diff_baton, - ctx, scratch_pool)); + if (left_kind == svn_node_dir && right_kind == svn_node_dir) + { + left_root_abspath = left_abspath; + right_root_abspath = right_abspath; + + if (root_relpath) + *root_relpath = ""; + if (root_is_dir) + *root_is_dir = TRUE; + } + else + { + svn_dirent_split(&left_root_abspath, root_relpath, left_abspath, + scratch_pool); + right_root_abspath = svn_dirent_dirname(right_abspath, scratch_pool); + + if (root_relpath) + *root_relpath = apr_pstrdup(result_pool, *root_relpath); + if (root_is_dir) + *root_is_dir = FALSE; + } + + if (left_kind == svn_node_dir && right_kind == svn_node_dir) + { + SVN_ERR(do_dir_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + FALSE, FALSE, left_before_right, + depth, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + } + else if (left_kind == svn_node_file && right_kind == svn_node_file) + { + SVN_ERR(do_file_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + FALSE, FALSE, + NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + } + else if (left_kind == svn_node_file || left_kind == svn_node_dir + || right_kind == svn_node_file || right_kind == svn_node_dir) + { + void *dir_baton; + svn_boolean_t skip = FALSE; + svn_boolean_t skip_children = FALSE; + svn_diff_source_t *left_src; + svn_diff_source_t *right_src; + + left_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); + right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool); + + /* The root is replaced... */ + /* Report delete and/or add */ + + SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children, "", + left_src, + right_src, + NULL /* copyfrom_src */, + NULL, + diff_processor, + scratch_pool, scratch_pool)); + + if (skip) + return SVN_NO_ERROR; + else if (!skip_children) + { + if (left_before_right) + { + if (left_kind == svn_node_file) + SVN_ERR(do_file_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + else if (left_kind == svn_node_dir) + SVN_ERR(do_dir_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, left_before_right, + depth, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + } + + if (right_kind == svn_node_file) + SVN_ERR(do_file_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + FALSE, TRUE, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + else if (right_kind == svn_node_dir) + SVN_ERR(do_dir_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + FALSE, TRUE, left_before_right, + depth, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + + if (! left_before_right) + { + if (left_kind == svn_node_file) + SVN_ERR(do_file_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + else if (left_kind == svn_node_dir) + SVN_ERR(do_dir_diff(left_abspath, right_abspath, + left_root_abspath, right_root_abspath, + TRUE, FALSE, left_before_right, + depth, NULL /* parent_baton */, + diff_processor, ctx, scratch_pool)); + } + } + + SVN_ERR(diff_processor->dir_closed("", + left_src, + right_src, + dir_baton, + diff_processor, + scratch_pool)); + } else return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, _("'%s' is not a file or directory"), - kind1 == svn_node_none - ? svn_dirent_local_style(local_abspath1, - scratch_pool) - : svn_dirent_local_style(local_abspath2, - scratch_pool)); + svn_dirent_local_style( + (left_kind == svn_node_none) + ? left_abspath + : right_abspath, + scratch_pool)); + return SVN_NO_ERROR; } |