You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2012/07/26 15:54:31 UTC

svn commit: r1365997 - in /subversion/trunk/subversion: libsvn_client/externals.c libsvn_wc/adm_ops.c libsvn_wc/crop.c libsvn_wc/externals.c libsvn_wc/wc-queries.sql libsvn_wc/wc_db.c libsvn_wc/wc_db.h

Author: rhuijben
Date: Thu Jul 26 13:54:30 2012
New Revision: 1365997

URL: http://svn.apache.org/viewvc?rev=1365997&view=rev
Log:
Replace the very wc-1.0 style svn_wc__internal_remove_from_revision_control
function with an atomic wc-ng function. This makes operations like reducing the
depth of a working copy or removing externals from a wc atomic by performing
a db operation and installing workqueue items.

* subversion/libsvn_client/externals.c
  (relegate_dir_external): Obtain lock here that was obtained in the caller.
    Properly handle working copy renames.
  (switch_dir_external): Remove function that has been moved to the callee.

* subversion/libsvn_wc/adm_ops.c
  (process_committed_leaf): Update caller.

* subversion/libsvn_wc/crop.c
  (includes): Include workqueue.h.
  (IGNORE_LOCAL_MOD): Remove now unused macro.
  (crop_children): Use db operation directly.
  (svn_wc_exclude): Use db operation directly. Avoid second db operation
    by passing absent information.
  (svn_wc_crop_tree2): Update caller. Run workqueue.

* subversion/libsvn_wc/externals.c
  (svn_wc__external_remove): If we leave an external, leave the whole external
     by using the fail early mode.

* subversion/libsvn_wc/wc-queries.sql
  (STMT_SELECT_WORKING_PRESENT,
   STMT_DELETE_NODE_RECURSIVE,
   STMT_DELETE_NODE): New queries.

* subversion/libsvn_wc/wc_db.c
  (remove_node_baton): Add fields for new options.
  (remove_node_txn): Add code to create workqueue items for nodes that exist
    on disk. Provide cancellation checking when checking for changes.
    Allow setting excluded and not-present.
  (svn_wc__db_op_remove_node): Update caller.

* subversion/libsvn_wc/wc_db.h
  (svn_wc__db_op_remove_node): Update prototype and documentation.

Modified:
    subversion/trunk/subversion/libsvn_client/externals.c
    subversion/trunk/subversion/libsvn_wc/adm_ops.c
    subversion/trunk/subversion/libsvn_wc/crop.c
    subversion/trunk/subversion/libsvn_wc/externals.c
    subversion/trunk/subversion/libsvn_wc/wc-queries.sql
    subversion/trunk/subversion/libsvn_wc/wc_db.c
    subversion/trunk/subversion/libsvn_wc/wc_db.h

Modified: subversion/trunk/subversion/libsvn_client/externals.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/externals.c?rev=1365997&r1=1365996&r2=1365997&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/externals.c (original)
+++ subversion/trunk/subversion/libsvn_client/externals.c Thu Jul 26 13:54:30 2012
@@ -63,6 +63,9 @@ relegate_dir_external(svn_wc_context_t *
 {
   svn_error_t *err = SVN_NO_ERROR;
 
+  SVN_ERR(svn_wc__acquire_write_lock(NULL, wc_ctx, local_abspath,
+                                     FALSE, scratch_pool, scratch_pool));
+
   err = svn_wc__external_remove(wc_ctx, wri_abspath, local_abspath, FALSE,
                                 cancel_func, cancel_baton, scratch_pool);
   if (err && (err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD))
@@ -103,8 +106,21 @@ relegate_dir_external(svn_wc_context_t *
       /* Do our best, but no biggy if it fails. The rename will fail. */
       svn_error_clear(svn_io_remove_file2(new_path, TRUE, scratch_pool));
 
-      /* Rename. */
-      SVN_ERR(svn_io_file_rename(local_abspath, new_path, scratch_pool));
+      /* Rename. If this is still a working copy we should use the working
+         copy rename function (to release open handles) */
+      err = svn_wc__rename_wc(wc_ctx, local_abspath, new_path,
+                              scratch_pool);
+
+      if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
+        {
+          svn_error_clear(err);
+
+          /* And if it is no longer a working copy, we should just rename
+             it */
+          err = svn_io_file_rename(local_abspath, new_path, scratch_pool);
+        }
+
+      /* ### TODO: We should notify the user about the rename */
     }
 
   return svn_error_trace(err);
@@ -247,9 +263,6 @@ switch_dir_external(const char *local_ab
   if (kind == svn_node_dir)
     {
       /* Buh-bye, old and busted ... */
-      SVN_ERR(svn_wc__acquire_write_lock(NULL, ctx->wc_ctx, local_abspath,
-                                         FALSE, pool, pool));
-
       SVN_ERR(relegate_dir_external(ctx->wc_ctx, defining_abspath,
                                     local_abspath,
                                     ctx->cancel_func, ctx->cancel_baton,

Modified: subversion/trunk/subversion/libsvn_wc/adm_ops.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/adm_ops.c?rev=1365997&r1=1365996&r2=1365997&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/adm_ops.c (original)
+++ subversion/trunk/subversion/libsvn_wc/adm_ops.c Thu Jul 26 13:54:30 2012
@@ -157,10 +157,15 @@ process_committed_leaf(svn_wc__db_t *db,
     {
       return svn_error_trace(
                 svn_wc__db_op_remove_node(
+                                NULL,
                                 db, local_abspath,
+                                FALSE, FALSE,
                                 (have_base && !via_recurse)
                                     ? new_revnum : SVN_INVALID_REVNUM,
+                                svn_wc__db_status_not_present,
                                 kind,
+                                NULL, NULL,
+                                NULL, NULL,
                                 scratch_pool));
     }
   else if (status == svn_wc__db_status_not_present)
@@ -2326,196 +2331,40 @@ svn_wc__internal_remove_from_revision_co
                                               void *cancel_baton,
                                               apr_pool_t *scratch_pool)
 {
-  svn_error_t *err;
   svn_boolean_t left_something = FALSE;
-  svn_wc__db_status_t status;
-  svn_kind_t kind;
-
-  /* ### This whole function should be rewritten to run inside a transaction,
-     ### to allow a stable cancel behavior.
-     ###
-     ### Subversion < 1.7 marked the directory as incomplete to allow updating
-     ### it from a canceled state. But this would not work because update
-     ### doesn't retrieve deleted items.
-     ###
-     ### WC-NG doesn't support a delete+incomplete state, but we can't build
-     ### transactions over multiple databases yet. */
-
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+  svn_boolean_t is_root;
+  svn_error_t *err = NULL;
 
-  /* Check cancellation here, so recursive calls get checked early. */
-  if (cancel_func)
-    SVN_ERR(cancel_func(cancel_baton));
+  SVN_ERR(svn_wc__db_is_wcroot(&is_root, db, local_abspath, scratch_pool));
 
-  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL,
-                               db, local_abspath, scratch_pool, scratch_pool));
-
-  if (kind == svn_kind_file || kind == svn_kind_symlink)
-    {
-      svn_boolean_t text_modified_p = FALSE;
-
-      if (destroy_wf)
-        {
-          svn_node_kind_t on_disk;
-          SVN_ERR(svn_io_check_path(local_abspath, &on_disk, scratch_pool));
-          if (on_disk == svn_node_file)
-            {
-              /* Check for local mods. before removing entry */
-              SVN_ERR(svn_wc__internal_file_modified_p(&text_modified_p, db,
-                                                       local_abspath, FALSE,
-                                                       scratch_pool));
-            }
-        }
+  SVN_ERR(svn_wc__db_op_remove_node(&left_something,
+                                    db, local_abspath,
+                                    destroy_wf /* destroy_wc */,
+                                    destroy_wf /* destroy_changes */,
+                                    SVN_INVALID_REVNUM,
+                                    svn_wc__db_status_not_present,
+                                    svn_kind_none,
+                                    NULL, NULL,
+                                    cancel_func, cancel_baton,
+                                    scratch_pool));
 
-      /* Remove NAME from DB */
-      SVN_ERR(svn_wc__db_op_remove_node(db, local_abspath,
-                                        SVN_INVALID_REVNUM,
-                                        svn_kind_unknown,
-                                        scratch_pool));
-
-      /* If we were asked to destroy the working file, do so unless
-         it has local mods. */
-      if (destroy_wf)
-        {
-          /* Don't kill local mods. */
-          if (text_modified_p)
-            return svn_error_create(SVN_ERR_WC_LEFT_LOCAL_MOD, NULL, NULL);
-          else  /* The working file is still present; remove it. */
-            SVN_ERR(svn_io_remove_file2(local_abspath, TRUE, scratch_pool));
-        }
+  SVN_ERR(svn_wc__wq_run(db, local_abspath,
+                         cancel_func, cancel_baton,
+                         scratch_pool));
 
-    }  /* done with file case */
-  else /* looking at THIS_DIR */
+  if (is_root)
     {
-      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-      const apr_array_header_t *children;
-      int i;
-
-      /* ### sanity check:  check 2 places for DELETED flag? */
-
-      /* Walk over every entry. */
-      SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
-                                       scratch_pool, iterpool));
-
-      for (i = 0; i < children->nelts; i++)
-        {
-          const char *node_name = APR_ARRAY_IDX(children, i, const char*);
-          const char *node_abspath;
-          svn_wc__db_status_t node_status;
-          svn_kind_t node_kind;
-
-          svn_pool_clear(iterpool);
-
-          node_abspath = svn_dirent_join(local_abspath, node_name, iterpool);
-
-          SVN_ERR(svn_wc__db_read_info(&node_status, &node_kind, NULL, NULL,
-                                       NULL, NULL, NULL, NULL, NULL, NULL,
-                                       NULL, NULL, NULL, NULL, NULL, NULL,
-                                       NULL, NULL, NULL, NULL, NULL, NULL,
-                                       NULL, NULL, NULL, NULL, NULL,
-                                       db, node_abspath,
-                                       iterpool, iterpool));
-
-          if (node_status == svn_wc__db_status_normal
-              && node_kind == svn_kind_dir)
-            {
-              svn_boolean_t is_root;
-
-              SVN_ERR(svn_wc__check_wc_root(&is_root, NULL, NULL,
-                                            db, node_abspath, iterpool));
-
-              if (is_root)
-                continue; /* Just skip working copies as obstruction */
-            }
-
-          if (node_status != svn_wc__db_status_normal
-              && node_status != svn_wc__db_status_added
-              && node_status != svn_wc__db_status_incomplete)
-            {
-              /* The node is already 'deleted', so nothing to do on
-                 versioned nodes */
-              SVN_ERR(svn_wc__db_op_remove_node(db, node_abspath,
-                                                SVN_INVALID_REVNUM,
-                                                svn_kind_unknown,
-                                                iterpool));
-
-              continue;
-            }
-
-          err = svn_wc__internal_remove_from_revision_control(
-                            db, node_abspath,
-                            destroy_wf,
-                            cancel_func, cancel_baton,
-                            iterpool);
-
-          if (err && (err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD))
-            {
-              svn_error_clear(err);
-              left_something = TRUE;
-            }
-          else if (err)
-            return svn_error_trace(err);
-        }
-
-      /* At this point, every directory below this one has been
-         removed from revision control. */
-
-      /* Remove self from parent's entries file, but only if parent is
-         a working copy.  If it's not, that's fine, we just move on. */
-      {
-        svn_boolean_t is_root;
-
-        SVN_ERR(svn_wc__check_wc_root(&is_root, NULL, NULL,
-                                      db, local_abspath, iterpool));
-
-        /* If full_path is not the top of a wc, then its parent
-           directory is also a working copy and has an entry for
-           full_path.  We need to remove that entry: */
-        if (! is_root)
-          {
-            SVN_ERR(svn_wc__db_op_remove_node(db, local_abspath,
-                                              SVN_INVALID_REVNUM,
-                                              svn_kind_unknown,
-                                              iterpool));
-          }
-        else
-          {
-            /* Remove the entire administrative .svn area, thereby removing
-               _this_ dir from revision control too.  */
-            SVN_ERR(svn_wc__adm_destroy(db, local_abspath,
-                                        cancel_func, cancel_baton, iterpool));
-          }
-      }
-
-      /* If caller wants us to recursively nuke everything on disk, go
-         ahead, provided that there are no dangling local-mod files
-         below */
-      if (destroy_wf && (! left_something))
-        {
-          /* If the dir is *truly* empty (i.e. has no unversioned
-             resources, all versioned files are gone, all .svn dirs are
-             gone, and contains nothing but empty dirs), then a
-             *non*-recursive dir_remove should work.  If it doesn't,
-             no big deal.  Just assume there are unversioned items in
-             there and set "left_something" */
-          err = svn_io_dir_remove_nonrecursive(local_abspath, iterpool);
-          if (err)
-            {
-              if (!APR_STATUS_IS_ENOENT(err->apr_err))
-                left_something = TRUE;
-              svn_error_clear(err);
-            }
-        }
-
-      svn_pool_destroy(iterpool);
+      /* Destroy the administrative area */
+      SVN_ERR(svn_wc__adm_destroy(db, local_abspath, cancel_func, cancel_baton,
+                                  scratch_pool));
 
-    }  /* end of directory case */
+      /* And if we didn't leave something interesting, remove the directory */
+      if (!left_something && destroy_wf)
+        err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool);
+    }
 
-  if (left_something)
-    return svn_error_create(SVN_ERR_WC_LEFT_LOCAL_MOD, NULL, NULL);
+  if (left_something || err)
+    return svn_error_create(SVN_ERR_WC_LEFT_LOCAL_MOD, err, NULL);
 
   return SVN_NO_ERROR;
 }

Modified: subversion/trunk/subversion/libsvn_wc/crop.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/crop.c?rev=1365997&r1=1365996&r2=1365997&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/crop.c (original)
+++ subversion/trunk/subversion/libsvn_wc/crop.c Thu Jul 26 13:54:30 2012
@@ -31,24 +31,10 @@
 #include "svn_path.h"
 
 #include "wc.h"
+#include "workqueue.h"
 
 #include "svn_private_config.h"
 
-/* Evaluate EXPR.  If it returns an error, return that error, unless
-   the error's code is SVN_ERR_WC_LEFT_LOCAL_MOD, in which case clear
-   the error and do not return. */
-#define IGNORE_LOCAL_MOD(expr)                                   \
-  do {                                                           \
-    svn_error_t *__temp = (expr);                                \
-    if (__temp)                                                  \
-      {                                                          \
-        if (__temp->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)        \
-          svn_error_clear(__temp);                               \
-        else                                                     \
-          return svn_error_trace(__temp);                       \
-      }                                                          \
-  } while (0)
-
 /* Helper function that crops the children of the LOCAL_ABSPATH, under the
  * constraint of NEW_DEPTH. The DIR_PATH itself will never be cropped. The
  * whole subtree should have been locked.
@@ -135,13 +121,16 @@ crop_children(svn_wc__db_t *db,
              also handle it. We only need to skip the notification in that
              case. */
           if (new_depth == svn_depth_empty)
-            IGNORE_LOCAL_MOD(
-              svn_wc__internal_remove_from_revision_control(
-                                                   db,
-                                                   child_abspath,
-                                                   TRUE, /* destroy */
-                                                   cancel_func, cancel_baton,
-                                                   iterpool));
+            SVN_ERR(svn_wc__db_op_remove_node(NULL,
+                                              db, child_abspath,
+                                              TRUE /* destroy */,
+                                              FALSE /* destroy_changes */,
+                                              SVN_INVALID_REVNUM,
+                                              svn_wc__db_status_not_present,
+                                              svn_kind_none,
+                                              NULL, NULL,
+                                              cancel_func, cancel_baton,
+                                              iterpool));
           else
             continue;
 
@@ -150,14 +139,16 @@ crop_children(svn_wc__db_t *db,
         {
           if (new_depth < svn_depth_immediates)
             {
-              IGNORE_LOCAL_MOD(
-                svn_wc__internal_remove_from_revision_control(
-                                                     db,
-                                                     child_abspath,
-                                                     TRUE, /* destroy */
-                                                     cancel_func,
-                                                     cancel_baton,
-                                                     iterpool));
+              SVN_ERR(svn_wc__db_op_remove_node(NULL,
+                                                db, child_abspath,
+                                                TRUE /* destroy */,
+                                                FALSE /* destroy_changes */,
+                                                SVN_INVALID_REVNUM,
+                                                svn_wc__db_status_not_present,
+                                                svn_kind_none,
+                                                NULL, NULL,
+                                                cancel_func, cancel_baton,
+                                                iterpool));
             }
           else
             {
@@ -269,26 +260,21 @@ svn_wc_exclude(svn_wc_context_t *wc_ctx,
         break; /* Ok to exclude */
     }
 
-  /* ### This could use some kind of transaction */
-
   /* Remove all working copy data below local_abspath */
-  IGNORE_LOCAL_MOD(svn_wc__internal_remove_from_revision_control(
-                                    wc_ctx->db,
-                                    local_abspath,
+  SVN_ERR(svn_wc__db_op_remove_node(NULL,
+                                    wc_ctx->db, local_abspath,
                                     TRUE /* destroy */,
+                                    FALSE /* destroy_changes */,
+                                    revision,
+                                    svn_wc__db_status_excluded,
+                                    kind,
+                                    NULL, NULL,
                                     cancel_func, cancel_baton,
                                     scratch_pool));
 
-  SVN_ERR(svn_wc__db_base_add_excluded_node(wc_ctx->db,
-                                            local_abspath,
-                                            repos_relpath,
-                                            repos_root,
-                                            repos_uuid,
-                                            revision,
-                                            kind,
-                                            svn_wc__db_status_excluded,
-                                            NULL, NULL,
-                                            scratch_pool));
+  SVN_ERR(svn_wc__wq_run(wc_ctx->db, local_abspath,
+                         cancel_func, cancel_baton,
+                         scratch_pool));
 
   if (notify_func)
     {
@@ -368,7 +354,11 @@ svn_wc_crop_tree2(svn_wc_context_t *wc_c
         SVN_ERR_MALFUNCTION();
     }
 
-  return crop_children(db, local_abspath, dir_depth, depth,
-                       notify_func, notify_baton,
-                       cancel_func, cancel_baton, scratch_pool);
+  SVN_ERR(crop_children(db, local_abspath, dir_depth, depth,
+                        notify_func, notify_baton,
+                        cancel_func, cancel_baton, scratch_pool));
+
+  return svn_error_trace(svn_wc__wq_run(db, local_abspath,
+                                        cancel_func, cancel_baton,
+                                        scratch_pool));
 }

Modified: subversion/trunk/subversion/libsvn_wc/externals.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/externals.c?rev=1365997&r1=1365996&r2=1365997&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/externals.c (original)
+++ subversion/trunk/subversion/libsvn_wc/externals.c Thu Jul 26 13:54:30 2012
@@ -1309,7 +1309,7 @@ svn_wc__external_remove(svn_wc_context_t
 
   if (kind == svn_kind_dir)
     SVN_ERR(svn_wc_remove_from_revision_control2(wc_ctx, local_abspath,
-                                                 TRUE, FALSE,
+                                                 TRUE, TRUE,
                                                  cancel_func, cancel_baton,
                                                  scratch_pool));
   else

Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=1365997&r1=1365996&r2=1365997&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Thu Jul 26 13:54:30 2012
@@ -163,6 +163,28 @@ WHERE wc_id = ?1 AND IS_STRICT_DESCENDAN
                    AND op_depth > 0)
 ORDER BY local_relpath DESC
 
+-- STMT_SELECT_WORKING_PRESENT
+SELECT local_relpath, kind, checksum, translated_size, last_mod_time
+FROM nodes n
+WHERE wc_id = ?1
+  AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
+  AND presence in ('normal', 'incomplete')
+  AND op_depth = (SELECT MAX(op_depth)
+                  FROM NODES w
+                  WHERE w.wc_id = ?1
+                    AND w.local_relpath = n.local_relpath)
+ORDER BY local_relpath DESC
+
+-- STMT_DELETE_NODE_RECURSIVE
+DELETE FROM NODES
+WHERE wc_id = ?1
+  AND IS_STRICT_DESCENDANT_OF(local_relpath, ?2)
+
+-- STMT_DELETE_NODE
+DELETE
+FROM NODES
+WHERE wc_id = ?1 AND local_relpath = ?2
+
 -- STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE
 /* The ACTUAL_NODE applies to BASE, unless there is in at least one op_depth
    a WORKING node that could have a conflict */

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1365997&r1=1365996&r2=1365997&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.c Thu Jul 26 13:54:30 2012
@@ -6546,8 +6546,17 @@ svn_wc__db_revert_list_done(svn_wc__db_t
 /* Baton for remove_node_txn */
 struct remove_node_baton
 {
+  svn_wc__db_t *db;
+  svn_boolean_t left_changes;
+  svn_boolean_t destroy_wc;
+  svn_boolean_t destroy_changes;
   svn_revnum_t not_present_rev;
+  svn_wc__db_status_t not_present_status;
   svn_kind_t not_present_kind;
+  const svn_skel_t *conflict;
+  const svn_skel_t *work_items;
+  svn_cancel_func_t cancel_func;
+  void *cancel_baton;
 };
 
 /* Implements svn_wc__db_txn_callback_t for svn_wc__db_op_remove_node */
@@ -6563,7 +6572,11 @@ remove_node_txn(void *baton,
   apr_int64_t repos_id;
   const char *repos_relpath;
 
-  SVN_ERR_ASSERT(*local_relpath != '\0'); /* Never on a wcroot */
+  /* Note that unlike many similar functions it is a valid scenario for this
+     function to be called on a wcroot! */
+
+   /* db set when destroying wc */
+  SVN_ERR_ASSERT(!rnb->destroy_wc || rnb->db != NULL);
 
   /* Need info for not_present node? */
   if (SVN_IS_VALID_REVNUM(rnb->not_present_rev))
@@ -6573,14 +6586,189 @@ remove_node_txn(void *baton,
                           wcroot, local_relpath,
                           scratch_pool, scratch_pool));
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
+  if (rnb->destroy_wc
+      && (!rnb->destroy_changes || *local_relpath == '\0'))
+    {
+      svn_boolean_t have_row;
+      apr_pool_t *iterpool;
+      svn_error_t *err = NULL;
 
-  /* Remove all nodes at or below local_relpath where op_depth >= 0 */
-  SVN_ERR(svn_sqlite__bindf(stmt, "isd",
-                            wcroot->wc_id, local_relpath, 0));
+      /* Install WQ items for deleting the unmodified files and all dirs */
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_SELECT_WORKING_PRESENT));
+      SVN_ERR(svn_sqlite__bindf(stmt, "is",
+                                wcroot->wc_id, local_relpath));
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+      iterpool = svn_pool_create(scratch_pool);
+
+      while (have_row)
+        {
+          const char *child_relpath;
+          const char *child_abspath;
+          svn_kind_t child_kind;
+          svn_boolean_t have_checksum;
+          svn_filesize_t recorded_size;
+          apr_int64_t recorded_mod_time;
+          const svn_io_dirent2_t *dirent;
+          svn_boolean_t modified_p = TRUE;
+          svn_skel_t *work_item = NULL;
+
+          svn_pool_clear(iterpool);
+
+          child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+          child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
+
+          child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
+                                          iterpool);
+
+          if (child_kind == svn_kind_file)
+            {
+              have_checksum = !svn_sqlite__column_is_null(stmt, 2);
+              recorded_size = get_recorded_size(stmt, 3);
+              recorded_mod_time = svn_sqlite__column_int64(stmt, 4);
+            }
+
+          if (rnb->cancel_func)
+            err = rnb->cancel_func(rnb->cancel_baton);
+
+          if (err)
+            break;
+
+          err = svn_io_stat_dirent(&dirent, child_abspath, TRUE,
+                                   iterpool, iterpool);
+
+          if (err)
+            break;
+
+          if (rnb->destroy_changes
+              || dirent->kind != svn_node_file
+              || child_kind != svn_kind_file)
+            {
+              /* Not interested in keeping changes */
+              modified_p = FALSE; 
+            }
+          else if (child_kind == svn_kind_file
+                   && dirent->kind == svn_node_file
+                   && dirent->filesize == recorded_size
+                   && dirent->mtime == recorded_mod_time)
+            {
+              modified_p = FALSE; /* File matches recorded state */
+            }
+          else if (have_checksum)
+            err = svn_wc__internal_file_modified_p(&modified_p,
+                                                   rnb->db, child_abspath,
+                                                   FALSE, iterpool);
+
+          if (err)
+            break;
+
+          if (modified_p)
+            rnb->left_changes = TRUE;
+          else if (child_kind == svn_kind_dir)
+            {
+              err = svn_wc__wq_build_dir_remove(&work_item,
+                                                rnb->db, wcroot->abspath,
+                                                child_abspath, FALSE,
+                                                iterpool, iterpool);
+            }
+          else /* svn_kind_file || svn_kind_symlink */
+            {
+              err = svn_wc__wq_build_file_remove(&work_item,
+                                                 rnb->db, wcroot->abspath,
+                                                 child_abspath,
+                                                 iterpool, iterpool);
+            }
+
+          if (err)
+            break;
+
+          if (work_item)
+            {
+              err = add_work_items(wcroot->sdb, work_item, iterpool);
+              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)));
+    }
+
+  if (rnb->destroy_wc && *local_relpath != '\0')
+    {
+      /* Create work item for destroying the root */
+      svn_wc__db_status_t status;
+      svn_kind_t kind;
+      SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
+                        NULL, NULL, NULL, 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
+          || status == svn_wc__db_status_added
+          || status == svn_wc__db_status_incomplete)
+        {
+          svn_skel_t *work_item = NULL;
+          const char *local_abspath = svn_dirent_join(wcroot->abspath,
+                                                          local_relpath,
+                                                          scratch_pool);
+
+          if (kind == svn_kind_dir)
+            {
+              SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
+                                                  rnb->db, wcroot->abspath,
+                                                  local_abspath,
+                                                  rnb->destroy_changes
+                                                      /* recursive */,
+                                                  scratch_pool, scratch_pool));
+            }
+          else
+            {
+              svn_boolean_t modified_p = FALSE;
+
+              if (!rnb->destroy_changes)
+                {
+                  SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
+                                                           rnb->db,
+                                                           local_abspath,
+                                                           FALSE,
+                                                           scratch_pool));
+                }
+
+              if (!modified_p)
+                SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
+                                                     rnb->db, wcroot->abspath,
+                                                     local_abspath,
+                                                     scratch_pool,
+                                                     scratch_pool));
+              else
+                rnb->left_changes = TRUE;
+            }
+
+          SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
+        }
+    }
+
+  /* Remove all nodes below local_relpath */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                    STMT_DELETE_NODE_RECURSIVE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath, 0));
   SVN_ERR(svn_sqlite__step_done(stmt));
 
+  /* Delete the root NODE when this is not the working copy root */
+  if (local_relpath[0] != '\0')
+    {
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_DELETE_NODE));
+      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath, 0));
+      SVN_ERR(svn_sqlite__step_done(stmt));
+    }
+
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_DELETE_ACTUAL_NODE_RECURSIVE));
 
@@ -6596,7 +6784,11 @@ remove_node_txn(void *baton,
       blank_ibb(&ibb);
 
       ibb.repos_id = repos_id;
-      ibb.status = svn_wc__db_status_not_present;
+
+      SVN_ERR_ASSERT(rnb->not_present_status == svn_wc__db_status_not_present
+                     || rnb->not_present_status == svn_wc__db_status_excluded);
+
+      ibb.status = rnb->not_present_status;
       ibb.kind = rnb->not_present_kind;
 
       ibb.repos_relpath = repos_relpath;
@@ -6605,14 +6797,26 @@ remove_node_txn(void *baton,
       SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
     }
 
+  SVN_ERR(add_work_items(wcroot->sdb, rnb->work_items, scratch_pool));
+  if (rnb->conflict)
+    SVN_ERR(mark_conflict(wcroot, local_relpath, rnb->conflict, scratch_pool));
+
   return SVN_NO_ERROR;
 }
 
 svn_error_t *
-svn_wc__db_op_remove_node(svn_wc__db_t *db,
+svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
+                          svn_wc__db_t *db,
                           const char *local_abspath,
+                          svn_boolean_t destroy_wc,
+                          svn_boolean_t destroy_changes,
                           svn_revnum_t not_present_revision,
+                          svn_wc__db_status_t not_present_status,
                           svn_kind_t not_present_kind,
+                          const svn_skel_t *conflict,
+                          const svn_skel_t *work_items,
+                          svn_cancel_func_t cancel_func,
+                          void *cancel_baton,
                           apr_pool_t *scratch_pool)
 {
   svn_wc__db_wcroot_t *wcroot;
@@ -6625,8 +6829,17 @@ svn_wc__db_op_remove_node(svn_wc__db_t *
                               local_abspath, scratch_pool, scratch_pool));
   VERIFY_USABLE_WCROOT(wcroot);
 
+  rnb.db = db;
+  rnb.left_changes = FALSE;
+  rnb.destroy_wc = destroy_wc;
+  rnb.destroy_changes = destroy_changes;
   rnb.not_present_rev = not_present_revision;
+  rnb.not_present_status = not_present_status;
   rnb.not_present_kind = not_present_kind;
+  rnb.conflict = conflict;
+  rnb.work_items = work_items;
+  rnb.cancel_func = cancel_func;
+  rnb.cancel_baton = cancel_baton;
 
   SVN_ERR(svn_wc__db_with_txn(wcroot, local_relpath, remove_node_txn,
                               &rnb, scratch_pool));
@@ -6635,6 +6848,9 @@ svn_wc__db_op_remove_node(svn_wc__db_t *
   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
                         scratch_pool));
 
+  if (left_changes)
+    *left_changes = rnb.left_changes;
+
   return SVN_NO_ERROR;
 }
 

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.h?rev=1365997&r1=1365996&r2=1365997&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.h (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.h Thu Jul 26 13:54:30 2012
@@ -2838,19 +2838,39 @@ svn_wc__db_wclock_owns_lock(svn_boolean_
 */
 
 /* Removes all references to LOCAL_ABSPATH from DB, while optionally leaving
-   tree conflicts and/or a not present node.
+   a not present node.
 
    This operation always recursively removes all nodes at and below
    LOCAL_ABSPATH from NODES and ACTUAL.
 
    If NOT_PRESENT_REVISION specifies a valid revision, leave a not_present
-   BASE node at local_abspath. (Requires an existing BASE node before removing)
+   BASE node at local_abspath of the specified status and kind.
+   (Requires an existing BASE node before removing)
+
+   If DESTROY_WC is TRUE, this operation *installs* workqueue operations to
+   update the local filesystem after the database operation. If DESTROY_CHANGES
+   is FALSE, modified and unversioned files are left after running this
+   operation (and the WQ). If DESTROY_CHANGES and DESTROY_WC are TRUE,
+   LOCAL_ABSPATH and everything below it will be removed by the WQ.
+
+
+   Note: Unlike many similar functions it is a valid scenario for this
+   function to be called on a wcroot! In this case it will just leave the root
+   record in BASE
  */
 svn_error_t *
-svn_wc__db_op_remove_node(svn_wc__db_t *db,
+svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
+                          svn_wc__db_t *db,
                           const char *local_abspath,
+                          svn_boolean_t destroy_wc,
+                          svn_boolean_t destroy_changes,
                           svn_revnum_t not_present_revision,
+                          svn_wc__db_status_t not_present_status,
                           svn_kind_t not_present_kind,
+                          const svn_skel_t *conflict,
+                          const svn_skel_t *work_items,
+                          svn_cancel_func_t cancel_func,
+                          void *cancel_baton,
                           apr_pool_t *scratch_pool);
 
 /* Sets the depth of LOCAL_ABSPATH in its working copy to DEPTH using DB.