You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by pr...@apache.org on 2013/02/13 11:21:36 UTC

svn commit: r1445542 [7/16] - in /subversion/branches/verify-keep-going: ./ build/generator/ build/generator/swig/ build/generator/templates/ build/win32/ contrib/server-side/fsfsfixer/fixer/ contrib/server-side/svncutter/ notes/ notes/api-errata/1.7/ ...

Modified: subversion/branches/verify-keep-going/subversion/libsvn_wc/diff_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/libsvn_wc/diff_editor.c?rev=1445542&r1=1445541&r2=1445542&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/libsvn_wc/diff_editor.c (original)
+++ subversion/branches/verify-keep-going/subversion/libsvn_wc/diff_editor.c Wed Feb 13 10:21:33 2013
@@ -55,6 +55,8 @@
 #include <apr_hash.h>
 #include <apr_md5.h>
 
+#include <assert.h>
+
 #include "svn_error.h"
 #include "svn_pools.h"
 #include "svn_dirent_uri.h"
@@ -203,14 +205,21 @@ get_pristine_file(const char **result_ab
 
 /* Overall crawler editor baton.
  */
-struct edit_baton {
+struct edit_baton_t 
+{
   /* A wc db. */
   svn_wc__db_t *db;
 
+  /* A diff tree processor, receiving the result of the diff. */
+  const svn_diff_tree_processor_t *processor;
+
+  /* A boolean indicating whether local additions should be reported before
+     remote deletes. The processor can transform adds in deletes and deletes
+     in adds, but it can't reorder the output. */
+  svn_boolean_t local_before_remote;
+
   /* ANCHOR/TARGET represent the base of the hierarchy to be compared. */
   const char *target;
-
-  /* The absolute path of the anchor directory */
   const char *anchor_abspath;
 
   /* Target revision */
@@ -219,12 +228,7 @@ struct edit_baton {
   /* Was the root opened? */
   svn_boolean_t root_opened;
 
-  /* The callbacks and callback argument that implement the file comparison
-     functions */
-  const svn_wc_diff_callbacks4_t *callbacks;
-  void *callback_baton;
-
-  /* How does this diff descend? */
+  /* How does this diff descend as seen from target? */
   svn_depth_t depth;
 
   /* Should this diff ignore node ancestry? */
@@ -233,17 +237,8 @@ struct edit_baton {
   /* Should this diff not compare copied files with their source? */
   svn_boolean_t show_copies_as_adds;
 
-  /* Are we producing a git-style diff? */
-  svn_boolean_t use_git_diff_format;
-
   /* Possibly diff repos against text-bases instead of working files. */
-  svn_boolean_t use_text_base;
-
-  /* Possibly show the diffs backwards. */
-  svn_boolean_t reverse_order;
-
-  /* Empty file used to diff adds / deletes */
-  const char *empty_file;
+  svn_boolean_t diff_pristine;
 
   /* Hash whose keys are const char * changelist names. */
   apr_hash_t *changelist_hash;
@@ -257,57 +252,84 @@ struct edit_baton {
 
 /* Directory level baton.
  */
-struct dir_baton {
+struct dir_baton_t
+{
   /* Gets set if the directory is added rather than replaced/unchanged. */
   svn_boolean_t added;
 
+  /* Reference to parent directory baton (or NULL for the root) */
+  struct dir_baton_t *parent_baton;
+
   /* The depth at which this directory should be diffed. */
   svn_depth_t depth;
 
   /* The name and path of this directory as if they would be/are in the
       local working copy. */
   const char *name;
+  const char *relpath;
   const char *local_abspath;
+  svn_boolean_t shadowed;
+
+  /* Processor state */
+  void *pdb;
+  svn_boolean_t skip;
+  svn_boolean_t skip_children;
+
+  svn_diff_source_t *left_src;
+  svn_diff_source_t *right_src;
 
-  /* The "correct" path of the directory, but it may not exist in the
-     working copy. */
-  const char *path;
+  apr_hash_t *local_info;
+
+  /* A hash containing the basenames of the nodes reported deleted by the
+     repository (or NULL for no values). */
+  apr_hash_t *deletes;
 
   /* Identifies those directory elements that get compared while running
      the crawler.  These elements should not be compared again when
      recursively looking for local modifications.
 
-     This hash maps the full path of the entry to an unimportant value
-     (presence in the hash is the important factor here, not the value
-     itself).
+     This hash maps the basename of the node to an unimportant value.
 
      If the directory's properties have been compared, an item with hash
-     key of path will be present in the hash. */
+     key of "" will be present in the hash. */
   apr_hash_t *compared;
 
   /* The list of incoming BASE->repos propchanges. */
   apr_array_header_t *propchanges;
 
+  /* Has a change on regular properties */
+  svn_boolean_t has_propchange;
+
   /* The overall crawler editor baton. */
-  struct edit_baton *eb;
+  struct edit_baton_t *eb;
 
   apr_pool_t *pool;
+  int users;
 };
 
 /* File level baton.
  */
-struct file_baton {
+struct file_baton_t
+{
   /* Gets set if the file is added rather than replaced. */
   svn_boolean_t added;
 
+  struct dir_baton_t *parent_baton;
+
   /* The name and path of this file as if they would be/are in the
-      local working copy. */
+     parent directory, diff session and local working copy. */
   const char *name;
+  const char *relpath;
   const char *local_abspath;
+  svn_boolean_t shadowed;
+
+  /* Processor state */
+  void *pfb;
+  svn_boolean_t skip;
+
+  const svn_diff_source_t *left_src;
+  const svn_diff_source_t *right_src;
 
-  /* PATH is the "correct" path of the file, but it may not exist in the
-     working copy */
-  const char *path;
 
  /* When constructing the requested repository version of the file, we
     drop the result into a file at TEMP_FILE_PATH. */
@@ -316,14 +338,19 @@ struct file_baton {
   /* The list of incoming BASE->repos propchanges. */
   apr_array_header_t *propchanges;
 
-  /* The current checksum on disk */
+  /* Has a change on regular properties */
+  svn_boolean_t has_propchange;
+
+  /* The current BASE checksum and props */
   const svn_checksum_t *base_checksum;
+  apr_hash_t *base_props;
 
   /* The resulting checksum from apply_textdelta */
-  svn_checksum_t *result_checksum;
+  unsigned char result_digest[APR_MD5_DIGESTSIZE];
+  svn_boolean_t got_textdelta;
 
   /* The overall crawler editor baton. */
-  struct edit_baton *eb;
+  struct edit_baton_t *eb;
 
   apr_pool_t *pool;
 };
@@ -343,7 +370,7 @@ struct file_baton {
  * filtering is requested.
  */
 static svn_error_t *
-make_edit_baton(struct edit_baton **edit_baton,
+make_edit_baton(struct edit_baton_t **edit_baton,
                 svn_wc__db_t *db,
                 const char *anchor_abspath,
                 const char *target,
@@ -361,7 +388,8 @@ make_edit_baton(struct edit_baton **edit
                 apr_pool_t *pool)
 {
   apr_hash_t *changelist_hash = NULL;
-  struct edit_baton *eb;
+  struct edit_baton_t *eb;
+  const svn_diff_tree_processor_t *processor;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(anchor_abspath));
 
@@ -369,18 +397,27 @@ make_edit_baton(struct edit_baton **edit
     SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
                                        pool));
 
+  SVN_ERR(svn_wc__wrap_diff_callbacks(&processor,
+                                      callbacks, callback_baton, TRUE,
+                                      pool, pool));
+
+  if (reverse_order)
+    processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool);
+
+  if (! show_copies_as_adds && !use_git_diff_format)
+    processor = svn_diff__tree_processor_copy_as_changed_create(processor,
+                                                                pool);
+
   eb = apr_pcalloc(pool, sizeof(*eb));
   eb->db = db;
   eb->anchor_abspath = apr_pstrdup(pool, anchor_abspath);
   eb->target = apr_pstrdup(pool, target);
-  eb->callbacks = callbacks;
-  eb->callback_baton = callback_baton;
+  eb->processor = processor;
   eb->depth = depth;
   eb->ignore_ancestry = ignore_ancestry;
   eb->show_copies_as_adds = show_copies_as_adds;
-  eb->use_git_diff_format = use_git_diff_format;
-  eb->use_text_base = use_text_base;
-  eb->reverse_order = reverse_order;
+  eb->local_before_remote = reverse_order;
+  eb->diff_pristine = use_text_base;
   eb->changelist_hash = changelist_hash;
   eb->cancel_func = cancel_func;
   eb->cancel_baton = cancel_baton;
@@ -398,32 +435,39 @@ make_edit_baton(struct edit_baton **edit
  * exist in the working copy.  EDIT_BATON is the overall crawler
  * editor baton.
  */
-static struct dir_baton *
+static struct dir_baton_t *
 make_dir_baton(const char *path,
-               struct dir_baton *parent_baton,
-               struct edit_baton *eb,
+               struct dir_baton_t *parent_baton,
+               struct edit_baton_t *eb,
                svn_boolean_t added,
                svn_depth_t depth,
                apr_pool_t *result_pool)
 {
-  apr_pool_t *dir_pool = svn_pool_create(result_pool);
-  struct dir_baton *db = apr_pcalloc(dir_pool, sizeof(*db));
+  apr_pool_t *dir_pool = svn_pool_create(parent_baton ? parent_baton->pool
+                                                      : eb->pool);
+  struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
+
+  db->parent_baton = parent_baton;
+
+  /* Allocate 1 string for using as 3 strings */
+  db->local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool);
+  db->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, db->local_abspath);
+  db->name = svn_dirent_basename(db->relpath, NULL);
 
   db->eb = eb;
   db->added = added;
   db->depth = depth;
   db->pool = dir_pool;
-  db->propchanges = apr_array_make(dir_pool, 1, sizeof(svn_prop_t));
+  db->propchanges = apr_array_make(dir_pool, 8, sizeof(svn_prop_t));
   db->compared = apr_hash_make(dir_pool);
-  db->path = apr_pstrdup(dir_pool, path);
-
-  db->name = svn_dirent_basename(db->path, NULL);
 
   if (parent_baton != NULL)
-    db->local_abspath = svn_dirent_join(parent_baton->local_abspath, db->name,
-                                        dir_pool);
-  else
-    db->local_abspath = apr_pstrdup(dir_pool, eb->anchor_abspath);
+    {
+      parent_baton->users++;
+      db->shadowed = parent_baton->shadowed;
+    }
+
+  db->users = 1;
 
   return db;
 }
@@ -433,86 +477,52 @@ make_dir_baton(const char *path,
  * replaced.  PARENT_BATON is the baton of the parent directory.
  * The directory and its parent may or may not exist in the working copy.
  */
-static struct file_baton *
+static struct file_baton_t *
 make_file_baton(const char *path,
                 svn_boolean_t added,
-                struct dir_baton *parent_baton,
+                struct dir_baton_t *parent_baton,
                 apr_pool_t *result_pool)
 {
   apr_pool_t *file_pool = svn_pool_create(result_pool);
-  struct file_baton *fb = apr_pcalloc(file_pool, sizeof(*fb));
-  struct edit_baton *eb = parent_baton->eb;
+  struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb));
+  struct edit_baton_t *eb = parent_baton->eb;
 
   fb->eb = eb;
+  fb->parent_baton = parent_baton;
+  fb->parent_baton->users++;
+
+  /* Allocate 1 string for using as 3 strings */
+  fb->local_abspath = svn_dirent_join(eb->anchor_abspath, path, file_pool);
+  fb->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, fb->local_abspath);
+  fb->name = svn_dirent_basename(fb->relpath, NULL);
+  fb->shadowed = parent_baton->shadowed;
+
   fb->added = added;
   fb->pool = file_pool;
-  fb->propchanges  = apr_array_make(file_pool, 1, sizeof(svn_prop_t));
-  fb->path = apr_pstrdup(file_pool, path);
-
-  fb->name = svn_dirent_basename(fb->path, NULL);
-  fb->local_abspath = svn_dirent_join(parent_baton->local_abspath, fb->name,
-                                      file_pool);
+  fb->propchanges  = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
 
   return fb;
 }
 
-/* Get the empty file associated with the edit baton. This is cached so
- * that it can be reused, all empty files are the same.
- */
+/* Destroy DB when there are no more registered users */
 static svn_error_t *
-get_empty_file(struct edit_baton *b,
-               const char **empty_file)
+maybe_done(struct dir_baton_t *db)
 {
-  /* Create the file if it does not exist */
-  /* Note that we tried to use /dev/null in r857294, but
-     that won't work on Windows: it's impossible to stat NUL */
-  if (!b->empty_file)
-    {
-      SVN_ERR(svn_io_open_unique_file3(NULL, &b->empty_file, NULL,
-                                       svn_io_file_del_on_pool_cleanup,
-                                       b->pool, b->pool));
-    }
-
-  *empty_file = b->empty_file;
-
-  return SVN_NO_ERROR;
-}
-
-
-/* Return the value of the svn:mime-type property held in PROPS, or NULL
-   if no such property exists. */
-static const char *
-get_prop_mimetype(apr_hash_t *props)
-{
-  return svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
-}
+  db->users--;
 
+  if (!db->users)
+    {
+      struct dir_baton_t *pb = db->parent_baton;
 
-/* Return the property hash resulting from combining PROPS and PROPCHANGES.
- *
- * A note on pool usage: The returned hash and hash keys are allocated in
- * the same pool as PROPS, but the hash values will be taken directly from
- * either PROPS or PROPCHANGES, as appropriate.  Caller must therefore
- * ensure that the returned hash is only used for as long as PROPS and
- * PROPCHANGES remain valid.
- */
-static apr_hash_t *
-apply_propchanges(apr_hash_t *props,
-                  const apr_array_header_t *propchanges)
-{
-  apr_hash_t *newprops = apr_hash_copy(apr_hash_pool_get(props), props);
-  int i;
+      svn_pool_clear(db->pool);
 
-  for (i = 0; i < propchanges->nelts; ++i)
-    {
-      const svn_prop_t *prop = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
-      apr_hash_set(newprops, prop->name, APR_HASH_KEY_STRING, prop->value);
+      if (pb != NULL)
+        SVN_ERR(maybe_done(pb));
     }
 
-  return newprops;
+  return SVN_NO_ERROR;
 }
 
-
 /* Diff the file PATH against its text base.  At this
  * stage we are dealing with a file that does exist in the working copy.
  *
@@ -525,16 +535,17 @@ apply_propchanges(apr_hash_t *props,
  * directory.
  */
 static svn_error_t *
-file_diff(struct edit_baton *eb,
+file_diff(struct edit_baton_t *eb,
           const char *local_abspath,
           const char *path,
+          void *dir_baton,
           apr_pool_t *scratch_pool)
 {
   svn_wc__db_t *db = eb->db;
   const char *textbase;
-  const char *empty_file;
   svn_boolean_t replaced;
   svn_wc__db_status_t status;
+  svn_revnum_t original_revision;
   const char *original_repos_relpath;
   svn_revnum_t revision;
   svn_revnum_t revert_base_revnum;
@@ -542,7 +553,7 @@ file_diff(struct edit_baton *eb,
   svn_wc__db_status_t base_status;
   svn_boolean_t use_base = FALSE;
 
-  SVN_ERR_ASSERT(! eb->use_text_base);
+  SVN_ERR_ASSERT(! eb->diff_pristine);
 
   /* If the item is not a member of a specified changelist (and there are
      some specified changelists), skip it. */
@@ -551,10 +562,11 @@ file_diff(struct edit_baton *eb,
     return SVN_NO_ERROR;
 
   SVN_ERR(svn_wc__db_read_info(&status, NULL, &revision, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL,
-                               &have_base, NULL, NULL,
+                               NULL, NULL, NULL, NULL, NULL, NULL,
+                               &original_repos_relpath, NULL, NULL,
+                               &original_revision, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL,
+                               NULL, &have_base, NULL, NULL,
                                db, local_abspath, scratch_pool, scratch_pool));
   if (have_base)
     SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &revert_base_revnum,
@@ -567,14 +579,6 @@ file_diff(struct edit_baton *eb,
               && have_base
               && base_status != svn_wc__db_status_not_present);
 
-  /* Now refine ADDED to one of: ADDED, COPIED, MOVED_HERE. Note that only
-     the latter two have corresponding pristine info to diff against.  */
-  if (status == svn_wc__db_status_added)
-    SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL,
-                                     &original_repos_relpath, NULL, NULL,
-                                     NULL, NULL, NULL, db, local_abspath,
-                                     scratch_pool, scratch_pool));
-
   /* A wc-wc diff of replaced files actually shows a diff against the
    * revert-base, showing all previous lines as removed and adding all new
    * lines. This does not happen for copied/moved-here files, not even with
@@ -582,8 +586,7 @@ file_diff(struct edit_baton *eb,
    * an add, diffing against the empty file).
    * So show the revert-base revision for plain replaces. */
   if (replaced
-      && ! (status == svn_wc__db_status_copied
-            || status == svn_wc__db_status_moved_here))
+      && ! original_repos_relpath)
     {
       use_base = TRUE;
       revision = revert_base_revnum;
@@ -597,8 +600,6 @@ file_diff(struct edit_baton *eb,
   SVN_ERR(get_pristine_file(&textbase, db, local_abspath,
                             use_base, scratch_pool, scratch_pool));
 
-  SVN_ERR(get_empty_file(eb, &empty_file));
-
   /* Delete compares text-base against empty file, modifications to the
    * working-copy version of the deleted file are not wanted.
    * Replace is treated like a delete plus an add: two comparisons are
@@ -608,25 +609,35 @@ file_diff(struct edit_baton *eb,
   if ((! replaced && status == svn_wc__db_status_deleted) ||
       (replaced && ! eb->ignore_ancestry))
     {
-      const char *base_mimetype;
-      apr_hash_t *baseprops;
+      apr_hash_t *left_props;
+      void *file_baton = NULL;
+      svn_boolean_t skip = FALSE;
+      svn_diff_source_t *left_src = svn_diff__source_create(revision,
+                                                            scratch_pool);
 
       /* Get svn:mime-type from pristine props (in BASE or WORKING) of PATH. */
-      SVN_ERR(svn_wc__db_read_pristine_props(&baseprops, db, local_abspath,
+      SVN_ERR(svn_wc__db_read_pristine_props(&left_props, db, local_abspath,
                                              scratch_pool, scratch_pool));
-      if (baseprops)
-        base_mimetype = get_prop_mimetype(baseprops);
-      else
-        base_mimetype = NULL;
 
-      SVN_ERR(eb->callbacks->file_deleted(NULL, NULL, path,
-                                          textbase,
-                                          empty_file,
-                                          base_mimetype,
-                                          NULL,
-                                          baseprops,
-                                          eb->callback_baton,
-                                          scratch_pool));
+      SVN_ERR(eb->processor->file_opened(&file_baton,
+                                         &skip,
+                                         path,
+                                         left_src,
+                                         NULL /* right_source */,
+                                         NULL /* copyfrom_source */,
+                                         dir_baton,
+                                         eb->processor,
+                                         scratch_pool,
+                                         scratch_pool));
+
+      if (!skip)
+        SVN_ERR(eb->processor->file_deleted(path,
+                                            left_src,
+                                            textbase,
+                                            left_props,
+                                            file_baton,
+                                            eb->processor,
+                                            scratch_pool));
 
       if (! (replaced && ! eb->ignore_ancestry))
         {
@@ -643,58 +654,99 @@ file_diff(struct edit_baton *eb,
   * diff, and the file was copied, we need to report the file as added and
   * diff it against the text base, so that a "copied" git diff header, and
   * possibly a diff against the copy source, will be generated for it. */
-  if ((! replaced && status == svn_wc__db_status_added) ||
-     (replaced && ! eb->ignore_ancestry) ||
-     ((status == svn_wc__db_status_copied ||
-       status == svn_wc__db_status_moved_here) &&
-         (eb->show_copies_as_adds || eb->use_git_diff_format)))
+  if (status == svn_wc__db_status_added
+      && !(eb->ignore_ancestry && replaced))
     {
+      void *file_baton = NULL;
+      svn_boolean_t skip = FALSE;
       const char *translated = NULL;
-      const char *working_mimetype;
-      apr_hash_t *baseprops;
-      apr_hash_t *workingprops;
-      apr_array_header_t *propchanges;
+      svn_diff_source_t *copyfrom_src = NULL;
+      svn_diff_source_t *right_src = svn_diff__source_create(
+                                                    SVN_INVALID_REVNUM,
+                                                    scratch_pool);
 
-      /* Get svn:mime-type from ACTUAL props of PATH. */
-      SVN_ERR(svn_wc__get_actual_props(&workingprops, db, local_abspath,
-                                       scratch_pool, scratch_pool));
-      working_mimetype = get_prop_mimetype(workingprops);
+      if (original_repos_relpath)
+        {
+          copyfrom_src = svn_diff__source_create(original_revision,
+                                                 scratch_pool);
+          copyfrom_src->repos_relpath = original_repos_relpath;
+        }
 
-      /* Set the original properties to empty, then compute "changes" from
-         that. Essentially, all ACTUAL props will be "added".  */
-      baseprops = apr_hash_make(scratch_pool);
-      SVN_ERR(svn_prop_diffs(&propchanges, workingprops, baseprops,
-                             scratch_pool));
+      SVN_ERR(eb->processor->file_opened(&file_baton, &skip,
+                                         path,
+                                         NULL /* left source */,
+                                         right_src,
+                                         copyfrom_src,
+                                         dir_baton,
+                                         eb->processor,
+                                         scratch_pool, scratch_pool));
 
-      SVN_ERR(svn_wc__internal_translated_file(
-              &translated, local_abspath, db, local_abspath,
-              SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
-              eb->cancel_func, eb->cancel_baton,
-              scratch_pool, scratch_pool));
-
-      SVN_ERR(eb->callbacks->file_added(NULL, NULL, NULL, path,
-                                        (! eb->show_copies_as_adds &&
-                                         eb->use_git_diff_format &&
-                                         status != svn_wc__db_status_added) ?
-                                          textbase : empty_file,
-                                        translated,
-                                        0, revision,
-                                        NULL,
-                                        working_mimetype,
-                                        original_repos_relpath,
-                                        SVN_INVALID_REVNUM, propchanges,
-                                        baseprops, eb->callback_baton,
-                                        scratch_pool));
+      if (!skip)
+        {
+          apr_hash_t *right_props;
+          apr_hash_t *copyfrom_props = NULL;
+
+          /* Get svn:mime-type from ACTUAL props of PATH. */
+          SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
+                                        scratch_pool, scratch_pool));
+
+          if (copyfrom_src)
+            {
+              SVN_ERR(svn_wc__db_read_pristine_props(&copyfrom_props,
+                                                     db, local_abspath,
+                                                     scratch_pool,
+                                                     scratch_pool));
+            }
+
+          SVN_ERR(svn_wc__internal_translated_file(&translated, local_abspath,
+                                                   db, local_abspath,
+                                                   SVN_WC_TRANSLATE_TO_NF
+                                             | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
+                                                   eb->cancel_func,
+                                                   eb->cancel_baton,
+                                                   scratch_pool,
+                                                   scratch_pool));
+
+          SVN_ERR(eb->processor->file_added(path,
+                                            copyfrom_src,
+                                            right_src,
+                                            copyfrom_src
+                                                ? textbase
+                                                : NULL,
+                                            translated,
+                                            copyfrom_props,
+                                            right_props,
+                                            file_baton,
+                                            eb->processor,
+                                            scratch_pool));
+        }
     }
   else
     {
       const char *translated = NULL;
-      apr_hash_t *baseprops;
-      const char *base_mimetype;
-      const char *working_mimetype;
-      apr_hash_t *workingprops;
+      apr_hash_t *left_props;
+      apr_hash_t *right_props;
       apr_array_header_t *propchanges;
       svn_boolean_t modified;
+      void *file_baton = NULL;
+      svn_boolean_t skip = FALSE;
+      svn_diff_source_t *left_src = svn_diff__source_create(revision,
+                                                            scratch_pool);
+      svn_diff_source_t *right_src = svn_diff__source_create(
+                                                    SVN_INVALID_REVNUM,
+                                                    scratch_pool);
+
+      SVN_ERR(eb->processor->file_opened(&file_baton, &skip,
+                                         path,
+                                         left_src,
+                                         right_src,
+                                         NULL,
+                                         dir_baton,
+                                         eb->processor,
+                                         scratch_pool, scratch_pool));
+
+      if (skip)
+        return SVN_NO_ERROR;
 
       /* Here we deal with showing pure modifications. */
       SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, local_abspath,
@@ -721,7 +773,7 @@ file_diff(struct edit_baton *eb,
           /* We don't want the normal pristine properties (which are
              from the WORKING tree). We want the pristines associated
              with the BASE tree, which are saved as "revert" props.  */
-          SVN_ERR(svn_wc__db_base_get_props(&baseprops,
+          SVN_ERR(svn_wc__db_base_get_props(&left_props,
                                             db, local_abspath,
                                             scratch_pool, scratch_pool));
         }
@@ -733,40 +785,63 @@ file_diff(struct edit_baton *eb,
                          || status == svn_wc__db_status_copied
                          || status == svn_wc__db_status_moved_here);
 
-          SVN_ERR(svn_wc__db_read_pristine_props(&baseprops, db, local_abspath,
+          SVN_ERR(svn_wc__db_read_pristine_props(&left_props, db, local_abspath,
                                                  scratch_pool, scratch_pool));
 
           /* baseprops will be NULL for added nodes */
-          if (!baseprops)
-            baseprops = apr_hash_make(scratch_pool);
+          if (!left_props)
+            left_props = apr_hash_make(scratch_pool);
         }
-      base_mimetype = get_prop_mimetype(baseprops);
 
-      SVN_ERR(svn_wc__get_actual_props(&workingprops, db, local_abspath,
+      SVN_ERR(svn_wc__get_actual_props(&right_props, db, local_abspath,
                                        scratch_pool, scratch_pool));
-      working_mimetype = get_prop_mimetype(workingprops);
 
-      SVN_ERR(svn_prop_diffs(&propchanges, workingprops, baseprops, scratch_pool));
+      SVN_ERR(svn_prop_diffs(&propchanges, right_props, left_props,
+                             scratch_pool));
 
       if (modified || propchanges->nelts > 0)
         {
-          SVN_ERR(eb->callbacks->file_changed(NULL, NULL, NULL,
-                                              path,
-                                              modified ? textbase : NULL,
+          SVN_ERR(eb->processor->file_changed(path,
+                                              left_src,
+                                              right_src,
+                                              textbase,
                                               translated,
-                                              revision,
-                                              SVN_INVALID_REVNUM,
-                                              base_mimetype,
-                                              working_mimetype,
-                                              propchanges, baseprops,
-                                              eb->callback_baton,
+                                              left_props,
+                                              right_props,
+                                              modified,
+                                              propchanges,
+                                              file_baton,
+                                              eb->processor,
                                               scratch_pool));
         }
+      else
+        SVN_ERR(eb->processor->file_closed(path,
+                                           left_src,
+                                           right_src,
+                                           file_baton,
+                                           eb->processor,
+                                           scratch_pool));
     }
 
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+ensure_local_info(struct dir_baton_t *db,
+                  apr_pool_t *scratch_pool)
+{
+  apr_hash_t *conflicts;
+
+  if (db->local_info)
+    return SVN_NO_ERROR;
+
+  SVN_ERR(svn_wc__db_read_children_info(&db->local_info, &conflicts,
+                                        db->eb->db, db->local_abspath,
+                                        db->pool, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* Called when the directory is closed to compare any elements that have
  * not yet been compared.  This identifies local, working copy only
  * changes.  At this stage we are dealing with files/directories that do
@@ -775,21 +850,27 @@ file_diff(struct edit_baton *eb,
  * DIR_BATON is the baton for the directory.
  */
 static svn_error_t *
-walk_local_nodes_diff(struct edit_baton *eb,
+walk_local_nodes_diff(struct edit_baton_t *eb,
                       const char *local_abspath,
                       const char *path,
                       svn_depth_t depth,
                       apr_hash_t *compared,
+                      void *parent_baton,
                       apr_pool_t *scratch_pool)
 {
   svn_wc__db_t *db = eb->db;
-  const apr_array_header_t *children;
-  int i;
   svn_boolean_t in_anchor_not_target;
   apr_pool_t *iterpool;
+  void *dir_baton = NULL;
+  svn_boolean_t skip = FALSE;
+  svn_boolean_t skip_children = FALSE;
+  svn_revnum_t revision;
+  svn_boolean_t props_mod;
+  svn_diff_source_t *left_src;
+  svn_diff_source_t *right_src;
 
   /* Everything we do below is useless if we are comparing to BASE. */
-  if (eb->use_text_base)
+  if (eb->diff_pristine)
     return SVN_NO_ERROR;
 
   /* Determine if this is the anchor directory if the anchor is different
@@ -798,230 +879,307 @@ walk_local_nodes_diff(struct edit_baton 
      skipped. */
   in_anchor_not_target = ((*path == '\0') && (*eb->target != '\0'));
 
-  /* Check for local property mods on this directory, if we haven't
-     already reported them and we aren't changelist-filted.
-     ### it should be noted that we do not currently allow directories
-     ### to be part of changelists, so if a changelist is provided, the
-     ### changelist check will always fail. */
-  if (svn_wc__internal_changelist_match(db, local_abspath,
-                                        eb->changelist_hash, scratch_pool)
-      && (! in_anchor_not_target)
-      && (!compared || ! apr_hash_get(compared, path, 0)))
-    {
-      svn_boolean_t modified;
-
-      SVN_ERR(svn_wc__props_modified(&modified, db, local_abspath,
-                                     scratch_pool));
-      if (modified)
-        {
-          apr_array_header_t *propchanges;
-          apr_hash_t *baseprops;
-
-          SVN_ERR(svn_wc__internal_propdiff(&propchanges, &baseprops,
-                                            db, local_abspath,
-                                            scratch_pool, scratch_pool));
-
-          SVN_ERR(eb->callbacks->dir_props_changed(NULL, NULL,
-                                                   path, FALSE /* ### ? */,
-                                                   propchanges, baseprops,
-                                                   eb->callback_baton,
-                                                   scratch_pool));
-        }
-    }
-
-  if (depth == svn_depth_empty && !in_anchor_not_target)
-    return SVN_NO_ERROR;
-
   iterpool = svn_pool_create(scratch_pool);
 
-  SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
-                                   scratch_pool, iterpool));
-
-  for (i = 0; i < children->nelts; i++)
-    {
-      const char *name = APR_ARRAY_IDX(children, i, const char*);
-      const char *child_abspath, *child_path;
-      svn_wc__db_status_t status;
-      svn_kind_t kind;
-
-      svn_pool_clear(iterpool);
-
-      if (eb->cancel_func)
-        SVN_ERR(eb->cancel_func(eb->cancel_baton));
-
-      /* In the anchor directory, if the anchor is not the target then all
-         entries other than the target should not be diff'd. Running diff
-         on one file in a directory should not diff other files in that
-         directory. */
-      if (in_anchor_not_target && strcmp(eb->target, name))
-        continue;
-
-      child_abspath = svn_dirent_join(local_abspath, name, iterpool);
-
-      SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
-                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                   db, child_abspath,
-                                   iterpool, iterpool));
-
-      if (status == svn_wc__db_status_not_present
-          || status == svn_wc__db_status_excluded
-          || status == svn_wc__db_status_server_excluded)
-        continue;
+  SVN_ERR(svn_wc__db_read_info(NULL, NULL, &revision, NULL, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                               NULL, &props_mod, NULL, NULL, NULL,
+                               db, local_abspath, scratch_pool, scratch_pool));
 
-      child_path = svn_relpath_join(path, name, iterpool);
+  left_src = svn_diff__source_create(revision, scratch_pool);
+  right_src = svn_diff__source_create(0, scratch_pool);
 
-      /* Skip this node if it is in the list of nodes already diff'd. */
-      if (compared && apr_hash_get(compared, child_path, APR_HASH_KEY_STRING))
-        continue;
+  if (compared)
+    dir_baton = parent_baton;
+  else if (!in_anchor_not_target)
+    SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children,
+                                      path,
+                                      left_src,
+                                      right_src,
+                                      NULL /* copyfrom_src */,
+                                      parent_baton,
+                                      eb->processor,
+                                      scratch_pool, scratch_pool));
+
+
+  if (!skip_children && depth != svn_depth_empty)
+    {
+      const apr_array_header_t *children;
+      int i;
+      SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
+                                       scratch_pool, iterpool));
 
-      switch (kind)
+      for (i = 0; i < children->nelts; i++)
         {
-        case svn_kind_file:
-        case svn_kind_symlink:
-          SVN_ERR(file_diff(eb, child_abspath, child_path, iterpool));
-          break;
+          const char *name = APR_ARRAY_IDX(children, i, const char*);
+          const char *child_abspath, *child_path;
+          svn_wc__db_status_t status;
+          svn_kind_t kind;
+
+          svn_pool_clear(iterpool);
+
+          if (eb->cancel_func)
+            SVN_ERR(eb->cancel_func(eb->cancel_baton));
+
+          /* In the anchor directory, if the anchor is not the target then all
+             entries other than the target should not be diff'd. Running diff
+             on one file in a directory should not diff other files in that
+             directory. */
+          if (in_anchor_not_target && strcmp(eb->target, name))
+            continue;
+
+          if (compared && svn_hash_gets(compared, name))
+            continue;
+
+          child_abspath = svn_dirent_join(local_abspath, name, iterpool);
+
+          SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
+                                       NULL, NULL, NULL, NULL, NULL, NULL,
+                                       NULL, NULL, NULL, NULL, NULL, NULL,
+                                       NULL, NULL, NULL, NULL, NULL, NULL,
+                                       NULL, NULL, NULL,
+                                       db, child_abspath,
+                                       iterpool, iterpool));
+
+          if (status == svn_wc__db_status_not_present
+              || status == svn_wc__db_status_excluded
+              || status == svn_wc__db_status_server_excluded)
+            continue;
+
+          child_path = svn_relpath_join(path, name, iterpool);
+
+          /* Skip this node if it is in the list of nodes already diff'd. */
+          if (compared && apr_hash_get(compared, child_path, APR_HASH_KEY_STRING))
+            continue;
 
-        case svn_kind_dir:
-          /* ### TODO: Don't know how to do replaced dirs. How do I get
-             information about what is being replaced? If it was a
-             directory then the directory elements are also going to be
-             deleted. We need to show deletion diffs for these
-             files. If it was a file we need to show a deletion diff
-             for that file. */
-
-          /* Check the subdir if in the anchor (the subdir is the target), or
-             if recursive */
-          if (in_anchor_not_target
-              || (depth > svn_depth_files)
-              || (depth == svn_depth_unknown))
+          switch (kind)
             {
-              svn_depth_t depth_below_here = depth;
+            case svn_kind_file:
+            case svn_kind_symlink:
+              SVN_ERR(file_diff(eb, child_abspath, child_path, dir_baton,
+                                iterpool));
+              break;
+
+            case svn_kind_dir:
+              /* ### TODO: Don't know how to do replaced dirs. How do I get
+                 information about what is being replaced? If it was a
+                 directory then the directory elements are also going to be
+                 deleted. We need to show deletion diffs for these
+                 files. If it was a file we need to show a deletion diff
+                 for that file. */
+
+              /* Check the subdir if in the anchor (the subdir is the target),
+                 or if recursive */
+              if (in_anchor_not_target
+                  || (depth > svn_depth_files)
+                  || (depth == svn_depth_unknown))
+                {
+                  svn_depth_t depth_below_here = depth;
+
+                  if (depth_below_here == svn_depth_immediates)
+                    depth_below_here = svn_depth_empty;
+
+                  SVN_ERR(walk_local_nodes_diff(eb,
+                                                child_abspath,
+                                                child_path,
+                                                depth_below_here,
+                                                NULL /* compared */,
+                                                dir_baton,
+                                                iterpool));
+                }
+              break;
+
+            default:
+              break;
+          }
+        }
+    }
 
-              if (depth_below_here == svn_depth_immediates)
-                depth_below_here = svn_depth_empty;
+  if (compared)
+    return SVN_NO_ERROR;
 
-              SVN_ERR(walk_local_nodes_diff(eb,
-                                            child_abspath,
-                                            child_path,
-                                            depth_below_here,
-                                            NULL,
-                                            iterpool));
-            }
-          break;
+    /* Check for local property mods on this directory, if we haven't
+     already reported them and we aren't changelist-filted.
+     ### it should be noted that we do not currently allow directories
+     ### to be part of changelists, so if a changelist is provided, the
+     ### changelist check will always fail. */
+  if (! eb->changelist_hash
+      && ! in_anchor_not_target
+      && (!compared || ! svn_hash_gets(compared, ""))
+      && props_mod
+      && ! skip)
+    {
+      apr_array_header_t *propchanges;
+      apr_hash_t *left_props;
+      apr_hash_t *right_props;
 
-        default:
-          break;
-        }
-    }
+      SVN_ERR(svn_wc__internal_propdiff(&propchanges, &left_props,
+                                        db, local_abspath,
+                                        scratch_pool, scratch_pool));
+
+      right_props = svn_prop__patch(left_props, propchanges, scratch_pool);
+
+      SVN_ERR(eb->processor->dir_changed(path,
+                                         left_src,
+                                         right_src,
+                                         left_props,
+                                         right_props,
+                                         propchanges,
+                                         dir_baton,
+                                         eb->processor,
+                                         scratch_pool));
+    }
+  else if (! skip)
+    SVN_ERR(eb->processor->dir_closed(path,
+                                      left_src,
+                                      right_src,
+                                      dir_baton,
+                                      eb->processor,
+                                      scratch_pool));
 
   svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
 
-/* Report an existing file in the working copy (either in BASE or WORKING)
- * as having been added.
- *
- * DIR_BATON is the parent directory baton, ADM_ACCESS/PATH is the path
- * to the file to be compared.
- *
- * Do all allocation in POOL.
+/* Report the local version of a file in the working copy as added.
+ * This file can be in either WORKING or BASE, as for the repository
+ * it does not exist.
  */
 static svn_error_t *
-report_wc_file_as_added(struct edit_baton *eb,
-                        const char *local_abspath,
-                        const char *path,
-                        apr_pool_t *scratch_pool)
+report_local_only_file(struct edit_baton_t *eb,
+                       const char *local_abspath,
+                       const char *path,
+                       void *parent_baton,
+                       apr_pool_t *scratch_pool)
 {
   svn_wc__db_t *db = eb->db;
-  apr_hash_t *emptyprops;
-  const char *mimetype;
-  apr_hash_t *wcprops = NULL;
-  apr_array_header_t *propchanges;
-  const char *empty_file;
-  const char *source_file;
-  const char *translated_file;
+  svn_diff_source_t *right_src;
+  svn_diff_source_t *copyfrom_src = NULL;
   svn_wc__db_status_t status;
+  svn_kind_t kind;
+  const svn_checksum_t *checksum;
+  const char *original_repos_relpath;
+  svn_revnum_t original_revision;
+  const char *changelist;
+  svn_boolean_t had_props;
+  svn_boolean_t props_mod;
+  apr_hash_t *pristine_props;
+  apr_hash_t *right_props = NULL;
+  const char *pristine_file;
+  const char *translated_file;
   svn_revnum_t revision;
+  void *file_baton = NULL;
+  svn_boolean_t skip = FALSE;
+  svn_boolean_t file_mod = TRUE;
+
+  SVN_ERR(svn_wc__db_read_info(&status, &kind, &revision, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL, &checksum, NULL,
+                               &original_repos_relpath, NULL, NULL,
+                               &original_revision, NULL, NULL, NULL,
+                               &changelist, NULL, NULL, &had_props,
+                               &props_mod, NULL, NULL, NULL,
+                               db, local_abspath,
+                               scratch_pool, scratch_pool));
 
-  /* If this entry is filtered by changelist specification, do nothing. */
-  if (! svn_wc__internal_changelist_match(db, local_abspath,
-                                          eb->changelist_hash, scratch_pool))
+  if (changelist && eb->changelist_hash
+      && !svn_hash_gets(eb->changelist_hash, changelist))
     return SVN_NO_ERROR;
 
-  SVN_ERR(get_empty_file(eb, &empty_file));
+  if (status == svn_wc__db_status_deleted)
+    {
+      assert(eb->diff_pristine);
 
-  SVN_ERR(svn_wc__db_read_info(&status, NULL, &revision, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               db, local_abspath,
-                               scratch_pool, scratch_pool));
+      SVN_ERR(svn_wc__db_read_pristine_info(&status, &kind, NULL, NULL, NULL,
+                                            NULL, &checksum, NULL, &had_props,
+                                            &pristine_props,
+                                            db, local_abspath,
+                                            scratch_pool, scratch_pool));
+      props_mod = FALSE;
+    }
+  else if (!had_props)
+    pristine_props = apr_hash_make(scratch_pool);
+  else
+    SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props,
+                                           db, local_abspath,
+                                           scratch_pool, scratch_pool));
 
-  if (status == svn_wc__db_status_added)
-    SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
-                                     NULL, NULL, NULL, NULL, NULL, db,
-                                     local_abspath, scratch_pool, scratch_pool));
-
-  /* We can't show additions for files that don't exist. */
-  SVN_ERR_ASSERT(status != svn_wc__db_status_deleted || eb->use_text_base);
-
-  /* If the file was added *with history*, then we don't want to
-     see a comparison to the empty file;  we want the usual working
-     vs. text-base comparison. */
-  if (status == svn_wc__db_status_copied ||
-      status == svn_wc__db_status_moved_here)
-    {
-      /* Don't show anything if we're comparing to BASE, since by
-         definition there can't be any local modifications. */
-      if (eb->use_text_base)
-        return SVN_NO_ERROR;
+  assert(kind == svn_kind_file);
 
-      /* Otherwise show just the local modifications. */
-      return file_diff(eb, local_abspath, path, scratch_pool);
+  if (original_repos_relpath)
+    {
+      copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
+      copyfrom_src->repos_relpath = original_repos_relpath;
+      revision = original_revision;
     }
 
-  emptyprops = apr_hash_make(scratch_pool);
-
-  if (eb->use_text_base)
-    SVN_ERR(svn_wc__db_read_pristine_props(&wcprops, db, local_abspath,
-                                           scratch_pool, scratch_pool));
+  if (props_mod || !SVN_IS_VALID_REVNUM(revision))
+    right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
   else
-    SVN_ERR(svn_wc__get_actual_props(&wcprops, db, local_abspath,
+    {
+      if (eb->diff_pristine)
+        file_mod = FALSE;
+      else
+        SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath,
+                                                 FALSE, scratch_pool));
+
+      if (!file_mod)
+        right_src = svn_diff__source_create(revision, scratch_pool);
+      else
+        right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
+    }
+
+  SVN_ERR(eb->processor->file_opened(&file_baton, &skip,
+                                     path,
+                                     NULL /* left_source */,
+                                     right_src,
+                                     copyfrom_src,
+                                     parent_baton,
+                                     eb->processor,
                                      scratch_pool, scratch_pool));
-  mimetype = get_prop_mimetype(wcprops);
 
-  SVN_ERR(svn_prop_diffs(&propchanges,
-                         wcprops, emptyprops, scratch_pool));
+  if (skip)
+    return SVN_NO_ERROR;
+
+  if (props_mod && !eb->diff_pristine)
+    SVN_ERR(svn_wc__db_read_props(&right_props, db, local_abspath,
+                                  scratch_pool, scratch_pool));
+  else
+    right_props = svn_prop_hash_dup(pristine_props, scratch_pool);
 
+  if (checksum)
+    SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, eb->anchor_abspath,
+                                         checksum, scratch_pool, scratch_pool));
+  else
+    pristine_file = NULL;
 
-  if (eb->use_text_base)
+  if (eb->diff_pristine)
     {
-      SVN_ERR(get_pristine_file(&source_file, db, local_abspath,
-                                FALSE, scratch_pool, scratch_pool));
-      translated_file = source_file; /* No translation needed */
+      translated_file = pristine_file; /* No translation needed */
     }
   else
     {
-      source_file = local_abspath;
-
       SVN_ERR(svn_wc__internal_translated_file(
-           &translated_file, source_file, db, local_abspath,
+           &translated_file, local_abspath, db, local_abspath,
            SVN_WC_TRANSLATE_TO_NF | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
            eb->cancel_func, eb->cancel_baton,
            scratch_pool, scratch_pool));
     }
 
-  SVN_ERR(eb->callbacks->file_added(NULL, NULL, NULL,
-                                    path,
-                                    empty_file, translated_file,
-                                    0, revision,
-                                    NULL, mimetype,
-                                    NULL, SVN_INVALID_REVNUM,
-                                    propchanges, emptyprops,
-                                    eb->callback_baton,
+  SVN_ERR(eb->processor->file_added(path,
+                                    copyfrom_src,
+                                    right_src,
+                                    copyfrom_src
+                                      ? pristine_file
+                                      : NULL,
+                                    translated_file,
+                                    copyfrom_src
+                                      ? pristine_props
+                                      : NULL,
+                                    right_props,
+                                    file_baton,
+                                    eb->processor,
                                     scratch_pool));
 
   return SVN_NO_ERROR;
@@ -1036,50 +1194,36 @@ report_wc_file_as_added(struct edit_bato
  * Do all allocation in POOL.
  */
 static svn_error_t *
-report_wc_directory_as_added(struct edit_baton *eb,
-                             const char *local_abspath,
-                             const char *path,
-                             svn_depth_t depth,
-                             apr_pool_t *scratch_pool)
+report_local_only_dir(struct edit_baton_t *eb,
+                      const char *local_abspath,
+                      const char *path,
+                      svn_depth_t depth,
+                      void *parent_baton,
+                      apr_pool_t *scratch_pool)
 {
   svn_wc__db_t *db = eb->db;
-  apr_hash_t *emptyprops, *wcprops = NULL;
-  apr_array_header_t *propchanges;
   const apr_array_header_t *children;
   int i;
   apr_pool_t *iterpool;
-
-  emptyprops = apr_hash_make(scratch_pool);
-
-  /* If this directory passes changelist filtering, get its BASE or
-     WORKING properties, as appropriate, and simulate their
-     addition.
-     ### it should be noted that we do not currently allow directories
-     ### to be part of changelists, so if a changelist is provided, this
-     ### check will always fail. */
-  if (svn_wc__internal_changelist_match(db, local_abspath,
-                                        eb->changelist_hash, scratch_pool))
-    {
-      if (eb->use_text_base)
-        SVN_ERR(svn_wc__db_read_pristine_props(&wcprops, db, local_abspath,
-                                               scratch_pool, scratch_pool));
-      else
-        SVN_ERR(svn_wc__get_actual_props(&wcprops, db, local_abspath,
-                                         scratch_pool, scratch_pool));
-
-      SVN_ERR(svn_prop_diffs(&propchanges, wcprops, emptyprops, scratch_pool));
-
-      if (propchanges->nelts > 0)
-        SVN_ERR(eb->callbacks->dir_props_changed(NULL, NULL,
-                                                 path, TRUE,
-                                                 propchanges, emptyprops,
-                                                 eb->callback_baton,
-                                                 scratch_pool));
-    }
+  void *pdb = NULL;
+  svn_boolean_t skip = FALSE;
+  svn_boolean_t skip_children = FALSE;
+  svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM,
+                                                         scratch_pool);
 
   /* Report the addition of the directory's contents. */
   iterpool = svn_pool_create(scratch_pool);
 
+  SVN_ERR(eb->processor->dir_opened(&pdb, &skip, &skip_children,
+                                    path,
+                                    NULL,
+                                    right_src,
+                                    NULL /* copyfrom_src */,
+                                    parent_baton,
+                                    eb->processor,
+                                    scratch_pool, scratch_pool));
+
+
   SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
                                    scratch_pool, iterpool));
 
@@ -1112,7 +1256,7 @@ report_wc_directory_as_added(struct edit
 
       /* If comparing against WORKING, skip entries that are
          schedule-deleted - they don't really exist. */
-      if (!eb->use_text_base && status == svn_wc__db_status_deleted)
+      if (!eb->diff_pristine && status == svn_wc__db_status_deleted)
         continue;
 
       child_path = svn_relpath_join(path, name, iterpool);
@@ -1121,8 +1265,8 @@ report_wc_directory_as_added(struct edit
         {
         case svn_kind_file:
         case svn_kind_symlink:
-          SVN_ERR(report_wc_file_as_added(eb, child_abspath, child_path,
-                                          iterpool));
+          SVN_ERR(report_local_only_file(eb, child_abspath, child_path,
+                                         pdb, iterpool));
           break;
 
         case svn_kind_dir:
@@ -1133,11 +1277,12 @@ report_wc_directory_as_added(struct edit
               if (depth_below_here == svn_depth_immediates)
                 depth_below_here = svn_depth_empty;
 
-              SVN_ERR(report_wc_directory_as_added(eb,
-                                                   child_abspath,
-                                                   child_path,
-                                                   depth_below_here,
-                                                   iterpool));
+              SVN_ERR(report_local_only_dir(eb,
+                                            child_abspath,
+                                            child_path,
+                                            depth_below_here,
+                                            pdb,
+                                            iterpool));
             }
           break;
 
@@ -1146,11 +1291,109 @@ report_wc_directory_as_added(struct edit
         }
     }
 
+  if (!skip)
+    {
+      apr_hash_t *right_props;
+      if (eb->diff_pristine)
+        SVN_ERR(svn_wc__db_read_pristine_props(&right_props, db, local_abspath,
+                                               scratch_pool, scratch_pool));
+      else
+        SVN_ERR(svn_wc__get_actual_props(&right_props, db, local_abspath,
+                                         scratch_pool, scratch_pool));
+
+      SVN_ERR(eb->processor->dir_added(path,
+                                       NULL /* copyfrom_src */,
+                                       right_src,
+                                       NULL,
+                                       right_props,
+                                       pdb,
+                                       eb->processor,
+                                       iterpool));
+    }
   svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
 
+/* Ensures that local changes are reported to pb->eb->processor if there
+   are any. */
+static svn_error_t *
+ensure_local_only_handled(struct dir_baton_t *pb,
+                          const char *name,
+                          apr_pool_t *scratch_pool)
+{
+  struct edit_baton_t *eb = pb->eb;
+  const struct svn_wc__db_info_t *info;
+  svn_boolean_t repos_delete = (pb->deletes
+                                && svn_hash_gets(pb->deletes, name));
+
+  assert(!strchr(name, '/'));
+  assert(!pb->added || eb->ignore_ancestry);
+
+  if (svn_hash_gets(pb->compared, name))
+    return SVN_NO_ERROR;
+
+  svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, name), "");
+
+  SVN_ERR(ensure_local_info(pb, scratch_pool));
+
+  info = svn_hash_gets(pb->local_info, name);
+
+  if (info == NULL)
+    return SVN_NO_ERROR;
+
+  switch (info->status)
+    {
+      case svn_wc__db_status_not_present:
+      case svn_wc__db_status_excluded:
+      case svn_wc__db_status_server_excluded:
+      case svn_wc__db_status_incomplete:
+        return SVN_NO_ERROR; /* Not local only */
+
+      case svn_wc__db_status_normal:
+        if (!repos_delete)
+          return SVN_NO_ERROR; /* Local and remote */
+        svn_hash_sets(pb->deletes, name, NULL);
+        break;
+
+      case svn_wc__db_status_deleted:
+        if (!(eb->diff_pristine && repos_delete))
+          return SVN_NO_ERROR;
+        break;
+
+      case svn_wc__db_status_added:
+      default:
+        break;
+    }
+
+  if (info->kind == svn_kind_dir)
+    {
+      svn_depth_t depth ;
+
+      if (pb->depth == svn_depth_infinity || pb->depth == svn_depth_unknown)
+        depth = pb->depth;
+      else
+        depth = svn_depth_empty;
+
+      SVN_ERR(report_local_only_dir(
+                      eb,
+                      svn_dirent_join(pb->local_abspath, name, scratch_pool),
+                      svn_relpath_join(pb->relpath, name, scratch_pool),
+                      repos_delete ? svn_depth_infinity : depth,
+                      pb->pdb,
+                      scratch_pool));
+    }
+  else
+    SVN_ERR(report_local_only_file(
+                      eb,
+                      svn_dirent_join(pb->local_abspath, name, scratch_pool),
+                      svn_relpath_join(pb->relpath, name, scratch_pool),
+                      pb->pdb,
+                      scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 
 /* An svn_delta_editor_t function. */
 static svn_error_t *
@@ -1158,7 +1401,7 @@ set_target_revision(void *edit_baton,
                     svn_revnum_t target_revision,
                     apr_pool_t *pool)
 {
-  struct edit_baton *eb = edit_baton;
+  struct edit_baton_t *eb = edit_baton;
   eb->revnum = target_revision;
 
   return SVN_NO_ERROR;
@@ -1171,8 +1414,8 @@ open_root(void *edit_baton,
           apr_pool_t *dir_pool,
           void **root_baton)
 {
-  struct edit_baton *eb = edit_baton;
-  struct dir_baton *db;
+  struct edit_baton_t *eb = edit_baton;
+  struct dir_baton_t *db;
 
   eb->root_opened = TRUE;
   db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool);
@@ -1188,85 +1431,13 @@ delete_entry(const char *path,
              void *parent_baton,
              apr_pool_t *pool)
 {
-  struct dir_baton *pb = parent_baton;
-  struct edit_baton *eb = pb->eb;
-  svn_wc__db_t *db = eb->db;
-  const char *empty_file;
-  const char *name = svn_dirent_basename(path, NULL);
-  const char *local_abspath = svn_dirent_join(pb->local_abspath, name, pool);
-  svn_wc__db_status_t status;
-  svn_kind_t kind;
-
-  /* Mark this node as compared in the parent directory's baton. */
-  apr_hash_set(pb->compared, apr_pstrdup(pb->pool, path),
-               APR_HASH_KEY_STRING, "");
-
-  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               db, local_abspath, pool, pool));
-
-  /* If comparing against WORKING, skip nodes that are deleted
-     - they don't really exist. */
-  if (!eb->use_text_base && status == svn_wc__db_status_deleted)
-    return SVN_NO_ERROR;
-
-  SVN_ERR(get_empty_file(pb->eb, &empty_file));
-  switch (kind)
-    {
-    case svn_kind_file:
-    case svn_kind_symlink:
-      /* A delete is required to change working-copy into requested
-         revision, so diff should show this as an add. Thus compare
-         the empty file against the current working copy.  If
-         'reverse_order' is set, then show a deletion. */
-
-      if (eb->reverse_order)
-        {
-          /* Whenever showing a deletion, we show the text-base vanishing. */
-          /* ### This is wrong if we're diffing WORKING->repos. */
-          const char *textbase;
-
-          apr_hash_t *baseprops = NULL;
-          const char *base_mimetype;
-
-          SVN_ERR(get_pristine_file(&textbase, db, local_abspath,
-                                    eb->use_text_base, pool, pool));
-
-          SVN_ERR(svn_wc__db_read_pristine_props(&baseprops,
-                                                 eb->db, local_abspath,
-                                                 pool, pool));
-          base_mimetype = get_prop_mimetype(baseprops);
+  struct dir_baton_t *pb = parent_baton;
+  const char *name = svn_dirent_basename(path, pb->pool);
 
-          SVN_ERR(eb->callbacks->file_deleted(NULL, NULL, path,
-                                              textbase,
-                                              empty_file,
-                                              base_mimetype,
-                                              NULL,
-                                              baseprops,
-                                              eb->callback_baton,
-                                              pool));
-        }
-      else
-        {
-          /* Or normally, show the working file being added. */
-          SVN_ERR(report_wc_file_as_added(eb, local_abspath, path, pool));
-        }
-      break;
-    case svn_kind_dir:
-      /* A delete is required to change working-copy into requested
-         revision, so diff should show this as an add. */
-      SVN_ERR(report_wc_directory_as_added(eb,
-                                           local_abspath,
-                                           path,
-                                           svn_depth_infinity,
-                                           pool));
-
-    default:
-      break;
-    }
+  if (!pb->deletes)
+    pb->deletes = apr_hash_make(pb->pool);
 
+  svn_hash_sets(pb->deletes, name, "");
   return SVN_NO_ERROR;
 }
 
@@ -1279,8 +1450,9 @@ add_directory(const char *path,
               apr_pool_t *dir_pool,
               void **child_baton)
 {
-  struct dir_baton *pb = parent_baton;
-  struct dir_baton *db;
+  struct dir_baton_t *pb = parent_baton;
+  struct edit_baton_t *eb = pb->eb;
+  struct dir_baton_t *db;
   svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
                               ? svn_depth_empty : pb->depth;
 
@@ -1290,11 +1462,36 @@ add_directory(const char *path,
                       dir_pool);
   *child_baton = db;
 
+  if (!db->shadowed)
+    {
+      struct svn_wc__db_info_t *info;
+      SVN_ERR(ensure_local_info(pb, dir_pool));
+
+      info = svn_hash_gets(pb->local_info, db->name);
+
+      if (!info || info->kind != svn_kind_dir)
+        db->shadowed = TRUE;
+    }
+
+  if (eb->local_before_remote && !eb->ignore_ancestry && !db->shadowed)
+    SVN_ERR(ensure_local_only_handled(pb, db->name, dir_pool));
+
   /* Issue #3797: Don't add this filename to the parent directory's list of
      elements that have been compared, to show local additions via the local
      diff. The repository node is unrelated from the working copy version
      (similar to not-present in the working copy) */
 
+  db->left_src  = svn_diff__source_create(eb->revnum, db->pool);
+
+  SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
+                                    db->relpath,
+                                    db->left_src,
+                                    NULL /* right_source */,
+                                    NULL /* copyfrom src */,
+                                    pb->pdb,
+                                    eb->processor,
+                                    db->pool, db->pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -1306,8 +1503,9 @@ open_directory(const char *path,
                apr_pool_t *dir_pool,
                void **child_baton)
 {
-  struct dir_baton *pb = parent_baton;
-  struct dir_baton *db;
+  struct dir_baton_t *pb = parent_baton;
+  struct edit_baton_t *eb = pb->eb;
+  struct dir_baton_t *db;
   svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
                               ? svn_depth_empty : pb->depth;
 
@@ -1316,14 +1514,35 @@ open_directory(const char *path,
   db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool);
   *child_baton = db;
 
+  if (!db->shadowed)
+    {
+      struct svn_wc__db_info_t *info;
+      SVN_ERR(ensure_local_info(pb, dir_pool));
+
+      info = svn_hash_gets(pb->local_info, db->name);
+
+      if (!info || info->kind != svn_kind_dir)
+        db->shadowed = TRUE;
+    }
+
+  if (eb->local_before_remote && !eb->ignore_ancestry && !db->shadowed)
+    SVN_ERR(ensure_local_only_handled(pb, db->name, dir_pool));
+
+  db->left_src  = svn_diff__source_create(eb->revnum, db->pool);
+  db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
+
   /* Add this path to the parent directory's list of elements that
      have been compared. */
-  apr_hash_set(pb->compared, apr_pstrdup(pb->pool, db->path),
-               APR_HASH_KEY_STRING, "");
+  svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
 
-  SVN_ERR(db->eb->callbacks->dir_opened(NULL, NULL, NULL,
-                                        path, base_revision,
-                                        db->eb->callback_baton, dir_pool));
+  SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
+                                    db->relpath,
+                                    db->left_src,
+                                    db->right_src,
+                                    NULL /* copyfrom src */,
+                                    pb->pdb,
+                                    eb->processor,
+                                    db->pool, db->pool));
 
   return SVN_NO_ERROR;
 }
@@ -1337,9 +1556,40 @@ static svn_error_t *
 close_directory(void *dir_baton,
                 apr_pool_t *pool)
 {
-  struct dir_baton *db = dir_baton;
-  struct edit_baton *eb = db->eb;
+  struct dir_baton_t *db = dir_baton;
+  struct edit_baton_t *eb = db->eb;
   apr_pool_t *scratch_pool = db->pool;
+  svn_boolean_t reported_closed = FALSE;
+
+  if (db->deletes)
+    {
+      apr_hash_index_t *hi;
+      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+      for (hi = apr_hash_first(scratch_pool, db->deletes); hi;
+           hi = apr_hash_next(hi))
+        {
+          const char *name = svn__apr_hash_index_key(hi);
+
+          svn_pool_clear(iterpool);
+          SVN_ERR(ensure_local_only_handled(db, name, iterpool));
+        }
+
+      svn_pool_destroy(iterpool);
+    }
+
+  /* Report local modifications for this directory.  Skip added
+     directories since they can only contain added elements, all of
+     which have already been diff'd. */
+  if (!db->added)
+    SVN_ERR(walk_local_nodes_diff(eb,
+                                  db->local_abspath,
+                                  db->relpath,
+                                  db->depth,
+                                  db->compared,
+                                  NULL /* ### parent_baton */,
+                                  scratch_pool));
+
 
   /* Report the property changes on the directory itself, if necessary. */
   if (db->propchanges->nelts > 0)
@@ -1354,7 +1604,7 @@ close_directory(void *dir_baton,
         }
       else
         {
-          if (db->eb->use_text_base)
+          if (db->eb->diff_pristine)
             {
               SVN_ERR(svn_wc__db_read_pristine_props(&originalprops,
                                                      eb->db, db->local_abspath,
@@ -1374,7 +1624,8 @@ close_directory(void *dir_baton,
                                                 eb->db, db->local_abspath,
                                                 scratch_pool, scratch_pool));
 
-              repos_props = apply_propchanges(base_props, db->propchanges);
+              repos_props = svn_prop__patch(base_props, db->propchanges,
+                                            scratch_pool);
 
               /* Recalculate b->propchanges as the change between WORKING
                  and repos. */
@@ -1383,41 +1634,50 @@ close_directory(void *dir_baton,
             }
         }
 
-      if (!eb->reverse_order)
-        reverse_propchanges(originalprops, db->propchanges, db->pool);
-
-      SVN_ERR(eb->callbacks->dir_props_changed(NULL, NULL,
-                                               db->path,
-                                               db->added,
-                                               db->propchanges,
-                                               originalprops,
-                                               eb->callback_baton,
-                                               scratch_pool));
+      if (!db->added)
+        {
+          reverse_propchanges(originalprops, db->propchanges, db->pool);
 
-      /* Mark the properties of this directory as having already been
-         compared so that we know not to show any local modifications
-         later on. */
-      apr_hash_set(db->compared, db->path, 0, "");
+          SVN_ERR(eb->processor->dir_changed(db->relpath,
+                                             db->left_src,
+                                             db->right_src,
+                                             originalprops,
+                                             svn_prop__patch(originalprops,
+                                                             db->propchanges,
+                                                             scratch_pool),
+                                             db->propchanges,
+                                             db->pdb,
+                                             eb->processor,
+                                             scratch_pool));
+        }
+      else
+        {
+          SVN_ERR(eb->processor->dir_deleted(db->relpath,
+                                             db->left_src,
+                                             svn_prop__patch(originalprops,
+                                                             db->propchanges,
+                                                             scratch_pool),
+                                             db->pdb,
+                                             eb->processor,
+                                             scratch_pool));
+        }
+      reported_closed = TRUE;
     }
 
-  /* Report local modifications for this directory.  Skip added
-     directories since they can only contain added elements, all of
-     which have already been diff'd. */
-  if (!db->added)
-    SVN_ERR(walk_local_nodes_diff(eb,
-                                  db->local_abspath,
-                                  db->path,
-                                  db->depth,
-                                  db->compared,
-                                  scratch_pool));
-
   /* Mark this directory as compared in the parent directory's baton,
      unless this is the root of the comparison. */
-  SVN_ERR(db->eb->callbacks->dir_closed(NULL, NULL, NULL, db->path,
-                                        db->added, db->eb->callback_baton,
-                                        scratch_pool));
+  if (!reported_closed)
+    SVN_ERR(eb->processor->dir_closed(db->relpath,
+                                      db->left_src,
+                                      db->right_src,
+                                      db->pdb,
+                                      eb->processor,
+                                      scratch_pool));
 
-  svn_pool_destroy(db->pool); /* destroys scratch_pool */
+  if (db->parent_baton)
+    SVN_ERR(ensure_local_only_handled(db->parent_baton, db->name,
+                                      scratch_pool));
+  SVN_ERR(maybe_done(db)); /* destroys scratch_pool */
 
   return SVN_NO_ERROR;
 }
@@ -1431,19 +1691,43 @@ add_file(const char *path,
          apr_pool_t *file_pool,
          void **file_baton)
 {
-  struct dir_baton *pb = parent_baton;
-  struct file_baton *fb;
+  struct dir_baton_t *pb = parent_baton;
+  struct edit_baton_t *eb = pb->eb;
+  struct file_baton_t *fb;
 
   /* ### TODO: support copyfrom? */
 
   fb = make_file_baton(path, TRUE, pb, file_pool);
   *file_baton = fb;
 
+  if (!fb->shadowed)
+    {
+      struct svn_wc__db_info_t *info;
+      SVN_ERR(ensure_local_info(pb, file_pool));
+
+      info = svn_hash_gets(pb->local_info, fb->name);
+
+      if (!info || info->kind != svn_kind_file)
+        fb->shadowed = TRUE;
+    }
+
   /* Issue #3797: Don't add this filename to the parent directory's list of
      elements that have been compared, to show local additions via the local
      diff. The repository node is unrelated from the working copy version
      (similar to not-present in the working copy) */
 
+  fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
+  fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
+
+  SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
+                                     fb->relpath,
+                                     fb->left_src,
+                                     fb->right_src,
+                                     NULL /* copyfrom src */,
+                                     pb->pdb,
+                                     eb->processor,
+                                     fb->pool, fb->pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -1455,57 +1739,45 @@ open_file(const char *path,
           apr_pool_t *file_pool,
           void **file_baton)
 {
-  struct dir_baton *pb = parent_baton;
-  struct edit_baton *eb = pb->eb;
-  struct file_baton *fb;
+  struct dir_baton_t *pb = parent_baton;
+  struct edit_baton_t *eb = pb->eb;
+  struct file_baton_t *fb;
 
   fb = make_file_baton(path, FALSE, pb, file_pool);
   *file_baton = fb;
 
+  if (!fb->shadowed)
+    {
+      struct svn_wc__db_info_t *info;
+      SVN_ERR(ensure_local_info(pb, file_pool));
+
+      info = svn_hash_gets(pb->local_info, fb->name);
+
+      if (!info || info->kind != svn_kind_file)
+        fb->shadowed = TRUE;
+    }
+
   /* Add this filename to the parent directory's list of elements that
      have been compared. */
-  apr_hash_set(pb->compared, apr_pstrdup(pb->pool, path),
-               APR_HASH_KEY_STRING, "");
+  svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
 
   SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                                    NULL, NULL, NULL, &fb->base_checksum, NULL,
-                                   NULL, NULL, NULL, NULL,
+                                   NULL, NULL, &fb->base_props, NULL,
                                    eb->db, fb->local_abspath,
                                    fb->pool, fb->pool));
 
-  SVN_ERR(eb->callbacks->file_opened(NULL, NULL, fb->path, base_revision,
-                                     eb->callback_baton, fb->pool));
-
-  return SVN_NO_ERROR;
-}
-
-/* Baton for window_handler */
-struct window_handler_baton
-{
-  struct file_baton *fb;
-
-  /* APPLY_HANDLER/APPLY_BATON represent the delta applcation baton. */
-  svn_txdelta_window_handler_t apply_handler;
-  void *apply_baton;
-
-  unsigned char result_digest[APR_MD5_DIGESTSIZE];
-};
+  fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
+  fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
 
-/* Do the work of applying the text delta. */
-static svn_error_t *
-window_handler(svn_txdelta_window_t *window,
-               void *window_baton)
-{
-  struct window_handler_baton *whb = window_baton;
-  struct file_baton *fb = whb->fb;
-
-  SVN_ERR(whb->apply_handler(window, whb->apply_baton));
-
-  if (!window)
-    {
-      fb->result_checksum = svn_checksum__from_digest_md5(whb->result_digest,
-                                                          fb->pool);
-    }
+  SVN_ERR(eb->processor->file_opened(&fb->pfb, &fb->skip,
+                                     fb->relpath,
+                                     fb->left_src,
+                                     fb->right_src,
+                                     NULL /* copyfrom src */,
+                                     pb->pdb,
+                                     eb->processor,
+                                     fb->pool, fb->pool));
 
   return SVN_NO_ERROR;
 }
@@ -1513,22 +1785,60 @@ window_handler(svn_txdelta_window_t *win
 /* An svn_delta_editor_t function. */
 static svn_error_t *
 apply_textdelta(void *file_baton,
-                const char *base_checksum,
+                const char *base_checksum_hex,
                 apr_pool_t *pool,
                 svn_txdelta_window_handler_t *handler,
                 void **handler_baton)
 {
-  struct file_baton *fb = file_baton;
-  struct window_handler_baton *whb;
-  struct edit_baton *eb = fb->eb;
+  struct file_baton_t *fb = file_baton;
+  struct edit_baton_t *eb = fb->eb;
   svn_stream_t *source;
   svn_stream_t *temp_stream;
+  svn_checksum_t *repos_checksum = NULL;
 
-  if (fb->base_checksum)
-    SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
-                                     eb->db, fb->local_abspath,
-                                     fb->base_checksum,
-                                     pool, pool));
+  if (base_checksum_hex && fb->base_checksum)
+    {
+      const svn_checksum_t *base_md5;
+      SVN_ERR(svn_checksum_parse_hex(&repos_checksum, svn_checksum_md5,
+                                     base_checksum_hex, pool));
+
+      SVN_ERR(svn_wc__db_pristine_get_md5(&base_md5,
+                                          eb->db, eb->anchor_abspath,
+                                          fb->base_checksum,
+                                          pool, pool));
+
+      if (! svn_checksum_match(repos_checksum, base_md5))
+        {
+          /* ### I expect that there are some bad drivers out there
+             ### that used to give bad results. We could look in
+             ### working to see if the expected checksum matches and
+             ### then return the pristine of that... But that only moves
+             ### the problem */
+
+          /* If needed: compare checksum obtained via md5 of working.
+             And if they match set fb->base_checksum and fb->base_props */
+
+          return svn_checksum_mismatch_err(
+                        base_md5,
+                        repos_checksum,
+                        pool,
+                        _("Checksum mismatch for '%s'"),
+                        svn_dirent_local_style(fb->local_abspath,
+                                               pool));
+        }
+
+      SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
+                                       eb->db, fb->local_abspath,
+                                       fb->base_checksum,
+                                       pool, pool));
+    }
+  else if (fb->base_checksum)
+    {
+      SVN_ERR(svn_wc__db_pristine_read(&source, NULL,
+                                       eb->db, fb->local_abspath,
+                                       fb->base_checksum,
+                                       pool, pool));
+    }
   else
     source = svn_stream_empty(pool);
 
@@ -1537,17 +1847,13 @@ apply_textdelta(void *file_baton,
                                  svn_io_file_del_on_pool_cleanup,
                                  fb->pool, fb->pool));
 
-  whb = apr_pcalloc(fb->pool, sizeof(*whb));
-  whb->fb = fb;
-
+  fb->got_textdelta = TRUE;
   svn_txdelta_apply(source, temp_stream,
-                    whb->result_digest,
-                    fb->path /* error_info */,
+                    fb->result_digest,
+                    fb->local_abspath /* error_info */,
                     fb->pool,
-                    &whb->apply_handler, &whb->apply_baton);
+                    handler, handler_baton);
 
-  *handler = window_handler;
-  *handler_baton = whb;
   return SVN_NO_ERROR;
 }
 
@@ -1562,23 +1868,18 @@ close_file(void *file_baton,
            const char *expected_md5_digest,
            apr_pool_t *pool)
 {
-  struct file_baton *fb = file_baton;
-  struct edit_baton *eb = fb->eb;
+  struct file_baton_t *fb = file_baton;
+  struct dir_baton_t *pb = fb->parent_baton;
+  struct edit_baton_t *eb = fb->eb;
   svn_wc__db_t *db = eb->db;
   apr_pool_t *scratch_pool = fb->pool;
   svn_wc__db_status_t status;
-  const char *empty_file;
-  svn_error_t *err;
-
-  /* The BASE information */
-  const svn_checksum_t *pristine_checksum;
-  const char *pristine_file;
-  apr_hash_t *pristine_props;
+  const char *original_repos_relpath;
+  svn_revnum_t original_revision;
 
   /* The repository information; constructed from BASE + Changes */
   const char *repos_file;
   apr_hash_t *repos_props;
-  const char *repos_mimetype;
   svn_boolean_t had_props, props_mod;
 
   /* The path to the wc file: either a pristine or actual. */
@@ -1591,151 +1892,179 @@ close_file(void *file_baton,
   if (expected_md5_digest != NULL)
     {
       svn_checksum_t *expected_checksum;
-      const svn_checksum_t *repos_checksum = fb->result_checksum;
+      const svn_checksum_t *result_checksum;
+
+      if (fb->got_textdelta)
+        result_checksum = svn_checksum__from_digest_md5(fb->result_digest,
+                                                        scratch_pool);
+      else
+        result_checksum = fb->base_checksum;
 
       SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
                                      expected_md5_digest, scratch_pool));
 
-      if (repos_checksum == NULL)
-        repos_checksum = fb->base_checksum;
-
-      if (repos_checksum->kind != svn_checksum_md5)
-        SVN_ERR(svn_wc__db_pristine_get_md5(&repos_checksum,
+      if (result_checksum->kind != svn_checksum_md5)
+        SVN_ERR(svn_wc__db_pristine_get_md5(&result_checksum,
                                             eb->db, fb->local_abspath,
-                                            repos_checksum,
+                                            result_checksum,
                                             scratch_pool, scratch_pool));
 
-      if (!svn_checksum_match(expected_checksum, repos_checksum))
+      if (!svn_checksum_match(expected_checksum, result_checksum))
         return svn_checksum_mismatch_err(
                             expected_checksum,
-                            repos_checksum,
+                            result_checksum,
                             pool,
                             _("Checksum mismatch for '%s'"),
                             svn_dirent_local_style(fb->local_abspath,
                                                    scratch_pool));
     }
 
-  err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
-                             NULL, NULL, NULL, &pristine_checksum, NULL, NULL,
-                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                             NULL, &had_props, &props_mod,
-                             NULL, NULL, NULL,
-                             db, fb->local_abspath,
-                             scratch_pool, scratch_pool);
-  if (fb->added
-      && err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
-    {
-      svn_error_clear(err);
-      status = svn_wc__db_status_not_present;
-      pristine_checksum = NULL;
-      had_props = FALSE;
-      props_mod = FALSE;
+  if (eb->local_before_remote
+      && (!eb->ignore_ancestry || fb->shadowed))
+    {
+      SVN_ERR(ensure_local_only_handled(pb, fb->name, scratch_pool));
     }
-  else
-    SVN_ERR(err);
 
-  SVN_ERR(get_empty_file(eb, &empty_file));
+  if ((fb->added && !eb->ignore_ancestry)
+      || fb->shadowed)
+    {
+      SVN_ERR(eb->processor->file_deleted(fb->relpath,
+                                          fb->left_src,
+                                          fb->temp_file_path,
+                                          svn_prop__patch(
+                                                apr_hash_make(scratch_pool),
+                                                fb->propchanges,
+                                                scratch_pool),
+                                          fb->pfb,
+                                          eb->processor,
+                                          scratch_pool));
+
+      svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, pb->name), "");
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL, NULL,
+                               &original_repos_relpath,
+                               NULL, NULL, &original_revision, NULL, NULL,
+                               NULL, NULL, NULL, NULL, &had_props,
+                               &props_mod, NULL, NULL, NULL,
+                               db, fb->local_abspath,
+                               scratch_pool, scratch_pool));
 
   if (fb->added)
     {
-      pristine_props = apr_hash_make(scratch_pool);
-      pristine_file = empty_file;
+      repos_props = svn_prop__patch(apr_hash_make(scratch_pool),
+                                    fb->propchanges, scratch_pool);
+      repos_file = fb->temp_file_path;
     }
   else
     {
-      if (status != svn_wc__db_status_normal)
-        SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL,
-                                         NULL, NULL, NULL, NULL,
-                                         &pristine_checksum,
-                                         NULL, NULL,
-                                         &had_props, NULL, NULL,
-                                         db, fb->local_abspath,
-                                         scratch_pool, scratch_pool));
-
-      SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
-                                           db, fb->local_abspath,
-                                           pristine_checksum,
-                                           scratch_pool, scratch_pool));
-
-      if (had_props)
-        SVN_ERR(svn_wc__db_base_get_props(&pristine_props,
-                                           db, fb->local_abspath,
-                                           scratch_pool, scratch_pool));
+      if (fb->temp_file_path)
+        repos_file = fb->temp_file_path;
       else
-        pristine_props = apr_hash_make(scratch_pool);
-    }
-
-  if (status == svn_wc__db_status_added)
-    SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
-                                     NULL, NULL, NULL, NULL, NULL, eb->db,
-                                     fb->local_abspath,
-                                     scratch_pool, scratch_pool));
+        SVN_ERR(svn_wc__db_pristine_get_path(&repos_file,
+                                             db, fb->local_abspath,
+                                             fb->base_checksum,
+                                             scratch_pool, scratch_pool));
 
-  repos_props = apply_propchanges(pristine_props, fb->propchanges);
-  repos_mimetype = get_prop_mimetype(repos_props);
-  repos_file = fb->temp_file_path ? fb->temp_file_path : pristine_file;
+      repos_props = svn_prop__patch(fb->base_props, fb->propchanges,
+                                    scratch_pool);
+    }
 

[... 514 lines stripped ...]