You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2010/12/02 21:55:18 UTC

svn commit: r1041580 [9/35] - in /subversion/branches/gpg-agent-password-store: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/hook-scripts/ contrib/server-side/ notes/http-and-webdav/ notes/wc-ng/ subversio...

Modified: subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/dirent_uri.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/dirent_uri.c?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/dirent_uri.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/dirent_uri.c Thu Dec  2 20:55:08 2010
@@ -63,7 +63,13 @@ typedef enum {
   type_relpath
 } path_type_t;
 
- 
+
+/**** Forward declarations *****/
+
+static svn_boolean_t
+relpath_is_canonical(const char *relpath);
+
+
 /**** Internal implementation functions *****/
 
 /* Return an internal-style new path based on PATH, allocated in POOL.
@@ -1189,8 +1195,8 @@ svn_relpath_join(const char *base,
   apr_size_t clen = strlen(component);
   char *path;
 
-  assert(svn_relpath_is_canonical(base, pool));
-  assert(svn_relpath_is_canonical(component, pool));
+  assert(relpath_is_canonical(base));
+  assert(relpath_is_canonical(component));
 
   /* If either is empty return the other */
   if (blen == 0)
@@ -1316,7 +1322,7 @@ svn_relpath_dirname(const char *relpath,
 {
   apr_size_t len = strlen(relpath);
 
-  assert(svn_relpath_is_canonical(relpath, pool));
+  assert(relpath_is_canonical(relpath));
 
   return apr_pstrmemdup(pool, relpath,
                         relpath_previous_segment(relpath, len));
@@ -1329,7 +1335,7 @@ svn_relpath_basename(const char *relpath
   apr_size_t len = strlen(relpath);
   apr_size_t start;
 
-  assert(!pool || svn_relpath_is_canonical(relpath, pool));
+  assert(relpath_is_canonical(relpath));
 
   start = len;
   while (start > 0 && relpath[start - 1] != '/')
@@ -1422,6 +1428,9 @@ svn_relpath_get_longest_ancestor(const c
                                  const char *relpath2,
                                  apr_pool_t *pool)
 {
+  assert(relpath_is_canonical(relpath1));
+  assert(relpath_is_canonical(relpath2));
+
   return apr_pstrndup(pool, relpath1,
                       get_longest_ancestor_length(type_relpath, relpath1,
                                                   relpath2, pool));
@@ -1497,6 +1506,9 @@ svn_relpath_is_child(const char *parent_
                      const char *child_relpath,
                      apr_pool_t *pool)
 {
+  /* assert(relpath_is_canonical(parent_relpath)); */
+  /* assert(relpath_is_canonical(child_relpath)); */
+
   return is_child(type_relpath, parent_relpath, child_relpath, pool);
 }
 
@@ -1517,6 +1529,9 @@ svn_dirent_is_ancestor(const char *paren
 svn_boolean_t
 svn_relpath_is_ancestor(const char *parent_relpath, const char *child_relpath)
 {
+  assert(relpath_is_canonical(parent_relpath));
+  assert(relpath_is_canonical(child_relpath));
+
   return is_ancestor(type_relpath, parent_relpath, child_relpath);
 }
 
@@ -1563,15 +1578,15 @@ svn_relpath_skip_ancestor(const char *pa
 {
   apr_size_t len = strlen(parent_relpath);
 
+  assert(relpath_is_canonical(parent_relpath));
+  assert(relpath_is_canonical(child_relpath));
+
   if (0 != memcmp(parent_relpath, child_relpath, len))
     return child_relpath; /* parent_relpath is no ancestor of child_relpath */
 
   if (child_relpath[len] == 0)
     return ""; /* parent_relpath == child_relpath */
 
-  if (len == 1 && child_relpath[0] == '/')
-    return child_relpath + 1;
-
   if (child_relpath[len] == '/')
     return child_relpath + len + 1;
 
@@ -1737,12 +1752,11 @@ svn_dirent_is_canonical(const char *dire
     }
 #endif /* SVN_USE_DOS_PATHS */
 
-  return svn_relpath_is_canonical(ptr, pool);
+  return relpath_is_canonical(ptr);
 }
 
-svn_boolean_t
-svn_relpath_is_canonical(const char *relpath,
-                         apr_pool_t *pool)
+static svn_boolean_t
+relpath_is_canonical(const char *relpath)
 {
   const char *ptr = relpath, *seg = relpath;
 
@@ -1787,6 +1801,13 @@ svn_relpath_is_canonical(const char *rel
 }
 
 svn_boolean_t
+svn_relpath_is_canonical(const char *relpath,
+                         apr_pool_t *pool)
+{
+  return relpath_is_canonical(relpath);
+}
+
+svn_boolean_t
 svn_uri_is_canonical(const char *uri, apr_pool_t *pool)
 {
   const char *ptr = uri, *seg = uri;
@@ -2444,3 +2465,167 @@ svn_uri_get_file_url_from_dirent(const c
 
   return SVN_NO_ERROR;
 }
+
+
+/* ------------------------ The fspath API ------------------------ */
+
+svn_boolean_t
+svn_fspath__is_canonical(const char *fspath)
+{
+  return fspath[0] == '/' && relpath_is_canonical(fspath + 1);
+}
+
+
+const char *
+svn_fspath__is_child(const char *parent_fspath,
+                     const char *child_fspath,
+                     apr_pool_t *pool)
+{
+  const char *result;
+  assert(svn_fspath__is_canonical(parent_fspath));
+  assert(svn_fspath__is_canonical(child_fspath));
+
+#ifdef FSPATH_USE_URI
+  result = svn_uri_is_child(parent_fspath, child_fspath, pool);
+#else
+  result = svn_relpath_is_child(parent_fspath + 1, child_fspath + 1, pool);
+#endif
+
+  assert(result == NULL || svn_relpath_is_canonical(result, pool));
+  return result;
+}
+
+const char *
+svn_fspath__skip_ancestor(const char *parent_fspath,
+                          const char *child_fspath)
+{
+  const char *result;
+  assert(svn_fspath__is_canonical(parent_fspath));
+  assert(svn_fspath__is_canonical(child_fspath));
+
+#ifdef FSPATH_USE_URI
+  result = svn_uri_skip_ancestor(parent_fspath, child_fspath);
+#else
+  if (svn_relpath_is_ancestor(parent_fspath + 1, child_fspath + 1))
+    result = svn_relpath_skip_ancestor(parent_fspath + 1, child_fspath + 1);
+  else
+    result = child_fspath;
+#endif
+
+  assert(svn_relpath_is_canonical(result, NULL)
+         || strcmp(result, child_fspath) == 0);
+  return result;
+}
+
+svn_boolean_t
+svn_fspath__is_ancestor(const char *parent_fspath,
+                        const char *child_fspath)
+{
+  assert(svn_fspath__is_canonical(parent_fspath));
+  assert(svn_fspath__is_canonical(child_fspath));
+
+#ifdef FSPATH_USE_URI
+  return svn_uri_is_ancestor(parent_fspath, child_fspath);
+#else
+  return svn_relpath_is_ancestor(parent_fspath + 1, child_fspath + 1);
+#endif
+}
+
+
+const char *
+svn_fspath__dirname(const char *fspath,
+                    apr_pool_t *pool)
+{
+  const char *result;
+  assert(svn_fspath__is_canonical(fspath));
+
+#ifdef FSPATH_USE_URI
+  result = svn_uri_dirname(fspath, pool);
+#else
+  result = apr_pstrcat(pool, "/", svn_relpath_dirname(fspath + 1, pool),
+                       (char *)NULL);
+#endif
+
+  assert(svn_fspath__is_canonical(result));
+  return result;
+}
+
+
+const char *
+svn_fspath__basename(const char *fspath,
+                     apr_pool_t *pool)
+{
+  const char *result;
+  assert(svn_fspath__is_canonical(fspath));
+
+#ifdef FSPATH_USE_URI
+  result = svn_uri_basename(fspath, pool);
+#else
+  result = svn_relpath_basename(fspath + 1, pool);
+#endif
+
+  assert(strchr(result, '/') == NULL);
+  return result;
+}
+
+void
+svn_fspath__split(const char **dirpath,
+                  const char **base_name,
+                  const char *fspath,
+                  apr_pool_t *result_pool)
+{
+  assert(dirpath != base_name);
+
+  if (dirpath)
+    *dirpath = svn_fspath__dirname(fspath, result_pool);
+
+  if (base_name)
+    *base_name = svn_fspath__basename(fspath, result_pool);
+}
+
+char *
+svn_fspath__join(const char *fspath,
+                 const char *relpath,
+                 apr_pool_t *result_pool)
+{
+  char *result;
+  assert(svn_fspath__is_canonical(fspath));
+  assert(svn_relpath_is_canonical(relpath, result_pool));
+
+#ifdef FSPATH_USE_URI
+  result = svn_uri_join(fspath, relpath, result_pool);
+#else
+  if (relpath[0] == '\0')
+    result = apr_pstrdup(result_pool, fspath);
+  else if (fspath[1] == '\0')
+    result = apr_pstrcat(result_pool, "/", relpath, (char *)NULL);
+  else
+    result = apr_pstrcat(result_pool, fspath, "/", relpath, (char *)NULL);
+#endif
+
+  assert(svn_fspath__is_canonical(result));
+  return result;
+}
+
+char *
+svn_fspath__get_longest_ancestor(const char *fspath1,
+                                 const char *fspath2,
+                                 apr_pool_t *result_pool)
+{
+  char *result;
+  assert(svn_fspath__is_canonical(fspath1));
+  assert(svn_fspath__is_canonical(fspath2));
+
+#ifdef FSPATH_USE_URI
+  result = svn_uri_get_longest_ancestor(fspath1, fspath2, result_pool);
+#else
+  result = apr_pstrcat(result_pool, "/",
+                       svn_relpath_get_longest_ancestor(fspath1 + 1,
+                                                        fspath2 + 1,
+                                                        result_pool),
+                       NULL);
+#endif
+
+  assert(svn_fspath__is_canonical(result));
+  return result;
+}

Modified: subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/eol.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/eol.c?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/eol.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/eol.c Thu Dec  2 20:55:08 2010
@@ -84,7 +84,7 @@ svn_eol__detect_file_eol(const char **eo
       err = svn_io_file_read(file, buf, &nbytes, pool);
       if (err)
         {
-          /* An error occured. We're going to return in any case,
+          /* An error occurred. We're going to return in any case,
            * so reset the file cursor right now. */
           pos = orig_pos;
           SVN_ERR(svn_io_file_seek(file, APR_SET, &pos, pool));

Modified: subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/io.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/io.c?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/io.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/io.c Thu Dec  2 20:55:08 2010
@@ -334,6 +334,10 @@ svn_io_open_uniquely_named(apr_file_t **
   unsigned int i;
   struct temp_file_cleanup_s *baton = NULL;
 
+  /* At the beginning, we don't know whether unique_path will need 
+     UTF8 conversion */
+  svn_boolean_t needs_utf8_conversion = TRUE;
+
   SVN_ERR_ASSERT(file || unique_path);
 
   if (dirpath == NULL)
@@ -394,7 +398,21 @@ svn_io_open_uniquely_named(apr_file_t **
          before starting iteration, then convert back to UTF-8 for
          return. But I suppose that would make the appending code
          sensitive to i18n in a way it shouldn't be... Oh well. */
-      SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name, scratch_pool));
+      if (needs_utf8_conversion)
+        {
+          SVN_ERR(cstring_from_utf8(&unique_name_apr, unique_name,
+                                    scratch_pool));
+          if (i == 1)
+            {
+              /* The variable parts of unique_name will not require UTF8
+                 conversion. Therefore, if UTF8 conversion had no effect
+                 on it in the first iteration, it won't require conversion
+                 in any future interation. */
+              needs_utf8_conversion = strcmp(unique_name_apr, unique_name);
+            }
+        }
+      else
+        unique_name_apr = unique_name;
 
       apr_err = file_open(&try_file, unique_name_apr, flag,
                           APR_OS_DEFAULT, FALSE, result_pool);
@@ -800,6 +818,29 @@ file_perms_set(const char *fname, apr_fi
   else
     return SVN_NO_ERROR;
 }
+
+/* Set permissions PERMS on the FILE. This is a cheaper variant of the 
+ * file_perms_set wrapper() function because no locale-dependent string
+ * conversion is required. 
+ */
+static svn_error_t *
+file_perms_set2(apr_file_t* file, apr_fileperms_t perms)
+{
+  const char *fname_apr;
+  apr_status_t status;
+
+  status = apr_file_name_get(&fname_apr, file);
+  if (status)
+    return svn_error_wrap_apr(status, _("Can't get file name"));
+
+  status = apr_file_perms_set(fname_apr, perms);
+  if (status)
+    return svn_error_wrap_apr(status, _("Can't set permissions on '%s'"),
+                              fname_apr);
+  else
+    return SVN_NO_ERROR;
+}
+
 #endif /* !WIN32 && !__OS2__ */
 
 svn_error_t *
@@ -1680,7 +1721,7 @@ svn_io_file_lock2(const char *lock_file,
 
 /* Data consistency/coherency operations. */
 
-static svn_error_t *
+static APR_INLINE svn_error_t *
 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
                            const char *msg, const char *msg_no_name,
                            apr_pool_t *pool);
@@ -2741,7 +2782,7 @@ svn_io_file_open(apr_file_t **new_file, 
 }
 
 
-static svn_error_t *
+static APR_INLINE svn_error_t *
 do_io_file_wrapper_cleanup(apr_file_t *file, apr_status_t status,
                            const char *msg, const char *msg_no_name,
                            apr_pool_t *pool)
@@ -3273,6 +3314,10 @@ svn_io_dir_walk2(const char *dirname,
 
   SVN_ERR(cstring_from_utf8(&dirname_apr, dirname, pool));
 
+  /* APR doesn't like "" directories */
+  if (dirname_apr[0] == '\0')
+    dirname_apr = ".";
+
   apr_err = apr_dir_open(&handle, dirname_apr, pool);
   if (apr_err)
     return svn_error_wrap_apr(apr_err, _("Can't open directory '%s'"),
@@ -3836,7 +3881,7 @@ svn_io_open_unique_file3(apr_file_t **fi
    * ### So we tweak perms of the tempfile here, but only if the umask
    * ### allows it. */
   SVN_ERR(merge_default_file_perms(tempfile, &perms, scratch_pool));
-  SVN_ERR(file_perms_set(tempname, perms, scratch_pool));
+  SVN_ERR(file_perms_set2(tempfile, perms));
 #endif
 
   if (file)

Modified: subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/mergeinfo.c?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/mergeinfo.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/mergeinfo.c Thu Dec  2 20:55:08 2010
@@ -1760,7 +1760,7 @@ svn_mergeinfo__remove_prefix_from_catalo
       apr_ssize_t padding = 0;
 
       SVN_ERR_ASSERT(klen >= prefix_len);
-      SVN_ERR_ASSERT(svn_uri_is_ancestor(prefix_path, original_path));
+      SVN_ERR_ASSERT(svn_fspath__is_ancestor(prefix_path, original_path));
 
       /* If the ORIGINAL_PATH doesn't match the PREFIX_PATH exactly
          and we're not simply removing a single leading slash (such as
@@ -1809,33 +1809,28 @@ svn_mergeinfo__add_prefix_to_catalog(svn
 svn_error_t *
 svn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo,
                                        svn_mergeinfo_t mergeinfo,
-                                       const char *suffix,
+                                       const char *suffix_relpath,
                                        apr_pool_t *result_pool,
                                        apr_pool_t *scratch_pool)
 {
-  if (!suffix || svn_dirent_is_absolute(suffix))
-    {
-      *out_mergeinfo = svn_mergeinfo_dup(mergeinfo, result_pool);
-    }
-  else
-    {
-      apr_hash_index_t *hi;
-      const char *canonical_suffix = svn_uri_canonicalize(suffix,
-                                                          scratch_pool);
-      *out_mergeinfo = apr_hash_make(result_pool);
+  apr_hash_index_t *hi;
 
-      for (hi = apr_hash_first(scratch_pool, mergeinfo);
-           hi;
-           hi = apr_hash_next(hi))
-        {
-          const char *path = svn__apr_hash_index_key(hi);
-          apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
+  SVN_ERR_ASSERT(suffix_relpath && svn_relpath_is_canonical(suffix_relpath,
+                                                            scratch_pool));
 
-          apr_hash_set(*out_mergeinfo,
-                       svn_dirent_join(path, canonical_suffix, result_pool),
-                       APR_HASH_KEY_STRING,
-                       svn_rangelist_dup(rangelist, result_pool));
-        }
+  *out_mergeinfo = apr_hash_make(result_pool);
+
+  for (hi = apr_hash_first(scratch_pool, mergeinfo);
+       hi;
+       hi = apr_hash_next(hi))
+    {
+      const char *path = svn__apr_hash_index_key(hi);
+      apr_array_header_t *rangelist = svn__apr_hash_index_val(hi);
+
+      apr_hash_set(*out_mergeinfo,
+                   svn_dirent_join(path, suffix_relpath, result_pool),
+                   APR_HASH_KEY_STRING,
+                   svn_rangelist_dup(rangelist, result_pool));
     }
 
   return SVN_NO_ERROR;

Modified: subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/opt.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/opt.c?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/opt.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/opt.c Thu Dec  2 20:55:08 2010
@@ -124,6 +124,84 @@ svn_opt_get_option_from_code(int code,
 }
 
 
+/* Like svn_opt_get_option_from_code2(), but also, if CODE appears a second
+ * time in OPTION_TABLE with a different name, then set *LONG_ALIAS to that
+ * second name, else set it to NULL. */
+static const apr_getopt_option_t *
+get_option_from_code(const char **long_alias,
+                     int code,
+                     const apr_getopt_option_t *option_table,
+                     const svn_opt_subcommand_desc2_t *command,
+                     apr_pool_t *pool)
+{
+  const apr_getopt_option_t *i;
+  const apr_getopt_option_t *opt
+    = svn_opt_get_option_from_code2(code, option_table, command, pool);
+
+  /* Find a long alias in the table, if there is one. */
+  *long_alias = NULL;
+  for (i = option_table; i->optch; i++)
+    {
+      if (i->optch == code && i->name != opt->name)
+        {
+          *long_alias = i->name;
+          break;
+        }
+    }
+
+  return opt;
+}
+
+
+/* Print an option OPT nicely into a STRING allocated in POOL.
+ * If OPT has a single-character short form, then print OPT->name (if not
+ * NULL) as an alias, else print LONG_ALIAS (if not NULL) as an alias.
+ * If DOC is set, include the generic documentation string of OPT,
+ * localized to the current locale if a translation is available.
+ */
+static void
+format_option(const char **string,
+              const apr_getopt_option_t *opt,
+              const char *long_alias,
+              svn_boolean_t doc,
+              apr_pool_t *pool)
+{
+  char *opts;
+
+  if (opt == NULL)
+    {
+      *string = "?";
+      return;
+    }
+
+  /* We have a valid option which may or may not have a "short
+     name" (a single-character alias for the long option). */
+  if (opt->optch <= 255)
+    opts = apr_psprintf(pool, "-%c [--%s]", opt->optch, opt->name);
+  else if (long_alias)
+    opts = apr_psprintf(pool, "--%s [--%s]", opt->name, long_alias);
+  else
+    opts = apr_psprintf(pool, "--%s", opt->name);
+
+  if (opt->has_arg)
+    opts = apr_pstrcat(pool, opts, _(" ARG"), (char *)NULL);
+
+  if (doc)
+    opts = apr_psprintf(pool, "%-24s : %s", opts, _(opt->description));
+
+  *string = opts;
+}
+
+void
+svn_opt_format_option(const char **string,
+                      const apr_getopt_option_t *opt,
+                      svn_boolean_t doc,
+                      apr_pool_t *pool)
+{
+  format_option(string, opt, NULL, doc, pool);
+}
+
+
 svn_boolean_t
 svn_opt_subcommand_takes_option3(const svn_opt_subcommand_desc2_t *command,
                                  int option_code,
@@ -207,6 +285,7 @@ print_command_info2(const svn_opt_subcom
   if (help)
     {
       const apr_getopt_option_t *option;
+      const char *long_alias;
       svn_boolean_t have_options = FALSE;
 
       SVN_ERR(svn_cmdline_fprintf(stream, pool, ": %s", _(cmd->help)));
@@ -224,16 +303,14 @@ print_command_info2(const svn_opt_subcom
                 }
 
               /* convert each option code into an option */
-              option =
-                svn_opt_get_option_from_code2(cmd->valid_options[i],
-                                              options_table,
-                                              cmd, pool);
+              option = get_option_from_code(&long_alias, cmd->valid_options[i],
+                                            options_table, cmd, pool);
 
               /* print the option's docstring */
               if (option && option->description)
                 {
                   const char *optstr;
-                  svn_opt_format_option(&optstr, option, TRUE, pool);
+                  format_option(&optstr, option, long_alias, TRUE, pool);
                   SVN_ERR(svn_cmdline_fprintf(stream, pool, "  %s\n",
                                               optstr));
                 }
@@ -250,16 +327,14 @@ print_command_info2(const svn_opt_subcom
             {
 
               /* convert each option code into an option */
-              option =
-                svn_opt_get_option_from_code2(global_options[i],
-                                              options_table,
-                                              cmd, pool);
+              option = get_option_from_code(&long_alias, global_options[i],
+                                            options_table, cmd, pool);
 
               /* print the option's docstring */
               if (option && option->description)
                 {
                   const char *optstr;
-                  svn_opt_format_option(&optstr, option, TRUE, pool);
+                  format_option(&optstr, option, long_alias, TRUE, pool);
                   SVN_ERR(svn_cmdline_fprintf(stream, pool, "  %s\n",
                                               optstr));
                 }
@@ -312,36 +387,6 @@ svn_opt_print_generic_help2(const char *
   svn_error_clear(err);
 }
 
-void
-svn_opt_format_option(const char **string,
-                      const apr_getopt_option_t *opt,
-                      svn_boolean_t doc,
-                      apr_pool_t *pool)
-{
-  char *opts;
-
-  if (opt == NULL)
-    {
-      *string = "?";
-      return;
-    }
-
-  /* We have a valid option which may or may not have a "short
-     name" (a single-character alias for the long option). */
-  if (opt->optch <= 255)
-    opts = apr_psprintf(pool, "-%c [--%s]", opt->optch, opt->name);
-  else
-    opts = apr_psprintf(pool, "--%s", opt->name);
-
-  if (opt->has_arg)
-    opts = apr_pstrcat(pool, opts, _(" ARG"), (char *)NULL);
-
-  if (doc)
-    opts = apr_psprintf(pool, "%-24s : %s", opts, _(opt->description));
-
-  *string = opts;
-}
-
 
 void
 svn_opt_subcommand_help3(const char *subcommand,

Modified: subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/subst.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/subst.c?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/subst.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/subst.c Thu Dec  2 20:55:08 2010
@@ -798,6 +798,10 @@ struct translation_baton
   /* Length of the EOL style string found in the chunk-source,
      or zero if none encountered yet */
   apr_size_t src_format_len;
+
+  /* If this is svn_tristate_false, translate_newline() will be called
+     for every newline in the file */
+  svn_tristate_t nl_translation_skippable;
 };
 
 
@@ -828,6 +832,7 @@ create_translation_baton(const char *eol
   b->newline_off = 0;
   b->keyword_off = 0;
   b->src_format_len = 0;
+  b->nl_translation_skippable = svn_tristate_unknown;
 
   /* Most characters don't start translation actions.
    * Mark those that do depending on the parameters we got. */
@@ -843,6 +848,38 @@ create_translation_baton(const char *eol
   return b;
 }
 
+/* Return TRUE if the EOL starting at BUF matches the eol_str member of B.
+ * Be aware of special cases like "\n\r\n" and "\n\n\r". For sequences like
+ * "\n$" (an EOL followed by a keyword), the result will be FALSE since it is
+ * more efficient to handle that special case implicitly in the calling code
+ * by exiting the quick scan loop.
+ * The caller must ensure that buf[0] and buf[1] refer to valid memory
+ * locations.
+ */
+static APR_INLINE svn_boolean_t
+eol_unchanged(struct translation_baton *b,
+              const char *buf)
+{
+  /* If the first byte doesn't match, the whole EOL won't.
+   * This does also handle the (certainly invalid) case that 
+   * eol_str would be an empty string.
+   */
+  if (buf[0] != b->eol_str[0])
+    return FALSE;
+
+  /* two-char EOLs must be a full match */
+  if (b->eol_str_len == 2)
+    return buf[1] == b->eol_str[1];
+
+  /* The first char matches the required 1-byte EOL. 
+   * But maybe, buf[] contains a 2-byte EOL?
+   * In that case, the second byte will be interesting
+   * and not be another EOL of its own.
+   */
+  return !b->interesting[(unsigned char)buf[1]] || buf[0] == buf[1];
+}
+
+
 /* Translate eols and keywords of a 'chunk' of characters BUF of size BUFLEN
  * according to the settings and state stored in baton B.
  *
@@ -948,19 +985,72 @@ translate_chunk(svn_stream_t *dst,
               continue;
             }
 
-          /* We're in the boring state; look for interest characters. */
-          len = 0;
+          /* translate_newline will modify the baton for src_format_len==0
+             or may return an error if b->repair is FALSE.  In all other
+             cases, we can skip the newline translation as long as source
+             EOL format and actual EOL format match.  If there is a 
+             mismatch, translate_newline will be called regardless of 
+             nl_translation_skippable. 
+           */
+          if (b->nl_translation_skippable == svn_tristate_unknown &&
+              b->src_format_len > 0)
+            {
+              /* test whether translate_newline may return an error */
+              if (b->eol_str_len == b->src_format_len &&
+                  strncmp(b->eol_str, b->src_format, b->eol_str_len) == 0)
+                b->nl_translation_skippable = svn_tristate_true;
+              else if (b->repair) 
+                b->nl_translation_skippable = svn_tristate_true;
+              else
+                b->nl_translation_skippable = svn_tristate_false;
+            }
+
+          /* We're in the boring state; look for interesting characters.
+             Offset len such that it will become 0 in the first iteration. 
+           */
+          len = 0 - b->eol_str_len;
+
+          /* Look for the next EOL (or $) that actually needs translation.
+             Stop there or at EOF, whichever is encountered first.
+           */
+          do
+            {
+              /* skip current EOL */
+              len += b->eol_str_len;
+
+              /* Check 4 bytes at once to allow for efficient pipelining
+                 and to reduce loop condition overhead. */
+              while ((p + len + 4) <= end)
+                {
+                  char sum = interesting[(unsigned char)p[len]]
+                           | interesting[(unsigned char)p[len+1]]
+                           | interesting[(unsigned char)p[len+2]]
+                           | interesting[(unsigned char)p[len+3]];
+
+                  if (sum != 0)
+                    break;
+
+                  len += 4;
+                }
+
+               /* Found an interesting char or EOF in the next 4 bytes. 
+                  Find its exact position. */
+               while ((p + len) < end && !interesting[(unsigned char)p[len]])
+                 ++len;
+            }
+          while (b->nl_translation_skippable ==
+                   svn_tristate_true &&       /* can potentially skip EOLs */
+                 p + len + 2 < end &&         /* not too close to EOF */
+                 eol_unchanged (b, p + len)); /* EOL format already ok */
 
-          /* We wanted memcspn(), but lacking that, the loop below has
-             the same effect. Also, skip NUL characters.
-          */
           while ((p + len) < end && !interesting[(unsigned char)p[len]])
             len++;
 
           if (len)
-            SVN_ERR(translate_write(dst, p, len));
-
-          p += len;
+            {
+              SVN_ERR(translate_write(dst, p, len));
+              p += len;
+            }
 
           /* Set up state according to the interesting character, if any. */
           if (p < end)

Modified: subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/svn_string.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/svn_string.c?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/svn_string.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/libsvn_subr/svn_string.c Thu Dec  2 20:55:08 2010
@@ -250,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';
 
@@ -261,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
@@ -368,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)
               {
@@ -406,7 +418,7 @@ svn_stringbuf_appendbyte(svn_stringbuf_t
    * to just write the new byte at the end of the used section
    * and terminate the string properly.
    */
-  if (str->blocksize < old_len + 1)
+  if (str->blocksize > old_len + 1)
     {
       /* The following read does not depend this write, so we
        * can issue the write first to minimize register pressure:

Modified: subversion/branches/gpg-agent-password-store/subversion/libsvn_wc/adm_crawler.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/libsvn_wc/adm_crawler.c?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/libsvn_wc/adm_crawler.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/libsvn_wc/adm_crawler.c Thu Dec  2 20:55:08 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,
@@ -792,22 +814,12 @@ svn_wc_crawl_revisions5(svn_wc_context_t
 
   if (!repos_root || !repos_relpath)
     {
-      err = svn_wc__db_scan_base_repos(&repos_relpath, &repos_root, NULL,
-                                      db, local_abspath,
-                                      scratch_pool, scratch_pool);
-
-      if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
-        svn_error_clear(err);
-      else
-        SVN_ERR(err);
-
       /* Ok, that leaves a local addition. Deleted and not existing nodes
          are already handled. */
-      if (!repos_root || !repos_relpath)
-        SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, &repos_relpath,
-                                         &repos_root, NULL, NULL, NULL, NULL,
-                                         NULL, db, local_abspath,
-                                         scratch_pool, scratch_pool));
+      SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, &repos_relpath,
+                                       &repos_root, NULL, NULL, NULL, NULL,
+                                       NULL, db, local_abspath,
+                                       scratch_pool, scratch_pool));
     }
 
   if (!SVN_IS_VALID_REVNUM(target_rev))
@@ -925,15 +937,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/gpg-agent-password-store/subversion/libsvn_wc/adm_files.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/libsvn_wc/adm_files.c?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/libsvn_wc/adm_files.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/libsvn_wc/adm_files.c Thu Dec  2 20:55:08 2010
@@ -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/gpg-agent-password-store/subversion/libsvn_wc/adm_ops.c
URL: http://svn.apache.org/viewvc/subversion/branches/gpg-agent-password-store/subversion/libsvn_wc/adm_ops.c?rev=1041580&r1=1041579&r2=1041580&view=diff
==============================================================================
--- subversion/branches/gpg-agent-password-store/subversion/libsvn_wc/adm_ops.c (original)
+++ subversion/branches/gpg-agent-password-store/subversion/libsvn_wc/adm_ops.c Thu Dec  2 20:55:08 2010
@@ -735,28 +735,127 @@ 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)
+    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 +863,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 +872,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 +884,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 +1072,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 +1084,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 +1114,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,193 +1257,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_not_present_node(
-                    db, local_abspath,
-                    repos_relpath, repos_root_url, repos_uuid,
-                    base_revision,
-                    base_kind,
-                    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.
  *
@@ -1393,6 +1330,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,
@@ -1404,10 +1342,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. */
@@ -1420,7 +1362,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);
 
@@ -1439,11 +1381,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)
@@ -1483,8 +1444,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
@@ -1492,17 +1458,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)
@@ -1514,7 +1481,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
@@ -1531,10 +1497,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)
@@ -1544,7 +1519,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;
@@ -1587,7 +1578,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));
@@ -1631,7 +1622,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,
@@ -1644,6 +1636,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;
 }
 
@@ -1666,7 +1672,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,