You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/09/15 21:32:38 UTC

svn commit: r997472 [25/41] - in /subversion/branches/py-tests-as-modules: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ contrib/server-side/ notes/ notes/tree-conflicts/ notes/wc-ng/ subversion/bindings/javahl/native/ subversi...

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_wc/update_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_wc/update_editor.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_wc/update_editor.c Wed Sep 15 19:32:26 2010
@@ -30,7 +30,6 @@
 #include <apr_hash.h>
 #include <apr_md5.h>
 #include <apr_tables.h>
-#include <apr_file_io.h>
 #include <apr_strings.h>
 
 #include "svn_types.h"
@@ -39,27 +38,20 @@
 #include "svn_string.h"
 #include "svn_dirent_uri.h"
 #include "svn_path.h"
-#include "svn_xml.h"
 #include "svn_error.h"
 #include "svn_io.h"
 #include "svn_private_config.h"
 #include "svn_time.h"
-#include "svn_config.h"
 #include "svn_iter.h"
 
 #include "wc.h"
-#include "log.h"
 #include "adm_files.h"
-#include "adm_ops.h"
 #include "entries.h"
-#include "lock.h"
-#include "props.h"
 #include "translate.h"
 #include "tree_conflicts.h"
 #include "workqueue.h"
 
 #include "private/svn_wc_private.h"
-
 /* Checks whether a svn_wc__db_status_t indicates whether a node is
    present in a working copy. Used by the editor implementation */
 #define IS_NODE_PRESENT(status)                             \
@@ -198,11 +190,6 @@ struct edit_baton
   /* Allow unversioned obstructions when adding a path. */
   svn_boolean_t allow_unver_obstructions;
 
-  /* The close_edit method destroys the edit pool and so runs the
-     cleanup_dir_baton cleanup handlers.  This flag is set to indicate
-     that the edit was completed successfully. */
-  svn_boolean_t close_edit_complete;
-
   /* If this is a 'switch' operation, the new relpath of target_abspath,
      else NULL. */
   const char *switch_relpath;
@@ -474,16 +461,14 @@ node_get_relpath_ignore_errors(svn_wc__d
   if (relpath)
     return relpath;
 
-  if (status == svn_wc__db_status_added ||
-      status == svn_wc__db_status_obstructed_add)
+  if (status == svn_wc__db_status_added)
     {
       svn_error_clear(svn_wc__db_scan_addition(NULL, NULL, &relpath, NULL,
                                                NULL, NULL, NULL, NULL, NULL,
                                                db, local_abspath,
                                                result_pool, scratch_pool));
     }
-  else if (status != svn_wc__db_status_deleted &&
-           status != svn_wc__db_status_obstructed_delete)
+  else if (status != svn_wc__db_status_deleted)
     {
       svn_error_clear(svn_wc__db_scan_base_repos(&relpath, NULL, NULL,
                                                  db, local_abspath,
@@ -505,20 +490,9 @@ cleanup_dir_baton(void *dir_baton)
   apr_pool_t *pool = apr_pool_parent_get(db->pool);
 
   err = svn_wc__wq_run(eb->db, db->local_abspath,
-                       eb->cancel_func, eb->cancel_baton,
+                       NULL /* cancel_func */, NULL /* cancel_baton */,
                        pool);
 
-  /* If the editor aborts for some sort of error, the command line
-     client relies on pool cleanup to run outstanding work queues and
-     remove locks.  This avoids leaving the working copy locked in
-     many cases, but plays havoc with operations that do multiple
-     updates (think externals). So we flag updates that complete
-     successfully and avoid removing locks.  In 1.6 locks were
-     associated with a pool distinct from the edit pool and so were
-     removed separately. */
-  if (!err && !eb->close_edit_complete)
-    err = svn_wc__release_write_lock(eb->wc_ctx, db->local_abspath, pool);
-
   if (err)
     {
       apr_status_t apr_err = err->apr_err;
@@ -726,15 +700,10 @@ complete_directory(struct edit_baton *eb
   int i;
   apr_pool_t *iterpool;
 
-  /* If this is the root directory and there is a target, we can't
+  /* If this is the root directory and there is a target, we don't have to
      mark this directory complete. */
   if (is_root_dir && *eb->target_basename != '\0')
     {
-      /* ### obsolete comment?
-         Before we can finish, we may need to clear the exclude flag for
-         target. Also give a chance to the target that is explicitly pulled
-         in. */
-      svn_wc__db_kind_t kind;
       svn_wc__db_status_t status;
       svn_error_t *err;
 
@@ -749,38 +718,26 @@ complete_directory(struct edit_baton *eb
 
          If there is no BASE node for the target, then we certainly don't
          have to worry about removing it.  */
-      err = svn_wc__db_base_get_info(&status, &kind, NULL,
-                                     NULL, NULL, NULL,
-                                     NULL, NULL, NULL,
-                                     NULL, NULL, NULL, NULL, NULL, NULL,
-                                     eb->db, eb->target_abspath,
-                                     pool, pool);
+      err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
+                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                     NULL, NULL,
+                                     eb->db, eb->target_abspath, pool, pool);
       if (err)
         {
           if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
             return svn_error_return(err);
 
           svn_error_clear(err);
-          return SVN_NO_ERROR;
         }
 
-      if (status == svn_wc__db_status_excluded)
+      if (!err && status == svn_wc__db_status_excluded)
         {
-          /* ### obsolete comment?
-             There is a small chance that the target is gone in the
-             repository.  If so, we should get rid of the entry now. */
+          /* There is a small chance that the explicit target of an update/
+             switch is gone in the repository, in that specific case the node
+             hasn't been re-added to the BASE tree by this update. If so, we
+             should get rid of this excluded node now. */
 
-          if (kind == svn_wc__db_kind_dir &&
-              svn_wc__adm_missing(eb->db, eb->target_abspath, pool))
-            {
-              /* ### obsolete comment?
-               * Still passing NULL for THEIR_URL. A case where THEIR_URL
-               * is needed in this call is rare or even non-existant.
-               * ### TODO: Construct a proper THEIR_URL anyway. See also
-               * NULL handling code in do_entry_deletion(). */
-              SVN_ERR(do_entry_deletion(eb, eb->target_abspath,
-                                        NULL, FALSE, pool));
-            }
+          SVN_ERR(do_entry_deletion(eb, eb->target_abspath, NULL, FALSE, pool));
         }
 
       return SVN_NO_ERROR;
@@ -836,46 +793,17 @@ complete_directory(struct edit_baton *eb
       svn_wc__db_status_t status;
       svn_wc__db_kind_t kind;
       svn_revnum_t revnum;
-      svn_error_t *err;
 
       svn_pool_clear(iterpool);
 
       node_abspath = svn_dirent_join(local_abspath, name, iterpool);
 
-      /* ### there is an edge case that we can run into right now: this
-         ### dir can have a "subdir" node in the BASE_NODE, but the
-         ### actual subdir does NOT have a record.
-         ###
-         ### in particular, copy_tests 21 and schedule_tests 10 can create
-         ### this situation. in short: the subdir is rm'd on the disk, and
-         ### a deletion of that directory is committed. a local-add then
-         ### reintroduces the directory and metadata (within WORKING).
-         ### before or after an update, the parent dir has the "subdir"
-         ### BASE_NODE and it is missing in the child. asking for the BASE
-         ### won't return status_obstructed since there is a true subdir
-         ### there now.
-         ###
-         ### at some point in the control flow, we should have removed
-         ### the "subdir" record. maybe there is a good place to remove
-         ### that record (or wait for single-dir). for now, we can correct
-         ### it when we detect it.  */
-      err = svn_wc__db_base_get_info(&status, &kind, &revnum,
-                                     NULL, NULL, NULL,
-                                     NULL, NULL, NULL,
-                                     NULL, NULL, NULL, NULL, NULL, NULL,
-                                     eb->db, node_abspath,
-                                     iterpool, iterpool);
-      if (err)
-        {
-          if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
-            return svn_error_return(err);
-
-          svn_error_clear(err);
-
-          SVN_ERR(svn_wc__db_temp_remove_subdir_record(eb->db, node_abspath,
-                                                       iterpool));
-          continue;
-        }
+      SVN_ERR(svn_wc__db_base_get_info(&status, &kind, &revnum,
+                                       NULL, NULL, NULL,
+                                       NULL, NULL, NULL,
+                                       NULL, NULL, NULL, NULL, NULL, NULL,
+                                       eb->db, node_abspath,
+                                       iterpool, iterpool));
 
       /* ### obsolete comment?
          Any entry still marked as deleted (and not schedule add) can now
@@ -888,24 +816,32 @@ complete_directory(struct edit_baton *eb
          number different from the target revision of the update means the
          update never mentioned the item, so the entry should be
          removed. */
-      if (status == svn_wc__db_status_not_present
-          || (status == svn_wc__db_status_absent
-              && revnum != *eb->target_revision))
+      if (status == svn_wc__db_status_not_present)
         {
-          SVN_ERR(svn_wc__db_base_remove(eb->db, node_abspath, iterpool));
+          /* "Usually", not_present nodes indicate that an 'svn delete' was
+           * committed and its parent has not been updated yet. We have just
+           * updated the parent and so the not_present BASE_NODE should go
+           * away.
+           * However, not_present can also mean that 'update' wanted to add a
+           * node and found an unversioned obstruction at that path. We don't
+           * want to remove such not_present state, so check if there is a
+           * tree conflict flagged against an unversioned node and leave the
+           * BASE_NODE alone if so.
+           * Note that add_file() automatically fixes such an
+           * added-not_present node when it finds the obstruction gone. */
+          const svn_wc_conflict_description2_t *tree_conflict;
+          SVN_ERR(svn_wc__db_op_read_tree_conflict(&tree_conflict,
+                                                   eb->db,
+                                                   node_abspath,
+                                                   iterpool, iterpool));
+          if (!tree_conflict
+              || tree_conflict->reason != svn_wc_conflict_reason_unversioned)
+            SVN_ERR(svn_wc__db_base_remove(eb->db, node_abspath, iterpool));
         }
-      else if (kind == svn_wc__db_kind_dir
-               && svn_wc__adm_missing(eb->db, node_abspath, iterpool)
-               && status != svn_wc__db_status_absent)
-        {
-          SVN_ERR(svn_wc__db_temp_op_remove_entry(eb->db, node_abspath,
-                                                  iterpool));
-
-          do_notification(eb, node_abspath, svn_wc_notify_update_delete,
-                          (kind == svn_wc__db_kind_dir)
-                            ? svn_node_dir
-                            : svn_node_file,
-                          iterpool);
+      else if (status == svn_wc__db_status_absent
+               && revnum != *eb->target_revision)
+        {
+          SVN_ERR(svn_wc__db_base_remove(eb->db, node_abspath, iterpool));
         }
     }
 
@@ -993,27 +929,14 @@ struct file_baton
      within a locally deleted tree. */
   svn_boolean_t deleted;
 
-  /* The path to the incoming text base (that is, to a text-base-file-
-     in-progress in the tmp area).  This gets set if there are file
-     content changes. */
-  const char *new_text_base_tmp_abspath;
-
-  /* The MD5 checksum of the incoming text base (pristine text). */
+  /* If there are file content changes, these are the checksums of the
+     resulting new text base, which is in the pristine store, else NULL. */
   svn_checksum_t *new_text_base_md5_checksum;
-
-  /* The SHA1 checksum of the incoming text base (pristine text). */
   svn_checksum_t *new_text_base_sha1_checksum;
 
-  /* If this file was added with history, this is the path to a copy
-     of the text base of the copyfrom file (in the temporary area). */
-  const char *copied_text_base_abspath;
-
-  /* If this file was added with history, this is the MD5 checksum of the
-     text base (see copied_text_base_abspath). May be NULL if unknown. */
+  /* If this file was added with history, these are the checksums of the
+     copy-from text base, which is in the pristine store, else NULL. */
   svn_checksum_t *copied_text_base_md5_checksum;
-
-  /* If this file was added with history, this is the SHA-1 checksum of the
-     text base (see copied_text_base_abspath). May be NULL if unknown. */
   svn_checksum_t *copied_text_base_sha1_checksum;
 
   /* If this file was added with history, and the copyfrom had local
@@ -1039,11 +962,18 @@ struct file_baton
 
   /* The last-changed-date of the file.  This is actually a property
      that comes through as an 'entry prop', and will be used to set
-     the working file's timestamp if it's added.  */
+     the working file's timestamp if it's added. 
+
+     Will be NULL unless eb->use_commit_times is TRUE. */
   const char *last_changed_date;
 
   /* Bump information for the directory this file lives in */
   struct bump_dir_info *bump_info;
+
+  /* This is set when there is an incoming add of a file/symlink node onto a
+   * locally added node of different identity (add-vs-add tree conflict). */
+  svn_boolean_t adding_base_under_local_add;
+
 };
 
 
@@ -1099,12 +1029,15 @@ make_file_baton(struct file_baton **f_p,
 }
 
 
-/* */
+/* Handle the next delta window of the file described by BATON.  If it is
+ * the end (WINDOW == NULL), then check the checksum, store the text in the
+ * pristine store and write its details into BATON->fb->new_text_base_*. */
 static svn_error_t *
 window_handler(svn_txdelta_window_t *window, void *baton)
 {
   struct handler_baton *hb = baton;
   struct file_baton *fb = hb->fb;
+  svn_wc__db_t *db = fb->edit_baton->db;
   svn_error_t *err;
 
   /* Apply this window.  We may be done at that point.  */
@@ -1142,16 +1075,20 @@ window_handler(svn_txdelta_window_t *win
     }
   else
     {
-      /* Tell the file baton about the new text base. */
-      fb->new_text_base_tmp_abspath = apr_pstrdup(fb->pool,
-                                              hb->new_text_base_tmp_abspath);
-
-      /* ... and its checksums. */
+      /* Tell the file baton about the new text base's checksums. */
       fb->new_text_base_md5_checksum =
         svn_checksum__from_digest(hb->new_text_base_md5_digest,
                                   svn_checksum_md5, fb->pool);
       fb->new_text_base_sha1_checksum =
         svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
+
+      /* Store the new pristine text in the pristine store now.  Later, in a
+         single transaction we will update the BASE_NODE to include a
+         reference to this pristine text's checksum. */
+      SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath,
+                                          fb->new_text_base_sha1_checksum,
+                                          fb->new_text_base_md5_checksum,
+                                          hb->pool));
     }
 
   svn_pool_destroy(hb->pool);
@@ -1172,37 +1109,13 @@ prep_directory(struct dir_baton *db,
                svn_revnum_t ancestor_revision,
                apr_pool_t *pool)
 {
-  const char *repos_root;
   const char *dir_abspath;
-  svn_boolean_t locked_here;
 
   dir_abspath = db->local_abspath;
 
   /* Make sure the directory exists. */
   SVN_ERR(svn_wc__ensure_directory(dir_abspath, pool));
 
-  /* Use the repository root of the anchor, but only if it actually is an
-     ancestor of the URL of this directory. */
-  if (svn_uri_is_ancestor(db->edit_baton->repos_root, ancestor_url))
-    repos_root = db->edit_baton->repos_root;
-  else
-    repos_root = NULL;
-
-  /* Make sure it's the right working copy, either by creating it so,
-     or by checking that it is so already. */
-  SVN_ERR(svn_wc__internal_ensure_adm(db->edit_baton->db, dir_abspath,
-                                      ancestor_url, repos_root,
-                                      db->edit_baton->repos_uuid,
-                                      ancestor_revision,
-                                      db->ambient_depth, pool));
-
-  SVN_ERR(svn_wc_locked2(&locked_here, NULL, db->edit_baton->wc_ctx,
-                         dir_abspath, pool));
-  if (!locked_here)
-    /* Recursive lock release on parent will release this lock. */
-    SVN_ERR(svn_wc__acquire_write_lock(NULL, db->edit_baton->wc_ctx,
-                                       dir_abspath, pool, pool));
-
   return SVN_NO_ERROR;
 }
 
@@ -1273,9 +1186,12 @@ check_path_under_root(const char *base_p
                       const char *add_path,
                       apr_pool_t *pool)
 {
-  char *full_path;
+  const char *full_path;
+  svn_boolean_t under_root;
 
-  if (! svn_dirent_is_under_root(&full_path, base_path, add_path, pool))
+  SVN_ERR(svn_dirent_is_under_root(&under_root, &full_path, base_path, add_path, pool));
+
+  if (! under_root)
     {
       return svn_error_createf(
           SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
@@ -1386,36 +1302,6 @@ open_root(void *edit_baton,
 }
 
 
-/* Helper for delete_entry() and do_entry_deletion().
-
-   If the error chain ERR contains evidence that a local mod was left
-   (an SVN_ERR_WC_LEFT_LOCAL_MOD error), clear ERR.  Otherwise, return ERR.
-*/
-static svn_error_t *
-leftmod_error_chain(svn_error_t *err)
-{
-  svn_error_t *tmp_err;
-
-  if (! err)
-    return SVN_NO_ERROR;
-
-  /* Advance TMP_ERR to the part of the error chain that reveals that
-     a local mod was left, or to the NULL end of the chain. */
-  for (tmp_err = err; tmp_err; tmp_err = tmp_err->child)
-    if (tmp_err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)
-      {
-        /* We just found a "left a local mod" error, so tolerate it
-           and clear the whole error. In that case we continue with
-           modified files left on the disk. */
-        svn_error_clear(err);
-        return SVN_NO_ERROR;
-      }
-
-  /* Otherwise, we just return our top-most error. */
-  return err;
-}
-
-
 /* ===================================================================== */
 /* Checking for local modifications. */
 
@@ -1481,7 +1367,9 @@ modcheck_found_node(const char *local_ab
 
   if (status != svn_wc__db_status_normal)
     modified = TRUE;
-  else
+  /* No need to check if we already have at least one non-delete
+     modification */
+  else if (!baton->found_mod || baton->all_edits_are_deletes)
     SVN_ERR(entry_has_local_mods(&modified, baton->db, local_abspath,
                                  kind, scratch_pool));
 
@@ -1533,6 +1421,199 @@ tree_has_local_mods(svn_boolean_t *modif
 /* Indicates an unset svn_wc_conflict_reason_t. */
 #define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
 
+/* Create a tree conflict struct.
+ *
+ * The REASON is stored directly in the tree conflict info.
+ *
+ * All temporary allocactions are be made in SCRATCH_POOL, while allocations
+ * needed for the returned conflict struct are made in RESULT_POOL.
+ *
+ * All other parameters are identical to and described by
+ * check_tree_conflict(), with the slight modification that this function
+ * relies on the reason passed in REASON instead of actively looking for one. */
+static svn_error_t *
+create_tree_conflict(svn_wc_conflict_description2_t **pconflict,
+                     struct edit_baton *eb,
+                     const char *local_abspath,
+                     svn_wc_conflict_reason_t reason,
+                     svn_wc_conflict_action_t action,
+                     svn_node_kind_t their_node_kind,
+                     const char *their_relpath,
+                     apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+  const char *repos_root_url = NULL;
+  const char *left_repos_relpath;
+  svn_revnum_t left_revision;
+  svn_node_kind_t left_kind;
+  const char *right_repos_relpath;
+  const char *added_repos_relpath = NULL;
+  svn_node_kind_t conflict_node_kind;
+  svn_wc_conflict_version_t *src_left_version;
+  svn_wc_conflict_version_t *src_right_version;
+
+  *pconflict = NULL;
+
+  SVN_ERR_ASSERT(reason != SVN_WC_CONFLICT_REASON_NONE);
+
+  /* Get the source-left information, i.e. the local state of the node
+   * before any changes were made to the working copy, i.e. the state the
+   * node would have if it was reverted. */
+  if (reason == svn_wc_conflict_reason_added)
+    {
+      svn_wc__db_status_t added_status;
+
+      /* ###TODO: It would be nice to tell the user at which URL and
+       * ### revision source-left was empty, which could be quite difficult
+       * ### to code, and is a slight theoretical leap of the svn mind.
+       * ### Update should show
+       * ###   URL: svn_wc__db_scan_addition( &repos_relpath )
+       * ###   REV: The base revision of the parent of before this update
+       * ###        started
+       * ###        ### BUT what if parent was updated/switched away with
+       * ###        ### depth=empty after this node was added?
+       * ### Switch should show
+       * ###   URL: scan_addition URL of before this switch started
+       * ###   REV: same as above */
+
+      /* In case of a local addition, source-left is non-existent / empty. */
+      left_kind = svn_node_none;
+      left_revision = SVN_INVALID_REVNUM;
+      left_repos_relpath = NULL;
+
+      /* Still get the repository root needed by both 'update' and 'switch',
+       * and the would-be repos_relpath needed to construct the source-right
+       * in case of an 'update'. Check sanity while we're at it. */
+      SVN_ERR(svn_wc__db_scan_addition(&added_status, NULL,
+                                       &added_repos_relpath,
+                                       &repos_root_url,
+                                       NULL, NULL, NULL, NULL, NULL,
+                                       eb->db, local_abspath,
+                                       result_pool, scratch_pool));
+
+      /* This better really be an added status. */
+      SVN_ERR_ASSERT(added_status == svn_wc__db_status_added
+                     || added_status == svn_wc__db_status_copied
+                     || added_status == svn_wc__db_status_moved_here);
+    }
+  else if (reason == svn_wc_conflict_reason_unversioned)
+    {
+      /* Obstructed by an unversioned node. Source-left is
+       * non-existent/empty. */
+      left_kind = svn_node_none;
+      left_revision = SVN_INVALID_REVNUM;
+      left_repos_relpath = NULL;
+      repos_root_url = eb->repos_root;
+    }
+  else
+    {
+      /* A BASE node should exist. */
+      svn_wc__db_kind_t base_kind;
+
+      /* If anything else shows up, then this assertion is probably naive
+       * and that other case should also be handled. */
+      SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_edited
+                     || reason == svn_wc_conflict_reason_deleted
+                     || reason == svn_wc_conflict_reason_replaced
+                     || reason == svn_wc_conflict_reason_obstructed);
+
+      SVN_ERR(svn_wc__db_base_get_info(NULL, &base_kind,
+                                       &left_revision,
+                                       &left_repos_relpath,
+                                       &repos_root_url,
+                                       NULL, NULL, NULL, NULL, NULL, NULL,
+                                       NULL, NULL, NULL, NULL,
+                                       eb->db,
+                                       local_abspath,
+                                       result_pool,
+                                       scratch_pool));
+      /* Translate the node kind. */
+      if (base_kind == svn_wc__db_kind_file
+          || base_kind == svn_wc__db_kind_symlink)
+        left_kind = svn_node_file;
+      else if (base_kind == svn_wc__db_kind_dir)
+        left_kind = svn_node_dir;
+      else
+        SVN_ERR_MALFUNCTION();
+    }
+
+  SVN_ERR_ASSERT(strcmp(repos_root_url, eb->repos_root) == 0);
+
+  /* Find the source-right information, i.e. the state in the repository
+   * to which we would like to update. */
+  if (eb->switch_relpath)
+    {
+      /* If this is a 'switch' operation, try to construct the switch
+       * target's REPOS_RELPATH. */
+      if (their_relpath != NULL)
+        right_repos_relpath = their_relpath;
+      else
+        {
+          /* The complete source-right URL is not available, but it
+           * is somewhere below the SWITCH_URL. For now, just go
+           * without it.
+           * ### TODO: Construct a proper THEIR_URL in some of the
+           * delete cases that still pass NULL for THEIR_URL when
+           * calling this function. Do that on the caller's side. */
+          right_repos_relpath = eb->switch_relpath;
+          right_repos_relpath = apr_pstrcat(result_pool, right_repos_relpath,
+                                            "_THIS_IS_INCOMPLETE", NULL);
+        }
+    }
+  else
+    {
+      /* This is an 'update', so REPOS_RELPATH would be the same as for
+       * source-left. However, we don't have a source-left for locally
+       * added files. */
+      right_repos_relpath = (reason == svn_wc_conflict_reason_added ?
+                             added_repos_relpath : left_repos_relpath);
+      if (! right_repos_relpath)
+        right_repos_relpath = their_relpath;
+    }
+
+  SVN_ERR_ASSERT(right_repos_relpath != NULL);
+
+  /* Determine PCONFLICT's overall node kind, which is not allowed to be
+   * svn_node_none. We give it the source-right revision (THEIR_NODE_KIND)
+   * -- unless source-right is deleted and hence == svn_node_none, in which
+   * case we take it from source-left, which has to be the node kind that
+   * was deleted. */
+  conflict_node_kind = (action == svn_wc_conflict_action_delete ?
+                        left_kind : their_node_kind);
+  SVN_ERR_ASSERT(conflict_node_kind == svn_node_file
+                 || conflict_node_kind == svn_node_dir);
+
+
+  /* Construct the tree conflict info structs. */
+
+  if (left_repos_relpath == NULL)
+    /* A locally added or unversioned path in conflict with an incoming add.
+     * Send an 'empty' left revision. */
+    src_left_version = NULL;
+  else
+    src_left_version = svn_wc_conflict_version_create(repos_root_url,
+                                                      left_repos_relpath,
+                                                      left_revision,
+                                                      left_kind,
+                                                      result_pool);
+
+  src_right_version = svn_wc_conflict_version_create(repos_root_url,
+                                                     right_repos_relpath,
+                                                     *eb->target_revision,
+                                                     their_node_kind,
+                                                     result_pool);
+
+  *pconflict = svn_wc_conflict_description_create_tree2(
+                   local_abspath, conflict_node_kind,
+                   eb->switch_relpath ?
+                     svn_wc_operation_switch : svn_wc_operation_update,
+                   src_left_version, src_right_version, result_pool);
+  (*pconflict)->action = action;
+  (*pconflict)->reason = reason;
+
+  return SVN_NO_ERROR;
+}
+
+
 /* Check whether the incoming change ACTION on FULL_PATH would conflict with
  * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
  * LOCAL_ABSPATH as the victim.
@@ -1540,24 +1621,20 @@ tree_has_local_mods(svn_boolean_t *modif
  * The edit baton EB gives information including whether the operation is
  * an update or a switch.
  *
- * If PCONFLICT is not null, set *PCONFLICT to the conflict description if
- * there is one or else to null.
+ * If a tree conflict reason was found for the incoming action, the resulting
+ * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
+ * while *PCONFLICT is always overwritten.
  *
- * THEIR_NODE_KIND is the node kind reflected by the incoming edit
+ * THEIR_NODE_KIND should be the node kind reflected by the incoming edit
  * function. E.g. dir_opened() should pass svn_node_dir, etc.
  * In some cases of delete, svn_node_none may be used here.
  *
- * THEIR_RELPATH is the involved node's repository relative path on the
- * source-right side, the side that the target should become after the
- * update. Simply put, that's the URL obtained from the node's
- * dir_baton->new_relpath or file_baton->new_relpath (but it's more
- * complex for a delete).
- *
- * ACCEPT_DELETED is true if one of the ancestors got tree conflicted, but
- * the operation continued updating a deleted base tree.
+ * THEIR_RELPATH should be the involved node's repository-relative path on the
+ * source-right side, the side that the target should become after the update.
+ * Simply put, that's the URL obtained from the node's dir_baton->new_relpath
+ * or file_baton->new_relpath (but it's more complex for a delete).
  *
- * Tree conflict use cases are described in issue #2282 and in
- * notes/tree-conflicts/detection.txt.
+ * All allocations are made in POOL.
  */
 static svn_error_t *
 check_tree_conflict(svn_wc_conflict_description2_t **pconflict,
@@ -1595,7 +1672,6 @@ check_tree_conflict(svn_wc_conflict_desc
   switch (status)
     {
       case svn_wc__db_status_added:
-      case svn_wc__db_status_obstructed_add:
       case svn_wc__db_status_moved_here:
       case svn_wc__db_status_copied:
         /* Is it a replace? */
@@ -1636,7 +1712,6 @@ check_tree_conflict(svn_wc_conflict_desc
 
 
       case svn_wc__db_status_deleted:
-      case svn_wc__db_status_obstructed_delete:
         /* The node is locally deleted. */
         reason = svn_wc_conflict_reason_deleted;
         break;
@@ -1648,10 +1723,6 @@ check_tree_conflict(svn_wc_conflict_desc
          * So the node exists and is essentially 'normal'. We still need to
          * check prop and text mods, and those checks will retrieve the
          * missing information (hopefully). */
-      case svn_wc__db_status_obstructed:
-        /* Tree-conflicts during update are only concerned with local
-         * modifications. We can safely update BASE, disregarding the
-         * obstruction. So let's treat this as normal. */
       case svn_wc__db_status_normal:
         if (action == svn_wc_conflict_action_edit)
           /* An edit onto a local edit or onto *no* local changes is no
@@ -1675,11 +1746,10 @@ check_tree_conflict(svn_wc_conflict_desc
                * but the update editor will not visit the subdirectories
                * of a directory that it wants to delete.  Therefore, we
                * need to start a separate crawl here. */
-              if (!svn_wc__adm_missing(eb->db, local_abspath, pool))
-                SVN_ERR(tree_has_local_mods(&modified, &all_mods_are_deletes,
-                                            eb->db, local_abspath,
-                                            eb->cancel_func, eb->cancel_baton,
-                                            pool));
+              SVN_ERR(tree_has_local_mods(&modified, &all_mods_are_deletes,
+                                          eb->db, local_abspath,
+                                          eb->cancel_func, eb->cancel_baton,
+                                          pool));
               break;
 
             default:
@@ -1739,162 +1809,12 @@ check_tree_conflict(svn_wc_conflict_desc
 
   /* A conflict was detected. Append log commands to the log accumulator
    * to record it. */
-  {
-    const char *repos_root_url = NULL;
-    const char *left_repos_relpath;
-    svn_revnum_t left_revision;
-    svn_node_kind_t left_kind;
-    const char *right_repos_relpath;
-    const char *added_repos_relpath = NULL;
-    svn_node_kind_t conflict_node_kind;
-    svn_wc_conflict_version_t *src_left_version;
-    svn_wc_conflict_version_t *src_right_version;
-
-    /* Get the source-left information, i.e. the local state of the node
-     * before any changes were made to the working copy, i.e. the state the
-     * node would have if it was reverted. */
-    if (reason == svn_wc_conflict_reason_added)
-      {
-        svn_wc__db_status_t added_status;
-
-        /* ###TODO: It would be nice to tell the user at which URL and
-         * ### revision source-left was empty, which could be quite difficult
-         * ### to code, and is a slight theoretical leap of the svn mind.
-         * ### Update should show
-         * ###   URL: svn_wc__db_scan_addition( &repos_relpath )
-         * ###   REV: The base revision of the parent of before this update
-         * ###        started
-         * ###        ### BUT what if parent was updated/switched away with
-         * ###        ### depth=empty after this node was added?
-         * ### Switch should show
-         * ###   URL: scan_addition URL of before this switch started
-         * ###   REV: same as above */
-
-        /* In case of a local addition, source-left is non-existent / empty. */
-        left_kind = svn_node_none;
-        left_revision = SVN_INVALID_REVNUM;
-        left_repos_relpath = NULL;
-
-        /* Still get the repository root needed by both 'update' and 'switch',
-         * and the would-be repos_relpath needed to construct the source-right
-         * in case of an 'update'. Check sanity while we're at it. */
-        SVN_ERR(svn_wc__db_scan_addition(&added_status, NULL,
-                                         &added_repos_relpath,
-                                         &repos_root_url,
-                                         NULL, NULL, NULL, NULL, NULL,
-                                         eb->db, local_abspath, pool, pool));
-
-        /* This better really be an added status. */
-        SVN_ERR_ASSERT(added_status == svn_wc__db_status_added
-                       || added_status == svn_wc__db_status_obstructed_add
-                       || added_status == svn_wc__db_status_copied
-                       || added_status == svn_wc__db_status_moved_here);
-      }
-    else
-      {
-        /* A BASE node should exist. */
-        svn_wc__db_kind_t base_kind;
-
-        /* If anything else shows up, then this assertion is probably naive
-         * and that other case should also be handled. */
-        SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_edited
-                       || reason == svn_wc_conflict_reason_deleted
-                       || reason == svn_wc_conflict_reason_replaced
-                       || reason == svn_wc_conflict_reason_obstructed);
-
-        SVN_ERR(svn_wc__db_base_get_info(NULL, &base_kind,
-                                         &left_revision,
-                                         &left_repos_relpath,
-                                         &repos_root_url,
-                                         NULL, NULL, NULL, NULL, NULL, NULL,
-                                         NULL, NULL, NULL, NULL,
-                                         eb->db,
-                                         local_abspath,
-                                         pool,
-                                         pool));
-        /* Translate the node kind. */
-        if (base_kind == svn_wc__db_kind_file
-            || base_kind == svn_wc__db_kind_symlink)
-          left_kind = svn_node_file;
-        else if (base_kind == svn_wc__db_kind_dir)
-          left_kind = svn_node_dir;
-        else
-          SVN_ERR_MALFUNCTION();
-      }
-
-
-    /* Find the source-right information, i.e. the state in the repository
-     * to which we would like to update. */
-    if (eb->switch_relpath)
-      {
-        /* If this is a 'switch' operation, try to construct the switch
-         * target's REPOS_RELPATH. */
-        if (their_relpath != NULL)
-          right_repos_relpath = their_relpath;
-        else
-          {
-            /* The complete source-right URL is not available, but it
-             * is somewhere below the SWITCH_URL. For now, just go
-             * without it.
-             * ### TODO: Construct a proper THEIR_URL in some of the
-             * delete cases that still pass NULL for THEIR_URL when
-             * calling this function. Do that on the caller's side. */
-            right_repos_relpath = eb->switch_relpath;
-            right_repos_relpath = apr_pstrcat(pool, right_repos_relpath,
-                                              "_THIS_IS_INCOMPLETE", NULL);
-          }
-      }
-    else
-      {
-        /* This is an 'update', so REPOS_RELPATH would be the same as for
-         * source-left. However, we don't have a source-left for locally
-         * added files. */
-        right_repos_relpath = (reason == svn_wc_conflict_reason_added ?
-                               added_repos_relpath : left_repos_relpath);
-      }
-
-    /* Determine PCONFLICT's overall node kind, which is not allowed to be
-     * svn_node_none. We give it the source-right revision (THEIR_NODE_KIND)
-     * -- unless source-right is deleted and hence == svn_node_none, in which
-     * case we take it from source-left, which has to be the node kind that
-     * was deleted. */
-    conflict_node_kind = (action == svn_wc_conflict_action_delete ?
-                          left_kind : their_node_kind);
-    SVN_ERR_ASSERT(conflict_node_kind == svn_node_file
-                   || conflict_node_kind == svn_node_dir);
-
-
-    /* Construct the tree conflict info structs. */
-
-    if (left_repos_relpath == NULL)
-      /* A locally added path in conflict with an incoming add.
-       * Send an 'empty' left revision. */
-      src_left_version = NULL;
-    else
-      src_left_version = svn_wc_conflict_version_create(repos_root_url,
-                                                        left_repos_relpath,
-                                                        left_revision,
-                                                        left_kind,
-                                                        pool);
-
-    src_right_version = svn_wc_conflict_version_create(repos_root_url,
-                                                       right_repos_relpath,
-                                                       *eb->target_revision,
-                                                       their_node_kind,
-                                                       pool);
-
-    *pconflict = svn_wc_conflict_description_create_tree2(
-                     local_abspath, conflict_node_kind,
-                     eb->switch_relpath ?
-                       svn_wc_operation_switch : svn_wc_operation_update,
-                     src_left_version, src_right_version, pool);
-    (*pconflict)->action = action;
-    (*pconflict)->reason = reason;
-  }
-
-  return SVN_NO_ERROR;
+  return svn_error_return(create_tree_conflict(pconflict, eb, local_abspath,
+                                               reason, action, their_node_kind,
+                                               their_relpath, pool, pool));
 }
 
+
 /* If LOCAL_ABSPATH is inside a conflicted tree, set *CONFLICTED to TRUE,
  * Otherwise set *CONFLICTED to FALSE.  Use SCRATCH_POOL for temporary
  * allocations.
@@ -1923,58 +1843,55 @@ already_in_a_tree_conflict(svn_boolean_t
 
   while (TRUE)
     {
-      svn_wc__db_kind_t kind;
-      svn_boolean_t hidden;
-      svn_boolean_t is_wc_root;
+      svn_wc__db_status_t status;
+      svn_boolean_t is_wc_root, has_conflict;
       svn_error_t *err;
       const svn_wc_conflict_description2_t *conflict;
 
       svn_pool_clear(iterpool);
 
-      err = svn_wc__db_read_kind(&kind, db, ancestor_abspath, TRUE,
-                                 iterpool);
+      err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
+                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                 NULL, &has_conflict, NULL,
+                                 db, ancestor_abspath, iterpool, iterpool);
 
       if (err)
         {
-          if (! SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
+          if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
+              && !SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
             return svn_error_return(err);
 
           svn_error_clear(err);
           break;
         }
 
-      if (kind == svn_wc__db_kind_unknown)
-        break;
-
-      SVN_ERR(svn_wc__db_node_hidden(&hidden, db, ancestor_abspath, iterpool));
-
-      if (hidden)
+      if (status == svn_wc__db_status_not_present
+          || status == svn_wc__db_status_absent
+          || status == svn_wc__db_status_excluded)
         break;
 
-      SVN_ERR(svn_wc__db_op_read_tree_conflict(&conflict, db, ancestor_abspath,
-                                               iterpool, iterpool));
-
-      if (conflict != NULL)
+      if (has_conflict)
         {
-          *conflicted = TRUE;
-          break;
+          SVN_ERR(svn_wc__db_op_read_tree_conflict(&conflict, db,
+                                                   ancestor_abspath,
+                                                   iterpool, iterpool));
+
+          if (conflict != NULL)
+            {
+              *conflicted = TRUE;
+              break;
+            }
         }
 
       if (svn_dirent_is_root(ancestor_abspath, strlen(ancestor_abspath)))
         break;
 
-      err = svn_wc__check_wc_root(&is_wc_root, NULL, NULL,
-                                  db, ancestor_abspath, iterpool);
+      SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
+                                   iterpool));
 
-      if (err
-          && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
-              || err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
-        {
-          svn_error_clear(err);
-          return SVN_NO_ERROR;
-        }
-      else
-        SVN_ERR(err);
+      if (is_wc_root)
+        break;
 
       ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
     }
@@ -2047,19 +1964,25 @@ do_entry_deletion(struct edit_baton *eb,
                   svn_boolean_t in_deleted_and_tree_conflicted_subtree,
                   apr_pool_t *pool)
 {
+  svn_wc__db_status_t status;
   svn_wc__db_kind_t kind;
-  svn_boolean_t already_conflicted;
+  svn_boolean_t conflicted;
   svn_wc_conflict_description2_t *tree_conflict = NULL;
   const char *dir_abspath = svn_dirent_dirname(local_abspath, pool);
   svn_boolean_t hidden;
   svn_skel_t *work_item;
 
-  SVN_ERR(svn_wc__db_read_kind(&kind, eb->db, local_abspath, FALSE, pool));
+  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,
+                               &conflicted, NULL,
+                               eb->db, local_abspath, pool, pool));
 
   /* Is this path a conflict victim? */
-  SVN_ERR(node_already_conflicted(&already_conflicted, eb->db,
-                                  local_abspath, pool));
-  if (already_conflicted)
+  if (conflicted)
+    SVN_ERR(node_already_conflicted(&conflicted, eb->db,
+                                    local_abspath, pool));
+  if (conflicted)
     {
       SVN_ERR(remember_skipped_tree(eb, local_abspath));
 
@@ -2072,10 +1995,11 @@ do_entry_deletion(struct edit_baton *eb,
 
     /* Receive the remote removal of excluded/absent/not present node.
        Do not notify. */
-  SVN_ERR(svn_wc__db_node_hidden(&hidden, eb->db, local_abspath, pool));
-  if (hidden)
+  if (status == svn_wc__db_status_not_present
+      || status == svn_wc__db_status_excluded
+      || status == svn_wc__db_status_absent)
     {
-      SVN_ERR(svn_wc__db_temp_op_remove_entry(eb->db, local_abspath, pool));
+      SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, pool));
 
       if (strcmp(local_abspath, eb->target_abspath) == 0)
         eb->target_deleted = TRUE;
@@ -2097,12 +2021,19 @@ do_entry_deletion(struct edit_baton *eb,
 
   if (tree_conflict != NULL)
     {
-      /* When we raise a tree conflict on a directory, we want to avoid
+      /* When we raise a tree conflict on a node, we want to avoid
        * making any changes inside it. (Will an update ever try to make
-       * further changes to or inside a directory it's just deleted?) */
-      SVN_ERR(svn_wc__loggy_add_tree_conflict(&work_item, eb->db, dir_abspath,
-                                              tree_conflict, pool));
-      SVN_ERR(svn_wc__db_wq_add(eb->db, dir_abspath, work_item, pool));
+       * further changes to or inside a directory it's just deleted?)
+       *
+       * ### BH: Only if a new node is added in it's place.
+       *          See svn_delta.h for further details. */
+
+      /* ### Note that we still add this on the parent of a node, so it is
+         ### safe to add it now, while we remove the node later */
+      SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db,
+                                              tree_conflict->local_abspath,
+                                              tree_conflict,
+                                              pool));
 
       SVN_ERR(remember_skipped_tree(eb, local_abspath));
 
@@ -2113,23 +2044,16 @@ do_entry_deletion(struct edit_baton *eb,
         {
           /* The item exists locally and has some sort of local mod.
            * It no longer exists in the repository at its target URL@REV.
-           * (### If its WC parent was not updated similarly, then it needs to
-           * be marked 'deleted' in its WC parent.)
+           *
            * To prepare the "accept mine" resolution for the tree conflict,
            * we must schedule the existing content for re-addition as a copy
            * of what it was, but with its local modifications preserved. */
 
-          /* Run the log in the parent dir, to record the tree conflict.
-           * Do this before schedule_existing_item_for_re_add(), in case
-           * that needs to modify the same entries. */
-          SVN_ERR(svn_wc__wq_run(eb->db, dir_abspath,
-                                 eb->cancel_func, eb->cancel_baton,
-                                 pool));
-
-          SVN_ERR(svn_wc__db_temp_op_make_copy(eb->db, local_abspath, TRUE,
+          SVN_ERR(svn_wc__db_temp_op_make_copy(eb->db, local_abspath, FALSE,
                                                pool));
 
-          return SVN_NO_ERROR;
+          /* Fall through to remove the BASE_NODEs properly, with potentially
+             keeping a not-present marker */
         }
       else if (tree_conflict->reason == svn_wc_conflict_reason_deleted)
         {
@@ -2143,31 +2067,19 @@ do_entry_deletion(struct edit_baton *eb,
       else if (tree_conflict->reason == svn_wc_conflict_reason_replaced)
         {
           /* The item was locally replaced with something else. We should
-           * keep the existing item schedule-replace, but we also need to
-           * update the BASE rev of the item to the revision we are updating
-           * to. Otherwise, the replace cannot be committed because the item
-           * is considered out-of-date, and it cannot be updated either because
-           * we're here to do just that. */
-
-          /* Run the log in the parent dir, to record the tree conflict.
-           * Do this before schedule_existing_item_for_re_add(), in case
-           * that needs to modify the same entries. */
-          SVN_ERR(svn_wc__wq_run(eb->db, dir_abspath,
-                                 eb->cancel_func, eb->cancel_baton,
-                                 pool));
-
-          SVN_ERR(svn_wc__db_temp_op_make_copy(eb->db, local_abspath, TRUE,
-                                               pool));
-
-          return SVN_NO_ERROR;
+           * remove the BASE node below the new working node, which turns
+           * the replacement in an addition. */
+           
+           /* Fall through to the normal "delete" code path. */
         }
       else
         SVN_ERR_MALFUNCTION();  /* other reasons are not expected here */
     }
 
-  /* Issue a loggy command to delete the entry from version control and to
-     delete it from disk if unmodified, but leave any modified files on disk
-     unversioned.
+  /* Issue a wq operation to delete the BASE_NODE data and to delete actual
+     nodes based on that from disk, but leave any WORKING_NODEs on disk.
+
+     Local modifications are already turned into copies at this point.
 
      If the thing being deleted is the *target* of this update, then
      we need to recreate a 'deleted' entry, so that the parent can give
@@ -2175,58 +2087,21 @@ do_entry_deletion(struct edit_baton *eb,
   if (strcmp(local_abspath, eb->target_abspath) != 0)
     {
       /* Delete, and do not leave a not-present node.  */
-      SVN_ERR(svn_wc__loggy_delete_entry(&work_item,
-                                         eb->db, dir_abspath, local_abspath,
-                                         SVN_INVALID_REVNUM,
-                                         svn_wc__db_kind_unknown,
-                                         pool));
+      SVN_ERR(svn_wc__wq_build_base_remove(&work_item,
+                                           eb->db, local_abspath, FALSE,
+                                           pool, pool));
       SVN_ERR(svn_wc__db_wq_add(eb->db, dir_abspath, work_item, pool));
     }
   else
     {
       /* Delete, leaving a not-present node.  */
-      SVN_ERR(svn_wc__loggy_delete_entry(&work_item,
-                                         eb->db, dir_abspath, local_abspath,
-                                         *eb->target_revision,
-                                         kind,
-                                         pool));
+      SVN_ERR(svn_wc__wq_build_base_remove(&work_item,
+                                           eb->db, local_abspath, TRUE,
+                                           pool, pool));
       SVN_ERR(svn_wc__db_wq_add(eb->db, dir_abspath, work_item, pool));
       eb->target_deleted = TRUE;
     }
 
-  if (eb->switch_relpath)
-    {
-      /* The SVN_WC__LOG_DELETE_ENTRY log item will cause
-       * svn_wc_remove_from_revision_control() to be run.  But that
-       * function checks whether the deletion target's URL is child of
-       * its parent directory's URL, and if it's not, then the entry
-       * in parent won't be deleted (because presumably the child
-       * represents a disjoint working copy, i.e., it is a wc_root).
-       *
-       * However, during a switch this works against us, because by
-       * the time we get here, the parent's URL has already been
-       * changed.  So we manually remove the child from revision
-       * control after the delete-entry item has been written in the
-       * parent's log, but before it is run, so the only work left for
-       * the log item is to remove the entry in the parent directory.
-       */
-
-      if (kind == svn_wc__db_kind_dir)
-        {
-          SVN_ERR(leftmod_error_chain(
-                    svn_wc__internal_remove_from_revision_control(
-                      eb->db,
-                      local_abspath,
-                      TRUE, /* destroy */
-                      FALSE, /* instant error */
-                      eb->cancel_func,
-                      eb->cancel_baton,
-                      pool)));
-        }
-    }
-
-  /* Note: these two lines are duplicated in the tree-conflicts bail out
-   * above. */
   SVN_ERR(svn_wc__wq_run(eb->db, dir_abspath,
                          eb->cancel_func, eb->cancel_baton,
                          pool));
@@ -2288,7 +2163,9 @@ add_directory(const char *path,
   svn_node_kind_t kind;
   svn_wc__db_status_t status;
   svn_wc__db_kind_t wc_kind;
-  svn_boolean_t already_conflicted;
+  svn_boolean_t conflicted;
+  svn_boolean_t versioned_locally_and_present;
+  svn_wc_conflict_description2_t *tree_conflict = NULL;
   svn_error_t *err;
 
   /* Semantic check.  Either both "copyfrom" args are valid, or they're
@@ -2364,25 +2241,6 @@ add_directory(const char *path,
       db->ambient_depth = svn_depth_infinity;
     }
 
-  /* Is this path a conflict victim? */
-  SVN_ERR(node_already_conflicted(&already_conflicted, eb->db,
-                                  db->local_abspath, pool));
-  if (already_conflicted)
-    {
-      /* Record this conflict so that its descendants are skipped silently. */
-      SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
-
-      db->skip_this = TRUE;
-      db->skip_descendants = TRUE;
-      db->already_notified = TRUE;
-
-      /* ### TODO: Also print victim_path in the skip msg. */
-      do_notification(eb, db->local_abspath, svn_node_unknown,
-                      svn_wc_notify_skip, pool);
-
-      return SVN_NO_ERROR;
-    }
-
   /* It may not be named the same as the administrative directory. */
   if (svn_wc_is_adm_dir(db->name, pool))
     return svn_error_createf(
@@ -2395,8 +2253,8 @@ add_directory(const char *path,
 
   err = svn_wc__db_read_info(&status, &wc_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, NULL, NULL, NULL, NULL,
+                             &conflicted, NULL,
                              eb->db, db->local_abspath, db->pool, db->pool);
   if (err)
     {
@@ -2406,48 +2264,98 @@ add_directory(const char *path,
       svn_error_clear(err);
       wc_kind = svn_wc__db_kind_unknown;
       status = svn_wc__db_status_normal;
+      conflicted = TRUE;  /* TRUE here causes us to check for a conflict */
+
+      versioned_locally_and_present = FALSE;
+    }
+  else
+    versioned_locally_and_present = IS_NODE_PRESENT(status);
+
+  /* Is this path a conflict victim? */
+  if (conflicted)
+    SVN_ERR(node_already_conflicted(&conflicted, eb->db,
+                                    db->local_abspath, pool));
+  if (conflicted
+      && status == svn_wc__db_status_not_present
+      && kind == svn_node_none)
+    {
+      /* A conflict is flagged. Now let's do some user convenience.
+       * When we flagged a tree conflict for a local unversioned node
+       * vs. an incoming add, and we find that this unversioned node is
+       * no longer in the way, automatically pull in the versioned node
+       * and remove the conflict marker.
+       * Right, the node status matches (not_present) and there is no
+       * unversioned obstruction in the file system (anymore?). If it
+       * has a tree conflict with reason 'unversioned', remove that. */
+      const svn_wc_conflict_description2_t *previous_tc;
+      SVN_ERR(svn_wc__get_tree_conflict(&previous_tc,
+                                        eb->wc_ctx,
+                                        db->local_abspath,
+                                        pool, pool));
+      if (previous_tc
+          && previous_tc->reason == svn_wc_conflict_reason_unversioned)
+        {
+          /* Remove tree conflict. */
+          SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db,
+                                                  db->local_abspath,
+                                                  NULL, pool));
+          /* Don't skip this path after all. */
+          conflicted = FALSE;
+        }
     }
 
-  /* The path can exist, but it must be a directory. */
-  if (kind == svn_node_file || kind == svn_node_unknown ||
-      (wc_kind != svn_wc__db_kind_unknown &&
-       wc_kind != svn_wc__db_kind_dir && IS_NODE_PRESENT(status)))
+  /* Now the "usual" behaviour if already conflicted. Skip it. */
+  if (conflicted)
     {
+      /* Record this conflict so that its descendants are skipped silently. */
+      SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
+
+      db->skip_this = TRUE;
+      db->skip_descendants = TRUE;
       db->already_notified = TRUE;
-      do_notification(eb, db->local_abspath, svn_node_dir,
-                      svn_wc_notify_update_obstruction, pool);
 
-      return svn_error_createf(
-       SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
-       _("Failed to add directory '%s': a non-directory object of the "
-         "same name already exists"),
-       svn_dirent_local_style(db->local_abspath, pool));
+      /* ### TODO: Also print victim_path in the skip msg. */
+      do_notification(eb, db->local_abspath, svn_node_unknown,
+                      svn_wc_notify_skip, pool);
+      return SVN_NO_ERROR;
     }
 
-  if (kind == svn_node_dir &&
-      (wc_kind == svn_wc__db_kind_unknown || !IS_NODE_PRESENT(status)))
+
+  if (versioned_locally_and_present)
     {
-      /* Found an unversioned directory */
-      db->obstruction_found = TRUE;
+      /* What to do with a versioned or schedule-add dir:
 
-      if (!eb->allow_unver_obstructions)
-        {
-          db->already_notified = TRUE;
-          do_notification(eb, db->local_abspath, svn_node_dir,
-                          svn_wc_notify_update_obstruction, pool);
+         A dir already added without history is OK.  Set add_existed
+         so that user notification is delayed until after any prop
+         conflicts have been found.
 
-          return svn_error_createf(
-             SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
-             _("Failed to add directory '%s': an unversioned "
-               "directory of the same name already exists"),
-             svn_dirent_local_style(db->local_abspath, pool));
-        }
-    }
-  else if (wc_kind == svn_wc__db_kind_dir)
-    {
-      /* Directory exists */
-      if (IS_NODE_PRESENT(status) &&
-          status != svn_wc__db_status_deleted)
+         An existing versioned dir is an error.  In the future we may
+         relax this restriction and simply update such dirs.
+
+         A dir added with history is a tree conflict. */
+
+      svn_boolean_t local_is_dir;
+      svn_boolean_t local_is_non_dir;
+      const char *local_is_copy = NULL;
+
+      /* Is the local add a copy? */
+      if (status == svn_wc__db_status_added)
+        SVN_ERR(svn_wc__node_get_copyfrom_info(&local_is_copy,
+                                               NULL, NULL, NULL, NULL,
+                                               eb->wc_ctx,
+                                               db->local_abspath,
+                                               pool, pool));
+
+
+      /* Is there something that is a file? */
+      local_is_dir = (wc_kind == svn_wc__db_kind_dir
+                      && status != svn_wc__db_status_deleted);
+
+      /* Is there *something* that is not a dir? */
+      local_is_non_dir = (wc_kind != svn_wc__db_kind_dir
+                          && status != svn_wc__db_status_deleted);
+
+      if (local_is_dir)
         {
           svn_boolean_t wc_root;
           svn_boolean_t switched;
@@ -2489,129 +2397,126 @@ add_directory(const char *path,
             }
         }
 
-      /* What to do with a versioned or schedule-add dir:
+      /* We can't properly handle add vs. add with mismatching
+       * node kinds before single db. */
+      if (local_is_non_dir)
+        {
+          db->already_notified = TRUE;
+          do_notification(eb, db->local_abspath, svn_node_dir,
+                          svn_wc_notify_update_obstruction, pool);
+          return svn_error_createf(
+                   SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
+                   _("Failed to add directory '%s': a non-directory object "
+                     "of the same name already exists"),
+                   svn_dirent_local_style(db->local_abspath,
+                                          pool));
+        }
 
-         A dir already added without history is OK.  Set add_existed
-         so that user notification is delayed until after any prop
-         conflicts have been found.
-
-         An existing versioned dir is an error.  In the future we may
-         relax this restriction and simply update such dirs.
-
-         A dir added with history is a tree conflict. */
-
-      if (status == svn_wc__db_status_added)
-        {
-          /* Specialize the added case to added, copied, moved */
-          SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL,
-                                           NULL, NULL, NULL, NULL, eb->db,
-                                           db->local_abspath, pool, pool));
+      /* Do tree conflict checking if
+       *  - if there is a local copy.
+       *  - if this is a switch operation
+       *  - the node kinds mismatch (when single db is here)
+       *
+       * During switch, local adds at the same path as incoming adds get
+       * "lost" in that switching back to the original will no longer have the
+       * local add. So switch always alerts the user with a tree conflict.
+       *
+       * Allow pulling absent/exluded/not_present nodes back in.
+       *
+       * ### This code is already gearing up to single db with respect to
+       * add-vs.-add conflicts with mismatching node kinds. But before single
+       * db, we cannot deal with mismatching node kinds properly.
+       *
+       * ### We would also like to be checking copyfrom infos to not flag tree
+       * conflicts on two copies with identical history. But at the time of
+       * writing, add_directory() does not get any copyfrom information. */
+      if (! pb->in_deleted_and_tree_conflicted_subtree
+          && (eb->switch_relpath != NULL
+              || local_is_non_dir
+              || local_is_copy
+             )
+         )
+        {
+          SVN_ERR(check_tree_conflict(&tree_conflict, eb,
+                                      db->local_abspath,
+                                      svn_wc_conflict_action_add,
+                                      svn_node_dir, db->new_relpath, pool));
         }
 
-      switch (status)
-        {
-          case svn_wc__db_status_absent:
-          case svn_wc__db_status_excluded:
-            /* Ignore these hidden states. Allow pulling them (back) in. */
-            break;
-          case svn_wc__db_status_not_present:
-            break;
-          case svn_wc__db_status_obstructed:
-          case svn_wc__db_status_obstructed_add:
-          case svn_wc__db_status_obstructed_delete:
-            /* ### In 1.6 we ignored these cases in this block as
-                   the behavior was just pulling them back in.
-
-                   Explicitly handle them as not raising a tree conflict
-                   now. Will never occur once we have a central DB. */
-            break;
-          case svn_wc__db_status_added:
-            /* ### BH: I think this case should be conditional with something
-                       like allow_unver_obstructions, as this changes the
-                       base of locally added files.
-               ### BH: Always generate tree conflict? */
-            db->add_existed = TRUE;
-            break;
-          default:
-            {
-              svn_wc_conflict_description2_t *tree_conflict = NULL;
-
-              /* Check for conflicts only when we haven't already recorded
-               * a tree-conflict on a parent node. */
-              if (!db->in_deleted_and_tree_conflicted_subtree)
-                SVN_ERR(check_tree_conflict(&tree_conflict, eb,
-                                            db->local_abspath,
-                                            svn_wc_conflict_action_add,
-                                            svn_node_dir, db->new_relpath, pool));
 
-              if (tree_conflict != NULL)
-                {
-                  svn_skel_t *work_item;
-
-                  /* Queue this conflict in the parent so that its descendants
-                     are skipped silently. */
-                  SVN_ERR(svn_wc__loggy_add_tree_conflict(&work_item,
-                                                          eb->db,
-                                                          pb->local_abspath,
-                                                          tree_conflict,
-                                                          pool));
-                  SVN_ERR(svn_wc__db_wq_add(eb->db, pb->local_abspath,
-                                            work_item, pool));
-
-                  SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
-
-                  db->skip_this = TRUE;
-                  db->skip_descendants = TRUE;
-                  db->already_notified = TRUE;
-
-                  do_notification(eb, db->local_abspath, svn_node_unknown,
-                                  svn_wc_notify_tree_conflict, pool);
-
-                  return SVN_NO_ERROR;
-                }
-            }
-            break;
+      if (tree_conflict == NULL)
+        {
+          /* We have a node in WORKING and we've decided not to flag a
+           * conflict, so merge it with the incoming add. */
+          db->add_existed = TRUE;
         }
     }
-
+  else if (kind != svn_node_none)
     {
-      /* Immediately create an entry for the new directory in the parent.
-         Note that the parent must already be either added or opened, and
-         thus it's in an 'incomplete' state just like the new dir.
-         The entry may already exist if the new directory is already
-         scheduled for addition without history, in that case set
-         its schedule to normal. */
-      SVN_ERR(svn_wc__db_temp_set_parent_stub_to_normal(eb->db,
-                                                        db->local_abspath,
-                                                        db->add_existed,
-                                                        pool));
+      /* There's an unversioned node at this path. */
+      db->obstruction_found = TRUE;
 
-      if (db->add_existed)
+      /* Unversioned, obstructing dirs are handled by prop merge/conflict,
+       * if unversioned obstructions are allowed. */
+      if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
         {
-          /* Immediately tweak the schedule for "this dir" so it too
-             is no longer scheduled for addition.  Change rev from 0
-             to the target revision allowing prep_directory() to do
-             its thing without error. 
-
-             ### In the future this should probably become a proper
-             ### tree conflict and just handled by putting a base
-             ### directory below the existing working node.
-             */
-          SVN_ERR(svn_wc__db_temp_op_set_new_dir_to_incomplete(
-                                                  eb->db,
-                                                  db->local_abspath,
+          /* ### Instead of skipping, this should bring in the BASE node
+           * and mark some sort of obstruction-conflict. Come, o single-db! */
+          db->skip_this = TRUE;
+
+          /* If we are skipping an add, we need to tell the WC that
+           * there's a node supposed to be here which we don't have. */
+          SVN_ERR(svn_wc__db_base_add_absent_node(eb->db, db->local_abspath,
                                                   db->new_relpath,
                                                   eb->repos_root,
                                                   eb->repos_uuid,
-                                                  *eb->target_revision,
-                                                  pool));
+                                                  (eb->target_revision?
+                                                   *eb->target_revision
+                                                   : SVN_INVALID_REVNUM),
+                                                  svn_wc__db_kind_dir,
+                                                  svn_wc__db_status_not_present,
+                                                  NULL, NULL, pool));
+          SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
 
-          SVN_ERR(svn_wc__db_temp_set_parent_stub_to_normal(eb->db,
-                                                            db->local_abspath,
-                                                            TRUE, pool));
+          /* Mark a conflict */
+          SVN_ERR(create_tree_conflict(&tree_conflict, eb,
+                                       db->local_abspath,
+                                       svn_wc_conflict_reason_unversioned,
+                                       svn_wc_conflict_action_add,
+                                       svn_node_dir,
+                                       db->new_relpath, pool, pool));
+          SVN_ERR_ASSERT(tree_conflict != NULL);
         }
     }
 
+  if (tree_conflict != NULL)
+    {
+      /* Queue this conflict in the parent so that its descendants
+         are skipped silently. */
+      SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db, db->local_abspath,
+                                              tree_conflict, pool));
+
+      SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
+
+      db->skip_this = TRUE;
+      db->skip_descendants = TRUE;
+      db->already_notified = TRUE;
+
+      do_notification(eb, db->local_abspath, svn_node_unknown,
+                      svn_wc_notify_tree_conflict, pool);
+      return SVN_NO_ERROR;
+    }
+
+
+  SVN_ERR(svn_wc__db_temp_op_set_new_dir_to_incomplete(eb->db,
+                                                       db->local_abspath,
+                                                       db->new_relpath,
+                                                       eb->repos_root,
+                                                       eb->repos_uuid,
+                                                       *eb->target_revision,
+                                                       db->ambient_depth,
+                                                       pool));
+
   SVN_ERR(prep_directory(db,
                          svn_path_url_add_component2(eb->repos_root,
                                                      db->new_relpath, pool),
@@ -2663,7 +2568,7 @@ open_directory(const char *path,
   struct dir_baton *db, *pb = parent_baton;
   struct edit_baton *eb = pb->edit_baton;
   svn_boolean_t have_work;
-  svn_boolean_t already_conflicted;
+  svn_boolean_t conflicted;
   svn_wc_conflict_description2_t *tree_conflict = NULL;
   svn_wc__db_status_t status, base_status;
 
@@ -2693,7 +2598,7 @@ open_directory(const char *path,
                                NULL, NULL, NULL, NULL, NULL,
                                &db->ambient_depth, NULL, NULL, NULL, NULL,
                                NULL, NULL, NULL, NULL, NULL,
-                               NULL, &have_work, NULL, NULL,
+                               NULL, &have_work, &conflicted, NULL,
                                eb->db, db->local_abspath, pool, pool));
 
   if (!have_work)
@@ -2708,9 +2613,10 @@ open_directory(const char *path,
   db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
 
   /* Is this path a conflict victim? */
-  SVN_ERR(node_already_conflicted(&already_conflicted, eb->db,
-                                  db->local_abspath, pool));
-  if (already_conflicted)
+  if (conflicted)
+    SVN_ERR(node_already_conflicted(&conflicted, eb->db,
+                                    db->local_abspath, pool));
+  if (conflicted)
     {
       SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
 
@@ -2737,13 +2643,9 @@ open_directory(const char *path,
   /* Remember the roots of any locally deleted trees. */
   if (tree_conflict != NULL)
     {
-      svn_skel_t *work_item;
-
       /* Place a tree conflict into the parent work queue.  */
-      SVN_ERR(svn_wc__loggy_add_tree_conflict(&work_item,
-                                              eb->db, pb->local_abspath,
+      SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db, db->local_abspath,
                                               tree_conflict, pool));
-      SVN_ERR(svn_wc__db_wq_add(eb->db, pb->local_abspath, work_item, pool));
 
       do_notification(eb, db->local_abspath, svn_node_dir,
                       svn_wc_notify_tree_conflict, pool);
@@ -2832,54 +2734,6 @@ prop_hash_from_array(const apr_array_hea
   return prop_hash;
 }
 
-
-/* Set *WORK_ITEM to a new work item to write an old-style propsfile for the
-   specified BASE properties, BASE_PROPS.
-
-   ### breaks the props encapsulation by using svn_wc__prop_path, but this
-   ### function will only last until we move props into the database.  */
-static svn_error_t *
-build_write_base_props(svn_skel_t **work_item,
-                       const char *local_abspath,
-                       svn_wc__db_kind_t kind,
-                       apr_hash_t *base_props,
-                       apr_pool_t *scratch_pool)
-{
-  const char *props_abspath;
-
-  SVN_ERR(svn_wc__prop_path(&props_abspath, local_abspath, kind,
-                            svn_wc__props_base, scratch_pool));
-  return svn_error_return(svn_wc__wq_build_write_old_props(work_item,
-                                                           props_abspath,
-                                                           base_props,
-                                                           scratch_pool));
-}
-
-
-/* Set *WORK_ITEM to a new work item to write an old-style propsfile for the
-   specified ACTUAL properties. If ACTUAL_PROPS is NULL, then the old-style
-   propsfile will be removed, indicating "no change" from the pristines.
-
-   ### breaks the props encapsulation by using svn_wc__prop_path, but this
-   ### function will only last until we move props into the database.  */
-static svn_error_t *
-build_write_actual_props(svn_skel_t **work_item,
-                         const char *local_abspath,
-                         svn_wc__db_kind_t kind,
-                         apr_hash_t *actual_props,
-                         apr_pool_t *scratch_pool)
-{
-  const char *props_abspath;
-
-  SVN_ERR(svn_wc__prop_path(&props_abspath, local_abspath, kind,
-                            svn_wc__props_working, scratch_pool));
-  return svn_error_return(svn_wc__wq_build_write_old_props(work_item,
-                                                           props_abspath,
-                                                           actual_props,
-                                                           scratch_pool));
-}
-
-
 /* An svn_delta_editor_t function. */
 static svn_error_t *
 close_directory(void *dir_baton,
@@ -2888,7 +2742,7 @@ close_directory(void *dir_baton,
   struct dir_baton *db = dir_baton;
   struct edit_baton *eb = db->edit_baton;
   svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
-  apr_array_header_t *entry_props, *wc_props, *regular_props;
+  apr_array_header_t *entry_props, *dav_props, *regular_props;
   apr_hash_t *base_props;
   apr_hash_t *actual_props;
   apr_hash_t *new_base_props = NULL, *new_actual_props = NULL;
@@ -2911,7 +2765,7 @@ close_directory(void *dir_baton,
       return SVN_NO_ERROR;
     }
 
-  SVN_ERR(svn_categorize_props(db->propchanges, &entry_props, &wc_props,
+  SVN_ERR(svn_categorize_props(db->propchanges, &entry_props, &dav_props,
                                &regular_props, pool));
 
   /* Fetch the existing properties.  */
@@ -2966,7 +2820,7 @@ close_directory(void *dir_baton,
 
   /* If this directory has property changes stored up, now is the time
      to deal with them. */
-  if (regular_props->nelts || entry_props->nelts || wc_props->nelts)
+  if (regular_props->nelts || entry_props->nelts || dav_props->nelts)
     {
       if (regular_props->nelts)
         {
@@ -3056,7 +2910,6 @@ close_directory(void *dir_baton,
       apr_time_t changed_date;
       const char *changed_author;
       apr_hash_t *props;
-      svn_skel_t *work_items;
 
       /* ### we know a base node already exists. it was created in
          ### open_directory or add_directory.  let's just preserve the
@@ -3078,22 +2931,17 @@ close_directory(void *dir_baton,
       if (new_changed_author != NULL)
         changed_author = new_changed_author;
 
+      /* If no depth is set yet, set to infinity. */
+      if (depth == svn_depth_unknown)
+        depth = svn_depth_infinity;
+
       /* Do we have new properties to install? Or shall we simply retain
          the prior set of properties? If we're installing new properties,
          then we also want to write them to an old-style props file.  */
       props = new_base_props;
       if (props == NULL)
-        {
-          SVN_ERR(svn_wc__db_base_get_props(&props, eb->db, db->local_abspath,
-                                            pool, pool));
-          work_items = NULL;
-        }
-      else
-        {
-          SVN_ERR(build_write_base_props(&work_items, db->local_abspath,
-                                         svn_wc__db_kind_dir, new_base_props,
-                                         pool));
-        }
+        SVN_ERR(svn_wc__db_base_get_props(&props, eb->db, db->local_abspath,
+                                          pool, pool));
 
       /* ### NOTE: from this point onwards, we make TWO changes to the
          ### database in a non-transactional way. some kind of revamp
@@ -3109,8 +2957,11 @@ close_directory(void *dir_baton,
                 changed_rev, changed_date, changed_author,
                 NULL /* children */,
                 depth,
+                (dav_props && dav_props->nelts > 0)
+                    ? prop_hash_from_array(dav_props, pool)
+                    : NULL,
                 NULL /* conflict */,
-                work_items,
+                NULL /* work_items */,
                 pool));
 
       /* If we updated the BASE properties, then we also have ACTUAL
@@ -3132,25 +2983,12 @@ close_directory(void *dir_baton,
           if (prop_diffs->nelts == 0)
             props = NULL;
 
-          SVN_ERR(build_write_actual_props(&work_items, db->local_abspath,
-                                           svn_wc__db_kind_dir,
-                                           props,
-                                           pool));
           SVN_ERR(svn_wc__db_op_set_props(eb->db, db->local_abspath,
                                           props,
                                           NULL /* conflict */,
-                                          work_items,
+                                          NULL /* work_items */,
                                           pool));
         }
-
-      /* Handle the wcprops. */
-      if (wc_props && wc_props->nelts > 0)
-        {
-          SVN_ERR(svn_wc__db_base_set_dav_cache(eb->db, db->local_abspath,
-                                                prop_hash_from_array(wc_props,
-                                                                     pool),
-                                                pool));
-        }
     }
 
   /* Process all of the queued work items for this directory.  */
@@ -3275,15 +3113,35 @@ absent_directory(const char *path,
 }
 
 
-/* Beginning at DIR_ABSPATH (from repository with uuid DIR_REPOS_UUID and
-   with repos_relpath DIR_REPOS_RELPATH) within a working copy, search the
-   working copy for a pre-existing versioned file which is exactly equal
-   to COPYFROM_PATH@COPYFROM_REV.
+/* Beginning at DIR_ABSPATH within a working copy, search the working copy
+   copy for a pre-existing versioned file which is exactly equal to
+   COPYFROM_PATH@COPYFROM_REV.
+
+   The current implementation does this by taking the repos_relpath of
+   dir_abspath and copyfrom_relpath to calculate where in the working copy
+   repos_relpath would be and then tries to confirm its guess.
+
+   1) When it finds a copied file there, it looks for its origin to see
+   if the origin matches the copied file good enough to use it as new base
+   contents and properties. If that is the case set NEW_BASE_CONTENTS
+   and NEW_BASE_PROPS to the found restult.
+
+   If the new base information is found check if the node is tree-conflicted,
+   and when that is the case use its in-wc contents and actual properties
+   to set NEW_CONTENTS and NEW_PROPS.
+
+   (If new base info is found, return)
+
+   2) If the node's BASE information matches the expected origin matches the the
+   copy origin good enough use it as NEW_BASE_CONTENTS and NEW_BASE_PROPS.
 
-   If the file isn't found, set *RETURN_ABSPATH to NULL.
+   If the new base information is found and the db_status of the node is normal,
+   then set NEW_CONTENTS and NEW_PROPS with the found values.
 
-   If the file is found, return the absolute path to it in
-   *RETURN_ABSPATH.
+   If data is not found, its values will be set to NULL. 
+
+   Allocate the return values in RESULT_POOL, but perform all temporary allocations
+   in SCRATCH_POOL.
 
    ### With a centralized datastore this becomes much easier. For now we
    ### keep the old algorithm because the result is also used for copying
@@ -3291,32 +3149,40 @@ absent_directory(const char *path,
    ### local file moves.
 */
 static svn_error_t *
-locate_copyfrom(svn_wc__db_t *db,
-                const char *copyfrom_path,
-                svn_revnum_t copyfrom_rev,
+locate_copyfrom(svn_stream_t **new_base_contents,
+                svn_stream_t **new_contents,
+                apr_hash_t **new_base_props,
+                apr_hash_t **new_props,
+                svn_wc__db_t *db,
                 const char *dir_abspath,
-                const char *dir_repos_uuid,
-                const char *dir_repos_relpath,
-                const char **return_abspath,
+                const char *copyfrom_relpath,
+                svn_revnum_t copyfrom_rev,
                 apr_pool_t *result_pool,
                 apr_pool_t *scratch_pool)
 {
   const char *ancestor_abspath, *ancestor_relpath;
-  const char *copyfrom_relpath;
+  const char *dir_repos_relpath, *dir_repos_root_url, *dir_repos_uuid;
+  const char *repos_relpath, *repos_root_url, *repos_uuid;
+  const char *local_abspath;
+
   apr_size_t levels_up;
-  const char *file_abspath;
   svn_error_t *err;
 
-  SVN_ERR_ASSERT(dir_repos_relpath && dir_repos_uuid);
-  SVN_ERR_ASSERT(copyfrom_path[0] == '/');
+  SVN_ERR_ASSERT(copyfrom_relpath[0] != '/');
+
+  SVN_ERR(svn_wc__db_scan_base_repos(&dir_repos_relpath, &dir_repos_root_url,
+                                     &dir_repos_uuid,
+                                     db, dir_abspath,
+                                     scratch_pool, scratch_pool));
 
   /* Be pessimistic.  This function is basically a series of tests
      that gives dozens of ways to fail our search, returning
      SVN_NO_ERROR in each case.  If we make it all the way to the
      bottom, we have a real discovery to return. */
-  *return_abspath = NULL;
-
-  copyfrom_relpath = copyfrom_path+1; /* Skip the initial '/' */
+  *new_base_contents = NULL;
+  *new_contents = NULL;
+  *new_base_props = NULL;
+  *new_props = NULL;
 
   /* Subtract the dest_dir's URL from the repository "root" URL to get
      the absolute FS path represented by dest_dir. */
@@ -3336,118 +3202,229 @@ locate_copyfrom(svn_wc__db_t *db,
     ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
 
   /* Verify hypothetical ancestor */
-  {
-    const char *repos_relpath, *repos_uuid;
-
-    err = svn_wc__db_scan_base_repos(&repos_relpath, NULL, &repos_uuid,
-                                     db, ancestor_abspath,
-                                     scratch_pool, scratch_pool);
-
-    if (err && ((err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) ||
-                (err->apr_err == SVN_ERR_WC_PATH_FOUND)))
-      {
-        svn_error_clear(err);
-        return SVN_NO_ERROR;
-      }
-    else
-      SVN_ERR(err);
+  err = svn_wc__db_scan_base_repos(&repos_relpath, &repos_root_url,
+                                   &repos_uuid,
+                                   db, ancestor_abspath,
+                                   scratch_pool, scratch_pool);
 
-    /* If we got this far, we know that the ancestor dir exists, and
-       that it's a working copy too.  But is it from the same
-       repository?  And does it represent the URL we expect it to? */
-    if (strcmp(dir_repos_uuid, repos_uuid) != 0)
+  if (err && ((err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) ||
+              (err->apr_err == SVN_ERR_WC_PATH_FOUND)))
+    {
+      svn_error_clear(err);
       return SVN_NO_ERROR;
+    }
+  else
+    SVN_ERR(err);
 
-    if (strcmp(ancestor_relpath, repos_relpath) != 0)
-      return SVN_NO_ERROR;
-  }
+  /* If we got this far, we know that the ancestor dir exists, and
+     that it's a working copy too.  But is it from the same
+     repository?  And does it represent the URL we expect it to? */
+  if ((strcmp(dir_repos_uuid, repos_uuid) != 0)
+      || (strcmp(dir_repos_root_url, repos_root_url) != 0)
+      || (strcmp(ancestor_relpath, repos_relpath) != 0))
+    return SVN_NO_ERROR;
 
   /* Add the remaining components to cwd, then add the remaining relpath to
      where we hope the copyfrom_relpath file exists. */
-  file_abspath = svn_dirent_join(ancestor_abspath,
+  local_abspath = svn_dirent_join(ancestor_abspath,
                                  svn_dirent_skip_ancestor(ancestor_relpath,
                                                           copyfrom_relpath),
                                  scratch_pool);
 
   /* Verify file in expected location */
   {
-    svn_node_kind_t kind;
-    svn_revnum_t addition_rev = SVN_INVALID_REVNUM;
-    const char *repos_relpath, *repos_uuid;
-
-    /* First: does the proposed file path even exist? */
-    SVN_ERR(svn_io_check_path(file_abspath, &kind, scratch_pool));
-    if (kind != svn_node_file)
-      return SVN_NO_ERROR;
+    svn_revnum_t rev, changed_rev;
+    svn_wc__db_status_t status, base_status;
+    svn_boolean_t conflicted, have_base;
+    const svn_checksum_t *checksum;
 
-    /* Next: is the file under version control?   */
-    err = svn_wc__db_scan_base_repos(&repos_relpath, NULL, &repos_uuid,
-                                     db, file_abspath,
-                                     scratch_pool, scratch_pool);
+    err = svn_wc__db_read_info(&status, NULL, &rev, &repos_relpath,
+                               &repos_root_url, &repos_uuid, &changed_rev,
+                               NULL, NULL, NULL, NULL, &checksum, NULL, NULL,
+                               NULL, NULL, NULL, NULL, NULL, NULL, &have_base,
+                               NULL, &conflicted, NULL,
+                               db, local_abspath, scratch_pool, scratch_pool);
 
-    if (err && ((err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) ||
-                (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)))
+    if (err && ((err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY ||
+                (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND))))
       {
         svn_error_clear(err);
+        return SVN_NO_ERROR;
+      }
+    else
+      SVN_ERR(err);
 
-        /* ### Our entries handling made us handle the following
-               scenario: An older version of a file was copied at
-               exactly the expected location. Reproduced this behavior
-               until we can really querry the entire workingcopy. */
-
-        err = svn_wc__db_scan_addition(NULL, NULL, NULL, NULL, NULL,
-                                       &repos_relpath, NULL, &repos_uuid,
-                                       &addition_rev, db, file_abspath,
-                                       scratch_pool, scratch_pool);
+    /* Check if we have an added node with the right copyfrom information, as
+       this is what you would see on a file move. */
+       
+    if (status == svn_wc__db_status_added)
+      {
+        const char *op_root_abspath;
+        const char *original_repos_relpath, *original_root_url;
+        const char *original_uuid;
+        svn_revnum_t original_rev;
+
+        SVN_ERR(svn_wc__db_scan_addition(&status, &op_root_abspath,
+                                         &repos_relpath, &repos_root_url,
+                                         &repos_uuid, &original_repos_relpath,
+                                         &original_root_url, &original_uuid,
+                                         &original_rev,
+                                         db, local_abspath,
+                                         scratch_pool, scratch_pool));
 
-        if (err && ((err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY ||
-              (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND))))
+        if (status == svn_wc__db_status_copied
+            || status == svn_wc__db_status_moved_here)
           {
-            svn_error_clear(err);
+            original_repos_relpath = svn_relpath_join(
+                                    original_repos_relpath,
+                                    svn_dirent_skip_ancestor(op_root_abspath,
+                                                             local_abspath),
+                                    scratch_pool);
 
-            return SVN_NO_ERROR;
+            /* If the repository location matches our exact guess and

[... 2379 lines stripped ...]