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/02/04 21:48:13 UTC

svn commit: r1442344 [10/39] - in /subversion/branches/fsfs-format7: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/server-side/fsfsfixer/fixer/ contrib/server-side/svncutter/ doc/...

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/repos_diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/repos_diff.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/repos_diff.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/repos_diff.c Mon Feb  4 20:48:05 2013
@@ -34,6 +34,7 @@
 
 #include <apr_uri.h>
 #include <apr_md5.h>
+#include <assert.h>
 
 #include "svn_checksum.h"
 #include "svn_hash.h"
@@ -49,16 +50,15 @@
 
 #include "private/svn_subr_private.h"
 #include "private/svn_wc_private.h"
+#include "private/svn_editor.h"
 
 /* Overall crawler editor baton.  */
 struct edit_baton {
   /* The passed depth */
   svn_depth_t depth;
 
-  /* The callback and calback argument that implement the file comparison
-     function */
-  const svn_wc_diff_callbacks4_t *diff_callbacks;
-  void *diff_cmd_baton;
+  /* The result processor */
+  const svn_diff_tree_processor_t *processor;
 
   /* RA_SESSION is the open session for making requests to the RA layer */
   svn_ra_session_t *ra_session;
@@ -78,19 +78,6 @@ struct edit_baton {
   /* Empty hash used for adds. */
   apr_hash_t *empty_hash;
 
-  /* Hash used to check replaced paths. Key is path relative CWD,
-   * Value is *deleted_path_notify_t.
-   * All allocations are from edit_baton's pool. */
-  apr_hash_t *deleted_paths;
-
-  /* If the func is non-null, send notifications of actions. */
-  svn_wc_notify_func2_t notify_func;
-  void *notify_baton;
-
-  /* TRUE if the operation needs to walk deleted dirs on the "old" side.
-     FALSE otherwise. */
-  svn_boolean_t walk_deleted_repos_dirs;
-
   /* Whether to report text deltas */
   svn_boolean_t text_deltas;
 
@@ -135,7 +122,7 @@ struct dir_baton {
 
   /* The baton for the parent directory, or null if this is the root of the
      hierarchy to be compared. */
-  struct dir_baton *dir_baton;
+  struct dir_baton *parent_baton;
 
   /* The overall crawler editor baton. */
   struct edit_baton *edit_baton;
@@ -143,17 +130,31 @@ struct dir_baton {
   /* A cache of any property changes (svn_prop_t) received for this dir. */
   apr_array_header_t *propchanges;
 
+  /* Boolean indicating whether a node property was changed */
+  svn_boolean_t has_propchange;
+
+  /* Baton for svn_diff_tree_processor_t */
+  void *pdb;
+  svn_diff_source_t *left_source;
+  svn_diff_source_t *right_source;
+
   /* The pool passed in by add_dir, open_dir, or open_root.
      Also, the pool this dir baton is allocated in. */
   apr_pool_t *pool;
 
   /* Base revision of directory. */
   svn_revnum_t base_revision;
+
+  /* Number of users of baton. Its pool will be destroyed 0 */
+  int users;
 };
 
 /* File level baton.
  */
 struct file_baton {
+  /* Reference to parent baton */
+  struct dir_baton *parent_baton;
+
   /* Gets set if the file is added rather than replaced. */
   svn_boolean_t added;
 
@@ -199,6 +200,14 @@ struct file_baton {
   /* A cache of any property changes (svn_prop_t) received for this file. */
   apr_array_header_t *propchanges;
 
+  /* Boolean indicating whether a node property was changed */
+  svn_boolean_t has_propchange;
+
+  /* Baton for svn_diff_tree_processor_t */
+  void *pfb;
+  svn_diff_source_t *left_source;
+  svn_diff_source_t *right_source;
+
   /* The pool passed in by add_file or open_file.
      Also, the pool this file_baton is allocated in. */
   apr_pool_t *pool;
@@ -217,12 +226,12 @@ make_dir_baton(const char *path,
                struct edit_baton *edit_baton,
                svn_boolean_t added,
                svn_revnum_t base_revision,
-               apr_pool_t *pool)
+               apr_pool_t *result_pool)
 {
-  apr_pool_t *dir_pool = svn_pool_create(pool);
+  apr_pool_t *dir_pool = svn_pool_create(result_pool);
   struct dir_baton *dir_baton = apr_pcalloc(dir_pool, sizeof(*dir_baton));
 
-  dir_baton->dir_baton = parent_baton;
+  dir_baton->parent_baton = parent_baton;
   dir_baton->edit_baton = edit_baton;
   dir_baton->added = added;
   dir_baton->tree_conflicted = FALSE;
@@ -230,12 +239,38 @@ make_dir_baton(const char *path,
   dir_baton->skip_children = FALSE;
   dir_baton->pool = dir_pool;
   dir_baton->path = apr_pstrdup(dir_pool, path);
-  dir_baton->propchanges  = apr_array_make(pool, 1, sizeof(svn_prop_t));
+  dir_baton->propchanges  = apr_array_make(dir_pool, 8, sizeof(svn_prop_t));
   dir_baton->base_revision = base_revision;
+  dir_baton->users++;
+
+  if (parent_baton)
+    parent_baton->users++;
 
   return dir_baton;
 }
 
+/* New function. Called by everyone who has a reference when done */
+static svn_error_t *
+release_dir(struct dir_baton *db)
+{
+  assert(db->users > 0);
+
+  db->users--;
+  if (db->users)
+     return SVN_NO_ERROR;
+
+  {
+    struct dir_baton *pb = db->parent_baton;
+
+    svn_pool_destroy(db->pool);
+
+    if (pb != NULL)
+      SVN_ERR(release_dir(pb));
+  }
+
+  return SVN_NO_ERROR;
+}
+
 /* Create a new file baton for PATH in POOL, which is a child of
  * directory PARENT_PATH. ADDED is set if this file is being added
  * rather than replaced.  EDIT_BATON is a pointer to the global edit
@@ -243,84 +278,44 @@ make_dir_baton(const char *path,
  */
 static struct file_baton *
 make_file_baton(const char *path,
+                struct dir_baton *parent_baton,
                 svn_boolean_t added,
-                struct edit_baton *edit_baton,
-                apr_pool_t *pool)
+                apr_pool_t *result_pool)
 {
-  apr_pool_t *file_pool = svn_pool_create(pool);
+  apr_pool_t *file_pool = svn_pool_create(result_pool);
   struct file_baton *file_baton = apr_pcalloc(file_pool, sizeof(*file_baton));
 
-  file_baton->edit_baton = edit_baton;
+  file_baton->parent_baton = parent_baton;
+  file_baton->edit_baton = parent_baton->edit_baton;
   file_baton->added = added;
   file_baton->tree_conflicted = FALSE;
   file_baton->skip = FALSE;
   file_baton->pool = file_pool;
   file_baton->path = apr_pstrdup(file_pool, path);
-  file_baton->propchanges  = apr_array_make(pool, 1, sizeof(svn_prop_t));
-  file_baton->base_revision = edit_baton->revision;
+  file_baton->propchanges  = apr_array_make(file_pool, 8, sizeof(svn_prop_t));
+  file_baton->base_revision = parent_baton->edit_baton->revision;
 
-  return file_baton;
-}
+  parent_baton->users++;
 
-/* Helper function: return up to two svn:mime-type values buried
- * within a file baton.  Set *MIMETYPE1 to the value within the file's
- * pristine properties, or NULL if not available.  Set *MIMETYPE2 to
- * the value within the "new" file's propchanges, or NULL if not
- * available.
- */
-static void
-get_file_mime_types(const char **mimetype1,
-                    const char **mimetype2,
-                    struct file_baton *b)
-{
-  /* Defaults */
-  *mimetype1 = NULL;
-  *mimetype2 = NULL;
-
-  if (b->pristine_props)
-    {
-      svn_string_t *pristine_val;
-      pristine_val = apr_hash_get(b->pristine_props, SVN_PROP_MIME_TYPE,
-                                  strlen(SVN_PROP_MIME_TYPE));
-      if (pristine_val)
-        *mimetype2 = *mimetype1 = pristine_val->data;
-    }
-
-  if (b->propchanges)
-    {
-      int i;
-      svn_prop_t *propchange;
-
-      for (i = 0; i < b->propchanges->nelts; i++)
-        {
-          propchange = &APR_ARRAY_IDX(b->propchanges, i, svn_prop_t);
-          if (strcmp(propchange->name, SVN_PROP_MIME_TYPE) == 0)
-            {
-              if (propchange->value)
-                *mimetype2 = propchange->value->data;
-              break;
-            }
-        }
-    }
+  return file_baton;
 }
 
-
-/* Get revision B->base_revision of the file described by B from the
- * repository, through B->edit_baton->ra_session.
+/* Get revision FB->base_revision of the file described by FB from the
+ * repository, through FB->edit_baton->ra_session.
  *
  * Unless PROPS_ONLY is true:
- *   Set B->path_start_revision to the path of a new temporary file containing
+ *   Set FB->path_start_revision to the path of a new temporary file containing
  *   the file's text.
- *   Set B->start_md5_checksum to that file's MD-5 checksum.
- *   Install a pool cleanup handler on B->pool to delete the file.
+ *   Set FB->start_md5_checksum to that file's MD-5 checksum.
+ *   Install a pool cleanup handler on FB->pool to delete the file.
  *
  * Always:
- *   Set B->pristine_props to a new hash containing the file's properties.
+ *   Set FB->pristine_props to a new hash containing the file's properties.
  *
- * Allocate all results in B->pool.
+ * Allocate all results in FB->pool.
  */
 static svn_error_t *
-get_file_from_ra(struct file_baton *b,
+get_file_from_ra(struct file_baton *fb,
                  svn_boolean_t props_only,
                  apr_pool_t *scratch_pool)
 {
@@ -328,30 +323,30 @@ get_file_from_ra(struct file_baton *b,
     {
       svn_stream_t *fstream;
 
-      SVN_ERR(svn_stream_open_unique(&fstream, &(b->path_start_revision), NULL,
-                                     svn_io_file_del_on_pool_cleanup,
-                                     b->pool, scratch_pool));
+      SVN_ERR(svn_stream_open_unique(&fstream, &(fb->path_start_revision),
+                                     NULL, svn_io_file_del_on_pool_cleanup,
+                                     fb->pool, scratch_pool));
 
-      fstream = svn_stream_checksummed2(fstream, NULL, &b->start_md5_checksum,
+      fstream = svn_stream_checksummed2(fstream, NULL, &fb->start_md5_checksum,
                                         svn_checksum_md5, TRUE, scratch_pool);
 
       /* Retrieve the file and its properties */
-      SVN_ERR(svn_ra_get_file(b->edit_baton->ra_session,
-                              b->path,
-                              b->base_revision,
+      SVN_ERR(svn_ra_get_file(fb->edit_baton->ra_session,
+                              fb->path,
+                              fb->base_revision,
                               fstream, NULL,
-                              &(b->pristine_props),
-                              b->pool));
+                              &(fb->pristine_props),
+                              fb->pool));
       SVN_ERR(svn_stream_close(fstream));
     }
   else
     {
-      SVN_ERR(svn_ra_get_file(b->edit_baton->ra_session,
-                              b->path,
-                              b->base_revision,
+      SVN_ERR(svn_ra_get_file(fb->edit_baton->ra_session,
+                              fb->path,
+                              fb->base_revision,
                               NULL, NULL,
-                              &(b->pristine_props),
-                              b->pool));
+                              &(fb->pristine_props),
+                              fb->pool));
     }
 
   return SVN_NO_ERROR;
@@ -420,7 +415,6 @@ remove_non_prop_changes(apr_hash_t *pris
     }
 }
 
-
 /* Get the empty file associated with the edit baton. This is cached so
  * that it can be reused, all empty files are the same.
  */
@@ -461,44 +455,68 @@ open_root(void *edit_baton,
           void **root_baton)
 {
   struct edit_baton *eb = edit_baton;
-  struct dir_baton *b = make_dir_baton("", NULL, eb, FALSE, base_revision,
-                                       pool);
+  struct dir_baton *db = make_dir_baton("", NULL, eb, FALSE, base_revision,
+                                        eb->pool);
+
+  db->left_source = svn_diff__source_create(eb->revision, db->pool);
+  db->right_source = svn_diff__source_create(eb->target_revision, db->pool);
+
+  SVN_ERR(eb->processor->dir_opened(&db->pdb,
+                                    &db->skip,
+                                    &db->skip_children,
+                                    "",
+                                    db->left_source,
+                                    db->right_source,
+                                    NULL,
+                                    NULL,
+                                    eb->processor,
+                                    db->pool,
+                                    db->pool /* scratch_pool */));
 
-  *root_baton = b;
+  *root_baton = db;
   return SVN_NO_ERROR;
 }
 
 /* Compare a file being deleted against an empty file.
  */
 static svn_error_t *
-diff_deleted_file(svn_wc_notify_state_t *state_p,
-                  svn_boolean_t *tree_conflicted_p,
-                  const char *path,
-                  struct edit_baton *eb,
+diff_deleted_file(const char *path,
+                  struct dir_baton *db,
                   apr_pool_t *scratch_pool)
 {
-  struct file_baton *b = make_file_baton(path, FALSE, eb, scratch_pool);
-/*  struct edit_baton *eb = b->edit_baton;*/
-  const char *mimetype1, *mimetype2;
+  struct edit_baton *eb = db->edit_baton;
+  struct file_baton *fb = make_file_baton(path, db, FALSE, scratch_pool);
+  svn_boolean_t skip = FALSE;
+  svn_diff_source_t *left_source = svn_diff__source_create(eb->revision,
+                                                           scratch_pool);
 
   if (eb->cancel_func)
     SVN_ERR(eb->cancel_func(eb->cancel_baton));
 
-  if (eb->text_deltas)
-    SVN_ERR(get_file_from_ra(b, FALSE, scratch_pool));
-  else
-    SVN_ERR(get_empty_file(eb, &b->path_start_revision));
-  SVN_ERR(get_empty_file(eb, &b->path_end_revision));
-  get_file_mime_types(&mimetype1, &mimetype2, b);
-
-  SVN_ERR(eb->diff_callbacks->file_deleted(state_p, tree_conflicted_p,
-                                           b->path,
-                                           b->path_start_revision,
-                                           b->path_end_revision,
-                                           mimetype1, mimetype2,
-                                           b->pristine_props,
-                                           eb->diff_cmd_baton,
-                                           scratch_pool));
+  SVN_ERR(eb->processor->file_opened(&fb->pfb, &skip, path,
+                                     left_source,
+                                     NULL /* right_source */,
+                                     NULL /* copyfrom_source */,
+                                     db->pdb,
+                                     eb->processor,
+                                     scratch_pool, scratch_pool));
+
+  if (eb->cancel_func)
+    SVN_ERR(eb->cancel_func(eb->cancel_baton));
+
+  if (skip)
+    return SVN_NO_ERROR;
+
+  SVN_ERR(get_file_from_ra(fb, ! eb->text_deltas, scratch_pool));
+
+  SVN_ERR(eb->processor->file_deleted(fb->path,
+                                      left_source,
+                                      fb->path_start_revision,
+                                      fb->pristine_props,
+                                      fb->pfb,
+                                      eb->processor,
+                                      scratch_pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -512,65 +530,88 @@ diff_deleted_file(svn_wc_notify_state_t 
  */
 /* ### TODO: Handle depth. */
 static svn_error_t *
-diff_deleted_dir(svn_wc_notify_state_t *state_p,
-                 svn_boolean_t *tree_conflicted_p,
-                 const char *dir,
-                 struct edit_baton *eb,
-                 apr_pool_t *pool)
+diff_deleted_dir(const char *path,
+                 struct dir_baton *pb,
+                 apr_pool_t *scratch_pool)
 {
-  apr_hash_t *dirents;
-  apr_pool_t *iterpool = svn_pool_create(pool);
-  apr_hash_index_t *hi;
+  struct edit_baton *eb = pb->edit_baton;
+  struct dir_baton *db;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  svn_boolean_t skip = FALSE;
+  svn_boolean_t skip_children = FALSE;
+  apr_hash_t *dirents = NULL;
+  apr_hash_t *left_props = NULL;
+  svn_diff_source_t *left_source = svn_diff__source_create(eb->revision,
+                                                           scratch_pool);
+  db = make_dir_baton(path, pb, pb->edit_baton, FALSE, SVN_INVALID_REVNUM,
+                      scratch_pool);
 
   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(eb->revision));
 
   if (eb->cancel_func)
     SVN_ERR(eb->cancel_func(eb->cancel_baton));
 
-  SVN_ERR(eb->diff_callbacks->dir_deleted(
-                        state_p, tree_conflicted_p, dir,
-                        eb->diff_cmd_baton, pool));
+  SVN_ERR(eb->processor->dir_opened(&db->pdb, &skip, &skip_children,
+                                    path,
+                                    left_source,
+                                    NULL /* right_source */,
+                                    NULL /* copyfrom_source */,
+                                    pb->pdb,
+                                    eb->processor,
+                                    scratch_pool, iterpool));
+
+  if (!skip || !skip_children)
+    SVN_ERR(svn_ra_get_dir2(eb->ra_session,
+                            skip_children ? NULL : &dirents,
+                            NULL,
+                            skip ? NULL : &left_props,
+                            path,
+                            eb->revision,
+                            SVN_DIRENT_KIND,
+                            scratch_pool));
 
   /* The "old" dir will be skipped by the repository report.  If required,
    * crawl it recursively, diffing each file against the empty file.  This
    * is a workaround for issue 2333 "'svn diff URL1 URL2' not reverse of
    * 'svn diff URL2 URL1'". */
-  if (! eb->walk_deleted_repos_dirs)
+  if (! skip_children)
     {
-      svn_pool_destroy(iterpool);
-      return SVN_NO_ERROR;
-    }
+      apr_hash_index_t *hi;
 
-  SVN_ERR(svn_ra_get_dir2(eb->ra_session,
-                          &dirents,
-                          NULL, NULL,
-                          dir,
-                          eb->revision,
-                          SVN_DIRENT_KIND,
-                          pool));
-
-  for (hi = apr_hash_first(pool, dirents); hi;
-       hi = apr_hash_next(hi))
-    {
-      const char *path;
-      const char *name = svn__apr_hash_index_key(hi);
-      svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
+      for (hi = apr_hash_first(scratch_pool, dirents); hi;
+           hi = apr_hash_next(hi))
+        {
+          const char *child_path;
+          const char *name = svn__apr_hash_index_key(hi);
+          svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
 
-      svn_pool_clear(iterpool);
+          svn_pool_clear(iterpool);
 
-      path = svn_relpath_join(dir, name, iterpool);
+          child_path = svn_relpath_join(path, name, iterpool);
 
-      if (dirent->kind == svn_node_file)
-        {
-          SVN_ERR(diff_deleted_file(NULL, NULL, path, eb, iterpool));
+          if (dirent->kind == svn_node_file)
+            {
+              SVN_ERR(diff_deleted_file(child_path, db, iterpool));
+            }
+          else if (dirent->kind == svn_node_dir)
+            {
+              SVN_ERR(diff_deleted_dir(child_path, db, iterpool));
+            }
         }
+    }
 
-      if (dirent->kind == svn_node_dir)
-        {
-          SVN_ERR(diff_deleted_dir(NULL, NULL, path, eb, iterpool));
-        }
+  if (! skip)
+    {
+      SVN_ERR(eb->processor->dir_deleted(path,
+                                         left_source,
+                                         left_props,
+                                         db->pdb,
+                                         eb->processor,
+                                         scratch_pool));
     }
 
+  SVN_ERR(release_dir(db));
+
   svn_pool_destroy(iterpool);
   return SVN_NO_ERROR;
 }
@@ -585,14 +626,10 @@ delete_entry(const char *path,
   struct dir_baton *pb = parent_baton;
   struct edit_baton *eb = pb->edit_baton;
   svn_node_kind_t kind;
-  svn_wc_notify_state_t state = svn_wc_notify_state_inapplicable;
-  svn_wc_notify_action_t action = svn_wc_notify_skip;
-  svn_boolean_t tree_conflicted = FALSE;
   apr_pool_t *scratch_pool;
 
-  /* Skip *everything* within a newly tree-conflicted directory,
-   * and directories the children of which should be skipped. */
-  if (pb->skip || pb->tree_conflicted || pb->skip_children)
+  /* Process skips. */
+  if (pb->skip_children)
     return SVN_NO_ERROR;
 
   scratch_pool = svn_pool_create(eb->pool);
@@ -605,39 +642,18 @@ delete_entry(const char *path,
     {
     case svn_node_file:
       {
-        SVN_ERR(diff_deleted_file(&state, &tree_conflicted, path, eb,
-                                  scratch_pool));
+        SVN_ERR(diff_deleted_file(path, pb, scratch_pool));
         break;
       }
     case svn_node_dir:
       {
-        SVN_ERR(diff_deleted_dir(&state, &tree_conflicted, path, eb,
-                                 scratch_pool));
+        SVN_ERR(diff_deleted_dir(path, pb, scratch_pool));
         break;
       }
     default:
       break;
     }
 
-  if ((state != svn_wc_notify_state_missing)
-      && (state != svn_wc_notify_state_obstructed)
-      && !tree_conflicted)
-    {
-      action = svn_wc_notify_update_delete;
-    }
-
-  if (eb->notify_func)
-    {
-      const char *deleted_path = apr_pstrdup(eb->pool, path);
-      deleted_path_notify_t *dpn = apr_pcalloc(eb->pool, sizeof(*dpn));
-
-      dpn->kind = kind;
-      dpn->action = tree_conflicted ? svn_wc_notify_tree_conflict : action;
-      dpn->state = state;
-      dpn->tree_conflicted = tree_conflicted;
-      apr_hash_set(eb->deleted_paths, deleted_path, APR_HASH_KEY_STRING, dpn);
-    }
-
   svn_pool_destroy(scratch_pool);
 
   return SVN_NO_ERROR;
@@ -654,80 +670,35 @@ add_directory(const char *path,
 {
   struct dir_baton *pb = parent_baton;
   struct edit_baton *eb = pb->edit_baton;
-  struct dir_baton *b;
-  svn_wc_notify_state_t state;
+  struct dir_baton *db;
 
   /* ### TODO: support copyfrom? */
 
-  b = make_dir_baton(path, pb, eb, TRUE, SVN_INVALID_REVNUM, pool);
-  *child_baton = b;
+  db = make_dir_baton(path, pb, eb, TRUE, SVN_INVALID_REVNUM, pb->pool);
+  *child_baton = db;
 
   /* Skip *everything* within a newly tree-conflicted directory,
    * and directories the children of which should be skipped. */
-  if (pb->skip || pb->tree_conflicted || pb->skip_children)
+  if (pb->skip_children)
     {
-      b->skip = TRUE;
+      db->skip = TRUE;
+      db->skip_children = TRUE;
       return SVN_NO_ERROR;
     }
 
+  db->right_source = svn_diff__source_create(eb->target_revision,
+                                             db->pool);
 
-  SVN_ERR(eb->diff_callbacks->dir_added(
-                &state, &b->tree_conflicted,
-                &b->skip, &b->skip_children, b->path,
-                eb->target_revision, copyfrom_path, copyfrom_revision,
-                eb->diff_cmd_baton, pool));
-
-  /* Notifications for directories are done at close_directory time.
-   * But for paths at which the editor drive adds directories, we make an
-   * exception to this rule, so that the path appears in the output before
-   * any children of the newly added directory. Since a deletion at this path
-   * must have happened before this addition, we can safely notify about
-   * replaced directories here, too. */
-  if (eb->notify_func)
-    {
-      deleted_path_notify_t *dpn;
-      svn_wc_notify_t *notify;
-      svn_wc_notify_action_t action;
-      svn_node_kind_t kind = svn_node_dir;
-
-      /* Find out if a pending delete notification for this path is
-       * still around. */
-      dpn = apr_hash_get(eb->deleted_paths, b->path, APR_HASH_KEY_STRING);
-      if (dpn)
-        {
-          /* If any was found, we will handle the pending 'deleted path
-           * notification' (DPN) here. Remove it from the list. */
-          apr_hash_set(eb->deleted_paths, b->path,
-                       APR_HASH_KEY_STRING, NULL);
-
-          /* the pending delete might be on a different node kind. */
-          kind = dpn->kind;
-          state = dpn->state;
-        }
-
-      /* Determine what the notification (ACTION) should be.
-       * In case of a pending 'delete', this might become a 'replace'. */
-      if (b->tree_conflicted)
-        action = svn_wc_notify_tree_conflict;
-      else if (dpn)
-        {
-          if (dpn->action == svn_wc_notify_update_delete)
-            action = svn_wc_notify_update_replace;
-          else
-            /* Note: dpn->action might be svn_wc_notify_tree_conflict */
-            action = dpn->action;
-        }
-      else if (state == svn_wc_notify_state_missing ||
-               state == svn_wc_notify_state_obstructed)
-        action = svn_wc_notify_skip;
-      else
-        action = svn_wc_notify_update_add;
-
-      notify = svn_wc_create_notify(b->path, action, pool);
-      notify->kind = kind;
-      notify->content_state = notify->prop_state = state;
-      (*eb->notify_func)(eb->notify_baton, notify, pool);
-    }
+  SVN_ERR(eb->processor->dir_opened(&db->pdb,
+                                    &db->skip,
+                                    &db->skip_children,
+                                    db->path,
+                                    NULL,
+                                    db->right_source,
+                                    NULL /* copyfrom_source */,
+                                    pb->pdb,
+                                    eb->processor,
+                                    db->pool, db->pool));
 
   return SVN_NO_ERROR;
 }
@@ -742,24 +713,32 @@ open_directory(const char *path,
 {
   struct dir_baton *pb = parent_baton;
   struct edit_baton *eb = pb->edit_baton;
-  struct dir_baton *b;
+  struct dir_baton *db;
 
-  b = make_dir_baton(path, pb, pb->edit_baton, FALSE, base_revision, pool);
+  db = make_dir_baton(path, pb, eb, FALSE, base_revision, pb->pool);
 
-  *child_baton = b;
+  *child_baton = db;
 
-  /* Skip *everything* within a newly tree-conflicted directory
-   * and directories the children of which should be skipped. */
-  if (pb->skip || pb->tree_conflicted || pb->skip_children)
+  /* Process Skips. */
+  if (pb->skip_children)
     {
-      b->skip = TRUE;
+      db->skip = TRUE;
+      db->skip_children = TRUE;
       return SVN_NO_ERROR;
     }
 
-  SVN_ERR(eb->diff_callbacks->dir_opened(
-                &b->tree_conflicted, &b->skip,
-                &b->skip_children, b->path, base_revision,
-                b->edit_baton->diff_cmd_baton, pool));
+  db->left_source = svn_diff__source_create(eb->revision, db->pool);
+  db->right_source = svn_diff__source_create(eb->target_revision, db->pool);
+
+  SVN_ERR(eb->processor->dir_opened(&db->pdb,
+                                    &db->skip, &db->skip_children,
+                                    path,
+                                    db->left_source,
+                                    db->right_source,
+                                    NULL /* copyfrom */,
+                                    pb ? pb->pdb : NULL,
+                                    eb->processor,
+                                    db->pool, db->pool));
 
   return SVN_NO_ERROR;
 }
@@ -775,22 +754,34 @@ add_file(const char *path,
          void **file_baton)
 {
   struct dir_baton *pb = parent_baton;
-  struct file_baton *b;
+  struct edit_baton *eb = pb->edit_baton;
+  struct file_baton *fb;
 
   /* ### TODO: support copyfrom? */
 
-  b = make_file_baton(path, TRUE, pb->edit_baton, pool);
-  *file_baton = b;
+  fb = make_file_baton(path, pb, TRUE, pb->pool);
+  *file_baton = fb;
 
-  /* Skip *everything* within a newly tree-conflicted directory.
-   * and directories the children of which should be skipped. */
-  if (pb->skip || pb->tree_conflicted || pb->skip_children)
+  /* Process Skips. */
+  if (pb->skip_children)
     {
-      b->skip = TRUE;
+      fb->skip = TRUE;
       return SVN_NO_ERROR;
     }
 
-  b->pristine_props = pb->edit_baton->empty_hash;
+  fb->pristine_props = pb->edit_baton->empty_hash;
+
+  fb->right_source = svn_diff__source_create(eb->target_revision, fb->pool);
+
+  SVN_ERR(eb->processor->file_opened(&fb->pfb,
+                                     &fb->skip,
+                                     path,
+                                     NULL,
+                                     fb->right_source,
+                                     NULL /* copy source */,
+                                     pb->pdb,
+                                     eb->processor,
+                                     fb->pool, fb->pool));
 
   return SVN_NO_ERROR;
 }
@@ -804,24 +795,32 @@ open_file(const char *path,
           void **file_baton)
 {
   struct dir_baton *pb = parent_baton;
-  struct file_baton *b;
+  struct file_baton *fb;
   struct edit_baton *eb = pb->edit_baton;
-  b = make_file_baton(path, FALSE, pb->edit_baton, pool);
-  *file_baton = b;
+  fb = make_file_baton(path, pb, FALSE, pb->pool);
+  *file_baton = fb;
 
-  /* Skip *everything* within a newly tree-conflicted directory
-   * and directories the children of which should be skipped. */
-  if (pb->skip || pb->tree_conflicted || pb->skip_children)
+  /* Process Skips. */
+  if (pb->skip_children)
     {
-      b->skip = TRUE;
+      fb->skip = TRUE;
       return SVN_NO_ERROR;
     }
 
-  b->base_revision = base_revision;
+  fb->base_revision = base_revision;
+
+  fb->left_source = svn_diff__source_create(eb->revision, fb->pool);
+  fb->right_source = svn_diff__source_create(eb->target_revision, fb->pool);
 
-  SVN_ERR(eb->diff_callbacks->file_opened(
-                   &b->tree_conflicted, &b->skip,
-                   b->path, base_revision, eb->diff_cmd_baton, pool));
+  SVN_ERR(eb->processor->file_opened(&fb->pfb,
+                                     &fb->skip,
+                                     path,
+                                     fb->left_source,
+                                     fb->right_source,
+                                     NULL /* copy source */,
+                                     pb->pdb,
+                                     eb->processor,
+                                     fb->pool, fb->pool));
 
   return SVN_NO_ERROR;
 }
@@ -831,14 +830,15 @@ static svn_error_t *
 window_handler(svn_txdelta_window_t *window,
                void *window_baton)
 {
-  struct file_baton *b = window_baton;
+  struct file_baton *fb = window_baton;
 
-  SVN_ERR(b->apply_handler(window, b->apply_baton));
+  SVN_ERR(fb->apply_handler(window, fb->apply_baton));
 
   if (!window)
     {
-      b->result_md5_checksum = svn_checksum__from_digest_md5(b->result_digest,
-                                                             b->pool);
+      fb->result_md5_checksum = svn_checksum__from_digest_md5(
+                                        fb->result_digest,
+                                        fb->pool);
     }
 
   return SVN_NO_ERROR;
@@ -852,13 +852,13 @@ apply_textdelta(void *file_baton,
                 svn_txdelta_window_handler_t *handler,
                 void **handler_baton)
 {
-  struct file_baton *b = file_baton;
+  struct file_baton *fb = file_baton;
   svn_stream_t *src_stream;
   svn_stream_t *result_stream;
-  apr_pool_t *scratch_pool = b->pool;
+  apr_pool_t *scratch_pool = fb->pool;
 
   /* Skip *everything* within a newly tree-conflicted directory. */
-  if (b->skip)
+  if (fb->skip)
     {
       *handler = svn_delta_noop_window_handler;
       *handler_baton = NULL;
@@ -866,11 +866,11 @@ apply_textdelta(void *file_baton,
     }
 
   /* If we're not sending file text, then ignore any that we receive. */
-  if (! b->edit_baton->text_deltas)
+  if (! fb->edit_baton->text_deltas)
     {
       /* Supply valid paths to indicate there is a text change. */
-      SVN_ERR(get_empty_file(b->edit_baton, &b->path_start_revision));
-      SVN_ERR(get_empty_file(b->edit_baton, &b->path_end_revision));
+      SVN_ERR(get_empty_file(fb->edit_baton, &fb->path_start_revision));
+      SVN_ERR(get_empty_file(fb->edit_baton, &fb->path_end_revision));
 
       *handler = svn_delta_noop_window_handler;
       *handler_baton = NULL;
@@ -879,12 +879,12 @@ apply_textdelta(void *file_baton,
     }
 
   /* We need the expected pristine file, so go get it */
-  if (!b->added)
-    SVN_ERR(get_file_from_ra(b, FALSE, scratch_pool));
+  if (!fb->added)
+    SVN_ERR(get_file_from_ra(fb, FALSE, scratch_pool));
   else
-    SVN_ERR(get_empty_file(b->edit_baton, &(b->path_start_revision)));
+    SVN_ERR(get_empty_file(fb->edit_baton, &(fb->path_start_revision)));
 
-  SVN_ERR_ASSERT(b->path_start_revision != NULL);
+  SVN_ERR_ASSERT(fb->path_start_revision != NULL);
 
   if (base_md5_digest != NULL)
     {
@@ -893,30 +893,30 @@ apply_textdelta(void *file_baton,
       SVN_ERR(svn_checksum_parse_hex(&base_md5_checksum, svn_checksum_md5,
                                      base_md5_digest, scratch_pool));
 
-      if (!svn_checksum_match(base_md5_checksum, b->start_md5_checksum))
+      if (!svn_checksum_match(base_md5_checksum, fb->start_md5_checksum))
         return svn_error_trace(svn_checksum_mismatch_err(
                                       base_md5_checksum,
-                                      b->start_md5_checksum,
+                                      fb->start_md5_checksum,
                                       scratch_pool,
                                       _("Base checksum mismatch for '%s'"),
-                                      b->path));
+                                      fb->path));
     }
 
   /* Open the file to be used as the base for second revision */
-  SVN_ERR(svn_stream_open_readonly(&src_stream, b->path_start_revision,
+  SVN_ERR(svn_stream_open_readonly(&src_stream, fb->path_start_revision,
                                    scratch_pool, scratch_pool));
 
   /* Open the file that will become the second revision after applying the
      text delta, it starts empty */
-  SVN_ERR(svn_stream_open_unique(&result_stream, &b->path_end_revision, NULL,
+  SVN_ERR(svn_stream_open_unique(&result_stream, &fb->path_end_revision, NULL,
                                  svn_io_file_del_on_pool_cleanup,
                                  scratch_pool, scratch_pool));
 
   svn_txdelta_apply(src_stream,
                     result_stream,
-                    b->result_digest,
-                    b->path, b->pool,
-                    &(b->apply_handler), &(b->apply_baton));
+                    fb->result_digest,
+                    fb->path, fb->pool,
+                    &(fb->apply_handler), &(fb->apply_baton));
 
   *handler = window_handler;
   *handler_baton = file_baton;
@@ -939,20 +939,20 @@ close_file(void *file_baton,
            const char *expected_md5_digest,
            apr_pool_t *pool)
 {
-  struct file_baton *b = file_baton;
-  struct edit_baton *eb = b->edit_baton;
-  svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
-  svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
+  struct file_baton *fb = file_baton;
+  struct dir_baton *pb = fb->parent_baton;
+  struct edit_baton *eb = fb->edit_baton;
   apr_pool_t *scratch_pool;
 
   /* Skip *everything* within a newly tree-conflicted directory. */
-  if (b->skip)
+  if (fb->skip)
     {
-      svn_pool_destroy(b->pool);
+      svn_pool_destroy(fb->pool);
+      SVN_ERR(release_dir(pb));
       return SVN_NO_ERROR;
     }
 
-  scratch_pool = b->pool;
+  scratch_pool = fb->pool;
 
   if (expected_md5_digest && eb->text_deltas)
     {
@@ -961,112 +961,63 @@ close_file(void *file_baton,
       SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
                                      expected_md5_digest, scratch_pool));
 
-      if (!svn_checksum_match(expected_md5_checksum, b->result_md5_checksum))
+      if (!svn_checksum_match(expected_md5_checksum, fb->result_md5_checksum))
         return svn_error_trace(svn_checksum_mismatch_err(
                                       expected_md5_checksum,
-                                      b->result_md5_checksum,
+                                      fb->result_md5_checksum,
                                       pool,
                                       _("Checksum mismatch for '%s'"),
-                                      b->path));
+                                      fb->path));
     }
 
-  if (!b->added && b->propchanges->nelts > 0)
+  if (fb->added || fb->path_end_revision || fb->has_propchange)
     {
-      if (!b->pristine_props)
+      apr_hash_t *right_props;
+
+      if (!fb->added && !fb->pristine_props)
         {
           /* We didn't receive a text change, so we have no pristine props.
              Retrieve just the props now. */
-          SVN_ERR(get_file_from_ra(b, TRUE, scratch_pool));
+          SVN_ERR(get_file_from_ra(fb, TRUE, scratch_pool));
         }
 
-      remove_non_prop_changes(b->pristine_props, b->propchanges);
-    }
+      if (fb->pristine_props)
+        remove_non_prop_changes(fb->pristine_props, fb->propchanges);
 
-  if (b->path_end_revision || b->propchanges->nelts > 0)
-    {
-      const char *mimetype1, *mimetype2;
-      get_file_mime_types(&mimetype1, &mimetype2, b);
+      right_props = svn_prop__patch(fb->pristine_props, fb->propchanges,
+                                    fb->pool);
 
-
-      if (b->added)
-        SVN_ERR(eb->diff_callbacks->file_added(
-                 &content_state, &prop_state, &b->tree_conflicted,
-                 b->path,
-                 b->path_end_revision ? b->path_start_revision : NULL,
-                 b->path_end_revision,
-                 0,
-                 b->edit_baton->target_revision,
-                 mimetype1, mimetype2,
-                 NULL, SVN_INVALID_REVNUM,
-                 b->propchanges, b->pristine_props,
-                 b->edit_baton->diff_cmd_baton,
-                 scratch_pool));
+      if (fb->added)
+        SVN_ERR(eb->processor->file_added(fb->path,
+                                          NULL /* copyfrom_src */,
+                                          fb->right_source,
+                                          NULL /* copyfrom_file */,
+                                          fb->path_end_revision,
+                                          NULL /* copyfrom_props */,
+                                          right_props,
+                                          fb->pfb,
+                                          eb->processor,
+                                          fb->pool));
       else
-        SVN_ERR(eb->diff_callbacks->file_changed(
-                 &content_state, &prop_state,
-                 &b->tree_conflicted, b->path,
-                 b->path_end_revision ? b->path_start_revision : NULL,
-                 b->path_end_revision,
-                 b->edit_baton->revision,
-                 b->edit_baton->target_revision,
-                 mimetype1, mimetype2,
-                 b->propchanges, b->pristine_props,
-                 b->edit_baton->diff_cmd_baton,
-                 scratch_pool));
+        SVN_ERR(eb->processor->file_changed(fb->path,
+                                            fb->left_source,
+                                            fb->right_source,
+                                            fb->path_end_revision
+                                                    ? fb->path_start_revision
+                                                    : NULL,
+                                            fb->path_end_revision,
+                                            fb->pristine_props,
+                                            right_props,
+                                            (fb->path_end_revision != NULL),
+                                            fb->propchanges,
+                                            fb->pfb,
+                                            eb->processor,
+                                            fb->pool));
     }
 
+  svn_pool_destroy(fb->pool); /* Destroy file and scratch pool */
 
-  if (eb->notify_func)
-    {
-      deleted_path_notify_t *dpn;
-      svn_wc_notify_t *notify;
-      svn_wc_notify_action_t action;
-      svn_node_kind_t kind = svn_node_file;
-
-      /* Find out if a pending delete notification for this path is
-       * still around. */
-      dpn = apr_hash_get(eb->deleted_paths, b->path, APR_HASH_KEY_STRING);
-      if (dpn)
-        {
-          /* If any was found, we will handle the pending 'deleted path
-           * notification' (DPN) here. Remove it from the list. */
-          apr_hash_set(eb->deleted_paths, b->path,
-                       APR_HASH_KEY_STRING, NULL);
-
-          /* the pending delete might be on a different node kind. */
-          kind = dpn->kind;
-          content_state = prop_state = dpn->state;
-        }
-
-      /* Determine what the notification (ACTION) should be.
-       * In case of a pending 'delete', this might become a 'replace'. */
-      if (b->tree_conflicted)
-        action = svn_wc_notify_tree_conflict;
-      else if (dpn)
-        {
-          if (dpn->action == svn_wc_notify_update_delete
-              && b->added)
-            action = svn_wc_notify_update_replace;
-          else
-            /* Note: dpn->action might be svn_wc_notify_tree_conflict */
-            action = dpn->action;
-        }
-      else if ((content_state == svn_wc_notify_state_missing)
-                || (content_state == svn_wc_notify_state_obstructed))
-        action = svn_wc_notify_skip;
-      else if (b->added)
-        action = svn_wc_notify_update_add;
-      else
-        action = svn_wc_notify_update_update;
-
-      notify = svn_wc_create_notify(b->path, action, scratch_pool);
-      notify->kind = kind;
-      notify->content_state = content_state;
-      notify->prop_state = prop_state;
-      (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
-    }
-
-  svn_pool_destroy(b->pool); /* Destroy file and scratch pool */
+  SVN_ERR(release_dir(pb));
 
   return SVN_NO_ERROR;
 }
@@ -1082,120 +1033,76 @@ static svn_error_t *
 close_directory(void *dir_baton,
                 apr_pool_t *pool)
 {
-  struct dir_baton *b = dir_baton;
-  struct edit_baton *eb = b->edit_baton;
-  svn_wc_notify_state_t content_state = svn_wc_notify_state_unknown;
-  svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
-  svn_boolean_t skipped = FALSE;
+  struct dir_baton *db = dir_baton;
+  struct edit_baton *eb = db->edit_baton;
   apr_pool_t *scratch_pool;
   apr_hash_t *pristine_props;
+  svn_boolean_t send_changed = FALSE;
 
-  /* Skip *everything* within a newly tree-conflicted directory. */
-  if (b->skip)
-    {
-      svn_pool_destroy(b->pool);
-      return SVN_NO_ERROR;
-    }
-
-  scratch_pool = b->pool;
+  scratch_pool = db->pool;
 
-  if (b->added)
+  if ((db->has_propchange || db->added) && !db->skip)
     {
-      pristine_props = eb->empty_hash;
-    }
-  else
-    {
-      SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, &pristine_props,
-                              b->path, b->base_revision, 0, scratch_pool));
-    }
-
-  if (b->propchanges->nelts > 0)
-    {
-      remove_non_prop_changes(pristine_props, b->propchanges);
-    }
-
-  if (b->propchanges->nelts > 0)
-    {
-      svn_boolean_t tree_conflicted = FALSE;
-      SVN_ERR(eb->diff_callbacks->dir_props_changed(
-               &prop_state, &tree_conflicted,
-               b->path, b->added,
-               b->propchanges, pristine_props,
-               b->edit_baton->diff_cmd_baton, scratch_pool));
-      if (tree_conflicted)
-        b->tree_conflicted = TRUE;
-
-      if (prop_state == svn_wc_notify_state_obstructed
-          || prop_state == svn_wc_notify_state_missing)
+      if (db->added)
         {
-          content_state = prop_state;
-          skipped = TRUE;
+          pristine_props = eb->empty_hash;
+        }
+      else
+        {
+          SVN_ERR(svn_ra_get_dir2(eb->ra_session, NULL, NULL, &pristine_props,
+                                  db->path, db->base_revision, 0, scratch_pool));
         }
-    }
-
-  SVN_ERR(eb->diff_callbacks->dir_closed(NULL, NULL, NULL,
-                                         b->path, b->added,
-                                         b->edit_baton->diff_cmd_baton,
-                                         scratch_pool));
-
-  /* Notify about any deleted paths within this directory that have not
-   * already been notified. */
-  if (!skipped && !b->added && eb->notify_func)
-    {
-      apr_hash_index_t *hi;
 
-      for (hi = apr_hash_first(pool, eb->deleted_paths); hi;
-           hi = apr_hash_next(hi))
+      if (db->propchanges->nelts > 0)
         {
-          svn_wc_notify_t *notify;
-          const char *deleted_path = svn__apr_hash_index_key(hi);
-          deleted_path_notify_t *dpn = svn__apr_hash_index_val(hi);
-
-          /* Ignore paths which are not children of b->path.  (There
-             should be none due to editor ordering constraints, but
-             ra_serf drops the ball here -- see issue #3802 for
-             details.) */
-          if (! svn_relpath_skip_ancestor(b->path, deleted_path))
-            continue;
-
-          notify = svn_wc_create_notify(deleted_path, dpn->action, pool);
-          notify->kind = dpn->kind;
-          notify->content_state = notify->prop_state = dpn->state;
-          notify->lock_state = svn_wc_notify_lock_state_inapplicable;
-          (*eb->notify_func)(eb->notify_baton, notify, pool);
-          apr_hash_set(eb->deleted_paths, deleted_path,
-                       APR_HASH_KEY_STRING, NULL);
+          remove_non_prop_changes(pristine_props, db->propchanges);
         }
-    }
 
-  /* Notify about this directory itself (unless it was added, in which
-   * case the notification was done at that time). */
-  if (!b->added && eb->notify_func)
-    {
-      svn_wc_notify_t *notify;
-      svn_wc_notify_action_t action;
-
-      if (b->tree_conflicted)
-        action = svn_wc_notify_tree_conflict;
-      else if (skipped)
-        action = svn_wc_notify_skip;
-      else
-        action = svn_wc_notify_update_update;
+      if (db->propchanges->nelts > 0 || db->added)
+        {
+          apr_hash_t *right_props;
 
-      notify = svn_wc_create_notify(b->path, action, pool);
-      notify->kind = svn_node_dir;
+          right_props = svn_prop__patch(pristine_props, db->propchanges,
+                                        scratch_pool);
 
-      /* In case of a tree conflict during merge, the diff callback
-       * sets content_state appropriately. So copy the state into the
-       * notify_t to make sure conflicts get displayed. */
-      notify->content_state = content_state;
+          if (db->added)
+            {
+              SVN_ERR(eb->processor->dir_added(db->path,
+                                           NULL /* copyfrom */,
+                                           db->right_source,
+                                           NULL /* copyfrom props */,
+                                           right_props,
+                                           db->pdb,
+                                           eb->processor,
+                                           db->pool));
+            }
+          else
+            {
+              SVN_ERR(eb->processor->dir_changed(db->path,
+                                                 db->left_source,
+                                                 db->right_source,
+                                                 pristine_props,
+                                                 right_props,
+                                                 db->propchanges,
+                                                 db->pdb,
+                                                 eb->processor,
+                                                 db->pool));
+            }
 
-      notify->prop_state = prop_state;
-      notify->lock_state = svn_wc_notify_lock_state_inapplicable;
-      (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
+          send_changed = TRUE; /* Skip dir_closed */
+        }
     }
 
-  svn_pool_destroy(b->pool); /* Destroy baton and scratch_pool */
+  if (! db->skip && !send_changed)
+    {
+      SVN_ERR(eb->processor->dir_closed(db->path,
+                                        db->left_source,
+                                        db->right_source,
+                                        db->pdb,
+                                        eb->processor,
+                                        db->pool));
+    }
+  SVN_ERR(release_dir(db));
 
   return SVN_NO_ERROR;
 }
@@ -1210,16 +1117,23 @@ change_file_prop(void *file_baton,
                  const svn_string_t *value,
                  apr_pool_t *pool)
 {
-  struct file_baton *b = file_baton;
+  struct file_baton *fb = file_baton;
   svn_prop_t *propchange;
+  svn_prop_kind_t propkind;
 
   /* Skip *everything* within a newly tree-conflicted directory. */
-  if (b->skip)
+  if (fb->skip)
+    return SVN_NO_ERROR;
+
+  propkind = svn_property_kind2(name);
+  if (propkind == svn_prop_wc_kind)
     return SVN_NO_ERROR;
+  else if (propkind == svn_prop_regular_kind)
+    fb->has_propchange = TRUE;
 
-  propchange = apr_array_push(b->propchanges);
-  propchange->name = apr_pstrdup(b->pool, name);
-  propchange->value = value ? svn_string_dup(value, b->pool) : NULL;
+  propchange = apr_array_push(fb->propchanges);
+  propchange->name = apr_pstrdup(fb->pool, name);
+  propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
 
   return SVN_NO_ERROR;
 }
@@ -1235,11 +1149,18 @@ change_dir_prop(void *dir_baton,
 {
   struct dir_baton *db = dir_baton;
   svn_prop_t *propchange;
+  svn_prop_kind_t propkind;
 
   /* Skip *everything* within a newly tree-conflicted directory. */
   if (db->skip)
     return SVN_NO_ERROR;
 
+  propkind = svn_property_kind2(name);
+  if (propkind == svn_prop_wc_kind)
+    return SVN_NO_ERROR;
+  else if (propkind == svn_prop_regular_kind)
+    db->has_propchange = TRUE;
+
   propchange = apr_array_push(db->propchanges);
   propchange->name = apr_pstrdup(db->pool, name);
   propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
@@ -1270,18 +1191,7 @@ absent_directory(const char *path,
   struct dir_baton *pb = parent_baton;
   struct edit_baton *eb = pb->edit_baton;
 
-  /* ### TODO: Raise a tree-conflict?? I sure hope not.*/
-
-  if (eb->notify_func)
-    {
-      svn_wc_notify_t *notify
-        = svn_wc_create_notify(path, svn_wc_notify_skip, pool);
-
-      notify->kind = svn_node_dir;
-      notify->content_state = notify->prop_state
-        = svn_wc_notify_state_missing;
-      (*eb->notify_func)(eb->notify_baton, notify, pool);
-    }
+  SVN_ERR(eb->processor->node_absent(path, pb->pdb, eb->processor, pool));
 
   return SVN_NO_ERROR;
 }
@@ -1297,18 +1207,7 @@ absent_file(const char *path,
   struct dir_baton *pb = parent_baton;
   struct edit_baton *eb = pb->edit_baton;
 
-  /* ### TODO: Raise a tree-conflict?? I sure hope not.*/
-
-  if (eb->notify_func)
-    {
-      svn_wc_notify_t *notify
-        = svn_wc_create_notify(path, svn_wc_notify_skip, pool);
-
-      notify->kind = svn_node_file;
-      notify->content_state = notify->prop_state
-        = svn_wc_notify_state_missing;
-      (*eb->notify_func)(eb->notify_baton, notify, pool);
-    }
+  SVN_ERR(eb->processor->node_absent(path, pb->pdb, eb->processor, pool));
 
   return SVN_NO_ERROR;
 }
@@ -1414,20 +1313,16 @@ fetch_base_func(const char **filename,
 
 /* Create a repository diff editor and baton.  */
 svn_error_t *
-svn_client__get_diff_editor(const svn_delta_editor_t **editor,
-                            void **edit_baton,
-                            svn_depth_t depth,
-                            svn_ra_session_t *ra_session,
-                            svn_revnum_t revision,
-                            svn_boolean_t walk_deleted_dirs,
-                            svn_boolean_t text_deltas,
-                            const svn_wc_diff_callbacks4_t *diff_callbacks,
-                            void *diff_cmd_baton,
-                            svn_cancel_func_t cancel_func,
-                            void *cancel_baton,
-                            svn_wc_notify_func2_t notify_func,
-                            void *notify_baton,
-                            apr_pool_t *result_pool)
+svn_client__get_diff_editor2(const svn_delta_editor_t **editor,
+                             void **edit_baton,
+                             svn_ra_session_t *ra_session,
+                             svn_depth_t depth,
+                             svn_revnum_t revision,
+                             svn_boolean_t text_deltas,
+                             const svn_diff_tree_processor_t *processor,
+                             svn_cancel_func_t cancel_func,
+                             void *cancel_baton,
+                             apr_pool_t *result_pool)
 {
   apr_pool_t *editor_pool = svn_pool_create(result_pool);
   svn_delta_editor_t *tree_editor = svn_delta_default_editor(editor_pool);
@@ -1437,17 +1332,14 @@ svn_client__get_diff_editor(const svn_de
 
   eb->pool = editor_pool;
   eb->depth = depth;
-  eb->diff_callbacks = diff_callbacks;
-  eb->diff_cmd_baton = diff_cmd_baton;
+
+  eb->processor = processor;
+
   eb->ra_session = ra_session;
 
   eb->revision = revision;
   eb->empty_file = NULL;
   eb->empty_hash = apr_hash_make(eb->pool);
-  eb->deleted_paths = apr_hash_make(eb->pool);
-  eb->notify_func = notify_func;
-  eb->notify_baton = notify_baton;
-  eb->walk_deleted_repos_dirs = walk_deleted_dirs;
   eb->text_deltas = text_deltas;
   eb->cancel_func = cancel_func;
   eb->cancel_baton = cancel_baton;

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/resolved.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/resolved.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/resolved.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/resolved.c Mon Feb  4 20:48:05 2013
@@ -55,17 +55,19 @@ svn_client_resolve(const char *path,
 
   SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
 
-  SVN_ERR(svn_wc__resolve_conflicts(ctx->wc_ctx, local_abspath,
-                                    depth,
-                                    TRUE /* resolve_text */,
-                                    "" /* resolve_prop (ALL props) */,
-                                    TRUE /* resolve_tree */,
-                                    conflict_choice,
-                                    ctx->conflict_func2,
-                                    ctx->conflict_baton2,
-                                    ctx->cancel_func, ctx->cancel_baton,
-                                    ctx->notify_func2, ctx->notify_baton2,
-                                    pool));
+  SVN_WC__CALL_WITH_WRITE_LOCK(
+      svn_wc__resolve_conflicts(ctx->wc_ctx, local_abspath,
+                                depth,
+                                TRUE /* resolve_text */,
+                                "" /* resolve_prop (ALL props) */,
+                                TRUE /* resolve_tree */,
+                                conflict_choice,
+                                ctx->conflict_func2,
+                                ctx->conflict_baton2,
+                                ctx->cancel_func, ctx->cancel_baton,
+                                ctx->notify_func2, ctx->notify_baton2,
+                                pool),
+      ctx->wc_ctx, local_abspath, TRUE, pool);
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/revert.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/revert.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/revert.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/revert.c Mon Feb  4 20:48:05 2013
@@ -166,8 +166,8 @@ svn_client_revert2(const apr_array_heade
       baton.changelists = changelists;
       baton.ctx = ctx;
 
-      SVN_ERR(svn_wc__strictly_is_wc_root(&wc_root, ctx->wc_ctx,
-                                          local_abspath, pool));
+      SVN_ERR(svn_wc__is_wcroot(&wc_root, ctx->wc_ctx, local_abspath,
+                                pool));
       lock_target = wc_root ? local_abspath
                             : svn_dirent_dirname(local_abspath, pool);
       err = svn_wc__call_with_write_lock(revert, &baton, ctx->wc_ctx,

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/status.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/status.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/status.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/status.c Mon Feb  4 20:48:05 2013
@@ -85,23 +85,12 @@ tweak_status(void *baton,
   /* If the status item has an entry, but doesn't belong to one of the
      changelists our caller is interested in, we filter out this status
      transmission.  */
-  /* ### duplicated in ../libsvn_wc/diff_local.c */
-  if (sb->changelist_hash)
+  if (sb->changelist_hash
+      && (! status->changelist
+          || ! apr_hash_get(sb->changelist_hash, status->changelist,
+                            APR_HASH_KEY_STRING)))
     {
-      if (status->changelist)
-        {
-          /* Skip unless the caller requested this changelist. */
-          if (! apr_hash_get(sb->changelist_hash, status->changelist,
-                             APR_HASH_KEY_STRING))
-            return SVN_NO_ERROR;
-        }
-      else
-        {
-          /* Skip unless the caller requested changelist-lacking items. */
-          if (! apr_hash_get(sb->changelist_hash, "",
-                             APR_HASH_KEY_STRING))
-            return SVN_NO_ERROR;
-        }
+      return SVN_NO_ERROR;
     }
 
   /* If we know that the target was deleted in HEAD of the repository,
@@ -541,6 +530,7 @@ svn_client_status5(svn_revnum_t *result_
       SVN_ERR(svn_client__do_external_status(ctx, external_map,
                                              depth, get_all,
                                              update, no_ignore,
+                                             sb.anchor_abspath, sb.anchor_relpath,
                                              status_func, status_baton, pool));
     }
 

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/switch.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/switch.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/switch.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/switch.c Mon Feb  4 20:48:05 2013
@@ -231,10 +231,8 @@ switch_internal(svn_revnum_t *result_rev
       svn_boolean_t wc_root;
       svn_boolean_t needs_iprop_cache = TRUE;
 
-      SVN_ERR(svn_wc__strictly_is_wc_root(&wc_root,
-                                          ctx->wc_ctx,
-                                          local_abspath,
-                                          pool));
+      SVN_ERR(svn_wc__is_wcroot(&wc_root, ctx->wc_ctx, local_abspath,
+                                pool));
 
       /* Switching the WC root to anything but the repos root means
          we need an iprop cache. */ 

Modified: subversion/branches/fsfs-format7/subversion/libsvn_client/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_client/update.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_client/update.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_client/update.c Mon Feb  4 20:48:05 2013
@@ -376,23 +376,9 @@ update_internal(svn_revnum_t *result_rev
   dfb.target_revision = revnum;
   dfb.anchor_url = anchor_loc->url;
 
-  err = svn_client__get_inheritable_props(&wcroot_iprops, local_abspath,
-                                          revnum, depth, ra_session, ctx,
-                                          pool, pool);
-
-  /* We might be trying to update to a non-existant path-rev. */
-  if (err)
-    {
-      if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
-        {
-          svn_error_clear(err);
-          err = NULL;
-        }
-      else
-        {
-          return svn_error_trace(err);
-        }
-    }
+  SVN_ERR(svn_client__get_inheritable_props(&wcroot_iprops, local_abspath,
+                                            revnum, depth, ra_session,
+                                            ctx, pool, pool));
 
   /* Fetch the update editor.  If REVISION is invalid, that's okay;
      the RA driver will call editor->set_target_revision later on. */

Modified: subversion/branches/fsfs-format7/subversion/libsvn_delta/editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_delta/editor.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_delta/editor.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_delta/editor.c Mon Feb  4 20:48:05 2013
@@ -26,17 +26,27 @@
 #include "svn_types.h"
 #include "svn_error.h"
 #include "svn_pools.h"
-#include "svn_editor.h"
 #include "svn_dirent_uri.h"
 
+#include "private/svn_editor.h"
+
 #ifdef SVN_DEBUG
 /* This enables runtime checks of the editor API constraints.  This may
    introduce additional memory and runtime overhead, and should not be used
    in production builds.
 
-   ### Remove before release? */
+   ### Remove before release? 
+
+   ### Disabled for now.  If I call svn_editor_alter_directory(A) then
+       svn_editor_add_file(A/f) the latter fails on SHOULD_ALLOW_ADD.
+       If I modify svn_editor_alter_directory to MARK_ALLOW_ADD(child)
+       then if I call svn_editor_alter_directory(A) followed by
+       svn_editor_alter_directory(A/B/C) the latter fails on
+       VERIFY_PARENT_MAY_EXIST. */
+#if 0
 #define ENABLE_ORDERING_CHECK
 #endif
+#endif
 
 
 struct svn_editor_t
@@ -665,6 +675,7 @@ svn_editor_alter_directory(svn_editor_t 
 
         apr_hash_set(editor->pending_incomplete_children, child,
                      APR_HASH_KEY_STRING, "");
+        /* Perhaps MARK_ALLOW_ADD(editor, child); ? */
       }
   }
 #endif

Modified: subversion/branches/fsfs-format7/subversion/libsvn_delta/svndiff.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_delta/svndiff.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_delta/svndiff.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_delta/svndiff.c Mon Feb  4 20:48:05 2013
@@ -203,7 +203,7 @@ send_simple_insertion_window(svn_txdelta
   assert(window->ops[0].offset == 0);
 
   /* write stream header if necessary */
-  if (eb->header_done == FALSE)
+  if (!eb->header_done)
     {
       eb->header_done = TRUE;
       headers[0] = 'S';
@@ -229,10 +229,11 @@ send_simple_insertion_window(svn_txdelta
       ip_len = encode_int(ibuf + 1, window->tview_len) - ibuf;
     }
 
-  /* encode the window header.  */
-  header_current[0] = 0;  /* source offset == 0 */
-  header_current[1] = 0;  /* source length == 0 */
-  header_current = encode_int(header_current + 2, window->tview_len);
+  /* encode the window header.  Please note that the source window may
+   * have content despite not being used for deltification. */
+  header_current = encode_int(header_current, window->sview_offset);
+  header_current = encode_int(header_current, window->sview_len);
+  header_current = encode_int(header_current, window->tview_len);
   header_current[0] = (unsigned char)ip_len;  /* 1 instruction */
   header_current = encode_int(&header_current[1], len);
 
@@ -268,7 +269,7 @@ window_handler(svn_txdelta_window_t *win
     return svn_error_trace(send_simple_insertion_window(window, eb));
 
   /* Make sure we write the header.  */
-  if (eb->header_done == FALSE)
+  if (!eb->header_done)
     {
       char svnver[4] = {'S','V','N','\0'};
       len = 4;