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 [22/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/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/subversion/libsvn_wc/wc_db.c?rev=1442344&r1=1442343&r2=1442344&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/fsfs-format7/subversion/libsvn_wc/wc_db.c Mon Feb  4 20:48:05 2013
@@ -46,6 +46,7 @@
 #include "conflicts.h"
 #include "wc_db_private.h"
 #include "workqueue.h"
+#include "token-map.h"
 
 #include "svn_private_config.h"
 #include "private/svn_sqlite.h"
@@ -104,9 +105,9 @@
 #define UNKNOWN_WC_ID ((apr_int64_t) -1)
 #define FORMAT_FROM_SDB (-1)
 
-/* Check if the column contains actual properties. The empty set of properties
-   is stored as "()", so we have properties if the size of the column is
-   larger than 2. */
+/* Check if column number I, a property-skel column, contains a non-empty
+   set of properties. The empty set of properties is stored as "()", so we
+   have properties if the size of the column is larger than 2. */
 #define SQLITE_PROPERTIES_AVAILABLE(stmt, i) \
                  (svn_sqlite__column_bytes(stmt, i) > 2)
 
@@ -117,6 +118,7 @@ svn_wc__db_op_depth_for_upgrade(const ch
 }
 
 
+/* Representation of a new base row for the NODES table */
 typedef struct insert_base_baton_t {
   /* common to all insertions into BASE */
   svn_wc__db_status_t status;
@@ -175,6 +177,7 @@ typedef struct insert_base_baton_t {
 } insert_base_baton_t;
 
 
+/* Representation of a new working row for the NODES table */
 typedef struct insert_working_baton_t {
   /* common to all insertions into WORKING (including NODE_DATA) */
   svn_wc__db_status_t presence;
@@ -216,6 +219,7 @@ typedef struct insert_working_baton_t {
 
 } insert_working_baton_t;
 
+/* Representation of a new row for the EXTERNALS table */
 typedef struct insert_external_baton_t {
   /* common to all insertions into EXTERNALS */
   svn_kind_t kind;
@@ -266,27 +270,6 @@ typedef struct insert_external_baton_t {
 } insert_external_baton_t;
 
 
-static const svn_token_map_t kind_map[] = {
-  { "file", svn_kind_file },
-  { "dir", svn_kind_dir },
-  { "symlink", svn_kind_symlink },
-  { "unknown", svn_kind_unknown },
-  { NULL }
-};
-
-/* Note: we only decode presence values from the database. These are a subset
-   of all the status values. */
-static const svn_token_map_t presence_map[] = {
-  { "normal", svn_wc__db_status_normal },
-  { "server-excluded", svn_wc__db_status_server_excluded },
-  { "excluded", svn_wc__db_status_excluded },
-  { "not-present", svn_wc__db_status_not_present },
-  { "incomplete", svn_wc__db_status_incomplete },
-  { "base-deleted", svn_wc__db_status_base_deleted },
-  { NULL }
-};
-
-
 /* Forward declarations  */
 static svn_error_t *
 add_work_items(svn_sqlite__db_t *sdb,
@@ -301,12 +284,6 @@ set_actual_props(apr_int64_t wc_id,
                  apr_pool_t *scratch_pool);
 
 static svn_error_t *
-mark_conflict(svn_wc__db_wcroot_t *wcroot,
-              const char *local_relpath,
-              const svn_skel_t *conflict_skel,
-              apr_pool_t *scratch_pool);
-
-static svn_error_t *
 insert_incomplete_children(svn_sqlite__db_t *sdb,
                            apr_int64_t wc_id,
                            const char *local_relpath,
@@ -321,6 +298,7 @@ static svn_error_t *
 db_read_pristine_props(apr_hash_t **props,
                        svn_wc__db_wcroot_t *wcroot,
                        const char *local_relpath,
+                       svn_boolean_t deleted_ok,
                        apr_pool_t *result_pool,
                        apr_pool_t *scratch_pool);
 
@@ -341,7 +319,7 @@ read_info(svn_wc__db_status_t *status,
           svn_revnum_t *original_revision,
           svn_wc__db_lock_t **lock,
           svn_filesize_t *recorded_size,
-          apr_time_t *recorded_mod_time,
+          apr_time_t *recorded_time,
           const char **changelist,
           svn_boolean_t *conflicted,
           svn_boolean_t *op_root,
@@ -382,19 +360,14 @@ wclock_owns_lock(svn_boolean_t *own_lock
                  svn_boolean_t exact,
                  apr_pool_t *scratch_pool);
 
-/* Baton for db_is_switched */
-struct db_is_switched_baton_t
-{
-  svn_boolean_t *is_switched;
-  svn_kind_t *kind;
-};
-
 static svn_error_t *
-db_is_switched(void *baton,
+db_is_switched(svn_boolean_t *is_switched,
+               svn_kind_t *kind,
                svn_wc__db_wcroot_t *wcroot,
                const char *local_relpath,
                apr_pool_t *scratch_pool);
- 
+
+
 /* Return the absolute path, in local path style, of LOCAL_RELPATH
    in WCROOT.  */
 static const char *
@@ -492,11 +465,11 @@ fetch_repos_info(const char **repos_root
 }
 
 
-/* Set *REPOS_ID, *REVISION and *REPOS_RELPATH from the
-   given columns of the SQLITE statement STMT, or to NULL if the respective
+/* Set *REPOS_ID, *REVISION and *REPOS_RELPATH from the given columns of the
+   SQLITE statement STMT, or to NULL/SVN_INVALID_REVNUM if the respective
    column value is null.  Any of the output parameters may be NULL if not
    required.  */
-static svn_error_t *
+static void
 repos_location_from_columns(apr_int64_t *repos_id,
                             svn_revnum_t *revision,
                             const char **repos_relpath,
@@ -506,8 +479,6 @@ repos_location_from_columns(apr_int64_t 
                             int col_repos_relpath,
                             apr_pool_t *result_pool)
 {
-  svn_error_t *err = SVN_NO_ERROR;
-
   if (repos_id)
     {
       /* Fetch repository information via REPOS_ID. */
@@ -525,8 +496,6 @@ repos_location_from_columns(apr_int64_t 
       *repos_relpath = svn_sqlite__column_text(stmt, col_repos_relpath,
                                                result_pool);
     }
-
-  return err;
 }
 
 
@@ -611,42 +580,12 @@ blank_ibb(insert_base_baton_t *pibb)
 }
 
 
-/* Extend any delete of the parent of LOCAL_RELPATH to LOCAL_RELPATH.
-
-   Given a wc:
-
-              0         1         2         3         4
-              normal
-   A          normal
-   A/B        normal              normal
-   A/B/C                          not-pres  normal
-   A/B/C/D                                            normal
-
-   That is checkout, delete A/B, copy a replacement A/B, delete copied
-   child A/B/C, add replacement A/B/C, add A/B/C/D.
-
-   Now an update that adds base nodes for A/B/C, A/B/C/D and A/B/C/D/E
-   must extend the A/B deletion:
-
-              0         1         2         3         4
-              normal
-   A          normal
-   A/B        normal              normal
-   A/B/C      normal              not-pres  normal
-   A/B/C/D    normal              base-del            normal
-   A/B/C/D/E  normal              base-del
-
-   When adding a base node if the parent has a working node then the
-   parent base is deleted and this must be extended to cover new base
-   node.
-
-   In the example above A/B/C/D and A/B/C/D/E are the nodes that get
-   the extended delete, A/B/C is already deleted.
- */
-static svn_error_t *
-extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
-                     const char *local_relpath,
-                     apr_pool_t *scratch_pool)
+svn_error_t *
+svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
+                                const char *local_relpath,
+                                svn_kind_t kind,
+                                int op_depth,
+                                apr_pool_t *scratch_pool)
 {
   svn_boolean_t have_row;
   svn_sqlite__stmt_t *stmt;
@@ -657,28 +596,29 @@ extend_parent_delete(svn_wc__db_wcroot_t
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_SELECT_LOWEST_WORKING_NODE));
-  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath,
+                            op_depth));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
   if (have_row)
     parent_op_depth = svn_sqlite__column_int(stmt, 0);
   SVN_ERR(svn_sqlite__reset(stmt));
   if (have_row)
     {
-      int op_depth;
+      int existing_op_depth;
 
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
+                                op_depth));
       SVN_ERR(svn_sqlite__step(&have_row, stmt));
       if (have_row)
-        op_depth = svn_sqlite__column_int(stmt, 0);
+        existing_op_depth = svn_sqlite__column_int(stmt, 0);
       SVN_ERR(svn_sqlite__reset(stmt));
-      if (!have_row || parent_op_depth < op_depth)
+      if (!have_row || parent_op_depth < existing_op_depth)
         {
           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_INSTALL_WORKING_NODE_FOR_DELETE));
-          SVN_ERR(svn_sqlite__bindf(stmt, "isdt", wcroot->wc_id,
+                              STMT_INSTALL_WORKING_NODE_FOR_DELETE));
+          SVN_ERR(svn_sqlite__bindf(stmt, "isdst", wcroot->wc_id,
                                     local_relpath, parent_op_depth,
-                                    presence_map,
-                                    svn_wc__db_status_base_deleted));
+                                    parent_relpath, kind_map, kind));
           SVN_ERR(svn_sqlite__update(NULL, stmt));
         }
     }
@@ -687,22 +627,24 @@ extend_parent_delete(svn_wc__db_wcroot_t
 }
 
 
-/* This is the reverse of extend_parent_delete.
+/* This is the reverse of svn_wc__db_extend_parent_delete.
 
-   When removing a base node if the parent has a working node then the
-   parent base and this node are both deleted and so the delete of
-   this node must be removed.
+   When removing a node if the parent has a higher working node then
+   the parent node and this node are both deleted or replaced and any
+   delete over this node must be removed.
  */
-static svn_error_t *
-retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
-                      const char *local_relpath,
-                      apr_pool_t *scratch_pool)
+svn_error_t *
+svn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
+                                 const char *local_relpath,
+                                 int op_depth,
+                                 apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_DELETE_LOWEST_WORKING_NODE));
-  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
+                            op_depth));
   SVN_ERR(svn_sqlite__step_done(stmt));
 
   return SVN_NO_ERROR;
@@ -710,7 +652,9 @@ retract_parent_delete(svn_wc__db_wcroot_
 
 
 
-/* */
+/* Insert the base row represented by (insert_base_baton_t *) BATON.
+ *
+ * Implements svn_wc__db_txn_callback_t. */
 static svn_error_t *
 insert_base_node(void *baton,
                  svn_wc__db_wcroot_t *wcroot,
@@ -721,7 +665,7 @@ insert_base_node(void *baton,
   apr_int64_t repos_id = pibb->repos_id;
   svn_sqlite__stmt_t *stmt;
   svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
-  apr_int64_t recorded_mod_time;
+  apr_int64_t recorded_time;
 
   /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
      bind the appropriate parent_relpath. */
@@ -747,7 +691,7 @@ insert_base_node(void *baton,
         {
           /* Preserve size and modification time if caller asked us to. */
           recorded_size = get_recorded_size(stmt, 6);
-          recorded_mod_time = svn_sqlite__column_int64(stmt, 12);
+          recorded_time = svn_sqlite__column_int64(stmt, 12);
         }
       SVN_ERR(svn_sqlite__reset(stmt));
     }
@@ -765,7 +709,7 @@ insert_base_node(void *baton,
                             pibb->revision,
                             presence_map, pibb->status, /* 8 */
                             (pibb->kind == svn_kind_dir) ? /* 9 */
-                               svn_depth_to_word(pibb->depth) : NULL,
+                             svn_token__to_word(depth_map, pibb->depth) : NULL,
                             kind_map, pibb->kind, /* 10 */
                             pibb->changed_rev,    /* 11 */
                             pibb->changed_date,   /* 12 */
@@ -789,10 +733,14 @@ insert_base_node(void *baton,
       if (recorded_size != SVN_INVALID_FILESIZE)
         {
           SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
-          SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_mod_time));
+          SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
         }
     }
 
+  /* Set properties.  Must be null if presence not normal or incomplete. */
+  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));
 
@@ -849,13 +797,16 @@ insert_base_node(void *baton,
               || (pibb->status == svn_wc__db_status_incomplete))
           && ! pibb->file_external)
         {
-          SVN_ERR(extend_parent_delete(wcroot, local_relpath, scratch_pool));
+          SVN_ERR(svn_wc__db_extend_parent_delete(wcroot, local_relpath,
+                                                  pibb->kind, 0,
+                                                  scratch_pool));
         }
       else if (pibb->status == svn_wc__db_status_not_present
                || pibb->status == svn_wc__db_status_server_excluded
                || pibb->status == svn_wc__db_status_excluded)
         {
-          SVN_ERR(retract_parent_delete(wcroot, local_relpath, scratch_pool));
+          SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
+                                                   scratch_pool));
         }
     }
 
@@ -878,12 +829,15 @@ insert_base_node(void *baton,
 
   SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
   if (pibb->conflict)
-    SVN_ERR(mark_conflict(wcroot, local_relpath, pibb->conflict, scratch_pool));
+    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
+                                              pibb->conflict, scratch_pool));
 
   return SVN_NO_ERROR;
 }
 
 
+/* Initialize the baton with appropriate "blank" values. This allows the
+   insertion function to leave certain columns null.  */
 static void
 blank_iwb(insert_working_baton_t *piwb)
 {
@@ -986,7 +940,9 @@ insert_incomplete_children(svn_sqlite__d
 }
 
 
-/* */
+/* Insert the working row represented by (insert_working_baton_t *) BATON.
+ *
+ * Implements svn_wc__db_txn_callback_t. */
 static svn_error_t *
 insert_working_node(void *baton,
                     svn_wc__db_wcroot_t *wcroot,
@@ -995,7 +951,9 @@ insert_working_node(void *baton,
 {
   const insert_working_baton_t *piwb = baton;
   const char *parent_relpath;
+  const char *moved_to_relpath = NULL;
   svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
 
   SVN_ERR_ASSERT(piwb->op_depth > 0);
 
@@ -1003,16 +961,27 @@ insert_working_node(void *baton,
   SVN_ERR_ASSERT(*local_relpath != '\0');
   parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
 
+  /* Preserve existing moved-to information for this relpath,
+   * which might exist in case we're replacing an existing base-deleted
+   * node. */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
+                            piwb->op_depth));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  if (have_row)
+    moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
+  SVN_ERR(svn_sqlite__reset(stmt)); 
+
   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 */
-                "snnd", /* symlink_target, file_external, moved_to, moved_here */
+                "snsd", /* symlink_target, file_external, moved_to, moved_here */
                 wcroot->wc_id, local_relpath,
                 piwb->op_depth,
                 parent_relpath,
                 presence_map, piwb->presence,
                 (piwb->kind == svn_kind_dir)
-                            ? svn_depth_to_word(piwb->depth) : NULL,
+                            ? svn_token__to_word(depth_map, piwb->depth) : NULL,
                 kind_map, piwb->kind,
                 piwb->changed_rev,
                 piwb->changed_date,
@@ -1020,6 +989,7 @@ insert_working_node(void *baton,
                 /* Note: incomplete nodes may have a NULL target.  */
                 (piwb->kind == svn_kind_symlink)
                             ? piwb->target : NULL,
+                moved_to_relpath,
                 piwb->moved_here));
 
   if (piwb->kind == svn_kind_file)
@@ -1035,6 +1005,10 @@ insert_working_node(void *baton,
       SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
     }
 
+  /* Set properties.  Must be null if presence not normal or incomplete. */
+  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));
 
   SVN_ERR(svn_sqlite__insert(NULL, stmt));
@@ -1112,8 +1086,8 @@ insert_working_node(void *baton,
 
   SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
   if (piwb->conflict)
-    SVN_ERR(mark_conflict(wcroot, local_relpath, piwb->conflict,
-                          scratch_pool));
+    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
+                                              piwb->conflict, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -1236,6 +1210,41 @@ gather_repo_children(const apr_array_hea
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_wc__db_get_children_op_depth(apr_hash_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)
+{
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+
+  *children = apr_hash_make(result_pool);
+
+  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));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  while (have_row)
+    {
+      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+      svn_kind_t *child_kind = apr_palloc(result_pool, sizeof(svn_kind_t));
+
+      *child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
+      apr_hash_set(*children,
+                   svn_relpath_basename(child_relpath, result_pool),
+                   APR_HASH_KEY_STRING, child_kind);
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  return SVN_NO_ERROR;
+}
+
 
 /* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
  * Else, return FALSE. */
@@ -1389,25 +1398,21 @@ does_node_exist(svn_boolean_t *exists,
   return svn_error_trace(svn_sqlite__reset(stmt));
 }
 
-/* baton for init_db */
-struct init_db_baton
-{
-  /* output values */
-  apr_int64_t wc_id;
-  apr_int64_t repos_id;
-  /* input values */
-  const char *repos_root_url;
-  const char *repos_uuid;
-  const char *root_node_repos_relpath;
-  svn_revnum_t root_node_revision;
-  svn_depth_t root_node_depth;
-};
-
-/* Helper for create_db(). Initializes our wc.db schema */
+/* Helper for create_db(). Initializes our wc.db schema.
+ */
 static svn_error_t *
-init_db( void *baton, svn_sqlite__db_t *db, apr_pool_t *scratch_pool)
+init_db(/* output values */
+        apr_int64_t *repos_id,
+        apr_int64_t *wc_id,
+        /* input values */
+        svn_sqlite__db_t *db,
+        const char *repos_root_url,
+        const char *repos_uuid,
+        const char *root_node_repos_relpath,
+        svn_revnum_t root_node_revision,
+        svn_depth_t root_node_depth,
+        apr_pool_t *scratch_pool)
 {
-  struct init_db_baton *idb = baton;
   svn_sqlite__stmt_t *stmt;
 
   /* Create the database's schema.  */
@@ -1417,32 +1422,33 @@ init_db( void *baton, svn_sqlite__db_t *
   SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS));
 
   /* Insert the repository. */
-  SVN_ERR(create_repos_id(&idb->repos_id, idb->repos_root_url, idb->repos_uuid,
+  SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
                           db, scratch_pool));
 
   /* Insert the wcroot. */
   /* ### Right now, this just assumes wc metadata is being stored locally. */
   SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
-  SVN_ERR(svn_sqlite__insert(&idb->wc_id, stmt));
+  SVN_ERR(svn_sqlite__insert(wc_id, stmt));
 
-  if (idb->root_node_repos_relpath)
+  if (root_node_repos_relpath)
     {
       svn_wc__db_status_t status = svn_wc__db_status_normal;
 
-      if (idb->root_node_revision > 0)
+      if (root_node_revision > 0)
         status = svn_wc__db_status_incomplete; /* Will be filled by update */
 
       SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
       SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
-                                idb->wc_id,          /* 1 */
+                                *wc_id,              /* 1 */
                                 "",                  /* 2 */
                                 0,                   /* op_depth is 0 for base */
                                 NULL,                /* 4 */
-                                idb->repos_id,
-                                idb->root_node_repos_relpath,
-                                idb->root_node_revision,
+                                *repos_id,
+                                root_node_repos_relpath,
+                                root_node_revision,
                                 presence_map, status, /* 8 */
-                                svn_depth_to_word(idb->root_node_depth),
+                                svn_token__to_word(depth_map,
+                                                   root_node_depth),
                                 kind_map, svn_kind_dir /* 10 */));
 
       SVN_ERR(svn_sqlite__insert(NULL, stmt));
@@ -1475,23 +1481,16 @@ create_db(svn_sqlite__db_t **sdb,
           apr_pool_t *result_pool,
           apr_pool_t *scratch_pool)
 {
-  struct init_db_baton idb;
-
   SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
                                   svn_sqlite__mode_rwcreate, exclusive,
                                   NULL /* my_statements */,
                                   result_pool, scratch_pool));
 
-  idb.repos_root_url = repos_root_url;
-  idb.repos_uuid = repos_uuid;
-  idb.root_node_repos_relpath = root_node_repos_relpath;
-  idb.root_node_revision = root_node_revision;
-  idb.root_node_depth = root_node_depth;
-
-  SVN_ERR(svn_sqlite__with_lock(*sdb, init_db, &idb, scratch_pool));
-
-  *repos_id = idb.repos_id;
-  *wc_id = idb.wc_id;
+  SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
+                                *sdb, repos_root_url, repos_uuid,
+                                root_node_repos_relpath, root_node_revision,
+                                root_node_depth, scratch_pool),
+                        *sdb);
 
   return SVN_NO_ERROR;
 }
@@ -1566,7 +1565,7 @@ svn_wc__db_to_relpath(const char **local
 
   /* This function is indirectly called from the upgrade code, so we
      can't verify the wcroot here. Just check that it is not NULL */
-  SVN_ERR_ASSERT(wcroot != NULL);
+  CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
 
   if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
     {
@@ -1598,7 +1597,11 @@ svn_wc__db_from_relpath(const char **loc
 
   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
                               wri_abspath, scratch_pool, scratch_pool));
-  VERIFY_USABLE_WCROOT(wcroot);
+
+  /* This function is indirectly called from the upgrade code, so we
+     can't verify the wcroot here. Just check that it is not NULL */
+  CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
+
 
   *local_abspath = svn_dirent_join(wcroot->abspath,
                                    local_relpath,
@@ -1622,12 +1625,7 @@ svn_wc__db_get_wcroot(const char **wcroo
 
   /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
      where call upgrade */
-
-  if (wcroot == NULL)
-    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
-                             _("The node '%s' is not in a working copy."),
-                             svn_dirent_local_style(wri_abspath,
-                                                    scratch_pool));
+  CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
 
   *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
 
@@ -2057,26 +2055,21 @@ svn_wc__db_base_add_not_present_node(svn
     kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
 }
 
-/* Baton for db_base_remove */
-struct base_remove_baton
-{
-  svn_wc__db_t *db; /* For checking conflicts */
-  svn_boolean_t keep_as_working;
-  svn_revnum_t not_present_revision;
-  svn_skel_t *conflict;
-  svn_skel_t *work_items;
-};
-
-/* This implements svn_wc__db_txn_callback_t */
+/* The body of svn_wc__db_base_remove().
+ */
 static svn_error_t *
-db_base_remove(void *baton,
-               svn_wc__db_wcroot_t *wcroot,
+db_base_remove(svn_wc__db_wcroot_t *wcroot,
                const char *local_relpath,
+               svn_wc__db_t *db, /* For checking conflicts */
+               svn_boolean_t keep_as_working,
+               svn_boolean_t queue_deletes,
+               svn_revnum_t not_present_revision,
+               svn_skel_t *conflict,
+               svn_skel_t *work_items,
                apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
-  struct base_remove_baton *rb = baton;
   svn_wc__db_status_t status;
   apr_int64_t repos_id;
   const char *repos_relpath;
@@ -2086,14 +2079,14 @@ db_base_remove(void *baton,
   SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, NULL,
                                             &repos_relpath, &repos_id,
                                             NULL, NULL, NULL, NULL, NULL,
-                                            NULL, NULL, NULL, NULL,
+                                            NULL, NULL, NULL, NULL, NULL,
                                             wcroot, local_relpath,
                                             scratch_pool, scratch_pool));
 
   if (status == svn_wc__db_status_normal
-      && rb->keep_as_working)
+      && keep_as_working)
     {
-      SVN_ERR(svn_wc__db_op_make_copy(rb->db,
+      SVN_ERR(svn_wc__db_op_make_copy(db,
                                       svn_dirent_join(wcroot->abspath,
                                                       local_relpath,
                                                       scratch_pool),
@@ -2114,6 +2107,7 @@ db_base_remove(void *baton,
   /* 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))
     {
@@ -2139,6 +2133,7 @@ db_base_remove(void *baton,
               svn_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
                                                               kind_map);
               const char *node_abspath;
+              svn_error_t *err;
 
               svn_pool_clear(iterpool);
 
@@ -2146,18 +2141,21 @@ db_base_remove(void *baton,
                                              iterpool);
 
               if (node_kind == svn_kind_dir)
-                SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
-                                                    rb->db, wcroot->abspath,
-                                                    node_abspath, FALSE,
-                                                    iterpool, iterpool));
+                err = svn_wc__wq_build_dir_remove(&work_item,
+                                                  db, wcroot->abspath,
+                                                  node_abspath, FALSE,
+                                                  iterpool, iterpool);
               else
-                SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
-                                                     rb->db,
-                                                     wcroot->abspath,
-                                                     node_abspath,
-                                                     iterpool, iterpool));
+                err = svn_wc__wq_build_file_remove(&work_item,
+                                                   db,
+                                                   wcroot->abspath,
+                                                   node_abspath,
+                                                   iterpool, iterpool);
 
-              SVN_ERR(add_work_items(wcroot->sdb, work_item, iterpool));
+              if (!err)
+                err = add_work_items(wcroot->sdb, work_item, iterpool);
+              if (err)
+                return svn_error_compose_create(err, svn_sqlite__reset(stmt));
 
               SVN_ERR(svn_sqlite__step(&have_row, stmt));
            }
@@ -2165,14 +2163,14 @@ db_base_remove(void *baton,
           SVN_ERR(svn_sqlite__reset(stmt));
 
           SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
-                                              rb->db, wcroot->abspath,
+                                              db, wcroot->abspath,
                                               local_abspath, FALSE,
                                               scratch_pool, iterpool));
           svn_pool_destroy(iterpool);
         }
       else
         SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
-                                             rb->db, wcroot->abspath,
+                                             db, wcroot->abspath,
                                              local_abspath,
                                              scratch_pool, scratch_pool));
 
@@ -2189,7 +2187,7 @@ db_base_remove(void *baton,
       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
       SVN_ERR(svn_sqlite__step_done(stmt));
     }
-  else if (! rb->keep_as_working)
+  else if (! keep_as_working)
     {
       /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
@@ -2228,7 +2226,8 @@ db_base_remove(void *baton,
   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
   SVN_ERR(svn_sqlite__step_done(stmt));
 
-  SVN_ERR(retract_parent_delete(wcroot, local_relpath, scratch_pool));
+  SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
+                                           scratch_pool));
 
   /* Step 6: Delete actual node if we don't keep working */
   if (! keep_working)
@@ -2239,7 +2238,7 @@ db_base_remove(void *baton,
       SVN_ERR(svn_sqlite__step_done(stmt));
     }
 
-  if (SVN_IS_VALID_REVNUM(rb->not_present_revision))
+  if (SVN_IS_VALID_REVNUM(not_present_revision))
     {
       struct insert_base_baton_t ibb;
       blank_ibb(&ibb);
@@ -2248,7 +2247,7 @@ db_base_remove(void *baton,
       ibb.status = svn_wc__db_status_not_present;
       ibb.kind = kind;
       ibb.repos_relpath = repos_relpath;
-      ibb.revision = rb->not_present_revision;
+      ibb.revision = not_present_revision;
 
       /* Depending upon KIND, any of these might get used. */
       ibb.children = NULL;
@@ -2259,9 +2258,10 @@ db_base_remove(void *baton,
       SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
     }
 
-  SVN_ERR(add_work_items(wcroot->sdb, rb->work_items, scratch_pool));
-  if (rb->conflict)
-    SVN_ERR(mark_conflict(wcroot, local_relpath, rb->conflict, 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,
+                                              conflict, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -2271,6 +2271,7 @@ 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_revnum_t not_present_revision,
                        svn_skel_t *conflict,
                        svn_skel_t *work_items,
@@ -2278,7 +2279,6 @@ svn_wc__db_base_remove(svn_wc__db_t *db,
 {
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
-  struct base_remove_baton rb;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
@@ -2286,14 +2286,11 @@ svn_wc__db_base_remove(svn_wc__db_t *db,
                               local_abspath, scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
-  rb.db = db;
-  rb.keep_as_working = keep_as_working;
-  rb.not_present_revision = not_present_revision;
-  rb.conflict = conflict;
-  rb.work_items = work_items;
-
-  SVN_ERR(svn_wc__db_with_txn(wcroot, local_relpath, db_base_remove, &rb,
-                              scratch_pool));
+  SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
+                                     db, keep_as_working, queue_deletes,
+                                     not_present_revision,
+                                     conflict, work_items, scratch_pool),
+                      wcroot);
 
   /* If this used to be a directory we should remove children so pass
    * depth infinity. */
@@ -2318,6 +2315,7 @@ svn_wc__db_base_get_info_internal(svn_wc
                                   const char **target,
                                   svn_wc__db_lock_t **lock,
                                   svn_boolean_t *had_props,
+                                  apr_hash_t **props,
                                   svn_boolean_t *update_root,
                                   svn_wc__db_wcroot_t *wcroot,
                                   const char *local_relpath,
@@ -2336,8 +2334,9 @@ svn_wc__db_base_get_info_internal(svn_wc
 
   if (have_row)
     {
-      svn_kind_t node_kind = svn_sqlite__column_token(stmt, 3,
-                                                             kind_map);
+      svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
+                                                                 presence_map);
+      svn_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
 
       if (kind)
         {
@@ -2345,10 +2344,10 @@ svn_wc__db_base_get_info_internal(svn_wc
         }
       if (status)
         {
-          *status = svn_sqlite__column_token(stmt, 2, presence_map);
+          *status = node_status;
         }
-      err = repos_location_from_columns(repos_id, revision, repos_relpath,
-                                        stmt, 0, 4, 1, result_pool);
+      repos_location_from_columns(repos_id, revision, repos_relpath,
+                                  stmt, 0, 4, 1, result_pool);
       SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
       SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
       if (lock)
@@ -2376,12 +2375,8 @@ svn_wc__db_base_get_info_internal(svn_wc
             }
           else
             {
-              const char *depth_str = svn_sqlite__column_text(stmt, 10, NULL);
-
-              if (depth_str == NULL)
-                *depth = svn_depth_unknown;
-              else
-                *depth = svn_depth_from_word(depth_str);
+              *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
+                                                     svn_depth_unknown);
             }
         }
       if (checksum)
@@ -2413,8 +2408,25 @@ svn_wc__db_base_get_info_internal(svn_wc
         {
           *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
         }
+      if (props)
+        {
+          if (node_status == svn_wc__db_status_normal
+              || node_status == svn_wc__db_status_incomplete)
+            {
+              SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
+                                                    result_pool, scratch_pool));
+              if (*props == NULL)
+                *props = apr_hash_make(result_pool);
+            }
+          else
+            {
+              assert(svn_sqlite__column_is_null(stmt, 13));
+              *props = NULL;
+            }
+        }
       if (update_root)
         {
+          /* It's an update root iff it's a file external. */
           *update_root = svn_sqlite__column_boolean(stmt, 14);
         }
     }
@@ -2446,6 +2458,7 @@ svn_wc__db_base_get_info(svn_wc__db_stat
                          const char **target,
                          svn_wc__db_lock_t **lock,
                          svn_boolean_t *had_props,
+                         apr_hash_t **props,
                          svn_boolean_t *update_root,
                          svn_wc__db_t *db,
                          const char *local_abspath,
@@ -2467,7 +2480,7 @@ svn_wc__db_base_get_info(svn_wc__db_stat
                                             changed_rev, changed_date,
                                             changed_author, depth,
                                             checksum, target, lock,
-                                            had_props, update_root,
+                                            had_props, props, update_root,
                                             wcroot, local_relpath,
                                             result_pool, scratch_pool));
   SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
@@ -2508,7 +2521,6 @@ svn_wc__db_base_get_children_info(apr_ha
       struct svn_wc__db_base_info_t *info;
       svn_error_t *err;
       apr_int64_t repos_id;
-      const char *depth_str;
       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
       const char *name = svn_relpath_basename(child_relpath, result_pool);
 
@@ -2520,10 +2532,8 @@ svn_wc__db_base_get_children_info(apr_ha
       info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
       info->revnum = svn_sqlite__column_revnum(stmt, 5);
 
-      depth_str = svn_sqlite__column_text(stmt, 6, NULL);
-
-      info->depth = (depth_str != NULL) ? svn_depth_from_word(depth_str)
-                                        : svn_depth_unknown;
+      info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
+                                                  svn_depth_unknown);
 
       info->update_root = svn_sqlite__column_boolean(stmt, 7);
 
@@ -2556,32 +2566,24 @@ svn_wc__db_base_get_props(apr_hash_t **p
                           apr_pool_t *result_pool,
                           apr_pool_t *scratch_pool)
 {
-  svn_sqlite__stmt_t *stmt;
-  svn_boolean_t have_row;
-  svn_error_t *err;
+  svn_wc__db_status_t presence;
 
-  SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
-                                 STMT_SELECT_BASE_PROPS, scratch_pool));
-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
-  if (!have_row)
+  SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
+                                   NULL, NULL, NULL, NULL, NULL,
+                                   NULL, NULL, NULL, NULL, props, NULL,
+                                   db, local_abspath,
+                                   result_pool, scratch_pool));
+  if (presence != svn_wc__db_status_normal
+      && presence != svn_wc__db_status_incomplete)
     {
-      err = svn_sqlite__reset(stmt);
-      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, err,
-                               _("The node '%s' was not found."),
+      return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
+                               _("The node '%s' has a BASE status that"
+                                  " has no properties."),
                                svn_dirent_local_style(local_abspath,
                                                       scratch_pool));
     }
 
-  err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
-                                      scratch_pool);
-  if (err == NULL && *props == NULL)
-    {
-      /* ### is this a DB constraint violation? the column "probably" should
-         ### never be null.  */
-      *props = apr_hash_make(result_pool);
-    }
-
-  return svn_error_compose_create(err, svn_sqlite__reset(stmt));
+  return SVN_NO_ERROR;
 }
 
 
@@ -2696,6 +2698,7 @@ svn_wc__db_depth_get_info(svn_wc__db_sta
                           const svn_checksum_t **checksum,
                           const char **target,
                           svn_boolean_t *had_props,
+                          apr_hash_t **props,
                           svn_wc__db_wcroot_t *wcroot,
                           const char *local_relpath,
                           int op_depth,
@@ -2714,8 +2717,9 @@ svn_wc__db_depth_get_info(svn_wc__db_sta
 
   if (have_row)
     {
-      svn_kind_t node_kind = svn_sqlite__column_token(stmt, 3,
-                                                             kind_map);
+      svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
+                                                                 presence_map);
+      svn_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
 
       if (kind)
         {
@@ -2723,13 +2727,13 @@ svn_wc__db_depth_get_info(svn_wc__db_sta
         }
       if (status)
         {
-          *status = svn_sqlite__column_token(stmt, 2, presence_map);
+          *status = node_status;
 
           if (op_depth > 0)
             SVN_ERR(convert_to_working_status(status, *status));
         }
-      err = repos_location_from_columns(repos_id, revision, repos_relpath,
-                                        stmt, 0, 4, 1, result_pool);
+      repos_location_from_columns(repos_id, revision, repos_relpath,
+                                  stmt, 0, 4, 1, result_pool);
 
       if (changed_rev)
         {
@@ -2752,12 +2756,8 @@ svn_wc__db_depth_get_info(svn_wc__db_sta
             }
           else
             {
-              const char *depth_str = svn_sqlite__column_text(stmt, 10, NULL);
-
-              if (depth_str == NULL)
-                *depth = svn_depth_unknown;
-              else
-                *depth = svn_depth_from_word(depth_str);
+              *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
+                                                     svn_depth_unknown);
             }
         }
       if (checksum)
@@ -2789,6 +2789,22 @@ svn_wc__db_depth_get_info(svn_wc__db_sta
         {
           *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
         }
+      if (props)
+        {
+          if (node_status == svn_wc__db_status_normal
+              || node_status == svn_wc__db_status_incomplete)
+            {
+              SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
+                                                    result_pool, scratch_pool));
+              if (*props == NULL)
+                *props = apr_hash_make(result_pool);
+            }
+          else
+            {
+              assert(svn_sqlite__column_is_null(stmt, 13));
+              *props = NULL;
+            }
+        }
     }
   else
     {
@@ -2803,11 +2819,7 @@ svn_wc__db_depth_get_info(svn_wc__db_sta
 }
 
 
-/* Helper for creating SQLite triggers, running the main transaction
-   callback, and then dropping the triggers.  It guarantees that the
-   triggers will not survive the transaction.  This could be used for
-   any general prefix/postscript statements where the postscript
-   *must* be executed if the transaction completes. */
+/* Baton for passing args to with_triggers(). */
 struct with_triggers_baton_t {
   int create_trigger;
   int drop_trigger;
@@ -2815,7 +2827,13 @@ struct with_triggers_baton_t {
   void *cb_baton;
 };
 
-/* conforms to svn_wc__db_txn_callback_t  */
+/* Helper for creating SQLite triggers, running the main transaction
+   callback, and then dropping the triggers.  It guarantees that the
+   triggers will not survive the transaction.  This could be used for
+   any general prefix/postscript statements where the postscript
+   *must* be executed if the transaction completes.
+
+   Implements svn_wc__db_txn_callback_t. */
 static svn_error_t *
 with_triggers(void *baton,
               svn_wc__db_wcroot_t *wcroot,
@@ -2894,6 +2912,8 @@ with_finalization(svn_wc__db_wcroot_t *w
 }
 
 
+/* Initialize the baton with appropriate "blank" values. This allows the
+   insertion function to leave certain columns null.  */
 static void
 blank_ieb(insert_external_baton_t *ieb)
 {
@@ -2906,6 +2926,9 @@ blank_ieb(insert_external_baton_t *ieb)
   ieb->recorded_revision = SVN_INVALID_REVNUM;
 }
 
+/* Insert the externals row represented by (insert_external_baton_t *) BATON.
+ *
+ * Implements svn_wc__db_txn_callback_t. */
 static svn_error_t *
 insert_external_node(void *baton,
                      svn_wc__db_wcroot_t *wcroot,
@@ -2928,7 +2951,7 @@ insert_external_node(void *baton,
   /* And there must be no existing BASE node or it must be a file external */
   err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
                                           NULL, NULL, NULL, NULL, NULL,
-                                          NULL, NULL, NULL, &update_root,
+                                          NULL, NULL, NULL, NULL, &update_root,
                                           wcroot, local_relpath,
                                           scratch_pool, scratch_pool);
   if (err)
@@ -3245,25 +3268,21 @@ svn_wc__db_external_add_dir(svn_wc__db_t
                                 &ieb, scratch_pool));
 }
 
-/* Baton for db_external_remove */
-struct external_remove_baton
-{
-  const svn_skel_t *work_items;
-};
-
+/* The body of svn_wc__db_external_remove(). */
 static svn_error_t *
-db_external_remove(void *baton, svn_wc__db_wcroot_t *wcroot,
-                   const char *local_relpath, apr_pool_t *scratch_pool)
+db_external_remove(const svn_skel_t *work_items,
+                   svn_wc__db_wcroot_t *wcroot,
+                   const char *local_relpath,
+                   apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
-  struct external_remove_baton *rb = baton;
 
   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(add_work_items(wcroot->sdb, rb->work_items, scratch_pool));
+  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
 
   /* ### What about actual? */
   return SVN_NO_ERROR;
@@ -3278,7 +3297,6 @@ svn_wc__db_external_remove(svn_wc__db_t 
 {
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
-  struct external_remove_baton rb;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
@@ -3293,9 +3311,9 @@ svn_wc__db_external_remove(svn_wc__db_t 
 
   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
 
-  rb.work_items = work_items;
-  SVN_ERR(svn_wc__db_with_txn(wcroot, local_relpath, db_external_remove,
-                              &rb, scratch_pool));
+  SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
+                                         scratch_pool),
+                      wcroot);
 
   return SVN_NO_ERROR;
 }
@@ -3410,11 +3428,13 @@ svn_wc__db_committable_externals_below(a
                               local_abspath, scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
+  SVN_ERR(svn_sqlite__get_statement(
+                &stmt, wcroot->sdb,
+                immediates_only 
+                    ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
+                    : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
 
-  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
-                            (immediates_only ? 1 : 0)));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
 
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
 
@@ -3551,15 +3571,14 @@ svn_wc__db_externals_gather_definitions(
 
           if (depths)
             {
-              const char *depth_word = svn_sqlite__column_text(stmt, 2, NULL);
-              svn_depth_t depth = svn_depth_unknown;
-
-              if (depth_word)
-                depth = svn_depth_from_word(depth_word);
+              svn_depth_t depth
+                = svn_sqlite__column_token_null(stmt, 2, depth_map,
+                                                svn_depth_unknown);
 
               apr_hash_set(*depths, node_abspath,
                            APR_HASH_KEY_STRING,
-                           svn_depth_to_word(depth)); /* Use static string */
+                           svn_token__to_word(depth_map,
+                                              depth)); /* Use static string */
             }
         }
 
@@ -3652,7 +3671,7 @@ cross_db_copy(svn_wc__db_wcroot_t *src_w
                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                     src_wcroot, src_relpath, scratch_pool, scratch_pool));
 
-  SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath,
+  SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
                                  scratch_pool, scratch_pool));
 
   blank_iwb(&iwb);
@@ -3684,46 +3703,313 @@ cross_db_copy(svn_wc__db_wcroot_t *src_w
   return SVN_NO_ERROR;
 }
 
+/* Helper for scan_deletion_txn. Extracts the moved-to information, if
+   any, from STMT.  Sets *SCAN to FALSE if moved-to was available. */
+static svn_error_t *
+get_moved_to(const char **moved_to_relpath_p,
+             const char **moved_to_op_root_relpath_p,
+             svn_boolean_t *scan,
+             svn_sqlite__stmt_t *stmt,
+             const char *current_relpath,
+             svn_wc__db_wcroot_t *wcroot,
+             const char *local_relpath,
+             apr_pool_t *result_pool,
+             apr_pool_t *scratch_pool)
+{
+  const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
 
-/* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
-   appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
-   since they are available.  This is a helper for
-   svn_wc__db_op_copy. */
+  if (moved_to_relpath)
+    {
+      const char *moved_to_op_root_relpath = moved_to_relpath;
+
+      if (strcmp(current_relpath, local_relpath))
+        {
+          /* LOCAL_RELPATH is a child inside the move op-root. */
+          const char *moved_child_relpath;
+
+          /* The CURRENT_RELPATH is the op_root of the delete-half of
+           * the move. LOCAL_RELPATH is a child that was moved along.
+           * Compute the child's new location within the move target. */
+          moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
+                                                          local_relpath);
+          SVN_ERR_ASSERT(moved_child_relpath &&
+                         strlen(moved_child_relpath) > 0);
+          moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
+                                              moved_child_relpath,
+                                              result_pool);
+        }
+
+      if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
+        *moved_to_op_root_relpath_p
+          = apr_pstrdup(result_pool, moved_to_op_root_relpath);
+
+      if (moved_to_relpath && moved_to_relpath_p)
+        *moved_to_relpath_p
+          = apr_pstrdup(result_pool, moved_to_relpath);
+
+      *scan = FALSE;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+/* The body of svn_wc__db_scan_deletion().
+ */
 static svn_error_t *
-get_info_for_copy(apr_int64_t *copyfrom_id,
-                  const char **copyfrom_relpath,
-                  svn_revnum_t *copyfrom_rev,
-                  svn_wc__db_status_t *status,
-                  svn_kind_t *kind,
-                  svn_boolean_t *op_root,
+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)
 {
-  const char *repos_relpath;
-  svn_revnum_t revision;
-  svn_wc__db_status_t node_status;
+  const char *current_relpath = local_relpath;
+  svn_sqlite__stmt_t *stmt;
+  svn_wc__db_status_t work_presence;
+  svn_boolean_t have_row, scan, have_base;
+  int op_depth;
 
-  SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, copyfrom_id,
-                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                    NULL, NULL, NULL, NULL, NULL, op_root, NULL, NULL,
-                    NULL /* have_base */,
-                    NULL /* have_more_work */,
-                    NULL /* have_work */,
-                    wcroot, local_relpath, result_pool, scratch_pool));
+  /* Initialize all the OUT parameters.  */
+  if (base_del_relpath != NULL)
+    *base_del_relpath = NULL;
+  if (moved_to_relpath != NULL)
+    *moved_to_relpath = NULL;
+  if (work_del_relpath != NULL)
+    *work_del_relpath = NULL;
+  if (moved_to_op_root_relpath != NULL)
+    *moved_to_op_root_relpath = NULL;
 
-  if (node_status == svn_wc__db_status_excluded)
-    {
-      /* The parent cannot be excluded, so look at the parent and then
-         adjust the relpath */
-      const char *parent_relpath, *base_name;
+  /* If looking for moved-to info then we need to scan every path
+     until we find it.  If not looking for moved-to we only need to
+     check op-roots and parents of op-roots. */
+  scan = (moved_to_op_root_relpath || moved_to_relpath);
 
-      svn_dirent_split(&parent_relpath, &base_name, local_relpath,
-                       scratch_pool);
-      SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
-                                NULL, NULL, NULL,
-                                wcroot, parent_relpath,
+  SVN_ERR(svn_sqlite__get_statement(
+                    &stmt, wcroot->sdb,
+                    scan ? STMT_SELECT_DELETION_INFO_SCAN
+                         : STMT_SELECT_DELETION_INFO));
+
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  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));
+
+  work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
+  have_base = !svn_sqlite__column_is_null(stmt, 0);
+  if (work_presence != svn_wc__db_status_not_present
+      && work_presence != svn_wc__db_status_base_deleted)
+    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
+                             svn_sqlite__reset(stmt),
+                             _("Expected node '%s' to be deleted."),
+                             path_for_error_message(wcroot, local_relpath,
+                                                    scratch_pool));
+
+  op_depth = svn_sqlite__column_int(stmt, 2);
+
+  /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
+     treat this as an op-root.  At commit time we need to explicitly
+     delete such nodes otherwise they will be present in the
+     repository copy. */
+  if (work_presence == svn_wc__db_status_not_present
+      && work_del_relpath && !*work_del_relpath)
+    {
+      *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
+
+      if (!scan && !base_del_relpath)
+        {
+          /* We have all we need, exit early */
+          SVN_ERR(svn_sqlite__reset(stmt));
+          return SVN_NO_ERROR;
+        }
+    }
+
+
+  while(TRUE)
+    {
+      svn_error_t *err;
+      const char *parent_relpath;
+      int current_depth = relpath_depth(current_relpath);
+
+      /* Step CURRENT_RELPATH to op-root */
+
+      while(TRUE)
+        {
+          if (scan)
+            {
+              err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
+                                 &scan, stmt, current_relpath,
+                                 wcroot, local_relpath,
+                                 result_pool, scratch_pool);
+              if (err || (!scan
+                          && !base_del_relpath
+                          && !work_del_relpath))
+                {
+                  /* We have all we need (or an error occurred) */
+                  SVN_ERR(svn_sqlite__reset(stmt));
+                  return svn_error_trace(err);
+                }
+            }
+
+          if(current_depth <= op_depth)
+            break;
+
+          current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
+          --current_depth;
+
+          if (scan || current_depth == op_depth)
+            {
+              SVN_ERR(svn_sqlite__reset(stmt));
+              SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
+                                        current_relpath));
+              SVN_ERR(svn_sqlite__step(&have_row, stmt));
+              SVN_ERR_ASSERT(have_row);
+              have_base = !svn_sqlite__column_is_null(stmt, 0);
+            }
+        }
+      SVN_ERR(svn_sqlite__reset(stmt));
+
+      /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
+
+      SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
+      parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
+      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+      if (!have_row)
+        {
+          /* No row means no WORKING node which mean we just fell off
+             the WORKING tree, so CURRENT_RELPATH is the op-root
+             closest to the wc root. */
+          if (have_base && base_del_relpath)
+            *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
+          break;
+        }
+
+      /* Still in the WORKING tree so the first time we get here
+         CURRENT_RELPATH is a delete op-root in the WORKING tree. */
+      if (work_del_relpath && !*work_del_relpath)
+        {
+          *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
+
+          if (!scan && !base_del_relpath)
+            break; /* We have all we need */
+        }
+
+      current_relpath = parent_relpath;
+      op_depth = svn_sqlite__column_int(stmt, 2);
+      have_base = !svn_sqlite__column_is_null(stmt, 0);
+    }
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__db_scan_deletion(const char **base_del_abspath,
+                         const char **moved_to_abspath,
+                         const char **work_del_abspath,
+                         const char **moved_to_op_root_abspath,
+                         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;
+  const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
+  const char *moved_to_op_root_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_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),
+    wcroot);
+
+  if (base_del_abspath)
+    {
+      *base_del_abspath = (base_del_relpath
+                           ? svn_dirent_join(wcroot->abspath,
+                                             base_del_relpath, result_pool)
+                           : NULL);
+    }
+  if (moved_to_abspath)
+    {
+      *moved_to_abspath = (moved_to_relpath
+                           ? svn_dirent_join(wcroot->abspath,
+                                             moved_to_relpath, result_pool)
+                           : NULL);
+    }
+  if (work_del_abspath)
+    {
+      *work_del_abspath = (work_del_relpath
+                           ? svn_dirent_join(wcroot->abspath,
+                                             work_del_relpath, result_pool)
+                           : NULL);
+    }
+  if (moved_to_op_root_abspath)
+    {
+      *moved_to_op_root_abspath = (moved_to_op_root_relpath
+                           ? svn_dirent_join(wcroot->abspath,
+                                             moved_to_op_root_relpath,
+                                             result_pool)
+                           : NULL);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
+   appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
+   since they are available.  This is a helper for
+   svn_wc__db_op_copy. */
+static svn_error_t *
+get_info_for_copy(apr_int64_t *copyfrom_id,
+                  const char **copyfrom_relpath,
+                  svn_revnum_t *copyfrom_rev,
+                  svn_wc__db_status_t *status,
+                  svn_kind_t *kind,
+                  svn_boolean_t *op_root,
+                  svn_wc__db_wcroot_t *wcroot,
+                  const char *local_relpath,
+                  apr_pool_t *result_pool,
+                  apr_pool_t *scratch_pool)
+{
+  const char *repos_relpath;
+  svn_revnum_t revision;
+  svn_wc__db_status_t node_status;
+
+  SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, copyfrom_id,
+                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                    NULL, NULL, NULL, NULL, NULL, op_root, NULL, NULL,
+                    NULL /* have_base */,
+                    NULL /* have_more_work */,
+                    NULL /* have_work */,
+                    wcroot, local_relpath, result_pool, scratch_pool));
+
+  if (node_status == svn_wc__db_status_excluded)
+    {
+      /* The parent cannot be excluded, so look at the parent and then
+         adjust the relpath */
+      const char *parent_relpath, *base_name;
+
+      svn_dirent_split(&parent_relpath, &base_name, local_relpath,
+                       scratch_pool);
+      SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
+                                NULL, NULL, NULL,
+                                wcroot, parent_relpath,
                                 scratch_pool, scratch_pool));
       if (*copyfrom_relpath)
         *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
@@ -3751,10 +4037,10 @@ get_info_for_copy(apr_int64_t *copyfrom_
     {
       const char *base_del_relpath, *work_del_relpath;
 
-      SVN_ERR(svn_wc__db_scan_deletion_internal(&base_del_relpath, NULL,
-                                                &work_del_relpath,
-                                                NULL, wcroot, local_relpath,
-                                                scratch_pool, scratch_pool));
+      SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
+                                &work_del_relpath,
+                                NULL, wcroot, local_relpath,
+                                scratch_pool, scratch_pool));
       if (work_del_relpath)
         {
           const char *op_root_relpath;
@@ -3780,7 +4066,7 @@ get_info_for_copy(apr_int64_t *copyfrom_
                                                     copyfrom_relpath,
                                                     copyfrom_id, NULL, NULL,
                                                     NULL, NULL, NULL, NULL,
-                                                    NULL, NULL, NULL,
+                                                    NULL, NULL, NULL, NULL,
                                                     wcroot, local_relpath,
                                                     result_pool,
                                                     scratch_pool));
@@ -3801,15 +4087,44 @@ get_info_for_copy(apr_int64_t *copyfrom_
 }
 
 
-/* Forward declarations for db_op_copy() to use.
-
-   ### these are just to avoid churn. a future commit should shuffle the
-   ### functions around.  */
+/* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
 static svn_error_t *
 op_depth_of(int *op_depth,
             svn_wc__db_wcroot_t *wcroot,
-            const char *local_relpath);
+            const char *local_relpath)
+{
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+
+  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_ASSERT(have_row);
+  *op_depth = svn_sqlite__column_int(stmt, 0);
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
+   revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
+   by checking if this would be a direct child of a copy of its parent
+   directory. If it is then set *OP_DEPTH to the op_depth of its parent.
+
+   If the node is not a direct copy at the same revision of the parent
+   *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
+   node should be inserted at this op_depth. This will be the case when the
+   parent already defined an incomplete child with the same name. Otherwise
+   *NP_OP_DEPTH will be set to -1.
 
+   If the parent node is not the parent of the to be copied node, then
+   *OP_DEPTH will be set to the proper op_depth for a new operation root.
+
+   Set *PARENT_OP_DEPTH to the op_depth of the parent.
+
+ */
 static svn_error_t *
 op_depth_for_copy(int *op_depth,
                   int *np_op_depth,
@@ -3819,7 +4134,86 @@ op_depth_for_copy(int *op_depth,
                   svn_revnum_t copyfrom_revision,
                   svn_wc__db_wcroot_t *wcroot,
                   const char *local_relpath,
-                  apr_pool_t *scratch_pool);
+                  apr_pool_t *scratch_pool)
+{
+  const char *parent_relpath, *name;
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+  int incomplete_op_depth = -1;
+  int min_op_depth = 1; /* Never touch BASE */
+
+  *op_depth = relpath_depth(local_relpath);
+  *np_op_depth = -1;
+
+  svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
+  *parent_op_depth = relpath_depth(parent_relpath);
+
+  if (!copyfrom_relpath)
+    return SVN_NO_ERROR;
+
+  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(&have_row, stmt));
+  if (have_row)
+    {
+      svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
+                                                            presence_map);
+
+      min_op_depth = svn_sqlite__column_int(stmt, 0);
+      if (status == svn_wc__db_status_incomplete)
+        incomplete_op_depth = min_op_depth;
+    }
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_WORKING_NODE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  if (have_row)
+    {
+      svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
+                                                              presence_map);
+
+      *parent_op_depth = svn_sqlite__column_int(stmt, 0);
+      if (*parent_op_depth < min_op_depth)
+        {
+          /* We want to create a copy; not overwrite the lower layers */
+          SVN_ERR(svn_sqlite__reset(stmt));
+          return SVN_NO_ERROR;
+        }
+
+      /* You can only add children below a node that exists.
+         In WORKING that must be status added, which is represented
+         as presence normal */
+      SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
+
+      if ((incomplete_op_depth < 0)
+          || (incomplete_op_depth == *parent_op_depth))
+        {
+          apr_int64_t parent_copyfrom_repos_id
+            = svn_sqlite__column_int64(stmt, 10);
+          const char *parent_copyfrom_relpath
+            = svn_sqlite__column_text(stmt, 11, NULL);
+          svn_revnum_t parent_copyfrom_revision
+            = svn_sqlite__column_revnum(stmt, 12);
+
+          if (parent_copyfrom_repos_id == copyfrom_repos_id)
+            {
+              if (copyfrom_revision == parent_copyfrom_revision
+                  && !strcmp(copyfrom_relpath,
+                             svn_relpath_join(parent_copyfrom_relpath, name,
+                                              scratch_pool)))
+                *op_depth = *parent_op_depth;
+              else if (incomplete_op_depth > 0)
+                *np_op_depth = incomplete_op_depth;
+            }
+        }
+    }
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  return SVN_NO_ERROR;
+}
 
 
 /* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
@@ -4069,7 +4463,7 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcro
   return SVN_NO_ERROR;
 }
 
-/* Baton for op_copy_txn */
+/* Baton for passing args to op_copy_txn(). */
 struct op_copy_baton
 {
   svn_wc__db_wcroot_t *src_wcroot;
@@ -4084,10 +4478,13 @@ struct op_copy_baton
   const char *dst_op_root_relpath;
 };
 
-/* Helper for svn_wc__db_op_copy.
-   Implements  svn_sqlite__transaction_callback_t */
+/* Helper for svn_wc__db_op_copy().
+ *
+ * Implements svn_sqlite__transaction_callback_t. */
 static svn_error_t *
-op_copy_txn(void * baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
+op_copy_txn(void * baton,
+            svn_sqlite__db_t *sdb,
+            apr_pool_t *scratch_pool)
 {
   struct op_copy_baton *ocb = baton;
   int move_op_depth;
@@ -4190,7 +4587,8 @@ db_op_copy_shadowed_layer(svn_wc__db_wcr
     svn_error_t *err;
     err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
                                     &node_repos_relpath, &node_repos_id,
-                                    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                    NULL, NULL, NULL, NULL, NULL, NULL,
+                                    NULL, NULL,
                                     src_wcroot, src_relpath, src_op_depth,
                                     scratch_pool, scratch_pool);
 
@@ -4360,10 +4758,12 @@ db_op_copy_shadowed_layer(svn_wc__db_wcr
   return SVN_NO_ERROR;
 }
 
-/* Helper for svn_wc__db_op_copy_shadowed_layer.
-   Implements  svn_sqlite__transaction_callback_t */
+/* Helper for svn_wc__db_op_copy_shadowed_layer().
+ *
+ * Implements  svn_sqlite__transaction_callback_t. */
 static svn_error_t *
-op_copy_shadowed_layer_txn(void * baton, svn_sqlite__db_t *sdb,
+op_copy_shadowed_layer_txn(void *baton,
+                           svn_sqlite__db_t *sdb,
                            apr_pool_t *scratch_pool)
 {
   struct op_copy_baton *ocb = baton;
@@ -4407,7 +4807,8 @@ op_copy_shadowed_layer_txn(void * baton,
   /* Get some information from the parent */
   SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
                                     &repos_id, NULL, NULL, NULL, NULL, NULL,
-                                    NULL, NULL, ocb->src_wcroot,
+                                    NULL, NULL, NULL,
+                                    ocb->src_wcroot,
                                     src_parent_relpath, src_op_depth,
                                     scratch_pool, scratch_pool));
 
@@ -4473,27 +4874,6 @@ svn_wc__db_op_copy_shadowed_layer(svn_wc
 }
 
 
-/* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
-static svn_error_t *
-op_depth_of(int *op_depth,
-            svn_wc__db_wcroot_t *wcroot,
-            const char *local_relpath)
-{
-  svn_sqlite__stmt_t *stmt;
-  svn_boolean_t have_row;
-
-  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_ASSERT(have_row);
-  *op_depth = svn_sqlite__column_int(stmt, 0);
-  SVN_ERR(svn_sqlite__reset(stmt));
-
-  return SVN_NO_ERROR;
-}
-
-
 /* If there are any server-excluded base nodes then the copy must fail
    as it's not possible to commit such a copy.
    Return an error if there are any server-excluded nodes. */
@@ -4518,117 +4898,9 @@ catch_copy_of_server_excluded(svn_wc__db
   if (have_row)
     return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
                              _("Cannot copy '%s' excluded by server"),
-                             path_for_error_message(wcroot,
-                                                    server_excluded_relpath,
-                                                    scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-
-/* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
-   revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
-   by checking if this would be a direct child of a copy of its parent
-   directory. If it is then set *OP_DEPTH to the op_depth of its parent.
-
-   If the node is not a direct copy at the same revision of the parent
-   *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
-   node should be inserted at this op_depth. This will be the case when the
-   parent already defined an incomplete child with the same name. Otherwise
-   *NP_OP_DEPTH will be set to -1.
-
-   If the parent node is not the parent of the to be copied node, then
-   *OP_DEPTH will be set to the proper op_depth for a new operation root.
-
-   Set *PARENT_OP_DEPTH to the op_depth of the parent.
-
- */
-static svn_error_t *
-op_depth_for_copy(int *op_depth,
-                  int *np_op_depth,
-                  int *parent_op_depth,
-                  apr_int64_t copyfrom_repos_id,
-                  const char *copyfrom_relpath,
-                  svn_revnum_t copyfrom_revision,
-                  svn_wc__db_wcroot_t *wcroot,
-                  const char *local_relpath,
-                  apr_pool_t *scratch_pool)
-{
-  const char *parent_relpath, *name;
-  svn_sqlite__stmt_t *stmt;
-  svn_boolean_t have_row;
-  int incomplete_op_depth = -1;
-  int min_op_depth = 1; /* Never touch BASE */
-
-  *op_depth = relpath_depth(local_relpath);
-  *np_op_depth = -1;
-
-  svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
-  *parent_op_depth = relpath_depth(parent_relpath);
-
-  if (!copyfrom_relpath)
-    return SVN_NO_ERROR;
-
-  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(&have_row, stmt));
-  if (have_row)
-    {
-      svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
-                                                            presence_map);
-
-      min_op_depth = svn_sqlite__column_int(stmt, 0);
-      if (status == svn_wc__db_status_incomplete)
-        incomplete_op_depth = min_op_depth;
-    }
-  SVN_ERR(svn_sqlite__reset(stmt));
-
-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_SELECT_WORKING_NODE));
-  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
-  if (have_row)
-    {
-      svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
-                                                              presence_map);
-
-      *parent_op_depth = svn_sqlite__column_int(stmt, 0);
-      if (*parent_op_depth < min_op_depth)
-        {
-          /* We want to create a copy; not overwrite the lower layers */
-          SVN_ERR(svn_sqlite__reset(stmt));
-          return SVN_NO_ERROR;
-        }
-
-      /* You can only add children below a node that exists.
-         In WORKING that must be status added, which is represented
-         as presence normal */
-      SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
-
-      if ((incomplete_op_depth < 0)
-          || (incomplete_op_depth == *parent_op_depth))
-        {
-          apr_int64_t parent_copyfrom_repos_id
-            = svn_sqlite__column_int64(stmt, 10);
-          const char *parent_copyfrom_relpath
-            = svn_sqlite__column_text(stmt, 11, NULL);
-          svn_revnum_t parent_copyfrom_revision
-            = svn_sqlite__column_revnum(stmt, 12);
-
-          if (parent_copyfrom_repos_id == copyfrom_repos_id)
-            {
-              if (copyfrom_revision == parent_copyfrom_revision
-                  && !strcmp(copyfrom_relpath,
-                             svn_relpath_join(parent_copyfrom_relpath, name,
-                                              scratch_pool)))
-                *op_depth = *parent_op_depth;
-              else if (incomplete_op_depth > 0)
-                *np_op_depth = incomplete_op_depth;
-            }
-        }
-    }
-  SVN_ERR(svn_sqlite__reset(stmt));
+                             path_for_error_message(wcroot,
+                                                    server_excluded_relpath,
+                                                    scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -4867,6 +5139,7 @@ svn_wc__db_op_copy_symlink(svn_wc__db_t 
 svn_error_t *
 svn_wc__db_op_add_directory(svn_wc__db_t *db,
                             const char *local_abspath,
+                            const apr_hash_t *props,
                             const svn_skel_t *work_items,
                             apr_pool_t *scratch_pool)
 {
@@ -4890,6 +5163,11 @@ svn_wc__db_op_add_directory(svn_wc__db_t
   iwb.presence = svn_wc__db_status_normal;
   iwb.kind = svn_kind_dir;
   iwb.op_depth = relpath_depth(local_relpath);
+  if (props && apr_hash_count((apr_hash_t *)props))
+    {
+      iwb.update_actual_props = TRUE;
+      iwb.new_actual_props = props;
+    }
 
   iwb.work_items = work_items;
 
@@ -4907,6 +5185,7 @@ svn_wc__db_op_add_directory(svn_wc__db_t
 svn_error_t *
 svn_wc__db_op_add_file(svn_wc__db_t *db,
                        const char *local_abspath,
+                       const apr_hash_t *props,
                        const svn_skel_t *work_items,
                        apr_pool_t *scratch_pool)
 {
@@ -4930,6 +5209,11 @@ svn_wc__db_op_add_file(svn_wc__db_t *db,
   iwb.presence = svn_wc__db_status_normal;
   iwb.kind = svn_kind_file;
   iwb.op_depth = relpath_depth(local_relpath);
+  if (props && apr_hash_count((apr_hash_t *)props))
+    {
+      iwb.update_actual_props = TRUE;
+      iwb.new_actual_props = props;
+    }
 
   iwb.work_items = work_items;
 
@@ -4945,6 +5229,7 @@ svn_error_t *
 svn_wc__db_op_add_symlink(svn_wc__db_t *db,
                           const char *local_abspath,
                           const char *target,
+                          const apr_hash_t *props,
                           const svn_skel_t *work_items,
                           apr_pool_t *scratch_pool)
 {
@@ -4971,6 +5256,11 @@ svn_wc__db_op_add_symlink(svn_wc__db_t *
   iwb.presence = svn_wc__db_status_normal;
   iwb.kind = svn_kind_symlink;
   iwb.op_depth = relpath_depth(local_relpath);
+  if (props && apr_hash_count((apr_hash_t *)props))
+    {
+      iwb.update_actual_props = TRUE;
+      iwb.new_actual_props = props;
+    }
 
   iwb.target = target;
 
@@ -4983,13 +5273,14 @@ svn_wc__db_op_add_symlink(svn_wc__db_t *
   return SVN_NO_ERROR;
 }
 
+/* Baton for passing args to db_record_fileinfo(). */
 struct record_baton_t {
-  svn_filesize_t translated_size;
-  apr_time_t last_mod_time;
+  svn_filesize_t recorded_size;
+  apr_time_t recorded_time;
 };
 
 
-/* Record TRANSLATED_SIZE and LAST_MOD_TIME into top layer in NODES */
+/* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
 static svn_error_t *
 db_record_fileinfo(void *baton,
                    svn_wc__db_wcroot_t *wcroot,
@@ -5003,7 +5294,7 @@ db_record_fileinfo(void *baton,
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_UPDATE_NODE_FILEINFO));
   SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
-                            rb->translated_size, rb->last_mod_time));
+                            rb->recorded_size, rb->recorded_time));
   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
 
   SVN_ERR_ASSERT(affected_rows == 1);
@@ -5015,8 +5306,8 @@ db_record_fileinfo(void *baton,
 svn_error_t *
 svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
                                   const char *local_abspath,
-                                  svn_filesize_t translated_size,
-                                  apr_time_t last_mod_time,
+                                  svn_filesize_t recorded_size,
+                                  apr_time_t recorded_time,
                                   apr_pool_t *scratch_pool)
 {
   svn_wc__db_wcroot_t *wcroot;
@@ -5029,8 +5320,8 @@ svn_wc__db_global_record_fileinfo(svn_wc
                               local_abspath, scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
-  rb.translated_size = translated_size;
-  rb.last_mod_time = last_mod_time;
+  rb.recorded_size = recorded_size;
+  rb.recorded_time = recorded_time;
 
   SVN_ERR(db_record_fileinfo(&rb, wcroot, local_relpath, scratch_pool));
 
@@ -5041,18 +5332,12 @@ svn_wc__db_global_record_fileinfo(svn_wc
 }
 
 
-struct set_props_baton_t
-{
-  apr_hash_t *props;
-  svn_boolean_t clear_recorded_info;
-
-  const svn_skel_t *conflict;
-  const svn_skel_t *work_items;
-};
-
-
 /* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
- * PROPS. */
+ * PROPS.
+ *
+ * Note: PROPS=NULL means the actual props are the same as the pristine
+ * 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,
                  const char *local_relpath,
@@ -5084,50 +5369,56 @@ set_actual_props(apr_int64_t wc_id,
 }
 
 
-/* Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
+/* The body of svn_wc__db_op_set_props().
+
+   Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
    Create an entry in the ACTUAL table for the node if it does not yet
    have one.
    To specify no properties, BATON->props must be an empty hash, not NULL.
-   BATON is of type 'struct set_props_baton_t'. */
+   BATON is of type 'struct set_props_baton_t'.
+*/
 static svn_error_t *
-set_props_txn(void *baton,
-              svn_wc__db_wcroot_t *wcroot,
+set_props_txn(svn_wc__db_wcroot_t *wcroot,
               const char *local_relpath,
+              apr_hash_t *props,
+              svn_boolean_t clear_recorded_info,
+              const svn_skel_t *conflict,
+              const svn_skel_t *work_items,
               apr_pool_t *scratch_pool)
 {
-  struct set_props_baton_t *spb = baton;
   apr_hash_t *pristine_props;
 
   /* Check if the props are modified. If no changes, then wipe out the
      ACTUAL props.  PRISTINE_PROPS==NULL means that any
      ACTUAL props are okay as provided, so go ahead and set them.  */
-  SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath,
+  SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
                                  scratch_pool, scratch_pool));
-  if (spb->props && pristine_props)
+  if (props && pristine_props)
     {
       apr_array_header_t *prop_diffs;
 
-      SVN_ERR(svn_prop_diffs(&prop_diffs, spb->props, pristine_props,
+      SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
                              scratch_pool));
       if (prop_diffs->nelts == 0)
-        spb->props = NULL;
+        props = NULL;
     }
 
   SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath,
-                           spb->props, wcroot->sdb, scratch_pool));
+                           props, wcroot->sdb, scratch_pool));
 
-  if (spb->clear_recorded_info)
+  if (clear_recorded_info)
     {
       struct record_baton_t rb;
-      rb.translated_size = SVN_INVALID_FILESIZE;
-      rb.last_mod_time = 0;
+      rb.recorded_size = SVN_INVALID_FILESIZE;
+      rb.recorded_time = 0;
       SVN_ERR(db_record_fileinfo(&rb, wcroot, local_relpath, scratch_pool));
     }
 
   /* And finally.  */
-  SVN_ERR(add_work_items(wcroot->sdb, spb->work_items, scratch_pool));
-  if (spb->conflict)
-    SVN_ERR(mark_conflict(wcroot, local_relpath, spb->conflict, 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,
+                                              conflict, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -5142,7 +5433,6 @@ svn_wc__db_op_set_props(svn_wc__db_t *db
                         const svn_skel_t *work_items,
                         apr_pool_t *scratch_pool)
 {
-  struct set_props_baton_t spb;
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
 
@@ -5152,77 +5442,13 @@ svn_wc__db_op_set_props(svn_wc__db_t *db
                               db, local_abspath, scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
-  spb.props = props;
-  spb.clear_recorded_info = clear_recorded_info;
-  spb.conflict = conflict;
-  spb.work_items = work_items;
-
-  return svn_error_trace(svn_wc__db_with_txn(wcroot, local_relpath,
-                                             set_props_txn, &spb,
-                                             scratch_pool));
-}
-
-
-#ifdef SVN__SUPPORT_BASE_MERGE
-
-/* Set properties in a given table. The row must exist.  */
-static svn_error_t *
-set_properties(svn_wc__db_t *db,
-               const char *local_abspath,
-               const apr_hash_t *props,
-               int stmt_idx,
-               const char *table_name,
-               apr_pool_t *scratch_pool)
-{
-  svn_sqlite__stmt_t *stmt;
-  int affected_rows;
-
-  SVN_ERR_ASSERT(props != NULL);
-
-  SVN_ERR(get_statement_for_path(&stmt, db, local_abspath, stmt_idx,
-                                 scratch_pool));
-
-  SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
-  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
-
-  if (affected_rows != 1)
-    return svn_error_createf(SVN_ERR_WC_DB_ERROR, NULL,
-                             _("Can't store properties for '%s' in '%s'."),
-                             svn_dirent_local_style(local_abspath,
-                                                    scratch_pool),
-                             table_name);
-
-  return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
-svn_wc__db_temp_base_set_props(svn_wc__db_t *db,
-                               const char *local_abspath,
-                               const apr_hash_t *props,
-                               apr_pool_t *scratch_pool)
-{
-  SVN_ERR(set_properties(db, local_abspath, props,
-                         STMT_UPDATE_NODE_BASE_PROPS,
-                         "base node", scratch_pool));
-  return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
-svn_wc__db_temp_working_set_props(svn_wc__db_t *db,
-                                  const char *local_abspath,
-                                  const apr_hash_t *props,
-                                  apr_pool_t *scratch_pool)
-{
-  SVN_ERR(set_properties(db, local_abspath, props,
-                         STMT_UPDATE_NODE_WORKING_PROPS,
-                         "working node", scratch_pool));
+  SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
+                                    clear_recorded_info, conflict, work_items,
+                                    scratch_pool),
+                      wcroot);
   return SVN_NO_ERROR;
 }
 
-#endif /* SVN__SUPPORT_BASE_MERGE  */
-
 
 svn_error_t *
 svn_wc__db_op_modified(svn_wc__db_t *db,
@@ -5399,7 +5625,9 @@ struct set_changelist_baton_t
 };
 
 
-/* */
+/* The main part of svn_wc__db_op_set_changelist().
+ *
+ * Implements svn_wc__db_txn_callback_t. */
 static svn_error_t *
 set_changelist_txn(void *baton,
                    svn_wc__db_wcroot_t *wcroot,
@@ -5457,7 +5685,9 @@ set_changelist_txn(void *baton,
 }
 
 
-/* Implement work_callback_t. */
+/* Send notifications for svn_wc__db_op_set_changelist().
+ *
+ * Implements work_callback_t. */
 static svn_error_t *
 do_changelist_notify(void *baton,

[... 4135 lines stripped ...]