summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client/repos_diff.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client/repos_diff.c')
-rw-r--r--subversion/libsvn_client/repos_diff.c1313
1 files changed, 670 insertions, 643 deletions
diff --git a/subversion/libsvn_client/repos_diff.c b/subversion/libsvn_client/repos_diff.c
index c303af0..6a7725f 100644
--- a/subversion/libsvn_client/repos_diff.c
+++ b/subversion/libsvn_client/repos_diff.c
@@ -34,6 +34,7 @@
#include <apr_uri.h>
#include <apr_md5.h>
+#include <assert.h>
#include "svn_checksum.h"
#include "svn_hash.h"
@@ -47,28 +48,17 @@
#include "client.h"
+#include "private/svn_subr_private.h"
#include "private/svn_wc_private.h"
+#include "private/svn_editor.h"
/* Overall crawler editor baton. */
struct edit_baton {
- /* TARGET is a working-copy directory which corresponds to the base
- URL open in RA_SESSION below. */
- const char *target;
-
- /* A working copy context for TARGET, NULL if this is purely a
- repository operation. */
- svn_wc_context_t *wc_ctx;
-
/* The passed depth */
svn_depth_t depth;
- /* The callback and calback argument that implement the file comparison
- function */
- const svn_wc_diff_callbacks4_t *diff_callbacks;
- void *diff_cmd_baton;
-
- /* DRY_RUN is TRUE if this is a dry-run diff, false otherwise. */
- svn_boolean_t dry_run;
+ /* The result processor */
+ const svn_diff_tree_processor_t *processor;
/* RA_SESSION is the open session for making requests to the RA layer */
svn_ra_session_t *ra_session;
@@ -88,18 +78,8 @@ struct edit_baton {
/* Empty hash used for adds. */
apr_hash_t *empty_hash;
- /* Hash used to check replaced paths. Key is path relative CWD,
- * Value is *deleted_path_notify_t.
- * All allocations are from edit_baton's pool. */
- apr_hash_t *deleted_paths;
-
- /* If the func is non-null, send notifications of actions. */
- svn_wc_notify_func2_t notify_func;
- void *notify_baton;
-
- /* TRUE if the operation needs to walk deleted dirs on the "old" side.
- FALSE otherwise. */
- svn_boolean_t walk_deleted_repos_dirs;
+ /* Whether to report text deltas */
+ svn_boolean_t text_deltas;
/* A callback used to see if the client wishes to cancel the running
operation. */
@@ -140,12 +120,9 @@ struct dir_baton {
/* The path of the directory within the repository */
const char *path;
- /* The path of the directory in the wc, relative to cwd */
- const char *wcpath;
-
/* The baton for the parent directory, or null if this is the root of the
hierarchy to be compared. */
- struct dir_baton *dir_baton;
+ struct dir_baton *parent_baton;
/* The overall crawler editor baton. */
struct edit_baton *edit_baton;
@@ -153,17 +130,31 @@ struct dir_baton {
/* A cache of any property changes (svn_prop_t) received for this dir. */
apr_array_header_t *propchanges;
- /* The pristine-property list attached to this directory. */
- apr_hash_t *pristine_props;
+ /* Boolean indicating whether a node property was changed */
+ svn_boolean_t has_propchange;
+
+ /* Baton for svn_diff_tree_processor_t */
+ void *pdb;
+ svn_diff_source_t *left_source;
+ svn_diff_source_t *right_source;
/* The pool passed in by add_dir, open_dir, or open_root.
Also, the pool this dir baton is allocated in. */
apr_pool_t *pool;
+
+ /* Base revision of directory. */
+ svn_revnum_t base_revision;
+
+ /* Number of users of baton. Its pool will be destroyed 0 */
+ int users;
};
/* File level baton.
*/
struct file_baton {
+ /* Reference to parent baton */
+ struct dir_baton *parent_baton;
+
/* Gets set if the file is added rather than replaced. */
svn_boolean_t added;
@@ -179,9 +170,6 @@ struct file_baton {
/* The path of the file within the repository */
const char *path;
- /* The path of the file in the wc, relative to cwd */
- const char *wcpath;
-
/* The path and APR file handle to the temporary file that contains the
first repository version. Also, the pristine-property list of
this file. */
@@ -212,6 +200,14 @@ struct file_baton {
/* A cache of any property changes (svn_prop_t) received for this file. */
apr_array_header_t *propchanges;
+ /* Boolean indicating whether a node property was changed */
+ svn_boolean_t has_propchange;
+
+ /* Baton for svn_diff_tree_processor_t */
+ void *pfb;
+ svn_diff_source_t *left_source;
+ svn_diff_source_t *right_source;
+
/* The pool passed in by add_file or open_file.
Also, the pool this file_baton is allocated in. */
apr_pool_t *pool;
@@ -229,12 +225,13 @@ make_dir_baton(const char *path,
struct dir_baton *parent_baton,
struct edit_baton *edit_baton,
svn_boolean_t added,
- apr_pool_t *pool)
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool)
{
- apr_pool_t *dir_pool = svn_pool_create(pool);
+ apr_pool_t *dir_pool = svn_pool_create(result_pool);
struct dir_baton *dir_baton = apr_pcalloc(dir_pool, sizeof(*dir_baton));
- dir_baton->dir_baton = parent_baton;
+ dir_baton->parent_baton = parent_baton;
dir_baton->edit_baton = edit_baton;
dir_baton->added = added;
dir_baton->tree_conflicted = FALSE;
@@ -242,12 +239,38 @@ make_dir_baton(const char *path,
dir_baton->skip_children = FALSE;
dir_baton->pool = dir_pool;
dir_baton->path = apr_pstrdup(dir_pool, path);
- dir_baton->wcpath = svn_dirent_join(edit_baton->target, path, dir_pool);
- dir_baton->propchanges = apr_array_make(pool, 1, sizeof(svn_prop_t));
+ dir_baton->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t));
+ dir_baton->base_revision = base_revision;
+ dir_baton->users++;
+
+ if (parent_baton)
+ parent_baton->users++;
return dir_baton;
}
+/* New function. Called by everyone who has a reference when done */
+static svn_error_t *
+release_dir(struct dir_baton *db)
+{
+ assert(db->users > 0);
+
+ db->users--;
+ if (db->users)
+ return SVN_NO_ERROR;
+
+ {
+ struct dir_baton *pb = db->parent_baton;
+
+ svn_pool_destroy(db->pool);
+
+ if (pb != NULL)
+ SVN_ERR(release_dir(pb));
+ }
+
+ return SVN_NO_ERROR;
+}
+
/* Create a new file baton for PATH in POOL, which is a child of
* directory PARENT_PATH. ADDED is set if this file is being added
* rather than replaced. EDIT_BATON is a pointer to the global edit
@@ -255,77 +278,44 @@ make_dir_baton(const char *path,
*/
static struct file_baton *
make_file_baton(const char *path,
+ struct dir_baton *parent_baton,
svn_boolean_t added,
- struct edit_baton *edit_baton,
- apr_pool_t *pool)
+ apr_pool_t *result_pool)
{
- apr_pool_t *file_pool = svn_pool_create(pool);
+ apr_pool_t *file_pool = svn_pool_create(result_pool);
struct file_baton *file_baton = apr_pcalloc(file_pool, sizeof(*file_baton));
- file_baton->edit_baton = edit_baton;
+ file_baton->parent_baton = parent_baton;
+ file_baton->edit_baton = parent_baton->edit_baton;
file_baton->added = added;
file_baton->tree_conflicted = FALSE;
file_baton->skip = FALSE;
file_baton->pool = file_pool;
file_baton->path = apr_pstrdup(file_pool, path);
- file_baton->wcpath = svn_dirent_join(edit_baton->target, path, file_pool);
- file_baton->propchanges = apr_array_make(pool, 1, sizeof(svn_prop_t));
- file_baton->base_revision = edit_baton->revision;
+ file_baton->propchanges = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
+ file_baton->base_revision = parent_baton->edit_baton->revision;
- return file_baton;
-}
+ parent_baton->users++;
-/* Helper function: return up to two svn:mime-type values buried
- * within a file baton. Set *MIMETYPE1 to the value within the file's
- * pristine properties, or NULL if not available. Set *MIMETYPE2 to
- * the value within the "new" file's propchanges, or NULL if not
- * available.
- */
-static void
-get_file_mime_types(const char **mimetype1,
- const char **mimetype2,
- struct file_baton *b)
-{
- /* Defaults */
- *mimetype1 = NULL;
- *mimetype2 = NULL;
-
- if (b->pristine_props)
- {
- svn_string_t *pristine_val;
- pristine_val = apr_hash_get(b->pristine_props, SVN_PROP_MIME_TYPE,
- strlen(SVN_PROP_MIME_TYPE));
- if (pristine_val)
- *mimetype2 = *mimetype1 = pristine_val->data;
- }
-
- if (b->propchanges)
- {
- int i;
- svn_prop_t *propchange;
-
- for (i = 0; i < b->propchanges->nelts; i++)
- {
- propchange = &APR_ARRAY_IDX(b->propchanges, i, svn_prop_t);
- if (strcmp(propchange->name, SVN_PROP_MIME_TYPE) == 0)
- {
- if (propchange->value)
- *mimetype2 = propchange->value->data;
- break;
- }
- }
- }
+ return file_baton;
}
-
-/* Get revision REVISION of the file described by B from the repository.
- * Set B->path_start_revision to the path of a new temporary file containing
- * the file's text. Set B->pristine_props to a new hash containing the
- * file's properties. Install a pool cleanup handler on B->pool to delete
- * the file.
+/* Get revision FB->base_revision of the file described by FB from the
+ * repository, through FB->edit_baton->ra_session.
+ *
+ * Unless PROPS_ONLY is true:
+ * Set FB->path_start_revision to the path of a new temporary file containing
+ * the file's text.
+ * Set FB->start_md5_checksum to that file's MD-5 checksum.
+ * Install a pool cleanup handler on FB->pool to delete the file.
+ *
+ * Always:
+ * Set FB->pristine_props to a new hash containing the file's properties.
+ *
+ * Allocate all results in FB->pool.
*/
static svn_error_t *
-get_file_from_ra(struct file_baton *b,
+get_file_from_ra(struct file_baton *fb,
svn_boolean_t props_only,
apr_pool_t *scratch_pool)
{
@@ -333,44 +323,47 @@ get_file_from_ra(struct file_baton *b,
{
svn_stream_t *fstream;
- SVN_ERR(svn_stream_open_unique(&fstream, &(b->path_start_revision), NULL,
- svn_io_file_del_on_pool_cleanup, b->pool,
- b->pool));
+ SVN_ERR(svn_stream_open_unique(&fstream, &(fb->path_start_revision),
+ NULL, svn_io_file_del_on_pool_cleanup,
+ fb->pool, scratch_pool));
- fstream = svn_stream_checksummed2(fstream, NULL, &b->start_md5_checksum,
- svn_checksum_md5, TRUE, b->pool);
+ fstream = svn_stream_checksummed2(fstream, NULL, &fb->start_md5_checksum,
+ svn_checksum_md5, TRUE, scratch_pool);
/* Retrieve the file and its properties */
- SVN_ERR(svn_ra_get_file(b->edit_baton->ra_session,
- b->path,
- b->base_revision,
+ SVN_ERR(svn_ra_get_file(fb->edit_baton->ra_session,
+ fb->path,
+ fb->base_revision,
fstream, NULL,
- &(b->pristine_props),
- b->pool));
+ &(fb->pristine_props),
+ fb->pool));
SVN_ERR(svn_stream_close(fstream));
}
else
{
- SVN_ERR(svn_ra_get_file(b->edit_baton->ra_session,
- b->path,
- b->base_revision,
+ SVN_ERR(svn_ra_get_file(fb->edit_baton->ra_session,
+ fb->path,
+ fb->base_revision,
NULL, NULL,
- &(b->pristine_props),
- b->pool));
+ &(fb->pristine_props),
+ fb->pool));
}
return SVN_NO_ERROR;
}
-/* Issue #3657 'dav update report handler in skelta mode can cause
- spurious conflicts'. When communicating with the repository via ra_serf
- and ra_neon, the change_dir_prop and change_file_prop svn_delta_editor_t
+/* Remove every no-op property change from CHANGES: that is, remove every
+ entry in which the target value is the same as the value of the
+ corresponding property in PRISTINE_PROPS.
+
+ Issue #3657 'dav update report handler in skelta mode can cause
+ spurious conflicts'. When communicating with the repository via ra_serf,
+ the change_dir_prop and change_file_prop svn_delta_editor_t
callbacks are called (obviously) when a directory or file property has
changed between the start and end of the edit. Less obvious however,
is that these callbacks may be made describing *all* of the properties
on FILE_BATON->PATH when using the DAV providers, not just the change(s).
- (Specifically ra_neon does this for diff/merge and ra_serf does it
- for diff/merge/update/switch).
+ (Specifically ra_serf does it for diff/merge/update/switch).
This means that the change_[file|dir]_prop svn_delta_editor_t callbacks
may be made where there are no property changes (i.e. a noop change of
@@ -389,8 +382,6 @@ get_file_from_ra(struct file_baton *b,
See http://subversion.tigris.org/issues/show_bug.cgi?id=3657#desc9 and
http://svn.haxx.se/dev/archive-2010-08/0351.shtml for more details.
-
- This function filters these property changes from the change hash
*/
static void
remove_non_prop_changes(apr_hash_t *pristine_props,
@@ -404,9 +395,8 @@ remove_non_prop_changes(apr_hash_t *pristine_props,
if (change->value)
{
- const svn_string_t *old_val = apr_hash_get(pristine_props,
- change->name,
- APR_HASH_KEY_STRING);
+ const svn_string_t *old_val = svn_hash_gets(pristine_props,
+ change->name);
if (old_val && svn_string_compare(old_val, change->value))
{
@@ -424,18 +414,6 @@ remove_non_prop_changes(apr_hash_t *pristine_props,
}
}
-/* Get the props attached to a directory in the repository at BASE_REVISION. */
-static svn_error_t *
-get_dirprops_from_ra(struct dir_baton *b, svn_revnum_t base_revision)
-{
- return svn_ra_get_dir2(b->edit_baton->ra_session,
- NULL, NULL, &(b->pristine_props),
- b->path,
- base_revision,
- 0,
- b->pool);
-}
-
/* Get the empty file associated with the edit baton. This is cached so
* that it can be reused, all empty files are the same.
*/
@@ -456,7 +434,7 @@ get_empty_file(struct edit_baton *eb,
return SVN_NO_ERROR;
}
-/* An editor function. The root of the comparison hierarchy */
+/* An svn_delta_editor_t function. */
static svn_error_t *
set_target_revision(void *edit_baton,
svn_revnum_t target_revision,
@@ -468,7 +446,7 @@ set_target_revision(void *edit_baton,
return SVN_NO_ERROR;
}
-/* An editor function. The root of the comparison hierarchy */
+/* An svn_delta_editor_t function. The root of the comparison hierarchy */
static svn_error_t *
open_root(void *edit_baton,
svn_revnum_t base_revision,
@@ -476,101 +454,168 @@ open_root(void *edit_baton,
void **root_baton)
{
struct edit_baton *eb = edit_baton;
- struct dir_baton *b = make_dir_baton("", NULL, eb, FALSE, pool);
+ struct dir_baton *db = make_dir_baton("", NULL, eb, FALSE, base_revision,
+ eb->pool);
+
+ db->left_source = svn_diff__source_create(eb->revision, db->pool);
+ db->right_source = svn_diff__source_create(eb->target_revision, db->pool);
+
+ SVN_ERR(eb->processor->dir_opened(&db->pdb,
+ &db->skip,
+ &db->skip_children,
+ "",
+ db->left_source,
+ db->right_source,
+ NULL,
+ NULL,
+ eb->processor,
+ db->pool,
+ db->pool /* scratch_pool */));
+
+ *root_baton = db;
+ return SVN_NO_ERROR;
+}
+
+/* Compare a file being deleted against an empty file.
+ */
+static svn_error_t *
+diff_deleted_file(const char *path,
+ struct dir_baton *db,
+ apr_pool_t *scratch_pool)
+{
+ struct edit_baton *eb = db->edit_baton;
+ struct file_baton *fb = make_file_baton(path, db, FALSE, scratch_pool);
+ svn_boolean_t skip = FALSE;
+ svn_diff_source_t *left_source = svn_diff__source_create(eb->revision,
+ scratch_pool);
+
+ if (eb->cancel_func)
+ SVN_ERR(eb->cancel_func(eb->cancel_baton));
+
+ SVN_ERR(eb->processor->file_opened(&fb->pfb, &skip, path,
+ left_source,
+ NULL /* right_source */,
+ NULL /* copyfrom_source */,
+ db->pdb,
+ eb->processor,
+ scratch_pool, scratch_pool));
+
+ if (eb->cancel_func)
+ SVN_ERR(eb->cancel_func(eb->cancel_baton));
+
+ if (skip)
+ return SVN_NO_ERROR;
- /* Override the wcpath in our baton. */
- b->wcpath = apr_pstrdup(pool, eb->target);
+ SVN_ERR(get_file_from_ra(fb, ! eb->text_deltas, scratch_pool));
- SVN_ERR(get_dirprops_from_ra(b, base_revision));
+ SVN_ERR(eb->processor->file_deleted(fb->path,
+ left_source,
+ fb->path_start_revision,
+ fb->pristine_props,
+ fb->pfb,
+ eb->processor,
+ scratch_pool));
- *root_baton = b;
return SVN_NO_ERROR;
}
-/* Recursively walk tree rooted at DIR (at REVISION) in the repository,
- * reporting all files as deleted. Part of a workaround for issue 2333.
+/* Recursively walk tree rooted at DIR (at EB->revision) in the repository,
+ * reporting all children as deleted. Part of a workaround for issue 2333.
*
- * DIR is a repository path relative to the URL in RA_SESSION. REVISION
- * must be a valid revision number, not SVN_INVALID_REVNUM. EB is the
- * overall crawler editor baton. If CANCEL_FUNC is not NULL, then it
- * should refer to a cancellation function (along with CANCEL_BATON).
+ * DIR is a repository path relative to the URL in EB->ra_session. EB is
+ * the overall crawler editor baton. EB->revision must be a valid revision
+ * number, not SVN_INVALID_REVNUM. Use EB->cancel_func (if not null) with
+ * EB->cancel_baton for cancellation.
*/
/* ### TODO: Handle depth. */
static svn_error_t *
-diff_deleted_dir(const char *dir,
- svn_revnum_t revision,
- svn_ra_session_t *ra_session,
- struct edit_baton *eb,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *pool)
+diff_deleted_dir(const char *path,
+ struct dir_baton *pb,
+ apr_pool_t *scratch_pool)
{
- apr_hash_t *dirents;
- apr_pool_t *iterpool = svn_pool_create(pool);
- apr_hash_index_t *hi;
-
- SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
-
- if (cancel_func)
- SVN_ERR(cancel_func(cancel_baton));
-
- SVN_ERR(svn_ra_get_dir2(ra_session,
- &dirents,
- NULL, NULL,
- dir,
- revision,
- SVN_DIRENT_KIND,
- pool));
+ struct edit_baton *eb = pb->edit_baton;
+ struct dir_baton *db;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ svn_boolean_t skip = FALSE;
+ svn_boolean_t skip_children = FALSE;
+ apr_hash_t *dirents = NULL;
+ apr_hash_t *left_props = NULL;
+ svn_diff_source_t *left_source = svn_diff__source_create(eb->revision,
+ scratch_pool);
+ db = make_dir_baton(path, pb, pb->edit_baton, FALSE, SVN_INVALID_REVNUM,
+ scratch_pool);
+
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(eb->revision));
+
+ if (eb->cancel_func)
+ SVN_ERR(eb->cancel_func(eb->cancel_baton));
+
+ SVN_ERR(eb->processor->dir_opened(&db->pdb, &skip, &skip_children,
+ path,
+ left_source,
+ NULL /* right_source */,
+ NULL /* copyfrom_source */,
+ pb->pdb,
+ eb->processor,
+ scratch_pool, iterpool));
+
+ if (!skip || !skip_children)
+ SVN_ERR(svn_ra_get_dir2(eb->ra_session,
+ skip_children ? NULL : &dirents,
+ NULL,
+ skip ? NULL : &left_props,
+ path,
+ eb->revision,
+ SVN_DIRENT_KIND,
+ scratch_pool));
- for (hi = apr_hash_first(pool, dirents); hi;
- hi = apr_hash_next(hi))
+ /* The "old" dir will be skipped by the repository report. If required,
+ * crawl it recursively, diffing each file against the empty file. This
+ * is a workaround for issue 2333 "'svn diff URL1 URL2' not reverse of
+ * 'svn diff URL2 URL1'". */
+ if (! skip_children)
{
- const char *path;
- const char *name = svn__apr_hash_index_key(hi);
- svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
-
- svn_pool_clear(iterpool);
-
- path = svn_relpath_join(dir, name, iterpool);
+ apr_hash_index_t *hi;
- if (dirent->kind == svn_node_file)
+ for (hi = apr_hash_first(scratch_pool, dirents); hi;
+ hi = apr_hash_next(hi))
{
- struct file_baton *b;
- const char *mimetype1, *mimetype2;
+ const char *child_path;
+ const char *name = svn__apr_hash_index_key(hi);
+ svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
- /* Compare a file being deleted against an empty file */
- b = make_file_baton(path, FALSE, eb, iterpool);
- SVN_ERR(get_file_from_ra(b, FALSE, iterpool));
+ svn_pool_clear(iterpool);
- SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
+ child_path = svn_relpath_join(path, name, iterpool);
- get_file_mime_types(&mimetype1, &mimetype2, b);
-
- SVN_ERR(eb->diff_callbacks->file_deleted(
- NULL, NULL, b->wcpath,
- b->path_start_revision,
- b->path_end_revision,
- mimetype1, mimetype2,
- b->pristine_props,
- b->edit_baton->diff_cmd_baton,
- iterpool));
+ if (dirent->kind == svn_node_file)
+ {
+ SVN_ERR(diff_deleted_file(child_path, db, iterpool));
+ }
+ else if (dirent->kind == svn_node_dir)
+ {
+ SVN_ERR(diff_deleted_dir(child_path, db, iterpool));
+ }
}
+ }
- if (dirent->kind == svn_node_dir)
- SVN_ERR(diff_deleted_dir(path,
- revision,
- ra_session,
- eb,
- cancel_func,
- cancel_baton,
- iterpool));
+ if (! skip)
+ {
+ SVN_ERR(eb->processor->dir_deleted(path,
+ left_source,
+ left_props,
+ db->pdb,
+ eb->processor,
+ scratch_pool));
}
+ SVN_ERR(release_dir(db));
+
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
-/* An editor function. */
+/* An svn_delta_editor_t function. */
static svn_error_t *
delete_entry(const char *path,
svn_revnum_t base_revision,
@@ -580,14 +625,10 @@ delete_entry(const char *path,
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
svn_node_kind_t kind;
- svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
- svn_wc_notify_action_t action = svn_wc_notify_skip;
- svn_boolean_t tree_conflicted = FALSE;
apr_pool_t *scratch_pool;
- /* Skip *everything* within a newly tree-conflicted directory,
- * and directories the children of which should be skipped. */
- if (pb->skip || pb->tree_conflicted || pb->skip_children)
+ /* Process skips. */
+ if (pb->skip_children)
return SVN_NO_ERROR;
scratch_pool = svn_pool_create(eb->pool);
@@ -600,78 +641,24 @@ delete_entry(const char *path,
{
case svn_node_file:
{
- const char *mimetype1, *mimetype2;
- struct file_baton *b;
-
- /* Compare a file being deleted against an empty file */
- b = make_file_baton(path, FALSE, eb, scratch_pool);
- SVN_ERR(get_file_from_ra(b, FALSE, scratch_pool));
- SVN_ERR(get_empty_file(b->edit_baton, &(b->path_end_revision)));
-
- get_file_mime_types(&mimetype1, &mimetype2, b);
-
- SVN_ERR(eb->diff_callbacks->file_deleted(
- &state, &tree_conflicted, b->wcpath,
- b->path_start_revision,
- b->path_end_revision,
- mimetype1, mimetype2,
- b->pristine_props,
- b->edit_baton->diff_cmd_baton,
- scratch_pool));
-
+ SVN_ERR(diff_deleted_file(path, pb, scratch_pool));
break;
}
case svn_node_dir:
{
- SVN_ERR(eb->diff_callbacks->dir_deleted(
- &state, &tree_conflicted,
- svn_dirent_join(eb->target, path, pool),
- eb->diff_cmd_baton, scratch_pool));
-
- if (eb->walk_deleted_repos_dirs)
- {
- /* A workaround for issue 2333. The "old" dir will be
- skipped by the repository report. Crawl it recursively,
- diffing each file against the empty file. */
- SVN_ERR(diff_deleted_dir(path,
- eb->revision,
- eb->ra_session,
- eb,
- eb->cancel_func,
- eb->cancel_baton,
- scratch_pool));
- }
+ SVN_ERR(diff_deleted_dir(path, pb, scratch_pool));
break;
}
default:
break;
}
- if ((state != svn_wc_notify_state_missing)
- && (state != svn_wc_notify_state_obstructed)
- && !tree_conflicted)
- {
- action = svn_wc_notify_update_delete;
- }
-
- if (eb->notify_func)
- {
- const char* deleted_path;
- deleted_path_notify_t *dpn = apr_pcalloc(eb->pool, sizeof(*dpn));
- deleted_path = svn_dirent_join(eb->target, path, eb->pool);
- dpn->kind = kind;
- dpn->action = tree_conflicted ? svn_wc_notify_tree_conflict : action;
- dpn->state = state;
- dpn->tree_conflicted = tree_conflicted;
- apr_hash_set(eb->deleted_paths, deleted_path, APR_HASH_KEY_STRING, dpn);
- }
-
svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
-/* An editor function. */
+/* An svn_delta_editor_t function. */
static svn_error_t *
add_directory(const char *path,
void *parent_baton,
@@ -682,86 +669,40 @@ add_directory(const char *path,
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
- struct dir_baton *b;
- svn_wc_notify_state_t state;
+ struct dir_baton *db;
/* ### TODO: support copyfrom? */
- b = make_dir_baton(path, pb, eb, TRUE, pool);
- b->pristine_props = eb->empty_hash;
- *child_baton = b;
+ db = make_dir_baton(path, pb, eb, TRUE, SVN_INVALID_REVNUM, pb->pool);
+ *child_baton = db;
/* Skip *everything* within a newly tree-conflicted directory,
* and directories the children of which should be skipped. */
- if (pb->skip || pb->tree_conflicted || pb->skip_children)
+ if (pb->skip_children)
{
- b->skip = TRUE;
+ db->skip = TRUE;
+ db->skip_children = TRUE;
return SVN_NO_ERROR;
}
+ db->right_source = svn_diff__source_create(eb->target_revision,
+ db->pool);
- SVN_ERR(eb->diff_callbacks->dir_added(
- &state, &b->tree_conflicted,
- &b->skip, &b->skip_children, b->wcpath,
- eb->target_revision, copyfrom_path, copyfrom_revision,
- eb->diff_cmd_baton, pool));
-
- /* Notifications for directories are done at close_directory time.
- * But for paths at which the editor drive adds directories, we make an
- * exception to this rule, so that the path appears in the output before
- * any children of the newly added directory. Since a deletion at this path
- * must have happened before this addition, we can safely notify about
- * replaced directories here, too. */
- if (eb->notify_func)
- {
- deleted_path_notify_t *dpn;
- svn_wc_notify_t *notify;
- svn_wc_notify_action_t action;
- svn_node_kind_t kind = svn_node_dir;
-
- /* Find out if a pending delete notification for this path is
- * still around. */
- dpn = apr_hash_get(eb->deleted_paths, b->wcpath, APR_HASH_KEY_STRING);
- if (dpn)
- {
- /* If any was found, we will handle the pending 'deleted path
- * notification' (DPN) here. Remove it from the list. */
- apr_hash_set(eb->deleted_paths, b->wcpath,
- APR_HASH_KEY_STRING, NULL);
-
- /* the pending delete might be on a different node kind. */
- kind = dpn->kind;
- state = dpn->state;
- }
-
- /* Determine what the notification (ACTION) should be.
- * In case of a pending 'delete', this might become a 'replace'. */
- if (b->tree_conflicted)
- action = svn_wc_notify_tree_conflict;
- else if (dpn)
- {
- if (dpn->action == svn_wc_notify_update_delete)
- action = svn_wc_notify_update_replace;
- else
- /* Note: dpn->action might be svn_wc_notify_tree_conflict */
- action = dpn->action;
- }
- else if (state == svn_wc_notify_state_missing ||
- state == svn_wc_notify_state_obstructed)
- action = svn_wc_notify_skip;
- else
- action = svn_wc_notify_update_add;
-
- notify = svn_wc_create_notify(b->wcpath, action, pool);
- notify->kind = kind;
- notify->content_state = notify->prop_state = state;
- (*eb->notify_func)(eb->notify_baton, notify, pool);
- }
+ SVN_ERR(eb->processor->dir_opened(&db->pdb,
+ &db->skip,
+ &db->skip_children,
+ db->path,
+ NULL,
+ db->right_source,
+ NULL /* copyfrom_source */,
+ pb->pdb,
+ eb->processor,
+ db->pool, db->pool));
return SVN_NO_ERROR;
}
-/* An editor function. */
+/* An svn_delta_editor_t function. */
static svn_error_t *
open_directory(const char *path,
void *parent_baton,
@@ -771,31 +712,38 @@ open_directory(const char *path,
{
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
- struct dir_baton *b;
+ struct dir_baton *db;
- b = make_dir_baton(path, pb, pb->edit_baton, FALSE, pool);
- *child_baton = b;
+ db = make_dir_baton(path, pb, eb, FALSE, base_revision, pb->pool);
- /* Skip *everything* within a newly tree-conflicted directory
- * and directories the children of which should be skipped. */
- if (pb->skip || pb->tree_conflicted || pb->skip_children)
+ *child_baton = db;
+
+ /* Process Skips. */
+ if (pb->skip_children)
{
- b->skip = TRUE;
+ db->skip = TRUE;
+ db->skip_children = TRUE;
return SVN_NO_ERROR;
}
- SVN_ERR(get_dirprops_from_ra(b, base_revision));
+ db->left_source = svn_diff__source_create(eb->revision, db->pool);
+ db->right_source = svn_diff__source_create(eb->target_revision, db->pool);
- SVN_ERR(eb->diff_callbacks->dir_opened(
- &b->tree_conflicted, &b->skip,
- &b->skip_children, b->wcpath, base_revision,
- b->edit_baton->diff_cmd_baton, pool));
+ SVN_ERR(eb->processor->dir_opened(&db->pdb,
+ &db->skip, &db->skip_children,
+ path,
+ db->left_source,
+ db->right_source,
+ NULL /* copyfrom */,
+ pb ? pb->pdb : NULL,
+ eb->processor,
+ db->pool, db->pool));
return SVN_NO_ERROR;
}
-/* An editor function. */
+/* An svn_delta_editor_t function. */
static svn_error_t *
add_file(const char *path,
void *parent_baton,
@@ -805,27 +753,39 @@ add_file(const char *path,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
- struct file_baton *b;
+ struct edit_baton *eb = pb->edit_baton;
+ struct file_baton *fb;
/* ### TODO: support copyfrom? */
- b = make_file_baton(path, TRUE, pb->edit_baton, pool);
- *file_baton = b;
+ fb = make_file_baton(path, pb, TRUE, pb->pool);
+ *file_baton = fb;
- /* Skip *everything* within a newly tree-conflicted directory.
- * and directories the children of which should be skipped. */
- if (pb->skip || pb->tree_conflicted || pb->skip_children)
+ /* Process Skips. */
+ if (pb->skip_children)
{
- b->skip = TRUE;
+ fb->skip = TRUE;
return SVN_NO_ERROR;
}
- b->pristine_props = pb->edit_baton->empty_hash;
+ fb->pristine_props = pb->edit_baton->empty_hash;
+
+ fb->right_source = svn_diff__source_create(eb->target_revision, fb->pool);
+
+ SVN_ERR(eb->processor->file_opened(&fb->pfb,
+ &fb->skip,
+ path,
+ NULL,
+ fb->right_source,
+ NULL /* copy source */,
+ pb->pdb,
+ eb->processor,
+ fb->pool, fb->pool));
return SVN_NO_ERROR;
}
-/* An editor function. */
+/* An svn_delta_editor_t function. */
static svn_error_t *
open_file(const char *path,
void *parent_baton,
@@ -834,24 +794,32 @@ open_file(const char *path,
void **file_baton)
{
struct dir_baton *pb = parent_baton;
- struct file_baton *b;
+ struct file_baton *fb;
struct edit_baton *eb = pb->edit_baton;
- b = make_file_baton(path, FALSE, pb->edit_baton, pool);
- *file_baton = b;
+ fb = make_file_baton(path, pb, FALSE, pb->pool);
+ *file_baton = fb;
- /* Skip *everything* within a newly tree-conflicted directory
- * and directories the children of which should be skipped. */
- if (pb->skip || pb->tree_conflicted || pb->skip_children)
+ /* Process Skips. */
+ if (pb->skip_children)
{
- b->skip = TRUE;
+ fb->skip = TRUE;
return SVN_NO_ERROR;
}
- b->base_revision = base_revision;
+ fb->base_revision = base_revision;
- SVN_ERR(eb->diff_callbacks->file_opened(
- &b->tree_conflicted, &b->skip,
- b->wcpath, base_revision, eb->diff_cmd_baton, pool));
+ fb->left_source = svn_diff__source_create(eb->revision, fb->pool);
+ fb->right_source = svn_diff__source_create(eb->target_revision, fb->pool);
+
+ SVN_ERR(eb->processor->file_opened(&fb->pfb,
+ &fb->skip,
+ path,
+ fb->left_source,
+ fb->right_source,
+ NULL /* copy source */,
+ pb->pdb,
+ eb->processor,
+ fb->pool, fb->pool));
return SVN_NO_ERROR;
}
@@ -861,21 +829,52 @@ static svn_error_t *
window_handler(svn_txdelta_window_t *window,
void *window_baton)
{
- struct file_baton *b = window_baton;
+ struct file_baton *fb = window_baton;
- SVN_ERR(b->apply_handler(window, b->apply_baton));
+ SVN_ERR(fb->apply_handler(window, fb->apply_baton));
if (!window)
{
- b->result_md5_checksum = svn_checksum__from_digest(b->result_digest,
- svn_checksum_md5,
- b->pool);
+ fb->result_md5_checksum = svn_checksum__from_digest_md5(
+ fb->result_digest,
+ fb->pool);
}
return SVN_NO_ERROR;
}
-/* An editor function. */
+/* Implements svn_stream_lazyopen_func_t. */
+static svn_error_t *
+lazy_open_source(svn_stream_t **stream,
+ void *baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct file_baton *fb = baton;
+
+ SVN_ERR(svn_stream_open_readonly(stream, fb->path_start_revision,
+ result_pool, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* Implements svn_stream_lazyopen_func_t. */
+static svn_error_t *
+lazy_open_result(svn_stream_t **stream,
+ void *baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct file_baton *fb = baton;
+
+ SVN_ERR(svn_stream_open_unique(stream, &fb->path_end_revision, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ result_pool, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* An svn_delta_editor_t function. */
static svn_error_t *
apply_textdelta(void *file_baton,
const char *base_md5_digest,
@@ -883,26 +882,39 @@ apply_textdelta(void *file_baton,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
- struct file_baton *b = file_baton;
+ struct file_baton *fb = file_baton;
svn_stream_t *src_stream;
svn_stream_t *result_stream;
- apr_pool_t *scratch_pool = b->pool;
+ apr_pool_t *scratch_pool = fb->pool;
/* Skip *everything* within a newly tree-conflicted directory. */
- if (b->skip)
+ if (fb->skip)
{
*handler = svn_delta_noop_window_handler;
*handler_baton = NULL;
return SVN_NO_ERROR;
}
+ /* If we're not sending file text, then ignore any that we receive. */
+ if (! fb->edit_baton->text_deltas)
+ {
+ /* Supply valid paths to indicate there is a text change. */
+ SVN_ERR(get_empty_file(fb->edit_baton, &fb->path_start_revision));
+ SVN_ERR(get_empty_file(fb->edit_baton, &fb->path_end_revision));
+
+ *handler = svn_delta_noop_window_handler;
+ *handler_baton = NULL;
+
+ return SVN_NO_ERROR;
+ }
+
/* We need the expected pristine file, so go get it */
- if (!b->added)
- SVN_ERR(get_file_from_ra(b, FALSE, b->pool));
+ if (!fb->added)
+ SVN_ERR(get_file_from_ra(fb, FALSE, scratch_pool));
else
- SVN_ERR(get_empty_file(b->edit_baton, &(b->path_start_revision)));
+ SVN_ERR(get_empty_file(fb->edit_baton, &(fb->path_start_revision)));
- SVN_ERR_ASSERT(b->path_start_revision != NULL);
+ SVN_ERR_ASSERT(fb->path_start_revision != NULL);
if (base_md5_digest != NULL)
{
@@ -911,30 +923,29 @@ apply_textdelta(void *file_baton,
SVN_ERR(svn_checksum_parse_hex(&base_md5_checksum, svn_checksum_md5,
base_md5_digest, scratch_pool));
- if (!svn_checksum_match(base_md5_checksum, b->start_md5_checksum))
+ if (!svn_checksum_match(base_md5_checksum, fb->start_md5_checksum))
return svn_error_trace(svn_checksum_mismatch_err(
base_md5_checksum,
- b->start_md5_checksum,
+ fb->start_md5_checksum,
scratch_pool,
_("Base checksum mismatch for '%s'"),
- b->path));
+ fb->path));
}
/* Open the file to be used as the base for second revision */
- SVN_ERR(svn_stream_open_readonly(&src_stream, b->path_start_revision,
- scratch_pool, scratch_pool));
+ src_stream = svn_stream_lazyopen_create(lazy_open_source, fb, TRUE,
+ scratch_pool);
/* Open the file that will become the second revision after applying the
text delta, it starts empty */
- SVN_ERR(svn_stream_open_unique(&result_stream, &b->path_end_revision, NULL,
- svn_io_file_del_on_pool_cleanup,
- scratch_pool, scratch_pool));
+ result_stream = svn_stream_lazyopen_create(lazy_open_result, fb, TRUE,
+ scratch_pool);
svn_txdelta_apply(src_stream,
result_stream,
- b->result_digest,
- b->path, b->pool,
- &(b->apply_handler), &(b->apply_baton));
+ fb->result_digest,
+ fb->path, fb->pool,
+ &(fb->apply_handler), &(fb->apply_baton));
*handler = window_handler;
*handler_baton = file_baton;
@@ -942,7 +953,7 @@ apply_textdelta(void *file_baton,
return SVN_NO_ERROR;
}
-/* An editor function. When the file is closed we have a temporary
+/* An svn_delta_editor_t function. When the file is closed we have a temporary
* file containing a pristine version of the repository file. This can
* be compared against the working copy.
*
@@ -957,265 +968,208 @@ close_file(void *file_baton,
const char *expected_md5_digest,
apr_pool_t *pool)
{
- struct file_baton *b = file_baton;
- struct edit_baton *eb = b->edit_baton;
- svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
- svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
+ struct file_baton *fb = file_baton;
+ struct dir_baton *pb = fb->parent_baton;
+ struct edit_baton *eb = fb->edit_baton;
apr_pool_t *scratch_pool;
/* Skip *everything* within a newly tree-conflicted directory. */
- if (b->skip)
+ if (fb->skip)
{
- svn_pool_destroy(b->pool);
+ svn_pool_destroy(fb->pool);
+ SVN_ERR(release_dir(pb));
return SVN_NO_ERROR;
}
- scratch_pool = b->pool;
+ scratch_pool = fb->pool;
- if (expected_md5_digest)
+ if (expected_md5_digest && eb->text_deltas)
{
svn_checksum_t *expected_md5_checksum;
SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
expected_md5_digest, scratch_pool));
- if (!svn_checksum_match(expected_md5_checksum, b->result_md5_checksum))
+ if (!svn_checksum_match(expected_md5_checksum, fb->result_md5_checksum))
return svn_error_trace(svn_checksum_mismatch_err(
expected_md5_checksum,
- b->result_md5_checksum,
+ fb->result_md5_checksum,
pool,
_("Checksum mismatch for '%s'"),
- b->path));
+ fb->path));
}
- if (!b->added && b->propchanges->nelts > 0)
+ if (fb->added || fb->path_end_revision || fb->has_propchange)
{
- if (!b->pristine_props)
+ apr_hash_t *right_props;
+
+ if (!fb->added && !fb->pristine_props)
{
/* We didn't receive a text change, so we have no pristine props.
Retrieve just the props now. */
- SVN_ERR(get_file_from_ra(b, TRUE, scratch_pool));
+ SVN_ERR(get_file_from_ra(fb, TRUE, scratch_pool));
}
- remove_non_prop_changes(b->pristine_props, b->propchanges);
- }
-
- if (b->path_end_revision || b->propchanges->nelts > 0)
- {
- const char *mimetype1, *mimetype2;
- get_file_mime_types(&mimetype1, &mimetype2, b);
-
-
- if (b->added)
- SVN_ERR(eb->diff_callbacks->file_added(
- &content_state, &prop_state, &b->tree_conflicted,
- b->wcpath,
- b->path_end_revision ? b->path_start_revision : NULL,
- b->path_end_revision,
- 0,
- b->edit_baton->target_revision,
- mimetype1, mimetype2,
- NULL, SVN_INVALID_REVNUM,
- b->propchanges, b->pristine_props,
- b->edit_baton->diff_cmd_baton,
- scratch_pool));
+ if (fb->pristine_props)
+ remove_non_prop_changes(fb->pristine_props, fb->propchanges);
+
+ right_props = svn_prop__patch(fb->pristine_props, fb->propchanges,
+ fb->pool);
+
+ if (fb->added)
+ SVN_ERR(eb->processor->file_added(fb->path,
+ NULL /* copyfrom_src */,
+ fb->right_source,
+ NULL /* copyfrom_file */,
+ fb->path_end_revision,
+ NULL /* copyfrom_props */,
+ right_props,
+ fb->pfb,
+ eb->processor,
+ fb->pool));
else
- SVN_ERR(eb->diff_callbacks->file_changed(
- &content_state, &prop_state,
- &b->tree_conflicted, b->wcpath,
- b->path_end_revision ? b->path_start_revision : NULL,
- b->path_end_revision,
- b->edit_baton->revision,
- b->edit_baton->target_revision,
- mimetype1, mimetype2,
- b->propchanges, b->pristine_props,
- b->edit_baton->diff_cmd_baton,
- scratch_pool));
+ SVN_ERR(eb->processor->file_changed(fb->path,
+ fb->left_source,
+ fb->right_source,
+ fb->path_end_revision
+ ? fb->path_start_revision
+ : NULL,
+ fb->path_end_revision,
+ fb->pristine_props,
+ right_props,
+ (fb->path_end_revision != NULL),
+ fb->propchanges,
+ fb->pfb,
+ eb->processor,
+ fb->pool));
}
+ svn_pool_destroy(fb->pool); /* Destroy file and scratch pool */
- if (eb->notify_func)
- {
- deleted_path_notify_t *dpn;
- svn_wc_notify_t *notify;
- svn_wc_notify_action_t action;
- svn_node_kind_t kind = svn_node_file;
-
- /* Find out if a pending delete notification for this path is
- * still around. */
- dpn = apr_hash_get(eb->deleted_paths, b->wcpath, APR_HASH_KEY_STRING);
- if (dpn)
- {
- /* If any was found, we will handle the pending 'deleted path
- * notification' (DPN) here. Remove it from the list. */
- apr_hash_set(eb->deleted_paths, b->wcpath,
- APR_HASH_KEY_STRING, NULL);
-
- /* the pending delete might be on a different node kind. */
- kind = dpn->kind;
- content_state = prop_state = dpn->state;
- }
-
- /* Determine what the notification (ACTION) should be.
- * In case of a pending 'delete', this might become a 'replace'. */
- if (b->tree_conflicted)
- action = svn_wc_notify_tree_conflict;
- else if (dpn)
- {
- if (dpn->action == svn_wc_notify_update_delete
- && b->added)
- action = svn_wc_notify_update_replace;
- else
- /* Note: dpn->action might be svn_wc_notify_tree_conflict */
- action = dpn->action;
- }
- else if ((content_state == svn_wc_notify_state_missing)
- || (content_state == svn_wc_notify_state_obstructed))
- action = svn_wc_notify_skip;
- else if (b->added)
- action = svn_wc_notify_update_add;
- else
- action = svn_wc_notify_update_update;
-
- notify = svn_wc_create_notify(b->wcpath, action, scratch_pool);
- notify->kind = kind;
- notify->content_state = content_state;
- notify->prop_state = prop_state;
- (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
- }
-
- svn_pool_destroy(b->pool); /* Destroy file and scratch pool */
+ SVN_ERR(release_dir(pb));
return SVN_NO_ERROR;
}
-/* An editor function. */
+/* Report any accumulated prop changes via the 'dir_props_changed' callback,
+ * and then call the 'dir_closed' callback. Notify about any deleted paths
+ * within this directory that have not already been notified, and then about
+ * this directory itself (unless it was added, in which case the notification
+ * was done at that time).
+ *
+ * An svn_delta_editor_t function. */
static svn_error_t *
close_directory(void *dir_baton,
apr_pool_t *pool)
{
- struct dir_baton *b = dir_baton;
- struct edit_baton *eb = b->edit_baton;
- svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
- svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
- svn_boolean_t skipped = FALSE;
+ struct dir_baton *db = dir_baton;
+ struct edit_baton *eb = db->edit_baton;
apr_pool_t *scratch_pool;
+ apr_hash_t *pristine_props;
+ svn_boolean_t send_changed = FALSE;
- /* Skip *everything* within a newly tree-conflicted directory. */
- if (b->skip)
- {
- svn_pool_destroy(b->pool);
- return SVN_NO_ERROR;
- }
-
- scratch_pool = b->pool;
-
- if (!b->added && b->propchanges->nelts > 0)
- remove_non_prop_changes(b->pristine_props, b->propchanges);
+ scratch_pool = db->pool;
- /* Don't do the props_changed stuff if this is a dry_run and we don't
- have an access baton, since in that case the directory will already
- have been recognised as added, in which case they cannot conflict. */
- if (b->propchanges->nelts > 0)
+ if ((db->has_propchange || db->added) && !db->skip)
{
- svn_boolean_t tree_conflicted = FALSE;
- SVN_ERR(eb->diff_callbacks->dir_props_changed(
- &prop_state, &tree_conflicted,
- b->wcpath, b->added,
- b->propchanges, b->pristine_props,
- b->edit_baton->diff_cmd_baton, scratch_pool));
- if (tree_conflicted)
- b->tree_conflicted = TRUE;
-
- if (prop_state == svn_wc_notify_state_obstructed
- || prop_state == svn_wc_notify_state_missing)
+ if (db->added)
{
- content_state = prop_state;
- skipped = TRUE;
+ pristine_props = eb->empty_hash;
}
- }
-
- SVN_ERR(eb->diff_callbacks->dir_closed(NULL, NULL, NULL,
- b->wcpath, b->added,
- b->edit_baton->diff_cmd_baton,
- scratch_pool));
-
- /* Don't notify added directories as they triggered notification
- in add_directory. */
- if (!skipped && !b->added && eb->notify_func)
- {
- apr_hash_index_t *hi;
-
- for (hi = apr_hash_first(pool, eb->deleted_paths); hi;
- hi = apr_hash_next(hi))
+ else
{
- svn_wc_notify_t *notify;
- const char *deleted_path = svn__apr_hash_index_key(hi);
- deleted_path_notify_t *dpn = svn__apr_hash_index_val(hi);
-
- notify = svn_wc_create_notify(deleted_path, dpn->action, pool);
- notify->kind = dpn->kind;
- notify->content_state = notify->prop_state = dpn->state;
- notify->lock_state = svn_wc_notify_lock_state_inapplicable;
- (*eb->notify_func)(eb->notify_baton, notify, pool);
- apr_hash_set(eb->deleted_paths, deleted_path,
- APR_HASH_KEY_STRING, NULL);
+ SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, &pristine_props,
+ db->path, db->base_revision, 0, scratch_pool));
}
- }
- if (!b->added && eb->notify_func)
- {
- svn_wc_notify_t *notify;
- svn_wc_notify_action_t action;
+ if (db->propchanges->nelts > 0)
+ {
+ remove_non_prop_changes(pristine_props, db->propchanges);
+ }
- if (b->tree_conflicted)
- action = svn_wc_notify_tree_conflict;
- else if (skipped)
- action = svn_wc_notify_skip;
- else
- action = svn_wc_notify_update_update;
+ if (db->propchanges->nelts > 0 || db->added)
+ {
+ apr_hash_t *right_props;
- notify = svn_wc_create_notify(b->wcpath, action, pool);
- notify->kind = svn_node_dir;
+ right_props = svn_prop__patch(pristine_props, db->propchanges,
+ scratch_pool);
- /* In case of a tree conflict during merge, the diff callback
- * sets content_state appropriately. So copy the state into the
- * notify_t to make sure conflicts get displayed. */
- notify->content_state = content_state;
+ if (db->added)
+ {
+ SVN_ERR(eb->processor->dir_added(db->path,
+ NULL /* copyfrom */,
+ db->right_source,
+ NULL /* copyfrom props */,
+ right_props,
+ db->pdb,
+ eb->processor,
+ db->pool));
+ }
+ else
+ {
+ SVN_ERR(eb->processor->dir_changed(db->path,
+ db->left_source,
+ db->right_source,
+ pristine_props,
+ right_props,
+ db->propchanges,
+ db->pdb,
+ eb->processor,
+ db->pool));
+ }
- notify->prop_state = prop_state;
- notify->lock_state = svn_wc_notify_lock_state_inapplicable;
- (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
+ send_changed = TRUE; /* Skip dir_closed */
+ }
}
- svn_pool_destroy(b->pool); /* Destroy baton and scratch_pool */
+ if (! db->skip && !send_changed)
+ {
+ SVN_ERR(eb->processor->dir_closed(db->path,
+ db->left_source,
+ db->right_source,
+ db->pdb,
+ eb->processor,
+ db->pool));
+ }
+ SVN_ERR(release_dir(db));
return SVN_NO_ERROR;
}
-/* An editor function. */
+/* Record a prop change, which we will report later in close_file().
+ *
+ * An svn_delta_editor_t function. */
static svn_error_t *
change_file_prop(void *file_baton,
const char *name,
const svn_string_t *value,
apr_pool_t *pool)
{
- struct file_baton *b = file_baton;
+ struct file_baton *fb = file_baton;
svn_prop_t *propchange;
+ svn_prop_kind_t propkind;
/* Skip *everything* within a newly tree-conflicted directory. */
- if (b->skip)
+ if (fb->skip)
+ return SVN_NO_ERROR;
+
+ propkind = svn_property_kind2(name);
+ if (propkind == svn_prop_wc_kind)
return SVN_NO_ERROR;
+ else if (propkind == svn_prop_regular_kind)
+ fb->has_propchange = TRUE;
- propchange = apr_array_push(b->propchanges);
- propchange->name = apr_pstrdup(b->pool, name);
- propchange->value = value ? svn_string_dup(value, b->pool) : NULL;
+ propchange = apr_array_push(fb->propchanges);
+ propchange->name = apr_pstrdup(fb->pool, name);
+ propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
return SVN_NO_ERROR;
}
-/* An editor function. */
+/* Make a note of this prop change, to be reported when the dir is closed.
+ *
+ * An svn_delta_editor_t function. */
static svn_error_t *
change_dir_prop(void *dir_baton,
const char *name,
@@ -1224,11 +1178,18 @@ change_dir_prop(void *dir_baton,
{
struct dir_baton *db = dir_baton;
svn_prop_t *propchange;
+ svn_prop_kind_t propkind;
/* Skip *everything* within a newly tree-conflicted directory. */
if (db->skip)
return SVN_NO_ERROR;
+ propkind = svn_property_kind2(name);
+ if (propkind == svn_prop_wc_kind)
+ return SVN_NO_ERROR;
+ else if (propkind == svn_prop_regular_kind)
+ db->has_propchange = TRUE;
+
propchange = apr_array_push(db->propchanges);
propchange->name = apr_pstrdup(db->pool, name);
propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
@@ -1237,7 +1198,7 @@ change_dir_prop(void *dir_baton,
}
-/* An editor function. */
+/* An svn_delta_editor_t function. */
static svn_error_t *
close_edit(void *edit_baton,
apr_pool_t *pool)
@@ -1249,7 +1210,8 @@ close_edit(void *edit_baton,
return SVN_NO_ERROR;
}
-/* An editor function. */
+/* Notify that the node at PATH is 'missing'.
+ * An svn_delta_editor_t function. */
static svn_error_t *
absent_directory(const char *path,
void *parent_baton,
@@ -1258,27 +1220,14 @@ absent_directory(const char *path,
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
- /* ### TODO: Raise a tree-conflict?? I sure hope not.*/
-
- if (eb->notify_func)
- {
- svn_wc_notify_t *notify
- = svn_wc_create_notify(svn_dirent_join(pb->wcpath,
- svn_relpath_basename(path,
- NULL),
- pool),
- svn_wc_notify_skip, pool);
- notify->kind = svn_node_dir;
- notify->content_state = notify->prop_state
- = svn_wc_notify_state_missing;
- (*eb->notify_func)(eb->notify_baton, notify, pool);
- }
+ SVN_ERR(eb->processor->node_absent(path, pb->pdb, eb->processor, pool));
return SVN_NO_ERROR;
}
-/* An editor function. */
+/* Notify that the node at PATH is 'missing'.
+ * An svn_delta_editor_t function. */
static svn_error_t *
absent_file(const char *path,
void *parent_baton,
@@ -1287,68 +1236,138 @@ absent_file(const char *path,
struct dir_baton *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
- /* ### TODO: Raise a tree-conflict?? I sure hope not.*/
+ SVN_ERR(eb->processor->node_absent(path, pb->pdb, eb->processor, pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_kind_func(svn_node_kind_t *kind,
+ void *baton,
+ const char *path,
+ svn_revnum_t base_revision,
+ apr_pool_t *scratch_pool)
+{
+ struct edit_baton *eb = baton;
+
+ if (!SVN_IS_VALID_REVNUM(base_revision))
+ base_revision = eb->revision;
+
+ SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, kind,
+ scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_props_func(apr_hash_t **props,
+ void *baton,
+ const char *path,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct edit_baton *eb = baton;
+ svn_node_kind_t node_kind;
+
+ if (!SVN_IS_VALID_REVNUM(base_revision))
+ base_revision = eb->revision;
- if (eb->notify_func)
+ SVN_ERR(svn_ra_check_path(eb->ra_session, path, base_revision, &node_kind,
+ scratch_pool));
+
+ if (node_kind == svn_node_file)
+ {
+ SVN_ERR(svn_ra_get_file(eb->ra_session, path, base_revision,
+ NULL, NULL, props, result_pool));
+ }
+ else if (node_kind == svn_node_dir)
+ {
+ apr_array_header_t *tmp_props;
+
+ SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, props, path,
+ base_revision, 0 /* Dirent fields */,
+ result_pool));
+ tmp_props = svn_prop_hash_to_array(*props, result_pool);
+ SVN_ERR(svn_categorize_props(tmp_props, NULL, NULL, &tmp_props,
+ result_pool));
+ *props = svn_prop_array_to_hash(tmp_props, result_pool);
+ }
+ else
+ {
+ *props = apr_hash_make(result_pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+fetch_base_func(const char **filename,
+ void *baton,
+ const char *path,
+ svn_revnum_t base_revision,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct edit_baton *eb = baton;
+ svn_stream_t *fstream;
+ svn_error_t *err;
+
+ if (!SVN_IS_VALID_REVNUM(base_revision))
+ base_revision = eb->revision;
+
+ SVN_ERR(svn_stream_open_unique(&fstream, filename, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ result_pool, scratch_pool));
+
+ err = svn_ra_get_file(eb->ra_session, path, base_revision,
+ fstream, NULL, NULL, scratch_pool);
+ if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
- svn_wc_notify_t *notify
- = svn_wc_create_notify(svn_dirent_join(pb->wcpath,
- svn_relpath_basename(path,
- pool),
- pool),
- svn_wc_notify_skip, pool);
- notify->kind = svn_node_file;
- notify->content_state = notify->prop_state
- = svn_wc_notify_state_missing;
- (*eb->notify_func)(eb->notify_baton, notify, pool);
+ svn_error_clear(err);
+ SVN_ERR(svn_stream_close(fstream));
+
+ *filename = NULL;
+ return SVN_NO_ERROR;
}
+ else if (err)
+ return svn_error_trace(err);
+
+ SVN_ERR(svn_stream_close(fstream));
return SVN_NO_ERROR;
}
/* Create a repository diff editor and baton. */
svn_error_t *
-svn_client__get_diff_editor(const svn_delta_editor_t **editor,
- void **edit_baton,
- svn_wc_context_t *wc_ctx,
- const char *target,
- svn_depth_t depth,
- svn_ra_session_t *ra_session,
- svn_revnum_t revision,
- svn_boolean_t walk_deleted_dirs,
- svn_boolean_t dry_run,
- const svn_wc_diff_callbacks4_t *diff_callbacks,
- void *diff_cmd_baton,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- svn_wc_notify_func2_t notify_func,
- void *notify_baton,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+svn_client__get_diff_editor2(const svn_delta_editor_t **editor,
+ void **edit_baton,
+ svn_ra_session_t *ra_session,
+ svn_depth_t depth,
+ svn_revnum_t revision,
+ svn_boolean_t text_deltas,
+ const svn_diff_tree_processor_t *processor,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *result_pool)
{
apr_pool_t *editor_pool = svn_pool_create(result_pool);
svn_delta_editor_t *tree_editor = svn_delta_default_editor(editor_pool);
struct edit_baton *eb = apr_pcalloc(editor_pool, sizeof(*eb));
- const char *target_abspath;
-
- SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, editor_pool));
+ svn_delta_shim_callbacks_t *shim_callbacks =
+ svn_delta_shim_callbacks_default(editor_pool);
eb->pool = editor_pool;
- eb->target = target;
- eb->wc_ctx = wc_ctx;
eb->depth = depth;
- eb->diff_callbacks = diff_callbacks;
- eb->diff_cmd_baton = diff_cmd_baton;
- eb->dry_run = dry_run;
+
+ eb->processor = processor;
+
eb->ra_session = ra_session;
eb->revision = revision;
eb->empty_file = NULL;
eb->empty_hash = apr_hash_make(eb->pool);
- eb->deleted_paths = apr_hash_make(eb->pool);
- eb->notify_func = notify_func;
- eb->notify_baton = notify_baton;
- eb->walk_deleted_repos_dirs = walk_deleted_dirs;
+ eb->text_deltas = text_deltas;
eb->cancel_func = cancel_func;
eb->cancel_baton = cancel_baton;
@@ -1368,11 +1387,19 @@ svn_client__get_diff_editor(const svn_delta_editor_t **editor,
tree_editor->absent_directory = absent_directory;
tree_editor->absent_file = absent_file;
- return svn_delta_get_cancellation_editor(cancel_func,
- cancel_baton,
- tree_editor,
- eb,
- editor,
- edit_baton,
- eb->pool);
+ SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
+ tree_editor, eb,
+ editor, edit_baton,
+ eb->pool));
+
+ shim_callbacks->fetch_kind_func = fetch_kind_func;
+ shim_callbacks->fetch_props_func = fetch_props_func;
+ shim_callbacks->fetch_base_func = fetch_base_func;
+ shim_callbacks->fetch_baton = eb;
+
+ SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
+ NULL, NULL, shim_callbacks,
+ result_pool, result_pool));
+
+ return SVN_NO_ERROR;
}