Modified: subversion/branches/pin-externals/subversion/libsvn_wc/wc-queries.sql
URL: 
http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/libsvn_wc/wc-queries.sql?rev=1658128&r1=1658127&r2=1658128&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/libsvn_wc/wc-queries.sql 
(original)
+++ subversion/branches/pin-externals/subversion/libsvn_wc/wc-queries.sql Sun 
Feb  8 03:10:25 2015
@@ -257,30 +257,35 @@ WHERE wc_id = ?1
   AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND op_depth = ?3
 
--- STMT_DELETE_WORKING_OP_DEPTH_ABOVE
-DELETE FROM nodes
-WHERE wc_id = ?1 
-  AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
-  AND op_depth > ?3
-
--- STMT_SELECT_LOCAL_RELPATH_OP_DEPTH
-SELECT local_relpath, kind
-FROM nodes
-WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
+/* Full layer replacement check code for handling moves
+The op_root must exist (or there is no layer to replace) and an op-root
+   always has presence 'normal' */
+-- STMT_SELECT_LAYER_FOR_REPLACE
+SELECT s.local_relpath, s.kind,
+  RELPATH_SKIP_JOIN(?2, ?4, s.local_relpath) drp, 'normal', 0
+FROM nodes s
+WHERE s.wc_id = ?1 AND s.local_relpath = ?2 AND s.op_depth = ?3
 UNION ALL
-SELECT local_relpath, kind
-FROM nodes
-WHERE wc_id = ?1
-  AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
-  AND op_depth = ?3
-ORDER BY local_relpath
+SELECT s.local_relpath, s.kind,
+  RELPATH_SKIP_JOIN(?2, ?4, s.local_relpath) drp, d.presence,
+  EXISTS(SELECT * FROM nodes sh
+         WHERE sh.wc_id = ?1 AND sh.op_depth > ?5
+           AND sh.local_relpath = d.local_relpath) shadowed
+FROM nodes s
+LEFT OUTER JOIN nodes d ON d.wc_id= ?1 AND d.op_depth = ?5
+     AND d.local_relpath = drp
+WHERE s.wc_id = ?1
+  AND IS_STRICT_DESCENDANT_OF(s.local_relpath, ?2)
+  AND s.op_depth = ?3
+ORDER BY s.local_relpath
 
--- STMT_SELECT_CHILDREN_OP_DEPTH
+-- STMT_SELECT_DESCENDANTS_OP_DEPTH_RV
 SELECT local_relpath, kind
 FROM nodes
 WHERE wc_id = ?1
   AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
   AND op_depth = ?3
+  AND presence in (MAP_NORMAL, MAP_INCOMPLETE)
 ORDER BY local_relpath DESC
 
 -- STMT_COPY_NODE_MOVE
@@ -302,6 +307,24 @@ SELECT
 FROM nodes
 WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3
 
+-- STMT_SELECT_NO_LONGER_MOVED_RV
+SELECT d.local_relpath, RELPATH_SKIP_JOIN(?2, ?4, d.local_relpath) srp,
+       b.presence, b.op_depth
+FROM nodes d
+LEFT OUTER JOIN nodes b ON b.wc_id = ?1 AND b.local_relpath = d.local_relpath
+            AND b.op_depth = (SELECT MAX(x.op_depth) FROM nodes x
+                              WHERE x.wc_id = ?1
+                                AND x.local_relpath = b.local_relpath
+                                AND x.op_depth < ?3)
+WHERE d.wc_id = ?1
+  AND IS_STRICT_DESCENDANT_OF(d.local_relpath, ?2)
+  AND d.op_depth = ?3
+  AND NOT EXISTS(SELECT * FROM nodes s
+                 WHERE s.wc_id = ?1
+                   AND s.local_relpath = srp
+                   AND s.op_depth = ?5)
+ORDER BY d.local_relpath DESC
+
 -- STMT_SELECT_OP_DEPTH_CHILDREN
 SELECT local_relpath, kind FROM nodes
 WHERE wc_id = ?1 
@@ -452,16 +475,12 @@ WHERE wc_id = ?1 AND local_relpath = ?2
 ORDER BY op_depth DESC
 
 -- STMT_SELECT_OP_DEPTH_MOVED_TO
-SELECT n.op_depth, n.moved_to, p.repos_path, p.revision
-FROM nodes p
-LEFT JOIN nodes n
-  ON p.wc_id=n.wc_id AND p.local_relpath = n.local_relpath
- AND  n.op_depth = (SELECT MIN(d.op_depth)
-                    FROM nodes d
-                    WHERE d.wc_id = ?1
-                      AND d.local_relpath = n.local_relpath
-                      AND d.op_depth > ?3)
-WHERE p.wc_id = ?1 AND p.local_relpath = ?2 AND p.op_depth = ?3
+SELECT op_depth, moved_to
+FROM nodes
+WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3
+  AND EXISTS(SELECT * from nodes
+             WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3)
+ORDER BY op_depth ASC
 LIMIT 1
 
 -- STMT_SELECT_MOVED_TO
@@ -949,17 +968,6 @@ INSERT INTO nodes (
     parent_relpath, presence, kind)
 VALUES(?1, ?2, ?3, ?4, MAP_BASE_DELETED, ?5)
 
--- STMT_DELETE_NO_LOWER_LAYER
-DELETE FROM nodes
- WHERE wc_id = ?1
-   AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
-   AND op_depth = ?3
-   AND NOT EXISTS (SELECT 1 FROM nodes n
-                    WHERE n.wc_id = ?1
-                    AND n.local_relpath = nodes.local_relpath
-                    AND n.op_depth = ?4
-                    AND n.presence IN (MAP_NORMAL, MAP_INCOMPLETE))
-
 -- STMT_REPLACE_WITH_BASE_DELETED
 INSERT OR REPLACE INTO nodes (wc_id, local_relpath, op_depth, parent_relpath,
                               kind, moved_to, presence)
@@ -967,7 +975,7 @@ SELECT wc_id, local_relpath, op_depth, p
        kind, moved_to, MAP_BASE_DELETED
   FROM nodes
  WHERE wc_id = ?1
-   AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
+   AND local_relpath = ?2
    AND op_depth = ?3
 
 /* If this query is updated, STMT_INSERT_DELETE_LIST should too.
@@ -1018,11 +1026,30 @@ WHERE wc_id = ?1
  AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
  AND op_depth = ?3
 
--- STMT_UPDATE_OP_DEPTH_RECURSIVE
-UPDATE nodes SET op_depth = ?4, moved_here = NULL
-WHERE wc_id = ?1
- AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
- AND op_depth = ?3
+/* Duplicated SELECT body to avoid creating temporary table */
+-- STMT_COPY_OP_DEPTH_RECURSIVE
+INSERT INTO nodes (
+    wc_id, local_relpath, op_depth, parent_relpath, repos_id, repos_path,
+    revision, presence, depth, kind, changed_revision, changed_date,
+    changed_author, checksum, properties, translated_size, last_mod_time,
+    symlink_target, moved_here, moved_to )
+SELECT
+    wc_id, local_relpath, ?4, parent_relpath, repos_id,
+    repos_path, revision, presence, depth, kind, changed_revision,
+    changed_date, changed_author, checksum, properties, translated_size,
+    last_mod_time, symlink_target, NULL, NULL
+FROM nodes
+WHERE wc_id = ?1 AND op_depth = ?3 AND local_relpath = ?2
+UNION ALL
+SELECT
+    wc_id, local_relpath, ?4, parent_relpath, repos_id,
+    repos_path, revision, presence, depth, kind, changed_revision,
+    changed_date, changed_author, checksum, properties, translated_size,
+    last_mod_time, symlink_target, NULL, NULL
+FROM nodes
+WHERE wc_id = ?1 AND op_depth = ?3
+  AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
+ORDER BY local_relpath
 
 -- STMT_DOES_NODE_EXIST
 SELECT 1 FROM nodes WHERE wc_id = ?1 AND local_relpath = ?2
@@ -1592,16 +1619,29 @@ UPDATE nodes SET moved_to = NULL
    AND IS_STRICT_DESCENDANT_OF(moved_to, ?2)
 
 -- STMT_SELECT_MOVED_PAIR3
-SELECT local_relpath, moved_to, op_depth, kind FROM nodes
-WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > ?3
-  AND moved_to IS NOT NULL
+SELECT n.local_relpath, d.moved_to, d.op_depth, n.kind
+FROM nodes n
+JOIN nodes d ON d.wc_id = ?1 AND d.local_relpath = n.local_relpath
+ AND d.op_depth = (SELECT MIN(dd.op_depth)
+                    FROM nodes dd
+                    WHERE dd.wc_id = ?1
+                      AND dd.local_relpath = d.local_relpath
+                      AND dd.op_depth > ?3)
+WHERE n.wc_id = ?1 AND n.local_relpath = ?2 AND n.op_depth = ?3
+  AND d.moved_to IS NOT NULL
 UNION ALL
-SELECT local_relpath, moved_to, op_depth, kind FROM nodes
-WHERE wc_id = ?1
-  AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
-  AND op_depth > ?3
-  AND moved_to IS NOT NULL
-ORDER BY local_relpath, op_depth
+SELECT n.local_relpath, d.moved_to, d.op_depth, n.kind
+FROM nodes n
+JOIN nodes d ON d.wc_id = ?1 AND d.local_relpath = n.local_relpath
+ AND d.op_depth = (SELECT MIN(dd.op_depth)
+                    FROM nodes dd
+                    WHERE dd.wc_id = ?1
+                      AND dd.local_relpath = d.local_relpath
+                      AND dd.op_depth > ?3)
+WHERE n.wc_id = ?1 AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)
+  AND n.op_depth = ?3
+  AND d.moved_to IS NOT NULL
+ORDER BY n.local_relpath
 
 -- STMT_SELECT_MOVED_OUTSIDE
 SELECT local_relpath, moved_to, op_depth FROM nodes
@@ -1613,16 +1653,12 @@ WHERE wc_id = ?1
 
 -- STMT_SELECT_OP_DEPTH_MOVED_PAIR
 SELECT n.local_relpath, p.kind, n.moved_to, p.repos_path
-FROM nodes AS n
-JOIN (SELECT local_relpath, kind, repos_path
-      FROM nodes AS o
-        WHERE o.wc_id = ?1
-          AND o.op_depth=(SELECT MAX(d.op_depth)
-                          FROM nodes AS d
-                          WHERE d.wc_id = ?1
-                            AND d.local_relpath = o.local_relpath
-                            AND d.op_depth < ?3)) AS p
-  ON n.local_relpath = p.local_relpath
+FROM nodes n
+JOIN nodes p ON p.wc_id = ?1 AND p.local_relpath = ?2
+ AND p.op_depth=(SELECT MAX(d.op_depth)
+                 FROM nodes d
+                 WHERE d.wc_id = ?1 AND d.local_relpath = ?2
+                   AND d.op_depth < ?3)
 WHERE n.wc_id = ?1
   AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)
   AND n.op_depth = ?3

Modified: subversion/branches/pin-externals/subversion/libsvn_wc/wc_db.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/libsvn_wc/wc_db.c?rev=1658128&r1=1658127&r2=1658128&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/pin-externals/subversion/libsvn_wc/wc_db.c Sun Feb  8 
03:10:25 2015
@@ -569,12 +569,54 @@ blank_ibb(insert_base_baton_t *pibb)
 }
 
 
-svn_error_t *
-svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
-                                const char *local_relpath,
-                                svn_node_kind_t kind,
-                                int op_depth,
-                                apr_pool_t *scratch_pool)
+/* Extend any delete of the parent of LOCAL_RELPATH to LOCAL_RELPATH.
+
+   ### What about KIND and OP_DEPTH?  KIND ought to be redundant; I'm
+       discussing on dev@ whether we can let that be null for presence
+       == base-deleted.  OP_DEPTH is the op-depth of what, and why?
+       It is used to select the lowest working node higher than OP_DEPTH,
+       so, in terms of the API, OP_DEPTH means ...?
+
+   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 node if the parent has a higher working node then the
+   parent node is deleted (or replaced) and the delete must be extended
+   to cover new 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.
+
+   If ADDED_DELETE is not NULL, set *ADDED_DELETE to TRUE if a new delete
+   was recorded, otherwise to FALSE.
+ */
+static svn_error_t *
+db_extend_parent_delete(svn_boolean_t *added_delete,
+                        svn_wc__db_wcroot_t *wcroot,
+                        const char *local_relpath,
+                        svn_node_kind_t kind,
+                        int op_depth,
+                        apr_pool_t *scratch_pool)
 {
   svn_boolean_t have_row;
   svn_sqlite__stmt_t *stmt;
@@ -583,6 +625,9 @@ svn_wc__db_extend_parent_delete(svn_wc__
 
   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,
@@ -609,6 +654,9 @@ svn_wc__db_extend_parent_delete(svn_wc__
                                     local_relpath, parent_op_depth,
                                     parent_relpath, kind_map, kind));
           SVN_ERR(svn_sqlite__update(NULL, stmt));
+
+          if (added_delete)
+            *added_delete = TRUE;
         }
     }
 
@@ -616,7 +664,7 @@ svn_wc__db_extend_parent_delete(svn_wc__
 }
 
 
-/* This is the reverse of svn_wc__db_extend_parent_delete.
+/* This is the reverse of db_extend_parent_delete.
 
    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
@@ -626,11 +674,11 @@ svn_wc__db_extend_parent_delete(svn_wc__
    only uses this function within an sqlite transaction if atomic
    behavior is needed.
  */
-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)
+static svn_error_t *
+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_boolean_t have_row;
@@ -833,16 +881,17 @@ insert_base_node(const insert_base_baton
               || (pibb->status == svn_wc__db_status_incomplete))
           && ! pibb->file_external)
         {
-          SVN_ERR(svn_wc__db_extend_parent_delete(wcroot, local_relpath,
-                                                  pibb->kind, 0,
-                                                  scratch_pool));
+          SVN_ERR(db_extend_parent_delete(NULL,
+                                          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(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
-                                                   scratch_pool));
+          SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0,
+                                           scratch_pool));
         }
     }
 
@@ -2387,8 +2436,7 @@ 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));
 
-  SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
-                                           scratch_pool));
+  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)
@@ -4797,6 +4845,187 @@ 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;
+  svn_boolean_t have_row;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  int dst_op_depth = relpath_depth(dst_op_relpath);
+  svn_boolean_t locked;
+  svn_error_t *err = NULL;
+
+  SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, dst_op_relpath,
+                                               FALSE, scratch_pool));
+
+  if (!locked)
+    return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
+                             _("No write-lock in '%s'"),
+                             path_for_error_message(wcroot, dst_op_relpath,
+                                                    scratch_pool));
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
+                                    STMT_COPY_NODE_MOVE));
+
+  /* Replace entire subtree at one op-depth. */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_LAYER_FOR_REPLACE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
+                            src_op_relpath, src_op_depth,
+                            dst_op_relpath, dst_op_depth));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  while (have_row)
+    {
+      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,
+                              svn_relpath_dirname(dst_relpath, iterpool));
+      if (!err)
+        err = svn_sqlite__step_done(stmt2);
+
+      /* stmt2 is reset (never modified or by step_done) */
+
+      if (err)
+        break;
+
+      if (strlen(dst_relpath) > strlen(dst_op_relpath))
+        {
+          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;
+            }
+
+          if (exists)
+            {
+              svn_wc__db_status_t presence;
+
+              presence = svn_sqlite__column_token(stmt, 3, presence_map);
+
+              if (presence == svn_wc__db_status_not_present)
+                exists = FALSE;
+            }
+
+          /* ### Fails in a few tests... Needs further research */
+          /*SVN_ERR_ASSERT(!(exists && added_delete));*/
+
+          if (!exists)
+            {
+              svn_boolean_t shadowed;
+
+              shadowed = svn_sqlite__column_int(stmt, 4);
+
+              /*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;
+                }*/
+            }
+        }
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+
+  SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
+
+  /* And now remove the records that are no longer needed */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_NO_LONGER_MOVED_RV));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
+                            dst_op_relpath, dst_op_depth,
+                            src_op_relpath, src_op_depth));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  while (have_row)
+    {
+      const char *dst_relpath;
+      svn_wc__db_status_t shadowed_presence;
+
+      svn_pool_clear(iterpool);
+
+      dst_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
+
+      if (!svn_sqlite__column_is_null(stmt, 2))
+        shadowed_presence = svn_sqlite__column_token(stmt, 2, presence_map);
+      else
+        shadowed_presence = svn_wc__db_status_not_present;
+
+      if (shadowed_presence != svn_wc__db_status_normal
+          && shadowed_presence != svn_wc__db_status_incomplete)
+        {
+          err = svn_sqlite__get_statement(&stmt2, wcroot->sdb,
+                                            STMT_DELETE_NODE);
+        }
+      else
+        {
+          err =svn_sqlite__get_statement(&stmt2, wcroot->sdb,
+                                         STMT_REPLACE_WITH_BASE_DELETED);
+        }
+
+      if (!err)
+        err = svn_sqlite__bindf(stmt2, "isd", wcroot->wc_id, dst_relpath,
+                                             dst_op_depth);
+
+      if (!err)
+        err = svn_sqlite__step_done(stmt2);
+
+      /* stmt2 is reset (never modified or by step_done) */
+
+      if (err)
+        break;
+
+      /* Retract base-delete for the node itself */
+      err = db_retract_parent_delete(wcroot, dst_relpath, dst_op_depth,
+                                     scratch_pool);
+
+      if (err)
+        break;
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+  svn_pool_destroy(iterpool);
+
+  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)
+    SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, dst_op_relpath /* ## */,
+                                              conflict, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 /* The txn body of svn_wc__db_op_handle_move_back */
 static svn_error_t *
 handle_move_back(svn_boolean_t *moved_back,
@@ -12518,7 +12747,7 @@ svn_wc__db_scan_moved(const char **moved
   return SVN_NO_ERROR;
 }
 
-/* ###
+/* ### Recursive helper for svn_wc__db_follow_moved_to()
  */
 static svn_error_t *
 follow_moved_to(svn_wc__db_wcroot_t *wcroot,
@@ -12530,11 +12759,13 @@ follow_moved_to(svn_wc__db_wcroot_t *wcr
 {
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
-  int working_op_depth;
+  int shadowing_op_depth;
   const char *ancestor_relpath;
   const char *node_moved_to = NULL;
   int i;
 
+  /* Obtain the depth of the node directly shadowing local_relpath
+     as it exists at OP_DEPTH, and perhaps moved to info */
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_SELECT_OP_DEPTH_MOVED_TO));
   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
@@ -12542,7 +12773,7 @@ follow_moved_to(svn_wc__db_wcroot_t *wcr
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
   if (have_row)
     {
-      working_op_depth = svn_sqlite__column_int(stmt, 0);
+      shadowing_op_depth = svn_sqlite__column_int(stmt, 0);
       node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
 
       if (node_moved_to)
@@ -12550,7 +12781,7 @@ follow_moved_to(svn_wc__db_wcroot_t *wcr
           struct svn_wc__db_moved_to_t *moved_to;
 
           moved_to = apr_palloc(result_pool, sizeof(*moved_to));
-          moved_to->op_depth = working_op_depth;
+          moved_to->op_depth = shadowing_op_depth;
           moved_to->local_relpath = node_moved_to;
           APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = 
moved_to;
         }
@@ -12559,16 +12790,18 @@ follow_moved_to(svn_wc__db_wcroot_t *wcr
   SVN_ERR(svn_sqlite__reset(stmt));
 
   if (!have_row)
-    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
-                             _("The node '%s' was not found."),
-                             path_for_error_message(wcroot, local_relpath,
-                                                    scratch_pool));
+    {
+      /* Node is not shadowed, so not moved */
+      return SVN_NO_ERROR;
+    }
   else if (node_moved_to)
-    return SVN_NO_ERROR;
-
+    {
+      /* Moved directly, so we have the final location */
+      return SVN_NO_ERROR;
+    }
   /* Need to handle being moved via an ancestor. */
   ancestor_relpath = local_relpath;
-  for (i = relpath_depth(local_relpath); i > working_op_depth; --i)
+  for (i = relpath_depth(local_relpath); i > shadowing_op_depth; --i)
     {
       const char *ancestor_moved_to;
 
@@ -12577,7 +12810,7 @@ follow_moved_to(svn_wc__db_wcroot_t *wcr
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                         STMT_SELECT_MOVED_TO));
       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
-                                working_op_depth));
+                                shadowing_op_depth));
       SVN_ERR(svn_sqlite__step_row(stmt));
 
       ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
@@ -12593,7 +12826,7 @@ follow_moved_to(svn_wc__db_wcroot_t *wcr
                                  result_pool);
 
           moved_to = apr_palloc(result_pool, sizeof(*moved_to));
-          moved_to->op_depth = working_op_depth;
+          moved_to->op_depth = shadowing_op_depth;
           moved_to->local_relpath = node_moved_to;
           APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = 
moved_to;
 
@@ -14621,8 +14854,6 @@ static svn_error_t *
 make_copy_txn(svn_wc__db_wcroot_t *wcroot,
               const char *local_relpath,
               int op_depth,
-              const svn_skel_t *conflicts,
-              const svn_skel_t *work_items,
               apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
@@ -14703,21 +14934,68 @@ make_copy_txn(svn_wc__db_wcroot_t *wcroo
 
       copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
 
-      SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, NULL, NULL,
-                            iterpool));
+      SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, iterpool));
     }
 
-  SVN_ERR(flush_entries(wcroot, svn_dirent_join(wcroot->abspath, local_relpath,
-                                                iterpool),
-                                                svn_depth_empty, iterpool));
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot,
+                                 const char *local_relpath,
+                                 const svn_skel_t *conflicts,
+                                 const svn_skel_t *work_items,
+                                 apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+  int op_depth = -1;
+
+  /* The update editor is supposed to call this function when there is
+     no working node for LOCAL_ABSPATH. */
+  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)
+    op_depth = svn_sqlite__column_int(stmt, 0);
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  if (have_row)
+    {
+      if (op_depth == relpath_depth(local_relpath))
+        return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
+                             _("Modification of '%s' already exists"),
+                             path_for_error_message(wcroot,
+                                                    local_relpath,
+                                                    scratch_pool));
+
+      /* We have a working layer, but not one at the op-depth of local-relpath,
+         so we can create a copy by just copying the lower layer */
+
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_COPY_OP_DEPTH_RECURSIVE));
+      SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath,
+                                op_depth, relpath_depth(local_relpath)));
+      SVN_ERR(svn_sqlite__step_done(stmt));
+    }
+  else
+    {
+      /* We don't allow copies to contain server-excluded nodes;
+         the update editor is going to have to bail out. */
+      SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, 
scratch_pool));
+
+      SVN_ERR(make_copy_txn(wcroot, local_relpath,
+                            relpath_depth(local_relpath), scratch_pool));
+    }
 
   if (conflicts)
     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
-                                              conflicts, iterpool));
+                                              conflicts, scratch_pool));
 
-  SVN_ERR(add_work_items(wcroot->sdb, work_items, iterpool));
-
-  svn_pool_destroy(iterpool);
+  SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -14732,8 +15010,6 @@ svn_wc__db_op_make_copy(svn_wc__db_t *db
 {
   svn_wc__db_wcroot_t *wcroot;
   const char *local_relpath;
-  svn_sqlite__stmt_t *stmt;
-  svn_boolean_t have_row;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
@@ -14741,30 +15017,14 @@ svn_wc__db_op_make_copy(svn_wc__db_t *db
                               local_abspath, scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
-  /* The update editor is supposed to call this function when there is
-     no working node for LOCAL_ABSPATH. */
-  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));
-  SVN_ERR(svn_sqlite__reset(stmt));
-  if (have_row)
-    return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
-                             _("Modification of '%s' already exists"),
-                             path_for_error_message(wcroot,
-                                                    local_relpath,
-                                                    scratch_pool));
-
-  /* We don't allow copies to contain server-excluded nodes;
-     the update editor is going to have to bail out. */
-  SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, scratch_pool));
-
   SVN_WC__DB_WITH_TXN(
-    make_copy_txn(wcroot, local_relpath,
-                  relpath_depth(local_relpath), conflicts, work_items,
-                  scratch_pool),
+    svn_wc__db_op_make_copy_internal(wcroot, local_relpath, conflicts, 
work_items,
+                                     scratch_pool),
     wcroot);
 
+  SVN_ERR(flush_entries(wcroot, local_abspath,
+                        svn_depth_infinity, scratch_pool));
+
   return SVN_NO_ERROR;
 }
 

Modified: subversion/branches/pin-externals/subversion/libsvn_wc/wc_db.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/libsvn_wc/wc_db.h?rev=1658128&r1=1658127&r2=1658128&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/branches/pin-externals/subversion/libsvn_wc/wc_db.h Sun Feb  8 
03:10:25 2015
@@ -3246,9 +3246,21 @@ svn_wc__db_temp_op_end_directory_update(
                                         apr_pool_t *scratch_pool);
 
 
-/* Copy the base tree at LOCAL_ABSPATH into the working tree as copy,
-   leaving any subtree additions and copies as-is.  This allows the
-   base node tree to be removed. */
+/* When local_abspath has no WORKING layer, copy the base tree at
+   LOCAL_ABSPATH into the working tree as copy, leaving any subtree
+   additions and copies as-is.  This may introduce multiple layers if
+   the tree is mixed revision.
+
+   When local_abspath has a WORKING node, but is not an op-root, copy
+   all descendants at the same op-depth to the op-depth of local_abspath,
+   thereby turning this node in a copy of what was already there.
+
+   Fails with a SVN_ERR_WC_PATH_UNEXPECTED_STATUS error if LOCAL_RELPATH
+   is already an op-root (as in that case it can't be copied as that
+   would overwrite what is already there).
+
+   After this operation the copied layer (E.g. BASE) can be removed, without
+   the WORKING nodes chaning. Typical usecase: tree conflict handling */
 svn_error_t *
 svn_wc__db_op_make_copy(svn_wc__db_t *db,
                         const char *local_abspath,

Modified: subversion/branches/pin-externals/subversion/libsvn_wc/wc_db_private.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/pin-externals/subversion/libsvn_wc/wc_db_private.h?rev=1658128&r1=1658127&r2=1658128&view=diff
==============================================================================
--- subversion/branches/pin-externals/subversion/libsvn_wc/wc_db_private.h 
(original)
+++ subversion/branches/pin-externals/subversion/libsvn_wc/wc_db_private.h Sun 
Feb  8 03:10:25 2015
@@ -394,58 +394,27 @@ svn_wc__db_get_children_op_depth(apr_has
                                  apr_pool_t *result_pool,
                                  apr_pool_t *scratch_pool);
 
-
-/* Extend any delete of the parent of LOCAL_RELPATH to LOCAL_RELPATH.
-
-   ### What about KIND and OP_DEPTH?  KIND ought to be redundant; I'm
-       discussing on dev@ whether we can let that be null for presence
-       == base-deleted.  OP_DEPTH is the op-depth of what, and why?
-       It is used to select the lowest working node higher than OP_DEPTH,
-       so, in terms of the API, OP_DEPTH means ...?
-
-   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 node if the parent has a higher working node then the
-   parent node is deleted (or replaced) and the delete must be extended
-   to cover new 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.
- */
+/* Update the single op-depth layer in the move destination subtree
+   rooted at DST_RELPATH to make it match the move source subtree
+   rooted at SRC_RELPATH. */
 svn_error_t *
-svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
-                                const char *local_relpath,
-                                svn_node_kind_t kind,
-                                int op_depth,
-                                apr_pool_t *scratch_pool);
+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);
 
+/* Like svn_wc__db_op_make_copy but with wcroot, local_relpath */
 svn_error_t *
-svn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
+svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot,
                                  const char *local_relpath,
-                                 int op_depth,
+                                 const svn_skel_t *conflicts,
+                                 const svn_skel_t *work_items,
                                  apr_pool_t *scratch_pool);
 
+
 /* Extract the moved-to information for LOCAL_RELPATH at OP-DEPTH by
    examining the lowest working node above OP_DEPTH.  The output paths
    are NULL if there is no move, otherwise:


Reply via email to