You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/03/06 12:10:05 UTC

svn commit: r1453290 [7/15] - in /subversion/branches/fsfs-format7: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ subversion/bindings/javahl/native/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ subversion/bindin...

Modified: subversion/branches/fsfs-format7/subversion/libsvn_wc/diff_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_wc/diff_editor.c?rev=1453290&r1=1453289&r2=1453290&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_wc/diff_editor.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_wc/diff_editor.c Wed Mar  6 11:10:01 2013
@@ -62,6 +62,7 @@
 #include "svn_dirent_uri.h"
 #include "svn_path.h"
 #include "svn_hash.h"
+#include "svn_sorts.h"
 
 #include "private/svn_subr_private.h"
 #include "private/svn_wc_private.h"
@@ -72,134 +73,10 @@
 #include "props.h"
 #include "adm_files.h"
 #include "translate.h"
+#include "diff.h"
 
 #include "svn_private_config.h"
 
-
-/*-------------------------------------------------------------------------*/
-/* A little helper function.
-
-   You see, when we ask the server to update us to a certain revision,
-   we construct the new fulltext, and then run
-
-         'diff <repos_fulltext> <working_fulltext>'
-
-   which is, of course, actually backwards from the repository's point
-   of view.  It thinks we want to move from working->repos.
-
-   So when the server sends property changes, they're effectively
-   backwards from what we want.  We don't want working->repos, but
-   repos->working.  So this little helper "reverses" the value in
-   BASEPROPS and PROPCHANGES before we pass them off to the
-   prop_changed() diff-callback.  */
-static void
-reverse_propchanges(apr_hash_t *baseprops,
-                    apr_array_header_t *propchanges,
-                    apr_pool_t *pool)
-{
-  int i;
-
-  /* ### todo: research lifetimes for property values below */
-
-  for (i = 0; i < propchanges->nelts; i++)
-    {
-      svn_prop_t *propchange
-        = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
-
-      const svn_string_t *original_value =
-        apr_hash_get(baseprops, propchange->name, APR_HASH_KEY_STRING);
-
-      if ((original_value == NULL) && (propchange->value != NULL))
-        {
-          /* found an addition.  make it look like a deletion. */
-          apr_hash_set(baseprops, propchange->name, APR_HASH_KEY_STRING,
-                       svn_string_dup(propchange->value, pool));
-          propchange->value = NULL;
-        }
-
-      else if ((original_value != NULL) && (propchange->value == NULL))
-        {
-          /* found a deletion.  make it look like an addition. */
-          propchange->value = svn_string_dup(original_value, pool);
-          apr_hash_set(baseprops, propchange->name, APR_HASH_KEY_STRING,
-                       NULL);
-        }
-
-      else if ((original_value != NULL) && (propchange->value != NULL))
-        {
-          /* found a change.  just swap the values.  */
-          const svn_string_t *str = svn_string_dup(propchange->value, pool);
-          propchange->value = svn_string_dup(original_value, pool);
-          apr_hash_set(baseprops, propchange->name, APR_HASH_KEY_STRING, str);
-        }
-    }
-}
-
-
-/* Set *RESULT_ABSPATH to the absolute path to a readable file containing
-   the pristine text of LOCAL_ABSPATH in DB, or to NULL if it does not have
-   any pristine text.
-
-   If USE_BASE is FALSE it gets the pristine text of what is currently in the
-   working copy. (So it returns the pristine file of a copy).
-
-   If USE_BASE is TRUE, it looks in the lowest layer of the working copy and
-   shows exactly what was originally checked out (or updated to).
-
-   Rationale:
-
-   Which text-base do we want to use for the diff?  If the node is replaced
-   by a new file, then the base of the replaced file is called (in WC-1) the
-   "revert base".  If the replacement is a copy or move, then there is also
-   the base of the copied file to consider.
-
-   One could argue that we should never diff against the revert
-   base, and instead diff against the empty-file for both types of
-   replacement.  After all, there is no ancestry relationship
-   between the working file and the base file.  But my guess is that
-   in practice, users want to see the diff between their working
-   file and "the nearest versioned thing", whatever that is.  I'm
-   not 100% sure this is the right decision, but it at least seems
-   to match our test suite's expectations. */
-static svn_error_t *
-get_pristine_file(const char **result_abspath,
-                  svn_wc__db_t *db,
-                  const char *local_abspath,
-                  svn_boolean_t use_base,
-                  apr_pool_t *result_pool,
-                  apr_pool_t *scratch_pool)
-{
-  const svn_checksum_t *checksum;
-
-  if (!use_base)
-    {
-      SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL, NULL,
-                                            &checksum, NULL, NULL, NULL,
-                                            db, local_abspath,
-                                            scratch_pool, scratch_pool));
-    }
-  else
-    {
-      SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL,
-                                       NULL, NULL, NULL, NULL, &checksum,
-                                       NULL, NULL, NULL, NULL, NULL,
-                                       db, local_abspath,
-                                       scratch_pool, scratch_pool));
-    }
-
-  if (checksum != NULL)
-    {
-      SVN_ERR(svn_wc__db_pristine_get_path(result_abspath, db, local_abspath,
-                                           checksum,
-                                           result_pool, scratch_pool));
-      return SVN_NO_ERROR;
-    }
-
-  *result_abspath = NULL;
-  return SVN_NO_ERROR;
-}
-
-
 /*-------------------------------------------------------------------------*/
 
 
@@ -234,9 +111,6 @@ struct edit_baton_t 
   /* Should this diff ignore node ancestry? */
   svn_boolean_t ignore_ancestry;
 
-  /* Should this diff not compare copied files with their source? */
-  svn_boolean_t show_copies_as_adds;
-
   /* Possibly diff repos against text-bases instead of working files. */
   svn_boolean_t diff_pristine;
 
@@ -254,9 +128,6 @@ struct edit_baton_t 
  */
 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;
 
@@ -268,7 +139,13 @@ struct dir_baton_t
   const char *name;
   const char *relpath;
   const char *local_abspath;
-  svn_boolean_t shadowed;
+
+  /* TRUE if the file is added by the editor drive. */
+  svn_boolean_t added;
+  /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
+  svn_boolean_t repos_only;
+  /* TRUE if the node is to be compared with an unrelated node*/
+  svn_boolean_t ignoring_ancestry;
 
   /* Processor state */
   void *pdb;
@@ -311,9 +188,6 @@ struct dir_baton_t
  */
 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
@@ -321,20 +195,21 @@ struct file_baton_t
   const char *name;
   const char *relpath;
   const char *local_abspath;
-  svn_boolean_t shadowed;
 
   /* Processor state */
   void *pfb;
   svn_boolean_t skip;
 
+  /* TRUE if the file is added by the editor drive. */
+  svn_boolean_t added;
+  /* TRUE if the node exists only on the repository side (op_depth 0 or added) */
+  svn_boolean_t repos_only;
+  /* TRUE if the node is to be compared with an unrelated node*/
+  svn_boolean_t ignoring_ancestry;
+
   const svn_diff_source_t *left_src;
   const svn_diff_source_t *right_src;
 
-
- /* When constructing the requested repository version of the file, we
-    drop the result into a file at TEMP_FILE_PATH. */
-  const char *temp_file_path;
-
   /* The list of incoming BASE->repos propchanges. */
   apr_array_header_t *propchanges;
 
@@ -345,9 +220,9 @@ struct file_baton_t
   const svn_checksum_t *base_checksum;
   apr_hash_t *base_props;
 
-  /* The resulting checksum from apply_textdelta */
+  /* The resulting from apply_textdelta */
+  const char *temp_file_path;
   unsigned char result_digest[APR_MD5_DIGESTSIZE];
-  svn_boolean_t got_textdelta;
 
   /* The overall crawler editor baton. */
   struct edit_baton_t *eb;
@@ -379,7 +254,6 @@ make_edit_baton(struct edit_baton_t **ed
                 svn_depth_t depth,
                 svn_boolean_t ignore_ancestry,
                 svn_boolean_t show_copies_as_adds,
-                svn_boolean_t use_git_diff_format,
                 svn_boolean_t use_text_base,
                 svn_boolean_t reverse_order,
                 const apr_array_header_t *changelist_filter,
@@ -404,7 +278,11 @@ make_edit_baton(struct edit_baton_t **ed
   if (reverse_order)
     processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool);
 
-  if (! show_copies_as_adds && !use_git_diff_format)
+  /* --show-copies-as-adds implies --notice-ancestry */
+  if (show_copies_as_adds)
+    ignore_ancestry = FALSE;
+
+  if (! show_copies_as_adds)
     processor = svn_diff__tree_processor_copy_as_changed_create(processor,
                                                                 pool);
 
@@ -415,7 +293,6 @@ make_edit_baton(struct edit_baton_t **ed
   eb->processor = processor;
   eb->depth = depth;
   eb->ignore_ancestry = ignore_ancestry;
-  eb->show_copies_as_adds = show_copies_as_adds;
   eb->local_before_remote = reverse_order;
   eb->diff_pristine = use_text_base;
   eb->changelist_hash = changelist_hash;
@@ -464,7 +341,6 @@ make_dir_baton(const char *path,
   if (parent_baton != NULL)
     {
       parent_baton->users++;
-      db->shadowed = parent_baton->shadowed;
     }
 
   db->users = 1;
@@ -495,7 +371,6 @@ make_file_baton(const char *path,
   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;
@@ -523,304 +398,183 @@ maybe_done(struct dir_baton_t *db)
   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.
- *
- * DIR_BATON is the parent directory baton, PATH is the path to the file to
- * be compared.
- *
- * Do all allocation in POOL.
- *
- * ### TODO: Need to work on replace if the new filename used to be a
- * directory.
- */
-static svn_error_t *
-file_diff(struct edit_baton_t *eb,
-          const char *local_abspath,
-          const char *path,
-          void *dir_baton,
-          apr_pool_t *scratch_pool)
+/* Standard check to see if a node is represented in the local working copy */
+#define NOT_PRESENT(status)                                    \
+            ((status) == svn_wc__db_status_not_present          \
+             || (status) == svn_wc__db_status_excluded          \
+             || (status) == svn_wc__db_status_server_excluded)
+
+svn_error_t *
+svn_wc__diff_base_working_diff(svn_wc__db_t *db,
+                               const char *local_abspath,
+                               const char *relpath,
+                               svn_revnum_t revision,
+                               apr_hash_t *changelist_hash,
+                               const svn_diff_tree_processor_t *processor,
+                               void *processor_dir_baton,
+                               svn_boolean_t diff_pristine,
+                               svn_cancel_func_t cancel_func,
+                               void *cancel_baton,
+                               apr_pool_t *scratch_pool)
 {
-  svn_wc__db_t *db = eb->db;
-  const char *textbase;
-  svn_boolean_t replaced;
+  void *file_baton = NULL;
+  svn_boolean_t skip = FALSE;
   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;
-  svn_boolean_t have_base;
+  svn_revnum_t db_revision;
+  svn_boolean_t had_props;
+  svn_boolean_t props_mod;
+  svn_boolean_t files_same = FALSE;
   svn_wc__db_status_t base_status;
-  svn_boolean_t use_base = FALSE;
+  const svn_checksum_t *working_checksum;
+  const svn_checksum_t *checksum;
+  svn_filesize_t recorded_size;
+  apr_time_t recorded_time;
+  const char *pristine_file;
+  const char *local_file;
+  svn_diff_source_t *left_src;
+  svn_diff_source_t *right_src;
+  apr_hash_t *base_props;
+  apr_hash_t *local_props;
+  apr_array_header_t *prop_changes;
+  const char *changelist;
 
-  SVN_ERR_ASSERT(! eb->diff_pristine);
+  SVN_ERR(svn_wc__db_read_info(&status, NULL, &db_revision, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL, &working_checksum, NULL,
+                               NULL, NULL, NULL, NULL, NULL, &recorded_size,
+                               &recorded_time, &changelist, NULL, NULL,
+                               &had_props, &props_mod, NULL, NULL, NULL,
+                               db, local_abspath, scratch_pool, scratch_pool));
+  checksum = working_checksum;
+
+  assert(status == svn_wc__db_status_normal
+         || status == svn_wc__db_status_added
+         || (status == svn_wc__db_status_deleted && diff_pristine));
 
   /* If the item is not a member of a specified changelist (and there are
      some specified changelists), skip it. */
-  if (! svn_wc__internal_changelist_match(db, local_abspath,
-                                          eb->changelist_hash, scratch_pool))
+  if (changelist_hash && !svn_hash_gets(changelist_hash, changelist))
     return SVN_NO_ERROR;
 
-  SVN_ERR(svn_wc__db_read_info(&status, NULL, &revision, NULL, 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,
-                                     NULL, NULL, NULL, NULL, NULL, NULL,
-                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                     db, local_abspath,
-                                     scratch_pool, scratch_pool));
 
-  replaced = ((status == svn_wc__db_status_added)
-              && have_base
-              && base_status != svn_wc__db_status_not_present);
-
-  /* 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
-   * show_copies_as_adds == TRUE (in which case copy/move is really shown as
-   * an add, diffing against the empty file).
-   * So show the revert-base revision for plain replaces. */
-  if (replaced
-      && ! original_repos_relpath)
-    {
-      use_base = TRUE;
-      revision = revert_base_revnum;
-    }
-
-  /* Set TEXTBASE to the path to the text-base file that we want to diff
-     against.
-
-     ### There shouldn't be cases where the result is NULL, but at present
-     there might be - see get_nearest_pristine_text_as_file(). */
-  SVN_ERR(get_pristine_file(&textbase, db, local_abspath,
-                            use_base, scratch_pool, scratch_pool));
-
-  /* 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
-   * generated, first one for the delete and then one for the add.
-   * However, if this file was replaced and we are ignoring ancestry,
-   * report it as a normal file modification instead. */
-  if ((! replaced && status == svn_wc__db_status_deleted) ||
-      (replaced && ! eb->ignore_ancestry))
+  if (status != svn_wc__db_status_normal)
     {
-      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(&left_props, db, local_abspath,
-                                             scratch_pool, 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));
+      SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db_revision,
+                                       NULL, NULL, NULL, NULL, NULL, NULL,
+                                       NULL, &checksum, NULL, NULL, &had_props,
+                                       NULL, NULL,
+                                       db, local_abspath,
+                                       scratch_pool, scratch_pool));
+      recorded_size = SVN_INVALID_FILESIZE;
+      recorded_time = 0;
+      props_mod = TRUE; /* Requires compare */
+    }
+  else if (diff_pristine)
+    files_same = TRUE;
+  else
+    {
+      const svn_io_dirent2_t *dirent;
 
-      if (!skip)
-        SVN_ERR(eb->processor->file_deleted(path,
-                                            left_src,
-                                            textbase,
-                                            left_props,
-                                            file_baton,
-                                            eb->processor,
-                                            scratch_pool));
+      SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath,
+                                  FALSE /* verify truename */,
+                                  TRUE /* ingore_enoent */,
+                                  scratch_pool, scratch_pool));
 
-      if (! (replaced && ! eb->ignore_ancestry))
+      if (dirent->kind == svn_node_file
+          && dirent->filesize == recorded_size
+          && dirent->mtime == recorded_time)
         {
-          /* We're here only for showing a delete, so we're done. */
-          return SVN_NO_ERROR;
+          files_same = TRUE;
         }
     }
 
- /* Now deal with showing additions, or the add-half of replacements.
-  * If the item is schedule-add *with history*, then we usually want
-  * to see the usual working vs. text-base comparison, which will show changes
-  * made since the file was copied.  But in case we're showing copies as adds,
-  * we need to compare the copied file to the empty file. If we're doing a git
-  * 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 (status == svn_wc__db_status_added
-      && !(eb->ignore_ancestry && replaced))
-    {
-      void *file_baton = NULL;
-      svn_boolean_t skip = FALSE;
-      const char *translated = NULL;
-      svn_diff_source_t *copyfrom_src = NULL;
-      svn_diff_source_t *right_src = svn_diff__source_create(
-                                                    SVN_INVALID_REVNUM,
-                                                    scratch_pool);
+  if (files_same && !props_mod)
+    return SVN_NO_ERROR; /* Cheap exit */
 
-      if (original_repos_relpath)
-        {
-          copyfrom_src = svn_diff__source_create(original_revision,
-                                                 scratch_pool);
-          copyfrom_src->repos_relpath = original_repos_relpath;
-        }
+  assert(checksum);
 
-      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));
+  if (!SVN_IS_VALID_REVNUM(revision))
+    revision = db_revision;
 
-      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));
+  left_src = svn_diff__source_create(revision, scratch_pool);
+  right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
 
-          if (copyfrom_src)
-            {
-              SVN_ERR(svn_wc__db_read_pristine_props(&copyfrom_props,
-                                                     db, local_abspath,
-                                                     scratch_pool,
-                                                     scratch_pool));
-            }
+  SVN_ERR(processor->file_opened(&file_baton, &skip, relpath,
+                                 left_src,
+                                 right_src,
+                                 NULL /* copyfrom_src */,
+                                 processor_dir_baton,
+                                 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));
+  if (skip)
+    return SVN_NO_ERROR;
 
-          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 *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(svn_wc__db_pristine_get_path(&pristine_file,
+                                       db, local_abspath, checksum,
+                                       scratch_pool, scratch_pool));
 
-      SVN_ERR(eb->processor->file_opened(&file_baton, &skip,
-                                         path,
-                                         left_src,
-                                         right_src,
-                                         NULL,
-                                         dir_baton,
-                                         eb->processor,
+  if (diff_pristine)
+    SVN_ERR(svn_wc__db_pristine_get_path(&local_file,
+                                         db, local_abspath,
+                                         working_checksum,
                                          scratch_pool, scratch_pool));
+  else if (! (had_props || props_mod))
+    local_file = local_abspath;
+  else if (files_same)
+    local_file = pristine_file;
+  else
+    SVN_ERR(svn_wc__internal_translated_file(
+                            &local_file, local_abspath,
+                            db, local_abspath,
+                            SVN_WC_TRANSLATE_TO_NF
+                                | SVN_WC_TRANSLATE_USE_GLOBAL_TMP,
+                            cancel_func, cancel_baton,
+                            scratch_pool, scratch_pool));
+
+  if (! files_same)
+    SVN_ERR(svn_io_files_contents_same_p(&files_same, local_file,
+                                         pristine_file, 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,
-                                               FALSE, scratch_pool));
-      if (modified)
-        {
-          /* Note that this might be the _second_ time we translate
-             the file, as svn_wc__text_modified_internal_p() might have used a
-             tmp translated copy too.  But what the heck, diff is
-             already expensive, translating twice for the sake of code
-             modularity is liveable. */
-          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));
-        }
-
-      /* Get the properties, the svn:mime-type values, and compute the
-         differences between the two.  */
-      if (replaced
-          && eb->ignore_ancestry)
-        {
-          /* 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(&left_props,
-                                            db, local_abspath,
-                                            scratch_pool, scratch_pool));
-        }
-      else
-        {
-          /* We can only fetch the pristine props (from BASE or WORKING) if
-             the node has not been replaced, or it was copied/moved here.  */
-          SVN_ERR_ASSERT(!replaced
-                         || status == svn_wc__db_status_copied
-                         || status == svn_wc__db_status_moved_here);
-
-          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 (!left_props)
-            left_props = apr_hash_make(scratch_pool);
-        }
+  if (had_props)
+    SVN_ERR(svn_wc__db_base_get_props(&base_props, db, local_abspath,
+                                      scratch_pool, scratch_pool));
+  else
+    base_props = apr_hash_make(scratch_pool);
 
-      SVN_ERR(svn_wc__get_actual_props(&right_props, db, local_abspath,
-                                       scratch_pool, scratch_pool));
+  if (status == svn_wc__db_status_normal && (diff_pristine || !props_mod))
+    local_props = base_props;
+  else if (diff_pristine)
+    SVN_ERR(svn_wc__db_read_pristine_props(&local_props, db, local_abspath,
+                                           scratch_pool, scratch_pool));
+  else
+    SVN_ERR(svn_wc__db_read_props(&local_props, db, local_abspath,
+                                  scratch_pool, scratch_pool));
 
-      SVN_ERR(svn_prop_diffs(&propchanges, right_props, left_props,
-                             scratch_pool));
+  SVN_ERR(svn_prop_diffs(&prop_changes, local_props, base_props, scratch_pool));
 
-      if (modified || propchanges->nelts > 0)
-        {
-          SVN_ERR(eb->processor->file_changed(path,
-                                              left_src,
-                                              right_src,
-                                              textbase,
-                                              translated,
-                                              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));
+  if (prop_changes->nelts || !files_same)
+    {
+      SVN_ERR(processor->file_changed(relpath,
+                                      left_src,
+                                      right_src,
+                                      pristine_file,
+                                      local_file,
+                                      base_props,
+                                      local_props,
+                                      ! files_same,
+                                      prop_changes,
+                                      file_baton,
+                                      processor,
+                                      scratch_pool));
+    }
+  else
+    {
+      SVN_ERR(processor->file_closed(relpath,
+                                     left_src,
+                                     right_src,
+                                     file_baton,
+                                     processor,
+                                     scratch_pool));
     }
 
   return SVN_NO_ERROR;
@@ -888,10 +642,13 @@ walk_local_nodes_diff(struct edit_baton_
                                db, local_abspath, scratch_pool, scratch_pool));
 
   left_src = svn_diff__source_create(revision, scratch_pool);
-  right_src = svn_diff__source_create(0, scratch_pool);
+  right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
 
   if (compared)
-    dir_baton = parent_baton;
+    {
+      dir_baton = parent_baton;
+      skip = TRUE;
+    }
   else if (!in_anchor_not_target)
     SVN_ERR(eb->processor->dir_opened(&dir_baton, &skip, &skip_children,
                                       path,
@@ -905,19 +662,41 @@ walk_local_nodes_diff(struct edit_baton_
 
   if (!skip_children && depth != svn_depth_empty)
     {
-      const apr_array_header_t *children;
+      apr_hash_t *nodes;
+      apr_hash_t *conflicts;
+      apr_array_header_t *children;
+      svn_depth_t depth_below_here = depth;
+      svn_boolean_t diff_files;
+      svn_boolean_t diff_dirs;
       int i;
-      SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
-                                       scratch_pool, iterpool));
+
+      if (depth_below_here == svn_depth_immediates)
+        depth_below_here = svn_depth_empty;
+
+      diff_files = (depth == svn_depth_unknown
+                   || depth >= svn_depth_files);
+      diff_dirs = (depth == svn_depth_unknown
+                   || depth >= svn_depth_immediates);
+
+      SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts,
+                                            db, local_abspath,
+                                            scratch_pool, iterpool));
+
+      children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
+                            scratch_pool);
 
       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);
+          svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
+                                                  svn_sort__item_t);
+          const char *name = item->key;
+          struct svn_wc__db_info_t *info = item->value;
+
+          const char *child_abspath;
+          const char *child_relpath;
+          svn_boolean_t repos_only;
+          svn_boolean_t local_only;
+          svn_kind_t base_kind;
 
           if (eb->cancel_func)
             SVN_ERR(eb->cancel_func(eb->cancel_baton));
@@ -932,67 +711,155 @@ walk_local_nodes_diff(struct edit_baton_
           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)
+          if (NOT_PRESENT(info->status))
             continue;
 
-          child_path = svn_relpath_join(path, name, iterpool);
+          assert(info->status == svn_wc__db_status_normal
+                 || info->status == svn_wc__db_status_added
+                 || info->status == svn_wc__db_status_deleted);
 
-          /* 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;
+          svn_pool_clear(iterpool);
+          child_abspath = svn_dirent_join(local_abspath, name, iterpool);
+          child_relpath = svn_relpath_join(path, name, iterpool);
+
+          repos_only = FALSE;
+          local_only = FALSE;
 
-          switch (kind)
+          if (!info->have_base)
             {
-            case svn_kind_file:
-            case svn_kind_symlink:
-              SVN_ERR(file_diff(eb, child_abspath, child_path, dir_baton,
-                                iterpool));
-              break;
+              local_only = TRUE; /* Only report additions */
+            }
+          else if (info->status == svn_wc__db_status_normal)
+            {
+              /* Simple diff */
+              base_kind = info->kind;
+            }
+          else if (info->status == svn_wc__db_status_deleted
+                   && (!eb->diff_pristine || !info->have_more_work))
+            {
+              svn_wc__db_status_t base_status;
+              repos_only = TRUE;
+              SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
+                                               NULL, NULL, NULL, NULL, NULL,
+                                               NULL, NULL, NULL, NULL, NULL,
+                                               NULL, NULL, NULL,
+                                               db, child_abspath,
+                                               iterpool, iterpool));
 
-            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))
+              if (NOT_PRESENT(base_status))
+                continue;
+            }
+          else
+            {
+              /* working status is either added or deleted */
+              svn_wc__db_status_t base_status;
+
+              SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
+                                               NULL, NULL, NULL, NULL, NULL,
+                                               NULL, NULL, NULL, NULL, NULL,
+                                               NULL, NULL, NULL,
+                                               db, child_abspath,
+                                               iterpool, iterpool));
+
+              if (NOT_PRESENT(base_status))
+                local_only = TRUE;
+              else if (base_kind != info->kind || !eb->ignore_ancestry)
                 {
-                  svn_depth_t depth_below_here = depth;
+                  repos_only = TRUE;
+                  local_only = TRUE;
+                }
+            }
 
-                  if (depth_below_here == svn_depth_immediates)
-                    depth_below_here = svn_depth_empty;
+          if (eb->local_before_remote && local_only)
+            {
+              if (info->kind == svn_kind_file && diff_files)
+                SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
+                                                     child_relpath,
+                                                     eb->processor, dir_baton,
+                                                     eb->changelist_hash,
+                                                     eb->diff_pristine,
+                                                     eb->cancel_func,
+                                                     eb->cancel_baton,
+                                                     iterpool));
+              else if (info->kind == svn_kind_dir && diff_dirs)
+                SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
+                                                    child_relpath,
+                                                    depth_below_here,
+                                                    eb->processor, dir_baton,
+                                                    eb->changelist_hash,
+                                                    eb->diff_pristine,
+                                                    eb->cancel_func,
+                                                    eb->cancel_baton,
+                                                    iterpool));
+            }
 
-                  SVN_ERR(walk_local_nodes_diff(eb,
-                                                child_abspath,
-                                                child_path,
-                                                depth_below_here,
-                                                NULL /* compared */,
-                                                dir_baton,
-                                                iterpool));
+          if (repos_only)
+            {
+              /* Report repository form deleted */
+              if (base_kind == svn_kind_file && diff_files)
+                SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
+                                                    child_relpath, eb->revnum,
+                                                    eb->processor, dir_baton,
+                                                    iterpool));
+              else if (base_kind == svn_kind_dir && diff_dirs)
+                SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
+                                                   child_relpath, eb->revnum,
+                                                   depth_below_here,
+                                                   eb->processor, dir_baton,
+                                                   eb->cancel_func,
+                                                   eb->cancel_baton,
+                                                   iterpool));
+            }
+          else if (!local_only) /* Not local only nor remote only */
+            {
+              /* Diff base against actual */
+              if (info->kind == svn_kind_file && diff_files)
+                {
+                  if (info->status != svn_wc__db_status_normal
+                      || !eb->diff_pristine)
+                    {
+                      SVN_ERR(svn_wc__diff_base_working_diff(
+                                                db, child_abspath,
+                                                child_relpath,
+                                                eb->revnum,
+                                                eb->changelist_hash,
+                                                eb->processor, dir_baton,
+                                                eb->diff_pristine,
+                                                eb->cancel_func,
+                                                eb->cancel_baton,
+                                                scratch_pool));
+                    }
                 }
-              break;
+              else if (info->kind == svn_kind_dir && diff_dirs)
+                SVN_ERR(walk_local_nodes_diff(eb, child_abspath,
+                                              child_relpath,
+                                              depth_below_here,
+                                              NULL /* compared */,
+                                              dir_baton,
+                                              scratch_pool));
+            }
 
-            default:
-              break;
-          }
+          if (!eb->local_before_remote && local_only)
+            {
+              if (info->kind == svn_kind_file && diff_files)
+                SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
+                                                     child_relpath,
+                                                     eb->processor, dir_baton,
+                                                     eb->changelist_hash,
+                                                     eb->diff_pristine,
+                                                     eb->cancel_func,
+                                                     eb->cancel_baton,
+                                                     iterpool));
+              else if (info->kind == svn_kind_dir && diff_dirs)
+                SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
+                                                     child_relpath, depth_below_here,
+                                                     eb->processor, dir_baton,
+                                                     eb->changelist_hash,
+                                                     eb->diff_pristine,
+                                                     eb->cancel_func,
+                                                     eb->cancel_baton,
+                                                     iterpool));
+            }
         }
     }
 
@@ -1004,11 +871,10 @@ walk_local_nodes_diff(struct edit_baton_
      ### 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
+  if (! skip
+      && ! eb->changelist_hash
       && ! in_anchor_not_target
-      && (!compared || ! svn_hash_gets(compared, ""))
-      && props_mod
-      && ! skip)
+      && props_mod)
     {
       apr_array_header_t *propchanges;
       apr_hash_t *left_props;
@@ -1043,131 +909,164 @@ walk_local_nodes_diff(struct edit_baton_
   return SVN_NO_ERROR;
 }
 
-/* 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_local_only_file(struct edit_baton_t *eb,
-                       const char *local_abspath,
-                       const char *path,
-                       void *parent_baton,
-                       apr_pool_t *scratch_pool)
+svn_error_t *
+svn_wc__diff_local_only_file(svn_wc__db_t *db,
+                             const char *local_abspath,
+                             const char *relpath,
+                             const svn_diff_tree_processor_t *processor,
+                             void *processor_parent_baton,
+                             apr_hash_t *changelist_hash,
+                             svn_boolean_t diff_pristine,
+                             svn_cancel_func_t cancel_func,
+                             void *cancel_baton,
+                             apr_pool_t *scratch_pool)
 {
-  svn_wc__db_t *db = eb->db;
   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 *source_file;
+  const char *pristine_file;
   const char *translated_file;
-  svn_wc__db_status_t status;
   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, NULL, &revision, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, &changelist,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+  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 (changelist && eb->changelist_hash
-      && !svn_hash_gets(eb->changelist_hash, changelist))
+  assert(kind == svn_kind_file
+         && (status == svn_wc__db_status_normal
+             || status == svn_wc__db_status_added
+             || (status == svn_wc__db_status_deleted && diff_pristine)));
+
+
+  if (changelist && changelist_hash
+      && !svn_hash_gets(changelist_hash, changelist))
     return SVN_NO_ERROR;
 
-  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->diff_pristine);
-
-  /* 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)
+  if (status == svn_wc__db_status_deleted)
     {
-      /* Don't show anything if we're comparing to BASE, since by
-         definition there can't be any local modifications. */
-      if (eb->diff_pristine)
-        return SVN_NO_ERROR;
+      assert(diff_pristine);
+
+      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));
 
-      /* Otherwise show just the local modifications. */
-      return file_diff(eb, local_abspath, path, parent_baton, scratch_pool);
+  if (original_repos_relpath)
+    {
+      copyfrom_src = svn_diff__source_create(original_revision, scratch_pool);
+      copyfrom_src->repos_relpath = original_repos_relpath;
     }
 
-  right_src = svn_diff__source_create(revision, scratch_pool);
+  if (props_mod || !SVN_IS_VALID_REVNUM(revision))
+    right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
+  else
+    {
+      if (diff_pristine)
+        file_mod = FALSE;
+      else
+        SVN_ERR(svn_wc__internal_file_modified_p(&file_mod, db, local_abspath,
+                                                 FALSE, scratch_pool));
 
-  SVN_ERR(eb->processor->file_opened(&file_baton, &skip,
-                                     path,
-                                     NULL,
-                                     right_src,
-                                     NULL,
-                                     parent_baton,
-                                     eb->processor,
-                                     scratch_pool, 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(processor->file_opened(&file_baton, &skip,
+                                 relpath,
+                                 NULL /* left_source */,
+                                 right_src,
+                                 copyfrom_src,
+                                 processor_parent_baton,
+                                 processor,
+                                 scratch_pool, scratch_pool));
 
   if (skip)
     return SVN_NO_ERROR;
 
-  if (eb->diff_pristine)
-    SVN_ERR(svn_wc__db_read_pristine_props(&right_props, db, local_abspath,
-                                           scratch_pool, scratch_pool));
-  else
+  if (props_mod && !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 (eb->diff_pristine)
+  if (checksum)
+    SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file, db, local_abspath,
+                                         checksum, scratch_pool, scratch_pool));
+  else
+    pristine_file = NULL;
+
+  if (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,
+           cancel_func, cancel_baton,
            scratch_pool, scratch_pool));
     }
 
-  SVN_ERR(eb->processor->file_added(path,
-                                    NULL /* copyfrom source */,
-                                    right_src,
-                                    NULL /* copyfrom file */,
-                                    translated_file,
-                                    NULL /* copyfrom props */,
-                                    right_props,
-                                    file_baton,
-                                    eb->processor,
-                                    scratch_pool));
+  SVN_ERR(processor->file_added(relpath,
+                                copyfrom_src,
+                                right_src,
+                                copyfrom_src
+                                  ? pristine_file
+                                  : NULL,
+                                translated_file,
+                                copyfrom_src
+                                  ? pristine_props
+                                  : NULL,
+                                right_props,
+                                file_baton,
+                                processor,
+                                scratch_pool));
 
   return SVN_NO_ERROR;
 }
 
-/* Report an existing directory in the working copy (either in BASE
- * or WORKING) as having been added.  If recursing, also report any
- * subdirectories as added.
- *
- * DIR_BATON is the baton for the directory.
- *
- * Do all allocation in POOL.
- */
-static svn_error_t *
-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_error_t *
+svn_wc__diff_local_only_dir(svn_wc__db_t *db,
+                            const char *local_abspath,
+                            const char *relpath,
+                            svn_depth_t depth,
+                            const svn_diff_tree_processor_t *processor,
+                            void *processor_parent_baton,
+                            apr_hash_t *changelist_hash,
+                            svn_boolean_t diff_pristine,
+                            svn_cancel_func_t cancel_func,
+                            void *cancel_baton,
+                            apr_pool_t *scratch_pool)
 {
-  svn_wc__db_t *db = eb->db;
   const apr_array_header_t *children;
   int i;
   apr_pool_t *iterpool;
@@ -1176,79 +1075,81 @@ report_local_only_dir(struct edit_baton_
   svn_boolean_t skip_children = FALSE;
   svn_diff_source_t *right_src = svn_diff__source_create(SVN_INVALID_REVNUM,
                                                          scratch_pool);
+  svn_depth_t depth_below_here = depth;
+  apr_hash_t *nodes;
+  apr_hash_t *conflicts;
 
   /* 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(processor->dir_opened(&pdb, &skip, &skip_children,
+                                relpath,
+                                NULL,
+                                right_src,
+                                NULL /* copyfrom_src */,
+                                processor_parent_baton,
+                                processor,
+                                scratch_pool, iterpool));
 
+  SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db, local_abspath,
+                                        scratch_pool, iterpool));
 
-  SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
-                                   scratch_pool, iterpool));
+  if (depth_below_here == svn_depth_immediates)
+    depth_below_here = svn_depth_empty;
+
+  children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
+                            scratch_pool);
 
   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_sort__item_t *item = &APR_ARRAY_IDX(children, i, svn_sort__item_t);
+      const char *name = item->key;
+      struct svn_wc__db_info_t *info = item->value;
+      const char *child_abspath;
+      const char *child_relpath;
 
       svn_pool_clear(iterpool);
 
-      if (eb->cancel_func)
-        SVN_ERR(eb->cancel_func(eb->cancel_baton));
+      if (cancel_func)
+        SVN_ERR(cancel_func(cancel_baton));
 
       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)
+      if (NOT_PRESENT(info->status))
         {
           continue;
         }
 
       /* If comparing against WORKING, skip entries that are
          schedule-deleted - they don't really exist. */
-      if (!eb->diff_pristine && status == svn_wc__db_status_deleted)
+      if (!diff_pristine && info->status == svn_wc__db_status_deleted)
         continue;
 
-      child_path = svn_relpath_join(path, name, iterpool);
+      child_relpath = svn_relpath_join(relpath, name, iterpool);
 
-      switch (kind)
+      switch (info->kind)
         {
         case svn_kind_file:
         case svn_kind_symlink:
-          SVN_ERR(report_local_only_file(eb, child_abspath, child_path,
-                                         pdb, iterpool));
+          SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
+                                               child_relpath,
+                                               processor, pdb,
+                                               changelist_hash,
+                                               diff_pristine,
+                                               cancel_func, cancel_baton,
+                                               scratch_pool));
           break;
 
         case svn_kind_dir:
           if (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(report_local_only_dir(eb,
-                                            child_abspath,
-                                            child_path,
-                                            depth_below_here,
-                                            pdb,
-                                            iterpool));
+              SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
+                                                  child_relpath, depth_below_here,
+                                                  processor, pdb,
+                                                  changelist_hash,
+                                                  diff_pristine,
+                                                  cancel_func, cancel_baton,
+                                                  iterpool));
             }
           break;
 
@@ -1260,33 +1161,32 @@ report_local_only_dir(struct edit_baton_
   if (!skip)
     {
       apr_hash_t *right_props;
-      if (eb->diff_pristine)
+      if (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_ERR(processor->dir_added(relpath,
+                                   NULL /* copyfrom_src */,
+                                   right_src,
+                                   NULL,
+                                   right_props,
+                                   pdb,
+                                   processor,
+                                   iterpool));
     }
   svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
 
-/* Ensures that local changes are reported to pb->eb->processor if there
-   are any. */
+/* Reports local changes. */
 static svn_error_t *
-ensure_local_only_handled(struct dir_baton_t *pb,
-                          const char *name,
-                          apr_pool_t *scratch_pool)
+handle_local_only(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;
@@ -1296,23 +1196,18 @@ ensure_local_only_handled(struct dir_bat
   assert(!strchr(name, '/'));
   assert(!pb->added || eb->ignore_ancestry);
 
-  if (svn_hash_gets(pb->compared, name))
+  if (pb->skip_children)
     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)
+  if (info == NULL || NOT_PRESENT(info->status))
     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 */
 
@@ -1341,25 +1236,211 @@ ensure_local_only_handled(struct dir_bat
       else
         depth = svn_depth_empty;
 
-      SVN_ERR(report_local_only_dir(
-                      eb,
+      SVN_ERR(svn_wc__diff_local_only_dir(
+                      eb->db,
                       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,
+                      eb->processor, pb->pdb,
+                      eb->changelist_hash,
+                      eb->diff_pristine,
+                      eb->cancel_func, eb->cancel_baton,
                       scratch_pool));
     }
   else
-    SVN_ERR(report_local_only_file(
-                      eb,
+    SVN_ERR(svn_wc__diff_local_only_file(
+                      eb->db,
                       svn_dirent_join(pb->local_abspath, name, scratch_pool),
                       svn_relpath_join(pb->relpath, name, scratch_pool),
-                      pb->pdb,
+                      eb->processor, pb->pdb,
+                      eb->changelist_hash,
+                      eb->diff_pristine,
+                      eb->cancel_func, eb->cancel_baton,
                       scratch_pool));
 
   return SVN_NO_ERROR;
 }
 
+/* Reports a file LOCAL_ABSPATH in BASE as deleted */
+svn_error_t *
+svn_wc__diff_base_only_file(svn_wc__db_t *db,
+                            const char *local_abspath,
+                            const char *relpath,
+                            svn_revnum_t revision,
+                            const svn_diff_tree_processor_t *processor,
+                            void *processor_parent_baton,
+                            apr_pool_t *scratch_pool)
+{
+  svn_wc__db_status_t status;
+  svn_kind_t kind;
+  const svn_checksum_t *checksum;
+  apr_hash_t *props;
+  void *file_baton = NULL;
+  svn_boolean_t skip = FALSE;
+  svn_diff_source_t *left_src;
+  const char *pristine_file;
+
+  SVN_ERR(svn_wc__db_base_get_info(&status, &kind,
+                                   SVN_IS_VALID_REVNUM(revision)
+                                        ? NULL : &revision,
+                                   NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                   &checksum, NULL, NULL, NULL, &props, NULL,
+                                   db, local_abspath,
+                                   scratch_pool, scratch_pool));
+
+  SVN_ERR_ASSERT(status == svn_wc__db_status_normal
+                 && kind == svn_kind_file
+                 && checksum);
+
+  left_src = svn_diff__source_create(revision, scratch_pool);
+
+  SVN_ERR(processor->file_opened(&file_baton, &skip,
+                                 relpath,
+                                 left_src,
+                                 NULL /* right_src */,
+                                 NULL /* copyfrom_source */,
+                                 processor_parent_baton,
+                                 processor,
+                                 scratch_pool, scratch_pool));
+
+  if (skip)
+    return SVN_NO_ERROR;
+
+  SVN_ERR(svn_wc__db_pristine_get_path(&pristine_file,
+                                       db, local_abspath, checksum,
+                                       scratch_pool, scratch_pool));
+
+  SVN_ERR(processor->file_deleted(relpath,
+                                  left_src,
+                                  pristine_file,
+                                  props,
+                                  file_baton,
+                                  processor,
+                                  scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__diff_base_only_dir(svn_wc__db_t *db,
+                           const char *local_abspath,
+                           const char *relpath,
+                           svn_revnum_t revision,
+                           svn_depth_t depth,
+                           const svn_diff_tree_processor_t *processor,
+                           void *processor_parent_baton,
+                           svn_cancel_func_t cancel_func,
+                           void *cancel_baton,
+                           apr_pool_t *scratch_pool)
+{
+  void *dir_baton = NULL;
+  svn_boolean_t skip = FALSE;
+  svn_boolean_t skip_children = FALSE;
+  svn_diff_source_t *left_src;
+  svn_revnum_t report_rev = revision;
+
+  if (!SVN_IS_VALID_REVNUM(report_rev))
+    SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &report_rev, NULL, NULL, NULL,
+                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                     NULL, NULL, NULL,
+                                     db, local_abspath,
+                                     scratch_pool, scratch_pool));
+
+  left_src = svn_diff__source_create(report_rev, scratch_pool);
+
+  SVN_ERR(processor->dir_opened(&dir_baton, &skip, &skip_children,
+                                relpath,
+                                left_src,
+                                NULL /* right_src */,
+                                NULL /* copyfrom_src */,
+                                processor_parent_baton,
+                                processor,
+                                scratch_pool, scratch_pool));
+
+  if (!skip_children && (depth == svn_depth_unknown || depth > svn_depth_empty))
+    {
+      apr_hash_t *nodes;
+      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+      apr_array_header_t *children;
+      int i;
+
+      SVN_ERR(svn_wc__db_base_get_children_info(&nodes, db, local_abspath,
+                                                scratch_pool, iterpool));
+
+      children = svn_sort__hash(nodes, svn_sort_compare_items_lexically,
+                                scratch_pool);
+
+      for (i = 0; i < children->nelts; i++)
+        {
+          svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
+                                                  svn_sort__item_t);
+          const char *name = item->key;
+          struct svn_wc__db_base_info_t *info = item->value;
+          const char *child_abspath;
+          const char *child_relpath;
+
+          if (info->status != svn_wc__db_status_normal)
+            continue;
+
+          if (cancel_func)
+            SVN_ERR(cancel_func(cancel_baton));
+
+          svn_pool_clear(iterpool);
+
+          child_abspath = svn_dirent_join(local_abspath, name, iterpool);
+          child_relpath = svn_relpath_join(relpath, name, iterpool);
+
+          switch (info->kind)
+            {
+              case svn_kind_file:
+              case svn_kind_symlink:
+                SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
+                                                    child_relpath,
+                                                    revision,
+                                                    processor, dir_baton,
+                                                    iterpool));
+                break;
+              case svn_kind_dir:
+                if (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(svn_wc__diff_base_only_dir(db, child_abspath,
+                                                       child_relpath,
+                                                       revision,
+                                                       depth_below_here,
+                                                       processor, dir_baton,
+                                                       cancel_func,
+                                                       cancel_baton,
+                                                       iterpool));
+                  }
+                break;
+
+              default:
+                break;
+            }
+        }
+    }
+
+  if (!skip)
+    {
+      apr_hash_t *props;
+      SVN_ERR(svn_wc__db_base_get_props(&props, db, local_abspath,
+                                        scratch_pool, scratch_pool));
+
+      SVN_ERR(processor->dir_deleted(relpath,
+                                     left_src,
+                                     props,
+                                     dir_baton,
+                                     processor,
+                                     scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
 
 /* An svn_delta_editor_t function. */
 static svn_error_t *
@@ -1387,6 +1468,24 @@ open_root(void *edit_baton,
   db = make_dir_baton("", NULL, eb, FALSE, eb->depth, dir_pool);
   *root_baton = db;
 
+  if (eb->target[0] == '\0')
+    {
+      db->left_src = svn_diff__source_create(eb->revnum, db->pool);
+      db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
+
+      SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip,
+                                        &db->skip_children,
+                                        "",
+                                        db->left_src,
+                                        db->right_src,
+                                        NULL /* copyfrom_source */,
+                                        NULL /* parent_baton */,
+                                        eb->processor,
+                                        db->pool, db->pool));
+    }
+  else
+    db->skip = TRUE; /* Skip this, but not the children */
+
   return SVN_NO_ERROR;
 }
 
@@ -1422,37 +1521,43 @@ add_directory(const char *path,
   svn_depth_t subdir_depth = (pb->depth == svn_depth_immediates)
                               ? svn_depth_empty : pb->depth;
 
-  /* ### TODO: support copyfrom? */
-
   db = make_dir_baton(path, pb, pb->eb, TRUE, subdir_depth,
                       dir_pool);
   *child_baton = db;
 
-  if (!db->shadowed)
+  if (pb->repos_only || !eb->ignore_ancestry)
+    db->repos_only = TRUE;
+  else
     {
       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 (!info || info->kind != svn_kind_dir || NOT_PRESENT(info->status))
+        db->repos_only = TRUE;
+
+      if (!db->repos_only && info->status != svn_wc__db_status_added)
+        db->repos_only = TRUE;
 
-  if (eb->local_before_remote && !eb->ignore_ancestry && !db->shadowed)
-    SVN_ERR(ensure_local_only_handled(pb, db->name, dir_pool));
+      if (!db->repos_only)
+        {
+          db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
+          db->ignoring_ancestry = TRUE;
+
+          svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
+        }
+    }
 
-  /* 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);
 
-  db->left_src  = svn_diff__source_create(eb->revnum, db->pool);
+  if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
+    SVN_ERR(handle_local_only(pb, db->name, dir_pool));
 
   SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
                                     db->relpath,
                                     db->left_src,
-                                    NULL /* right_source */,
+                                    db->right_src,
                                     NULL /* copyfrom src */,
                                     pb->pdb,
                                     eb->processor,
@@ -1480,26 +1585,51 @@ open_directory(const char *path,
   db = make_dir_baton(path, pb, pb->eb, FALSE, subdir_depth, dir_pool);
   *child_baton = db;
 
-  if (!db->shadowed)
+  if (pb->repos_only)
+    db->repos_only = TRUE;
+  else
     {
       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 (!info || info->kind != svn_kind_dir || NOT_PRESENT(info->status))
+        db->repos_only = TRUE;
+
+      if (!db->repos_only)
+        switch (info->status)
+          {
+            case svn_wc__db_status_normal:
+              break;
+            case svn_wc__db_status_deleted:
+              db->repos_only = TRUE;
+
+              if (!info->have_more_work)
+                svn_hash_sets(pb->compared,
+                              apr_pstrdup(pb->pool, db->name), "");
+              break;
+            case svn_wc__db_status_added:
+              if (eb->ignore_ancestry)
+                db->ignoring_ancestry = TRUE;
+              else
+                db->repos_only = TRUE;
+              break;
+            default:
+              SVN_ERR_MALFUNCTION();
+        }
 
-  if (eb->local_before_remote && !eb->ignore_ancestry && !db->shadowed)
-    SVN_ERR(ensure_local_only_handled(pb, db->name, dir_pool));
+      if (!db->repos_only)
+        {
+          db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
+          svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
+        }
+    }
 
-  db->left_src  = svn_diff__source_create(eb->revnum, db->pool);
-  db->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, db->pool);
+  db->left_src = svn_diff__source_create(eb->revnum, db->pool);
 
-  /* Add this path to the parent directory's list of elements that
-     have been compared. */
-  svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, db->name), "");
+  if (eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
+    SVN_ERR(handle_local_only(pb, db->name, dir_pool));
 
   SVN_ERR(eb->processor->dir_opened(&db->pdb, &db->skip, &db->skip_children,
                                     db->relpath,
@@ -1523,22 +1653,29 @@ close_directory(void *dir_baton,
                 apr_pool_t *pool)
 {
   struct dir_baton_t *db = dir_baton;
+  struct dir_baton_t *pb = db->parent_baton;
   struct edit_baton_t *eb = db->eb;
   apr_pool_t *scratch_pool = db->pool;
   svn_boolean_t reported_closed = FALSE;
 
-  if (db->deletes)
+  if (!db->skip_children && db->deletes && apr_hash_count(db->deletes))
     {
-      apr_hash_index_t *hi;
       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+      apr_array_header_t *children;
+      int i;
+      children = svn_sort__hash(db->deletes, svn_sort_compare_items_lexically,
+                                scratch_pool);
 
-      for (hi = apr_hash_first(scratch_pool, db->deletes); hi;
-           hi = apr_hash_next(hi))
+      for (i = 0; i < children->nelts; i++)
         {
-          const char *name = svn__apr_hash_index_key(hi);
+          svn_sort__item_t *item = &APR_ARRAY_IDX(children, i,
+                                                  svn_sort__item_t);
+          const char *name = item->key;
 
           svn_pool_clear(iterpool);
-          SVN_ERR(ensure_local_only_handled(db, name, iterpool));
+          SVN_ERR(handle_local_only(db, name, iterpool));
+
+          svn_hash_sets(db->compared, name, "");
         }
 
       svn_pool_destroy(iterpool);
@@ -1547,92 +1684,94 @@ close_directory(void *dir_baton,
   /* 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)
+  if (!db->repos_only && !db->skip_children)
+  {
     SVN_ERR(walk_local_nodes_diff(eb,
                                   db->local_abspath,
                                   db->relpath,
                                   db->depth,
                                   db->compared,
-                                  NULL /* ### parent_baton */,
+                                  db->pdb,
                                   scratch_pool));
-
+  }
 
   /* Report the property changes on the directory itself, if necessary. */
-  if (db->propchanges->nelts > 0)
+  if (db->skip)
+    {
+      /* Diff processor requested no directory details */
+    }
+  else if (db->propchanges->nelts > 0 || db->repos_only)
     {
-      /* The working copy properties at the base of the wc->repos comparison:
-         either BASE or WORKING. */
-      apr_hash_t *originalprops;
+      apr_hash_t *repos_props;
 
       if (db->added)
         {
-          originalprops = apr_hash_make(scratch_pool);
+          repos_props = apr_hash_make(scratch_pool);
         }
       else
         {
-          if (db->eb->diff_pristine)
-            {
-              SVN_ERR(svn_wc__db_read_pristine_props(&originalprops,
-                                                     eb->db, db->local_abspath,
-                                                     scratch_pool,
-                                                     scratch_pool));
-            }
-          else
-            {
-              apr_hash_t *base_props, *repos_props;
-
-              SVN_ERR(svn_wc__get_actual_props(&originalprops,
-                                               eb->db, db->local_abspath,
-                                               scratch_pool, scratch_pool));
-
-              /* Load the BASE and repository directory properties. */
-              SVN_ERR(svn_wc__db_base_get_props(&base_props,
-                                                eb->db, db->local_abspath,
-                                                scratch_pool, scratch_pool));
-
-              repos_props = svn_prop__patch(base_props, db->propchanges,
-                                            scratch_pool);
-
-              /* Recalculate b->propchanges as the change between WORKING
-                 and repos. */
-              SVN_ERR(svn_prop_diffs(&db->propchanges, repos_props,
-                                     originalprops, scratch_pool));
-            }
+          SVN_ERR(svn_wc__db_base_get_props(&repos_props,
+                                            eb->db, db->local_abspath,
+                                            scratch_pool, scratch_pool));
         }
 
-      if (!db->added)
-        {
-          reverse_propchanges(originalprops, db->propchanges, db->pool);
+      /* Add received property changes and entry props */
+      if (db->propchanges->nelts)
+        repos_props = svn_prop__patch(repos_props, db->propchanges,
+                                      scratch_pool);
 
-          SVN_ERR(eb->processor->dir_changed(db->relpath,
+      if (db->repos_only)
+        {
+          SVN_ERR(eb->processor->dir_deleted(db->relpath,
                                              db->left_src,
-                                             db->right_src,
-                                             originalprops,
-                                             svn_prop__patch(originalprops,
-                                                             db->propchanges,
-                                                             scratch_pool),
-                                             db->propchanges,
+                                             repos_props,
                                              db->pdb,
                                              eb->processor,
                                              scratch_pool));
+          reported_closed = TRUE;
         }
       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));
+          apr_hash_t *local_props;
+          apr_array_header_t *prop_changes;
+
+          if (eb->diff_pristine)
+            SVN_ERR(svn_wc__db_read_pristine_info(NULL, NULL, NULL, NULL, NULL,
+                                                  NULL, NULL, NULL, NULL,
+                                                  &local_props,
+                                                  eb->db, db->local_abspath,
+                                                  scratch_pool, scratch_pool));
+          else
+            SVN_ERR(svn_wc__db_read_props(&local_props,
+                                          eb->db, db->local_abspath,
+                                          scratch_pool, scratch_pool));
+
+          SVN_ERR(svn_prop_diffs(&prop_changes, local_props, repos_props,
+                                 scratch_pool));
+
+          /* ### as a good diff processor we should now only report changes
+                 if there are non-entry changes, but for now we stick to
+                 compatibility */
+
+          if (prop_changes->nelts)
+            {
+              SVN_ERR(eb->processor->dir_changed(db->relpath,
+                                                 db->left_src,
+                                                 db->right_src,
+                                                 repos_props,
+                                                 local_props,
+                                                 prop_changes,
+                                                 db->pdb,
+                                                 eb->processor,
+                                                 scratch_pool));
+              reported_closed = TRUE;
+          }
         }
-      reported_closed = TRUE;
     }
 
   /* Mark this directory as compared in the parent directory's baton,
      unless this is the root of the comparison. */
-  if (!reported_closed)
+  if (!reported_closed && !db->skip)
     SVN_ERR(eb->processor->dir_closed(db->relpath,
                                       db->left_src,
                                       db->right_src,
@@ -1640,9 +1779,9 @@ close_directory(void *dir_baton,
                                       eb->processor,
                                       scratch_pool));
 
-  if (db->parent_baton)
-    SVN_ERR(ensure_local_only_handled(db->parent_baton, db->name,
-                                      scratch_pool));
+  if (pb && !eb->local_before_remote && !db->repos_only && !db->ignoring_ancestry)
+    SVN_ERR(handle_local_only(pb, db->name, scratch_pool));
+
   SVN_ERR(maybe_done(db)); /* destroys scratch_pool */
 
   return SVN_NO_ERROR;
@@ -1661,29 +1800,41 @@ add_file(const char *path,
   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)
+  if (pb->skip_children)
+    {
+      fb->skip = TRUE;
+      return SVN_NO_ERROR;
+    }
+  else if (pb->repos_only || !eb->ignore_ancestry)
+    fb->repos_only = TRUE;
+  else
     {
       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;
-    }
+      if (!info || info->kind != svn_kind_file || NOT_PRESENT(info->status))
+        fb->repos_only = 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) */
+      if (!fb->repos_only && info->status != svn_wc__db_status_added)
+        fb->repos_only = TRUE;
+
+      if (!fb->repos_only)
+        {
+          /* Add this path to the parent directory's list of elements that
+             have been compared. */
+          fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
+          fb->ignoring_ancestry = TRUE;
+
+          svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
+        }
+    }
 
   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,
@@ -1712,20 +1863,51 @@ open_file(const char *path,
   fb = make_file_baton(path, FALSE, pb, file_pool);
   *file_baton = fb;
 
-  if (!fb->shadowed)
+  if (pb->skip_children)
+    fb->skip = TRUE;
+  else if (pb->repos_only)
+    fb->repos_only = TRUE;
+  else
     {
       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;
+      if (!info || info->kind != svn_kind_file || NOT_PRESENT(info->status))
+        fb->repos_only = TRUE;
+
+      if (!fb->repos_only)
+        switch (info->status)
+          {
+            case svn_wc__db_status_normal:
+              break;
+            case svn_wc__db_status_deleted:
+              fb->repos_only = TRUE;
+              if (!info->have_more_work)
+                svn_hash_sets(pb->compared,
+                              apr_pstrdup(pb->pool, fb->name), "");
+              break;
+            case svn_wc__db_status_added:
+              if (eb->ignore_ancestry)
+                fb->ignoring_ancestry = TRUE;
+              else
+                fb->repos_only = TRUE;
+              break;
+            default:
+              SVN_ERR_MALFUNCTION();
+        }
+
+      if (!fb->repos_only)
+        {
+          /* Add this path to the parent directory's list of elements that
+             have been compared. */
+          fb->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, fb->pool);
+          svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
+        }
     }
 
-  /* Add this filename to the parent directory's list of elements that
-     have been compared. */
-  svn_hash_sets(pb->compared, apr_pstrdup(pb->pool, fb->name), "");
+  fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);
 
   SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                                    NULL, NULL, NULL, &fb->base_checksum, NULL,
@@ -1733,9 +1915,6 @@ open_file(const char *path,
                                    eb->db, fb->local_abspath,
                                    fb->pool, fb->pool));
 
-  fb->left_src = svn_diff__source_create(eb->revnum, fb->pool);

[... 417 lines stripped ...]