Index: subversion/include/svn_wc.h
===================================================================
--- subversion/include/svn_wc.h	(revision 21559)
+++ subversion/include/svn_wc.h	(working copy)
@@ -2308,7 +2308,54 @@
 
 /* Commits. */
 
+
 /**
+ * Storage type for queued post-commit data.
+ *
+ * @since New in 1.5.
+ */
+typedef void svn_wc_committed_queue_t;
+
+
+/**
+ * Queue committed items to be processed later by
+ * svn_wc_process_committed_queue().
+ *
+ * The first time this function is called, @a *queue should
+ * be @c NULL to signal that initialization is required.
+ *
+ * All pointer data passed to this function (@a path, @a wcprop_changes
+ * and @a digest) should remain valid until the queue has been
+ * processed by svn_wc_process_committed_queue().
+ *
+ * @since New in 1.5.
+ */
+svn_error_t *
+svn_wc_queue_committed(svn_wc_committed_queue_t **queue,
+                       const char *path,
+                       svn_wc_adm_access_t *adm_access,
+                       svn_boolean_t recurse,
+                       apr_array_header_t *wcprop_changes,
+                       svn_boolean_t remove_lock,
+                       svn_boolean_t remove_changelist,
+                       const unsigned char *digest,
+                       apr_pool_t *pool);
+
+
+/**
+ * Like svn_wc_process_committed4(), but batch processes
+ * items queued with svn_wc_queue_committed().
+ */
+svn_error_t *
+svn_wc_process_committed_queue(svn_wc_committed_queue_t *queue,
+                               svn_wc_adm_access_t *adm_access,
+                               svn_revnum_t new_revnum,
+                               const char *rev_date,
+                               const char *rev_author,
+                               apr_pool_t *pool);
+
+
+/**
  * Bump a successfully committed absolute @a path to @a new_revnum after a
  * commit succeeds.  @a rev_date and @a rev_author are the (server-side)
  * date and author of the new revision; one or both may be @c NULL.
Index: subversion/libsvn_wc/adm_ops.c
===================================================================
--- subversion/libsvn_wc/adm_ops.c	(revision 21559)
+++ subversion/libsvn_wc/adm_ops.c	(working copy)
@@ -26,6 +26,7 @@
 #include <string.h>
 
 #include <apr_pools.h>
+#include <apr_tables.h>
 #include <apr_hash.h>
 #include <apr_md5.h>
 #include <apr_file_io.h>
@@ -468,22 +469,21 @@
 }
 
 
-svn_error_t *
-svn_wc_process_committed4(const char *path,
-                          svn_wc_adm_access_t *adm_access,
-                          svn_boolean_t recurse,
-                          svn_revnum_t new_revnum,
-                          const char *rev_date,
-                          const char *rev_author,
-                          apr_array_header_t *wcprop_changes,
-                          svn_boolean_t remove_lock,
-                          svn_boolean_t remove_changelist,
-                          const unsigned char *digest,
-                          apr_pool_t *pool)
+static svn_error_t *
+process_committed_internal(int *log_number,
+                           const char *path,
+                           svn_wc_adm_access_t *adm_access,
+                           svn_boolean_t recurse,
+                           svn_revnum_t new_revnum,
+                           const char *rev_date,
+                           const char *rev_author,
+                           apr_array_header_t *wcprop_changes,
+                           svn_boolean_t remove_lock,
+                           svn_boolean_t remove_changelist,
+                           const unsigned char *digest,
+                           apr_pool_t *pool)
 {
-  int log_number = 1;
-
-  SVN_ERR(process_committed_leaf(0, path, adm_access, &recurse,
+  SVN_ERR(process_committed_leaf((*log_number)++, path, adm_access, &recurse,
                                  new_revnum, rev_date, rev_author,
                                  wcprop_changes,
                                  remove_lock, remove_changelist,
@@ -513,11 +513,11 @@
           apr_hash_this(hi, &key, NULL, &val);
           name = key;
           current_entry = val;
-          
+
           /* Ignore the "this dir" entry. */
           if (! strcmp(name, SVN_WC_ENTRY_THIS_DIR))
             continue;
-          
+
           /* Create child path by telescoping the main path. */
           this_path = svn_path_join(path, name, subpool);
 
@@ -526,7 +526,7 @@
                                         subpool));
           else
              child_access = adm_access;
-          
+
           /* Recurse, but only allow further recursion if the child is
              a directory.  Pass null for wcprop_changes, because the
              ones present in the current call are only applicable to
@@ -539,7 +539,7 @@
                      remove_changelist, NULL, subpool));
           else
             SVN_ERR(process_committed_leaf
-                    (log_number++, this_path, adm_access, NULL,
+                    ((*log_number)++, this_path, adm_access, NULL,
                      new_revnum, rev_date, rev_author, NULL, FALSE,
                      remove_changelist, NULL, subpool));
         }
@@ -547,6 +547,209 @@
       svn_pool_destroy(subpool); 
    }
 
+  return SVN_NO_ERROR;
+}
+
+typedef struct committed_queue_item_t
+{
+  const char *path;
+  svn_wc_adm_access_t *adm_access;
+  svn_boolean_t recurse;
+  svn_boolean_t remove_lock;
+  svn_boolean_t remove_changelist;
+  apr_array_header_t *wcprop_changes;
+  const char *digest;
+} committed_queue_item_t;
+
+
+svn_error_t *
+svn_wc_queue_committed(svn_wc_committed_queue_t **queue,
+                       const char *path,
+                       svn_wc_adm_access_t *adm_access,
+                       svn_boolean_t recurse,
+                       apr_array_header_t *wcprop_changes,
+                       svn_boolean_t remove_lock,
+                       svn_boolean_t remove_changelist,
+                       const unsigned char *digest,
+                       apr_pool_t *pool)
+{
+  committed_queue_item_t *cqi;
+  /*
+    Build an array with records with paths and options
+   */
+  if (! *queue)
+    *queue = apr_array_make(pool, 1, sizeof(cqi));
+
+  cqi = apr_palloc(pool, sizeof(*cqi));
+  cqi->path = path;
+  cqi->adm_access = adm_access;
+  cqi->recurse = recurse;
+  cqi->remove_lock = remove_lock;
+  cqi->remove_changelist = remove_changelist;
+  cqi->wcprop_changes = wcprop_changes;
+  cqi->digest = digest;
+
+  APR_ARRAY_PUSH((apr_array_header_t *)*queue, committed_queue_item_t *) = cqi;
+
+  return SVN_NO_ERROR;
+}
+
+typedef struct affected_adm_t
+{
+  int next_log;
+  svn_wc_adm_access_t *adm_access;
+} affected_adm_t;
+
+
+/* Return TRUE if any item of QUEUE (except CURRENT)
+   is a parent of PATH and will be processed recursively,
+   return FALSE otherwise.
+
+   If HAVE_RECURSIVE is FALSE, exit early returning FALSE.
+   Recalculate its value otherwise, changing it to FALSE
+   iff no recursive items are found.
+*/
+static svn_boolean_t
+have_recursive_parent(svn_boolean_t *have_recursive,
+                      apr_array_header_t *queue,
+                      int current,
+                      const char *path,
+                      apr_pool_t *pool)
+{
+  int i;
+  svn_boolean_t found_recursive = FALSE;
+
+  if (! *have_recursive)
+    return FALSE;
+
+  for (i = 0; i < queue->nelts; i++)
+    {
+      committed_queue_item_t *item
+        = APR_ARRAY_IDX(queue, i, committed_queue_item_t *);
+
+      found_recursive |= item->recurse;
+
+      if (i == current)
+        continue;
+
+      if (item->recurse
+          && svn_path_is_child(item->path, path, pool))
+        return TRUE;
+    }
+
+  *have_recursive = found_recursive;
+
+  return FALSE;
+}
+
+svn_error_t *
+svn_wc_process_committed_queue(svn_wc_committed_queue_t *queue,
+                               svn_wc_adm_access_t *adm_access,
+                               svn_revnum_t new_revnum,
+                               const char *rev_date,
+                               const char *rev_author,
+                               apr_pool_t *pool)
+{
+  int i;
+  apr_hash_index_t *hi;
+  apr_hash_t *updated_adms;
+  apr_pool_t *iterpool;
+  svn_boolean_t have_recursive = TRUE;
+  /* Assume we do have recursive items queued:
+     we need to search for recursive parents until proven otherwise */
+
+  if (! queue)
+    /* The queue hasn't been initialized: Nothing to process. */
+    return SVN_NO_ERROR;
+
+  updated_adms = apr_hash_make(pool);
+  iterpool = svn_pool_create(pool);
+
+
+  /* Now, we write all log files,
+     collecting the affected adms in the process ... */
+  for (i = 0; i < ((apr_array_header_t *)queue)->nelts; i++)
+    {
+      affected_adm_t *affected_adm;
+      const char *this_path;
+      committed_queue_item_t *cqi
+        = APR_ARRAY_IDX((apr_array_header_t *)queue,
+                        i, committed_queue_item_t *);
+
+      apr_pool_clear(iterpool);
+
+      if (have_recursive
+          && have_recursive_parent(&have_recursive,
+                                   (apr_array_header_t *)queue,
+                                   i, cqi->path, iterpool))
+        continue;
+
+      this_path = svn_wc_adm_access_path(cqi->adm_access);
+      affected_adm = apr_hash_get(updated_adms,
+                                  this_path, APR_HASH_KEY_STRING);
+      if (! affected_adm)
+        {
+          /* allocate in pool instead of subpool:
+             we don't want this cleared at the next iteration */
+          affected_adm = apr_palloc(pool, sizeof(*affected_adm));
+          affected_adm->next_log = 0;
+          affected_adm->adm_access = cqi->adm_access;
+          apr_hash_set(updated_adms, this_path, APR_HASH_KEY_STRING,
+                       affected_adm);
+        }
+
+      SVN_ERR(process_committed_internal(&affected_adm->next_log, cqi->path,
+                                         cqi->adm_access, cqi->recurse,
+                                         new_revnum, rev_date, rev_author,
+                                         cqi->wcprop_changes,
+                                         cqi->remove_lock,
+                                         cqi->remove_changelist,
+                                         cqi->digest, iterpool));
+    }
+
+  /* ... and then we run them; all at once.
+
+         This prevents writing the entries file
+         more than once per adm area */
+  for (hi = apr_hash_first(pool, updated_adms); hi; hi = apr_hash_next(hi))
+    {
+      void *val;
+      affected_adm_t *this_adm;
+
+      apr_pool_clear(iterpool);
+
+      apr_hash_this(hi, NULL, NULL, &val);
+      this_adm = val;
+
+      SVN_ERR(svn_wc__run_log(this_adm->adm_access, NULL, iterpool));
+    }
+
+  ((apr_array_header_t *)queue)->nelts = 0;
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc_process_committed4(const char *path,
+                          svn_wc_adm_access_t *adm_access,
+                          svn_boolean_t recurse,
+                          svn_revnum_t new_revnum,
+                          const char *rev_date,
+                          const char *rev_author,
+                          apr_array_header_t *wcprop_changes,
+                          svn_boolean_t remove_lock,
+                          svn_boolean_t remove_changelist,
+                          const unsigned char *digest,
+                          apr_pool_t *pool)
+{
+  int log_number = 0;
+
+  SVN_ERR(process_committed_internal(&log_number,
+                                     path, adm_access, recurse,
+                                     new_revnum, rev_date, rev_author,
+                                     wcprop_changes, remove_lock,
+                                     remove_changelist, digest, pool));
+
   /* Run the log file(s) we just created. */
   SVN_ERR(svn_wc__run_log(adm_access, NULL, pool));
 
Index: subversion/libsvn_client/commit.c
===================================================================
--- subversion/libsvn_client/commit.c	(revision 21559)
+++ subversion/libsvn_client/commit.c	(working copy)
@@ -910,27 +910,6 @@
   return err;
 }
 
-/* Return TRUE if one of the first PROCESSED items in COMMIT_ITEMS is a
-   parent of PATH, return FALSE otherwise. */
-static svn_boolean_t
-have_processed_parent(apr_array_header_t *commit_items,
-                      int processed,
-                      const char *path,
-                      apr_pool_t *pool)
-{
-  int i;
-  for (i = 0; i < processed && i < commit_items->nelts; ++i)
-    {
-      svn_client_commit_item2_t *item
-        = APR_ARRAY_IDX(commit_items, i, svn_client_commit_item2_t *);
-
-      if (svn_path_is_child(item->path, path, pool))
-        return TRUE;
-    }
-  return FALSE;
-}
-
-
 /* Remove redundancies by removing duplicates from NONRECURSIVE_TARGETS,
  * and removing any target that either is, or is a descendant of, a path in
  * RECURSIVE_TARGETS.  Return the result in *PUNIQUE_TARGETS.
@@ -1523,6 +1502,7 @@
       || (cmt_err->apr_err == SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED))
     {
       apr_pool_t *subpool = svn_pool_create(pool);
+      svn_wc_committed_queue_t *queue = NULL;
 
       /* Make a note that our commit is finished. */
       commit_in_progress = FALSE;
@@ -1534,7 +1514,6 @@
           svn_boolean_t loop_recurse = FALSE;
           const char *adm_access_path;
           svn_wc_adm_access_t *adm_access;
-          const svn_wc_entry_t *entry;
           svn_boolean_t remove_lock;
 
           /* Clear the subpool here because there are some 'continue'
@@ -1548,49 +1527,27 @@
 
           bump_err = svn_wc_adm_retrieve(&adm_access, base_dir_access,
                                          adm_access_path, subpool);
-          if (bump_err)
+          if (bump_err
+              && bump_err->apr_err == SVN_ERR_WC_NOT_LOCKED)
             {
-              if (bump_err->apr_err == SVN_ERR_WC_NOT_LOCKED)
+              /* Is it a directory that was deleted in the commit?
+                 Then we probably committed a missing directory. */
+              if (item->kind == svn_node_dir
+                  && item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
                 {
-                  if (have_processed_parent(commit_items, i,
-                                            item->path, subpool))
-                    {
-                      /* This happens when the item is a directory that is
-                         deleted, and it has been processed as a child of an
-                         earlier item. */
-                      svn_error_clear(bump_err);
-                      bump_err = SVN_NO_ERROR;
-                      continue;
-                    }
-
-                  /* Is it a directory that was deleted in the commit? */
-                  if (item->kind == svn_node_dir
-                      && item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
-                    {
-                      /* It better be missing then.  Assuming it is,
-                         mark as deleted in parent.  If not, then
-                         something is way bogus. */
-                      svn_error_clear(bump_err);
-                      bump_err = svn_wc_mark_missing_deleted(item->path,
-                                                             base_dir_access,
-                                                             subpool);
-                      if (bump_err)
-                        goto cleanup;
-                      continue;                      
-                    }                  
+                  /* Mark it as deleted in the parent. */
+                  svn_error_clear(bump_err);
+                  bump_err = svn_wc_mark_missing_deleted(item->path,
+                                                         base_dir_access,
+                                                         subpool);
+                  if (bump_err)
+                    goto cleanup;
+                  continue;
                 }
-              goto cleanup;              
+
+              goto cleanup;
             }
-          if ((bump_err = svn_wc_entry(&entry, item->path, adm_access, TRUE,
-                                       subpool)))
-            goto cleanup;
 
-          if (! entry
-              && have_processed_parent(commit_items, i, item->path, subpool))
-            /* This happens when the item is a file that is deleted, and it
-               has been processed as a child of an earlier item. */
-            continue;
-
           if ((item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD) 
               && (item->kind == svn_node_dir)
               && (item->copyfrom_url))
@@ -1599,23 +1556,25 @@
           remove_lock = (! keep_locks && (item->state_flags
                                           & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN));
           assert(*commit_info_p);
-          if ((bump_err = svn_wc_process_committed4
-               (item->path, adm_access,
-                loop_recurse,
-                (*commit_info_p)->revision,
-                (*commit_info_p)->date,
-                (*commit_info_p)->author,
+          if ((bump_err = svn_wc_queue_committed
+               (&queue,
+                item->path, adm_access, loop_recurse,
                 item->wcprop_changes,
-                remove_lock,
-                (! keep_changelist),
-                apr_hash_get(digests, item->path, APR_HASH_KEY_STRING),
-                subpool)))
+                remove_lock, (! keep_changelist),
+                apr_hash_get(digests, item->path, APR_HASH_KEY_STRING), pool)))
             break;
 
         }
 
       /* Destroy the subpool. */
       svn_pool_destroy(subpool);
+
+      bump_err
+        = svn_wc_process_committed_queue(queue, base_dir_access,
+                                         (*commit_info_p)->revision,
+                                         (*commit_info_p)->date,
+                                         (*commit_info_p)->author,
+                                         pool);
     }
 
   /* Sleep to ensure timestamp integrity. */
