summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/diff_local.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/diff_local.c')
-rw-r--r--subversion/libsvn_client/diff_local.c1077
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;
}