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 [20/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_wc/update_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_wc/update_editor.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_wc/update_editor.c Mon Feb  4 20:48:05 2013
@@ -34,7 +34,6 @@
 
 #include "svn_types.h"
 #include "svn_pools.h"
-#include "svn_delta.h"
 #include "svn_hash.h"
 #include "svn_string.h"
 #include "svn_dirent_uri.h"
@@ -47,12 +46,13 @@
 #include "wc.h"
 #include "adm_files.h"
 #include "conflicts.h"
-#include "entries.h"
 #include "translate.h"
 #include "workqueue.h"
 
 #include "private/svn_subr_private.h"
 #include "private/svn_wc_private.h"
+#include "private/svn_editor.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)                             \
@@ -322,6 +322,10 @@ struct dir_baton
      marked as deleted. */
   svn_boolean_t shadowed;
 
+  /* Set on a node when the existing node is obstructed, and the edit operation
+     continues as semi-shadowed update */
+  svn_boolean_t edit_obstructed;
+
   /* The (new) changed_* information, cached to avoid retrieving it later */
   svn_revnum_t changed_rev;
   apr_time_t changed_date;
@@ -496,19 +500,6 @@ cleanup_edit_baton(void *edit_baton)
   return APR_SUCCESS;
 }
 
-/* An APR pool cleanup handler.  This is a child handler, it removes
-   the mail pool handler.
-   <stsp> mail pool?
-   <hwright> that's where the missing commit mails are going!  */
-static apr_status_t
-cleanup_edit_baton_child(void *edit_baton)
-{
-  struct edit_baton *eb = edit_baton;
-  apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
-  return APR_SUCCESS;
-}
-
-
 /* Make a new dir baton in a subpool of PB->pool. PB is the parent baton.
    If PATH and PB are NULL, this is the root directory of the edit; in this
    case, make the new dir baton in a subpool of EB->pool.
@@ -634,7 +625,7 @@ make_dir_baton(struct dir_baton **d_p,
   if (pb)
     {
       d->skip_this = pb->skip_this;
-      d->shadowed = pb->shadowed;
+      d->shadowed = pb->shadowed || pb->edit_obstructed;
     }
 
   /* The caller of this function needs to fill these in. */
@@ -747,6 +738,10 @@ struct file_baton
      in the working copy (replaced, deleted, etc.). */
   svn_boolean_t shadowed;
 
+  /* Set on a node when the existing node is obstructed, and the edit operation
+     continues as semi-shadowed update */
+  svn_boolean_t edit_obstructed;
+
   /* The (new) changed_* information, cached to avoid retrieving it later */
   svn_revnum_t changed_rev;
   apr_time_t changed_date;
@@ -843,7 +838,7 @@ make_file_baton(struct file_baton **f_p,
   f->obstruction_found = FALSE;
   f->add_existed       = FALSE;
   f->skip_this         = pb->skip_this;
-  f->shadowed          = pb->shadowed;
+  f->shadowed          = pb->shadowed || pb->edit_obstructed;
   f->dir_baton         = pb;
   f->changed_rev       = SVN_INVALID_REVNUM;
 
@@ -856,6 +851,9 @@ make_file_baton(struct file_baton **f_p,
 
 /* Complete a conflict skel by describing the update.
  *
+ * LOCAL_KIND is the node kind of the tree conflict victim in the
+ * working copy.
+ *
  * All temporary allocations are be made in SCRATCH_POOL, while allocations
  * needed for the returned conflict struct are made in RESULT_POOL.
  */
@@ -865,11 +863,15 @@ complete_conflict(svn_skel_t *conflict,
                   const char *local_abspath,
                   const char *old_repos_relpath,
                   svn_revnum_t old_revision,
-                  svn_node_kind_t kind,
-                  apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+                  const char *new_repos_relpath,
+                  svn_node_kind_t local_kind,
+                  svn_node_kind_t target_kind,
+                  apr_pool_t *result_pool,
+                  apr_pool_t *scratch_pool)
 {
   const struct edit_baton *eb = pb->edit_baton;
-  svn_wc_conflict_version_t *src_left_version;
+  svn_wc_conflict_version_t *original_version;
+  svn_wc_conflict_version_t *target_version;
   svn_boolean_t is_complete;
 
   if (!conflict)
@@ -881,23 +883,34 @@ complete_conflict(svn_skel_t *conflict,
     return SVN_NO_ERROR; /* Already completed */
 
   if (old_repos_relpath)
-    src_left_version = svn_wc_conflict_version_create2(eb->repos_root,
+    original_version = svn_wc_conflict_version_create2(eb->repos_root,
                                                        eb->repos_uuid,
                                                        old_repos_relpath,
                                                        old_revision,
-                                                       kind,
+                                                       local_kind,
                                                        result_pool);
   else
-    src_left_version = NULL;
+    original_version = NULL;
 
+  if (new_repos_relpath)
+    target_version = svn_wc_conflict_version_create2(eb->repos_root,
+                                                        eb->repos_uuid,
+                                                        new_repos_relpath,
+                                                        *eb->target_revision,
+                                                        target_kind,
+                                                        result_pool);
+  else
+    target_version = NULL;
 
   if (eb->switch_relpath)
     SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict,
-                                                src_left_version,
+                                                original_version,
+                                                target_version,
                                                 result_pool, scratch_pool));
   else
     SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict,
-                                                src_left_version,
+                                                original_version,
+                                                target_version,
                                                 result_pool, scratch_pool));
 
   return SVN_NO_ERROR;
@@ -922,8 +935,10 @@ mark_directory_edited(struct dir_baton *
       /* We have a (delayed) tree conflict to install */
 
       SVN_ERR(complete_conflict(db->edit_conflict, db->parent_baton,
-                                db->local_abspath, db->old_repos_relpath,
-                                db->old_revision, svn_node_dir,
+                                db->local_abspath,
+                                db->old_repos_relpath, db->old_revision,
+                                db->new_relpath,
+                                svn_node_dir, svn_node_dir,
                                 db->pool, scratch_pool));
       SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db,
                                           db->local_abspath,
@@ -956,7 +971,8 @@ mark_file_edited(struct file_baton *fb, 
 
       SVN_ERR(complete_conflict(fb->edit_conflict, fb->dir_baton,
                                 fb->local_abspath, fb->old_repos_relpath,
-                                fb->old_revision, svn_node_file,
+                                fb->old_revision, fb->new_relpath,
+                                svn_node_file, svn_node_file,
                                 fb->pool, scratch_pool));
 
       SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db,
@@ -1159,6 +1175,8 @@ open_root(void *edit_baton,
   svn_boolean_t already_conflicted;
   svn_error_t *err;
   svn_wc__db_status_t status;
+  svn_kind_t kind;
+  svn_boolean_t have_work;
 
   /* Note that something interesting is actually happening in this
      edit run. */
@@ -1191,19 +1209,48 @@ open_root(void *edit_baton,
       return SVN_NO_ERROR;
     }
 
+
+  SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision,
+                               &db->old_repos_relpath, NULL, NULL,
+                               &db->changed_rev, &db->changed_date,
+                               &db->changed_author, &db->ambient_depth,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, &have_work,
+                               eb->db, db->local_abspath,
+                               db->pool, pool));
+
+  if (have_work)
+    db->shadowed = TRUE; /* Needed for the close_directory() on the root, to
+                            make sure it doesn't use the ACTUAL tree */
+
   if (*eb->target_basename == '\0')
     {
+      svn_wc__db_status_t base_status;
       /* For an update with a NULL target, this is equivalent to open_dir(): */
 
-      /* Read the depth from the entry. */
-      SVN_ERR(svn_wc__db_base_get_info(&status, NULL, &db->old_revision,
-                                       &db->old_repos_relpath, NULL, NULL,
-                                       &db->changed_rev, &db->changed_date,
-                                       &db->changed_author, &db->ambient_depth,
-                                       NULL, NULL, NULL, NULL, NULL,
-                                       eb->db, db->local_abspath,
-                                       db->pool, pool));
-      db->was_incomplete = (status == svn_wc__db_status_incomplete);
+      if (! have_work)
+        base_status = status;
+      else
+        {
+          SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
+                                           &db->old_repos_relpath, NULL, NULL,
+                                           &db->changed_rev, &db->changed_date,
+                                           &db->changed_author, &db->ambient_depth,
+                                           NULL, NULL, NULL, NULL, NULL, NULL,
+                                           eb->db, db->local_abspath,
+                                           db->pool, pool));
+        }
+      db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
+
+      /* ### TODO: Add some tree conflict and obstruction detection, etc. like
+                   open_directory() does.
+                   (or find a way to reuse that code here)
+
+         ### BH 2013: I don't think we need all of the detection here, as the
+                      user explicitly asked to update this node. So we don't
+                      have to tell that it is a local replacement/delete.
+       */
 
       SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
                                                         db->local_abspath,
@@ -1224,8 +1271,6 @@ typedef struct modcheck_baton_t {
   svn_wc__db_t *db;         /* wc_db to access nodes */
   svn_boolean_t found_mod;  /* whether a modification has been found */
   svn_boolean_t found_not_delete;  /* Found a not-delete modification */
-  svn_boolean_t is_copy; /* check for post-copy modifications only */
-  const char *modcheck_root_abspath;
 } modcheck_baton_t;
 
 /* An implementation of svn_wc_status_func4_t. */
@@ -1253,30 +1298,13 @@ modcheck_callback(void *baton,
 
       case svn_wc_status_missing:
       case svn_wc_status_obstructed:
-        if (status->prop_status != svn_wc_status_modified)
-          break;
-
         mb->found_mod = TRUE;
         mb->found_not_delete = TRUE;
         /* Exit from the status walker: We know what we want to know */
         return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
 
-      case svn_wc_status_added:
-        /* Ignore the copy status if we expect a copy, but don't ignore
-           other changes */
-        if (!mb->is_copy
-            || status->text_status == svn_wc_status_modified
-            || status->prop_status == svn_wc_status_modified
-            || strcmp(mb->modcheck_root_abspath, local_abspath) != 0)
-          {
-            mb->found_mod = TRUE;
-            mb->found_not_delete = TRUE;
-            /* Exit from the status walker: We know what we want to know */
-            return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
-          }
-        break;
-
       default:
+      case svn_wc_status_added:
       case svn_wc_status_replaced:
       case svn_wc_status_modified:
         mb->found_mod = TRUE;
@@ -1293,25 +1321,20 @@ modcheck_callback(void *baton,
  * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED
  * is set to true and all the local modifications were deletes then set
  * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise.  LOCAL_ABSPATH
- * may be a file or a directory. If IS_COPY is TRUE, LOCAL_ABSPATH is a
- * copied (or moved) node, and nodes at or within LOCAL_ABSPATH are only
- * considered modified if they were modified post-copy. */
-static svn_error_t *
-node_has_local_mods(svn_boolean_t *modified,
-                    svn_boolean_t *all_edits_are_deletes,
-                    svn_boolean_t is_copy,
-                    svn_wc__db_t *db,
-                    const char *local_abspath,
-                    svn_cancel_func_t cancel_func,
-                    void *cancel_baton,
-                    apr_pool_t *scratch_pool)
+ * may be a file or a directory. */
+svn_error_t *
+svn_wc__node_has_local_mods(svn_boolean_t *modified,
+                            svn_boolean_t *all_edits_are_deletes,
+                            svn_wc__db_t *db,
+                            const char *local_abspath,
+                            svn_cancel_func_t cancel_func,
+                            void *cancel_baton,
+                            apr_pool_t *scratch_pool)
 {
-  modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE, FALSE };
+  modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE };
   svn_error_t *err;
 
   modcheck_baton.db = db;
-  modcheck_baton.is_copy = is_copy;
-  modcheck_baton.modcheck_root_abspath = local_abspath;
 
   /* Walk the WC tree for status with depth infinity, looking for any local
    * modifications. If it's a "sparse" directory, that's OK: there can be
@@ -1330,7 +1353,8 @@ node_has_local_mods(svn_boolean_t *modif
     SVN_ERR(err);
 
   *modified = modcheck_baton.found_mod;
-  *all_edits_are_deletes = !modcheck_baton.found_not_delete;
+  *all_edits_are_deletes = (modcheck_baton.found_mod
+                            && !modcheck_baton.found_not_delete);
 
   return SVN_NO_ERROR;
 }
@@ -1345,9 +1369,9 @@ node_has_local_mods(svn_boolean_t *modif
  * The edit baton EB gives information including whether the operation is
  * an update or a switch.
  *
- * WORKING_STATUS and WORKING_KIND are the current node status of LOCAL_ABSPATH
+ * WORKING_STATUS is the current node status of LOCAL_ABSPATH
  * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
- * for this node.
+ * for this node. In that case the on disk type is compared to EXPECTED_KIND.
  *
  * 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,
@@ -1361,8 +1385,8 @@ check_tree_conflict(svn_skel_t **pconfli
                     struct edit_baton *eb,
                     const char *local_abspath,
                     svn_wc__db_status_t working_status,
-                    svn_kind_t working_kind,
                     svn_boolean_t exists_in_repos,
+                    svn_node_kind_t expected_kind,
                     svn_wc_conflict_action_t action,
                     apr_pool_t *result_pool,
                     apr_pool_t *scratch_pool)
@@ -1370,6 +1394,7 @@ check_tree_conflict(svn_skel_t **pconfli
   svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
   svn_boolean_t modified = FALSE;
   svn_boolean_t all_mods_are_deletes = FALSE;
+  const char *moved_away_op_root_abspath = NULL;
 
   *pconflict = NULL;
 
@@ -1407,21 +1432,26 @@ check_tree_conflict(svn_skel_t **pconfli
           }
         else
           {
-            /* The node is locally replaced. */
-            reason = svn_wc_conflict_reason_replaced;
+            /* The node is locally replaced but could also be moved-away. */
+            SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL,
+                                             &moved_away_op_root_abspath,
+                                             eb->db, local_abspath,
+                                             scratch_pool, scratch_pool));
+            if (moved_away_op_root_abspath)
+              reason = svn_wc_conflict_reason_moved_away;
+            else
+              reason = svn_wc_conflict_reason_replaced;
           }
         break;
 
 
       case svn_wc__db_status_deleted:
         {
-          const char *moved_to_abspath;
-
-          SVN_ERR(svn_wc__db_scan_deletion(NULL, &moved_to_abspath,
-                                           NULL, NULL, eb->db,
-                                           local_abspath,
+          SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL,
+                                           &moved_away_op_root_abspath,
+                                           eb->db, local_abspath,
                                            scratch_pool, scratch_pool));
-          if (moved_to_abspath)
+          if (moved_away_op_root_abspath)
             reason = svn_wc_conflict_reason_moved_away;
           else
             reason = svn_wc_conflict_reason_deleted;
@@ -1437,10 +1467,29 @@ check_tree_conflict(svn_skel_t **pconfli
          * missing information (hopefully). */
       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
-           * tree-conflict. (It's possibly a text- or prop-conflict,
-           * but we don't handle those here.) */
-          return SVN_NO_ERROR;
+          {
+            /* An edit onto a local edit or onto *no* local changes is no
+             * tree-conflict. (It's possibly a text- or prop-conflict,
+             * but we don't handle those here.) 
+             *
+             * Except when there is a local obstruction
+             */
+            if (exists_in_repos)
+              {
+                svn_node_kind_t disk_kind;
+
+                SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
+                                          scratch_pool));
+
+                if (disk_kind != expected_kind && disk_kind != svn_node_none)
+                  {
+                    reason = svn_wc_conflict_reason_obstructed;
+                    break;
+                  }
+
+              }
+            return SVN_NO_ERROR;
+          }
 
         /* Replace is handled as delete and then specifically in
            add_directory() and add_file(), so we only expect deletes here */
@@ -1454,10 +1503,10 @@ check_tree_conflict(svn_skel_t **pconfli
          * not visit the subdirectories of a directory that it wants to delete.
          * Therefore, we need to start a separate crawl here. */
 
-        SVN_ERR(node_has_local_mods(&modified, &all_mods_are_deletes, FALSE,
-                                    eb->db, local_abspath,
-                                    eb->cancel_func, eb->cancel_baton,
-                                    scratch_pool));
+        SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_mods_are_deletes,
+                                            eb->db, local_abspath,
+                                            eb->cancel_func, eb->cancel_baton,
+                                            scratch_pool));
 
         if (modified)
           {
@@ -1493,6 +1542,7 @@ check_tree_conflict(svn_skel_t **pconfli
   /* Sanity checks. Note that if there was no action on the node, this function
    * would not have been called in the first place.*/
   if (reason == svn_wc_conflict_reason_edited
+      || reason == svn_wc_conflict_reason_obstructed
       || reason == svn_wc_conflict_reason_deleted
       || reason == svn_wc_conflict_reason_moved_away
       || reason == svn_wc_conflict_reason_replaced)
@@ -1516,6 +1566,7 @@ check_tree_conflict(svn_skel_t **pconfli
                                                   eb->db, local_abspath,
                                                   reason,
                                                   action,
+                                                  moved_away_op_root_abspath,
                                                   result_pool, scratch_pool));
 
   return SVN_NO_ERROR;
@@ -1609,7 +1660,9 @@ delete_entry(const char *path,
   svn_wc__db_status_t base_status;
   apr_pool_t *scratch_pool;
   svn_boolean_t deleting_target;
+  svn_boolean_t deleting_switched;
   svn_boolean_t keep_as_working = FALSE;
+  svn_boolean_t queue_deletes = TRUE;
 
   if (pb->skip_this)
     return SVN_NO_ERROR;
@@ -1660,10 +1713,22 @@ delete_entry(const char *path,
     SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision,
                                      &repos_relpath,
                                      NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                     NULL, NULL, NULL, NULL,
+                                     NULL, NULL, NULL, NULL, NULL,
                                      eb->db, local_abspath,
                                      scratch_pool, scratch_pool));
 
+  if (pb->old_repos_relpath && repos_relpath)
+    {
+      const char *expected_name;
+
+      expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath,
+                                                repos_relpath);
+
+      deleting_switched = (!expected_name || strcmp(expected_name, base) != 0);
+    }
+  else
+    deleting_switched = FALSE;
+
   /* Is this path a conflict victim? */
   if (conflicted)
     SVN_ERR(node_already_conflicted(&conflicted, eb->db, local_abspath,
@@ -1690,7 +1755,9 @@ delete_entry(const char *path,
       || base_status == svn_wc__db_status_server_excluded)
     {
       SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
-                                     FALSE, SVN_INVALID_REVNUM,
+                                     FALSE /* keep_as_working */,
+                                     FALSE /* queue_deletes */,
+                                     SVN_INVALID_REVNUM /* not_present_rev */,
                                      NULL, NULL,
                                      scratch_pool));
 
@@ -1709,13 +1776,18 @@ delete_entry(const char *path,
 
   /* Check for conflicts only when we haven't already recorded
    * a tree-conflict on a parent node. */
-  if (!pb->shadowed)
+  if (!pb->shadowed && !pb->edit_obstructed)
     {
       SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
-                                  status, kind, TRUE,
+                                  status, TRUE,
+                                  (kind == svn_kind_dir)
+                                        ? svn_node_dir
+                                        : svn_node_file,
                                   svn_wc_conflict_action_delete,
                                   pb->pool, scratch_pool));
     }
+  else
+    queue_deletes = FALSE; /* There is no in-wc representation */
 
   if (tree_conflict != NULL)
     {
@@ -1730,12 +1802,13 @@ delete_entry(const char *path,
       apr_hash_set(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
                    APR_HASH_KEY_STRING, tree_conflict);
 
-      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
+      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
                                                   eb->db, local_abspath,
                                                   tree_conflict,
                                                   scratch_pool, scratch_pool));
 
-      if (reason == svn_wc_conflict_reason_edited)
+      if (reason == svn_wc_conflict_reason_edited
+          || reason == svn_wc_conflict_reason_obstructed)
         {
           /* The item exists locally and has some sort of local mod.
            * It no longer exists in the repository at its target URL@REV.
@@ -1763,10 +1836,11 @@ delete_entry(const char *path,
     }
 
   SVN_ERR(complete_conflict(tree_conflict, pb, local_abspath, repos_relpath,
-                            old_revision,
+                            old_revision, NULL,
                             (kind == svn_kind_dir)
                                 ? svn_node_dir
                                 : svn_node_file,
+                            svn_node_none,
                             pb->pool, scratch_pool));
 
   /* Issue a wq operation to delete the BASE_NODE data and to delete actual
@@ -1777,11 +1851,12 @@ delete_entry(const char *path,
      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
      accurate reports about itself in the future. */
-  if (! deleting_target)
+  if (! deleting_target && ! deleting_switched)
     {
       /* Delete, and do not leave a not-present node.  */
       SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
-                                     keep_as_working, SVN_INVALID_REVNUM,
+                                     keep_as_working, queue_deletes,
+                                     SVN_INVALID_REVNUM /* not_present_rev */,
                                      tree_conflict, NULL,
                                      scratch_pool));
     }
@@ -1789,10 +1864,17 @@ delete_entry(const char *path,
     {
       /* Delete, leaving a not-present node.  */
       SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
-                                     keep_as_working, *eb->target_revision,
+                                     keep_as_working, queue_deletes,
+                                     *eb->target_revision,
                                      tree_conflict, NULL,
                                      scratch_pool));
-      eb->target_deleted = TRUE;
+      if (deleting_target)
+        eb->target_deleted = TRUE;
+      else
+        {
+          /* Don't remove the not-present marker at the final bump */
+          SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
+        }
     }
 
   SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
@@ -1808,7 +1890,7 @@ delete_entry(const char *path,
       svn_wc_notify_action_t action = svn_wc_notify_update_delete;
       svn_node_kind_t node_kind;
 
-      if (pb->shadowed)
+      if (pb->shadowed || pb->edit_obstructed)
         action = svn_wc_notify_update_shadowed_delete;
 
       if (kind == svn_kind_dir)
@@ -1976,7 +2058,7 @@ add_directory(const char *path,
           /* ### Should store the conflict in DB to allow reinstalling
              ### with theoretically more data in close_directory() */
 
-          SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
+          SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
                                                       eb->db,
                                                       db->local_abspath,
                                                       tree_conflict,
@@ -1988,6 +2070,7 @@ add_directory(const char *path,
                                         tree_conflict,
                                         eb->db, db->local_abspath,
                                         reason, svn_wc_conflict_action_replace,
+                                        NULL,
                                         db->pool, db->pool));
 
           /* And now stop checking for conflicts here and just perform
@@ -2083,7 +2166,7 @@ add_directory(const char *path,
         {
           SVN_ERR(check_tree_conflict(&tree_conflict, eb,
                                       db->local_abspath,
-                                      status, wc_kind, FALSE,
+                                      status, FALSE, svn_node_none,
                                       svn_wc_conflict_action_add,
                                       pool, pool));
         }
@@ -2112,15 +2195,19 @@ add_directory(const char *path,
                                         tree_conflict,
                                         eb->db, db->local_abspath,
                                         svn_wc_conflict_reason_unversioned,
-                                        svn_wc_conflict_action_add,
+                                        svn_wc_conflict_action_add, NULL,
                                         db->pool, pool));
           db->edit_conflict = tree_conflict;
         }
     }
 
-  SVN_ERR(complete_conflict(tree_conflict, pb, db->local_abspath,
-                            db->old_repos_relpath, db->old_revision,
-                            svn_node_dir, db->pool, pool));
+  if (tree_conflict)
+    SVN_ERR(complete_conflict(tree_conflict, pb, db->local_abspath,
+                              db->old_repos_relpath, db->old_revision,
+                              db->new_relpath,
+                              svn__node_kind_from_kind(wc_kind),
+                              svn_node_dir,
+                              db->pool, pool));
 
   SVN_ERR(svn_wc__db_base_add_incomplete_directory(
                                      eb->db, db->local_abspath,
@@ -2149,7 +2236,7 @@ add_directory(const char *path,
 
 
   /* If this add was obstructed by dir scheduled for addition without
-     history let close_file() handle the notification because there
+     history let close_directory() handle the notification because there
      might be properties to deal with.  If PATH was added inside a locally
      deleted tree, then suppress notification, a tree conflict was already
      issued. */
@@ -2159,7 +2246,7 @@ add_directory(const char *path,
 
       if (db->shadowed)
         action = svn_wc_notify_update_shadowed_add;
-      else if (db->obstruction_found)
+      else if (db->obstruction_found || db->add_existed)
         action = svn_wc_notify_exists;
       else
         action = svn_wc_notify_update_add;
@@ -2236,7 +2323,7 @@ open_directory(const char *path,
                                      &db->old_repos_relpath, NULL, NULL,
                                      &db->changed_rev, &db->changed_date,
                                      &db->changed_author, &db->ambient_depth,
-                                     NULL, NULL, NULL, NULL, NULL,
+                                     NULL, NULL, NULL, NULL, NULL, NULL,
                                      eb->db, db->local_abspath,
                                      db->pool, pool));
 
@@ -2266,7 +2353,7 @@ open_directory(const char *path,
    * a tree-conflict on a parent node. */
   if (!db->shadowed)
     SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
-                                status, wc_kind, TRUE,
+                                status, TRUE, svn_node_dir,
                                 svn_wc_conflict_action_edit,
                                 db->pool, pool));
 
@@ -2277,16 +2364,20 @@ open_directory(const char *path,
       db->edit_conflict = tree_conflict;
       /* Other modifications wouldn't be a tree conflict */
 
-      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
+      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
                                                   eb->db, db->local_abspath,
                                                   tree_conflict,
                                                   db->pool, db->pool));
       SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
                      || reason == svn_wc_conflict_reason_moved_away
-                     || reason == svn_wc_conflict_reason_replaced);
+                     || reason == svn_wc_conflict_reason_replaced
+                     || reason == svn_wc_conflict_reason_obstructed);
 
       /* Continue updating BASE */
-      db->shadowed = TRUE;
+      if (reason == svn_wc_conflict_reason_obstructed)
+        db->edit_obstructed = TRUE;
+      else
+        db->shadowed = TRUE;
     }
 
   /* Mark directory as being at target_revision and URL, but incomplete. */
@@ -2394,9 +2485,9 @@ close_directory(void *dir_baton,
   if (db->add_existed)
     {
       /* This node already exists. Grab the current pristine properties. */
-      SVN_ERR(svn_wc__get_pristine_props(&base_props,
-                                         eb->db, db->local_abspath,
-                                         scratch_pool, scratch_pool));
+      SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
+                                             eb->db, db->local_abspath,
+                                             scratch_pool, scratch_pool));
     }
   else if (!db->adding_dir)
     {
@@ -2494,23 +2585,18 @@ close_directory(void *dir_baton,
             actual_props = base_props;
         }
 
-      /* Merge pending properties into temporary files (ignoring
-         conflicts). */
+      /* Merge pending properties. */
+      new_base_props = svn_prop__patch(base_props, regular_prop_changes,
+                                       db->pool);
       SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
                                     &prop_state,
-                                    &new_base_props,
                                     &new_actual_props,
                                     eb->db,
                                     db->local_abspath,
-                                    svn_kind_dir,
                                     NULL /* use baseprops */,
                                     base_props,
                                     actual_props,
                                     regular_prop_changes,
-                                    TRUE /* base_merge */,
-                                    FALSE /* dry_run */,
-                                    eb->cancel_func,
-                                    eb->cancel_baton,
                                     db->pool,
                                     scratch_pool),
                 _("Couldn't do property merge"));
@@ -2563,7 +2649,7 @@ close_directory(void *dir_baton,
             /* ### We just check if there is some node in BASE at this path */
             err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
                                            NULL, NULL, NULL, NULL, NULL, NULL,
-                                           NULL, NULL, NULL, NULL,
+                                           NULL, NULL, NULL, NULL, NULL,
                                            eb->db, child_abspath,
                                            iterpool, iterpool);
 
@@ -2696,7 +2782,8 @@ close_directory(void *dir_baton,
                                     db->local_abspath,
                                     db->old_repos_relpath,
                                     db->old_revision,
-                                    svn_node_dir,
+                                    db->new_relpath,
+                                    svn_node_dir, svn_node_dir,
                                     db->pool, scratch_pool));
 
           SVN_ERR(svn_wc__conflict_create_markers(&work_item,
@@ -2757,6 +2844,8 @@ close_directory(void *dir_baton,
                                              NULL /* merge_options */,
                                              eb->conflict_func,
                                              eb->conflict_baton,
+                                             eb->cancel_func,
+                                             eb->conflict_baton,
                                              scratch_pool));
 
   /* Notify of any prop changes on this directory -- but do nothing if
@@ -2769,7 +2858,7 @@ close_directory(void *dir_baton,
       svn_wc_notify_t *notify;
       svn_wc_notify_action_t action;
 
-      if (db->shadowed)
+      if (db->shadowed || db->edit_obstructed)
         action = svn_wc_notify_update_shadowed_update;
       else if (db->obstruction_found || db->add_existed)
         action = svn_wc_notify_exists;
@@ -3055,7 +3144,7 @@ add_file(const char *path,
           /* ### Should store the conflict in DB to allow reinstalling
              ### with theoretically more data in close_directory() */
 
-          SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
+          SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
                                                       eb->db,
                                                       fb->local_abspath,
                                                       tree_conflict,
@@ -3067,6 +3156,7 @@ add_file(const char *path,
                                         tree_conflict,
                                         eb->db, fb->local_abspath,
                                         reason, svn_wc_conflict_action_replace,
+                                        NULL,
                                         fb->pool, fb->pool));
 
           /* And now stop checking for conflicts here and just perform
@@ -3158,7 +3248,7 @@ add_file(const char *path,
         {
           SVN_ERR(check_tree_conflict(&tree_conflict, eb,
                                       fb->local_abspath,
-                                      status, wc_kind, FALSE,
+                                      status, FALSE, svn_node_none,
                                       svn_wc_conflict_action_add,
                                       scratch_pool, scratch_pool));
         }
@@ -3189,6 +3279,7 @@ add_file(const char *path,
                                         eb->db, fb->local_abspath,
                                         svn_wc_conflict_reason_unversioned,
                                         svn_wc_conflict_action_add,
+                                        NULL,
                                         fb->pool, scratch_pool));
         }
     }
@@ -3211,6 +3302,8 @@ add_file(const char *path,
                                 fb->local_abspath,
                                 fb->old_repos_relpath,
                                 fb->old_revision,
+                                fb->new_relpath,
+                                svn__node_kind_from_kind(wc_kind),
                                 svn_node_file,
                                 fb->pool, scratch_pool));
 
@@ -3298,7 +3391,7 @@ open_file(const char *path,
                                      &fb->changed_rev, &fb->changed_date,
                                      &fb->changed_author, NULL,
                                      &fb->original_checksum, NULL, NULL,
-                                     NULL, NULL,
+                                     NULL, NULL, NULL,
                                      eb->db, fb->local_abspath,
                                      fb->pool, scratch_pool));
 
@@ -3321,13 +3414,11 @@ open_file(const char *path,
       return SVN_NO_ERROR;
     }
 
-  fb->shadowed = pb->shadowed;
-
   /* Check for conflicts only when we haven't already recorded
    * a tree-conflict on a parent node. */
   if (!fb->shadowed)
     SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
-                                status, wc_kind, TRUE,
+                                status, TRUE, svn_node_file,
                                 svn_wc_conflict_action_edit,
                                 fb->pool, scratch_pool));
 
@@ -3338,16 +3429,20 @@ open_file(const char *path,
       fb->edit_conflict = tree_conflict;
       /* Other modifications wouldn't be a tree conflict */
 
-      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
+      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
                                                   eb->db, fb->local_abspath,
                                                   tree_conflict,
-                                                  fb->pool, fb->pool));
+                                                  scratch_pool, scratch_pool));
       SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
                      || reason == svn_wc_conflict_reason_moved_away
-                     || reason == svn_wc_conflict_reason_replaced);
+                     || reason == svn_wc_conflict_reason_replaced
+                     || reason == svn_wc_conflict_reason_obstructed);
 
       /* Continue updating BASE */
-      fb->shadowed = TRUE;
+      if (reason == svn_wc_conflict_reason_obstructed)
+        fb->edit_obstructed = TRUE;
+      else
+        fb->shadowed = TRUE;
     }
 
   svn_pool_destroy(scratch_pool);
@@ -3570,11 +3665,13 @@ change_file_prop(void *file_baton,
                                      eb->db, fb->local_abspath,
                                      svn_wc_conflict_reason_edited,
                                      svn_wc_conflict_action_replace,
+                                     NULL,
                                      fb->pool, scratch_pool));
 
           SVN_ERR(complete_conflict(fb->edit_conflict, fb->dir_baton,
                                     fb->local_abspath, fb->old_repos_relpath,
-                                    fb->old_revision, svn_node_file,
+                                    fb->old_revision, fb->new_relpath,
+                                    svn_node_file, svn_node_file,
                                     fb->pool, scratch_pool));
 
           /* Create a copy of the existing (pre update) BASE node in WORKING,
@@ -3606,18 +3703,18 @@ change_file_prop(void *file_baton,
    identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
    the intermediate files.
 
-   The rest of the arguments are passed to svn_wc__internal_merge.
+   The rest of the arguments are passed to svn_wc__internal_merge().
  */
 svn_error_t *
 svn_wc__perform_file_merge(svn_skel_t **work_items,
                            svn_skel_t **conflict_skel,
-                           enum svn_wc_merge_outcome_t *merge_outcome,
+                           svn_boolean_t *found_conflict,
                            svn_wc__db_t *db,
                            const char *local_abspath,
                            const char *wri_abspath,
                            const svn_checksum_t *new_checksum,
                            const svn_checksum_t *original_checksum,
-                           apr_hash_t *actual_props,
+                           apr_hash_t *old_actual_props,
                            const apr_array_header_t *ext_patterns,
                            svn_revnum_t old_revision,
                            svn_revnum_t target_revision,
@@ -3629,13 +3726,14 @@ svn_wc__perform_file_merge(svn_skel_t **
                            apr_pool_t *scratch_pool)
 {
   /* Actual file exists and has local mods:
-     Now we need to let loose svn_wc__merge_internal() to merge
+     Now we need to let loose svn_wc__internal_merge() to merge
      the textual changes into the working file. */
   const char *oldrev_str, *newrev_str, *mine_str;
   const char *merge_left;
   svn_boolean_t delete_left = FALSE;
   const char *path_ext = "";
   const char *new_text_base_tmp_abspath;
+  enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
   svn_skel_t *work_item;
 
   *work_items = NULL;
@@ -3690,20 +3788,21 @@ svn_wc__perform_file_merge(svn_skel_t **
      Remember that this function wants full paths! */
   SVN_ERR(svn_wc__internal_merge(&work_item,
                                  conflict_skel,
-                                 merge_outcome,
+                                 &merge_outcome,
                                  db,
                                  merge_left,
                                  new_text_base_tmp_abspath,
                                  local_abspath,
                                  wri_abspath,
                                  oldrev_str, newrev_str, mine_str,
-                                 actual_props,
+                                 old_actual_props,
                                  FALSE /* dry_run */,
                                  diff3_cmd, NULL, propchanges,
                                  cancel_func, cancel_baton,
                                  result_pool, scratch_pool));
 
   *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
+  *found_conflict = (merge_outcome == svn_wc_merge_conflict);
 
   /* If we created a temporary left merge file, get rid of it. */
   if (delete_left)
@@ -3755,9 +3854,11 @@ merge_file(svn_skel_t **work_items,
   struct edit_baton *eb = fb->edit_baton;
   struct dir_baton *pb = fb->dir_baton;
   svn_boolean_t is_locally_modified;
-  enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
+  svn_boolean_t found_text_conflict = FALSE;
 
-  SVN_ERR_ASSERT(! fb->shadowed && !fb->obstruction_found);
+  SVN_ERR_ASSERT(! fb->shadowed
+                 && ! fb->obstruction_found
+                 && ! fb->edit_obstructed);
 
   /*
      When this function is called on file F, we assume the following
@@ -3843,7 +3944,7 @@ merge_file(svn_skel_t **work_items,
          the textual changes into the working file. */
       SVN_ERR(svn_wc__perform_file_merge(work_items,
                                          conflict_skel,
-                                         &merge_outcome,
+                                         &found_text_conflict,
                                          eb->db,
                                          fb->local_abspath,
                                          pb->local_abspath,
@@ -3926,14 +4027,7 @@ merge_file(svn_skel_t **work_items,
 
   /* Set the returned content state. */
 
-  /* This is kind of interesting.  Even if no new text was
-     installed (i.e., NEW_TEXT_BASE_ABSPATH was null), we could still
-     report a pre-existing conflict state.  Say a file, already
-     in a state of textual conflict, receives prop mods during an
-     update.  Then we'll notify that it has text conflicts.  This
-     seems okay to me.  I guess.  I dunno.  You? */
-
-  if (merge_outcome == svn_wc_merge_conflict)
+  if (found_text_conflict)
     *content_state = svn_wc_notify_state_conflicted;
   else if (fb->new_text_base_sha1_checksum)
     {
@@ -4043,11 +4137,17 @@ close_file(void *file_baton,
            and we should likewise remove our cached copy of it.  */
         if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
           {
-            SVN_ERR_ASSERT(prop->value == NULL);
-            SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath,
-                                           scratch_pool));
+            /* If we lose the lock, but not because we are switching to
+               another url, remove the state lock from the wc */
+            if (! eb->switch_relpath 
+                || strcmp(fb->new_relpath, fb->old_repos_relpath) == 0)
+              {
+                SVN_ERR_ASSERT(prop->value == NULL);
+                SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath,
+                                               scratch_pool));
 
-            lock_state = svn_wc_notify_lock_state_unlocked;
+                lock_state = svn_wc_notify_lock_state_unlocked;
+              }
             break;
           }
       }
@@ -4077,9 +4177,9 @@ close_file(void *file_baton,
   if (fb->add_existed)
     {
       /* This node already exists. Grab the current pristine properties. */
-      SVN_ERR(svn_wc__get_pristine_props(&current_base_props,
-                                         eb->db, fb->local_abspath,
-                                         scratch_pool, scratch_pool));
+      SVN_ERR(svn_wc__db_read_pristine_props(&current_base_props,
+                                             eb->db, fb->local_abspath,
+                                             scratch_pool, scratch_pool));
       current_actual_props = local_actual_props;
     }
   else if (!fb->adding_file)
@@ -4111,27 +4211,24 @@ close_file(void *file_baton,
       /* This will merge the old and new props into a new prop db, and
          write <cp> commands to the logfile to install the merged
          props.  */
+      new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
+                                       scratch_pool);
       SVN_ERR(svn_wc__merge_props(&conflict_skel,
                                   &prop_state,
-                                  &new_base_props,
                                   &new_actual_props,
                                   eb->db,
                                   fb->local_abspath,
-                                  svn_kind_file,
                                   NULL /* server_baseprops (update, not merge)  */,
                                   current_base_props,
                                   current_actual_props,
                                   regular_prop_changes, /* propchanges */
-                                  TRUE /* base_merge */,
-                                  FALSE /* dry_run */,
-                                  eb->cancel_func, eb->cancel_baton,
                                   scratch_pool,
                                   scratch_pool));
       /* We will ALWAYS have properties to save (after a not-dry-run merge). */
       SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
 
       /* Merge the text. This will queue some additional work.  */
-      if (!fb->obstruction_found)
+      if (!fb->obstruction_found && !fb->edit_obstructed)
         {
           svn_error_t *err;
           err = merge_file(&work_item, &conflict_skel,
@@ -4246,20 +4343,17 @@ close_file(void *file_baton,
 
       /* Store the incoming props (sent as propchanges) in new_base_props
          and create a set of new actual props to use for notifications */
+      new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
+                                       scratch_pool);
       SVN_ERR(svn_wc__merge_props(&conflict_skel,
                                   &prop_state,
-                                  &new_base_props,
                                   &new_actual_props,
                                   eb->db,
                                   fb->local_abspath,
-                                  svn_kind_file,
                                   NULL /* server_baseprops (not merging) */,
                                   current_base_props /* pristine_props */,
                                   fake_actual_props /* actual_props */,
                                   regular_prop_changes, /* propchanges */
-                                  TRUE /* base_merge */,
-                                  FALSE /* dry_run */,
-                                  eb->cancel_func, eb->cancel_baton,
                                   scratch_pool,
                                   scratch_pool));
 
@@ -4288,7 +4382,8 @@ close_file(void *file_baton,
                                 fb->local_abspath,
                                 fb->old_repos_relpath,
                                 fb->old_revision,
-                                svn_node_file,
+                                fb->new_relpath,
+                                svn_node_file, svn_node_file,
                                 fb->pool, scratch_pool));
 
       SVN_ERR(svn_wc__conflict_create_markers(&work_item,
@@ -4347,6 +4442,8 @@ close_file(void *file_baton,
                                              NULL /* merge_options */,
                                              eb->conflict_func,
                                              eb->conflict_baton,
+                                             eb->cancel_func,
+                                             eb->cancel_baton,
                                              scratch_pool));
 
   /* Deal with the WORKING tree, based on updates to the BASE tree.  */
@@ -4364,7 +4461,7 @@ close_file(void *file_baton,
 
       if (fb->edited)
         {
-          if (fb->shadowed)
+          if (fb->shadowed || fb->edit_obstructed)
             action = fb->adding_file
                             ? svn_wc_notify_update_shadowed_add
                             : svn_wc_notify_update_shadowed_update;
@@ -4476,7 +4573,7 @@ close_edit(void *edit_baton,
              have to worry about removing it. */
           err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
                                          NULL, NULL, NULL, NULL, NULL, NULL,
-                                         NULL, NULL, NULL,
+                                         NULL, NULL, NULL, NULL,
                                          eb->db, eb->target_abspath,
                                          scratch_pool, scratch_pool);
           if (err)
@@ -4495,7 +4592,9 @@ close_edit(void *edit_baton,
                  If so, we should get rid of this excluded node now. */
 
               SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
-                                             FALSE, SVN_INVALID_REVNUM,
+                                             FALSE /* keep_as_working */,
+                                             FALSE /* queue_deletes */,
+                                             SVN_INVALID_REVNUM,
                                              NULL, NULL, scratch_pool));
             }
         }
@@ -4633,7 +4732,7 @@ make_editor(svn_revnum_t *target_revisio
   eb->ext_patterns             = preserved_exts;
 
   apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
-                            cleanup_edit_baton_child);
+                            apr_pool_cleanup_null);
 
   /* Construct an editor. */
   tree_editor->set_target_revision = set_target_revision;
@@ -4674,7 +4773,7 @@ make_editor(svn_revnum_t *target_revisio
       err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
                                      &dir_repos_relpath, NULL, NULL, NULL,
                                      NULL, NULL, &dir_depth, NULL, NULL, NULL,
-                                     NULL, NULL,
+                                     NULL, NULL, NULL,
                                      db, eb->target_abspath,
                                      scratch_pool, scratch_pool);
 
@@ -4730,7 +4829,7 @@ make_editor(svn_revnum_t *target_revisio
                                                    NULL, &dir_repos_relpath,
                                                    NULL, NULL, NULL, NULL,
                                                    NULL, &dir_depth, NULL,
-                                                   NULL, NULL, NULL,
+                                                   NULL, NULL, NULL, NULL,
                                                    NULL,
                                                    db, child_abspath,
                                                    iterpool, iterpool));
@@ -4904,207 +5003,6 @@ svn_wc__get_switch_editor(const svn_delt
                      result_pool, scratch_pool);
 }
 
-/* ABOUT ANCHOR AND TARGET, AND svn_wc_get_actual_target2()
-
-   THE GOAL
-
-   Note the following actions, where X is the thing we wish to update,
-   P is a directory whose repository URL is the parent of
-   X's repository URL, N is directory whose repository URL is *not*
-   the parent directory of X (including the case where N is not a
-   versioned resource at all):
-
-      1.  `svn up .' from inside X.
-      2.  `svn up ...P/X' from anywhere.
-      3.  `svn up ...N/X' from anywhere.
-
-   For the purposes of the discussion, in the '...N/X' situation, X is
-   said to be a "working copy (WC) root" directory.
-
-   Now consider the four cases for X's type (file/dir) in the working
-   copy vs. the repository:
-
-      A.  dir in working copy, dir in repos.
-      B.  dir in working copy, file in repos.
-      C.  file in working copy, dir in repos.
-      D.  file in working copy, file in repos.
-
-   Here are the results we expect for each combination of the above:
-
-      1A. Successfully update X.
-      1B. Error (you don't want to remove your current working
-          directory out from underneath the application).
-      1C. N/A (you can't be "inside X" if X is a file).
-      1D. N/A (you can't be "inside X" if X is a file).
-
-      2A. Successfully update X.
-      2B. Successfully update X.
-      2C. Successfully update X.
-      2D. Successfully update X.
-
-      3A. Successfully update X.
-      3B. Error (you can't create a versioned file X inside a
-          non-versioned directory).
-      3C. N/A (you can't have a versioned file X in directory that is
-          not its repository parent).
-      3D. N/A (you can't have a versioned file X in directory that is
-          not its repository parent).
-
-   To summarize, case 2 always succeeds, and cases 1 and 3 always fail
-   (or can't occur) *except* when the target is a dir that remains a
-   dir after the update.
-
-   ACCOMPLISHING THE GOAL
-
-   Updates are accomplished by driving an editor, and an editor is
-   "rooted" on a directory.  So, in order to update a file, we need to
-   break off the basename of the file, rooting the editor in that
-   file's parent directory, and then updating only that file, not the
-   other stuff in its parent directory.
-
-   Secondly, we look at the case where we wish to update a directory.
-   This is typically trivial.  However, one problematic case, exists
-   when we wish to update a directory that has been removed from the
-   repository and replaced with a file of the same name.  If we root
-   our edit at the initial directory, there is no editor mechanism for
-   deleting that directory and replacing it with a file (this would be
-   like having an editor now anchored on a file, which is disallowed).
-
-   All that remains is to have a function with the knowledge required
-   to properly decide where to root our editor, and what to act upon
-   with that now-rooted editor.  Given a path to be updated, this
-   function should conditionally split that path into an "anchor" and
-   a "target", where the "anchor" is the directory at which the update
-   editor is rooted (meaning, editor->open_root() is called with
-   this directory in mind), and the "target" is the actual intended
-   subject of the update.
-
-   svn_wc_get_actual_target2() is that function.
-
-   So, what are the conditions?
-
-   Case I: Any time X is '.' (implying it is a directory), we won't
-   lop off a basename.  So we'll root our editor at X, and update all
-   of X.
-
-   Cases II & III: Any time we are trying to update some path ...N/X,
-   we again will not lop off a basename.  We can't root an editor at
-   ...N with X as a target, either because ...N isn't a versioned
-   resource at all (Case II) or because X is X is not a child of ...N
-   in the repository (Case III).  We root at X, and update X.
-
-   Cases IV-???: We lop off a basename when we are updating a
-   path ...P/X, rooting our editor at ...P and updating X, or when X
-   is missing from disk.
-
-   These conditions apply whether X is a file or directory.
-
-   ---
-
-   As it turns out, commits need to have a similar check in place,
-   too, specifically for the case where a single directory is being
-   committed (we have to anchor at that directory's parent in case the
-   directory itself needs to be modified).
-*/
-
-
-svn_error_t *
-svn_wc__check_wc_root(svn_boolean_t *wc_root,
-                      svn_kind_t *kind,
-                      svn_boolean_t *switched,
-                      svn_wc__db_t *db,
-                      const char *local_abspath,
-                      apr_pool_t *scratch_pool)
-{
-  return svn_error_trace(
-            svn_wc__db_is_switched(wc_root, switched, kind,
-                                   db, local_abspath,
-                                   scratch_pool));
-}
-
-svn_error_t *
-svn_wc_check_root(svn_boolean_t *is_wcroot,
-                  svn_boolean_t *is_switched,
-                  svn_kind_t *kind,
-                  svn_wc_context_t *wc_ctx,
-                  const char *local_abspath,
-                  apr_pool_t *scratch_pool)
-{
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
-
-  return svn_error_trace(svn_wc__db_is_switched(is_wcroot,is_switched, kind,
-                                                wc_ctx->db, local_abspath,
-                                                scratch_pool));
-}
-
-svn_error_t*
-svn_wc__strictly_is_wc_root(svn_boolean_t *wc_root,
-                            svn_wc_context_t *wc_ctx,
-                            const char *local_abspath,
-                            apr_pool_t *scratch_pool)
-{
-  return svn_error_trace(svn_wc__db_is_wcroot(wc_root,
-                                              wc_ctx->db,
-                                              local_abspath,
-                                              scratch_pool));
-}
-
-
-svn_error_t *
-svn_wc__get_wc_root(const char **wcroot_abspath,
-                    svn_wc_context_t *wc_ctx,
-                    const char *local_abspath,
-                    apr_pool_t *result_pool,
-                    apr_pool_t *scratch_pool)
-{
-  return svn_wc__db_get_wcroot(wcroot_abspath, wc_ctx->db,
-                               local_abspath, result_pool, scratch_pool);
-}
-
-
-svn_error_t *
-svn_wc_get_actual_target2(const char **anchor,
-                          const char **target,
-                          svn_wc_context_t *wc_ctx,
-                          const char *path,
-                          apr_pool_t *result_pool,
-                          apr_pool_t *scratch_pool)
-{
-  svn_boolean_t is_wc_root, is_switched;
-  svn_kind_t kind;
-  const char *local_abspath;
-  svn_error_t *err;
-
-  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
-
-  err = svn_wc__check_wc_root(&is_wc_root, &kind, &is_switched,
-                              wc_ctx->db, local_abspath,
-                              scratch_pool);
-
-  if (err)
-    {
-      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND &&
-          err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
-        return svn_error_trace(err);
-
-      svn_error_clear(err);
-      is_wc_root = FALSE;
-      is_switched = FALSE;
-    }
-
-  /* If PATH is not a WC root, or if it is a file, lop off a basename. */
-  if (!(is_wc_root || is_switched) || (kind != svn_kind_dir))
-    {
-      svn_dirent_split(anchor, target, path, result_pool);
-    }
-  else
-    {
-      *anchor = apr_pstrdup(result_pool, path);
-      *target = "";
-    }
-
-  return SVN_NO_ERROR;
-}
 
 
 /* ### Note that this function is completely different from the rest of the
@@ -5339,7 +5237,7 @@ svn_wc_add_repos_file4(svn_wc_context_t 
 
     /* If new contents were provided, then we do NOT want to record the
        file information. We assume the new contents do not match the
-       "proper" values for TRANSLATED_SIZE and LAST_MOD_TIME.  */
+       "proper" values for RECORDED_SIZE and RECORDED_TIME.  */
     record_fileinfo = (new_contents == NULL);
 
     /* Install the working copy file (with appropriate translation) from
@@ -5391,3 +5289,90 @@ svn_wc_add_repos_file4(svn_wc_context_t 
                                         cancel_func, cancel_baton,
                                         pool));
 }
+
+svn_error_t *
+svn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
+                               const char *local_abspath,
+                               apr_hash_t *new_original_props,
+                               const char *copyfrom_url,
+                               svn_revnum_t copyfrom_rev,
+                               apr_pool_t *scratch_pool)
+{
+  svn_wc__db_status_t status;
+  svn_kind_t kind;
+  const char *original_repos_relpath;
+  const char *original_root_url;
+  const char *original_uuid;
+  svn_boolean_t had_props;
+  svn_boolean_t props_mod;
+
+  svn_revnum_t original_revision;
+  svn_revnum_t changed_rev;
+  apr_time_t changed_date;
+  const char *changed_author;
+
+  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
+                               NULL, NULL, NULL, NULL, NULL,
+                               &original_repos_relpath, &original_root_url,
+                               &original_uuid, &original_revision, NULL, NULL,
+                               NULL, NULL, NULL, NULL, &had_props, &props_mod,
+                               NULL, NULL, NULL,
+                               wc_ctx->db, local_abspath,
+                               scratch_pool, scratch_pool));
+
+  if (status != svn_wc__db_status_added
+      || kind != svn_kind_dir
+      || had_props
+      || props_mod
+      || !original_repos_relpath)
+    {
+      return svn_error_createf(
+                    SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
+                    _("'%s' is not an unmodified copied directory"),
+                    svn_dirent_local_style(local_abspath, scratch_pool));
+    }
+  if (original_revision != copyfrom_rev
+      || strcmp(copyfrom_url,
+                 svn_path_url_add_component2(original_root_url,
+                                             original_repos_relpath,
+                                             scratch_pool)))
+    {
+      return svn_error_createf(
+                    SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
+                    _("Copyfrom '%s' doesn't match original location of '%s'"),
+                    copyfrom_url,
+                    svn_dirent_local_style(local_abspath, scratch_pool));
+    }
+
+  {
+    apr_array_header_t *regular_props;
+    apr_array_header_t *entry_props;
+
+    SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
+                                                        scratch_pool),
+                                 &entry_props, NULL, &regular_props,
+                                 scratch_pool));
+
+    /* Put regular props back into a hash table. */
+    new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
+
+    /* Get the change_* info from the entry props.  */
+    SVN_ERR(accumulate_last_change(&changed_rev,
+                                   &changed_date,
+                                   &changed_author,
+                                   entry_props, scratch_pool, scratch_pool));
+  }
+
+  return svn_error_trace(
+            svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
+                                   new_original_props,
+                                   changed_rev, changed_date, changed_author,
+                                   original_repos_relpath, original_root_url,
+                                   original_uuid, original_revision,
+                                   NULL /* children */,
+                                   FALSE /* is_move */,
+                                   svn_depth_infinity,
+                                   NULL /* conflict */,
+                                   NULL /* work_items */,
+                                   scratch_pool));
+}

Modified: subversion/branches/fsfs-format7/subversion/libsvn_wc/upgrade.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_wc/upgrade.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_wc/upgrade.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_wc/upgrade.c Mon Feb  4 20:48:05 2013
@@ -1169,7 +1169,8 @@ bump_to_23(void *baton, svn_sqlite__db_t
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_HAS_WORKING_NODES));
+  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+                                    STMT_UPGRADE_23_HAS_WORKING_NODES));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
   SVN_ERR(svn_sqlite__reset(stmt));
   if (have_row)
@@ -1456,6 +1457,7 @@ svn_wc__upgrade_conflict_skel_from_raw(s
                                                       db, wri_abspath,
                                                       tc->reason,
                                                       tc->action,
+                                                      NULL,
                                                       scratch_pool,
                                                       scratch_pool));
 
@@ -1465,12 +1467,14 @@ svn_wc__upgrade_conflict_skel_from_raw(s
           default:
             SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data,
                                                        tc->src_left_version,
+                                                       tc->src_right_version,
                                                        scratch_pool,
                                                        scratch_pool));
             break;
           case svn_wc_operation_switch:
             SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict_data,
                                                         tc->src_left_version,
+                                                        tc->src_right_version,
                                                         scratch_pool,
                                                         scratch_pool));
             break;
@@ -1485,7 +1489,7 @@ svn_wc__upgrade_conflict_skel_from_raw(s
     }
   else if (conflict_data)
     {
-      SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data, NULL,
+      SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict_data, NULL, NULL,
                                                   scratch_pool,
                                                   scratch_pool));
     }
@@ -1494,6 +1498,51 @@ svn_wc__upgrade_conflict_skel_from_raw(s
   return SVN_NO_ERROR;
 }
 
+/* Helper function to upgrade a single conflict from bump_to_30 */
+static svn_error_t *
+bump_30_upgrade_one_conflict(svn_wc__db_t *wc_db,
+                             const char *wcroot_abspath,
+                             svn_sqlite__stmt_t *stmt,
+                             svn_sqlite__db_t *sdb,
+                             apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt_store;
+  svn_stringbuf_t *skel_data;
+  svn_skel_t *conflict_data;
+  apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0);
+  const char *local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
+  const char *conflict_old = svn_sqlite__column_text(stmt, 2, NULL);
+  const char *conflict_wrk = svn_sqlite__column_text(stmt, 3, NULL);
+  const char *conflict_new = svn_sqlite__column_text(stmt, 4, NULL);
+  const char *prop_reject = svn_sqlite__column_text(stmt, 5, NULL);
+  apr_size_t tree_conflict_size;
+  const char *tree_conflict_data = svn_sqlite__column_blob(stmt, 6,
+                                           &tree_conflict_size, NULL);
+
+  SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(&conflict_data,
+                                                 wc_db, wcroot_abspath,
+                                                 local_relpath,
+                                                 conflict_old,
+                                                 conflict_wrk,
+                                                 conflict_new,
+                                                 prop_reject,
+                                                 tree_conflict_data,
+                                                 tree_conflict_size,
+                                                 scratch_pool, scratch_pool));
+
+  SVN_ERR_ASSERT(conflict_data != NULL);
+
+  skel_data = svn_skel__unparse(conflict_data, scratch_pool);
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt_store, sdb,
+                                    STMT_UPGRADE_30_SET_CONFLICT));
+  SVN_ERR(svn_sqlite__bindf(stmt_store, "isb", wc_id, local_relpath,
+                            skel_data->data, skel_data->len));
+  SVN_ERR(svn_sqlite__step_done(stmt_store));
+
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *
 bump_to_30(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
 {
@@ -1501,54 +1550,30 @@ bump_to_30(void *baton, svn_sqlite__db_t
   svn_boolean_t have_row;
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
   svn_sqlite__stmt_t *stmt;
-  svn_sqlite__stmt_t *stmt_store;
   svn_wc__db_t *db; /* Read only temp db */
-  const char *wri_abspath = bb->wcroot_abspath;
 
-  SVN_ERR(svn_wc__db_open(&db, NULL, FALSE, FALSE,
+  SVN_ERR(svn_wc__db_open(&db, NULL, TRUE /* open_without_upgrade */, FALSE,
                           scratch_pool, scratch_pool));
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt_store, sdb,
-                                    STMT_UPGRADE_30_SET_CONFLICT));
-
   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
                                     STMT_UPGRADE_30_SELECT_CONFLICT_SEPARATE));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
 
   while (have_row)
     {
-      svn_stringbuf_t *skel_data;
-      svn_skel_t *conflict_data;
-      apr_int64_t wc_id = svn_sqlite__column_int64(stmt, 0);
-      const char *local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
-      const char *conflict_old = svn_sqlite__column_text(stmt, 2, NULL);
-      const char *conflict_wrk = svn_sqlite__column_text(stmt, 3, NULL);
-      const char *conflict_new = svn_sqlite__column_text(stmt, 4, NULL);
-      const char *prop_reject = svn_sqlite__column_text(stmt, 5, NULL);
-      apr_size_t tree_conflict_size;
-      const char *tree_conflict_data = svn_sqlite__column_blob(stmt, 6,
-                                               &tree_conflict_size, NULL);
-
+      svn_error_t *err;
       svn_pool_clear(iterpool);
 
-      SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(&conflict_data,
-                                                     db, wri_abspath,
-                                                     local_relpath,
-                                                     conflict_old,
-                                                     conflict_wrk,
-                                                     conflict_new,
-                                                     prop_reject,
-                                                     tree_conflict_data,
-                                                     tree_conflict_size,
-                                                     iterpool, iterpool));
-
-      SVN_ERR_ASSERT(conflict_data != NULL);
-
-      skel_data = svn_skel__unparse(conflict_data, iterpool);
-
-      SVN_ERR(svn_sqlite__bindf(stmt_store, "isb", wc_id, local_relpath,
-                                skel_data->data, skel_data->len));
-      SVN_ERR(svn_sqlite__step_done(stmt_store));
+      err = bump_30_upgrade_one_conflict(db, bb->wcroot_abspath, stmt, sdb,
+                                         iterpool);
+
+      if (err)
+        {
+          return svn_error_trace(
+                    svn_error_compose_create(
+                            err,
+                            svn_sqlite__reset(stmt)));
+        }
 
       SVN_ERR(svn_sqlite__step(&have_row, stmt));
     }
@@ -1753,16 +1778,8 @@ upgrade_to_wcng(void **dir_baton,
   return SVN_NO_ERROR;
 }
 
-
-/* Return a string indicating the released version (or versions) of
- * Subversion that used WC format number WC_FORMAT, or some other
- * suitable string if no released version used WC_FORMAT.
- *
- * ### It's not ideal to encode this sort of knowledge in this low-level
- * library.  On the other hand, it doesn't need to be updated often and
- * should be easily found when it does need to be updated.  */
-static const char *
-version_string_from_format(int wc_format)
+const char *
+svn_wc__version_string_from_format(int wc_format)
 {
   switch (wc_format)
     {
@@ -1770,6 +1787,7 @@ version_string_from_format(int wc_format
       case 8: return "1.4";
       case 9: return "1.5";
       case 10: return "1.6";
+      case SVN_WC__WC_NG_VERSION: return "1.7";
     }
   return _("(unreleased development version)");
 }
@@ -1792,7 +1810,7 @@ svn_wc__upgrade_sdb(int *result_format,
                              svn_dirent_local_style(wcroot_abspath,
                                                     scratch_pool),
                              start_format,
-                             version_string_from_format(start_format));
+                             svn_wc__version_string_from_format(start_format));
 
   /* Early WCNG formats no longer supported. */
   if (start_format < 19)
@@ -2138,17 +2156,21 @@ svn_wc_upgrade(svn_wc_context_t *wc_ctx,
   SVN_ERR(svn_wc__db_open(&db, NULL /* ### config */, TRUE, FALSE,
                           scratch_pool, scratch_pool));
 
+
   err = svn_wc__db_bump_format(&result_format, local_abspath, db,
                                scratch_pool);
   if (err)
     {
-      if (err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED) /* pre-1.7 WC */
+      if (err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
         {
-          svn_error_clear(err);
-          SVN_ERR(svn_wc__db_close(db));
+          return svn_error_trace(
+                    svn_error_compose_create(
+                            err,
+                            svn_wc__db_close(db)));
         }
-      else
-        return svn_error_trace(err);
+
+      svn_error_clear(err);
+      /* Pre 1.7: Fall through */
     }
   else
     {
@@ -2171,10 +2193,6 @@ svn_wc_upgrade(svn_wc_context_t *wc_ctx,
      'cleanup' with a new client will complete any outstanding
      upgrade. */
 
-  SVN_ERR(svn_wc__db_open(&db,
-                          NULL /* ### config */, TRUE, FALSE,
-                          scratch_pool, scratch_pool));
-
   SVN_ERR(svn_wc__read_entries_old(&entries, local_abspath,
                                    scratch_pool, scratch_pool));
 

Modified: subversion/branches/fsfs-format7/subversion/libsvn_wc/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_wc/util.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_wc/util.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_wc/util.c Mon Feb  4 20:48:05 2013
@@ -610,8 +610,9 @@ svn_wc__fetch_base_func(const char **fil
 
   err = svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, NULL, NULL,
                                  NULL, NULL, NULL, NULL, &checksum,
-                                 NULL, NULL, NULL, NULL, sfb->db,
-                                 local_abspath, scratch_pool, scratch_pool);
+                                 NULL, NULL, NULL, NULL, NULL,
+                                 sfb->db, local_abspath,
+                                 scratch_pool, scratch_pool);
   if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
     {
       svn_error_clear(err);

Modified: subversion/branches/fsfs-format7/subversion/libsvn_wc/wc-metadata.sql
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_wc/wc-metadata.sql?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_wc/wc-metadata.sql (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_wc/wc-metadata.sql Mon Feb  4 20:48:05 2013
@@ -181,8 +181,8 @@ CREATE TABLE ACTUAL_NODE (
   PRIMARY KEY (wc_id, local_relpath)
   );
 
-CREATE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath);
-CREATE INDEX I_ACTUAL_CHANGELIST ON ACTUAL_NODE (changelist);
+CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath,
+                                                    local_relpath);
 
 
 /* ------------------------------------------------------------------------- */
@@ -253,19 +253,10 @@ PRAGMA user_version =
    op_depth values are not normally visible to the user but may become
    visible after reverting local changes.
 
-   ### The following text needs revision
-
-   Each row in BASE_NODE has an associated row NODE_DATA. Additionally, each
-   row in WORKING_NODE has one or more associated rows in NODE_DATA.
-
    This table contains full node descriptions for nodes in either the BASE
    or WORKING trees as described in notes/wc-ng/design. Fields relate
    both to BASE and WORKING trees, unless documented otherwise.
 
-   ### This table is to be integrated into the SCHEMA statement as soon
-       the experimental status of NODES is lifted.
-   ### This table superseeds NODE_DATA
-
    For illustration, with a scenario like this:
 
      # (0)
@@ -275,12 +266,11 @@ PRAGMA user_version =
      touch foo/bar
      svn add foo/bar    # (2)
 
-   , these are the NODES for the path foo/bar (before single-db, the
-   numbering of op_depth is still a bit different):
+   , these are the NODES table rows for the path foo/bar:
 
-   (0)  BASE_NODE ----->  NODES (op_depth == 0)
-   (1)                    NODES (op_depth == 1) ( <----_ )
-   (2)                    NODES (op_depth == 2)   <----- WORKING_NODE
+   (0)  "BASE" --->  NODES (op_depth == 0)
+   (1)               NODES (op_depth == 1)
+   (2)               NODES (op_depth == 2)
 
    0 is the original data for foo/bar before 'svn rm foo' (if it existed).
    1 is the data for foo/bar copied in from ^/moo/bar.
@@ -408,8 +398,11 @@ CREATE TABLE NODES (
   /* the kind of the new node. may be "unknown" if the node is not present. */
   kind  TEXT NOT NULL,
 
-  /* serialized skel of this node's properties. NULL if we
-     have no information about the properties (a non-present node). */
+  /* serialized skel of this node's properties (when presence is 'normal' or
+     'incomplete'); an empty skel or NULL indicates no properties.  NULL if
+     we have no information about the properties (any other presence).
+     TODO: Choose & require a single representation for 'no properties'.
+  */
   properties  BLOB,
 
   /* NULL depth means "default" (typically svn_depth_infinity) */
@@ -483,7 +476,8 @@ CREATE TABLE NODES (
 
   );
 
-CREATE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath, op_depth);
+CREATE UNIQUE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath,
+                                             local_relpath, op_depth);
 /* I_NODES_MOVED is introduced in format 30 */
 CREATE UNIQUE INDEX I_NODES_MOVED ON NODES (wc_id, moved_to, op_depth);
 
@@ -553,7 +547,7 @@ CREATE TABLE EXTERNALS (
   /* Repository location fields */
   repos_id  INTEGER NOT NULL REFERENCES REPOSITORY (id),
 
-  /* Either 'normal' or 'excluded' */
+  /* Either MAP_NORMAL or MAP_EXCLUDED */
   presence  TEXT NOT NULL,
 
   /* the kind of the external. */
@@ -574,7 +568,6 @@ CREATE TABLE EXTERNALS (
   PRIMARY KEY (wc_id, local_relpath)
 );
 
-CREATE INDEX I_EXTERNALS_PARENT ON EXTERNALS (wc_id, parent_relpath);
 CREATE UNIQUE INDEX I_EXTERNALS_DEFINED ON EXTERNALS (wc_id,
                                                       def_local_relpath,
                                                       local_relpath);
@@ -667,6 +660,9 @@ PRAGMA user_version = 22;
 -- STMT_UPGRADE_TO_23
 PRAGMA user_version = 23;
 
+-- STMT_UPGRADE_23_HAS_WORKING_NODES
+SELECT 1 FROM nodes WHERE op_depth > 0
+LIMIT 1
 
 /* ------------------------------------------------------------------------- */
 
@@ -820,6 +816,8 @@ WHERE wc_id = ?1 and local_relpath = ?2
    inherited properties */
 -- STMT_UPGRADE_TO_31
 ALTER TABLE NODES ADD COLUMN inherited_props BLOB;
+DROP INDEX IF EXISTS I_ACTUAL_CHANGELIST;
+DROP INDEX IF EXISTS I_EXTERNALS_PARENT;
 
 PRAGMA user_version = 31;
 
@@ -846,6 +844,23 @@ WHERE (l.local_relpath = '' AND l.repos_
                                  '/'))
 
 /* ------------------------------------------------------------------------- */
+/* Format 32 ....  */
+-- STMT_UPGRADE_TO_32
+
+/* Drop old index. ### Remove this part from the upgrade to 31 once bumped */
+DROP INDEX IF EXISTS I_ACTUAL_CHANGELIST;
+DROP INDEX IF EXISTS I_EXTERNALS_PARENT;
+CREATE INDEX I_EXTERNALS_PARENT ON EXTERNALS (wc_id, parent_relpath);
+
+DROP INDEX I_NODES_PARENT;
+CREATE UNIQUE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath,
+                                             local_relpath, op_depth);
+
+DROP INDEX I_ACTUAL_PARENT;
+CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL (wc_id, parent_relpath,
+                                               local_relpath);
+
+/* ------------------------------------------------------------------------- */
 
 /* Format YYY introduces new handling for conflict information.  */
 -- format: YYY
@@ -902,8 +917,8 @@ CREATE TABLE ACTUAL_NODE (
   PRIMARY KEY (wc_id, local_relpath)
   );
 
-CREATE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath);
-CREATE INDEX I_ACTUAL_CHANGELIST ON ACTUAL_NODE (changelist);
+CREATE UNIQUE INDEX I_ACTUAL_PARENT ON ACTUAL_NODE (wc_id, parent_relpath,
+                                                    local_relpath);
 
 INSERT INTO ACTUAL_NODE SELECT
   wc_id, local_relpath, parent_relpath, properties, conflict_old,