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 2011/04/05 16:18:01 UTC

svn commit: r1089058 - in /subversion/trunk/subversion: libsvn_wc/wc_db.c tests/libsvn_wc/op-depth-test.c

Author: rhuijben
Date: Tue Apr  5 14:18:00 2011
New Revision: 1089058

URL: http://svn.apache.org/viewvc?rev=1089058&view=rev
Log:
Start recording mixed-revision copies as multiple op_depths. Do this by
adding not-present nodes at the op-depth of the parent operation. Before this
patch we created multiple copy operations at the same op depth when you
were using a simple 'svn cp DIR DIR2', while using separate operations
could give you multiple layers with the same result on commit.

This change doesn't introduce new database states that couldn't exist before,
but it does bring the working copy closer to the model used for committing
(and the formal tree model we used for designing WC-NG).

This change also slightly changes the behavior of delete and revert on
children of a copy operation as it allows you to revert the separate commands
that will be performed on commit.

On top of that this commit will finally allow the commit processing to use
depth handling.

* subversion/libsvn_wc/wc_db.c
  (insert_working_baton_t): Add not_present_op_depth field.
  (insert_working_node): When a valid not_present_op_depth is provided add a
    not present row for the new node.
  (cross_db_copy): Add argument for a not present row and set it in the baton.
  (op_depth_for_copy): Add argument.
  (db_op_copy): Update caller and if necessary add a not present row.
  (op_depth_for_copy): Check if we should introduce a not-present row instead
    of using the parent's op_depth.
  (svn_wc__db_op_copy_dir,
   svn_wc__db_op_copy_file,
   svn_wc__db_op_copy_symlink): Check for not present.

* subversion/tests/libsvn_wc/op-depth-test.c
  (test_mixed_rev_copy): Update expected rows.

Modified:
    subversion/trunk/subversion/libsvn_wc/wc_db.c
    subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1089058&r1=1089057&r2=1089058&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.c Tue Apr  5 14:18:00 2011
@@ -201,6 +201,10 @@ typedef struct insert_working_baton_t {
   /* may have work items to queue in this transaction  */
   const svn_skel_t *work_items;
 
+  /* If the value is > 0 and < op_depth, also insert a not-present
+     at op-depth NOT_PRESENT_OP_DEPTH, based on this same information */
+  apr_int64_t not_present_op_depth;
+
 } insert_working_baton_t;
 
 
@@ -955,6 +959,27 @@ insert_working_node(void *baton,
 
   SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
 
+  if (piwb->not_present_op_depth > 0
+      && piwb->not_present_op_depth < piwb->op_depth)
+    {
+      /* And also insert a not-present node to tell the commit processing that
+         a child of the parent node was not copied. */
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                        STMT_INSERT_NODE));
+          
+      SVN_ERR(svn_sqlite__bindf(stmt, "isisisrtnt", 
+                                wcroot->wc_id, local_relpath,
+                                piwb->not_present_op_depth, parent_relpath,
+                                piwb->original_repos_id,
+                                piwb->original_repos_relpath,
+                                piwb->original_revnum,
+                                presence_map, svn_wc__db_status_not_present,
+                                /* NULL */
+                                kind_map, piwb->kind));
+
+      SVN_ERR(svn_sqlite__step_done(stmt));
+    }
+
   return SVN_NO_ERROR;
 }
 
@@ -2157,6 +2182,7 @@ cross_db_copy(svn_wc__db_wcroot_t *src_w
               const char *dst_relpath,
               svn_wc__db_status_t dst_status,
               apr_int64_t dst_op_depth,
+              apr_int64_t dst_np_op_depth,
               svn_wc__db_kind_t kind,
               const apr_array_header_t *children,
               apr_int64_t copyfrom_id,
@@ -2222,6 +2248,8 @@ cross_db_copy(svn_wc__db_wcroot_t *src_w
   iwb.children = children;
   iwb.depth = depth;
 
+  iwb.not_present_op_depth = dst_np_op_depth;
+
   SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
 
   SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
@@ -2401,6 +2429,7 @@ op_depth_of(apr_int64_t *op_depth,
 
 static svn_error_t *
 op_depth_for_copy(apr_int64_t *op_depth,
+                  apr_int64_t *np_op_depth,
                   apr_int64_t copyfrom_repos_id,
                   const char *copyfrom_relpath,
                   svn_revnum_t copyfrom_revision,
@@ -2426,6 +2455,7 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcro
   svn_boolean_t have_work;
   apr_int64_t copyfrom_id;
   apr_int64_t dst_op_depth;
+  apr_int64_t dst_np_op_depth;
   svn_wc__db_kind_t kind;
   const apr_array_header_t *children;
 
@@ -2433,7 +2463,7 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcro
                             &status, &kind, &have_work, src_wcroot,
                             src_relpath, scratch_pool, scratch_pool));
 
-  SVN_ERR(op_depth_for_copy(&dst_op_depth, copyfrom_id,
+  SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth, copyfrom_id,
                             copyfrom_relpath, copyfrom_rev,
                             dst_wcroot, dst_relpath, scratch_pool));
 
@@ -2515,6 +2545,27 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcro
                                 dst_relpath, dst_parent_relpath));
       SVN_ERR(svn_sqlite__step_done(stmt));
 
+      if (dst_np_op_depth > 0)
+        {
+          /* We introduce a not-present node at the parent's op_depth to
+             properly start a new op-depth at our own op_depth. This marks
+             us as an op_root for commit and allows reverting just this
+             operation */
+
+          SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
+                                            STMT_INSERT_NODE));
+          SVN_ERR(svn_sqlite__bindf(stmt, "isisisrtnt", 
+                                    src_wcroot->wc_id, dst_relpath,
+                                    dst_np_op_depth, dst_parent_relpath,
+                                    copyfrom_id, copyfrom_relpath,
+                                    copyfrom_rev,
+                                    presence_map,
+                                       svn_wc__db_status_not_present,
+                                    /* NULL */
+                                    kind_map, kind));
+
+          SVN_ERR(svn_sqlite__step_done(stmt));
+        }
       /* Insert incomplete children, if relevant.
          The children are part of the same op and so have the same op_depth.
          (The only time we'd want a different depth is during a recursive
@@ -2534,7 +2585,8 @@ db_op_copy(svn_wc__db_wcroot_t *src_wcro
   else
     {
       SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
-                            dst_relpath, dst_status, dst_op_depth, kind,
+                            dst_relpath, dst_status, dst_op_depth,
+                            dst_np_op_depth, kind,
                             children, copyfrom_id, copyfrom_relpath,
                             copyfrom_rev, scratch_pool));
     }
@@ -2680,14 +2732,23 @@ catch_copy_of_absent(svn_wc__db_wcroot_t
 }
 
 
-/* If LOCAL_RELPATH is presence=incomplete then set *OP_DEPTH to the
-   op_depth of the incomplete node, otherwise if the copyfrom
-   information COPYFROM_REPOS_ID+COPYFROM_RELPATH+COPYFROM_REVISION
-   "matches" the copyfrom information for the parent of LOCAL_RELPATH
-   then set *OP_DEPTH to the op_depth of the parent, otherwise set
-   *OP_DEPTH to the op_depth of LOCAL_RELPATH. */
+/* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
+   revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
+   by checking if this would be a direct child of a copy of its parent
+   directory. If it is then set *OP_DEPTH to the op_depth of its parent.
+
+   If the node is not a direct copy at the same revision of the parent
+   *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
+   node should be inserted at this op_depth. This will be the case when the
+   parent already defined an incomplete child with the same name. Otherwise
+   *NP_OP_DEPTH will be set to -1.
+
+   If the parent node is not the parent of the to be copied node, then
+   *OP_DEPTH will be set to the proper op_depth for a new oew operation root.
+ */
 static svn_error_t *
 op_depth_for_copy(apr_int64_t *op_depth,
+                  apr_int64_t *np_op_depth,
                   apr_int64_t copyfrom_repos_id,
                   const char *copyfrom_relpath,
                   svn_revnum_t copyfrom_revision,
@@ -2698,8 +2759,10 @@ op_depth_for_copy(apr_int64_t *op_depth,
   const char *parent_relpath, *name;
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
+  apr_int64_t incomplete_op_depth = -1;
 
   *op_depth = relpath_depth(local_relpath);
+  *np_op_depth = -1;
 
   if (!copyfrom_relpath)
     return SVN_NO_ERROR;
@@ -2713,11 +2776,7 @@ op_depth_for_copy(apr_int64_t *op_depth,
       svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
                                                             presence_map);
       if (status == svn_wc__db_status_incomplete)
-        {
-          *op_depth = svn_sqlite__column_int64(stmt, 0);
-          SVN_ERR(svn_sqlite__reset(stmt));
-          return SVN_NO_ERROR;
-        }
+        incomplete_op_depth = svn_sqlite__column_int64(stmt, 0);
     }
   SVN_ERR(svn_sqlite__reset(stmt));
 
@@ -2728,12 +2787,17 @@ op_depth_for_copy(apr_int64_t *op_depth,
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
   if (have_row)
     {
+      apr_int64_t parent_op_depth = svn_sqlite__column_int64(stmt, 0);
       svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
                                                             presence_map);
+
       svn_error_t *err = convert_to_working_status(&status, status);
       if (err)
         SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
-      if (status == svn_wc__db_status_added)
+
+      if (status == svn_wc__db_status_added
+          && ((incomplete_op_depth < 0)
+              || (incomplete_op_depth == parent_op_depth)))
         {
           apr_int64_t parent_copyfrom_repos_id
             = svn_sqlite__column_int64(stmt, 10);
@@ -2742,12 +2806,16 @@ op_depth_for_copy(apr_int64_t *op_depth,
           svn_revnum_t parent_copyfrom_revision
             = svn_sqlite__column_revnum(stmt, 12);
 
-          if (parent_copyfrom_repos_id == copyfrom_repos_id
-              && copyfrom_revision == parent_copyfrom_revision
-              && !strcmp(svn_relpath_join(parent_copyfrom_relpath, name,
-                                          scratch_pool),
-                         copyfrom_relpath))
-            *op_depth = svn_sqlite__column_int64(stmt, 0);
+          if (parent_copyfrom_repos_id == copyfrom_repos_id)
+            {
+              if (copyfrom_revision == parent_copyfrom_revision
+                  && !strcmp(copyfrom_relpath,
+                             svn_relpath_join(parent_copyfrom_relpath, name,
+                                              scratch_pool)))
+                *op_depth = parent_op_depth;
+              else if (incomplete_op_depth > 0)
+                *np_op_depth = incomplete_op_depth;
+            }
         }
     }
   SVN_ERR(svn_sqlite__reset(stmt));
@@ -2811,7 +2879,8 @@ svn_wc__db_op_copy_dir(svn_wc__db_t *db,
     }
 
   /* ### Should we do this inside the transaction? */
-  SVN_ERR(op_depth_for_copy(&iwb.op_depth, iwb.original_repos_id,
+  SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
+                            iwb.original_repos_id,
                             original_repos_relpath, original_revision,
                             wcroot, local_relpath, scratch_pool));
 
@@ -2884,7 +2953,8 @@ svn_wc__db_op_copy_file(svn_wc__db_t *db
     }
 
   /* ### Should we do this inside the transaction? */
-  SVN_ERR(op_depth_for_copy(&iwb.op_depth, iwb.original_repos_id,
+  SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
+                            iwb.original_repos_id,
                             original_repos_relpath, original_revision,
                             wcroot, local_relpath, scratch_pool));
 
@@ -2952,7 +3022,8 @@ svn_wc__db_op_copy_symlink(svn_wc__db_t 
     }
 
   /* ### Should we do this inside the transaction? */
-  SVN_ERR(op_depth_for_copy(&iwb.op_depth, iwb.original_repos_id,
+  SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
+                            iwb.original_repos_id,
                             original_repos_relpath, original_revision,
                             wcroot, local_relpath, scratch_pool));
 

Modified: subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c?rev=1089058&r1=1089057&r2=1089058&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c Tue Apr  5 14:18:00 2011
@@ -1693,8 +1693,10 @@ test_mixed_rev_copy(const svn_test_opts_
   {
     nodes_row_t rows[] = {
       { 1, "X",     "normal",       1, "A" },
-      { 1, "X/B",   "normal",       2, "A/B" },
-      { 1, "X/B/C", "normal",       3, "A/B/C" },
+      { 1, "X/B",   "not-present",  2, "A/B" },
+      { 2, "X/B",   "normal",       2, "A/B" },
+      { 2, "X/B/C", "not-present",  3, "A/B/C" },
+      { 3, "X/B/C", "normal",       3, "A/B/C" },
       { 0 }
     };
     SVN_ERR(check_db_rows(&b, "X", rows));
@@ -1704,10 +1706,13 @@ test_mixed_rev_copy(const svn_test_opts_
   {
     nodes_row_t rows[] = {
       { 1, "X",     "normal",       1, "A" },
-      { 1, "X/B",   "normal",       2, "A/B" },
-      { 1, "X/B/C", "normal",       3, "A/B/C" },
+      { 1, "X/B",   "not-present",  2, "A/B" },
+      { 2, "X/B",   "normal",       2, "A/B" },
+      { 2, "X/B/C", "not-present",  3, "A/B/C" },
+      { 3, "X/B/C", "normal",       3, "A/B/C" },
       { 2, "X/Y",   "normal",       2, "A/B" },
-      { 2, "X/Y/C", "normal",       3, "A/B/C" },
+      { 2, "X/Y/C", "not-present",  3, "A/B/C" },
+      { 3, "X/Y/C", "normal",       3, "A/B/C" },
       { 0 }
     };
     SVN_ERR(check_db_rows(&b, "X", rows));



Re: svn commit: r1089058 - in /subversion/trunk/subversion: libsvn_wc/wc_db.c tests/libsvn_wc/op-depth-test.c

Posted by Philip Martin <ph...@wandisco.com>.
rhuijben@apache.org writes:

> This change also slightly changes the behavior of delete and revert on
> children of a copy operation as it allows you to revert the separate commands
> that will be performed on commit.

I suppose revert removes the higher op-depth child leaving the
not-present child at the parent's op-depth.  But that not-present still
counts as deleted and will generate a delete on commit.  It's not really
new behaviour, with the old model it was possible to delete the
mixed-rev child (creating a new base-deleted layer) and get the same
behaviour.  What is different is that in the old model one could revert
the delete and go back to the mixed-rev child being present, in the new
model it is not possible to revert.

When the mixed-rev child is deleted (either as not-present in the new
model or base-deleted in the old) one runs into the problem of
copy_tests.py 89, mixed_rev_copy_del, that the client doesn't know
whether or not to send the delete.

> On top of that this commit will finally allow the commit processing to use
> depth handling.

Not sure what you mean by "depth handling" but commit is still going to
have to detect the not-present child at the same op-depth as the parent
and generate a delete if there is no higher op-depth for the child.

-- 
Philip

Re: svn commit: r1089058 - in /subversion/trunk/subversion: libsvn_wc/wc_db.c tests/libsvn_wc/op-depth-test.c

Posted by Stefan Sperling <st...@elego.de>.
On Tue, Apr 05, 2011 at 02:18:01PM -0000, rhuijben@apache.org wrote:
> Author: rhuijben
> Date: Tue Apr  5 14:18:00 2011
> New Revision: 1089058
> 
> URL: http://svn.apache.org/viewvc?rev=1089058&view=rev
> Log:
> Start recording mixed-revision copies as multiple op_depths. Do this by
> adding not-present nodes at the op-depth of the parent operation. Before this
> patch we created multiple copy operations at the same op depth when you
> were using a simple 'svn cp DIR DIR2', while using separate operations
> could give you multiple layers with the same result on commit.
> 
> This change doesn't introduce new database states that couldn't exist before,
> but it does bring the working copy closer to the model used for committing
> (and the formal tree model we used for designing WC-NG).
> 
> This change also slightly changes the behavior of delete and revert on
> children of a copy operation as it allows you to revert the separate commands
> that will be performed on commit.
> 
> On top of that this commit will finally allow the commit processing to use
> depth handling.

Here is my patch that introduces depth to post-commit processing.

Last I tried, tests failed with it. I don't have time to look at
it today but in case it helps I thought I'd share it.

Index: subversion/include/svn_wc.h
===================================================================
--- subversion/include/svn_wc.h	(revision 1086444)
+++ subversion/include/svn_wc.h	(working copy)
@@ -4753,9 +4753,11 @@ svn_wc_committed_queue_create(apr_pool_t *pool);
  * identify the node's pristine text.
  * ### NOT YET IMPLEMENTED.
  *
- * If @a recurse is TRUE and @a local_abspath is a directory, then bump every
- * versioned object at or under @a local_abspath.  This is usually done for
- * copied trees.
+ * If @a local_abspath is a directory, then bump every versioned object
+ * within the specified @a depth at or under @a local_abspath. 
+ * This is usually done for copied trees. 
+ * @a depth must be greater or equal @c svn_depth_empty.
+ * It must NOT be @c svn_depth_unknown or @c svn_depth_exclude.
  *
  * ### In the present implementation, if a recursive directory item is in
  *     the queue, then any children (at any depth) of that directory that
@@ -4771,7 +4773,7 @@ svn_wc_committed_queue_create(apr_pool_t *pool);
  *       'remove_changelist' from the recursive parent item;
  *       'md5_checksum' = NULL  ### means what?
  *
- * @note the @a recurse parameter should be used with extreme care since
+ * @note the @a depth infinity should be used with extreme care since
  * it will bump ALL nodes under the directory, regardless of their
  * actual inclusion in the new revision.
  *
@@ -4788,7 +4790,7 @@ svn_error_t *
 svn_wc_queue_committed3(svn_wc_committed_queue_t *queue,
                         svn_wc_context_t *wc_ctx,
                         const char *local_abspath,
-                        svn_boolean_t recurse,
+                        svn_depth_t depth,
                         const apr_array_header_t *wcprop_changes,
                         svn_boolean_t remove_lock,
                         svn_boolean_t remove_changelist,
@@ -4797,8 +4799,8 @@ svn_wc_queue_committed3(svn_wc_committed_queue_t *
                         apr_pool_t *scratch_pool);
 
 /** Same as svn_wc_queue_committed3() except @a path doesn't have to be an
- * abspath and @a adm_access is unused and a SHA-1 checksum cannot be
- * specified.
+ * abspath, and @a adm_access is unused, and a SHA-1 checksum cannot be
+ * specified, and there is a boolean parameter @a recurse instead of depth.
  *
  * @since New in 1.6.
  *
Index: subversion/include/svn_client.h
===================================================================
--- subversion/include/svn_client.h	(revision 1086444)
+++ subversion/include/svn_client.h	(working copy)
@@ -473,6 +473,11 @@ typedef struct svn_client_commit_item3_t
    * same lifetime as this data structure.
    */
   apr_array_header_t *outgoing_prop_changes;
+
+  /** The depth this item was committed with. This is needed to retain
+   * local mods (such as deleted children) within copied directories that
+   * are committed with depth less than infinity. */
+  svn_depth_t depth;
 } svn_client_commit_item3_t;
 
 /** The commit candidate structure.
Index: subversion/libsvn_wc/deprecated.c
===================================================================
--- subversion/libsvn_wc/deprecated.c	(revision 1086444)
+++ subversion/libsvn_wc/deprecated.c	(working copy)
@@ -664,7 +664,8 @@ svn_wc_process_committed4(const char *path,
   SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
 
   wcprop_changes_hash = svn_wc__prop_array_to_hash(wcprop_changes, pool);
-  SVN_ERR(svn_wc__process_committed_internal(db, local_abspath, recurse, TRUE,
+  SVN_ERR(svn_wc__process_committed_internal(db, local_abspath,
+                                             svn_depth_infinity, TRUE,
                                              new_revnum, new_date, rev_author,
                                              wcprop_changes_hash,
                                              !remove_lock, !remove_changelist,
Index: subversion/libsvn_wc/wc.h
===================================================================
--- subversion/libsvn_wc/wc.h	(revision 1086444)
+++ subversion/libsvn_wc/wc.h	(working copy)
@@ -270,7 +270,7 @@ svn_wc__get_committed_queue_pool(const struct svn_
 svn_error_t *
 svn_wc__process_committed_internal(svn_wc__db_t *db,
                                    const char *local_abspath,
-                                   svn_boolean_t recurse,
+                                   svn_depth_t depth,
                                    svn_boolean_t top_of_recurse,
                                    svn_revnum_t new_revnum,
                                    apr_time_t new_date,
Index: subversion/libsvn_wc/adm_ops.c
===================================================================
--- subversion/libsvn_wc/adm_ops.c	(revision 1086444)
+++ subversion/libsvn_wc/adm_ops.c	(working copy)
@@ -70,14 +70,14 @@ struct svn_wc_committed_queue_t
   apr_pool_t *pool;
   /* Mapping (const char *) local_abspath to (committed_queue_item_t *). */
   apr_hash_t *queue;
-  /* Is any item in the queue marked as 'recursive'? */
-  svn_boolean_t have_recursive;
+  /* Is any item in the queue committed with depth > 'empty'? */
+  svn_boolean_t have_depth_greater_empty;
 };
 
 typedef struct committed_queue_item_t
 {
   const char *local_abspath;
-  svn_boolean_t recurse;
+  svn_depth_t depth;
   svn_boolean_t no_unlock;
   svn_boolean_t keep_changelist;
 
@@ -225,7 +225,7 @@ process_committed_leaf(svn_wc__db_t *db,
 svn_error_t *
 svn_wc__process_committed_internal(svn_wc__db_t *db,
                                    const char *local_abspath,
-                                   svn_boolean_t recurse,
+                                   svn_depth_t depth,
                                    svn_boolean_t top_of_recurse,
                                    svn_revnum_t new_revnum,
                                    apr_time_t new_date,
@@ -239,9 +239,25 @@ svn_wc__process_committed_internal(svn_wc__db_t *d
                                    apr_pool_t *scratch_pool)
 {
   svn_wc__db_kind_t kind;
+  svn_depth_t depth_below_here;
 
   SVN_ERR(svn_wc__db_read_kind(&kind, db, local_abspath, TRUE, scratch_pool));
 
+  switch (depth)
+    {
+      case svn_depth_empty:
+      case svn_depth_files:
+      case svn_depth_immediates:
+        depth_below_here = svn_depth_empty;
+        break;
+      case svn_depth_infinity:
+        depth_below_here = svn_depth_infinity;
+        break;
+      default:
+        SVN_ERR_MALFUNCTION();
+    }
+
+
   SVN_ERR(process_committed_leaf(db, local_abspath, !top_of_recurse,
                                  new_revnum, new_date, rev_author,
                                  new_dav_cache,
@@ -249,7 +265,7 @@ svn_wc__process_committed_internal(svn_wc__db_t *d
                                  sha1_checksum,
                                  scratch_pool));
 
-  if (recurse && kind == svn_wc__db_kind_dir)
+  if (depth > svn_depth_empty && kind == svn_wc__db_kind_dir)
     {
       const apr_array_header_t *children;
       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
@@ -331,7 +347,7 @@ svn_wc__process_committed_internal(svn_wc__db_t *d
              ones present in the current call are only applicable to
              this one committed item. */
           SVN_ERR(svn_wc__process_committed_internal(db, this_abspath,
-                                                     TRUE /* recurse */,
+                                                     depth_below_here,
                                                      FALSE,
                                                      new_revnum, new_date,
                                                      rev_author,
@@ -384,7 +400,7 @@ svn_wc_committed_queue_create(apr_pool_t *pool)
   q = apr_palloc(pool, sizeof(*q));
   q->pool = pool;
   q->queue = apr_hash_make(pool);
-  q->have_recursive = FALSE;
+  q->have_depth_greater_empty = FALSE;
 
   return q;
 }
@@ -393,7 +409,7 @@ svn_error_t *
 svn_wc_queue_committed3(svn_wc_committed_queue_t *queue,
                         svn_wc_context_t *wc_ctx,
                         const char *local_abspath,
-                        svn_boolean_t recurse,
+                        svn_depth_t depth,
                         const apr_array_header_t *wcprop_changes,
                         svn_boolean_t remove_lock,
                         svn_boolean_t remove_changelist,
@@ -405,7 +421,7 @@ svn_wc_queue_committed3(svn_wc_committed_queue_t *
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
 
-  queue->have_recursive |= recurse;
+  queue->have_depth_greater_empty |= (depth > svn_depth_empty);
 
   /* Use the same pool as the one QUEUE was allocated in,
      to prevent lifetime issues.  Intermediate operations
@@ -414,7 +430,7 @@ svn_wc_queue_committed3(svn_wc_committed_queue_t *
   /* Add to the array with paths and options */
   cqi = apr_palloc(queue->pool, sizeof(*cqi));
   cqi->local_abspath = local_abspath;
-  cqi->recurse = recurse;
+  cqi->depth = depth;
   cqi->no_unlock = !remove_lock;
   cqi->keep_changelist = !remove_changelist;
   cqi->md5_checksum = md5_checksum;
@@ -443,13 +459,31 @@ have_recursive_parent(apr_hash_t *queue,
   for (hi = apr_hash_first(scratch_pool, queue); hi; hi = apr_hash_next(hi))
     {
       const committed_queue_item_t *qi = svn__apr_hash_index_val(hi);
+      const char *child_remainder;
 
       if (qi == item)
         continue;
 
-      if (qi->recurse && svn_dirent_is_child(qi->local_abspath, local_abspath,
-                                             NULL))
-        return TRUE;
+      child_remainder = svn_dirent_is_child(qi->local_abspath,
+                                            local_abspath, NULL);
+      if (child_remainder == NULL)
+        continue;
+
+      /* Check if the item is within reach of the parent's commit depth. */
+      switch (qi->depth)
+        {
+          case svn_depth_empty:
+            continue;
+          case svn_depth_files:
+          case svn_depth_immediates:
+            if (svn_path_component_count(child_remainder) == 1)
+              return TRUE;
+            continue;
+          case svn_depth_infinity:
+            return TRUE;
+          default:
+            SVN_ERR_MALFUNCTION_NO_RETURN();
+        }
     }
 
   return FALSE;
@@ -488,12 +522,12 @@ svn_wc_process_committed_queue2(svn_wc_committed_q
       /* Skip this item if it is a child of a recursive item, because it has
          been (or will be) accounted for when that recursive item was (or
          will be) processed. */
-      if (queue->have_recursive && have_recursive_parent(queue->queue, cqi,
-                                                         iterpool))
+      if (queue->have_depth_greater_empty &&
+          have_recursive_parent(queue->queue, cqi, iterpool))
         continue;
 
       SVN_ERR(svn_wc__process_committed_internal(wc_ctx->db, cqi->local_abspath,
-                                                 cqi->recurse, TRUE, new_revnum,
+                                                 cqi->depth, TRUE, new_revnum,
                                                  new_date, rev_author,
                                                  cqi->new_dav_cache,
                                                  cqi->no_unlock,
Index: subversion/libsvn_client/commit_util.c
===================================================================
--- subversion/libsvn_client/commit_util.c	(revision 1086444)
+++ subversion/libsvn_client/commit_util.c	(working copy)
@@ -84,6 +84,7 @@ add_committable(apr_hash_t *committables,
                 const char *copyfrom_relpath,
                 svn_revnum_t copyfrom_rev,
                 apr_byte_t state_flags,
+                svn_depth_t depth,
                 apr_pool_t *result_pool,
                 apr_pool_t *scratch_pool)
 {
@@ -126,6 +127,7 @@ add_committable(apr_hash_t *committables,
   new_item->state_flags    = state_flags;
   new_item->incoming_prop_changes = apr_array_make(result_pool, 1,
                                                    sizeof(svn_prop_t *));
+  new_item->depth          = depth;
 
   /* Now, add the commit item to the array. */
   APR_ARRAY_PUSH(array, svn_client_commit_item3_t *) = new_item;
@@ -717,6 +719,7 @@ harvest_committables(apr_hash_t *committables,
                                   cf_relpath,
                                   cf_rev,
                                   state_flags,
+                                  depth,
                                   result_pool, scratch_pool));
           if (state_flags & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN)
             apr_hash_set(lock_tokens,
Index: subversion/libsvn_client/commit.c
===================================================================
--- subversion/libsvn_client/commit.c	(revision 1086444)
+++ subversion/libsvn_client/commit.c	(working copy)
@@ -925,19 +925,13 @@ post_process_commit_item(svn_wc_committed_queue_t
                          const svn_checksum_t *sha1_checksum,
                          apr_pool_t *scratch_pool)
 {
-  svn_boolean_t loop_recurse = FALSE;
   svn_boolean_t remove_lock;
 
-  if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD)
-      && (item->kind == svn_node_dir)
-      && (item->copyfrom_url))
-    loop_recurse = TRUE;
-
   remove_lock = (! keep_locks && (item->state_flags
                                        & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN));
 
   return svn_wc_queue_committed3(queue, wc_ctx, item->path,
-                                 loop_recurse, item->incoming_prop_changes,
+                                 item->depth, item->incoming_prop_changes,
                                  remove_lock, !keep_changelists,
                                  md5_checksum, sha1_checksum, scratch_pool);
 }