Modified: subversion/branches/fsx-1.10/subversion/libsvn_wc/wc_db.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/fsx-1.10/subversion/libsvn_wc/wc_db.c?rev=1685464&r1=1685463&r2=1685464&view=diff
==============================================================================
--- subversion/branches/fsx-1.10/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/fsx-1.10/subversion/libsvn_wc/wc_db.c Sun Jun 14 
20:58:10 2015
@@ -278,10 +278,9 @@ add_work_items(svn_sqlite__db_t *sdb,
                apr_pool_t *scratch_pool);
 
 static svn_error_t *
-set_actual_props(apr_int64_t wc_id,
+set_actual_props(svn_wc__db_wcroot_t *wcroot,
                  const char *local_relpath,
                  apr_hash_t *props,
-                 svn_sqlite__db_t *db,
                  apr_pool_t *scratch_pool);
 
 static svn_error_t *
@@ -487,35 +486,6 @@ repos_location_from_columns(apr_int64_t
     }
 }
 
-
-/* Get the statement given by STMT_IDX, and bind the appropriate wc_id and
-   local_relpath based upon LOCAL_ABSPATH.  Store it in *STMT, and use
-   SCRATCH_POOL for temporary allocations.
-
-   Note: WC_ID and LOCAL_RELPATH must be arguments 1 and 2 in the statement. */
-static svn_error_t *
-get_statement_for_path(svn_sqlite__stmt_t **stmt,
-                       svn_wc__db_t *db,
-                       const char *local_abspath,
-                       int stmt_idx,
-                       apr_pool_t *scratch_pool)
-{
-  svn_wc__db_wcroot_t *wcroot;
-  const char *local_relpath;
-
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
-
-  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
-                              local_abspath, scratch_pool, scratch_pool));
-  VERIFY_USABLE_WCROOT(wcroot);
-
-  SVN_ERR(svn_sqlite__get_statement(stmt, wcroot->sdb, stmt_idx));
-  SVN_ERR(svn_sqlite__bindf(*stmt, "is", wcroot->wc_id, local_relpath));
-
-  return SVN_NO_ERROR;
-}
-
-
 /* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID
    value. If one does not exist, then create a new one. */
 static svn_error_t *
@@ -611,8 +581,7 @@ blank_ibb(insert_base_baton_t *pibb)
    was recorded, otherwise to FALSE.
  */
 static svn_error_t *
-db_extend_parent_delete(svn_boolean_t *added_delete,
-                        svn_wc__db_wcroot_t *wcroot,
+db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
                         const char *local_relpath,
                         svn_node_kind_t kind,
                         int op_depth,
@@ -625,9 +594,6 @@ db_extend_parent_delete(svn_boolean_t *a
 
   SVN_ERR_ASSERT(local_relpath[0]);
 
-  if (added_delete)
-    *added_delete = FALSE;
-
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_SELECT_LOWEST_WORKING_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath,
@@ -654,9 +620,6 @@ db_extend_parent_delete(svn_boolean_t *a
                                     local_relpath, parent_op_depth,
                                     parent_relpath, kind_map, kind));
           SVN_ERR(svn_sqlite__update(NULL, stmt));
-
-          if (added_delete)
-            *added_delete = TRUE;
         }
     }
 
@@ -750,6 +713,7 @@ insert_base_node(const insert_base_baton
   svn_sqlite__stmt_t *stmt;
   svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
   apr_int64_t recorded_time;
+  svn_boolean_t present;
 
   /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
      bind the appropriate parent_relpath. */
@@ -780,6 +744,9 @@ insert_base_node(const insert_base_baton
       SVN_ERR(svn_sqlite__reset(stmt));
     }
 
+  present = (pibb->status == svn_wc__db_status_normal
+             || pibb->status == svn_wc__db_status_incomplete);
+
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
                             "tstr"               /* 8 - 11 */
@@ -792,15 +759,16 @@ insert_base_node(const insert_base_baton
                             pibb->repos_relpath,
                             pibb->revision,
                             presence_map, pibb->status, /* 8 */
-                            (pibb->kind == svn_node_dir) ? /* 9 */
-                             svn_token__to_word(depth_map, pibb->depth) : NULL,
+                            (pibb->kind == svn_node_dir && present) /* 9 */
+                              ? svn_token__to_word(depth_map, pibb->depth)
+                              : NULL,
                             kind_map, pibb->kind, /* 10 */
                             pibb->changed_rev,    /* 11 */
                             pibb->changed_date,   /* 12 */
                             pibb->changed_author, /* 13 */
-                            (pibb->kind == svn_node_symlink) ?
+                            (pibb->kind == svn_node_symlink && present) ?
                                 pibb->target : NULL)); /* 19 */
-  if (pibb->kind == svn_node_file)
+  if (pibb->kind == svn_node_file && present)
     {
       if (!pibb->checksum
           && pibb->status != svn_wc__db_status_not_present
@@ -825,11 +793,14 @@ insert_base_node(const insert_base_baton
   assert(pibb->status == svn_wc__db_status_normal
          || pibb->status == svn_wc__db_status_incomplete
          || pibb->props == NULL);
-  SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
-                                      scratch_pool));
+  if (present)
+    {
+      SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
+                                          scratch_pool));
 
-  SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
+      SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
                                       scratch_pool));
+    }
 
   if (pibb->dav_cache)
     SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
@@ -859,8 +830,8 @@ insert_base_node(const insert_base_baton
             new_actual_props = NULL;
         }
 
-      SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
-                               wcroot->sdb, scratch_pool));
+      SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props,
+                               scratch_pool));
     }
 
   if (pibb->kind == svn_node_dir && pibb->children)
@@ -881,8 +852,7 @@ insert_base_node(const insert_base_baton
               || (pibb->status == svn_wc__db_status_incomplete))
           && ! pibb->file_external)
         {
-          SVN_ERR(db_extend_parent_delete(NULL,
-                                          wcroot, local_relpath,
+          SVN_ERR(db_extend_parent_delete(wcroot, local_relpath,
                                           pibb->kind, 0,
                                           scratch_pool));
         }
@@ -1035,6 +1005,7 @@ insert_working_node(const insert_working
   const char *moved_to_relpath = NULL;
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
+  svn_boolean_t present;
 
   SVN_ERR_ASSERT(piwb->op_depth > 0);
 
@@ -1053,6 +1024,9 @@ insert_working_node(const insert_working
     moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
   SVN_ERR(svn_sqlite__reset(stmt));
 
+  present = (piwb->presence == svn_wc__db_status_normal
+             || piwb->presence == svn_wc__db_status_incomplete);
+
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
                 "nnnn" /* properties translated_size last_mod_time dav_cache */
@@ -1061,14 +1035,14 @@ insert_working_node(const insert_working
                 piwb->op_depth,
                 parent_relpath,
                 presence_map, piwb->presence,
-                (piwb->kind == svn_node_dir)
+                (piwb->kind == svn_node_dir && present)
                             ? svn_token__to_word(depth_map, piwb->depth) : 
NULL,
                 kind_map, piwb->kind,
                 piwb->changed_rev,
                 piwb->changed_date,
                 piwb->changed_author,
                 /* Note: incomplete nodes may have a NULL target.  */
-                (piwb->kind == svn_node_symlink)
+                (piwb->kind == svn_node_symlink && present)
                             ? piwb->target : NULL,
                 moved_to_relpath));
 
@@ -1077,7 +1051,7 @@ insert_working_node(const insert_working
       SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
     }
 
-  if (piwb->kind == svn_node_file)
+  if (piwb->kind == svn_node_file && present)
     {
       SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
                                         scratch_pool));
@@ -1094,7 +1068,8 @@ insert_working_node(const insert_working
   assert(piwb->presence == svn_wc__db_status_normal
          || piwb->presence == svn_wc__db_status_incomplete
          || piwb->props == NULL);
-  SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
+  if (present && piwb->original_repos_relpath)
+    SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
 
   SVN_ERR(svn_sqlite__insert(NULL, stmt));
 
@@ -1131,8 +1106,8 @@ insert_working_node(const insert_working
             new_actual_props = NULL;
         }
 
-      SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
-                               wcroot->sdb, scratch_pool));
+      SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props,
+                               scratch_pool));
     }
 
   if (piwb->kind == svn_node_dir)
@@ -1178,119 +1153,40 @@ insert_working_node(const insert_working
 }
 
 
-/* Each name is allocated in RESULT_POOL and stored into CHILDREN as a key
-   pointed to the same name.  */
-static svn_error_t *
-add_children_to_hash(apr_hash_t *children,
-                     int stmt_idx,
-                     svn_sqlite__db_t *sdb,
-                     apr_int64_t wc_id,
-                     const char *parent_relpath,
-                     apr_pool_t *result_pool)
-{
-  svn_sqlite__stmt_t *stmt;
-  svn_boolean_t have_row;
-
-  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, stmt_idx));
-  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath));
-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
-  while (have_row)
-    {
-      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
-      const char *name = svn_relpath_basename(child_relpath, result_pool);
-
-      svn_hash_sets(children, name, name);
-
-      SVN_ERR(svn_sqlite__step(&have_row, stmt));
-    }
-
-  return svn_sqlite__reset(stmt);
-}
-
-
-/* Set *CHILDREN to a new array of the (const char *) basenames of the
-   immediate children, whatever their status, of the working node at
-   LOCAL_RELPATH. */
-static svn_error_t *
-gather_children2(const apr_array_header_t **children,
-                 svn_wc__db_wcroot_t *wcroot,
-                 const char *local_relpath,
-                 apr_pool_t *result_pool,
-                 apr_pool_t *scratch_pool)
-{
-  apr_hash_t *names_hash = apr_hash_make(scratch_pool);
-  apr_array_header_t *names_array;
-
-  /* All of the names get allocated in RESULT_POOL.  It
-     appears to be faster to use the hash to remove duplicates than to
-     use DISTINCT in the SQL query. */
-  SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_WORKING_CHILDREN,
-                               wcroot->sdb, wcroot->wc_id,
-                               local_relpath, result_pool));
-
-  SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
-  *children = names_array;
-  return SVN_NO_ERROR;
-}
-
 /* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
    of any status, in all op-depths in the NODES table. */
 static svn_error_t *
 gather_children(const apr_array_header_t **children,
                 svn_wc__db_wcroot_t *wcroot,
-                const char *local_relpath,
+                const char *parent_relpath,
+                int stmt_idx,
+                int op_depth,
                 apr_pool_t *result_pool,
                 apr_pool_t *scratch_pool)
 {
-  apr_hash_t *names_hash = apr_hash_make(scratch_pool);
-  apr_array_header_t *names_array;
-
-  /* All of the names get allocated in RESULT_POOL.  It
-     appears to be faster to use the hash to remove duplicates than to
-     use DISTINCT in the SQL query. */
-  SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_NODE_CHILDREN,
-                               wcroot->sdb, wcroot->wc_id,
-                               local_relpath, result_pool));
-
-  SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
-  *children = names_array;
-  return SVN_NO_ERROR;
-}
-
-
-/* Set *CHILDREN to a new array of (const char *) names of the children of
-   the repository directory corresponding to WCROOT:LOCAL_RELPATH:OP_DEPTH -
-   that is, only the children that are at the same op-depth as their parent. */
-static svn_error_t *
-gather_repo_children(const apr_array_header_t **children,
-                     svn_wc__db_wcroot_t *wcroot,
-                     const char *local_relpath,
-                     int op_depth,
-                     apr_pool_t *result_pool,
-                     apr_pool_t *scratch_pool)
-{
-  apr_array_header_t *result
-    = apr_array_make(result_pool, 0, sizeof(const char *));
+  apr_array_header_t *result;
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_SELECT_OP_DEPTH_CHILDREN));
-  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
-                            op_depth));
+  result = apr_array_make(result_pool, 16, sizeof(const char*));
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
+  if (op_depth >= 0)
+    SVN_ERR(svn_sqlite__bind_int(stmt, 3, op_depth));
+
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
   while (have_row)
     {
       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+      const char *name = svn_relpath_basename(child_relpath, result_pool);
 
-      /* Allocate the name in RESULT_POOL so we won't have to copy it. */
-      APR_ARRAY_PUSH(result, const char *)
-        = svn_relpath_basename(child_relpath, result_pool);
+      APR_ARRAY_PUSH(result, const char *) = name;
 
       SVN_ERR(svn_sqlite__step(&have_row, stmt));
     }
-  SVN_ERR(svn_sqlite__reset(stmt));
 
+  SVN_ERR(svn_sqlite__reset(stmt));
   *children = result;
   return SVN_NO_ERROR;
 }
@@ -1585,7 +1481,7 @@ svn_wc__db_init(svn_wc__db_t *db,
 
   /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd  */
 
-  SVN_ERR(svn_config_get_bool((svn_config_t *)db->config, &sqlite_exclusive,
+  SVN_ERR(svn_config_get_bool(db->config, &sqlite_exclusive,
                               SVN_CONFIG_SECTION_WORKING_COPY,
                               SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
                               FALSE));
@@ -2177,7 +2073,7 @@ clear_moved_here(svn_wc__db_wcroot_t *wc
 svn_error_t *
 svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot,
                                   const char *src_relpath,
-                                  int src_op_depth,
+                                  int delete_op_depth,
                                   const char *dst_relpath,
                                   const svn_skel_t *work_items,
                                   apr_pool_t *scratch_pool)
@@ -2188,7 +2084,7 @@ svn_wc__db_op_break_move_internal(svn_wc
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_CLEAR_MOVED_TO_RELPATH));
   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath,
-                            src_op_depth));
+                            delete_op_depth));
   SVN_ERR(svn_sqlite__update(&affected, stmt));
 
   if (affected != 1)
@@ -2211,9 +2107,9 @@ db_base_remove(svn_wc__db_wcroot_t *wcro
                const char *local_relpath,
                svn_wc__db_t *db, /* For checking conflicts */
                svn_boolean_t keep_as_working,
-               svn_boolean_t queue_deletes,
-               svn_boolean_t remove_locks,
-               svn_revnum_t not_present_revision,
+               svn_boolean_t mark_not_present,
+               svn_boolean_t mark_excluded,
+               svn_revnum_t marker_revision,
                svn_skel_t *conflict,
                svn_skel_t *work_items,
                apr_pool_t *scratch_pool)
@@ -2221,66 +2117,82 @@ db_base_remove(svn_wc__db_wcroot_t *wcro
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
   svn_wc__db_status_t status;
+  svn_revnum_t revision;
   apr_int64_t repos_id;
   const char *repos_relpath;
   svn_node_kind_t kind;
   svn_boolean_t keep_working;
+  int op_depth;
+  svn_node_kind_t wrk_kind;
+  svn_boolean_t no_delete_wc = FALSE;
+  svn_boolean_t file_external;
 
-  SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, NULL,
+  SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, &revision,
                                             &repos_relpath, &repos_id,
                                             NULL, NULL, NULL, NULL, NULL,
-                                            NULL, NULL, NULL, NULL, NULL,
+                                            NULL, NULL, NULL, NULL,
+                                            &file_external,
                                             wcroot, local_relpath,
                                             scratch_pool, scratch_pool));
 
-  if (remove_locks)
-    {
-      svn_sqlite__stmt_t *lock_stmt;
+  /* Check if there is already a working node */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_NODE_INFO));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
 
-      SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
-                                        STMT_DELETE_LOCK_RECURSIVELY));
-      SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
-      SVN_ERR(svn_sqlite__step_done(lock_stmt));
-    }
+  if (!have_row)
+    return svn_error_trace(svn_sqlite__reset(stmt)); /* No BASE */
 
-  if (status == svn_wc__db_status_normal
-      && keep_as_working)
+  op_depth = svn_sqlite__column_int(stmt, 0);
+  wrk_kind = svn_sqlite__column_token(stmt, 4, kind_map);
+
+  if (op_depth > 0
+      && op_depth == relpath_depth(local_relpath))
     {
-      SVN_ERR(svn_wc__db_op_make_copy(db,
-                                      svn_dirent_join(wcroot->abspath,
-                                                      local_relpath,
-                                                      scratch_pool),
-                                      NULL, NULL,
-                                      scratch_pool));
-      keep_working = TRUE;
+      svn_wc__db_status_t presence;
+      presence = svn_sqlite__column_token(stmt, 3, presence_map);
+
+      if (presence == svn_wc__db_status_base_deleted)
+        {
+          keep_working = FALSE;
+          no_delete_wc = TRUE;
+        }
+      else
+        {
+          keep_working = TRUE;
+        }
     }
   else
+    keep_working = FALSE;
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  if (keep_as_working && op_depth == 0)
     {
-      /* Check if there is already a working node */
-      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_SELECT_WORKING_NODE));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
-      SVN_ERR(svn_sqlite__step(&keep_working, stmt));
-      SVN_ERR(svn_sqlite__reset(stmt));
+      if (status == svn_wc__db_status_normal
+          || status == svn_wc__db_status_incomplete)
+        {
+          SVN_ERR(svn_wc__db_op_make_copy_internal(wcroot, local_relpath, TRUE,
+                                                   NULL, NULL,
+                                                   scratch_pool));
+        }
+      keep_working = TRUE;
     }
 
   /* Step 1: Create workqueue operations to remove files and dirs in the
      local-wc */
-  if (!keep_working
-      && queue_deletes
-      && (status == svn_wc__db_status_normal
-          || status == svn_wc__db_status_incomplete))
+  if (!keep_working && !no_delete_wc)
     {
       svn_skel_t *work_item;
       const char *local_abspath;
 
       local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
                                       scratch_pool);
-      if (kind == svn_node_dir)
+      if (wrk_kind == svn_node_dir)
         {
           apr_pool_t *iterpool;
           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                            STMT_SELECT_BASE_PRESENT));
+                                            STMT_SELECT_WORKING_PRESENT));
           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
 
           iterpool = svn_pool_create(scratch_pool);
@@ -2359,27 +2271,12 @@ db_base_remove(svn_wc__db_wcroot_t *wcro
            ACTUAL_NODE records */
 
   /* Step 3: Delete WORKING nodes */
-  if (conflict)
+  if (!keep_working)
     {
       apr_pool_t *iterpool;
 
-      /*
-       * When deleting a conflicted node, moves of any moved-outside children
-       * of the node must be broken. Else, the destination will still be marked
-       * moved-here after the move source disappears from the working copy.
-       *
-       * ### FIXME: It would be nicer to have the conflict resolver
-       * break the move instead. It might also be a good idea to
-       * flag a tree conflict on each moved-away child. But doing so
-       * might introduce actual-only nodes without direct parents,
-       * and we're not yet sure if other existing code is prepared
-       * to handle such nodes. To be revisited post-1.8.
-       *
-       * ### In case of a conflict we are most likely creating WORKING nodes
-       *     describing a copy of what was in BASE. The move information
-       *     should be updated to describe a move from the WORKING layer.
-       *     When stored that way the resolver of the tree conflict still has
-       *     the knowledge of what was moved.
+      /* When deleting everything in working we should break moves from
+         here and to here.
        */
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                         STMT_SELECT_MOVED_OUTSIDE));
@@ -2403,10 +2300,51 @@ db_base_remove(svn_wc__db_wcroot_t *wcro
       svn_pool_destroy(iterpool);
       SVN_ERR(svn_sqlite__reset(stmt));
     }
-  if (keep_working)
+  else
     {
+      /* We are keeping things that are in WORKING, but we should still
+         break moves of things in BASE. (Mixed revisions make it
+         impossible to guarantee that we can keep everything moved) */
+
+      apr_pool_t *iterpool;
+
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_DELETE_WORKING_BASE_DELETE));
+                                        STMT_SELECT_MOVED_DESCENDANTS_SRC));
+      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
+                                local_relpath, 0));
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+      iterpool = svn_pool_create(scratch_pool);
+      while (have_row)
+        {
+          int delete_op_depth = svn_sqlite__column_int(stmt, 0);
+          const char *src_relpath;
+          const char *dst_relpath;
+          svn_error_t *err;
+
+          svn_pool_clear(iterpool);
+
+          src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
+          dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
+
+          err = svn_wc__db_op_break_move_internal(wcroot, src_relpath,
+                                                  delete_op_depth,
+                                                  dst_relpath,
+                                                  NULL,
+                                                  iterpool);
+
+          if (err)
+            return svn_error_compose_create(err, svn_sqlite__reset(stmt));
+
+          SVN_ERR(svn_sqlite__step(&have_row, stmt));
+        }
+      svn_pool_destroy(iterpool);
+      SVN_ERR(svn_sqlite__reset(stmt));
+    }
+  if (keep_working)
+    {
+      SVN_ERR(svn_sqlite__get_statement(
+                    &stmt, wcroot->sdb,
+                    STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
       SVN_ERR(svn_sqlite__step_done(stmt));
     }
@@ -2424,41 +2362,69 @@ db_base_remove(svn_wc__db_wcroot_t *wcro
   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
   SVN_ERR(svn_sqlite__step_done(stmt));
 
-  /* Step 5: handle the BASE node itself */
-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_DELETE_BASE_NODE));
-  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
-  SVN_ERR(svn_sqlite__step_done(stmt));
-
   SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0, scratch_pool));
 
-  /* Step 6: Delete actual node if we don't keep working */
-  if (! keep_working)
-    {
-      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_DELETE_ACTUAL_NODE));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
-      SVN_ERR(svn_sqlite__step_done(stmt));
-    }
-
-  if (SVN_IS_VALID_REVNUM(not_present_revision))
+  if (mark_not_present || mark_excluded)
     {
       struct insert_base_baton_t ibb;
-      blank_ibb(&ibb);
+      svn_boolean_t no_marker = FALSE;
 
-      ibb.repos_id = repos_id;
-      ibb.status = svn_wc__db_status_not_present;
-      ibb.kind = kind;
-      ibb.repos_relpath = repos_relpath;
-      ibb.revision = not_present_revision;
+      if (file_external)
+        {
+          const char *parent_local_relpath;
+          const char *name;
+          svn_error_t *err;
 
-      /* Depending upon KIND, any of these might get used. */
-      ibb.children = NULL;
-      ibb.depth = svn_depth_unknown;
-      ibb.checksum = NULL;
-      ibb.target = NULL;
+          /* For file externals we only want to place a not present marker
+             if there is a BASE parent */
+          
+          svn_relpath_split(&parent_local_relpath, &name, local_relpath,
+                            scratch_pool);
 
-      SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
+          err = svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
+                                                  &repos_relpath, &repos_id,
+                                                  NULL, NULL, NULL, NULL, NULL,
+                                                  NULL, NULL, NULL, NULL, NULL,
+                                                  wcroot, parent_local_relpath,
+                                                  scratch_pool, scratch_pool);
+
+          if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
+            return svn_error_trace(err);
+          else if (err)
+            {
+              svn_error_clear(err);
+              no_marker = TRUE;
+            }
+          else
+            {
+              /* Replace the repos_relpath with something more expected than
+                 the unrelated old file external repository relpath, which
+                 one day may come from a different repository */
+              repos_relpath = svn_relpath_join(repos_relpath, name, 
scratch_pool);
+            }
+        }
+
+      if (!no_marker)
+        {
+          blank_ibb(&ibb);
+
+          ibb.repos_id = repos_id;
+          ibb.status = mark_excluded ? svn_wc__db_status_excluded
+                                     : svn_wc__db_status_not_present;
+          ibb.kind = kind;
+          ibb.repos_relpath = repos_relpath;
+          ibb.revision = SVN_IS_VALID_REVNUM(marker_revision)
+                            ? marker_revision
+                            : revision;
+
+          /* Depending upon KIND, any of these might get used. */
+          ibb.children = NULL;
+          ibb.depth = svn_depth_unknown;
+          ibb.checksum = NULL;
+          ibb.target = NULL;
+
+          SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
+        }
     }
 
   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
@@ -2474,9 +2440,9 @@ svn_error_t *
 svn_wc__db_base_remove(svn_wc__db_t *db,
                        const char *local_abspath,
                        svn_boolean_t keep_as_working,
-                       svn_boolean_t queue_deletes,
-                       svn_boolean_t remove_locks,
-                       svn_revnum_t not_present_revision,
+                       svn_boolean_t mark_not_present,
+                       svn_boolean_t mark_excluded,
+                       svn_revnum_t marker_revision,
                        svn_skel_t *conflict,
                        svn_skel_t *work_items,
                        apr_pool_t *scratch_pool)
@@ -2491,8 +2457,9 @@ svn_wc__db_base_remove(svn_wc__db_t *db,
   VERIFY_USABLE_WCROOT(wcroot);
 
   SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
-                                     db, keep_as_working, queue_deletes,
-                                     remove_locks, not_present_revision,
+                                     db, keep_as_working,
+                                     mark_not_present, mark_excluded,
+                                     marker_revision,
                                      conflict, work_items, scratch_pool),
                       wcroot);
 
@@ -2843,8 +2810,10 @@ svn_wc__db_base_get_children(const apr_a
                                              scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
-  return gather_repo_children(children, wcroot, local_relpath, 0,
-                              result_pool, scratch_pool);
+  return svn_error_trace(
+              gather_children(children, wcroot, local_relpath,
+                              STMT_SELECT_OP_DEPTH_CHILDREN, 0,
+                              result_pool, scratch_pool));
 }
 
 
@@ -2854,12 +2823,20 @@ svn_wc__db_base_set_dav_cache(svn_wc__db
                               const apr_hash_t *props,
                               apr_pool_t *scratch_pool)
 {
+  svn_wc__db_wcroot_t *wcroot;
+  const char *local_relpath;
   svn_sqlite__stmt_t *stmt;
   int affected_rows;
 
-  SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
-                                 STMT_UPDATE_BASE_NODE_DAV_CACHE,
-                                 scratch_pool));
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
+                              local_abspath, scratch_pool, scratch_pool));
+  VERIFY_USABLE_WCROOT(wcroot);
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_UPDATE_BASE_NODE_DAV_CACHE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
   SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
 
   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
@@ -2881,11 +2858,20 @@ svn_wc__db_base_get_dav_cache(apr_hash_t
                               apr_pool_t *result_pool,
                               apr_pool_t *scratch_pool)
 {
+  svn_wc__db_wcroot_t *wcroot;
+  const char *local_relpath;
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
 
-  SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
-                                 STMT_SELECT_BASE_DAV_CACHE, scratch_pool));
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
+                              local_abspath, scratch_pool, scratch_pool));
+  VERIFY_USABLE_WCROOT(wcroot);
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_BASE_DAV_CACHE));
+
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
   if (!have_row)
     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
@@ -3057,6 +3043,11 @@ svn_wc__db_depth_get_info(svn_wc__db_sta
   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
 }
 
+/* A callback which supplies WCROOTs and LOCAL_RELPATHs. */
+typedef svn_error_t *(*svn_wc__db_txn_callback_t)(void *baton,
+                                          svn_wc__db_wcroot_t *wcroot,
+                                          const char *local_relpath,
+                                          apr_pool_t *scratch_pool);
 
 /* Baton for passing args to with_triggers(). */
 struct with_triggers_baton_t {
@@ -3133,8 +3124,13 @@ with_finalization(svn_wc__db_wcroot_t *w
   svn_error_t *err1;
   svn_error_t *err2;
 
-  err1 = svn_wc__db_with_txn(wcroot, local_relpath, txn_cb, txn_baton,
-                             scratch_pool);
+  err1 = svn_sqlite__begin_savepoint(wcroot->sdb);
+  if (!err1)
+    {
+      err1 = txn_cb(txn_baton, wcroot, local_relpath, scratch_pool);
+
+      err1 = svn_sqlite__finish_savepoint(wcroot->sdb, err1);
+    }
 
   if (err1 == NULL && notify_func != NULL)
     {
@@ -3520,11 +3516,18 @@ db_external_remove(const svn_skel_t *wor
                    apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
+  int affected_rows;
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_DELETE_EXTERNAL));
   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
-  SVN_ERR(svn_sqlite__step_done(stmt));
+  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
+
+  if (!affected_rows)
+    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
+                             _("The node '%s' is not an external."),
+                             path_for_error_message(wcroot, local_relpath,
+                                                    scratch_pool));
 
   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
 
@@ -4007,14 +4010,14 @@ get_moved_to(const char **moved_to_relpa
 /* The body of svn_wc__db_scan_deletion().
  */
 static svn_error_t *
-scan_deletion_txn(const char **base_del_relpath,
-                  const char **moved_to_relpath,
-                  const char **work_del_relpath,
-                  const char **moved_to_op_root_relpath,
-                  svn_wc__db_wcroot_t *wcroot,
-                  const char *local_relpath,
-                  apr_pool_t *result_pool,
-                  apr_pool_t *scratch_pool)
+scan_deletion(const char **base_del_relpath,
+              const char **moved_to_relpath,
+              const char **work_del_relpath,
+              const char **moved_to_op_root_relpath,
+              svn_wc__db_wcroot_t *wcroot,
+              const char *local_relpath,
+              apr_pool_t *result_pool,
+              apr_pool_t *scratch_pool)
 {
   const char *current_relpath = local_relpath;
   svn_sqlite__stmt_t *stmt;
@@ -4038,9 +4041,7 @@ scan_deletion_txn(const char **base_del_
   scan = (moved_to_op_root_relpath || moved_to_relpath);
 
   SVN_ERR(svn_sqlite__get_statement(
-                    &stmt, wcroot->sdb,
-                    scan ? STMT_SELECT_DELETION_INFO_SCAN
-                         : STMT_SELECT_DELETION_INFO));
+                    &stmt, wcroot->sdb, STMT_SELECT_DELETION_INFO));
 
   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
@@ -4161,6 +4162,25 @@ scan_deletion_txn(const char **base_del_
 }
 
 svn_error_t *
+svn_wc__db_scan_deletion_internal(
+              const char **base_del_relpath,
+              const char **moved_to_relpath,
+              const char **work_del_relpath,
+              const char **moved_to_op_root_relpath,
+              svn_wc__db_wcroot_t *wcroot,
+              const char *local_relpath,
+              apr_pool_t *result_pool,
+              apr_pool_t *scratch_pool)
+{
+  return svn_error_trace(
+            scan_deletion(base_del_relpath, moved_to_relpath, work_del_relpath,
+                          moved_to_op_root_relpath,
+                          wcroot, local_relpath,
+                          result_pool, scratch_pool));
+}
+
+
+svn_error_t *
 svn_wc__db_scan_deletion(const char **base_del_abspath,
                          const char **moved_to_abspath,
                          const char **work_del_abspath,
@@ -4182,9 +4202,9 @@ svn_wc__db_scan_deletion(const char **ba
   VERIFY_USABLE_WCROOT(wcroot);
 
   SVN_WC__DB_WITH_TXN(
-    scan_deletion_txn(&base_del_relpath, &moved_to_relpath,
-                      &work_del_relpath, &moved_to_op_root_relpath,
-                      wcroot, local_relpath, result_pool, scratch_pool),
+    scan_deletion(&base_del_relpath, &moved_to_relpath,
+                  &work_del_relpath, &moved_to_op_root_relpath,
+                  wcroot, local_relpath, result_pool, scratch_pool),
     wcroot);
 
   if (base_del_abspath)
@@ -4282,10 +4302,10 @@ get_info_for_copy(apr_int64_t *copyfrom_
     {
       const char *base_del_relpath, *work_del_relpath;
 
-      SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
-                                &work_del_relpath,
-                                NULL, src_wcroot, local_relpath,
-                                scratch_pool, scratch_pool));
+      SVN_ERR(scan_deletion(&base_del_relpath, NULL,
+                            &work_del_relpath,
+                            NULL, src_wcroot, local_relpath,
+                            scratch_pool, scratch_pool));
       if (work_del_relpath)
         {
           const char *op_root_relpath;
@@ -4614,8 +4634,9 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcro
       int src_op_depth;
 
       SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
-      SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
-                                   src_op_depth, scratch_pool, scratch_pool));
+      SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
+                              STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
+                              scratch_pool, scratch_pool));
     }
   else
     children = NULL;
@@ -4839,16 +4860,82 @@ svn_wc__db_op_copy(svn_wc__db_t *db,
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot,
-                                  const char *src_op_relpath,
-                                  int src_op_depth,
-                                  const char *dst_op_relpath,
-                                  svn_skel_t *conflict,
-                                  svn_skel_t *work_items,
-                                  apr_pool_t *scratch_pool)
-{
-  svn_sqlite__stmt_t *stmt, *stmt2;
+/* Remove unneeded actual nodes for svn_wc__db_op_copy_layer_internal */
+static svn_error_t *
+clear_or_remove_actual(svn_wc__db_wcroot_t *wcroot,
+                       const char *local_relpath,
+                       int op_depth,
+                       apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row, shadowed;
+  svn_boolean_t keep_conflict = FALSE;
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_NODE_INFO));
+
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  if (have_row)
+    {
+      svn_wc__db_status_t presence;
+
+      shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
+      presence = svn_sqlite__column_token(stmt, 3, presence_map);
+
+      if (shadowed && presence == svn_wc__db_status_base_deleted)
+        {
+          keep_conflict = TRUE;
+          SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+          if (have_row)
+            shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
+          else
+            shadowed = FALSE;
+        }
+    }
+  else
+    shadowed = FALSE;
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+  if (shadowed)
+    return SVN_NO_ERROR;
+
+  if (keep_conflict)
+    {
+      /* We don't want to accidentally remove delete-delete conflicts */
+      SVN_ERR(svn_sqlite__get_statement(
+                          &stmt, wcroot->sdb,
+                          STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT));
+      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+      SVN_ERR(svn_sqlite__step_done(stmt));
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_DELETE_ACTUAL_EMPTY));
+      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+      SVN_ERR(svn_sqlite__step_done(stmt));
+    }
+  else
+    {
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_DELETE_ACTUAL_NODE));
+      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+      SVN_ERR(svn_sqlite__step_done(stmt));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot,
+                                  const char *src_op_relpath,
+                                  int src_op_depth,
+                                  const char *dst_op_relpath,
+                                  svn_skel_t *conflict,
+                                  svn_skel_t *work_items,
+                                  apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt, *stmt2;
   svn_boolean_t have_row;
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
   int dst_op_depth = relpath_depth(dst_op_relpath);
@@ -4878,15 +4965,12 @@ svn_wc__db_op_copy_layer_internal(svn_wc
     {
       const char *src_relpath;
       const char *dst_relpath;
-      svn_boolean_t exists;
 
       svn_pool_clear(iterpool);
 
       src_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
       dst_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
 
-      exists = !svn_sqlite__column_is_null(stmt, 3);
-
       err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id,
                               src_relpath, src_op_depth,
                               dst_relpath, dst_op_depth,
@@ -4899,21 +4983,11 @@ svn_wc__db_op_copy_layer_internal(svn_wc
       if (err)
         break;
 
-      if (strlen(dst_relpath) > strlen(dst_op_relpath))
+      /* The node can't be deleted where it is added, so extension of
+         an existing shadowing is only interesting 2 levels deep. */
+      if (relpath_depth(dst_relpath) > (dst_op_depth+1))
         {
-          svn_boolean_t added_delete = FALSE;
-          svn_node_kind_t kind = svn_sqlite__column_token(stmt, 1, kind_map);
-
-          /* The op root can't be shadowed, so extension of a parent delete
-             is only needed when the parent can be deleted */
-          if (relpath_depth(dst_relpath) > (dst_op_depth+1))
-            {
-              err = db_extend_parent_delete(&added_delete, wcroot, dst_relpath,
-                                            kind, dst_op_depth, iterpool);
-
-              if (err)
-                break;
-            }
+          svn_boolean_t exists = !svn_sqlite__column_is_null(stmt, 3);
 
           if (exists)
             {
@@ -4921,28 +4995,19 @@ svn_wc__db_op_copy_layer_internal(svn_wc
 
               presence = svn_sqlite__column_token(stmt, 3, presence_map);
 
-              if (presence == svn_wc__db_status_not_present)
+              if (presence != svn_wc__db_status_normal)
                 exists = FALSE;
             }
 
-          /* ### Fails in a few tests... Needs further research */
-          /*SVN_ERR_ASSERT(!(exists && added_delete));*/
-
           if (!exists)
             {
-              svn_boolean_t shadowed;
+              svn_node_kind_t kind = svn_sqlite__column_token(stmt, 1, 
kind_map);
 
-              shadowed = svn_sqlite__column_int(stmt, 4);
+              err = db_extend_parent_delete(wcroot, dst_relpath,
+                                            kind, dst_op_depth, iterpool);
 
-              /*if (!shadowed && !added_delete)
-                {
-                  err = svn_error_createf(
-                              SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
-                              _("Node '%s' was unexpectedly added unshadowed"),
-                                path_for_error_message(wcroot, dst_relpath,
-                                                       iterpool));
-                  break;
-                }*/
+              if (err)
+                break;
             }
         }
 
@@ -4992,6 +5057,12 @@ svn_wc__db_op_copy_layer_internal(svn_wc
         err = svn_sqlite__step_done(stmt2);
 
       /* stmt2 is reset (never modified or by step_done) */
+      if (err)
+        break;
+
+      /* Delete ACTUAL information about this node that we just deleted */
+      err = clear_or_remove_actual(wcroot, dst_relpath, dst_op_depth,
+                                   scratch_pool);
 
       if (err)
         break;
@@ -5009,8 +5080,6 @@ svn_wc__db_op_copy_layer_internal(svn_wc
 
   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
 
-  /* ### TODO: Did we handle ACTUAL as intended? */
-  
   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
 
   if (conflict)
@@ -5427,8 +5496,9 @@ db_op_copy_shadowed_layer(svn_wc__db_wcr
       return SVN_NO_ERROR;
     }
 
-  SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
-                               src_op_depth, scratch_pool, iterpool));
+  SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
+                          STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
+                          scratch_pool, iterpool));
 
   for (i = 0; i < children->nelts; i++)
     {
@@ -5615,8 +5685,8 @@ svn_wc__db_op_copy_dir(svn_wc__db_t *db,
                        const char *original_uuid,
                        svn_revnum_t original_revision,
                        const apr_array_header_t *children,
-                       svn_boolean_t is_move,
                        svn_depth_t depth,
+                       svn_boolean_t is_move,
                        const svn_skel_t *conflict,
                        const svn_skel_t *work_items,
                        apr_pool_t *scratch_pool)
@@ -5643,11 +5713,6 @@ svn_wc__db_op_copy_dir(svn_wc__db_t *db,
   iwb.presence = svn_wc__db_status_normal;
   iwb.kind = svn_node_dir;
 
-  iwb.props = props;
-  iwb.changed_rev = changed_rev;
-  iwb.changed_date = changed_date;
-  iwb.changed_author = changed_author;
-
   if (original_root_url != NULL)
     {
       SVN_ERR(create_repos_id(&iwb.original_repos_id,
@@ -5655,6 +5720,11 @@ svn_wc__db_op_copy_dir(svn_wc__db_t *db,
                               wcroot->sdb, scratch_pool));
       iwb.original_repos_relpath = original_repos_relpath;
       iwb.original_revnum = original_revision;
+
+      iwb.props = props;
+      iwb.changed_rev = changed_rev;
+      iwb.changed_date = changed_date;
+      iwb.changed_author = changed_author;
     }
 
   /* ### Should we do this inside the transaction? */
@@ -5723,11 +5793,6 @@ svn_wc__db_op_copy_file(svn_wc__db_t *db
   iwb.presence = svn_wc__db_status_normal;
   iwb.kind = svn_node_file;
 
-  iwb.props = props;
-  iwb.changed_rev = changed_rev;
-  iwb.changed_date = changed_date;
-  iwb.changed_author = changed_author;
-
   if (original_root_url != NULL)
     {
       SVN_ERR(create_repos_id(&iwb.original_repos_id,
@@ -5735,6 +5800,11 @@ svn_wc__db_op_copy_file(svn_wc__db_t *db
                               wcroot->sdb, scratch_pool));
       iwb.original_repos_relpath = original_repos_relpath;
       iwb.original_revnum = original_revision;
+
+      iwb.props = props;
+      iwb.changed_rev = changed_rev;
+      iwb.changed_date = changed_date;
+      iwb.changed_author = changed_author;
     }
 
   /* ### Should we do this inside the transaction? */
@@ -5777,6 +5847,7 @@ svn_wc__db_op_copy_symlink(svn_wc__db_t
                            const char *original_uuid,
                            svn_revnum_t original_revision,
                            const char *target,
+                           svn_boolean_t is_move,
                            const svn_skel_t *conflict,
                            const svn_skel_t *work_items,
                            apr_pool_t *scratch_pool)
@@ -5801,11 +5872,6 @@ svn_wc__db_op_copy_symlink(svn_wc__db_t
   iwb.presence = svn_wc__db_status_normal;
   iwb.kind = svn_node_symlink;
 
-  iwb.props = props;
-  iwb.changed_rev = changed_rev;
-  iwb.changed_date = changed_date;
-  iwb.changed_author = changed_author;
-  iwb.moved_here = FALSE;
 
   if (original_root_url != NULL)
     {
@@ -5814,6 +5880,11 @@ svn_wc__db_op_copy_symlink(svn_wc__db_t
                               wcroot->sdb, scratch_pool));
       iwb.original_repos_relpath = original_repos_relpath;
       iwb.original_revnum = original_revision;
+
+      iwb.props = props;
+      iwb.changed_rev = changed_rev;
+      iwb.changed_date = changed_date;
+      iwb.changed_author = changed_author;
     }
 
   /* ### Should we do this inside the transaction? */
@@ -5823,6 +5894,8 @@ svn_wc__db_op_copy_symlink(svn_wc__db_t
                             wcroot, local_relpath, scratch_pool));
 
   iwb.target = target;
+  iwb.moved_here = is_move && (parent_op_depth == 0 ||
+                               iwb.op_depth == parent_op_depth);
 
   iwb.work_items = work_items;
   iwb.conflict = conflict;
@@ -6032,27 +6105,39 @@ svn_wc__db_global_record_fileinfo(svn_wc
  * props; to indicate no properties when the pristine has some props,
  * PROPS must be an empty hash. */
 static svn_error_t *
-set_actual_props(apr_int64_t wc_id,
+set_actual_props(svn_wc__db_wcroot_t *wcroot,
                  const char *local_relpath,
                  apr_hash_t *props,
-                 svn_sqlite__db_t *db,
                  apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
   int affected_rows;
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPDATE_ACTUAL_PROPS));
-  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_UPDATE_ACTUAL_PROPS));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
   SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
 
   if (affected_rows == 1 || !props)
-    return SVN_NO_ERROR; /* We are done */
+    {
+      /* Perhaps the entire ACTUAL record is unneeded now? */
+      if (!props && affected_rows)
+        {
+          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                            STMT_DELETE_ACTUAL_EMPTY));
+          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+          SVN_ERR(svn_sqlite__step_done(stmt));
+        }
+
+      return SVN_NO_ERROR; /* We are done */
+    }
 
   /* We have to insert a row in ACTUAL */
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_ACTUAL_PROPS));
-  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_INSERT_ACTUAL_PROPS));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
   if (*local_relpath != '\0')
     SVN_ERR(svn_sqlite__bind_text(stmt, 3,
                                   svn_relpath_dirname(local_relpath,
@@ -6068,8 +6153,7 @@ svn_wc__db_op_set_props_internal(svn_wc_
                                  svn_boolean_t clear_recorded_info,
                                  apr_pool_t *scratch_pool)
 {
-  SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath,
-                           props, wcroot->sdb, scratch_pool));
+  SVN_ERR(set_actual_props(wcroot, local_relpath, props, scratch_pool));
 
   if (clear_recorded_info)
     {
@@ -6348,7 +6432,7 @@ set_changelist_txn(void *baton,
   if (scb->new_changelist)
     {
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_INSERT_ACTUAL_EMPTIES));
+                                        STMT_INSERT_ACTUAL_EMPTIES_FILES));
       SVN_ERR(svn_sqlite__step_done(stmt));
     }
 
@@ -6565,15 +6649,15 @@ svn_wc__db_op_mark_conflict(svn_wc__db_t
 
 /* The body of svn_wc__db_op_mark_resolved().
  */
-static svn_error_t *
-db_op_mark_resolved(svn_wc__db_wcroot_t *wcroot,
-                    const char *local_relpath,
-                    svn_wc__db_t *db,
-                    svn_boolean_t resolved_text,
-                    svn_boolean_t resolved_props,
-                    svn_boolean_t resolved_tree,
-                    const svn_skel_t *work_items,
-                    apr_pool_t *scratch_pool)
+svn_error_t *
+svn_wc__db_op_mark_resolved_internal(svn_wc__db_wcroot_t *wcroot,
+                                     const char *local_relpath,
+                                     svn_wc__db_t *db,
+                                     svn_boolean_t resolved_text,
+                                     svn_boolean_t resolved_props,
+                                     svn_boolean_t resolved_tree,
+                                     const svn_skel_t *work_items,
+                                     apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
@@ -6671,7 +6755,8 @@ svn_wc__db_op_mark_resolved(svn_wc__db_t
   VERIFY_USABLE_WCROOT(wcroot);
 
   SVN_WC__DB_WITH_TXN(
-    db_op_mark_resolved(wcroot, local_relpath, db,
+    svn_wc__db_op_mark_resolved_internal(
+                        wcroot, local_relpath, db,
                         resolved_text, resolved_props, resolved_tree,
                         work_items, scratch_pool),
     wcroot);
@@ -6739,6 +6824,7 @@ op_revert_txn(void *baton,
   int affected_rows;
   const char *moved_to;
   int op_depth_increased = 0;
+  int op_depth_below;
   svn_skel_t *conflict;
 
   /* ### Similar structure to op_revert_recursive_txn, should they be
@@ -6786,6 +6872,13 @@ op_revert_txn(void *baton,
   op_depth = svn_sqlite__column_int(stmt, 0);
   moved_here = svn_sqlite__column_boolean(stmt, 15);
   moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
+
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  if (have_row)
+    op_depth_below = svn_sqlite__column_int(stmt, 0);
+  else
+    op_depth_below = -1;
+
   SVN_ERR(svn_sqlite__reset(stmt));
 
   if (moved_to)
@@ -6796,7 +6889,7 @@ op_revert_txn(void *baton,
     }
   else
     {
-      SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, wcroot,
+      SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, wcroot,
                                                 local_relpath,
                                                 scratch_pool, scratch_pool));
     }
@@ -6873,7 +6966,7 @@ op_revert_txn(void *baton,
               || reason == svn_wc_conflict_reason_replaced)
             {
               SVN_ERR(svn_wc__db_op_raise_moved_away_internal(
-                          wcroot, local_relpath, op_depth+1, db,
+                          wcroot, local_relpath, op_depth_below, db,
                           operation, action,
                           (locations && locations->nelts > 0)
                             ? APR_ARRAY_IDX(locations, 0,
@@ -7024,7 +7117,7 @@ op_revert_recursive_txn(void *baton,
                         STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
       SVN_ERR(svn_sqlite__step_done(stmt));
-      
+
       SVN_ERR(svn_sqlite__get_statement(
                         &stmt, wcroot->sdb,
                         STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
@@ -7397,9 +7490,6 @@ remove_node_txn(svn_boolean_t *left_chan
                 svn_wc__db_t *db,
                 svn_boolean_t destroy_wc,
                 svn_boolean_t destroy_changes,
-                svn_revnum_t not_present_rev,
-                svn_wc__db_status_t not_present_status,
-                svn_node_kind_t not_present_kind,
                 const svn_skel_t *conflict,
                 const svn_skel_t *work_items,
                 svn_cancel_func_t cancel_func,
@@ -7408,9 +7498,6 @@ remove_node_txn(svn_boolean_t *left_chan
 {
   svn_sqlite__stmt_t *stmt;
 
-  apr_int64_t repos_id;
-  const char *repos_relpath;
-
   /* Note that unlike many similar functions it is a valid scenario for this
      function to be called on a wcroot! */
 
@@ -7420,15 +7507,6 @@ remove_node_txn(svn_boolean_t *left_chan
   if (left_changes)
     *left_changes = FALSE;
 
-  /* Need info for not_present node? */
-  if (SVN_IS_VALID_REVNUM(not_present_rev))
-    SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
-                                              &repos_relpath, &repos_id,
-                                              NULL, NULL, NULL, NULL, NULL,
-                                              NULL, NULL, NULL, NULL, NULL,
-                                              wcroot, local_relpath,
-                                              scratch_pool, scratch_pool));
-
   if (destroy_wc
       && (!destroy_changes || *local_relpath == '\0'))
     {
@@ -7625,26 +7703,6 @@ remove_node_txn(svn_boolean_t *left_chan
                                          local_relpath));
   SVN_ERR(svn_sqlite__step_done(stmt));
 
-  /* Should we leave a not-present node? */
-  if (SVN_IS_VALID_REVNUM(not_present_rev))
-    {
-      insert_base_baton_t ibb;
-      blank_ibb(&ibb);
-
-      ibb.repos_id = repos_id;
-
-      SVN_ERR_ASSERT(not_present_status == svn_wc__db_status_not_present
-                     || not_present_status == svn_wc__db_status_excluded);
-
-      ibb.status = not_present_status;
-      ibb.kind = not_present_kind;
-
-      ibb.repos_relpath = repos_relpath;
-      ibb.revision = not_present_rev;
-
-      SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
-    }
-
   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
   if (conflict)
     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
@@ -7659,9 +7717,6 @@ svn_wc__db_op_remove_node(svn_boolean_t
                           const char *local_abspath,
                           svn_boolean_t destroy_wc,
                           svn_boolean_t destroy_changes,
-                          svn_revnum_t not_present_revision,
-                          svn_wc__db_status_t not_present_status,
-                          svn_node_kind_t not_present_kind,
                           const svn_skel_t *conflict,
                           const svn_skel_t *work_items,
                           svn_cancel_func_t cancel_func,
@@ -7680,8 +7735,7 @@ svn_wc__db_op_remove_node(svn_boolean_t
   SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
                                       wcroot, local_relpath, db,
                                       destroy_wc, destroy_changes,
-                                      not_present_revision, not_present_status,
-                                      not_present_kind, conflict, work_items,
+                                      conflict, work_items,
                                       cancel_func, cancel_baton, scratch_pool),
                       wcroot);
 
@@ -9008,10 +9062,8 @@ read_info(svn_wc__db_status_t *status,
     err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
 
   if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
-    err = svn_error_quick_wrap(err,
-                               apr_psprintf(scratch_pool,
-                                            _("Error reading node '%s'"),
-                                            local_relpath));
+    err = svn_error_quick_wrapf(err, _("Error reading node '%s'"),
+                                local_relpath);
 
   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
 
@@ -9131,6 +9183,27 @@ is_wclocked(svn_boolean_t *locked,
             const char *dir_relpath,
             apr_pool_t *scratch_pool);
 
+/* Helper for read_children_info and single variant */
+static svn_error_t *
+find_conflict_descendants(svn_boolean_t *conflict_exists,
+                          svn_wc__db_wcroot_t *wcroot,
+                          const char *local_relpath,
+                          apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt;
+
+  /* Only used on files, so certainly not wcroot*/
+  assert(local_relpath[0] != '\0');
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_FIND_CONFLICT_DESCENDANT));
+
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__step(conflict_exists, stmt));
+
+  return svn_error_trace(svn_sqlite__reset(stmt));
+}
+
 /* What we really want to store about a node.  This relies on the
    offset of svn_wc__db_info_t being zero. */
 struct read_children_info_item_t
@@ -9138,6 +9211,7 @@ struct read_children_info_item_t
   struct svn_wc__db_info_t info;
   int op_depth;
   int nr_layers;
+  svn_boolean_t was_dir;
 };
 
 /* Implementation of svn_wc__db_read_children_info */
@@ -9187,7 +9261,7 @@ read_children_info(svn_wc__db_wcroot_t *
       op_depth = svn_sqlite__column_int(stmt, 0);
 
       /* Do we have new or better information? */
-      if (new_child || op_depth > child_item->op_depth)
+      if (new_child)
         {
           struct svn_wc__db_info_t *child = &child_item->info;
           child_item->op_depth = op_depth;
@@ -9268,6 +9342,8 @@ read_children_info(svn_wc__db_wcroot_t *
             child->depth = svn_depth_unknown;
           else
             {
+              child->has_descendants = TRUE;
+              child_item->was_dir = TRUE;
               child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
                                                            svn_depth_unknown);
               if (new_child)
@@ -9310,6 +9386,17 @@ read_children_info(svn_wc__db_wcroot_t *
           if (new_child)
             svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
         }
+      else if (!child_item->was_dir
+               && svn_sqlite__column_token(stmt, 4, kind_map) == svn_node_dir)
+        {
+          child_item->was_dir = TRUE;
+
+          err = find_conflict_descendants(&child_item->info.has_descendants,
+                                          wcroot, child_relpath,
+                                          scratch_pool);
+          if (err)
+            SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
+        }
 
       if (op_depth == 0)
         {
@@ -9347,8 +9434,8 @@ read_children_info(svn_wc__db_wcroot_t *
                                                            moved_to_relpath,
                                                            result_pool);
 
-              shadow_op_relpath = svn_relpath_limit(child_relpath, op_depth,
-                                                    scratch_pool);
+              shadow_op_relpath = svn_relpath_prefix(child_relpath, op_depth,
+                                                     scratch_pool);
 
               moved_to->shadow_op_root_abspath =
                         svn_dirent_join(wcroot->abspath, shadow_op_relpath,
@@ -9537,8 +9624,8 @@ read_single_info(const struct svn_wc__db
                                                    moved_to_relpath,
                                                    result_pool);
 
-          cur_relpath = svn_relpath_limit(local_relpath, op_depth,
-                                          scratch_pool);
+          cur_relpath = svn_relpath_prefix(local_relpath, op_depth,
+                                           scratch_pool);
 
           move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
                                                          cur_relpath,
@@ -9618,6 +9705,12 @@ read_single_info(const struct svn_wc__db
   if (!base_tree_only && mtb->kind == svn_node_dir)
     SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
 
+  if (mtb->kind == svn_node_dir)
+    mtb->has_descendants = TRUE;
+  else
+    SVN_ERR(find_conflict_descendants(&mtb->has_descendants,
+                                      wcroot, local_relpath, scratch_pool));
+
   *info = mtb;
 
   return SVN_NO_ERROR;
@@ -9812,7 +9905,7 @@ svn_wc__db_read_pristine_info(svn_wc__db
 }
 
 svn_error_t *
-svn_wc__db_read_children_walker_info(apr_hash_t **nodes,
+svn_wc__db_read_children_walker_info(const apr_array_header_t **items,
                                      svn_wc__db_t *db,
                                      const char *dir_abspath,
                                      apr_pool_t *result_pool,
@@ -9822,6 +9915,7 @@ svn_wc__db_read_children_walker_info(apr
   const char *dir_relpath;
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
+  apr_array_header_t *nodes;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
 
@@ -9835,16 +9929,18 @@ svn_wc__db_read_children_walker_info(apr
   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
 
-  *nodes = apr_hash_make(result_pool);
+  nodes = apr_array_make(result_pool, 16,
+                          sizeof(struct svn_wc__db_walker_info_t *));
   while (have_row)
     {
       struct svn_wc__db_walker_info_t *child;
       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
-      const char *name = svn_relpath_basename(child_relpath, NULL);
+      const char *name = svn_relpath_basename(child_relpath, result_pool);
       int op_depth = svn_sqlite__column_int(stmt, 1);
       svn_error_t *err;
 
       child = apr_palloc(result_pool, sizeof(*child));
+      child->name = name;
       child->status = svn_sqlite__column_token(stmt, 2, presence_map);
       if (op_depth > 0)
         {
@@ -9853,13 +9949,16 @@ svn_wc__db_read_children_walker_info(apr
             SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
         }
       child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
-      svn_hash_sets(*nodes, apr_pstrdup(result_pool, name), child);
+
+      APR_ARRAY_PUSH(nodes, struct svn_wc__db_walker_info_t *) = child;
 
       SVN_ERR(svn_sqlite__step(&have_row, stmt));
     }
 
   SVN_ERR(svn_sqlite__reset(stmt));
 
+  *items = nodes;
+
   return SVN_NO_ERROR;
 }
 
@@ -9943,68 +10042,48 @@ svn_wc__db_read_node_install_info(const
 
 
 
-/* The body of svn_wc__db_read_url().
+/* The body of svn_wc__db_read_repos_info().
  */
 static svn_error_t *
-read_url_txn(const char **url,
-             svn_wc__db_wcroot_t *wcroot,
-             const char *local_relpath,
-             apr_pool_t *result_pool,
-             apr_pool_t *scratch_pool)
+db_read_repos_info(svn_revnum_t *revision,
+                   const char **repos_relpath,
+                   apr_int64_t *repos_id,
+                   svn_wc__db_wcroot_t *wcroot,
+                   const char *local_relpath,
+                   apr_pool_t *result_pool,
+                   apr_pool_t *scratch_pool)
 {
   svn_wc__db_status_t status;
-  const char *repos_relpath;
-  const char *repos_root_url;
-  apr_int64_t repos_id;
-  svn_boolean_t have_base;
 
-  SVN_ERR(read_info(&status, NULL, NULL, &repos_relpath, &repos_id, NULL,
+  SVN_ERR(read_info(&status, NULL, revision, repos_relpath, repos_id, NULL,
                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                    &have_base, NULL, NULL,
-                    wcroot, local_relpath, scratch_pool, scratch_pool));
+                    NULL, NULL, NULL,
+                    wcroot, local_relpath, result_pool, scratch_pool));
 
-  if (repos_relpath == NULL)
+  if ((repos_relpath && !*repos_relpath)
+      || (repos_id && *repos_id == INVALID_REPOS_ID))
     {
       if (status == svn_wc__db_status_added)
         {
-          SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, NULL,
+          SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
                                 NULL, NULL, NULL, NULL, NULL,
                                 wcroot, local_relpath,
-                                scratch_pool, scratch_pool));
+                                result_pool, scratch_pool));
         }
       else if (status == svn_wc__db_status_deleted)
         {
           const char *base_del_relpath;
           const char *work_del_relpath;
 
-          SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
-                                    &work_del_relpath,
-                                    NULL, wcroot,
-                                    local_relpath,
-                                    scratch_pool,
-                                    scratch_pool));
-
-          if (base_del_relpath)
-            {
-              SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
-                                                        &repos_relpath,
-                                                        &repos_id,
-                                                        NULL, NULL, NULL,
-                                                        NULL, NULL, NULL,
-                                                        NULL, NULL, NULL, NULL,
-                                                        wcroot,
-                                                        base_del_relpath,
-                                                        scratch_pool,
-                                                        scratch_pool));
+          SVN_ERR(scan_deletion(&base_del_relpath, NULL,
+                                &work_del_relpath,
+                                NULL, wcroot,
+                                local_relpath,
+                                scratch_pool,
+                                scratch_pool));
 
-              repos_relpath = svn_relpath_join(
-                                    repos_relpath,
-                                    svn_dirent_skip_ancestor(base_del_relpath,
-                                                             local_relpath),
-                                    scratch_pool);
-            }
-          else
+          if (work_del_relpath)
             {
               /* The parent of the WORKING delete, must be an addition */
               const char *work_relpath = NULL;
@@ -10017,34 +10096,57 @@ read_url_txn(const char **url,
               work_relpath = svn_relpath_dirname(work_del_relpath,
                                                  scratch_pool);
 
-              SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id,
+              SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id,
                                     NULL, NULL, NULL, NULL, NULL, NULL,
                                     wcroot, work_relpath,
                                     scratch_pool, scratch_pool));
 
-              repos_relpath = svn_relpath_join(
-                                    repos_relpath,
+              if (repos_relpath)
+                *repos_relpath = svn_relpath_join(
+                                    *repos_relpath,
                                     svn_dirent_skip_ancestor(work_relpath,
                                                              local_relpath),
-                                    scratch_pool);
+                                    result_pool);
+            }
+          else
+            {
+              SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, revision,
+                                                        repos_relpath,
+                                                        repos_id,
+                                                        NULL, NULL, NULL,
+                                                        NULL, NULL, NULL,
+                                                        NULL, NULL, NULL, NULL,
+                                                        wcroot,
+                                                        base_del_relpath,
+                                                        scratch_pool,
+                                                        scratch_pool));
+
+              if (repos_relpath)
+                *repos_relpath = svn_relpath_join(
+                                    *repos_relpath,
+                                    svn_dirent_skip_ancestor(base_del_relpath,
+                                                             local_relpath),
+                                    result_pool);
             }
         }
       else if (status == svn_wc__db_status_excluded)
         {
           const char *parent_relpath;
           const char *name;
-          const char *url2;
 
-          /* Set 'url' to the *full URL* of the parent WC dir,
-           * and 'name' to the *single path component* that is the
-           * basename of this WC directory, so that joining them will result
-           * in the correct full URL. */
+          /* A BASE excluded would have had repository information, so
+             we have a working exclude, which must be below an addition */
+
           svn_relpath_split(&parent_relpath, &name, local_relpath,
                             scratch_pool);
-          SVN_ERR(read_url_txn(&url2, wcroot, parent_relpath,
-                               scratch_pool, scratch_pool));
+          SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
+                                NULL, NULL, NULL, NULL, NULL,
+                                wcroot, parent_relpath,
+                                scratch_pool, scratch_pool));
 
-          *url = svn_path_url_add_component2(url2, name, result_pool);
+          if (repos_relpath)
+            *repos_relpath = svn_relpath_join(*repos_relpath, name,
+                                              result_pool);
 
           return SVN_NO_ERROR;
         }
@@ -10056,26 +10158,23 @@ read_url_txn(const char **url,
         }
     }
 
-  SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot,
-                                      repos_id, scratch_pool));
-
-  SVN_ERR_ASSERT(repos_root_url != NULL && repos_relpath != NULL);
-  *url = svn_path_url_add_component2(repos_root_url, repos_relpath,
-                                     result_pool);
-
   return SVN_NO_ERROR;
 }
 
 
 svn_error_t *
-svn_wc__db_read_url(const char **url,
-                    svn_wc__db_t *db,
-                    const char *local_abspath,
-                    apr_pool_t *result_pool,
-                    apr_pool_t *scratch_pool)
+svn_wc__db_read_repos_info(svn_revnum_t *revision,
+                           const char **repos_relpath,
+                           const char **repos_root_url,
+                           const char **repos_uuid,
+                           svn_wc__db_t *db,
+                           const char *local_abspath,
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool)
 {
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
+  apr_int64_t repos_id = INVALID_REPOS_ID;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
@@ -10084,9 +10183,17 @@ svn_wc__db_read_url(const char **url,
                                                 scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
-  SVN_WC__DB_WITH_TXN(read_url_txn(url, wcroot, local_relpath,
-                                   result_pool, scratch_pool),
-                      wcroot);
+  SVN_WC__DB_WITH_TXN4(db_read_repos_info(revision, repos_relpath,
+                                          (repos_root_url || repos_uuid)
+                                            ? &repos_id : NULL,
+                                          wcroot, local_relpath,
+                                          result_pool, scratch_pool),
+                       svn_wc__db_fetch_repos_info(repos_root_url,
+                                                   repos_uuid,
+                                                   wcroot, repos_id,
+                                                   result_pool),
+                       SVN_NO_ERROR, SVN_NO_ERROR,
+                       wcroot);
 
   return SVN_NO_ERROR;
 }
@@ -10928,19 +11035,45 @@ svn_wc__db_read_children_of_working_node
                                              scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
-  return gather_children2(children, wcroot, local_relpath,
-                          result_pool, scratch_pool);
+  return svn_error_trace(
+          gather_children(children, wcroot, local_relpath,
+                          STMT_SELECT_WORKING_CHILDREN, -1,
+                          result_pool, scratch_pool));
 }
 
-/* Helper for svn_wc__db_node_check_replace().
- */
-static svn_error_t *
-check_replace_txn(svn_boolean_t *is_replace_root_p,
-                  svn_boolean_t *base_replace_p,
-                  svn_boolean_t *is_replace_p,
-                  svn_wc__db_wcroot_t *wcroot,
-                  const char *local_relpath,
-                  apr_pool_t *scratch_pool)
+svn_error_t *
+svn_wc__db_base_read_not_present_children(
+                                const apr_array_header_t **children,
+                                svn_wc__db_t *db,
+                                const char *local_abspath,
+                                apr_pool_t *result_pool,
+                                apr_pool_t *scratch_pool)
+{
+  svn_wc__db_wcroot_t *wcroot;
+  const char *local_relpath;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
+                                             local_abspath,
+                                             scratch_pool, scratch_pool));
+  VERIFY_USABLE_WCROOT(wcroot);
+
+  return svn_error_trace(
+          gather_children(children, wcroot, local_relpath,
+                          STMT_SELECT_BASE_NOT_PRESENT_CHILDREN, -1,
+                          result_pool, scratch_pool));
+}
+
+/* Helper for svn_wc__db_node_check_replace().
+ */
+static svn_error_t *
+check_replace_txn(svn_boolean_t *is_replace_root_p,
+                  svn_boolean_t *base_replace_p,
+                  svn_boolean_t *is_replace_p,
+                  svn_wc__db_wcroot_t *wcroot,
+                  const char *local_relpath,
+                  apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
@@ -11116,6 +11249,7 @@ svn_wc__db_read_children(const apr_array
   VERIFY_USABLE_WCROOT(wcroot);
 
   return gather_children(children, wcroot, local_relpath,
+                         STMT_SELECT_NODE_CHILDREN, -1,
                          result_pool, scratch_pool);
 }
 
@@ -11174,11 +11308,11 @@ relocate_txn(svn_wc__db_wcroot_t *wcroot
         {
           const char *work_del_relpath;
 
-          SVN_ERR(scan_deletion_txn(NULL, NULL,
-                                    &work_del_relpath, NULL,
-                                    wcroot, local_dir_relpath,
-                                    scratch_pool,
-                                    scratch_pool));
+          SVN_ERR(scan_deletion(NULL, NULL,
+                                &work_del_relpath, NULL,
+                                wcroot, local_dir_relpath,
+                                scratch_pool,
+                                scratch_pool));
           if (work_del_relpath)
             {
               /* Deleted within a copy/move */
@@ -11266,90 +11400,100 @@ svn_wc__db_global_relocate(svn_wc__db_t
 }
 
 
-/* Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
+/* Helper for commit_node()
+   Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
    (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
    its parent's BASE row if not. In the latter case, error if the parent
    BASE row does not exist.  */
 static svn_error_t *
-determine_repos_info(apr_int64_t *repos_id,
-                     const char **repos_relpath,
-                     svn_wc__db_wcroot_t *wcroot,
-                     const char *local_relpath,
-                     apr_pool_t *result_pool,
-                     apr_pool_t *scratch_pool)
+determine_commit_repos_info(apr_int64_t *repos_id,
+                            const char **repos_relpath,
+                            svn_wc__db_wcroot_t *wcroot,
+                            const char *local_relpath,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
-  const char *repos_parent_relpath;
-  const char *local_parent_relpath, *name;
-
-  /* ### is it faster to fetch fewer columns? */
+  int op_depth;
 
   /* Prefer the current node's repository information.  */
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_SELECT_BASE_NODE));
+                                    STMT_SELECT_NODE_INFO));
   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
 
-  if (have_row)
+  if (!have_row)
+    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
+                             svn_sqlite__reset(stmt),
+                             _("The node '%s' was not found."),
+                             path_for_error_message(wcroot, local_relpath,
+                                                    scratch_pool));
+
+  op_depth = svn_sqlite__column_int(stmt, 0);
+
+  if (op_depth > 0)
     {
-      SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 0));
-      SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
+      svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 3,
+                                                              presence_map);
 
-      *repos_id = svn_sqlite__column_int64(stmt, 0);
-      *repos_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
+      if (presence == svn_wc__db_status_base_deleted)
+        {
+          SVN_ERR(svn_sqlite__step_row(stmt)); /* There must be a row */
+          op_depth = svn_sqlite__column_int(stmt, 0);
+        }
+      else
+        {
+          const char *parent_repos_relpath;
+          const char *parent_relpath;
+          const char *name;
 
-      return svn_error_trace(svn_sqlite__reset(stmt));
-    }
+          SVN_ERR(svn_sqlite__reset(stmt));
 
-  SVN_ERR(svn_sqlite__reset(stmt));
+          /* The repository relative path of an add/copy is based on its
+             ancestor, not on the shadowed base layer.
 
-  /* This was a child node within this wcroot. We want to look at the
-     BASE node of the directory.  */
-  svn_relpath_split(&local_parent_relpath, &name, local_relpath, scratch_pool);
+             As this function is only used from the commit processing we know
+             the parent directory has only a BASE row, so we can just obtain
+             the information directly by recursing (once!)  */
 
-  /* The REPOS_ID will be the same (### until we support mixed-repos)  */
-  SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
-                                            &repos_parent_relpath, repos_id,
-                                            NULL, NULL, NULL, NULL, NULL,
-                                            NULL, NULL, NULL, NULL, NULL,
-                                            wcroot, local_parent_relpath,
-                                            scratch_pool, scratch_pool));
+          svn_relpath_split(&parent_relpath, &name, local_relpath,
+                            scratch_pool);
 
-  *repos_relpath = svn_relpath_join(repos_parent_relpath, name, result_pool);
+          SVN_ERR(determine_commit_repos_info(repos_id, &parent_repos_relpath,
+                                              wcroot, parent_relpath,
+                                              scratch_pool, scratch_pool));
 
-  return SVN_NO_ERROR;
-}
+          *repos_relpath = svn_relpath_join(parent_repos_relpath, name,
+                                            result_pool);
+          return SVN_NO_ERROR;
+        }
+    }
 
-/* Helper for svn_wc__db_global_commit()
 
-   Makes local_relpath and all its descendants at the same op-depth represent
-   the copy origin repos_id:repos_relpath@revision.
+  SVN_ERR_ASSERT(op_depth == 0); /* And that row must be BASE */
 
-   This code is only valid to fix-up a move from an old location, to a new
-   location during a commit.
+  SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
+  SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 2));
+
+  *repos_id = svn_sqlite__column_int64(stmt, 1);
+  *repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
+
+  return svn_error_trace(svn_sqlite__reset(stmt));
+}
 
-   Assumptions:
-     * local_relpath is not the working copy root (can't be moved)
-     * repos_relpath is not the repository root (can't be moved)
-   */
 static svn_error_t *
-moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
+moved_descendant_collect(apr_hash_t **map,
+                        svn_wc__db_wcroot_t *wcroot,
                         const char *local_relpath,
                         int op_depth,
-                        apr_int64_t repos_id,
-                        const char *repos_relpath,
-                        svn_revnum_t revision,
+                        apr_pool_t *result_pool,
                         apr_pool_t *scratch_pool)
 {
-  apr_hash_t *children;
-  apr_pool_t *iterpool;
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
-  apr_hash_index_t *hi;
 
-  SVN_ERR_ASSERT(*local_relpath != '\0'
-                 && *repos_relpath != '\0');
+  *map = NULL;
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_SELECT_MOVED_DESCENDANTS_SRC));
@@ -11361,21 +11505,56 @@ moved_descendant_commit(svn_wc__db_wcroo
   if (! have_row)
     return svn_error_trace(svn_sqlite__reset(stmt));
 
-  children = apr_hash_make(scratch_pool);
-
-  /* First, obtain all moved descendants */
-  /* To keep error handling simple, first cache them in a hashtable */
+  /* Find all moved descendants. Key them on target, because that is
+     always unique */
   while (have_row)
     {
-      const char *src_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
-      const char *to_relpath = svn_sqlite__column_text(stmt, 4, scratch_pool);
+      const char *src_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
+      const char *to_relpath = svn_sqlite__column_text(stmt, 4, result_pool);
+
+      if (!*map)
+        *map = apr_hash_make(result_pool);
 
-      svn_hash_sets(children, src_relpath, to_relpath);
+      svn_hash_sets(*map, to_relpath, src_relpath);
 
       SVN_ERR(svn_sqlite__step(&have_row, stmt));
     }
   SVN_ERR(svn_sqlite__reset(stmt));
 
+  return SVN_NO_ERROR;
+}
+
+/* Helper for svn_wc__db_global_commit()
+
+   Makes local_relpath and all its descendants at the same op-depth represent
+   the copy origin repos_id:repos_relpath@revision.
+
+   This code is only valid to fix-up a move from an old location, to a new
+   location during a commit.
+
+   Assumptions:
+     * local_relpath is not the working copy root (can't be moved)
+     * repos_relpath is not the repository root (can't be moved)
+ */
+static svn_error_t *
+moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
+                        const char *local_relpath,
+                        apr_int64_t repos_id,
+                        const char *repos_relpath,
+                        svn_revnum_t revision,
+                        apr_hash_t *children,
+                        apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool;
+  svn_sqlite__stmt_t *stmt;
+  apr_hash_index_t *hi;
+
+  SVN_ERR_ASSERT(*local_relpath != '\0'
+                 && *repos_relpath != '\0');
+
+  if (!children)
+    return SVN_NO_ERROR;
+
   /* Then update them */
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_COMMIT_UPDATE_ORIGIN));
@@ -11383,11 +11562,12 @@ moved_descendant_commit(svn_wc__db_wcroo
   iterpool = svn_pool_create(scratch_pool);
   for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
     {
-      const char *src_relpath = apr_hash_this_key(hi);
-      const char *to_relpath = apr_hash_this_val(hi);
+      const char *src_relpath = apr_hash_this_val(hi);
+      const char *to_relpath = apr_hash_this_key(hi);
       const char *new_repos_relpath;
       int to_op_depth = relpath_depth(to_relpath);
       int affected;
+      apr_hash_t *map;
 
       svn_pool_clear(iterpool);
 
@@ -11414,9 +11594,11 @@ moved_descendant_commit(svn_wc__db_wcroo
       SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
 #endif
 
-      SVN_ERR(moved_descendant_commit(wcroot, to_relpath, to_op_depth,
+      SVN_ERR(moved_descendant_collect(&map, wcroot, to_relpath, to_op_depth,
+                                       iterpool, iterpool));
+      SVN_ERR(moved_descendant_commit(wcroot, to_relpath,
                                       repos_id, new_repos_relpath, revision,
-                                      iterpool));
+                                      map, iterpool));
     }
 
   svn_pool_destroy(iterpool);
@@ -11475,7 +11657,6 @@ commit_node(svn_wc__db_wcroot_t *wcroot,
             apr_time_t changed_date,
             const char *changed_author,
             const svn_checksum_t *new_checksum,
-            const apr_array_header_t *new_children,
             apr_hash_t *new_dav_cache,
             svn_boolean_t keep_changelist,
             svn_boolean_t no_unlock,

[... 1601 lines stripped ...]

Reply via email to