summaryrefslogtreecommitdiff
path: root/subversion/libsvn_diff/diff_tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_diff/diff_tree.c')
-rw-r--r--subversion/libsvn_diff/diff_tree.c1705
1 files changed, 1705 insertions, 0 deletions
diff --git a/subversion/libsvn_diff/diff_tree.c b/subversion/libsvn_diff/diff_tree.c
new file mode 100644
index 0000000..8490179
--- /dev/null
+++ b/subversion/libsvn_diff/diff_tree.c
@@ -0,0 +1,1705 @@
+/*
+ * diff_tree.c : default diff tree processor
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include <apr.h>
+#include <apr_pools.h>
+#include <apr_general.h>
+
+#include <assert.h>
+
+#include "svn_dirent_uri.h"
+#include "svn_error.h"
+#include "svn_io.h"
+#include "svn_pools.h"
+#include "svn_props.h"
+#include "svn_types.h"
+
+#include "private/svn_diff_tree.h"
+#include "svn_private_config.h"
+
+typedef struct tree_processor_t
+{
+ svn_diff_tree_processor_t tp;
+
+ /* void *future_extension */
+} tree_processor_t;
+
+
+static svn_error_t *
+default_dir_opened(void **new_dir_baton,
+ svn_boolean_t *skip,
+ svn_boolean_t *skip_children,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *parent_dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ *new_dir_baton = NULL;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+default_dir_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(processor->dir_closed(relpath, NULL, right_source,
+ dir_baton, processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+default_dir_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ /*const*/ apr_hash_t *left_props,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(processor->dir_closed(relpath, left_source, NULL,
+ dir_baton, processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+default_dir_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ const apr_array_header_t *prop_changes,
+ void *dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(processor->dir_closed(relpath,
+ left_source, right_source,
+ dir_baton,
+ processor, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+default_dir_closed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+default_file_opened(void **new_file_baton,
+ svn_boolean_t *skip,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ *new_file_baton = dir_baton;
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+default_file_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ const char *copyfrom_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(processor->file_closed(relpath,
+ NULL, right_source,
+ file_baton, processor, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+default_file_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const char *left_file,
+ /*const*/ apr_hash_t *left_props,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(processor->file_closed(relpath,
+ left_source, NULL,
+ file_baton, processor, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+default_file_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const char *left_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ svn_boolean_t file_modified,
+ const apr_array_header_t *prop_changes,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(processor->file_closed(relpath,
+ left_source, right_source,
+ file_baton, processor, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+default_file_closed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+default_node_absent(const char *relpath,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ return SVN_NO_ERROR;
+}
+
+svn_diff_tree_processor_t *
+svn_diff__tree_processor_create(void *baton,
+ apr_pool_t *result_pool)
+{
+ tree_processor_t *wrapper;
+ wrapper = apr_pcalloc(result_pool, sizeof(*wrapper));
+
+ wrapper->tp.baton = baton;
+
+ wrapper->tp.dir_opened = default_dir_opened;
+ wrapper->tp.dir_added = default_dir_added;
+ wrapper->tp.dir_deleted = default_dir_deleted;
+ wrapper->tp.dir_changed = default_dir_changed;
+ wrapper->tp.dir_closed = default_dir_closed;
+
+ wrapper->tp.file_opened = default_file_opened;
+ wrapper->tp.file_added = default_file_added;
+ wrapper->tp.file_deleted = default_file_deleted;
+ wrapper->tp.file_changed = default_file_changed;
+ wrapper->tp.file_closed = default_file_closed;
+
+ wrapper->tp.node_absent = default_node_absent;
+
+
+ return &wrapper->tp;
+}
+
+struct reverse_tree_baton_t
+{
+ const svn_diff_tree_processor_t *processor;
+ const char *prefix_relpath;
+};
+
+static svn_error_t *
+reverse_dir_opened(void **new_dir_baton,
+ svn_boolean_t *skip,
+ svn_boolean_t *skip_children,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *parent_dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct reverse_tree_baton_t *rb = processor->baton;
+
+ if (rb->prefix_relpath)
+ relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool);
+
+ SVN_ERR(rb->processor->dir_opened(new_dir_baton, skip, skip_children,
+ relpath,
+ right_source, left_source,
+ NULL /* copyfrom */,
+ parent_dir_baton,
+ rb->processor,
+ result_pool, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reverse_dir_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct reverse_tree_baton_t *rb = processor->baton;
+
+ if (rb->prefix_relpath)
+ relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool);
+
+ SVN_ERR(rb->processor->dir_deleted(relpath,
+ right_source,
+ right_props,
+ dir_baton,
+ rb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reverse_dir_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ /*const*/ apr_hash_t *left_props,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct reverse_tree_baton_t *rb = processor->baton;
+
+ if (rb->prefix_relpath)
+ relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool);
+
+ SVN_ERR(rb->processor->dir_added(relpath,
+ NULL,
+ left_source,
+ NULL,
+ left_props,
+ dir_baton,
+ rb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reverse_dir_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ const apr_array_header_t *prop_changes,
+ void *dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct reverse_tree_baton_t *rb = processor->baton;
+ apr_array_header_t *reversed_prop_changes = NULL;
+
+ if (rb->prefix_relpath)
+ relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool);
+
+ if (prop_changes)
+ {
+ SVN_ERR_ASSERT(left_props != NULL && right_props != NULL);
+ SVN_ERR(svn_prop_diffs(&reversed_prop_changes, left_props, right_props,
+ scratch_pool));
+ }
+
+ SVN_ERR(rb->processor->dir_changed(relpath,
+ right_source,
+ left_source,
+ right_props,
+ left_props,
+ reversed_prop_changes,
+ dir_baton,
+ rb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reverse_dir_closed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct reverse_tree_baton_t *rb = processor->baton;
+
+ if (rb->prefix_relpath)
+ relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool);
+
+ SVN_ERR(rb->processor->dir_closed(relpath,
+ right_source,
+ left_source,
+ dir_baton,
+ rb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reverse_file_opened(void **new_file_baton,
+ svn_boolean_t *skip,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct reverse_tree_baton_t *rb = processor->baton;
+
+ if (rb->prefix_relpath)
+ relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool);
+
+ SVN_ERR(rb->processor->file_opened(new_file_baton,
+ skip,
+ relpath,
+ right_source,
+ left_source,
+ NULL /* copy_from */,
+ dir_baton,
+ rb->processor,
+ result_pool,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reverse_file_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ const char *copyfrom_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct reverse_tree_baton_t *rb = processor->baton;
+
+ if (rb->prefix_relpath)
+ relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool);
+
+ SVN_ERR(rb->processor->file_deleted(relpath,
+ right_source,
+ right_file,
+ right_props,
+ file_baton,
+ rb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reverse_file_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const char *left_file,
+ /*const*/ apr_hash_t *left_props,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct reverse_tree_baton_t *rb = processor->baton;
+
+ if (rb->prefix_relpath)
+ relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool);
+
+ SVN_ERR(rb->processor->file_added(relpath,
+ NULL /* copyfrom src */,
+ left_source,
+ NULL /* copyfrom file */,
+ left_file,
+ NULL /* copyfrom props */,
+ left_props,
+ file_baton,
+ rb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reverse_file_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const char *left_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ svn_boolean_t file_modified,
+ const apr_array_header_t *prop_changes,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct reverse_tree_baton_t *rb = processor->baton;
+ apr_array_header_t *reversed_prop_changes = NULL;
+
+ if (rb->prefix_relpath)
+ relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool);
+
+ if (prop_changes)
+ {
+ SVN_ERR_ASSERT(left_props != NULL && right_props != NULL);
+ SVN_ERR(svn_prop_diffs(&reversed_prop_changes, left_props, right_props,
+ scratch_pool));
+ }
+
+ SVN_ERR(rb->processor->file_changed(relpath,
+ right_source,
+ left_source,
+ right_file,
+ left_file,
+ right_props,
+ left_props,
+ file_modified,
+ reversed_prop_changes,
+ file_baton,
+ rb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reverse_file_closed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct reverse_tree_baton_t *rb = processor->baton;
+
+ if (rb->prefix_relpath)
+ relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool);
+
+ SVN_ERR(rb->processor->file_closed(relpath,
+ right_source,
+ left_source,
+ file_baton,
+ rb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+reverse_node_absent(const char *relpath,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct reverse_tree_baton_t *rb = processor->baton;
+
+ if (rb->prefix_relpath)
+ relpath = svn_relpath_join(rb->prefix_relpath, relpath, scratch_pool);
+
+ SVN_ERR(rb->processor->node_absent(relpath,
+ dir_baton,
+ rb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+
+const svn_diff_tree_processor_t *
+svn_diff__tree_processor_reverse_create(const svn_diff_tree_processor_t * processor,
+ const char *prefix_relpath,
+ apr_pool_t *result_pool)
+{
+ struct reverse_tree_baton_t *rb;
+ svn_diff_tree_processor_t *reverse;
+
+ rb = apr_pcalloc(result_pool, sizeof(*rb));
+ rb->processor = processor;
+ if (prefix_relpath)
+ rb->prefix_relpath = apr_pstrdup(result_pool, prefix_relpath);
+
+ reverse = svn_diff__tree_processor_create(rb, result_pool);
+
+ reverse->dir_opened = reverse_dir_opened;
+ reverse->dir_added = reverse_dir_added;
+ reverse->dir_deleted = reverse_dir_deleted;
+ reverse->dir_changed = reverse_dir_changed;
+ reverse->dir_closed = reverse_dir_closed;
+
+ reverse->file_opened = reverse_file_opened;
+ reverse->file_added = reverse_file_added;
+ reverse->file_deleted = reverse_file_deleted;
+ reverse->file_changed = reverse_file_changed;
+ reverse->file_closed = reverse_file_closed;
+
+ reverse->node_absent = reverse_node_absent;
+
+ return reverse;
+}
+
+struct filter_tree_baton_t
+{
+ const svn_diff_tree_processor_t *processor;
+ const char *prefix_relpath;
+};
+
+static svn_error_t *
+filter_dir_opened(void **new_dir_baton,
+ svn_boolean_t *skip,
+ svn_boolean_t *skip_children,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *parent_dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
+
+ if (! relpath)
+ {
+ /* Skip work for this, but NOT for DESCENDANTS */
+ *skip = TRUE;
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(fb->processor->dir_opened(new_dir_baton, skip, skip_children,
+ relpath,
+ left_source, right_source,
+ copyfrom_source,
+ parent_dir_baton,
+ fb->processor,
+ result_pool, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_dir_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
+ assert(relpath != NULL); /* Driver error */
+
+ SVN_ERR(fb->processor->dir_added(relpath,
+ copyfrom_source,
+ right_source,
+ copyfrom_props,
+ right_props,
+ dir_baton,
+ fb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_dir_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ /*const*/ apr_hash_t *left_props,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
+ assert(relpath != NULL); /* Driver error */
+
+ SVN_ERR(fb->processor->dir_deleted(relpath,
+ left_source,
+ left_props,
+ dir_baton,
+ fb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_dir_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ const apr_array_header_t *prop_changes,
+ void *dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
+ assert(relpath != NULL); /* Driver error */
+
+ SVN_ERR(fb->processor->dir_changed(relpath,
+ left_source,
+ right_source,
+ left_props,
+ right_props,
+ prop_changes,
+ dir_baton,
+ fb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_dir_closed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
+ assert(relpath != NULL); /* Driver error */
+
+ SVN_ERR(fb->processor->dir_closed(relpath,
+ left_source,
+ right_source,
+ dir_baton,
+ fb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_opened(void **new_file_baton,
+ svn_boolean_t *skip,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
+
+ if (! relpath)
+ {
+ *skip = TRUE;
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(fb->processor->file_opened(new_file_baton,
+ skip,
+ relpath,
+ left_source,
+ right_source,
+ copyfrom_source,
+ dir_baton,
+ fb->processor,
+ result_pool,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ const char *copyfrom_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
+ assert(relpath != NULL); /* Driver error */
+
+ SVN_ERR(fb->processor->file_added(relpath,
+ copyfrom_source,
+ right_source,
+ copyfrom_file,
+ right_file,
+ copyfrom_props,
+ right_props,
+ file_baton,
+ fb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const char *left_file,
+ /*const*/ apr_hash_t *left_props,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
+ assert(relpath != NULL); /* Driver error */
+
+ SVN_ERR(fb->processor->file_deleted(relpath,
+ left_source,
+ left_file,
+ left_props,
+ file_baton,
+ fb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const char *left_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ svn_boolean_t file_modified,
+ const apr_array_header_t *prop_changes,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
+ assert(relpath != NULL); /* Driver error */
+
+ SVN_ERR(fb->processor->file_changed(relpath,
+ left_source,
+ right_source,
+ left_file,
+ right_file,
+ left_props,
+ right_props,
+ file_modified,
+ prop_changes,
+ file_baton,
+ fb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_file_closed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
+ assert(relpath != NULL); /* Driver error */
+
+ SVN_ERR(fb->processor->file_closed(relpath,
+ left_source,
+ right_source,
+ file_baton,
+ fb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+filter_node_absent(const char *relpath,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct filter_tree_baton_t *fb = processor->baton;
+
+ relpath = svn_relpath_skip_ancestor(fb->prefix_relpath, relpath);
+ assert(relpath != NULL); /* Driver error */
+
+ SVN_ERR(fb->processor->node_absent(relpath,
+ dir_baton,
+ fb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+
+const svn_diff_tree_processor_t *
+svn_diff__tree_processor_filter_create(const svn_diff_tree_processor_t * processor,
+ const char *prefix_relpath,
+ apr_pool_t *result_pool)
+{
+ struct filter_tree_baton_t *fb;
+ svn_diff_tree_processor_t *filter;
+
+ fb = apr_pcalloc(result_pool, sizeof(*fb));
+ fb->processor = processor;
+ if (prefix_relpath)
+ fb->prefix_relpath = apr_pstrdup(result_pool, prefix_relpath);
+
+ filter = svn_diff__tree_processor_create(fb, result_pool);
+
+ filter->dir_opened = filter_dir_opened;
+ filter->dir_added = filter_dir_added;
+ filter->dir_deleted = filter_dir_deleted;
+ filter->dir_changed = filter_dir_changed;
+ filter->dir_closed = filter_dir_closed;
+
+ filter->file_opened = filter_file_opened;
+ filter->file_added = filter_file_added;
+ filter->file_deleted = filter_file_deleted;
+ filter->file_changed = filter_file_changed;
+ filter->file_closed = filter_file_closed;
+
+ filter->node_absent = filter_node_absent;
+
+ return filter;
+}
+
+struct copy_as_changed_baton_t
+{
+ const svn_diff_tree_processor_t *processor;
+};
+
+static svn_error_t *
+copy_as_changed_dir_opened(void **new_dir_baton,
+ svn_boolean_t *skip,
+ svn_boolean_t *skip_children,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *parent_dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct copy_as_changed_baton_t *cb = processor->baton;
+
+ if (!left_source && copyfrom_source)
+ {
+ assert(right_source != NULL);
+
+ left_source = copyfrom_source;
+ copyfrom_source = NULL;
+ }
+
+ SVN_ERR(cb->processor->dir_opened(new_dir_baton, skip, skip_children,
+ relpath,
+ left_source, right_source,
+ copyfrom_source,
+ parent_dir_baton,
+ cb->processor,
+ result_pool, scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+copy_as_changed_dir_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct copy_as_changed_baton_t *cb = processor->baton;
+
+ if (copyfrom_source)
+ {
+ apr_array_header_t *propchanges;
+ SVN_ERR(svn_prop_diffs(&propchanges, right_props, copyfrom_props,
+ scratch_pool));
+ SVN_ERR(cb->processor->dir_changed(relpath,
+ copyfrom_source,
+ right_source,
+ copyfrom_props,
+ right_props,
+ propchanges,
+ dir_baton,
+ cb->processor,
+ scratch_pool));
+ }
+ else
+ {
+ SVN_ERR(cb->processor->dir_added(relpath,
+ copyfrom_source,
+ right_source,
+ copyfrom_props,
+ right_props,
+ dir_baton,
+ cb->processor,
+ scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+copy_as_changed_dir_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ /*const*/ apr_hash_t *left_props,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct copy_as_changed_baton_t *cb = processor->baton;
+
+ SVN_ERR(cb->processor->dir_deleted(relpath,
+ left_source,
+ left_props,
+ dir_baton,
+ cb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+copy_as_changed_dir_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ const apr_array_header_t *prop_changes,
+ void *dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct copy_as_changed_baton_t *cb = processor->baton;
+
+ SVN_ERR(cb->processor->dir_changed(relpath,
+ left_source,
+ right_source,
+ left_props,
+ right_props,
+ prop_changes,
+ dir_baton,
+ cb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+copy_as_changed_dir_closed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct copy_as_changed_baton_t *cb = processor->baton;
+
+ SVN_ERR(cb->processor->dir_closed(relpath,
+ left_source,
+ right_source,
+ dir_baton,
+ cb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+copy_as_changed_file_opened(void **new_file_baton,
+ svn_boolean_t *skip,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct copy_as_changed_baton_t *cb = processor->baton;
+
+ if (!left_source && copyfrom_source)
+ {
+ assert(right_source != NULL);
+
+ left_source = copyfrom_source;
+ copyfrom_source = NULL;
+ }
+
+ SVN_ERR(cb->processor->file_opened(new_file_baton,
+ skip,
+ relpath,
+ left_source,
+ right_source,
+ copyfrom_source,
+ dir_baton,
+ cb->processor,
+ result_pool,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+copy_as_changed_file_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ const char *copyfrom_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct copy_as_changed_baton_t *cb = processor->baton;
+
+ if (copyfrom_source)
+ {
+ apr_array_header_t *propchanges;
+ svn_boolean_t same;
+ SVN_ERR(svn_prop_diffs(&propchanges, right_props, copyfrom_props,
+ scratch_pool));
+
+ /* "" is sometimes a marker for just modified (E.g. no-textdeltas),
+ and it is certainly not a file */
+ if (*copyfrom_file && *right_file)
+ {
+ SVN_ERR(svn_io_files_contents_same_p(&same, copyfrom_file,
+ right_file, scratch_pool));
+ }
+ else
+ same = FALSE;
+
+ SVN_ERR(cb->processor->file_changed(relpath,
+ copyfrom_source,
+ right_source,
+ copyfrom_file,
+ right_file,
+ copyfrom_props,
+ right_props,
+ !same,
+ propchanges,
+ file_baton,
+ cb->processor,
+ scratch_pool));
+ }
+ else
+ {
+ SVN_ERR(cb->processor->file_added(relpath,
+ copyfrom_source,
+ right_source,
+ copyfrom_file,
+ right_file,
+ copyfrom_props,
+ right_props,
+ file_baton,
+ cb->processor,
+ scratch_pool));
+ }
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+copy_as_changed_file_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const char *left_file,
+ /*const*/ apr_hash_t *left_props,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct copy_as_changed_baton_t *cb = processor->baton;
+
+ SVN_ERR(cb->processor->file_deleted(relpath,
+ left_source,
+ left_file,
+ left_props,
+ file_baton,
+ cb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+copy_as_changed_file_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const char *left_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ svn_boolean_t file_modified,
+ const apr_array_header_t *prop_changes,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct copy_as_changed_baton_t *cb = processor->baton;
+
+ SVN_ERR(cb->processor->file_changed(relpath,
+ left_source,
+ right_source,
+ left_file,
+ right_file,
+ left_props,
+ right_props,
+ file_modified,
+ prop_changes,
+ file_baton,
+ cb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+copy_as_changed_file_closed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct copy_as_changed_baton_t *cb = processor->baton;
+
+ SVN_ERR(cb->processor->file_closed(relpath,
+ left_source,
+ right_source,
+ file_baton,
+ cb->processor,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+copy_as_changed_node_absent(const char *relpath,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct copy_as_changed_baton_t *cb = processor->baton;
+
+ SVN_ERR(cb->processor->node_absent(relpath,
+ dir_baton,
+ cb->processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+
+const svn_diff_tree_processor_t *
+svn_diff__tree_processor_copy_as_changed_create(
+ const svn_diff_tree_processor_t * processor,
+ apr_pool_t *result_pool)
+{
+ struct copy_as_changed_baton_t *cb;
+ svn_diff_tree_processor_t *filter;
+
+ cb = apr_pcalloc(result_pool, sizeof(*cb));
+ cb->processor = processor;
+
+ filter = svn_diff__tree_processor_create(cb, result_pool);
+ filter->dir_opened = copy_as_changed_dir_opened;
+ filter->dir_added = copy_as_changed_dir_added;
+ filter->dir_deleted = copy_as_changed_dir_deleted;
+ filter->dir_changed = copy_as_changed_dir_changed;
+ filter->dir_closed = copy_as_changed_dir_closed;
+
+ filter->file_opened = copy_as_changed_file_opened;
+ filter->file_added = copy_as_changed_file_added;
+ filter->file_deleted = copy_as_changed_file_deleted;
+ filter->file_changed = copy_as_changed_file_changed;
+ filter->file_closed = copy_as_changed_file_closed;
+
+ filter->node_absent = copy_as_changed_node_absent;
+
+ return filter;
+}
+
+
+/* Processor baton for the tee tree processor */
+struct tee_baton_t
+{
+ const svn_diff_tree_processor_t *p1;
+ const svn_diff_tree_processor_t *p2;
+};
+
+/* Wrapper baton for file and directory batons in the tee processor */
+struct tee_node_baton_t
+{
+ void *baton1;
+ void *baton2;
+};
+
+static svn_error_t *
+tee_dir_opened(void **new_dir_baton,
+ svn_boolean_t *skip,
+ svn_boolean_t *skip_children,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *parent_dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct tee_baton_t *tb = processor->baton;
+ struct tee_node_baton_t *pb = parent_dir_baton;
+ struct tee_node_baton_t *nb = apr_pcalloc(result_pool, sizeof(*nb));
+
+ SVN_ERR(tb->p1->dir_opened(&(nb->baton1),
+ skip,
+ skip_children,
+ relpath,
+ left_source,
+ right_source,
+ copyfrom_source,
+ pb ? pb->baton1 : NULL,
+ tb->p1,
+ result_pool,
+ scratch_pool));
+
+ SVN_ERR(tb->p2->dir_opened(&(nb->baton2),
+ skip,
+ skip_children,
+ relpath,
+ left_source,
+ right_source,
+ copyfrom_source,
+ pb ? pb->baton2 : NULL,
+ tb->p2,
+ result_pool,
+ scratch_pool));
+
+ *new_dir_baton = nb;
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+tee_dir_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct tee_baton_t *tb = processor->baton;
+ struct tee_node_baton_t *db = dir_baton;
+
+ SVN_ERR(tb->p1->dir_added(relpath,
+ copyfrom_source,
+ right_source,
+ copyfrom_props,
+ right_props,
+ db->baton1,
+ tb->p1,
+ scratch_pool));
+
+ SVN_ERR(tb->p2->dir_added(relpath,
+ copyfrom_source,
+ right_source,
+ copyfrom_props,
+ right_props,
+ db->baton2,
+ tb->p2,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+tee_dir_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ /*const*/ apr_hash_t *left_props,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct tee_baton_t *tb = processor->baton;
+ struct tee_node_baton_t *db = dir_baton;
+
+ SVN_ERR(tb->p1->dir_deleted(relpath,
+ left_source,
+ left_props,
+ db->baton1,
+ tb->p1,
+ scratch_pool));
+
+ SVN_ERR(tb->p2->dir_deleted(relpath,
+ left_source,
+ left_props,
+ db->baton2,
+ tb->p2,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+tee_dir_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ const apr_array_header_t *prop_changes,
+ void *dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct tee_baton_t *tb = processor->baton;
+ struct tee_node_baton_t *db = dir_baton;
+
+ SVN_ERR(tb->p1->dir_changed(relpath,
+ left_source,
+ right_source,
+ left_props,
+ right_props,
+ prop_changes,
+ db->baton1,
+ tb->p1,
+ scratch_pool));
+
+ SVN_ERR(tb->p2->dir_changed(relpath,
+ left_source,
+ right_source,
+ left_props,
+ right_props,
+ prop_changes,
+ db->baton2,
+ tb->p2,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+tee_dir_closed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct tee_baton_t *tb = processor->baton;
+ struct tee_node_baton_t *db = dir_baton;
+
+ SVN_ERR(tb->p1->dir_closed(relpath,
+ left_source,
+ right_source,
+ db->baton1,
+ tb->p1,
+ scratch_pool));
+
+ SVN_ERR(tb->p2->dir_closed(relpath,
+ left_source,
+ right_source,
+ db->baton2,
+ tb->p2,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+tee_file_opened(void **new_file_baton,
+ svn_boolean_t *skip,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct tee_baton_t *tb = processor->baton;
+ struct tee_node_baton_t *pb = dir_baton;
+ struct tee_node_baton_t *nb = apr_pcalloc(result_pool, sizeof(*nb));
+
+ SVN_ERR(tb->p1->file_opened(&(nb->baton1),
+ skip,
+ relpath,
+ left_source,
+ right_source,
+ copyfrom_source,
+ pb ? pb->baton1 : NULL,
+ tb->p1,
+ result_pool,
+ scratch_pool));
+
+ SVN_ERR(tb->p2->file_opened(&(nb->baton2),
+ skip,
+ relpath,
+ left_source,
+ right_source,
+ copyfrom_source,
+ pb ? pb->baton2 : NULL,
+ tb->p2,
+ result_pool,
+ scratch_pool));
+
+ *new_file_baton = nb;
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+tee_file_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ const char *copyfrom_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct tee_baton_t *tb = processor->baton;
+ struct tee_node_baton_t *fb = file_baton;
+
+ SVN_ERR(tb->p1->file_added(relpath,
+ copyfrom_source,
+ right_source,
+ copyfrom_file,
+ right_file,
+ copyfrom_props,
+ right_props,
+ fb->baton1,
+ tb->p1,
+ scratch_pool));
+
+ SVN_ERR(tb->p2->file_added(relpath,
+ copyfrom_source,
+ right_source,
+ copyfrom_file,
+ right_file,
+ copyfrom_props,
+ right_props,
+ fb->baton2,
+ tb->p2,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+tee_file_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const char *left_file,
+ /*const*/ apr_hash_t *left_props,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct tee_baton_t *tb = processor->baton;
+ struct tee_node_baton_t *fb = file_baton;
+
+ SVN_ERR(tb->p1->file_deleted(relpath,
+ left_source,
+ left_file,
+ left_props,
+ fb->baton1,
+ tb->p1,
+ scratch_pool));
+
+ SVN_ERR(tb->p2->file_deleted(relpath,
+ left_source,
+ left_file,
+ left_props,
+ fb->baton2,
+ tb->p2,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+tee_file_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const char *left_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ svn_boolean_t file_modified,
+ const apr_array_header_t *prop_changes,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct tee_baton_t *tb = processor->baton;
+ struct tee_node_baton_t *fb = file_baton;
+
+ SVN_ERR(tb->p1->file_changed(relpath,
+ left_source,
+ right_source,
+ left_file,
+ right_file,
+ left_props,
+ right_props,
+ file_modified,
+ prop_changes,
+ fb->baton1,
+ tb->p1,
+ scratch_pool));
+
+ SVN_ERR(tb->p2->file_changed(relpath,
+ left_source,
+ right_source,
+ left_file,
+ right_file,
+ left_props,
+ right_props,
+ file_modified,
+ prop_changes,
+ fb->baton2,
+ tb->p2,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+tee_file_closed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ void *file_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct tee_baton_t *tb = processor->baton;
+ struct tee_node_baton_t *fb = file_baton;
+
+ SVN_ERR(tb->p1->file_closed(relpath,
+ left_source,
+ right_source,
+ fb->baton1,
+ tb->p1,
+ scratch_pool));
+
+ SVN_ERR(tb->p2->file_closed(relpath,
+ left_source,
+ right_source,
+ fb->baton2,
+ tb->p2,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+tee_node_absent(const char *relpath,
+ void *dir_baton,
+ const svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
+{
+ struct tee_baton_t *tb = processor->baton;
+ struct tee_node_baton_t *db = dir_baton;
+
+ SVN_ERR(tb->p1->node_absent(relpath,
+ db ? db->baton1 : NULL,
+ tb->p1,
+ scratch_pool));
+
+ SVN_ERR(tb->p2->node_absent(relpath,
+ db ? db->baton2 : NULL,
+ tb->p2,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+const svn_diff_tree_processor_t *
+svn_diff__tree_processor_tee_create(const svn_diff_tree_processor_t *processor1,
+ const svn_diff_tree_processor_t *processor2,
+ apr_pool_t *result_pool)
+{
+ struct tee_baton_t *tb = apr_pcalloc(result_pool, sizeof(*tb));
+ svn_diff_tree_processor_t *tee;
+ tb->p1 = processor1;
+ tb->p2 = processor2;
+
+ tee = svn_diff__tree_processor_create(tb, result_pool);
+
+ tee->dir_opened = tee_dir_opened;
+ tee->dir_added = tee_dir_added;
+ tee->dir_deleted = tee_dir_deleted;
+ tee->dir_changed = tee_dir_changed;
+ tee->dir_closed = tee_dir_closed;
+ tee->file_opened = tee_file_opened;
+ tee->file_added = tee_file_added;
+ tee->file_deleted = tee_file_deleted;
+ tee->file_changed = tee_file_changed;
+ tee->file_closed = tee_file_closed;
+ tee->node_absent = tee_node_absent;
+
+ return tee;
+}
+
+svn_diff_source_t *
+svn_diff__source_create(svn_revnum_t revision,
+ apr_pool_t *result_pool)
+{
+ svn_diff_source_t *src = apr_pcalloc(result_pool, sizeof(*src));
+
+ src->revision = revision;
+ return src;
+}