You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/11/04 21:48:30 UTC

svn commit: r1031230 [8/21] - in /subversion/branches/py-tests-as-modules: ./ build/ build/ac-macros/ build/win32/ contrib/client-side/ notes/ notes/http-and-webdav/ notes/wc-ng/ subversion/bindings/ctypes-python/csvn/ subversion/bindings/javahl/native...

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_subr/svn_string.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_subr/svn_string.c?rev=1031230&r1=1031229&r2=1031230&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_subr/svn_string.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_subr/svn_string.c Thu Nov  4 20:48:21 2010
@@ -114,6 +114,10 @@ find_char_backward(const char *str, apr_
 
 /* svn_string functions */
 
+/* Return a new svn_string_t object, allocated in POOL, initialized with
+ * DATA and SIZE.  Do not copy the contents of DATA, just store the pointer.
+ * SIZE is the length in bytes of DATA, excluding the required NUL
+ * terminator. */
 static svn_string_t *
 create_string(const char *data, apr_size_t size,
               apr_pool_t *pool)
@@ -246,7 +250,15 @@ create_stringbuf(char *data, apr_size_t 
 svn_stringbuf_t *
 svn_stringbuf_create_ensure(apr_size_t blocksize, apr_pool_t *pool)
 {
-  char *data = apr_palloc(pool, ++blocksize); /* + space for '\0' */
+  char *data;
+
+  /* apr_palloc will allocate multiples of 8.
+   * Thus, we would waste some of that memory if we stuck to the
+   * smaller size. Note that this is safe even if apr_palloc would
+   * use some other aligment or none at all. */
+
+  ++blocksize; /* + space for '\0' */
+  data = apr_palloc(pool, APR_ALIGN_DEFAULT(blocksize));
 
   data[0] = '\0';
 
@@ -257,9 +269,7 @@ svn_stringbuf_create_ensure(apr_size_t b
 svn_stringbuf_t *
 svn_stringbuf_ncreate(const char *bytes, apr_size_t size, apr_pool_t *pool)
 {
-  /* Ensure string buffer of size + 1 */
   svn_stringbuf_t *strbuf = svn_stringbuf_create_ensure(size, pool);
-
   memcpy(strbuf->data, bytes, size);
 
   /* Null termination is the convention -- even if we suspect the data
@@ -364,12 +374,18 @@ svn_stringbuf_ensure(svn_stringbuf_t *st
   if (str->blocksize < minimum_size)
     {
       if (str->blocksize == 0)
-        str->blocksize = minimum_size;
+        /* APR will increase odd allocation sizes to the next
+         * multiple for 8, for instance. Take advantage of that
+         * knowledge and allow for the extra size to be used. */
+        str->blocksize = APR_ALIGN_DEFAULT(minimum_size);
       else
         while (str->blocksize < minimum_size)
           {
+            /* str->blocksize is aligned;
+             * doubling it should keep it aligned */
             apr_size_t prev_size = str->blocksize;
             str->blocksize *= 2;
+
             /* check for apr_size_t overflow */
             if (prev_size > str->blocksize)
               {
@@ -388,28 +404,60 @@ svn_stringbuf_ensure(svn_stringbuf_t *st
 }
 
 
+/* WARNING - Optimized code ahead! 
+ * This function has been hand-tuned for performance. Please read 
+ * the comments below before modifying the code.
+ */
 void
 svn_stringbuf_appendbyte(svn_stringbuf_t *str, char byte)
 {
+  char *dest;
+  apr_size_t old_len = str->len;
+
   /* In most cases, there will be pre-allocated memory left
    * to just write the new byte at the end of the used section
    * and terminate the string properly.
    */
-  apr_size_t old_len = str->len;
   if (str->blocksize > old_len + 1)
     {
-      char *dest = str->data;
+      /* The following read does not depend this write, so we
+       * can issue the write first to minimize register pressure:
+       * The value of old_len+1 is no longer needed; on most processors,
+       * dest[old_len+1] will be calculated implicitly as part of 
+       * the addressing scheme.
+       */
+      str->len = old_len+1;
+
+      /* Since the compiler cannot be sure that *src->data and *src
+       * don't overlap, we read src->data *once* before writing
+       * to *src->data. Replacing dest with str->data would force
+       * the compiler to read it again after the first byte.
+       */
+      dest = str->data;
 
+      /* If not already available in a register as per ABI, load
+       * "byte" into the register (e.g. the one freed from old_len+1),
+       * then write it to the string buffer and terminate it properly.
+       *
+       * Including the "byte" fetch, all operations so far could be
+       * issued at once and be scheduled at the CPU's descression.
+       * Most likely, no-one will soon depend on the data that will be 
+       * written in this function. So, no stalls there, either.
+       */
       dest[old_len] = byte;
       dest[old_len+1] = '\0';
-
-      str->len = old_len+1;
     }
   else
     {
       /* we need to re-allocate the string buffer
        * -> let the more generic implementation take care of that part
        */
+
+      /* Depending on the ABI, "byte" is a register value. If we were
+       * to take its address directly, the compiler might decide to
+       * put in on the stack *unconditionally*, even if that would
+       * only be necessary for this block.
+       */
       char b = byte;
       svn_stringbuf_appendbytes(str, &b, 1);
     }
@@ -661,7 +709,7 @@ svn_cstring_strtoui64(apr_uint64_t *n, c
       val < 0 || (apr_uint64_t)val < minval || (apr_uint64_t)val > maxval)
     return svn_error_return(
              svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
-                               _("Number '%s' is out of range '[%lu, %lu]'"),
+                               _("Number '%s' is out of range '[%llu, %llu]'"),
                                str, minval, maxval));
   *n = val;
   return SVN_NO_ERROR;
@@ -705,7 +753,7 @@ svn_cstring_strtoi64(apr_int64_t *n, con
       val < minval || val > maxval)
     return svn_error_return(
              svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL,
-                               _("Number '%s' is out of range '[%ld, %ld]'"),
+                               _("Number '%s' is out of range '[%lld, %lld]'"),
                                str, minval, maxval));
   *n = val;
   return SVN_NO_ERROR;

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_subr/utf.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_subr/utf.c?rev=1031230&r1=1031229&r2=1031230&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_subr/utf.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_subr/utf.c Thu Nov  4 20:48:21 2010
@@ -155,7 +155,7 @@ get_xlate_key(const char *topage,
     topage = "APR_DEFAULT_CHARSET";
 
   return apr_pstrcat(pool, "svn-utf-", frompage, "to", topage,
-                     "-xlate-handle", NULL);
+                     "-xlate-handle", (char *)NULL);
 }
 
 /* Set *RET to a handle node for converting from FROMPAGE to TOPAGE,
@@ -588,7 +588,8 @@ invalid_utf8(const char *data, apr_size_
   for (i = 0; i < valid; ++i)
     valid_txt = apr_pstrcat(pool, valid_txt,
                             apr_psprintf(pool, " %02x",
-                                         (unsigned char)last[i-valid]), NULL);
+                                         (unsigned char)last[i-valid]),
+                                         (char *)NULL);
 
   /* 4 invalid octets will guarantee that the faulty octet is displayed */
   invalid = data + len - last;
@@ -597,7 +598,8 @@ invalid_utf8(const char *data, apr_size_
   for (i = 0; i < invalid; ++i)
     invalid_txt = apr_pstrcat(pool, invalid_txt,
                               apr_psprintf(pool, " %02x",
-                                           (unsigned char)last[i]), NULL);
+                                           (unsigned char)last[i]),
+                                           (char *)NULL);
 
   return svn_error_createf(APR_EINVAL, NULL,
                            _("Valid UTF-8 data\n(hex:%s)\n"

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_wc/adm_crawler.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_wc/adm_crawler.c?rev=1031230&r1=1031229&r2=1031230&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_wc/adm_crawler.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_wc/adm_crawler.c Thu Nov  4 20:48:21 2010
@@ -347,7 +347,8 @@ report_revisions_and_depths(svn_wc__db_t
       svn_revnum_t this_rev;
       svn_depth_t this_depth;
       svn_wc__db_lock_t *this_lock;
-      svn_boolean_t this_switched;
+      svn_boolean_t this_switched = FALSE;
+      svn_boolean_t this_file_external = FALSE;
 
       /* Clear the iteration subpool here because the loop has a bunch
          of 'continue' jump statements. */
@@ -461,7 +462,6 @@ report_revisions_and_depths(svn_wc__db_t
       /* And finally prepare for reporting */
       if (!this_repos_relpath)
         {
-          this_switched = FALSE;
           this_repos_relpath = svn_relpath_join(dir_repos_relpath, child,
                                                 iterpool);
         }
@@ -472,9 +472,20 @@ report_revisions_and_depths(svn_wc__db_t
                                                        NULL);
 
           if (childname == NULL || strcmp(childname, child) != 0)
-            this_switched = TRUE;
-          else
-            this_switched = FALSE;
+            {
+              const char *file_ext_str;
+
+              this_switched = TRUE;
+             
+              /* This could be a file external!  We need to know
+                 that. */
+              SVN_ERR(svn_wc__db_temp_get_file_external(&file_ext_str, db,
+                                                        this_abspath,
+                                                        scratch_pool,
+                                                        scratch_pool));
+              if (file_ext_str)
+                this_file_external = TRUE;
+            }
         }
 
       /* Tweak THIS_DEPTH to a useful value.  */
@@ -489,9 +500,15 @@ report_revisions_and_depths(svn_wc__db_t
       if (!SVN_IS_VALID_REVNUM(this_rev))
         this_rev = dir_rev;
 
+      /*** File Externals **/
+      if (this_file_external)
+        {
+          /* File externals are ... special.  We ignore them. */;
+        }
+
       /*** Files ***/
-      if (this_kind == svn_wc__db_kind_file ||
-          this_kind == svn_wc__db_kind_symlink)
+      else if (this_kind == svn_wc__db_kind_file ||
+               this_kind == svn_wc__db_kind_symlink)
         {
           if (report_everything)
             {
@@ -563,7 +580,7 @@ report_revisions_and_depths(svn_wc__db_t
 
           if (report_everything)
             {
-              /* Report the dir unconditionally, one way or another. */
+              /* Report the dir unconditionally, one way or another... */
               if (this_switched)
                 SVN_ERR(reporter->link_path(report_baton,
                                             this_path,
@@ -585,24 +602,20 @@ report_revisions_and_depths(svn_wc__db_t
                                            this_lock ? this_lock->token : NULL,
                                            iterpool));
             }
-
-          /* Possibly report a disjoint URL ... */
           else if (this_switched)
-            SVN_ERR(reporter->link_path(report_baton,
-                                        this_path,
-                                        svn_path_url_add_component2(
-                                                dir_repos_root,
-                                                this_repos_relpath, iterpool),
-                                        this_rev,
-                                        this_depth,
-                                        start_empty,
-                                        this_lock ? this_lock->token : NULL,
-                                        iterpool));
-          /* ... or perhaps just a differing revision, lock token, incomplete
-             subdir, the mere presence of the directory in a depth-empty or
-             depth-files dir, or if the parent dir is at depth-immediates but
-             the child is not at depth-empty.  Also describe shallow subdirs
-             if we are trying to set depth to infinity. */
+            {
+              /* ...or possibly report a disjoint URL ... */
+              SVN_ERR(reporter->link_path(report_baton,
+                                          this_path,
+                                          svn_path_url_add_component2(
+                                              dir_repos_root,
+                                              this_repos_relpath, iterpool),
+                                          this_rev,
+                                          this_depth,
+                                          start_empty,
+                                          this_lock ? this_lock->token : NULL,
+                                          iterpool));
+            }
           else if (this_rev != dir_rev
                    || this_lock
                    || is_incomplete
@@ -612,16 +625,25 @@ report_revisions_and_depths(svn_wc__db_t
                        && this_depth != svn_depth_empty)
                    || (this_depth < svn_depth_infinity
                        && depth == svn_depth_infinity))
-            SVN_ERR(reporter->set_path(report_baton,
-                                       this_path,
-                                       this_rev,
-                                       this_depth,
-                                       start_empty,
-                                       this_lock ? this_lock->token : NULL,
-                                       iterpool));
+            {
+              /* ... or perhaps just a differing revision, lock token,
+                 incomplete subdir, the mere presence of the directory
+                 in a depth-empty or depth-files dir, or if the parent
+                 dir is at depth-immediates but the child is not at
+                 depth-empty.  Also describe shallow subdirs if we are
+                 trying to set depth to infinity. */
+              SVN_ERR(reporter->set_path(report_baton,
+                                         this_path,
+                                         this_rev,
+                                         this_depth,
+                                         start_empty,
+                                         this_lock ? this_lock->token : NULL,
+                                         iterpool));
+            }
 
+          /* Finally, recurse if necessary and appropriate. */
           if (SVN_DEPTH_IS_RECURSIVE(depth))
-             SVN_ERR(report_revisions_and_depths(db,
+            SVN_ERR(report_revisions_and_depths(db,
                                                 anchor_abspath,
                                                 this_path,
                                                 this_rev,
@@ -748,52 +770,21 @@ svn_wc_crawl_revisions5(svn_wc_context_t
                                  db, local_abspath, scratch_pool,
                                  scratch_pool);
 
-  {
-    svn_boolean_t has_base = TRUE;
+  if (err)
+    {
+      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
+        return svn_error_return(err);
 
-    if (err)
-      {
-        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
-          return svn_error_return(err);
+      svn_error_clear(err);
+      SVN_ERR(svn_wc__db_read_kind(&target_kind, db, local_abspath, TRUE,
+                                   scratch_pool));
 
-        svn_error_clear(err);
-        has_base = FALSE;
-        SVN_ERR(svn_wc__db_read_kind(&target_kind, db, local_abspath, TRUE,
-                                     scratch_pool));
-
-        if (target_kind == svn_wc__db_kind_file
-            || target_kind == svn_wc__db_kind_symlink)
-          status = svn_wc__db_status_absent; /* Crawl via parent dir */
-        else
-          status = svn_wc__db_status_not_present; /* As checkout */
-      }
-
-    /* ### Check the parentstub if we don't find a BASE. But don't
-           do this if we already have the info we want or we break
-           some copy scenarios. */
-    if (!has_base && target_kind == svn_wc__db_kind_dir)
-      {
-        svn_boolean_t not_present;
-        svn_revnum_t rev = SVN_INVALID_REVNUM;
-        err = svn_wc__db_temp_is_dir_deleted(&not_present, &rev,
-                                             db, local_abspath, scratch_pool);
-
-        if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
-                    || err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
-          {
-            svn_error_clear(err);
-            not_present = FALSE;
-          }
-        else
-          SVN_ERR(err);
-
-        if (not_present)
-          status = svn_wc__db_status_not_present;
-
-        if (!SVN_IS_VALID_REVNUM(target_rev))
-          target_rev = rev;
-      }
-  }
+      if (target_kind == svn_wc__db_kind_file
+          || target_kind == svn_wc__db_kind_symlink)
+        status = svn_wc__db_status_absent; /* Crawl via parent dir */
+      else
+        status = svn_wc__db_status_not_present; /* As checkout */
+    }
 
   if ((status == svn_wc__db_status_not_present)
       || (target_kind == svn_wc__db_kind_dir
@@ -956,15 +947,6 @@ svn_wc_crawl_revisions5(svn_wc_context_t
       if (err)
         goto abort_report;
 
-      if (!parent_repos_relpath)
-        err = svn_wc__db_scan_base_repos(&parent_repos_relpath, NULL,
-                                         NULL,
-                                         db, parent_abspath,
-                                         scratch_pool, scratch_pool);
-
-      if (err)
-        goto abort_report;
-
       if (strcmp(repos_relpath,
                  svn_relpath_join(parent_repos_relpath, base,
                                   scratch_pool)) != 0)

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_wc/adm_files.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_wc/adm_files.c?rev=1031230&r1=1031229&r2=1031230&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_wc/adm_files.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_wc/adm_files.c Thu Nov  4 20:48:21 2010
@@ -119,7 +119,7 @@ simple_extend(const char *adm_path,  /* 
   if (subdir)
     child = svn_dirent_join(subdir, child, result_pool);
   if (extension)
-    child = apr_pstrcat(result_pool, child, extension, NULL);
+    child = apr_pstrcat(result_pool, child, extension, (char *)NULL);
 
   if (use_tmp)
     return svn_dirent_join_many(result_pool,
@@ -774,7 +774,6 @@ svn_wc_create_tmp_file2(apr_file_t **fp,
   svn_wc__db_t *db;
   const char *local_abspath;
   const char *temp_dir;
-  apr_file_t *file;
   svn_error_t *err;
 
   SVN_ERR_ASSERT(fp || new_name);
@@ -792,14 +791,9 @@ svn_wc_create_tmp_file2(apr_file_t **fp,
   if (err)
     return svn_error_return(err);
 
-  SVN_ERR(svn_io_open_unique_file3(&file, new_name, temp_dir,
+  SVN_ERR(svn_io_open_unique_file3(fp, new_name, temp_dir,
                                    delete_when, pool, pool));
 
-  if (fp)
-    *fp = file;
-  else
-    SVN_ERR(svn_io_file_close(file, pool));
-
   return SVN_NO_ERROR;
 }
 

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_wc/adm_ops.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_wc/adm_ops.c?rev=1031230&r1=1031229&r2=1031230&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_wc/adm_ops.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_wc/adm_ops.c Thu Nov  4 20:48:21 2010
@@ -735,28 +735,129 @@ svn_wc_delete4(svn_wc_context_t *wc_ctx,
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_wc_add4(svn_wc_context_t *wc_ctx,
-            const char *local_abspath,
-            svn_depth_t depth,
-            const char *copyfrom_url,
-            svn_revnum_t copyfrom_rev,
-            svn_cancel_func_t cancel_func,
-            void *cancel_baton,
-            svn_wc_notify_func2_t notify_func,
-            void *notify_baton,
-            apr_pool_t *scratch_pool)
+/* Schedule the single node at LOCAL_ABSPATH, of kind KIND, for addition in
+ * its parent directory in the WC.  It will have no properties. */
+static svn_error_t *
+add_from_disk(svn_wc_context_t *wc_ctx,
+              const char *local_abspath,
+              svn_node_kind_t kind,
+              apr_pool_t *scratch_pool)
+{
+  svn_wc__db_t *db = wc_ctx->db;
+
+  if (kind == svn_node_file)
+    {
+      SVN_ERR(svn_wc__db_op_add_file(db, local_abspath, NULL, scratch_pool));
+    }
+  else
+    {
+      SVN_ERR(svn_wc__db_op_add_directory(db, local_abspath, NULL,
+                                          scratch_pool));
+    }
+  return SVN_NO_ERROR;
+}
+
+/* Set *REPOS_ROOT_URL and *REPOS_UUID to the repository of the parent of
+   LOCAL_ABSPATH.  REPOS_ROOT_URL and/or REPOS_UUID may be NULL if not
+   wanted.  Check that the parent of LOCAL_ABSPATH is a versioned directory
+   in a state in which a new child node can be scheduled for addition;
+   return an error if not. */
+static svn_error_t *
+check_can_add_to_parent(const char **repos_root_url,
+                        const char **repos_uuid,
+                        svn_wc_context_t *wc_ctx,
+                        const char *local_abspath,
+                        apr_pool_t *result_pool,
+                        apr_pool_t *scratch_pool)
 {
-  const char *parent_abspath;
-  const char *base_name;
-  const char *parent_repos_relpath;
-  const char *repos_root_url, *repos_uuid;
-  svn_boolean_t is_wc_root = FALSE;
-  svn_node_kind_t kind;
   svn_wc__db_t *db = wc_ctx->db;
+  const char *parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
+  svn_wc__db_status_t parent_status;
+  svn_wc__db_kind_t parent_kind;
   svn_error_t *err;
-  svn_wc__db_status_t status;
-  svn_wc__db_kind_t db_kind;
+
+  SVN_ERR(svn_wc__write_check(db, parent_abspath, scratch_pool));
+
+  err = svn_wc__db_read_info(&parent_status, &parent_kind, NULL,
+                             NULL, repos_root_url,
+                             repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                             NULL, NULL, NULL, NULL,
+                             db, parent_abspath, result_pool, scratch_pool);
+
+  if (err
+      || parent_status == svn_wc__db_status_not_present
+      || parent_status == svn_wc__db_status_excluded
+      || parent_status == svn_wc__db_status_absent)
+    {
+      return
+        svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
+                          _("Can't find parent directory's node while"
+                            " trying to add '%s'"),
+                          svn_dirent_local_style(local_abspath,
+                                                 scratch_pool));
+    }
+  else if (parent_status == svn_wc__db_status_deleted)
+    {
+      return
+        svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
+                          _("Can't add '%s' to a parent directory"
+                            " scheduled for deletion"),
+                          svn_dirent_local_style(local_abspath,
+                                                 scratch_pool));
+    }
+  else if (parent_kind != svn_wc__db_kind_dir)
+    /* Can't happen until single db; but then it causes serious
+       trouble if we allow this. */
+    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+                             _("Can't schedule an addition of '%s'"
+                               " below a not-directory node"),
+                             svn_dirent_local_style(local_abspath,
+                                                    scratch_pool));
+
+  /* If we haven't found the repository info yet, find it now. */
+  if ((repos_root_url && ! *repos_root_url)
+      || (repos_uuid && ! *repos_uuid))
+    {
+      if (parent_status == svn_wc__db_status_added)
+        SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
+                                         repos_root_url, repos_uuid, NULL,
+                                         NULL, NULL, NULL,
+                                         db, parent_abspath,
+                                         result_pool, scratch_pool));
+      else
+        SVN_ERR(svn_wc__db_scan_base_repos(NULL,
+                                           repos_root_url, repos_uuid,
+                                           db, parent_abspath,
+                                           result_pool, scratch_pool));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Check that the on-disk item at LOCAL_ABSPATH can be scheduled for
+ * addition to its WC parent directory.
+ *
+ * Set *KIND_P to the kind of node to be added, *DB_ROW_EXISTS_P to whether
+ * it is already a versioned path, and if so, *IS_WC_ROOT_P to whether it's
+ * a WC root.
+ *
+ * ### The checks here, and the outputs, are geared towards svn_wc_add4().
+ */
+static svn_error_t *
+check_can_add_node(svn_node_kind_t *kind_p,
+                   svn_boolean_t *db_row_exists_p,
+                   svn_boolean_t *is_wc_root_p,
+                   svn_wc_context_t *wc_ctx,
+                   const char *local_abspath,
+                   const char *copyfrom_url,
+                   svn_revnum_t copyfrom_rev,
+                   apr_pool_t *scratch_pool)
+{
+  const char *base_name = svn_dirent_basename(local_abspath, scratch_pool);
+  svn_boolean_t is_wc_root;
+  svn_node_kind_t kind;
+  svn_wc__db_t *db = wc_ctx->db;
   svn_boolean_t exists;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
@@ -764,7 +865,7 @@ svn_wc_add4(svn_wc_context_t *wc_ctx,
                                                         scratch_pool)
                                    && SVN_IS_VALID_REVNUM(copyfrom_rev)));
 
-  svn_dirent_split(&parent_abspath, &base_name, local_abspath, scratch_pool);
+  /* Check that the proposed node has an acceptable name. */
   if (svn_wc_is_adm_dir(base_name, scratch_pool))
     return svn_error_createf
       (SVN_ERR_ENTRY_FORBIDDEN, NULL,
@@ -773,7 +874,7 @@ svn_wc_add4(svn_wc_context_t *wc_ctx,
 
   SVN_ERR(svn_path_check_valid(local_abspath, scratch_pool));
 
-  /* Make sure something's there. */
+  /* Make sure something's there; set KIND and *KIND_P. */
   SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
   if (kind == svn_node_none)
     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
@@ -785,123 +886,170 @@ svn_wc_add4(svn_wc_context_t *wc_ctx,
                              _("Unsupported node kind for path '%s'"),
                              svn_dirent_local_style(local_abspath,
                                                     scratch_pool));
+  if (kind_p)
+    *kind_p = kind;
 
-  /* Get the node information for this path if one exists (perhaps
-     this is actually a replacement of a previously deleted thing). */
-  err = svn_wc__db_read_info(&status, &db_kind, NULL, NULL, NULL, NULL, NULL,
+  /* Determine whether a DB row for this node EXISTS, and whether it
+     IS_WC_ROOT.  If it exists, check that it is in an acceptable state for
+     adding the new node; if not, return an error. */
+  {
+    svn_wc__db_status_t status;
+    svn_error_t *err
+      = svn_wc__db_read_info(&status, 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 (err)
-    {
-      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
-        return svn_error_return(err);
+    if (err)
+      {
+        if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
+          return svn_error_return(err);
 
-      svn_error_clear(err);
-      exists = FALSE;
-      is_wc_root = FALSE;
-    }
-  else
-    {
-      is_wc_root = FALSE;
-      exists = TRUE;
-      switch (status)
-        {
-          case svn_wc__db_status_not_present:
-            break;
-          case svn_wc__db_status_deleted:
-            /* A working copy root should never have a WORKING_NODE */
-            SVN_ERR_ASSERT(!is_wc_root);
-            break;
-          case svn_wc__db_status_normal:
-            if (copyfrom_url)
-              {
-                SVN_ERR(svn_wc__check_wc_root(&is_wc_root, NULL, NULL,
-                                              db, local_abspath,
-                                              scratch_pool));
+        svn_error_clear(err);
+        exists = FALSE;
+        is_wc_root = FALSE;
+      }
+    else
+      {
+        is_wc_root = FALSE;
+        exists = TRUE;
+        switch (status)
+          {
+            case svn_wc__db_status_not_present:
+              break;
+            case svn_wc__db_status_deleted:
+              /* A working copy root should never have a WORKING_NODE */
+              SVN_ERR_ASSERT(!is_wc_root);
+              break;
+            case svn_wc__db_status_normal:
+              if (copyfrom_url)
+                {
+                  SVN_ERR(svn_wc__check_wc_root(&is_wc_root, NULL, NULL,
+                                                db, local_abspath,
+                                                scratch_pool));
 
-                if (is_wc_root)
-                  break;
-              }
-            /* else: Fall through in default error */
+                  if (is_wc_root)
+                    break;
+                }
+              /* else: Fall through in default error */
 
-          default:
-            return svn_error_createf(
-                             SVN_ERR_ENTRY_EXISTS, NULL,
-                             _("'%s' is already under version control"),
-                             svn_dirent_local_style(local_abspath,
-                                                    scratch_pool));
-        }
-    } /* err */
+            default:
+              return svn_error_createf(
+                               SVN_ERR_ENTRY_EXISTS, NULL,
+                               _("'%s' is already under version control"),
+                               svn_dirent_local_style(local_abspath,
+                                                      scratch_pool));
+          }
+      } /* err */
 
-  SVN_ERR(svn_wc__write_check(db, parent_abspath, scratch_pool));
+    if (db_row_exists_p)
+      *db_row_exists_p = exists;
+    if (is_wc_root_p)
+      *is_wc_root_p = is_wc_root;
+  }
 
-  {
-    svn_wc__db_status_t parent_status;
-    svn_wc__db_kind_t parent_kind;
+  return SVN_NO_ERROR;
+}
 
-    err = svn_wc__db_read_info(&parent_status, &parent_kind, NULL,
-                               &parent_repos_relpath, &repos_root_url,
-                               &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                               NULL, NULL, NULL, NULL,
-                               db, parent_abspath, scratch_pool, scratch_pool);
+/* Convert the nested pristine working copy rooted at LOCAL_ABSPATH into
+ * a copied subtree in the outer working copy.
+ *
+ * LOCAL_ABSPATH must be the root of a nested working copy that has no
+ * local modifications.  The parent directory of LOCAL_ABSPATH must be a
+ * versioned directory in the outer WC, and must belong to the same
+ * repository as the nested WC.  The nested WC will be integrated into the
+ * parent's WC, and will no longer be a separate WC. */
+static svn_error_t *
+integrate_nested_wc_as_copy(svn_wc_context_t *wc_ctx,
+                            const char *local_abspath,
+                            apr_pool_t *scratch_pool)
+{
+  svn_wc__db_t *db = wc_ctx->db;
+  const char *moved_abspath;
 
-    if (err
-        || parent_status == svn_wc__db_status_not_present
-        || parent_status == svn_wc__db_status_excluded
-        || parent_status == svn_wc__db_status_absent)
-      {
-        return
-          svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
-                            _("Can't find parent directory's node while"
-                              " trying to add '%s'"),
-                            svn_dirent_local_style(local_abspath,
-                                                   scratch_pool));
-      }
-    else if (parent_status == svn_wc__db_status_deleted)
-      {
-        return
-          svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
-                            _("Can't add '%s' to a parent directory"
-                              " scheduled for deletion"),
-                            svn_dirent_local_style(local_abspath,
-                                                   scratch_pool));
-      }
-    else if (parent_kind != svn_wc__db_kind_dir)
-      /* Can't happen until single db; but then it causes serious
-         trouble if we allow this. */
-      return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
-                               _("Can't schedule an addition of '%s'"
-                                 " below a not-directory node"),
-                               svn_dirent_local_style(local_abspath,
-                                                   scratch_pool));
+  /* Drop any references to the wc that is to be rewritten */
+  SVN_ERR(svn_wc__db_drop_root(db, local_abspath, scratch_pool));
 
-    if (!repos_root_url)
-      {
-        if (parent_status == svn_wc__db_status_added)
-          SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, &parent_repos_relpath,
-                                           &repos_root_url, &repos_uuid, NULL,
-                                           NULL, NULL, NULL,
-                                           db, parent_abspath,
+  /* Move the admin dir from the wc to a temporary location: MOVED_ABSPATH */
+  {
+    const char *tmpdir_abspath, *moved_adm_abspath, *adm_abspath;
+
+    SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db,
+                                           svn_dirent_dirname(local_abspath,
+                                                              scratch_pool),
                                            scratch_pool, scratch_pool));
-        else
-          SVN_ERR(svn_wc__db_scan_base_repos(&parent_repos_relpath,
-                                             &repos_root_url, &repos_uuid,
-                                             db, parent_abspath,
-                                             scratch_pool, scratch_pool));
-      }
+    SVN_ERR(svn_io_open_unique_file3(NULL, &moved_abspath, tmpdir_abspath,
+                                     svn_io_file_del_on_close,
+                                     scratch_pool, scratch_pool));
+    SVN_ERR(svn_io_dir_make(moved_abspath, APR_OS_DEFAULT, scratch_pool));
+
+    adm_abspath = svn_wc__adm_child(local_abspath, "", scratch_pool);
+    moved_adm_abspath = svn_wc__adm_child(moved_abspath, "", scratch_pool);
+    SVN_ERR(svn_io_file_move(adm_abspath, moved_adm_abspath, scratch_pool));
+  }
 
-    if (copyfrom_url
-        && !svn_uri_is_ancestor(repos_root_url, copyfrom_url))
-      return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
-                               _("The URL '%s' has a different repository "
-                                 "root than its parent"), copyfrom_url);
+  /* Copy entries from temporary location into the main db */
+  SVN_ERR(svn_wc_copy3(wc_ctx, moved_abspath, local_abspath,
+                       TRUE /* metadata_only */,
+                       NULL, NULL, NULL, NULL, scratch_pool));
+
+  /* Cleanup the temporary admin dir */
+  SVN_ERR(svn_wc__db_drop_root(db, moved_abspath, scratch_pool));
+  SVN_ERR(svn_io_remove_dir2(moved_abspath, FALSE, NULL, NULL,
+                             scratch_pool));
+
+  /* The subdir is now part of our parent working copy. Our caller assumes
+     that we return the new node locked, so obtain a lock if we didn't
+     receive the lock via our depth infinity lock */
+  {
+    svn_boolean_t owns_lock;
+    SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath,
+                                        FALSE, scratch_pool));
+    if (!owns_lock)
+      SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
+                                       scratch_pool));
   }
 
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc_add4(svn_wc_context_t *wc_ctx,
+            const char *local_abspath,
+            svn_depth_t depth,
+            const char *copyfrom_url,
+            svn_revnum_t copyfrom_rev,
+            svn_cancel_func_t cancel_func,
+            void *cancel_baton,
+            svn_wc_notify_func2_t notify_func,
+            void *notify_baton,
+            apr_pool_t *scratch_pool)
+{
+  svn_wc__db_t *db = wc_ctx->db;
+  svn_node_kind_t kind;
+  svn_boolean_t db_row_exists, is_wc_root;
+  const char *repos_root_url, *repos_uuid;
+
+  SVN_ERR(check_can_add_node(&kind, &db_row_exists, &is_wc_root,
+                             wc_ctx, local_abspath, copyfrom_url, copyfrom_rev,
+                             scratch_pool));
+
+  /* Get REPOS_ROOT_URL and REPOS_UUID.  Check that the
+     parent is a versioned directory in an acceptable state. */
+  SVN_ERR(check_can_add_to_parent(&repos_root_url, &repos_uuid,
+                                  wc_ctx, local_abspath, scratch_pool,
+                                  scratch_pool));
+
+  /* If we're performing a repos-to-WC copy, check that the copyfrom
+     repository is the same as the parent dir's repository. */
+  if (copyfrom_url
+      && !svn_uri_is_ancestor(repos_root_url, copyfrom_url))
+    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+                             _("The URL '%s' has a different repository "
+                               "root than its parent"), copyfrom_url);
+
   /* Verify that we can actually integrate the inner working copy */
   if (is_wc_root)
     {
@@ -926,13 +1074,8 @@ svn_wc_add4(svn_wc_context_t *wc_ctx,
                                  inner_repos_root_url, inner_repos_uuid,
                                  repos_root_url, repos_uuid);
 
-      if (!svn_uri_is_ancestor(repos_root_url, copyfrom_url))
-        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
-                                 _("The URL '%s' is not in repository '%s'"),
-                                 copyfrom_url, repos_root_url);
-
       inner_url = svn_path_url_add_component2(repos_root_url, repos_relpath,
-                                             scratch_pool);
+                                              scratch_pool);
 
       if (strcmp(copyfrom_url, inner_url))
         return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
@@ -943,11 +1086,27 @@ svn_wc_add4(svn_wc_context_t *wc_ctx,
                                  copyfrom_url, inner_url);
     }
 
-  if (kind == svn_node_file)
+  if (!copyfrom_url)  /* Case 2a: It's a simple add */
     {
-      if (!copyfrom_url)
-        SVN_ERR(svn_wc__db_op_add_file(db, local_abspath, NULL, scratch_pool));
-      else
+      SVN_ERR(add_from_disk(wc_ctx, local_abspath, kind, scratch_pool));
+      if (kind == svn_node_dir && !db_row_exists)
+        {
+          /* If using the legacy 1.6 interface the parent lock may not
+             be recursive and add is expected to lock the new dir.
+
+             ### Perhaps the lock should be created in the same
+             transaction that adds the node? */
+          svn_boolean_t owns_lock;
+          SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath,
+                                              FALSE, scratch_pool));
+          if (!owns_lock)
+            SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
+                                             scratch_pool));
+        }
+    }
+  else if (!is_wc_root)  /* Case 2b: It's a copy from the repository */
+    {
+      if (kind == svn_node_file)
         {
           /* This code should never be used, as it doesn't install proper
              pristine and/or properties. But it was not an error in the old
@@ -957,93 +1116,60 @@ svn_wc_add4(svn_wc_context_t *wc_ctx,
           svn_stream_t *content = svn_stream_empty(scratch_pool);
 
           SVN_ERR(svn_wc_add_repos_file4(wc_ctx, local_abspath,
-                                         content, NULL,
-                                         NULL, NULL,
+                                         content, NULL, NULL, NULL,
                                          copyfrom_url, copyfrom_rev,
                                          cancel_func, cancel_baton,
-                                         NULL, NULL,
                                          scratch_pool));
         }
+      else
+        SVN_ERR(svn_wc__db_op_copy_dir(db, local_abspath,
+                                       apr_hash_make(scratch_pool),
+                                       copyfrom_rev, 0, NULL,
+                                       svn_path_uri_decode(
+                                         svn_uri_skip_ancestor(repos_root_url,
+                                                               copyfrom_url),
+                                         scratch_pool),
+                                       repos_root_url, repos_uuid,
+                                       copyfrom_rev,
+                                       NULL /* children */, depth,
+                                       NULL /* conflicts */,
+                                       NULL /* work items */,
+                                       scratch_pool));
     }
-  else if (!copyfrom_url)
+  else  /* Case 1: Integrating a separate WC into this one, in place */
     {
-      SVN_ERR(svn_wc__db_op_add_directory(db, local_abspath, NULL,
+      SVN_ERR(integrate_nested_wc_as_copy(wc_ctx, local_abspath,
                                           scratch_pool));
-      if (!exists)
-        {
-          /* If using the legacy 1.6 interface the parent lock may not
-             be recursive and add is expected to lock the new dir.
-
-             ### Perhaps the lock should be created in the same
-             transaction that adds the node? */
-          svn_boolean_t owns_lock;
-          SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath,
-                                              FALSE, scratch_pool));
-          if (!owns_lock)
-            SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
-                                             scratch_pool));
-        }
     }
-  else if (!is_wc_root)
-    SVN_ERR(svn_wc__db_op_copy_dir(db,
-                                   local_abspath,
-                                   apr_hash_make(scratch_pool),
-                                   copyfrom_rev,
-                                   0,
-                                   NULL,
-                                   svn_path_uri_decode(
-                                        svn_uri_skip_ancestor(repos_root_url,
-                                                              copyfrom_url),
-                                        scratch_pool),
-                                   repos_root_url,
-                                   repos_uuid,
-                                   copyfrom_rev,
-                                   NULL,
-                                   depth,
-                                   NULL,
-                                   NULL,
-                                   scratch_pool));
-  else
+
+  /* Report the addition to the caller. */
+  if (notify_func != NULL)
     {
-      svn_boolean_t owns_lock;
-      const char *tmpdir_abspath, *moved_abspath, *moved_adm_abspath;
-      const char *adm_abspath = svn_wc__adm_child(local_abspath, "",
-                                                  scratch_pool);
-
-      /* Drop any references to the wc that is to be rewritten */
-      SVN_ERR(svn_wc__db_drop_root(db, local_abspath, scratch_pool));
-
-      /* Move the admin dir from the wc to a temporary location */
-      SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmpdir_abspath, db,
-                                             parent_abspath,
-                                             scratch_pool, scratch_pool));
-      SVN_ERR(svn_io_open_unique_file3(NULL, &moved_abspath, tmpdir_abspath,
-                                       svn_io_file_del_on_close,
-                                       scratch_pool, scratch_pool));
-      SVN_ERR(svn_io_dir_make(moved_abspath, APR_OS_DEFAULT, scratch_pool));
-      moved_adm_abspath = svn_wc__adm_child(moved_abspath, "", scratch_pool);
-      SVN_ERR(svn_io_file_move(adm_abspath, moved_adm_abspath, scratch_pool));
-
-      /* Copy entries from temporary location into the main db */
-      SVN_ERR(svn_wc_copy3(wc_ctx, moved_abspath, local_abspath,
-                           TRUE /* metadata_only */,
-                           NULL, NULL, NULL, NULL, scratch_pool));
-
-      /* Cleanup the temporary admin dir */
-      SVN_ERR(svn_wc__db_drop_root(db, moved_abspath, scratch_pool));
-      SVN_ERR(svn_io_remove_dir2(moved_abspath, FALSE, NULL, NULL,
-                                 scratch_pool));
-
-      /* The subdir is now part of our parent working copy. Our caller assumes
-         that we return the new node locked, so obtain a lock if we didn't
-         receive the lock via our depth infinity lock */
-      SVN_ERR(svn_wc__db_wclock_owns_lock(&owns_lock, db, local_abspath, FALSE,
-                                          scratch_pool));
-      if (!owns_lock)
-        SVN_ERR(svn_wc__db_wclock_obtain(db, local_abspath, 0, FALSE,
-                                         scratch_pool));
+      svn_wc_notify_t *notify = svn_wc_create_notify(local_abspath,
+                                                     svn_wc_notify_add,
+                                                     scratch_pool);
+      notify->kind = kind;
+      (*notify_func)(notify_baton, notify, scratch_pool);
     }
 
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc_add_from_disk(svn_wc_context_t *wc_ctx,
+                     const char *local_abspath,
+                     svn_wc_notify_func2_t notify_func,
+                     void *notify_baton,
+                     apr_pool_t *scratch_pool)
+{
+  svn_node_kind_t kind;
+
+  SVN_ERR(check_can_add_node(&kind, NULL, NULL, wc_ctx, local_abspath,
+                             NULL, SVN_INVALID_REVNUM, scratch_pool));
+  SVN_ERR(check_can_add_to_parent(NULL, NULL, wc_ctx, local_abspath,
+                                  scratch_pool, scratch_pool));
+  SVN_ERR(add_from_disk(wc_ctx, local_abspath, kind, scratch_pool));
+
   /* Report the addition to the caller. */
   if (notify_func != NULL)
     {
@@ -1133,194 +1259,6 @@ svn_wc__register_file_external(svn_wc_co
 */
 
 
-/* */
-static svn_error_t *
-revert_admin_things(svn_boolean_t *reverted,
-                    svn_wc__db_t *db,
-                    const char *local_abspath,
-                    svn_boolean_t use_commit_times,
-                    apr_pool_t *pool)
-{
-  SVN_ERR(svn_wc__wq_add_revert(reverted, db, local_abspath, use_commit_times,
-                                pool));
-  SVN_ERR(svn_wc__wq_run(db, local_abspath, NULL, NULL, pool));
-
-  return SVN_NO_ERROR;
-}
-
-
-/* Revert LOCAL_ABSPATH in DB, where the on-disk node kind is DISK_KIND.
-   *DEPTH is the depth of the reversion crawl the caller is
-   using; this function may choose to override that value as needed.
-
-   See svn_wc_revert4() for the interpretations of
-   USE_COMMIT_TIMES, CANCEL_FUNC and CANCEL_BATON.
-
-   Set *DID_REVERT to true if actually reverting anything, else do not
-   touch *DID_REVERT.
-
-   Use POOL for allocations.
- */
-static svn_error_t *
-revert_entry(svn_depth_t *depth,
-             svn_wc__db_t *db,
-             const char *local_abspath,
-             svn_node_kind_t disk_kind,
-             svn_boolean_t use_commit_times,
-             svn_cancel_func_t cancel_func,
-             void *cancel_baton,
-             svn_boolean_t *did_revert,
-             apr_pool_t *pool)
-{
-  svn_wc__db_status_t status, base_status;
-  svn_wc__db_kind_t kind, base_kind;
-  svn_boolean_t replaced;
-  svn_boolean_t have_base;
-  svn_revnum_t base_revision;
-  svn_boolean_t is_add_root;
-
-  /* Initialize this even though revert_admin_things() is guaranteed
-     to set it, because we don't know that revert_admin_things() will
-     be called. */
-  svn_boolean_t reverted = FALSE;
-
-  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, &have_base, NULL,
-                               NULL, NULL,
-                               db, local_abspath, pool, pool));
-
-  if (have_base)
-    SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &base_revision,
-                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                     NULL, NULL, NULL, NULL, NULL,
-                                     db, local_abspath, pool, pool));
-
-  replaced = (status == svn_wc__db_status_added
-              && have_base
-              && base_status != svn_wc__db_status_not_present);
-
-  if (status == svn_wc__db_status_added)
-    {
-      const char *op_root_abspath;
-      SVN_ERR(svn_wc__db_scan_addition(NULL, &op_root_abspath, NULL, NULL,
-                                       NULL, NULL, NULL, NULL, NULL,
-                                       db, local_abspath, pool, pool));
-
-      is_add_root = (strcmp(op_root_abspath, local_abspath) == 0);
-    }
-  else
-    is_add_root = FALSE;
-
-  /* Additions. */
-  if (!replaced
-      && is_add_root)
-    {
-      const char *repos_relpath;
-      const char *repos_root_url;
-      const char *repos_uuid;
-      /* Before removing item from revision control, notice if the
-         BASE_NODE is in a 'not-present' state. */
-      svn_boolean_t was_not_present = FALSE;
-
-      /* NOTE: if WAS_NOT_PRESENT gets set, then we have BASE nodes.
-         The code below will then figure out the repository information, so
-         that we can later insert a node for the same repository. */
-
-      if (have_base
-          && base_status == svn_wc__db_status_not_present)
-        {
-          /* Remember the BASE revision. (already handled)  */
-          /* Remember the repository this node is associated with.  */
-
-          was_not_present = TRUE;
-
-          SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath,
-                                             &repos_root_url,
-                                             &repos_uuid,
-                                             db, local_abspath,
-                                             pool, pool));
-        }
-
-      /* ### much of this is probably bullshit. we should be able to just
-         ### remove the WORKING and ACTUAL rows, and be done. but we're
-         ### not quite there yet, so nodes get fully removed and then
-         ### shoved back into the database. this is why we need to record
-         ### the repository information, and the BASE revision.  */
-
-      if (kind == svn_wc__db_kind_file)
-        {
-          SVN_ERR(svn_wc__internal_remove_from_revision_control(db,
-                                                                local_abspath,
-                                                                FALSE, FALSE,
-                                                                cancel_func,
-                                                                cancel_baton,
-                                                                pool));
-        }
-      else if (kind == svn_wc__db_kind_dir)
-        {
-          /* Before single-db we didn't have to perform a recursive delete
-             here. With single-db we really must delete missing nodes */
-          SVN_ERR(svn_wc__internal_remove_from_revision_control(db,
-                                                                local_abspath,
-                                                                FALSE, FALSE,
-                                                                cancel_func,
-                                                                cancel_baton,
-                                                                pool));
-        }
-      else  /* Else it's `none', or something exotic like a symlink... */
-        {
-          return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
-                                   _("Unknown or unexpected kind for path "
-                                     "'%s'"),
-                                   svn_dirent_local_style(local_abspath,
-                                                          pool));
-
-        }
-
-      /* Recursivity is taken care of by svn_wc_remove_from_revision_control,
-         and we've definitely reverted PATH at this point. */
-      *depth = svn_depth_empty;
-      reverted = TRUE;
-
-      /* If the removed item was *also* in a 'not-present' state, make
-         sure we leave a not-present node behind */
-      if (was_not_present)
-        {
-          SVN_ERR(svn_wc__db_base_add_absent_node(
-                    db, local_abspath,
-                    repos_relpath, repos_root_url, repos_uuid,
-                    base_revision,
-                    base_kind,
-                    svn_wc__db_status_not_present,
-                    NULL, NULL,
-                    pool));
-        }
-    }
-  /* Regular prop and text edit. */
-  /* Deletions and replacements. */
-  else if (status == svn_wc__db_status_normal
-           || status == svn_wc__db_status_deleted
-           || replaced
-           || (status == svn_wc__db_status_added && !is_add_root))
-    {
-      /* Revert the prop and text mods (if any). */
-      SVN_ERR(revert_admin_things(&reverted, db, local_abspath,
-                                  use_commit_times, pool));
-
-      /* Force recursion on replaced directories. */
-      if (kind == svn_wc__db_kind_dir && replaced)
-        *depth = svn_depth_infinity;
-    }
-
-  /* If PATH was reverted, tell our client that. */
-  if (reverted)
-    *did_revert = TRUE;
-
-  return SVN_NO_ERROR;
-}
-
 /* Verifies if an add (or copy) to LOCAL_ABSPATH can be reverted with depth
  * DEPTH, without touching nodes that are filtered by DEPTH.
  *
@@ -1394,6 +1332,7 @@ verify_revert_depth(svn_wc__db_t *db,
    documentation. */
 static svn_error_t *
 revert_internal(svn_wc__db_t *db,
+                const char *revert_root,
                 const char *local_abspath,
                 svn_depth_t depth,
                 svn_boolean_t use_commit_times,
@@ -1405,10 +1344,14 @@ revert_internal(svn_wc__db_t *db,
                 apr_pool_t *pool)
 {
   svn_node_kind_t disk_kind;
-  svn_wc__db_status_t status;
+  svn_wc__db_status_t status, base_status;
   svn_wc__db_kind_t db_kind;
   svn_boolean_t unversioned;
+  svn_boolean_t have_base;
+  svn_boolean_t replaced;
+  svn_boolean_t reverted = FALSE;
   const svn_wc_conflict_description2_t *tree_conflict;
+  const char *op_root_abspath = NULL;
   svn_error_t *err;
 
   /* Check cancellation here, so recursive calls get checked early. */
@@ -1421,7 +1364,7 @@ revert_internal(svn_wc__db_t *db,
   err = svn_wc__db_read_info(&status, &db_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, &have_base, NULL, NULL,
                              NULL,
                              db, local_abspath, pool, pool);
 
@@ -1440,11 +1383,30 @@ revert_internal(svn_wc__db_t *db,
         case svn_wc__db_status_excluded:
           unversioned = TRUE;
           break;
+        case svn_wc__db_status_incomplete:
+          /* Remove NAME from PATH's entries file
+
+             Not being able to revert incomplete entries breaks working
+             copies flat out, but the usual revert process can't be
+             applied.  Most preconditions aren't met. */
+          SVN_ERR(svn_wc__db_temp_op_remove_entry(db, local_abspath, pool));
+          return SVN_NO_ERROR;
+          break;
         default:
           unversioned = FALSE;
           break;
       }
 
+  if (! unversioned && have_base)
+    SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, NULL,
+                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+                                     NULL, NULL, NULL, NULL, NULL,
+                                     db, local_abspath, pool, pool));
+
+  replaced = ! unversioned && (status == svn_wc__db_status_added
+              && have_base
+              && base_status != svn_wc__db_status_not_present);
+
   SVN_ERR(svn_wc__db_op_read_tree_conflict(&tree_conflict, db, local_abspath,
                                            pool, pool));
   if (unversioned && tree_conflict == NULL)
@@ -1484,8 +1446,13 @@ revert_internal(svn_wc__db_t *db,
        svn_dirent_local_style(local_abspath, pool));
 
   /* Safeguard 3:  can we deal with the node kind of PATH currently in
-     the working copy? */
-  if ((disk_kind != svn_node_none)
+     the working copy?
+
+     Note: we can reach this point for paths which have tree conflict info
+           set on them.  Those are not necessarily nodes we can version,
+           meaning this check doesn't make sense for unversioned nodes. */
+  if (!unversioned
+      && (disk_kind != svn_node_none)
       && (disk_kind != svn_node_file)
       && (disk_kind != svn_node_dir))
     return svn_error_createf
@@ -1493,17 +1460,18 @@ revert_internal(svn_wc__db_t *db,
        _("Cannot revert '%s': unsupported node kind in working copy"),
        svn_dirent_local_style(local_abspath, pool));
 
+  if (!unversioned && status == svn_wc__db_status_added)
+    SVN_ERR(svn_wc__db_scan_addition(NULL, &op_root_abspath, NULL, NULL,
+                                     NULL, NULL, NULL, NULL, NULL,
+                                     db, local_abspath, pool, pool));
+
   /* Safeguard 4:  Make sure we don't revert deeper then asked */
-  if (status == svn_wc__db_status_added
+  if (!unversioned
+      && status == svn_wc__db_status_added
       && db_kind == svn_wc__db_kind_dir
       && depth >= svn_depth_empty
       && depth < svn_depth_infinity)
     {
-      const char *op_root_abspath;
-      SVN_ERR(svn_wc__db_scan_addition(NULL, &op_root_abspath, NULL, NULL,
-                                       NULL, NULL, NULL, NULL, NULL,
-                                       db, local_abspath, pool, pool));
-
       /* If this node is an operation root for a copy/add, then reverting
          it will change its descendants, if it has any. */
       if (strcmp(local_abspath, op_root_abspath) == 0)
@@ -1515,7 +1483,6 @@ revert_internal(svn_wc__db_t *db,
   if (svn_wc__internal_changelist_match(db, local_abspath, changelist_hash,
                                         pool))
     {
-      svn_boolean_t reverted = FALSE;
       const svn_wc_conflict_description2_t *conflict;
 
       /* Clear any tree conflict on the path, even if it is not a versioned
@@ -1532,10 +1499,19 @@ revert_internal(svn_wc__db_t *db,
       /* Actually revert this entry.  If this is a working copy root,
          we provide a base_name from the parent path. */
       if (!unversioned)
-        SVN_ERR(revert_entry(&depth, db, local_abspath, disk_kind,
-                             use_commit_times,
-                             cancel_func, cancel_baton,
-                             &reverted, pool));
+        {
+          /* Revert the prop, text and tree mods (if any). */
+          SVN_ERR(svn_wc__wq_add_revert(&reverted, db, revert_root,
+                                        local_abspath, use_commit_times,
+                                        pool));
+          SVN_ERR(svn_wc__wq_run(db, local_abspath,
+                                 cancel_func, cancel_baton, pool));
+
+          /* Force recursion on replaced directories. */
+          if (db_kind == svn_wc__db_kind_dir && replaced)
+            depth = svn_depth_infinity;
+
+        }
 
       /* Notify */
       if (notify_func && reverted)
@@ -1545,7 +1521,23 @@ revert_internal(svn_wc__db_t *db,
                        pool);
     }
 
+
+  if (op_root_abspath && strcmp(local_abspath, op_root_abspath) == 0)
+    /* If this is a copy or add root, disable notifications for the children,
+       because wc-1.0 used to behave like that. */
+    {
+      notify_func = NULL;
+      notify_baton = NULL;
+    }
+
   /* Finally, recurse if requested. */
+
+  /* ### This doesn't work properly for added directories.  Reverting
+     ### the parent before the children is wrong, it means node rows
+     ### exist for the children after the parent row is removed.
+     ### Either the wq revert of the parent above has to remove the
+     ### children or this recursion has to do children before parents.
+   */
   if (!unversioned && db_kind == svn_wc__db_kind_dir && depth > svn_depth_empty)
     {
       const apr_array_header_t *children;
@@ -1588,7 +1580,7 @@ revert_internal(svn_wc__db_t *db,
             continue;
 
           /* Revert the entry. */
-          SVN_ERR(revert_internal(db, node_abspath,
+          SVN_ERR(revert_internal(db, revert_root, node_abspath,
                                   depth_under_here, use_commit_times,
                                   changelist_hash, cancel_func, cancel_baton,
                                   notify_func, notify_baton, iterpool));
@@ -1632,7 +1624,8 @@ revert_internal(svn_wc__db_t *db,
                                 const svn_wc_conflict_description2_t *);
 
                 if (conflict->kind == svn_wc_conflict_kind_tree)
-                  SVN_ERR(revert_internal(db, conflict->local_abspath,
+                  SVN_ERR(revert_internal(db, revert_root,
+                                          conflict->local_abspath,
                                           svn_depth_empty,
                                           use_commit_times, changelist_hash,
                                           cancel_func, cancel_baton,
@@ -1645,6 +1638,20 @@ revert_internal(svn_wc__db_t *db,
       svn_pool_destroy(iterpool);
     }
 
+  if (reverted  /* implies !unversioned; only versioned paths get reverted */
+      && ! replaced
+      && status == svn_wc__db_status_added
+      && db_kind == svn_wc__db_kind_dir)
+    {
+      /* Non-replaced directories have their admin area deleted. wc-1.0 */
+      /* In wc-ng, this call does not really delete the admin area - since
+         there isn't one - but it does destroy the adm_access structure
+         which may be cached inside DB, if the DB is used with old entries
+         functions. */
+      SVN_ERR(svn_wc__adm_destroy(db, local_abspath,
+                                  cancel_func, cancel_baton, pool));
+    }
+
   return SVN_NO_ERROR;
 }
 
@@ -1667,7 +1674,7 @@ svn_wc_revert4(svn_wc_context_t *wc_ctx,
     SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool));
 
   return svn_error_return(revert_internal(wc_ctx->db,
-                                          local_abspath, depth,
+                                          local_abspath, local_abspath, depth,
                                           use_commit_times, changelist_hash,
                                           cancel_func, cancel_baton,
                                           notify_func, notify_baton,

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_wc/conflicts.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_wc/conflicts.c?rev=1031230&r1=1031229&r2=1031230&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_wc/conflicts.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_wc/conflicts.c Thu Nov  4 20:48:21 2010
@@ -50,152 +50,6 @@
 
 #include "svn_private_config.h"
 
-struct svn_wc_conflict_t
-{
-  /* ### kind + property name are the primary keys of a conflict */
-  /* The kind of conflict recorded */
-  svn_wc_conflict_kind_t kind;
-
-  /* When describing a property conflict the property name
-     or "" when no property name is available. (Upgrade from old WC or
-     raised via compatibility apis). */
-  const char *property_name;
-
-  /* ### TODO: Add more fields */
-};
-
-/* */
-static svn_error_t *
-conflict_alloc(svn_wc_conflict_t **conflict, apr_pool_t *result_pool)
-{
-  svn_wc_conflict_t *c = apr_pcalloc(result_pool, sizeof(*c));
-
-  *conflict = c;
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_wc_conflict_dup(svn_wc_conflict_t **duplicate,
-                    const svn_wc_conflict_t *base,
-                    apr_pool_t *result_pool)
-{
-  svn_wc_conflict_t *c;
-
-  SVN_ERR(conflict_alloc(&c, result_pool));
-
-  c->kind = base->kind;
-  c->property_name = base->property_name
-                          ? apr_pstrdup(result_pool, base->property_name)
-                          : NULL;
-
-  *duplicate = c;
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_wc_create_property_conflict(svn_wc_conflict_t **conflict,
-                                const char *property_name,
-                                const svn_wc_conflict_version_t *older_version,
-                                const svn_wc_conflict_version_t *left_version,
-                                const svn_wc_conflict_version_t *right_version,
-                                const svn_string_t *older_value,
-                                const svn_string_t *left_value,
-                                const svn_string_t *right_value,
-                                const char *marker_abspath,
-                                svn_wc_operation_t operation,
-                                apr_pool_t *result_pool,
-                                apr_pool_t *scratch_pool)
-{
-  SVN_ERR_MALFUNCTION(); /* ### Not implemented yet */
-}
-
-svn_error_t *
-svn_wc_create_text_conflict(svn_wc_conflict_t **conflict,
-                            const svn_wc_conflict_version_t *older_version,
-                            const svn_wc_conflict_version_t *left_version,
-                            const svn_wc_conflict_version_t *right_version,
-                            const char *older_abspath,
-                            const char *left_abspath,
-                            const char *right_abspath,
-                            svn_wc_operation_t operation,
-                            apr_pool_t *result_pool,
-                            apr_pool_t *scratch_pool)
-{
-  SVN_ERR_MALFUNCTION(); /* ### Not implemented yet */
-}
-
-svn_error_t *
-svn_wc_create_tree_conflict(svn_wc_conflict_t **conflict,
-                            const svn_wc_conflict_version_t *older_version,
-                            const svn_wc_conflict_version_t *left_version,
-                            const svn_wc_conflict_version_t *right_version,
-                            svn_wc_conflict_action_t action,
-                            svn_wc_conflict_reason_t reason,
-                            svn_wc_operation_t operation,
-                            apr_pool_t *result_pool,
-                            apr_pool_t *scratch_pool)
-{
-  SVN_ERR_MALFUNCTION(); /* ### Not implemented yet */
-}
-
-svn_error_t *
-svn_wc_get_conflict_info(svn_wc_conflict_kind_t *kind,
-                         const char **property_name,
-                         svn_wc_conflict_action_t *action,
-                         svn_wc_conflict_reason_t *reason,
-                         svn_wc_operation_t *operation,
-                         svn_boolean_t *conflict_resolved,
-                         svn_wc_context_t *wc_ctx,
-                         const char *local_abspath,
-                         svn_wc_conflict_t *conflict,
-                         apr_pool_t *result_pool,
-                         apr_pool_t *scratch_pool)
-{
-  SVN_ERR_MALFUNCTION(); /* ### Not implemented yet */
-}
-
-
-svn_error_t *
-svn_wc_get_conflict_marker_files(const char **older_abspath,
-                                 const char **left_abspath,
-                                 const char **right_abspath,
-                                 svn_wc_context_t *wc_ctx,
-                                 const char *local_abspath,
-                                 svn_wc_conflict_t *conflict,
-                                 apr_pool_t *result_pool,
-                                 apr_pool_t *scratch_pool)
-{
-  SVN_ERR_MALFUNCTION(); /* ### Not implemented yet */
-}
-
-svn_error_t *
-svn_wc_get_conflict_sources(const svn_wc_conflict_version_t **older_version,
-                            const svn_wc_conflict_version_t **left_version,
-                            const svn_wc_conflict_version_t **right_version,
-                            svn_wc_context_t *wc_ctx,
-                            const char *local_abspath,
-                            svn_wc_conflict_t *conflict,
-                            apr_pool_t *result_pool,
-                            apr_pool_t *scratch_pool)
-{
-  SVN_ERR_MALFUNCTION(); /* ### Not implemented yet */
-}
-
-svn_error_t *
-svn_wc_get_property_conflict_data(const svn_string_t **older_value,
-                                  const svn_string_t **left_value,
-                                  const svn_string_t **right_value,
-                                  svn_wc_context_t *wc_ctx,
-                                  const char *local_abspath,
-                                  svn_wc_conflict_t *conflict,
-                                  apr_pool_t *result_pool,
-                                  apr_pool_t *scratch_pool)
-{
-  SVN_ERR_MALFUNCTION(); /* ### Not implemented yet */
-}
-
-
 svn_skel_t *
 svn_wc__conflict_skel_new(apr_pool_t *result_pool)
 {
@@ -312,6 +166,8 @@ attempt_deletion(const char *parent_dir,
    ### leave, for example, one of the conflict artifact files deleted but
    ### the entry still referring to it and trying to use it for the next
    ### attempt at resolving.
+
+   ### Does this still apply in the world of WC-NG?  -hkw
 */
 static svn_error_t *
 resolve_conflict_on_node(svn_wc__db_t *db,
@@ -334,11 +190,7 @@ resolve_conflict_on_node(svn_wc__db_t *d
 
   *did_resolve = FALSE;
 
-  SVN_ERR(svn_wc__db_read_info(NULL, &kind, 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, pool, pool));
+  SVN_ERR(svn_wc__db_read_kind(&kind, db, local_abspath, TRUE, pool));
   SVN_ERR(svn_wc__db_read_conflicts(&conflicts, db, local_abspath,
                                     pool, pool));
 
@@ -366,6 +218,7 @@ resolve_conflict_on_node(svn_wc__db_t *d
 
   if (resolve_text)
     {
+      svn_stream_t *tmp_stream = NULL;
       const char *auto_resolve_src;
 
       /* Handle automatic conflict resolution before the temporary files are
@@ -390,7 +243,6 @@ resolve_conflict_on_node(svn_wc__db_t *d
             if (conflict_old && conflict_working && conflict_new)
               {
                 const char *temp_dir;
-                svn_stream_t *tmp_stream;
                 svn_diff_t *diff;
                 svn_diff_conflict_display_style_t style =
                   conflict_choice == svn_wc_conflict_choose_theirs_conflict
@@ -402,7 +254,8 @@ resolve_conflict_on_node(svn_wc__db_t *d
                                                        pool, pool));
                 SVN_ERR(svn_stream_open_unique(&tmp_stream,
                                                &auto_resolve_src,
-                                               temp_dir, svn_io_file_del_none,
+                                               temp_dir,
+                                               svn_io_file_del_on_close,
                                                pool, pool));
 
                 /* ### If any of these paths isn't absolute, treat it
@@ -436,7 +289,6 @@ resolve_conflict_on_node(svn_wc__db_t *d
                                                     NULL, NULL, NULL, NULL,
                                                     style,
                                                     pool));
-                SVN_ERR(svn_stream_close(tmp_stream));
               }
             else
               auto_resolve_src = NULL;
@@ -451,6 +303,9 @@ resolve_conflict_on_node(svn_wc__db_t *d
         SVN_ERR(svn_io_copy_file(
           svn_dirent_join(conflict_dir_abspath, auto_resolve_src, pool),
           local_abspath, TRUE, pool));
+
+      if (tmp_stream)
+        SVN_ERR(svn_stream_close(tmp_stream));
     }
 
   /* Records whether we found any of the conflict files.  */