Modified: subversion/branches/1.8.x-r1536854/subversion/libsvn_wc/wc_db.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/1.8.x-r1536854/subversion/libsvn_wc/wc_db.c?rev=1640562&r1=1640561&r2=1640562&view=diff
==============================================================================
--- subversion/branches/1.8.x-r1536854/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/1.8.x-r1536854/subversion/libsvn_wc/wc_db.c Wed Nov 19 
14:37:04 2014
@@ -627,6 +627,10 @@ svn_wc__db_extend_parent_delete(svn_wc__
    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.
+
+   This function (like most wcroot functions) assumes that its caller
+   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,
@@ -635,14 +639,60 @@ svn_wc__db_retract_parent_delete(svn_wc_
                                  apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+  int working_depth;
+  svn_wc__db_status_t presence;
+  const char *moved_to;
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_DELETE_LOWEST_WORKING_NODE));
+                                    STMT_SELECT_LOWEST_WORKING_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
                             op_depth));
-  SVN_ERR(svn_sqlite__step_done(stmt));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
 
-  return SVN_NO_ERROR;
+  if (!have_row)
+    return svn_error_trace(svn_sqlite__reset(stmt));
+
+  working_depth = svn_sqlite__column_int(stmt, 0);
+  presence = svn_sqlite__column_token(stmt, 1, presence_map);
+  moved_to = svn_sqlite__column_text(stmt, 3, scratch_pool);
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  if (moved_to)
+    {
+      /* Turn the move into a copy to keep the NODES table valid */
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_CLEAR_MOVED_HERE_RECURSIVE));
+      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
+                                moved_to, relpath_depth(moved_to)));
+      SVN_ERR(svn_sqlite__step_done(stmt));
+
+      /* This leaves just the moved_to information on the origin,
+         which we will remove in the next step */
+    }
+
+  if (presence == svn_wc__db_status_base_deleted)
+    {
+      /* Nothing left to shadow; remove the base-deleted node */
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_NODE));
+    }
+  else if (moved_to)
+    {
+      /* Clear moved to information, as this node is no longer base-deleted */
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_CLEAR_MOVED_TO_RELPATH));
+      }
+  else
+    {
+      /* Nothing to update */
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
+                            working_depth));
+
+  return svn_error_trace(svn_sqlite__update(NULL, stmt));
 }
 
 
@@ -3765,8 +3815,15 @@ 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, FALSE,
-                                 scratch_pool, scratch_pool));
+  if (dst_status != svn_wc__db_status_not_present
+      && dst_status != svn_wc__db_status_excluded
+      && dst_status != svn_wc__db_status_server_excluded)
+    {
+      SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
+                                     scratch_pool, scratch_pool));
+    }
+  else
+    props = NULL;
 
   blank_iwb(&iwb);
   iwb.presence = dst_status;
@@ -4076,8 +4133,9 @@ get_info_for_copy(apr_int64_t *copyfrom_
                   svn_wc__db_status_t *status,
                   svn_node_kind_t *kind,
                   svn_boolean_t *op_root,
-                  svn_wc__db_wcroot_t *wcroot,
+                  svn_wc__db_wcroot_t *src_wcroot,
                   const char *local_relpath,
+                  svn_wc__db_wcroot_t *dst_wcroot,
                   apr_pool_t *result_pool,
                   apr_pool_t *scratch_pool)
 {
@@ -4094,7 +4152,7 @@ get_info_for_copy(apr_int64_t *copyfrom_
                     NULL /* have_base */,
                     NULL /* have_more_work */,
                     NULL /* have_work */,
-                    wcroot, local_relpath, result_pool, scratch_pool));
+                    src_wcroot, local_relpath, result_pool, scratch_pool));
 
   if (op_root)
     *op_root = is_op_root;
@@ -4109,7 +4167,7 @@ get_info_for_copy(apr_int64_t *copyfrom_
                        scratch_pool);
       SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
                                 NULL, NULL, NULL,
-                                wcroot, parent_relpath,
+                                src_wcroot, parent_relpath, dst_wcroot,
                                 scratch_pool, scratch_pool));
       if (*copyfrom_relpath)
         *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
@@ -4118,7 +4176,7 @@ get_info_for_copy(apr_int64_t *copyfrom_
   else if (node_status == svn_wc__db_status_added)
     {
       SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
-                            NULL, NULL, NULL, wcroot, local_relpath,
+                            NULL, NULL, NULL, src_wcroot, local_relpath,
                             scratch_pool, scratch_pool));
     }
   else if (node_status == svn_wc__db_status_deleted && is_op_root)
@@ -4127,7 +4185,7 @@ get_info_for_copy(apr_int64_t *copyfrom_
 
       SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
                                 &work_del_relpath,
-                                NULL, wcroot, local_relpath,
+                                NULL, src_wcroot, local_relpath,
                                 scratch_pool, scratch_pool));
       if (work_del_relpath)
         {
@@ -4140,7 +4198,8 @@ get_info_for_copy(apr_int64_t *copyfrom_
           SVN_ERR(scan_addition(NULL, &op_root_relpath,
                                 NULL, NULL, /* repos_* */
                                 copyfrom_relpath, copyfrom_id, copyfrom_rev,
-                                NULL, NULL, NULL, wcroot, parent_del_relpath,
+                                NULL, NULL, NULL,
+                                src_wcroot, parent_del_relpath,
                                 scratch_pool, scratch_pool));
           *copyfrom_relpath
             = svn_relpath_join(*copyfrom_relpath,
@@ -4155,7 +4214,7 @@ get_info_for_copy(apr_int64_t *copyfrom_
                                                     copyfrom_id, NULL, NULL,
                                                     NULL, NULL, NULL, NULL,
                                                     NULL, NULL, NULL, NULL,
-                                                    wcroot, local_relpath,
+                                                    src_wcroot, local_relpath,
                                                     result_pool,
                                                     scratch_pool));
         }
@@ -4177,6 +4236,24 @@ get_info_for_copy(apr_int64_t *copyfrom_
   if (status)
     *status = node_status;
 
+  if (src_wcroot != dst_wcroot && *copyfrom_relpath)
+    {
+      const char *repos_root_url;
+      const char *repos_uuid;
+
+      /* Pass the right repos-id for the destination db. We can't just use
+         the id of the source database, as this value can change after
+         relocation (and perhaps also when we start storing multiple
+         working copies in a single db)! */
+
+      SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
+                                          src_wcroot->sdb, *copyfrom_id,
+                                          scratch_pool));
+
+      SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid,
+                              dst_wcroot->sdb, scratch_pool));
+    }
+
   return SVN_NO_ERROR;
 }
 
@@ -4336,8 +4413,9 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcro
   const apr_array_header_t *children;
 
   SVN_ERR(get_info_for_copy(&copyfrom_id, &copyfrom_relpath, &copyfrom_rev,
-                            &status, &kind, &op_root, src_wcroot,
-                            src_relpath, scratch_pool, scratch_pool));
+                            &status, &kind, &op_root,
+                            src_wcroot, src_relpath, dst_wcroot,
+                            scratch_pool, scratch_pool));
 
   SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
                             &dst_parent_op_depth,
@@ -4562,21 +4640,6 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcro
     }
   else
     {
-      if (copyfrom_relpath)
-        {
-          const char *repos_root_url;
-          const char *repos_uuid;
-
-          /* Pass the right repos-id for the destination db! */
-
-          SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
-                                              src_wcroot->sdb, copyfrom_id,
-                                              scratch_pool));
-
-          SVN_ERR(create_repos_id(&copyfrom_id, repos_root_url, repos_uuid,
-                                  dst_wcroot->sdb, scratch_pool));
-        }
-
       SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
                             dst_relpath, dst_presence, dst_op_depth,
                             dst_np_op_depth, kind,
@@ -5075,6 +5138,17 @@ db_op_copy_shadowed_layer(svn_wc__db_wcr
                                     scratch_pool));
     }
 
+  if (dst_presence == svn_wc__db_status_not_present)
+    {
+      /* Don't create descendants of a not present node! */
+
+      /* This code is currently still triggered by copying deleted nodes
+         between separate working copies. See ### comment above. */
+
+      svn_pool_destroy(iterpool);
+      return SVN_NO_ERROR;
+    }
+
   SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
                                src_op_depth, scratch_pool, iterpool));
 
@@ -7176,7 +7250,7 @@ remove_node_txn(svn_boolean_t *left_chan
   if (local_relpath[0] != '\0')
     {
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_DELETE_NODE));
+                                        STMT_DELETE_NODE_ALL_LAYERS));
       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
       SVN_ERR(svn_sqlite__step_done(stmt));
     }
@@ -7487,8 +7561,86 @@ struct moved_node_t {
 
   /* The op-depth of the deleted node at the source of the move. */
   int op_depth;
+
+  /* When >= 1 the op_depth at which local_relpath was moved to its
+     location. Used to find its original location outside the delete */
+  int moved_from_depth;
 };
 
+/* Helper function to resolve the original location of local_relpath at 
OP_DEPTH
+   before it was moved into the tree rooted at ROOT_RELPATH. */
+static svn_error_t *
+resolve_moved_from(const char **moved_from_relpath,
+                   int *moved_from_op_depth,
+                   svn_wc__db_wcroot_t *wcroot,
+                   const char *root_relpath,
+                   const char *local_relpath,
+                   int op_depth,
+                   apr_pool_t *result_pool,
+                   apr_pool_t *scratch_pool)
+{
+  const char *suffix = "";
+  svn_sqlite__stmt_t *stmt;
+  const char *m_from_relpath;
+  int m_from_op_depth;
+  int m_move_from_depth;
+  svn_boolean_t have_row;
+
+  while (relpath_depth(local_relpath) > op_depth)
+    {
+      const char *name;
+      svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool);
+      suffix = svn_relpath_join(suffix, name, scratch_pool);
+    }
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_MOVED_FROM_FOR_DELETE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is",
+                            wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  if (!have_row)
+    {
+      /* assert(have_row); */
+      *moved_from_relpath = NULL;
+      *moved_from_op_depth = -1;
+
+      SVN_ERR(svn_sqlite__reset(stmt));
+
+      return SVN_NO_ERROR;
+    }
+
+  m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
+  m_from_op_depth = svn_sqlite__column_int(stmt, 1);
+  m_move_from_depth = svn_sqlite__column_int(stmt, 2);
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath))
+    {
+      *moved_from_relpath = svn_relpath_join(m_from_relpath, suffix,
+                                             result_pool);
+      *moved_from_op_depth = m_from_op_depth; /* ### Ok? */
+      return SVN_NO_ERROR;
+    }
+  else if (!m_move_from_depth)
+    {
+      *moved_from_relpath = NULL;
+      *moved_from_op_depth = -1;
+      return SVN_NO_ERROR;
+    }
+
+  return svn_error_trace(
+        resolve_moved_from(moved_from_relpath,
+                           moved_from_op_depth,
+                           wcroot,
+                           root_relpath,
+                           svn_relpath_join(m_from_relpath, suffix,
+                                            scratch_pool),
+                           m_move_from_depth,
+                           result_pool, scratch_pool));
+}
+
 static svn_error_t *
 delete_node(void *baton,
             svn_wc__db_wcroot_t *wcroot,
@@ -7500,19 +7652,70 @@ delete_node(void *baton,
   svn_boolean_t have_row, op_root;
   svn_boolean_t add_work = FALSE;
   svn_sqlite__stmt_t *stmt;
-  int select_depth; /* Depth of what is to be deleted */
-  svn_boolean_t refetch_depth = FALSE;
+  int working_op_depth; /* Depth of what is to be deleted */
+  int keep_op_depth = 0; /* Depth of what is below what is deleted */
   svn_node_kind_t kind;
   apr_array_header_t *moved_nodes = NULL;
-  int delete_depth = relpath_depth(local_relpath);
+  int delete_op_depth = relpath_depth(local_relpath);
 
-  SVN_ERR(read_info(&status,
-                    &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                    &op_root, NULL, NULL,
-                    NULL, NULL, NULL,
-                    wcroot, local_relpath,
-                    scratch_pool, scratch_pool));
+  assert(*local_relpath); /* Can't delete wcroot */
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_SELECT_NODE_INFO));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  if (!have_row)
+    {
+      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));
+    }
+
+  working_op_depth = svn_sqlite__column_int(stmt, 0);
+  status = svn_sqlite__column_token(stmt, 3, presence_map);
+  kind = svn_sqlite__column_token(stmt, 4, kind_map);
+
+  if (working_op_depth < delete_op_depth)
+    {
+      op_root = FALSE;
+      add_work = TRUE;
+      keep_op_depth = working_op_depth;
+    }
+  else
+    {
+      op_root = TRUE;
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+      if (have_row)
+        {
+          svn_wc__db_status_t below_status;
+          int below_op_depth;
+
+          below_op_depth = svn_sqlite__column_int(stmt, 0);
+          below_status = svn_sqlite__column_token(stmt, 3, presence_map);
+
+          if (below_status != svn_wc__db_status_not_present
+              && below_status != svn_wc__db_status_base_deleted)
+            {
+              add_work = TRUE;
+              keep_op_depth = below_op_depth;
+            }
+          else
+            keep_op_depth = 0;
+        }
+      else
+        keep_op_depth = -1;
+    }
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  if (working_op_depth != 0) /* WORKING */
+    SVN_ERR(convert_to_working_status(&status, status));
 
   if (status == svn_wc__db_status_deleted
       || status == svn_wc__db_status_not_present)
@@ -7591,6 +7794,7 @@ delete_node(void *baton,
                                                          part, scratch_pool);
           moved_node->op_depth = move_op_depth;
           moved_node->moved_to_relpath = b->moved_to_relpath;
+          moved_node->moved_from_depth = -1;
 
           APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = 
moved_node;
         }
@@ -7602,8 +7806,9 @@ delete_node(void *baton,
            * possibly because of a nested move operation. */
           moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
           moved_node->local_relpath = local_relpath;
-          moved_node->op_depth = delete_depth;
+          moved_node->op_depth = delete_op_depth;
           moved_node->moved_to_relpath = b->moved_to_relpath;
+          moved_node->moved_from_depth = -1;
 
           APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = 
moved_node;
         }
@@ -7618,24 +7823,18 @@ delete_node(void *baton,
                                              b->moved_to_relpath));
       SVN_ERR(svn_sqlite__update(NULL, stmt));
     }
-  else
-    {
-      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_CLEAR_MOVED_TO_DESCENDANTS));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
-                                            local_relpath));
-      SVN_ERR(svn_sqlite__update(NULL, stmt));
-    }
 
   /* Find children that were moved out of the subtree rooted at this node.
    * We'll need to update their op-depth columns because their deletion
    * is now implied by the deletion of their parent (i.e. this node). */
     {
       apr_pool_t *iterpool;
+      int i;
 
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                         STMT_SELECT_MOVED_FOR_DELETE));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+      SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
+                                delete_op_depth));
 
       SVN_ERR(svn_sqlite__step(&have_row, stmt));
       iterpool = svn_pool_create(scratch_pool);
@@ -7645,52 +7844,85 @@ delete_node(void *baton,
           const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
           const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
           int child_op_depth = svn_sqlite__column_int(stmt, 2);
+          int moved_from_depth = -1;
           svn_boolean_t fixup = FALSE;
 
-          if (!b->moved_to_relpath
+          if (! b->moved_to_relpath
               && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
             {
-              /* Update the op-depth of an moved node below this tree */
-              fixup = TRUE;
-              child_op_depth = delete_depth;
-            }
-          else if (b->moved_to_relpath
-                   && delete_depth == child_op_depth)
-            {
-              /* Update the op-depth of a tree shadowed by this tree */
-              fixup = TRUE;
-              child_op_depth = delete_depth;
-            }
-          else if (b->moved_to_relpath
-                   && child_op_depth >= delete_depth
-                   && !svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
-            {
-              /* Update the move destination of something that is now moved
-                 away further */
+              /* a NULL moved_here_depth will be reported as 0 */
+              int moved_here_depth = svn_sqlite__column_int(stmt, 3);
 
-              child_relpath = svn_relpath_skip_ancestor(local_relpath, 
child_relpath);
+              /* Plain delete. Fixup move information of descendants that were
+                 moved here, or that were moved out */
 
-              if (child_relpath)
+              if (moved_here_depth >= delete_op_depth)
                 {
-                  child_relpath = svn_relpath_join(b->moved_to_relpath, 
child_relpath, scratch_pool);
+                  /* The move we recorded here must be moved to the location
+                     this node had before it was moved here.
 
-                  if (child_op_depth > delete_depth
-                      && svn_relpath_skip_ancestor(local_relpath, 
child_relpath))
-                    child_op_depth = delete_depth;
-                  else
-                    child_op_depth = relpath_depth(child_relpath);
+                     This might contain multiple steps when the node was moved
+                     in several places within the to be deleted tree */
 
+                  /* ### TODO: Add logic */
                   fixup = TRUE;
+                  moved_from_depth = moved_here_depth;
+                }
+              else
+                {
+                  /* Update the op-depth of an moved away node that was
+                     registered as moved by the records that we are about
+                     to delete */
+                  fixup = TRUE;
+                  child_op_depth = delete_op_depth;
+                }
+            }
+          else if (b->moved_to_relpath)
+            {
+              /* The node is moved to a new location */
+
+              if (delete_op_depth == child_op_depth)
+                {
+                  /* Update the op-depth of a tree shadowed by this tree */
+                  fixup = TRUE;
+                  /*child_op_depth = delete_depth;*/
+                }
+              else if (child_op_depth >= delete_op_depth
+                       && !svn_relpath_skip_ancestor(local_relpath,
+                                                     mv_to_relpath))
+                {
+                  /* Update the move destination of something that is now moved
+                     away further */
+
+                  child_relpath = svn_relpath_skip_ancestor(local_relpath,
+                                                            child_relpath);
+
+                  if (child_relpath)
+                    {
+                      child_relpath = svn_relpath_join(b->moved_to_relpath,
+                                                       child_relpath,
+                                                       scratch_pool);
+
+                      if (child_op_depth > delete_op_depth
+                           && svn_relpath_skip_ancestor(local_relpath,
+                                                        child_relpath))
+                        child_op_depth = delete_op_depth;
+                      else
+                        child_op_depth = relpath_depth(child_relpath);
+
+                      fixup = TRUE;
+                    }
                 }
             }
 
           if (fixup)
             {
-              mn = apr_pcalloc(scratch_pool, sizeof(struct moved_node_t));
+              mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
 
               mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
               mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
               mn->op_depth = child_op_depth;
+              mn->moved_from_depth = moved_from_depth;
 
               if (!moved_nodes)
                 moved_nodes = apr_array_make(scratch_pool, 1,
@@ -7700,58 +7932,67 @@ delete_node(void *baton,
 
           SVN_ERR(svn_sqlite__step(&have_row, stmt));
         }
-      svn_pool_destroy(iterpool);
       SVN_ERR(svn_sqlite__reset(stmt));
-    }
 
-  if (op_root)
-    {
-      svn_boolean_t below_base;
-      svn_boolean_t below_work;
-      svn_wc__db_status_t below_status;
-
-      /* Use STMT_SELECT_NODE_INFO directly instead of read_info plus
-         info_below_working */
-      SVN_ERR(info_below_working(&below_base, &below_work, &below_status,
-                                 wcroot, local_relpath, -1, scratch_pool));
-      if ((below_base || below_work)
-          && below_status != svn_wc__db_status_not_present
-          && below_status != svn_wc__db_status_deleted)
+      for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++)
         {
-          add_work = TRUE;
-          refetch_depth = TRUE;
+          struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i,
+                                                  struct moved_node_t *);
+
+          if (mn->moved_from_depth > 0)
+            {
+              svn_pool_clear(iterpool);
+
+              SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth,
+                                         wcroot, local_relpath,
+                                         mn->local_relpath,
+                                         mn->moved_from_depth,
+                                         scratch_pool, iterpool));
+
+              if (!mn->local_relpath)
+                svn_sort__array_delete(moved_nodes, i--, 1);
+            }
         }
 
-      select_depth = relpath_depth(local_relpath);
+      svn_pool_destroy(iterpool);
     }
-  else
+
+  if (!b->moved_to_relpath)
     {
-      add_work = TRUE;
-      if (status != svn_wc__db_status_normal)
-        SVN_ERR(op_depth_of(&select_depth, wcroot, local_relpath));
-      else
-        select_depth = 0; /* Deleting BASE node */
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_CLEAR_MOVED_TO_DESCENDANTS));
+      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
+                                local_relpath));
+      SVN_ERR(svn_sqlite__update(NULL, stmt));
+
+      if (op_root)
+        {
+          SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                            STMT_CLEAR_MOVED_TO_FROM_DEST));
+          SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
+                                    local_relpath));
+
+          SVN_ERR(svn_sqlite__update(NULL, stmt));
+        }
     }
 
+
   /* ### Put actual-only nodes into the list? */
   if (b->notify)
     {
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                         STMT_INSERT_DELETE_LIST));
       SVN_ERR(svn_sqlite__bindf(stmt, "isd",
-                                wcroot->wc_id, local_relpath, select_depth));
+                                wcroot->wc_id, local_relpath, 
working_op_depth));
       SVN_ERR(svn_sqlite__step_done(stmt));
     }
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
   SVN_ERR(svn_sqlite__bindf(stmt, "isd",
-                            wcroot->wc_id, local_relpath, delete_depth));
+                            wcroot->wc_id, local_relpath, delete_op_depth));
   SVN_ERR(svn_sqlite__step_done(stmt));
 
-  if (refetch_depth)
-    SVN_ERR(op_depth_of(&select_depth, wcroot, local_relpath));
-
   /* Delete ACTUAL_NODE rows, but leave those that have changelist
      and a NODES row. */
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
@@ -7781,7 +8022,7 @@ delete_node(void *baton,
                                  STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
       SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
                                 wcroot->wc_id, local_relpath,
-                                select_depth, delete_depth));
+                                keep_op_depth, delete_op_depth));
       SVN_ERR(svn_sqlite__step_done(stmt));
     }
 
@@ -8680,7 +8921,11 @@ read_children_info(svn_wc__db_wcroot_t *
           else
             child->op_root = (op_depth == relpath_depth(child_relpath));
 
-          svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
+          if (op_depth && child->op_root)
+            child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
+
+          if (new_child)
+            svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
         }
 
       if (op_depth == 0)
@@ -8702,18 +8947,48 @@ read_children_info(svn_wc__db_wcroot_t *
           child_item->nr_layers++;
           child_item->info.have_more_work = (child_item->nr_layers > 1);
 
-          /* Moved-to can only exist at op_depth > 0. */
-          /* ### Should we really do this for every layer where op_depth > 0
-                 in undefined order? */
+
+          /* A local_relpath can be moved multiple times at different op
+             depths and it really depends on the caller what is interesting.
+             We provide a simple linked list with the moved_from information */
+
           moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
           if (moved_to_relpath)
-            child_item->info.moved_to_abspath =
-              svn_dirent_join(wcroot->abspath, moved_to_relpath, result_pool);
+            {
+              struct svn_wc__db_moved_to_info_t *moved_to;
+              struct svn_wc__db_moved_to_info_t **next;
+              const char *shadow_op_relpath;
+              int cur_op_depth;
+
+              moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
+              moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
+                                                           moved_to_relpath,
+                                                           result_pool);
+
+              cur_op_depth = relpath_depth(child_relpath);
+              shadow_op_relpath = child_relpath;
+
+              while (cur_op_depth > op_depth)
+                {
+                  shadow_op_relpath = svn_relpath_dirname(shadow_op_relpath,
+                                                          scratch_pool);
+                  cur_op_depth--;
+                }
+
+              moved_to->shadow_op_root_abspath =
+                        svn_dirent_join(wcroot->abspath, shadow_op_relpath,
+                                        result_pool);
+
+              next = &child_item->info.moved_to;
+
+              while (*next &&
+                     0 < strcmp((*next)->shadow_op_root_abspath,
+                                moved_to->shadow_op_root_abspath))
+                next = &((*next)->next);
 
-          /* Moved-here can only exist at op_depth > 0. */
-          /* ### Should we really do this for every layer where op_depth > 0
-                 in undefined order? */
-          child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
+              moved_to->next = *next;
+              *next = moved_to;
+            }
         }
 
       SVN_ERR(svn_sqlite__step(&have_row, stmt));
@@ -8801,6 +9076,178 @@ svn_wc__db_read_children_info(apr_hash_t
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+db_read_props(apr_hash_t **props,
+              svn_wc__db_wcroot_t *wcroot,
+              const char *local_relpath,
+              apr_pool_t *result_pool,
+              apr_pool_t *scratch_pool);
+
+static svn_error_t *
+read_single_info(const struct svn_wc__db_info_t **info,
+                 svn_wc__db_wcroot_t *wcroot,
+                 const char *local_relpath,
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
+{
+  struct svn_wc__db_info_t *mtb;
+  apr_int64_t repos_id;
+  const svn_checksum_t *checksum;
+  const char *original_repos_relpath;
+  svn_boolean_t have_work;
+
+  mtb = apr_pcalloc(result_pool, sizeof(*mtb));
+
+  SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
+                    &mtb->repos_relpath, &repos_id, &mtb->changed_rev,
+                    &mtb->changed_date, &mtb->changed_author, &mtb->depth,
+                    &checksum, NULL, &original_repos_relpath, NULL, NULL,
+                    &mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
+                    &mtb->changelist, &mtb->conflicted, &mtb->op_root,
+                    &mtb->had_props, &mtb->props_mod, &mtb->have_base,
+                    &mtb->have_more_work, &have_work,
+                    wcroot, local_relpath,
+                    result_pool, scratch_pool));
+
+  /* Query the same rows in the database again for move information */
+  if (have_work && (mtb->have_base || mtb->have_more_work))
+    {
+      svn_sqlite__stmt_t *stmt;
+      svn_boolean_t have_row;
+      const char *cur_relpath = NULL;
+      int cur_op_depth;
+
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_SELECT_MOVED_TO_NODE));
+      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+      while (have_row)
+        {
+          struct svn_wc__db_moved_to_info_t *move;
+          int op_depth = svn_sqlite__column_int(stmt, 0);
+          const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, 
NULL);
+
+          move = apr_pcalloc(result_pool, sizeof(*move));
+          move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
+                                                   moved_to_relpath,
+                                                   result_pool);
+
+          if (!cur_relpath)
+            {
+              cur_relpath = local_relpath;
+              cur_op_depth = relpath_depth(cur_relpath);
+            }
+          while (cur_op_depth > op_depth)
+            {
+              cur_relpath = svn_relpath_dirname(cur_relpath, scratch_pool);
+              cur_op_depth--;
+            }
+          move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
+                                                         cur_relpath,
+                                                         result_pool);
+
+          move->next = mtb->moved_to;
+          mtb->moved_to = move;
+
+          SVN_ERR(svn_sqlite__step(&have_row, stmt));
+        }
+
+      SVN_ERR(svn_sqlite__reset(stmt));
+    }
+
+  /* Maybe we have to get some shadowed lock from BASE to make our test suite
+     happy... (It might be completely unrelated, but...)
+     This queries the same BASE row again, joined to the lock table */
+  if (mtb->have_base && (have_work || mtb->kind == svn_node_file))
+    {
+      svn_boolean_t update_root;
+      svn_wc__db_lock_t **lock_arg = NULL;
+
+      if (have_work)
+        lock_arg = &mtb->lock;
+
+      SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
+                                                NULL, NULL, NULL, NULL, NULL,
+                                                NULL, lock_arg, NULL, NULL,
+                                                &update_root,
+                                                wcroot, local_relpath,
+                                                result_pool, scratch_pool));
+
+      mtb->file_external = (update_root && mtb->kind == svn_node_file);
+    }
+
+  if (mtb->status == svn_wc__db_status_added)
+    {
+      svn_wc__db_status_t status;
+
+      SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                            NULL, NULL,
+                            wcroot, local_relpath,
+                            result_pool, scratch_pool));
+
+      mtb->moved_here = (status == svn_wc__db_status_moved_here);
+      mtb->incomplete = (status == svn_wc__db_status_incomplete);
+    }
+
+#ifdef HAVE_SYMLINK
+  if (mtb->kind == svn_node_file
+      && (mtb->had_props || mtb->props_mod))
+    {
+      apr_hash_t *properties;
+
+      if (mtb->props_mod)
+        SVN_ERR(db_read_props(&properties,
+                              wcroot, local_relpath,
+                              scratch_pool, scratch_pool));
+      else
+        SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
+                                       TRUE /* deleted_ok */,
+                                       scratch_pool, scratch_pool));
+
+      mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
+    }
+#endif
+
+  mtb->has_checksum = (checksum != NULL);
+  mtb->copied = (original_repos_relpath != NULL);
+
+  SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
+                                      wcroot->sdb, repos_id, result_pool));
+
+  if (mtb->kind == svn_node_dir)
+    SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
+
+  *info = mtb;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
+                            svn_wc__db_t *db,
+                            const char *local_abspath,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool)
+{
+  svn_wc__db_wcroot_t *wcroot;
+  const char *local_relpath;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
+                                                local_abspath,
+                                                scratch_pool, scratch_pool));
+  VERIFY_USABLE_WCROOT(wcroot);
+
+  SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
+                                       result_pool, scratch_pool),
+                      wcroot);
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
                               svn_node_kind_t *kind,
@@ -10730,7 +11177,7 @@ commit_node(svn_wc__db_wcroot_t *wcroot,
          if we need to remove shadowed layers below our descendants. */
 
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_DELETE_ALL_LAYERS));
+                                        STMT_DELETE_NODE_ALL_LAYERS));
       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
 

Modified: subversion/branches/1.8.x-r1536854/subversion/libsvn_wc/wc_db.h
URL: 
http://svn.apache.org/viewvc/subversion/branches/1.8.x-r1536854/subversion/libsvn_wc/wc_db.h?rev=1640562&r1=1640561&r2=1640562&view=diff
==============================================================================
--- subversion/branches/1.8.x-r1536854/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/branches/1.8.x-r1536854/subversion/libsvn_wc/wc_db.h Wed Nov 19 
14:37:04 2014
@@ -1909,6 +1909,16 @@ svn_wc__db_read_info(svn_wc__db_status_t
                      apr_pool_t *result_pool,
                      apr_pool_t *scratch_pool);
 
+/* Structure used as linked list in svn_wc__db_info_t to describe all nodes
+   in this location that were moved to another location */
+struct svn_wc__db_moved_to_info_t
+{
+  const char *moved_to_abspath;
+  const char *shadow_op_root_abspath;
+
+  struct svn_wc__db_moved_to_info_t *next;
+};
+
 /* Structure returned by svn_wc__db_read_children_info.  Only has the
    fields needed by status. */
 struct svn_wc__db_info_t {
@@ -1945,7 +1955,10 @@ struct svn_wc__db_info_t {
   svn_wc__db_lock_t *lock;  /* Repository file lock */
   svn_boolean_t incomplete; /* TRUE if a working node is incomplete */
 
-  const char *moved_to_abspath; /* Only on op-roots. See svn_wc_status3_t. */
+  struct svn_wc__db_moved_to_info_t *moved_to; /* A linked list of locations
+                                                 where nodes at this path
+                                                 are moved to. Highest layers
+                                                 first */
   svn_boolean_t moved_here;     /* Only on op-roots. */
 
   svn_boolean_t file_external;
@@ -1967,6 +1980,14 @@ svn_wc__db_read_children_info(apr_hash_t
                               apr_pool_t *result_pool,
                               apr_pool_t *scratch_pool);
 
+/* Like svn_wc__db_read_children_info, but only gets an info node for the root
+   element. */
+svn_error_t *
+svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
+                            svn_wc__db_t *db,
+                            const char *local_abspath,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool);
 
 /* Structure returned by svn_wc__db_read_walker_info.  Only has the
    fields needed by svn_wc__internal_walk_children(). */

Modified: subversion/branches/1.8.x-r1536854/subversion/mod_dav_svn/lock.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/1.8.x-r1536854/subversion/mod_dav_svn/lock.c?rev=1640562&r1=1640561&r2=1640562&view=diff
==============================================================================
--- subversion/branches/1.8.x-r1536854/subversion/mod_dav_svn/lock.c (original)
+++ subversion/branches/1.8.x-r1536854/subversion/mod_dav_svn/lock.c Wed Nov 19 
14:37:04 2014
@@ -453,7 +453,8 @@ get_locks(dav_lockdb *lockdb,
      lock.  For the --force case, this is required and for the non-force case,
      we allow the filesystem to produce a better error for svn clients.
   */
-  if (info->r->method_number == M_LOCK)
+  if (info->r->method_number == M_LOCK
+      && resource->info->repos->is_svn_client)
     {
       *locks = NULL;
       return 0;
@@ -594,7 +595,8 @@ has_locks(dav_lockdb *lockdb, const dav_
      lock.  For the --force case, this is required and for the non-force case,
      we allow the filesystem to produce a better error for svn clients.
   */
-  if (info->r->method_number == M_LOCK)
+  if (info->r->method_number == M_LOCK
+      && resource->info->repos->is_svn_client)
     {
       *locks_present = 0;
       return 0;

Modified: subversion/branches/1.8.x-r1536854/subversion/mod_dav_svn/repos.c
URL: 
http://svn.apache.org/viewvc/subversion/branches/1.8.x-r1536854/subversion/mod_dav_svn/repos.c?rev=1640562&r1=1640561&r2=1640562&view=diff
==============================================================================
--- subversion/branches/1.8.x-r1536854/subversion/mod_dav_svn/repos.c (original)
+++ subversion/branches/1.8.x-r1536854/subversion/mod_dav_svn/repos.c Wed Nov 
19 14:37:04 2014
@@ -1790,14 +1790,97 @@ do_out_of_date_check(dav_resource_combin
                                 "Could not get created rev of "
                                 "resource", r->pool);
 
-  if (comb->priv.version_name < created_rev)
+  if (SVN_IS_VALID_REVNUM(created_rev))
     {
-      serr = svn_error_createf(SVN_ERR_RA_OUT_OF_DATE, NULL,
-                               "Item '%s' is out of date",
-                               comb->priv.repos_path);
-      return dav_svn__convert_err(serr, HTTP_CONFLICT,
-                                  "Attempting to modify out-of-date resource.",
-                                  r->pool);
+      if (comb->priv.version_name < created_rev)
+        {
+          serr = svn_error_createf(SVN_ERR_RA_OUT_OF_DATE, NULL,
+                                   comb->res.collection
+                                    ? "Directory '%s' is out of date"
+                                    : (comb->res.exists
+                                        ? "File '%s' is out of date"
+                                        : "'%s' is out of date"),
+                                   comb->priv.repos_path);
+          return dav_svn__convert_err(serr, HTTP_CONFLICT,
+                                      "Attempting to modify out-of-date 
resource.",
+                                      r->pool);
+        }
+    }
+  else if (SVN_IS_VALID_REVNUM(comb->priv.version_name)
+           && comb->res.collection)
+    {
+      /* Issue #4480: With HTTPv2 we can receive the first change for a
+         directory after it has been made mutable, because one of its
+         descendants was changed before changing the directory.
+
+         We have to check if whatever the node is in HEAD is equivalent
+         to what it was in the provided BASE revision.
+
+         If the node was copied, we would process it before its decendants
+         and we already performed quite a few checks when making it mutable
+         via its descendant, so what we should really check here is if the
+         properties changed since the BASE version.
+
+         ### I think svn_fs_node_relation() checks for more changes than we
+             should check for here. Needs further review. But it looks like\
+             this check matches the checks in the libsvn_fs commit editor.
+
+             For now I would say reporting out of date in a few too many
+             cases is safer than not reporting out of date when we should.
+       */
+      svn_revnum_t txn_base_rev;
+      svn_fs_root_t *txn_base_root;
+      svn_fs_root_t *rev_root;
+      const svn_fs_id_t *txn_base_id;
+      const svn_fs_id_t *rev_id;
+
+      txn_base_rev = svn_fs_txn_base_revision(comb->res.info->root.txn);
+
+      if (comb->priv.version_name == txn_base_rev)
+        return NULL; /* Easy out: Nothing changed */
+
+      serr = svn_fs_revision_root(&txn_base_root, comb->res.info->repos->fs,
+                                  txn_base_rev, r->pool);
+                                  
+      if (!serr)
+        serr = svn_fs_node_id(&txn_base_id, txn_base_root,
+                              comb->priv.repos_path, r->pool);
+
+      if (serr != NULL)
+        {
+          return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+                                      "Could not open youngest revision root "
+                                      "for verification against the base "
+                                      "revision", r->pool);
+        }
+
+      serr = svn_fs_revision_root(&rev_root, comb->res.info->repos->fs,
+                                  comb->priv.version_name, r->pool);
+
+      if (!serr)
+        serr = svn_fs_node_id(&rev_id, rev_root,
+                              comb->priv.repos_path, r->pool);
+
+      if (serr != NULL)
+        {
+          return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
+                                      "Could not open the base revision"
+                                      "for verification against the youngest "
+                                      "revision", r->pool);
+        }
+
+      svn_fs_close_root(rev_root);
+      svn_fs_close_root(txn_base_root);
+
+      if (0 != svn_fs_compare_ids(txn_base_id, rev_id))
+        {
+          serr = svn_error_createf(SVN_ERR_RA_OUT_OF_DATE, NULL,
+                                   "Directory '%s' is out of date",
+                                   comb->priv.repos_path);
+          return dav_svn__convert_err(serr, HTTP_CONFLICT,
+                                      "Attempting to modify out-of-date 
resource.",
+                                      r->pool);
+        }
     }
 
   return NULL;


Reply via email to