You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2013/01/06 03:33:39 UTC

svn commit: r1429457 [16/21] - in /subversion/branches/tree-read-api: ./ build/ build/ac-macros/ build/generator/templates/ build/win32/ contrib/server-side/svncutter/ doc/ subversion/bindings/cxxhl/include/ subversion/bindings/cxxhl/include/svncxxhl/ ...

Modified: subversion/branches/tree-read-api/subversion/svnrdump/dump_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnrdump/dump_editor.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnrdump/dump_editor.c (original)
+++ subversion/branches/tree-read-api/subversion/svnrdump/dump_editor.c Sun Jan  6 02:33:34 2013
@@ -50,7 +50,8 @@ struct dir_baton
   struct dump_edit_baton *eb;
   struct dir_baton *parent_dir_baton;
 
-  apr_pool_t *pool; /* Directory pool */
+  /* Pool for per-directory allocations */
+  apr_pool_t *pool;
 
   /* is this directory a new addition to this revision? */
   svn_boolean_t added;
@@ -65,11 +66,21 @@ struct dir_baton
   const char *copyfrom_path; /* a relpath */
   svn_revnum_t copyfrom_rev;
 
+  /* Properties which were modified during change_dir_prop. */
+  apr_hash_t *props;
+
+  /* Properties which were deleted during change_dir_prop. */
+  apr_hash_t *deleted_props;
+
   /* Hash of paths that need to be deleted, though some -might- be
      replaced.  Maps const char * paths to this dir_baton. Note that
      they're full paths, because that's what the editor driver gives
      us, although they're all really within this directory. */
   apr_hash_t *deleted_entries;
+
+  /* Flags to trigger dumping props and record termination newlines. */
+  svn_boolean_t dump_props;
+  svn_boolean_t dump_newlines;
 };
 
 /* A file baton used by all file-related callback functions in the dump
@@ -79,11 +90,32 @@ struct file_baton
   struct dump_edit_baton *eb;
   struct dir_baton *parent_dir_baton;
 
+  /* Pool for per-file allocations */
+  apr_pool_t *pool;
+
   /* the path to this file */
   const char *repos_relpath; /* a relpath */
 
+  /* Properties which were modified during change_file_prop. */
+  apr_hash_t *props;
+
+  /* Properties which were deleted during change_file_prop. */
+  apr_hash_t *deleted_props;
+
   /* The checksum of the file the delta is being applied to */
   const char *base_checksum;
+
+  /* Copy state and source information (if any). */
+  svn_boolean_t is_copy;
+  const char *copyfrom_path;
+  svn_revnum_t copyfrom_rev;
+
+  /* The action associate with this node. */
+  enum svn_node_action action;
+  
+  /* Flags to trigger dumping props and text. */
+  svn_boolean_t dump_text;
+  svn_boolean_t dump_props;
 };
 
 /* A handler baton to be used in window_handler().  */
@@ -101,34 +133,31 @@ struct dump_edit_baton {
   /* A backdoor ra session to fetch additional information during the edit. */
   svn_ra_session_t *ra_session;
 
+  /* The repository relpath of the anchor of the editor when driven
+     via the RA update mechanism; NULL otherwise. (When the editor is
+     driven via the RA "replay" mechanism instead, the editor is
+     always anchored at the repository, we don't need to prepend an
+     anchor path to the dumped node paths, and open_root() doesn't
+     need to manufacture directory additions.)  */
+  const char *update_anchor_relpath;
+
   /* Pool for per-revision allocations */
   apr_pool_t *pool;
 
-  /* Properties which were modified during change_file_prop
-   * or change_dir_prop. */
-  apr_hash_t *props;
-
-  /* Properties which were deleted during change_file_prop
-   * or change_dir_prop. */
-  apr_hash_t *deleted_props;
-
-  /* Temporary buffer to write property hashes to in human-readable
-   * form. ### Is this really needed? */
-  svn_stringbuf_t *propstring;
-
   /* Temporary file used for textdelta application along with its
      absolute path; these two variables should be allocated in the
      per-edit-session pool */
   const char *delta_abspath;
   apr_file_t *delta_file;
 
-  /* Flags to trigger dumping props and text */
-  svn_boolean_t dump_text;
-  svn_boolean_t dump_props;
-  svn_boolean_t dump_newlines;
-
   /* The revision we're currently dumping. */
   svn_revnum_t current_revision;
+
+  /* The kind (file or directory) and baton of the item whose block of
+     dump stream data has not been fully completed; NULL if there's no
+     such item. */
+  svn_node_kind_t pending_kind;
+  void *pending_baton;
 };
 
 /* Make a directory baton to represent the directory at PATH (relative
@@ -139,21 +168,20 @@ struct dump_edit_baton {
  * information is valid, the directory will be compared against its
  * copy source.
  *
- * PARENT_DIR_BATON is the directory baton of this directory's parent,
- * or NULL if this is the top-level directory of the edit.  ADDED
- * indicates if this directory is newly added in this revision.
- * Perform all allocations in POOL.  */
+ * PB is the directory baton of this directory's parent, or NULL if
+ * this is the top-level directory of the edit.  ADDED indicates if
+ * this directory is newly added in this revision.  Perform all
+ * allocations in POOL.  */
 static struct dir_baton *
 make_dir_baton(const char *path,
                const char *copyfrom_path,
                svn_revnum_t copyfrom_rev,
                void *edit_baton,
-               void *parent_dir_baton,
+               struct dir_baton *pb,
                svn_boolean_t added,
                apr_pool_t *pool)
 {
   struct dump_edit_baton *eb = edit_baton;
-  struct dir_baton *pb = parent_dir_baton;
   struct dir_baton *new_db = apr_pcalloc(pool, sizeof(*new_db));
   const char *repos_relpath;
 
@@ -178,11 +206,38 @@ make_dir_baton(const char *path,
   new_db->copyfrom_rev = copyfrom_rev;
   new_db->added = added;
   new_db->written_out = FALSE;
+  new_db->props = apr_hash_make(pool);
+  new_db->deleted_props = apr_hash_make(pool);
   new_db->deleted_entries = apr_hash_make(pool);
 
   return new_db;
 }
 
+/* Make a file baton to represent the directory at PATH (relative to
+ * PB->eb).  PB is the directory baton of this directory's parent, or
+ * NULL if this is the top-level directory of the edit.  Perform all
+ * allocations in POOL.  */
+static struct file_baton *
+make_file_baton(const char *path,
+                struct dir_baton *pb,
+                apr_pool_t *pool)
+{
+  struct file_baton *new_fb = apr_pcalloc(pool, sizeof(*new_fb));
+
+  new_fb->eb = pb->eb;
+  new_fb->parent_dir_baton = pb;
+  new_fb->pool = pool;
+  new_fb->repos_relpath = svn_relpath_canonicalize(path, pool);
+  new_fb->props = apr_hash_make(pool);
+  new_fb->deleted_props = apr_hash_make(pool);
+  new_fb->is_copy = FALSE;
+  new_fb->copyfrom_path = NULL;
+  new_fb->copyfrom_rev = SVN_INVALID_REVNUM;
+  new_fb->action = svn_node_action_change;
+
+  return new_fb;
+}
+
 /* Return in *HEADER and *CONTENT the headers and content for PROPS. */
 static svn_error_t *
 get_props_content(svn_stringbuf_t **header,
@@ -219,9 +274,13 @@ get_props_content(svn_stringbuf_t **head
   return SVN_NO_ERROR;
 }
 
-/* Extract and dump properties stored in edit baton EB, using POOL for
- * any temporary allocations. If TRIGGER_VAR is not NULL, it is set to FALSE.
- * Unless DUMP_DATA_TOO is set, only property headers are dumped.
+/* Extract and dump properties stored in PROPS and property deletions
+ * stored in DELETED_PROPS. If TRIGGER_VAR is not NULL, it is set to
+ * FALSE.
+ *
+ * If PROPSTRING is non-NULL, set *PROPSTRING to a string containing
+ * the content block of the property changes; otherwise, dump that to
+ * the stream, too.
  */
 static svn_error_t *
 do_dump_props(svn_stringbuf_t **propstring,
@@ -229,7 +288,6 @@ do_dump_props(svn_stringbuf_t **propstri
               apr_hash_t *props,
               apr_hash_t *deleted_props,
               svn_boolean_t *trigger_var,
-              svn_boolean_t dump_data_too,
               apr_pool_t *result_pool,
               apr_pool_t *scratch_pool)
 {
@@ -242,14 +300,14 @@ do_dump_props(svn_stringbuf_t **propstri
 
   SVN_ERR(get_props_content(&header, &content, props, deleted_props,
                             result_pool, scratch_pool));
-
-  /* This is a wacky side-effect of this function. */
-  *propstring = content;
-
   len = header->len;
   SVN_ERR(svn_stream_write(stream, header->data, &len));
 
-  if (dump_data_too)
+  if (propstring)
+    {
+      *propstring = content;
+    }
+  else
     {
       /* Content-length: 14 */
       SVN_ERR(svn_stream_printf(stream, scratch_pool,
@@ -301,25 +359,35 @@ do_dump_newlines(struct dump_edit_baton 
 static svn_error_t *
 dump_node(struct dump_edit_baton *eb,
           const char *repos_relpath,
-          svn_node_kind_t kind,
+          struct dir_baton *db,
+          struct file_baton *fb,
           enum svn_node_action action,
           svn_boolean_t is_copy,
           const char *copyfrom_path,
           svn_revnum_t copyfrom_rev,
           apr_pool_t *pool)
 {
+  const char *node_relpath = repos_relpath;
+
   assert(svn_relpath_is_canonical(repos_relpath));
   assert(!copyfrom_path || svn_relpath_is_canonical(copyfrom_path));
+  assert(! (db && fb));
+
+  /* Add the edit root relpath prefix if necessary. */
+  if (eb->update_anchor_relpath)
+    node_relpath = svn_relpath_join(eb->update_anchor_relpath,
+                                    node_relpath, pool);
 
-  /* Node-path: commons/STATUS */
+  /* Node-path: ... */
   SVN_ERR(svn_stream_printf(eb->stream, pool,
-                            SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n", repos_relpath));
+                            SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
+                            node_relpath));
 
-  /* Node-kind: file */
-  if (kind == svn_node_file)
+  /* Node-kind: "file" | "dir" */
+  if (fb)
     SVN_ERR(svn_stream_printf(eb->stream, pool,
                               SVN_REPOS_DUMPFILE_NODE_KIND ": file\n"));
-  else if (kind == svn_node_dir)
+  else if (db)
     SVN_ERR(svn_stream_printf(eb->stream, pool,
                               SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
 
@@ -330,13 +398,24 @@ dump_node(struct dump_edit_baton *eb,
     case svn_node_action_change:
       /* We are here after a change_file_prop or change_dir_prop. They
          set up whatever dump_props they needed to- nothing to
-         do here but print node action information */
+         do here but print node action information.
+
+         Node-action: change.  */
       SVN_ERR(svn_stream_puts(eb->stream,
                               SVN_REPOS_DUMPFILE_NODE_ACTION ": change\n"));
       break;
 
     case svn_node_action_replace:
-      if (!is_copy)
+      if (is_copy)
+        {
+          /* Delete the original, and then re-add the replacement as a
+             copy using recursive calls into this function. */
+          SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_delete,
+                            FALSE, NULL, SVN_INVALID_REVNUM, pool));
+          SVN_ERR(dump_node(eb, repos_relpath, db, fb, svn_node_action_add,
+                            is_copy, copyfrom_path, copyfrom_rev, pool));
+        }
+      else
         {
           /* Node-action: replace */
           SVN_ERR(svn_stream_puts(eb->stream,
@@ -345,25 +424,15 @@ dump_node(struct dump_edit_baton *eb,
 
           /* Wait for a change_*_prop to be called before dumping
              anything */
-          eb->dump_props = TRUE;
-          break;
+          if (fb)
+            fb->dump_props = TRUE;
+          else if (db)
+            db->dump_props = TRUE;
         }
-      /* More complex case: is_copy is true, and copyfrom_path/
-         copyfrom_rev are present: delete the original, and then re-add
-         it */
-
-      SVN_ERR(svn_stream_puts(eb->stream,
-                              SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n\n"));
-
-      /* Recurse: Print an additional add-with-history record. */
-      SVN_ERR(dump_node(eb, repos_relpath, kind, svn_node_action_add,
-                        is_copy, copyfrom_path, copyfrom_rev, pool));
-
-      /* We can leave this routine quietly now, don't need to dump any
-         content; that was already done in the second record. */
       break;
 
     case svn_node_action_delete:
+      /* Node-action: delete */
       SVN_ERR(svn_stream_puts(eb->stream,
                               SVN_REPOS_DUMPFILE_NODE_ACTION ": delete\n"));
 
@@ -371,44 +440,57 @@ dump_node(struct dump_edit_baton *eb,
          print a couple of newlines because we're not dumping props or
          text. */
       SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
+
       break;
 
     case svn_node_action_add:
+      /* Node-action: add */
       SVN_ERR(svn_stream_puts(eb->stream,
                               SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
 
-      if (!is_copy)
+      if (is_copy)
         {
-          /* eb->dump_props for files is handled in close_file
-             which is called immediately.  However, directories are not
-             closed until all the work inside them has been done;
-             eb->dump_props for directories is handled in all the
-             functions that can possibly be called after add_directory:
-             add_directory, open_directory, delete_entry, close_directory,
-             add_file, open_file. change_dir_prop is a special case. */
-
-          /* Wait for a change_*_prop to be called before dumping
-             anything */
-          eb->dump_props = TRUE;
-          break;
+          /* Node-copyfrom-rev / Node-copyfrom-path */
+          SVN_ERR(svn_stream_printf(eb->stream, pool,
+                                    SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
+                                    ": %ld\n"
+                                    SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
+                                    ": %s\n",
+                                    copyfrom_rev, copyfrom_path));
+
+          /* Ugly hack: If a directory was copied from a previous
+             revision, nothing like close_file() will be called to write two
+             blank lines. If change_dir_prop() is called, props are dumped
+             (along with the necessary PROPS-END\n\n and we're good. So
+             set DUMP_NEWLINES here to print the newlines unless
+             change_dir_prop() is called next otherwise the `svnadmin load`
+             parser will fail.  */
+          if (db)
+            db->dump_newlines = TRUE;
         }
+      else
+        {
+          /* fb->dump_props (for files) is handled in close_file()
+             which is called immediately.
 
-      SVN_ERR(svn_stream_printf(eb->stream, pool,
-                                SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV
-                                ": %ld\n"
-                                SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH
-                                ": %s\n",
-                                copyfrom_rev, copyfrom_path));
-
-      /* Ugly hack: If a directory was copied from a previous
-         revision, nothing like close_file() will be called to write two
-         blank lines. If change_dir_prop() is called, props are dumped
-         (along with the necessary PROPS-END\n\n and we're good. So
-         set DUMP_NEWLINES here to print the newlines unless
-         change_dir_prop() is called next otherwise the `svnadmin load`
-         parser will fail.  */
-      if (kind == svn_node_dir)
-        eb->dump_newlines = TRUE;
+             However, directories are not closed until all the work
+             inside them has been done; db->dump_props (for directories)
+             is handled (via dump_pending()) in all the functions that
+             can possibly be called after add_directory():
+
+               - add_directory()
+               - open_directory()
+               - delete_entry()
+               - close_directory()
+               - add_file()
+               - open_file()
+
+             change_dir_prop() is a special case. */
+          if (fb)
+            fb->dump_props = TRUE;
+          else if (db)
+            db->dump_props = TRUE;
+        }
 
       break;
     }
@@ -416,23 +498,42 @@ dump_node(struct dump_edit_baton *eb,
 }
 
 static svn_error_t *
-open_root(void *edit_baton,
-          svn_revnum_t base_revision,
-          apr_pool_t *pool,
-          void **root_baton)
+dump_mkdir(struct dump_edit_baton *eb,
+           const char *repos_relpath,
+           apr_pool_t *pool)
 {
-  struct dump_edit_baton *eb = edit_baton;
+  svn_stringbuf_t *prop_header, *prop_content;
+  apr_size_t len;
+  const char *buf;
 
-  /* Clear the per-revision pool after each revision */
-  svn_pool_clear(eb->pool);
+  /* Node-path: ... */
+  SVN_ERR(svn_stream_printf(eb->stream, pool,
+                            SVN_REPOS_DUMPFILE_NODE_PATH ": %s\n",
+                            repos_relpath));
 
-  eb->props = apr_hash_make(eb->pool);
-  eb->deleted_props = apr_hash_make(eb->pool);
-  eb->propstring = svn_stringbuf_create_empty(eb->pool);
+  /* Node-kind: dir */
+  SVN_ERR(svn_stream_printf(eb->stream, pool,
+                            SVN_REPOS_DUMPFILE_NODE_KIND ": dir\n"));
 
-  *root_baton = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
-                               edit_baton, NULL, FALSE, eb->pool);
-  LDR_DBG(("open_root %p\n", *root_baton));
+  /* Node-action: add */
+  SVN_ERR(svn_stream_puts(eb->stream,
+                          SVN_REPOS_DUMPFILE_NODE_ACTION ": add\n"));
+
+  /* Dump the (empty) property block. */
+  SVN_ERR(get_props_content(&prop_header, &prop_content,
+                            apr_hash_make(pool), apr_hash_make(pool),
+                            pool, pool));
+  len = prop_header->len;
+  SVN_ERR(svn_stream_write(eb->stream, prop_header->data, &len));
+  len = prop_content->len;
+  buf = apr_psprintf(pool, SVN_REPOS_DUMPFILE_CONTENT_LENGTH
+                     ": %" APR_SIZE_T_FMT "\n", len);
+  SVN_ERR(svn_stream_puts(eb->stream, buf));
+  SVN_ERR(svn_stream_puts(eb->stream, "\n"));
+  SVN_ERR(svn_stream_write(eb->stream, prop_content->data, &len));
+
+  /* Newlines to tie it all off. */
+  SVN_ERR(svn_stream_puts(eb->stream, "\n\n"));
 
   return SVN_NO_ERROR;
 }
@@ -440,18 +541,113 @@ open_root(void *edit_baton,
 /* Dump pending items from the specified node, to allow starting the dump
    of a child node */
 static svn_error_t *
-dump_pending(struct dir_baton *pb,
+dump_pending(struct dump_edit_baton *eb,
              apr_pool_t *scratch_pool)
 {
-  /* Some pending properties to dump? */
-  SVN_ERR(do_dump_props(&pb->eb->propstring, pb->eb->stream,
-                        pb->eb->props, pb->eb->deleted_props,
-                        &(pb->eb->dump_props), TRUE,
-                        pb->pool, scratch_pool));
+  if (! eb->pending_baton)
+    return SVN_NO_ERROR;
 
-  /* Some pending newlines to dump? */
-  SVN_ERR(do_dump_newlines(pb->eb, &(pb->eb->dump_newlines), scratch_pool));
+  if (eb->pending_kind == svn_node_dir)
+    {
+      struct dir_baton *db = eb->pending_baton;
 
+      /* Some pending properties to dump? */
+      SVN_ERR(do_dump_props(NULL, eb->stream, db->props, db->deleted_props,
+                            &(db->dump_props), db->pool, scratch_pool));
+      
+      /* Some pending newlines to dump? */
+      SVN_ERR(do_dump_newlines(eb, &(db->dump_newlines), scratch_pool));
+    }
+  else if (eb->pending_kind == svn_node_file)
+    {
+      struct file_baton *fb = eb->pending_baton;
+
+      /* Some pending properties to dump? */
+      SVN_ERR(do_dump_props(NULL, eb->stream, fb->props, fb->deleted_props,
+                            &(fb->dump_props), fb->pool, scratch_pool));
+    }
+  else
+    abort();
+
+  /* Anything that was pending is pending no longer. */
+  eb->pending_baton = NULL;
+  eb->pending_kind = svn_node_none;
+
+  return SVN_NO_ERROR;
+}
+
+
+
+/*** Editor Function Implementations ***/
+
+static svn_error_t *
+open_root(void *edit_baton,
+          svn_revnum_t base_revision,
+          apr_pool_t *pool,
+          void **root_baton)
+{
+  struct dump_edit_baton *eb = edit_baton;
+  struct dir_baton *new_db = NULL;
+  
+  /* Clear the per-revision pool after each revision */
+  svn_pool_clear(eb->pool);
+
+  LDR_DBG(("open_root %p\n", *root_baton));
+
+  if (eb->update_anchor_relpath)
+    {
+      int i;
+      const char *parent_path = eb->update_anchor_relpath;
+      apr_array_header_t *dirs_to_add =
+        apr_array_make(pool, 4, sizeof(const char *));
+      apr_pool_t *iterpool = svn_pool_create(pool);
+
+      while (! svn_path_is_empty(parent_path))
+        {
+          APR_ARRAY_PUSH(dirs_to_add, const char *) = parent_path;
+          parent_path = svn_relpath_dirname(parent_path, pool);
+        }
+
+      for (i = dirs_to_add->nelts; i; --i)
+        {
+          const char *dir_to_add =
+            APR_ARRAY_IDX(dirs_to_add, i - 1, const char *);
+
+          svn_pool_clear(iterpool);
+
+          /* For parents of the source directory, we just manufacture
+             the adds ourselves. */
+          if (i > 1)
+            {
+              SVN_ERR(dump_mkdir(eb, dir_to_add, iterpool));
+            }
+          else
+            {
+              /* ... but for the source directory itself, we'll defer
+                 to letting the typical plumbing handle this task. */
+              new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
+                                      edit_baton, NULL, TRUE, pool);
+              SVN_ERR(dump_node(eb, new_db->repos_relpath, new_db,
+                                NULL, svn_node_action_add, FALSE,
+                                NULL, SVN_INVALID_REVNUM, pool));
+
+              /* Remember that we've started but not yet finished
+                 handling this directory. */
+              new_db->written_out = TRUE;
+              eb->pending_baton = new_db;
+              eb->pending_kind = svn_node_dir;
+            }
+        }
+      svn_pool_destroy(iterpool);
+    }
+
+  if (! new_db)
+    {
+      new_db = make_dir_baton(NULL, NULL, SVN_INVALID_REVNUM,
+                              edit_baton, NULL, FALSE, pool);
+    }
+
+  *root_baton = new_db;
   return SVN_NO_ERROR;
 }
 
@@ -465,10 +661,12 @@ delete_entry(const char *path,
 
   LDR_DBG(("delete_entry %s\n", path));
 
-  SVN_ERR(dump_pending(pb, pool));
+  SVN_ERR(dump_pending(pb->eb, pool));
 
-  /* Add this path to the deleted_entries of the parent directory
-     baton. */
+  /* We don't dump this deletion immediate.  Rather, we add this path
+     to the deleted_entries of the parent directory baton.  That way,
+     we can tell (later) an addition from a replacement.  All the real
+     deletions get handled in close_directory().  */
   apr_hash_set(pb->deleted_entries, apr_pstrdup(pb->eb->pool, path),
                APR_HASH_KEY_STRING, pb);
 
@@ -490,11 +688,11 @@ add_directory(const char *path,
 
   LDR_DBG(("add_directory %s\n", path));
 
+  SVN_ERR(dump_pending(pb->eb, pool));
+
   new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb,
                           pb, TRUE, pb->eb->pool);
 
-  SVN_ERR(dump_pending(pb, pool));
-
   /* This might be a replacement -- is the path already deleted? */
   val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
 
@@ -502,8 +700,7 @@ add_directory(const char *path,
   is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
 
   /* Dump the node */
-  SVN_ERR(dump_node(pb->eb, new_db->repos_relpath,
-                    svn_node_dir,
+  SVN_ERR(dump_node(pb->eb, new_db->repos_relpath, new_db, NULL,
                     val ? svn_node_action_replace : svn_node_action_add,
                     is_copy,
                     is_copy ? new_db->copyfrom_path : NULL,
@@ -514,7 +711,11 @@ add_directory(const char *path,
     /* Delete the path, it's now been dumped */
     apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
 
+  /* Remember that we've started, but not yet finished handling this
+     directory. */
   new_db->written_out = TRUE;
+  pb->eb->pending_baton = new_db;
+  pb->eb->pending_kind = svn_node_dir;
 
   *child_baton = new_db;
   return SVN_NO_ERROR;
@@ -534,7 +735,7 @@ open_directory(const char *path,
 
   LDR_DBG(("open_directory %s\n", path));
 
-  SVN_ERR(dump_pending(pb, pool));
+  SVN_ERR(dump_pending(pb->eb, pool));
 
   /* If the parent directory has explicit comparison path and rev,
      record the same for this one. */
@@ -548,6 +749,7 @@ open_directory(const char *path,
 
   new_db = make_dir_baton(path, copyfrom_path, copyfrom_rev, pb->eb, pb,
                           FALSE, pb->eb->pool);
+
   *child_baton = new_db;
   return SVN_NO_ERROR;
 }
@@ -558,10 +760,33 @@ close_directory(void *dir_baton,
 {
   struct dir_baton *db = dir_baton;
   apr_hash_index_t *hi;
+  svn_boolean_t this_pending;
 
   LDR_DBG(("close_directory %p\n", dir_baton));
 
-  SVN_ERR(dump_pending(db, pool));
+  /* Remember if this directory is the one currently pending. */
+  this_pending = (db->eb->pending_baton == db);
+
+  SVN_ERR(dump_pending(db->eb, pool));
+
+  /* If this directory was pending, then dump_pending() should have
+     taken care of all the props and such.  Of course, the only way
+     that would be the case is if this directory was added/replaced.
+
+     Otherwise, if stuff for this directory has already been written
+     out (at some point in the past, prior to our handling other
+     nodes), we might need to generate a second "change" record just
+     to carry the information we've since learned about the
+     directory. */ 
+  if ((! this_pending) && (db->dump_props))
+    {
+      SVN_ERR(dump_node(db->eb, db->repos_relpath, db, NULL,
+                        svn_node_action_change, FALSE,
+                        NULL, SVN_INVALID_REVNUM, pool));
+      db->eb->pending_baton = db;
+      db->eb->pending_kind = svn_node_dir;
+      SVN_ERR(dump_pending(db->eb, pool));
+    }
 
   /* Dump the deleted directory entries */
   for (hi = apr_hash_first(pool, db->deleted_entries); hi;
@@ -569,11 +794,13 @@ close_directory(void *dir_baton,
     {
       const char *path = svn__apr_hash_index_key(hi);
 
-      SVN_ERR(dump_node(db->eb, path, svn_node_unknown, svn_node_action_delete,
+      SVN_ERR(dump_node(db->eb, path, NULL, NULL, svn_node_action_delete,
                         FALSE, NULL, SVN_INVALID_REVNUM, pool));
     }
 
+  /* ### should be unnecessary */
   SVN_ERR(svn_hash__clear(db->deleted_entries, pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -586,42 +813,33 @@ add_file(const char *path,
          void **file_baton)
 {
   struct dir_baton *pb = parent_baton;
-  struct file_baton *fb = apr_pcalloc(pool, sizeof(*fb));
+  struct file_baton *fb;
   void *val;
-  svn_boolean_t is_copy;
-
-  fb->eb = pb->eb;
-  fb->parent_dir_baton = pb;
-  fb->repos_relpath = svn_relpath_canonicalize(path, pool);
 
   LDR_DBG(("add_file %s\n", path));
 
-  SVN_ERR(dump_pending(pb, pool));
+  SVN_ERR(dump_pending(pb->eb, pool));
 
+  /* Make the file baton. */
+  fb = make_file_baton(path, pb, pool);
+  
   /* This might be a replacement -- is the path already deleted? */
   val = apr_hash_get(pb->deleted_entries, path, APR_HASH_KEY_STRING);
 
   /* Detect add-with-history. */
-  is_copy = ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev);
-
-  /* Dump the node. */
-  SVN_ERR(dump_node(pb->eb, fb->repos_relpath,
-                    svn_node_file,
-                    val ? svn_node_action_replace : svn_node_action_add,
-                    is_copy,
-                    is_copy ? svn_relpath_canonicalize(copyfrom_path, pool)
-                            : NULL,
-                    is_copy ? copyfrom_rev : SVN_INVALID_REVNUM,
-                    pool));
+  if (ARE_VALID_COPY_ARGS(copyfrom_path, copyfrom_rev))
+    {    
+      fb->copyfrom_path = svn_relpath_canonicalize(copyfrom_path, fb->pool);
+      fb->copyfrom_rev = copyfrom_rev;
+      fb->is_copy = TRUE;
+    }
+  fb->action = val ? svn_node_action_replace : svn_node_action_add;
 
+  /* Delete the path, it's now been dumped. */
   if (val)
-    /* delete the path, it's now been dumped. */
     apr_hash_set(pb->deleted_entries, path, APR_HASH_KEY_STRING, NULL);
 
-  /* Build a nice file baton to pass to change_file_prop and
-     apply_textdelta */
   *file_baton = fb;
-
   return SVN_NO_ERROR;
 }
 
@@ -633,36 +851,26 @@ open_file(const char *path,
           void **file_baton)
 {
   struct dir_baton *pb = parent_baton;
-  struct file_baton *fb = apr_pcalloc(pool, sizeof(*fb));
-  const char *copyfrom_path = NULL;
-  svn_revnum_t copyfrom_rev = SVN_INVALID_REVNUM;
-
-  fb->eb = pb->eb;
-  fb->parent_dir_baton = pb;
-  fb->repos_relpath = svn_relpath_canonicalize(path, pool);
+  struct file_baton *fb;
 
   LDR_DBG(("open_file %s\n", path));
 
-  SVN_ERR(dump_pending(pb, pool));
+  SVN_ERR(dump_pending(pb->eb, pool));
+
+  /* Make the file baton. */
+  fb = make_file_baton(path, pb, pool);
 
   /* If the parent directory has explicit copyfrom path and rev,
      record the same for this one. */
   if (ARE_VALID_COPY_ARGS(pb->copyfrom_path, pb->copyfrom_rev))
     {
-      copyfrom_path = svn_relpath_join(pb->copyfrom_path,
-                                       svn_relpath_basename(path, NULL),
-                                       pb->eb->pool);
-      copyfrom_rev = pb->copyfrom_rev;
+      fb->copyfrom_path = svn_relpath_join(pb->copyfrom_path,
+                                           svn_relpath_basename(path, NULL),
+                                           pb->eb->pool);
+      fb->copyfrom_rev = pb->copyfrom_rev;
     }
 
-  SVN_ERR(dump_node(pb->eb, fb->repos_relpath, svn_node_file,
-                    svn_node_action_change, FALSE, copyfrom_path,
-                    copyfrom_rev, pool));
-
-  /* Build a nice file baton to pass to change_file_prop and
-     apply_textdelta */
   *file_baton = fb;
-
   return SVN_NO_ERROR;
 }
 
@@ -673,37 +881,30 @@ change_dir_prop(void *parent_baton,
                 apr_pool_t *pool)
 {
   struct dir_baton *db = parent_baton;
-
+  svn_boolean_t this_pending;
+  
   LDR_DBG(("change_dir_prop %p\n", parent_baton));
 
+  /* This directory is not pending, but something else is, so handle
+     the "something else".  */
+  this_pending = (db->eb->pending_baton == db);
+  if (! this_pending)
+    SVN_ERR(dump_pending(db->eb, pool));
+
   if (svn_property_kind2(name) != svn_prop_regular_kind)
     return SVN_NO_ERROR;
 
   if (value)
-    apr_hash_set(db->eb->props, apr_pstrdup(db->eb->pool, name),
-                 APR_HASH_KEY_STRING, svn_string_dup(value, db->eb->pool));
+    apr_hash_set(db->props, apr_pstrdup(db->pool, name),
+                 APR_HASH_KEY_STRING, svn_string_dup(value, db->pool));
   else
-    apr_hash_set(db->eb->deleted_props, apr_pstrdup(db->eb->pool, name),
+    apr_hash_set(db->deleted_props, apr_pstrdup(db->pool, name),
                  APR_HASH_KEY_STRING, "");
 
-  if (! db->written_out)
-    {
-      /* If db->written_out is set, it means that the node information
-         corresponding to this directory has already been written: don't
-         do anything; do_dump_props() will take care of dumping the
-         props. If it not, dump the node itself before dumping the
-         props. */
-
-      SVN_ERR(dump_node(db->eb, db->repos_relpath, svn_node_dir,
-                        svn_node_action_change, FALSE, db->copyfrom_path,
-                        db->copyfrom_rev, pool));
-      db->written_out = TRUE;
-    }
-
   /* Make sure we eventually output the props, and disable printing
      a couple of extra newlines */
-  db->eb->dump_newlines = FALSE;
-  db->eb->dump_props = TRUE;
+  db->dump_newlines = FALSE;
+  db->dump_props = TRUE;
 
   return SVN_NO_ERROR;
 }
@@ -715,7 +916,6 @@ change_file_prop(void *file_baton,
                  apr_pool_t *pool)
 {
   struct file_baton *fb = file_baton;
-  struct dump_edit_baton *eb = fb->eb;
 
   LDR_DBG(("change_file_prop %p\n", file_baton));
 
@@ -723,16 +923,16 @@ change_file_prop(void *file_baton,
     return SVN_NO_ERROR;
 
   if (value)
-    apr_hash_set(eb->props, apr_pstrdup(eb->pool, name),
-                 APR_HASH_KEY_STRING, svn_string_dup(value, eb->pool));
+    apr_hash_set(fb->props, apr_pstrdup(fb->pool, name),
+                 APR_HASH_KEY_STRING, svn_string_dup(value, fb->pool));
   else
-    apr_hash_set(eb->deleted_props, apr_pstrdup(eb->pool, name),
+    apr_hash_set(fb->deleted_props, apr_pstrdup(fb->pool, name),
                  APR_HASH_KEY_STRING, "");
 
   /* Dump the property headers and wait; close_file might need
      to write text headers too depending on whether
      apply_textdelta is called */
-  eb->dump_props = TRUE;
+  fb->dump_props = TRUE;
 
   return SVN_NO_ERROR;
 }
@@ -761,15 +961,14 @@ apply_textdelta(void *file_baton, const 
 {
   struct file_baton *fb = file_baton;
   struct dump_edit_baton *eb = fb->eb;
-
-  /* Custom handler_baton allocated in a separate pool */
   struct handler_baton *hb;
   svn_stream_t *delta_filestream;
 
-  hb = apr_pcalloc(eb->pool, sizeof(*hb));
-
   LDR_DBG(("apply_textdelta %p\n", file_baton));
 
+  /* This is custom handler_baton, allocated from a separate pool.  */
+  hb = apr_pcalloc(eb->pool, sizeof(*hb));
+
   /* Use a temporary file to measure the Text-content-length */
   delta_filestream = svn_stream_from_aprfile2(eb->delta_file, TRUE, pool);
 
@@ -778,7 +977,8 @@ apply_textdelta(void *file_baton, const 
                           delta_filestream, 0,
                           SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
 
-  eb->dump_text = TRUE;
+  /* Record that there's text to be dumped, and its base checksum. */
+  fb->dump_text = TRUE;
   fb->base_checksum = apr_pstrdup(eb->pool, base_checksum);
 
   /* The actual writing takes place when this function has
@@ -798,17 +998,25 @@ close_file(void *file_baton,
   struct file_baton *fb = file_baton;
   struct dump_edit_baton *eb = fb->eb;
   apr_finfo_t *info = apr_pcalloc(pool, sizeof(apr_finfo_t));
-
+  svn_stringbuf_t *propstring;
+  
   LDR_DBG(("close_file %p\n", file_baton));
 
-  /* Some pending properties to dump? Dump just the headers- dump the
-     props only after dumping the text headers too (if present) */
-  SVN_ERR(do_dump_props(&eb->propstring, eb->stream,
-                        eb->props, eb->deleted_props,
-                        &(eb->dump_props), FALSE, pool, pool));
+  SVN_ERR(dump_pending(eb, pool));
+
+  /* Dump the node. */
+  SVN_ERR(dump_node(eb, fb->repos_relpath, NULL, fb,
+                    fb->action, fb->is_copy, fb->copyfrom_path,
+                    fb->copyfrom_rev, pool));
+
+  /* Some pending properties to dump?  We'll dump just the headers for
+     now, then dump the actual propchange content only after dumping
+     the text headers too (if present). */
+  SVN_ERR(do_dump_props(&propstring, eb->stream, fb->props, fb->deleted_props,
+                        &(fb->dump_props), pool, pool));
 
   /* Dump the text headers */
-  if (eb->dump_text)
+  if (fb->dump_text)
     {
       apr_status_t err;
 
@@ -843,31 +1051,31 @@ close_file(void *file_baton,
 
   /* Content-length: 1549 */
   /* If both text and props are absent, skip this header */
-  if (eb->dump_props)
+  if (fb->dump_props)
     SVN_ERR(svn_stream_printf(eb->stream, pool,
                               SVN_REPOS_DUMPFILE_CONTENT_LENGTH
                               ": %ld\n\n",
-                              (unsigned long)info->size + eb->propstring->len));
-  else if (eb->dump_text)
+                              (unsigned long)info->size + propstring->len));
+  else if (fb->dump_text)
     SVN_ERR(svn_stream_printf(eb->stream, pool,
                               SVN_REPOS_DUMPFILE_CONTENT_LENGTH
                               ": %ld\n\n",
                               (unsigned long)info->size));
 
   /* Dump the props now */
-  if (eb->dump_props)
+  if (fb->dump_props)
     {
-      SVN_ERR(svn_stream_write(eb->stream, eb->propstring->data,
-                               &(eb->propstring->len)));
+      SVN_ERR(svn_stream_write(eb->stream, propstring->data,
+                               &(propstring->len)));
 
       /* Cleanup */
-      eb->dump_props = FALSE;
-      SVN_ERR(svn_hash__clear(eb->props, eb->pool));
-      SVN_ERR(svn_hash__clear(eb->deleted_props, eb->pool));
+      fb->dump_props = FALSE;
+      SVN_ERR(svn_hash__clear(fb->props, fb->pool));
+      SVN_ERR(svn_hash__clear(fb->deleted_props, fb->pool));
     }
 
   /* Dump the text */
-  if (eb->dump_text)
+  if (fb->dump_text)
     {
       /* Seek to the beginning of the delta file, map it to a stream,
          and copy the stream to eb->stream. Then close the stream and
@@ -884,7 +1092,6 @@ close_file(void *file_baton,
       /* Cleanup */
       SVN_ERR(svn_stream_close(delta_filestream));
       SVN_ERR(svn_io_file_trunc(eb->delta_file, 0, pool));
-      eb->dump_text = FALSE;
     }
 
   /* Write a couple of blank lines for matching output with `svnadmin
@@ -1014,6 +1221,7 @@ svn_rdump__get_dump_editor(const svn_del
                            svn_revnum_t revision,
                            svn_stream_t *stream,
                            svn_ra_session_t *ra_session,
+                           const char *update_anchor_relpath,
                            svn_cancel_func_t cancel_func,
                            void *cancel_baton,
                            apr_pool_t *pool)
@@ -1026,7 +1234,9 @@ svn_rdump__get_dump_editor(const svn_del
   eb = apr_pcalloc(pool, sizeof(struct dump_edit_baton));
   eb->stream = stream;
   eb->ra_session = ra_session;
+  eb->update_anchor_relpath = update_anchor_relpath;
   eb->current_revision = revision;
+  eb->pending_kind = svn_node_none;
 
   /* Create a special per-revision pool */
   eb->pool = svn_pool_create(pool);

Modified: subversion/branches/tree-read-api/subversion/svnrdump/load_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnrdump/load_editor.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnrdump/load_editor.c (original)
+++ subversion/branches/tree-read-api/subversion/svnrdump/load_editor.c Sun Jan  6 02:33:34 2013
@@ -501,7 +501,8 @@ get_shim_callbacks(struct revision_baton
 
 /* Acquire a lock (of sorts) on the repository associated with the
  * given RA SESSION. This lock is just a revprop change attempt in a
- * time-delay loop. This function is duplicated by svnsync in main.c.
+ * time-delay loop. This function is duplicated by svnsync in
+ * svnsync/svnsync.c
  *
  * ### TODO: Make this function more generic and
  * expose it through a header for use by other Subversion
@@ -855,6 +856,8 @@ set_revision_property(void *baton,
 {
   struct revision_baton *rb = baton;
 
+  SVN_ERR(svn_rdump__normalize_prop(name, &value, rb->pool));
+  
   SVN_ERR(svn_repos__validate_prop(name, value, rb->pool));
 
   if (rb->rev > 0)
@@ -934,6 +937,8 @@ set_node_property(void *baton,
         }
     }
 
+  SVN_ERR(svn_rdump__normalize_prop(name, &value, pool));
+
   SVN_ERR(svn_repos__validate_prop(name, value, pool));
 
   switch (nb->kind)

Modified: subversion/branches/tree-read-api/subversion/svnrdump/svnrdump.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnrdump/svnrdump.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnrdump/svnrdump.c (original)
+++ subversion/branches/tree-read-api/subversion/svnrdump/svnrdump.c Sun Jan  6 02:33:34 2013
@@ -23,6 +23,7 @@
  */
 
 #include <apr_signal.h>
+#include <apr_uri.h>
 
 #include "svn_pools.h"
 #include "svn_cmdline.h"
@@ -79,6 +80,7 @@ enum svn_svnrdump__longopt_t
     opt_auth_password,
     opt_auth_nocache,
     opt_non_interactive,
+    opt_force_interactive,
     opt_incremental,
     opt_trust_server_cert,
     opt_version
@@ -90,7 +92,8 @@ enum svn_svnrdump__longopt_t
                                    opt_auth_password, \
                                    opt_auth_nocache, \
                                    opt_trust_server_cert, \
-                                   opt_non_interactive
+                                   opt_non_interactive, \
+                                   opt_force_interactive
 
 static const svn_opt_subcommand_desc2_t svnrdump__cmd_table[] =
 {
@@ -126,7 +129,13 @@ static const apr_getopt_option_t svnrdum
     {"password",      opt_auth_password, 1,
                       N_("specify a password ARG")},
     {"non-interactive", opt_non_interactive, 0,
-                      N_("do no interactive prompting")},
+                      N_("do no interactive prompting (default is to prompt\n"
+                         "                             "
+                         "only if standard input is a terminal device)")},
+    {"force-interactive", opt_force_interactive, 0,
+                      N_("do interactive prompting even if standard input\n"
+                         "                             "
+                         "is not a terminal device")},
     {"no-auth-cache", opt_auth_nocache, 0,
                       N_("do not cache authentication tokens")},
     {"help",          'h', 0,
@@ -223,7 +232,7 @@ replay_revstart(svn_revnum_t revision,
 
   SVN_ERR(svn_rdump__get_dump_editor(editor, edit_baton, revision,
                                      rb->stdout_stream, rb->extra_ra_session,
-                                     check_cancel, NULL, pool));
+                                     NULL, check_cancel, NULL, pool));
 
   return SVN_NO_ERROR;
 }
@@ -298,7 +307,7 @@ replay_revstart_v2(svn_revnum_t revision
   SVN_ERR(svn_rdump__get_dump_editor_v2(editor, revision,
                                         rb->stdout_stream,
                                         rb->extra_ra_session,
-                                        check_cancel, NULL, pool, pool));
+                                        NULL, check_cancel, NULL, pool, pool));
 
   return SVN_NO_ERROR;
 }
@@ -328,6 +337,8 @@ replay_revend_v2(svn_revnum_t revision,
  * allocated from POOL.  Use CONFIG_DIR and pass USERNAME, PASSWORD,
  * CONFIG_DIR and NO_AUTH_CACHE to initialize the authorization baton.
  * CONFIG_OPTIONS (if not NULL) is a list of configuration overrides.
+ * REPOS_URL is used to fiddle with server-specific configuration
+ * options.
  */
 static svn_error_t *
 init_client_context(svn_client_ctx_t **ctx_p,
@@ -335,13 +346,14 @@ init_client_context(svn_client_ctx_t **c
                     const char *username,
                     const char *password,
                     const char *config_dir,
+                    const char *repos_url,
                     svn_boolean_t no_auth_cache,
                     svn_boolean_t trust_server_cert,
                     apr_array_header_t *config_options,
                     apr_pool_t *pool)
 {
   svn_client_ctx_t *ctx = NULL;
-  svn_config_t *cfg_config;
+  svn_config_t *cfg_config, *cfg_servers;
 
   SVN_ERR(svn_ra_initialize(pool));
 
@@ -357,6 +369,47 @@ init_client_context(svn_client_ctx_t **c
   cfg_config = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_CONFIG,
                             APR_HASH_KEY_STRING);
 
+  /* ### FIXME: This is a hack to work around the fact that our dump
+     ### editor simply can't handle the way ra_serf violates the
+     ### editor v1 drive ordering requirements.
+     ###
+     ### We'll override both the global value and server-specific one
+     ### for the 'http-bulk-updates' and 'http-max-connections'
+     ### options in order to get ra_serf to try a bulk-update if the
+     ### server will allow it, or at least try to limit all its
+     ### auxiliary GETs/PROPFINDs to happening (well-ordered) on a
+     ### single server connection.
+     ### 
+     ### See http://subversion.tigris.org/issues/show_bug.cgi?id=4116.
+  */
+  cfg_servers = apr_hash_get(ctx->config, SVN_CONFIG_CATEGORY_SERVERS,
+                             APR_HASH_KEY_STRING);
+  svn_config_set_bool(cfg_servers, SVN_CONFIG_SECTION_GLOBAL,
+                      SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, TRUE);
+  svn_config_set_int64(cfg_servers, SVN_CONFIG_SECTION_GLOBAL,
+                       SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, 2);
+  if (cfg_servers)
+    {
+      apr_status_t status;
+      apr_uri_t parsed_url;
+
+      status = apr_uri_parse(pool, repos_url, &parsed_url);
+      if (! status)
+        {
+          const char *server_group;
+
+          server_group = svn_config_find_group(cfg_servers, parsed_url.hostname,
+                                               SVN_CONFIG_SECTION_GROUPS, pool);
+          if (server_group)
+            {
+              svn_config_set_bool(cfg_servers, server_group,
+                                  SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, TRUE);
+              svn_config_set_int64(cfg_servers, server_group,
+                                   SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, 2);
+            }
+        }
+    }
+
   /* Set up our cancellation support. */
   ctx->cancel_func = check_cancel;
 
@@ -413,16 +466,68 @@ dump_revision_header(svn_ra_session_t *s
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+dump_initial_full_revision(svn_ra_session_t *session,
+                           svn_ra_session_t *extra_ra_session,
+                           svn_stream_t *stdout_stream,
+                           svn_revnum_t revision,
+                           svn_boolean_t quiet,
+                           apr_pool_t *pool)
+{
+  const svn_ra_reporter3_t *reporter;
+  void *report_baton;
+  const svn_delta_editor_t *dump_editor;
+  void *dump_baton;
+  const char *session_url, *source_relpath;
+
+  /* Determine whether we're dumping the repository root URL or some
+     child thereof.  If we're dumping a subtree of the repository
+     rather than the root, we have to jump through some hoops to make
+     our update-driven dump generation work the way a replay-driven
+     one would.
+
+     See http://subversion.tigris.org/issues/show_bug.cgi?id=4101
+  */
+  SVN_ERR(svn_ra_get_session_url(session, &session_url, pool));
+  SVN_ERR(svn_ra_get_path_relative_to_root(session, &source_relpath,
+                                           session_url, pool));
+
+  /* Start with a revision record header. */
+  SVN_ERR(dump_revision_header(session, stdout_stream, revision, pool));
+
+  /* Then, we'll drive the dump editor with what would look like a
+     full checkout of the repository as it looked in START_REVISION.
+     We do this by manufacturing a basic 'report' to the update
+     reporter, telling it that we have nothing to start with.  The
+     delta between nothing and everything-at-REV is, effectively, a
+     full dump of REV. */
+  SVN_ERR(svn_rdump__get_dump_editor(&dump_editor, &dump_baton, revision,
+                                     stdout_stream, extra_ra_session,
+                                     source_relpath, check_cancel, NULL, pool));
+  SVN_ERR(svn_ra_do_update2(session, &reporter, &report_baton, revision,
+                            "", svn_depth_infinity, FALSE,
+                            dump_editor, dump_baton, pool));
+  SVN_ERR(reporter->set_path(report_baton, "", revision,
+                             svn_depth_infinity, TRUE, NULL, pool));
+  SVN_ERR(reporter->finish_report(report_baton, pool));
+
+  /* All finished with START_REVISION! */
+  if (! quiet)
+    SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n",
+                                revision));
+
+  return SVN_NO_ERROR;
+}
+
 /* Replay revisions START_REVISION thru END_REVISION (inclusive) of
- * the repository located at URL, using callbacks which generate
- * Subversion repository dumpstreams describing the changes made in
- * those revisions.  If QUIET is set, don't generate progress
- * messages.
+ * the repository URL at which SESSION is rooted, using callbacks
+ * which generate Subversion repository dumpstreams describing the
+ * changes made in those revisions.  If QUIET is set, don't generate
+ * progress messages.
  */
 static svn_error_t *
 replay_revisions(svn_ra_session_t *session,
                  svn_ra_session_t *extra_ra_session,
-                 const char *url,
                  svn_revnum_t start_revision,
                  svn_revnum_t end_revision,
                  svn_boolean_t quiet,
@@ -465,7 +570,18 @@ replay_revisions(svn_ra_session_t *sessi
       incremental = TRUE;
     }
 
-  if (incremental)
+  /* If what remains to be dumped is not going to be dumped
+     incrementally, then dump the first revision in full. */
+  if (!incremental)
+    {
+      SVN_ERR(dump_initial_full_revision(session, extra_ra_session,
+                                         stdout_stream, start_revision,
+                                         quiet, pool));
+      start_revision++;
+    }
+
+  /* If there are still revisions left to be dumped, do so. */
+  if (start_revision <= end_revision)
     {
 #ifndef USE_EV2_IMPL
       SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,
@@ -478,54 +594,6 @@ replay_revisions(svn_ra_session_t *sessi
                                        NULL, NULL, NULL, NULL, pool));
 #endif
     }
-  else
-    {
-      const svn_ra_reporter3_t *reporter;
-      void *report_baton;
-      const svn_delta_editor_t *dump_editor;
-      void *dump_baton;
-
-      /* First, we need to dump the start_revision in full.  We'll
-         start with a revision record header. */
-      SVN_ERR(dump_revision_header(session, stdout_stream,
-                                   start_revision, pool));
-
-      /* Then, we'll drive the dump editor with what would look like a
-         full checkout of the repository as it looked in
-         START_REVISION.  We do this by manufacturing a basic 'report'
-         to the update reporter, telling it that we have nothing to
-         start with.  The delta between nothing and everything-at-REV
-         is, effectively, a full dump of REV. */
-      SVN_ERR(svn_rdump__get_dump_editor(&dump_editor, &dump_baton,
-                                         start_revision,
-                                         stdout_stream, extra_ra_session,
-                                         check_cancel, NULL, pool));
-      SVN_ERR(svn_ra_do_update2(session, &reporter, &report_baton,
-                                start_revision, "", svn_depth_infinity,
-                                FALSE, dump_editor, dump_baton, pool));
-      SVN_ERR(reporter->set_path(report_baton, "", start_revision,
-                                 svn_depth_infinity, TRUE, NULL, pool));
-      SVN_ERR(reporter->finish_report(report_baton, pool));
-
-      /* All finished with START_REVISION! */
-      if (! quiet)
-        SVN_ERR(svn_cmdline_fprintf(stderr, pool, "* Dumped revision %lu.\n",
-                                    start_revision));
-      start_revision++;
-
-      /* Now go pick up additional revisions in the range, if any. */
-      if (start_revision <= end_revision)
-#ifndef USE_EV2_IMPL
-        SVN_ERR(svn_ra_replay_range(session, start_revision, end_revision,
-                                    0, TRUE, replay_revstart, replay_revend,
-                                    replay_baton, pool));
-#else
-      SVN_ERR(svn_ra__replay_range_ev2(session, start_revision, end_revision,
-                                       0, TRUE, replay_revstart_v2,
-                                       replay_revend_v2, replay_baton,
-                                       NULL, NULL, NULL, NULL, pool));
-#endif
-    }
 
   SVN_ERR(svn_stream_close(stdout_stream));
   return SVN_NO_ERROR;
@@ -635,7 +703,6 @@ dump_cmd(apr_getopt_t *os,
   SVN_ERR(svn_ra_reparent(extra_ra_session, repos_root, pool));
 
   return replay_revisions(opt_baton->session, extra_ra_session,
-                          opt_baton->url,
                           opt_baton->start_revision.value.number,
                           opt_baton->end_revision.value.number,
                           opt_baton->quiet, opt_baton->incremental, pool);
@@ -780,6 +847,7 @@ main(int argc, const char **argv)
   svn_boolean_t no_auth_cache = FALSE;
   svn_boolean_t trust_server_cert = FALSE;
   svn_boolean_t non_interactive = FALSE;
+  svn_boolean_t force_interactive = FALSE;
   apr_array_header_t *config_options = NULL;
   apr_getopt_t *os;
   const char *first_arg;
@@ -898,6 +966,9 @@ main(int argc, const char **argv)
         case opt_non_interactive:
           non_interactive = TRUE;
           break;
+        case opt_force_interactive:
+          force_interactive = TRUE;
+          break;
         case opt_incremental:
           opt_baton->incremental = TRUE;
           break;
@@ -916,6 +987,19 @@ main(int argc, const char **argv)
         }
     }
 
+  /* The --non-interactive and --force-interactive options are mutually
+   * exclusive. */
+  if (non_interactive && force_interactive)
+    {
+      err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                             _("--non-interactive and --force-interactive "
+                               "are mutually exclusive"));
+      return svn_cmdline_handle_exit_error(err, pool, "svnrdump: ");
+    }
+  else
+    non_interactive = !svn_cmdline__be_interactive(non_interactive,
+                                                   force_interactive);
+
   if (opt_baton->help)
     {
       subcommand = svn_opt_get_canonical_subcommand2(svnrdump__cmd_table,
@@ -1050,6 +1134,7 @@ main(int argc, const char **argv)
                                    username,
                                    password,
                                    config_dir,
+                                   opt_baton->url,
                                    no_auth_cache,
                                    trust_server_cert,
                                    config_options,

Modified: subversion/branches/tree-read-api/subversion/svnrdump/svnrdump.h
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnrdump/svnrdump.h?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnrdump/svnrdump.h (original)
+++ subversion/branches/tree-read-api/subversion/svnrdump/svnrdump.h Sun Jan  6 02:33:34 2013
@@ -39,9 +39,15 @@ extern "C" {
 
 /**
  * Get a dump editor @a editor along with a @a edit_baton allocated in
- * @a pool.  The editor will write output to @a stream.  Use @a
- * cancel_func and @a cancel_baton to check for user cancellation of
- * the operation (for timely-but-safe termination).
+ * @a pool.  The editor will write output to @a stream.
+ *
+ * @a update_anchor_relpath is the repository relative path of the
+ * anchor of the update-style drive which will happen on @a *editor;
+ * if a replay-style drive will instead be used, it should be passed
+ * as @c NULL.
+ *
+ * Use @a cancel_func and @a cancel_baton to check for user
+ * cancellation of the operation (for timely-but-safe termination).
  */
 svn_error_t *
 svn_rdump__get_dump_editor(const svn_delta_editor_t **editor,
@@ -49,6 +55,7 @@ svn_rdump__get_dump_editor(const svn_del
                            svn_revnum_t revision,
                            svn_stream_t *stream,
                            svn_ra_session_t *ra_session,
+                           const char *update_anchor_relpath,
                            svn_cancel_func_t cancel_func,
                            void *cancel_baton,
                            apr_pool_t *pool);
@@ -59,6 +66,7 @@ svn_rdump__get_dump_editor_v2(svn_editor
                               svn_revnum_t revision,
                               svn_stream_t *stream,
                               svn_ra_session_t *ra_session,
+                              const char *edit_root_relpath,
                               svn_cancel_func_t cancel_func,
                               void *cancel_baton,
                               apr_pool_t *scratch_pool,
@@ -98,6 +106,20 @@ svn_rdump__normalize_props(apr_hash_t **
                            apr_hash_t *props,
                            apr_pool_t *result_pool);
 
+/* Normalize the line ending style of a single property that "needs
+ * translation" (according to svn_prop_needs_translation(),
+ * currently all svn:* props) so that they contain only LF (\n) line endings.
+ * "\r" characters found mid-line are replaced with "\n".
+ * "\r\n" sequences are replaced with "\n"
+ *
+ * NAME is used to check that VALUE should be normalized, and if this is the
+ * case, VALUE is then normalized, allocated from RESULT_POOL
+ */
+svn_error_t *
+svn_rdump__normalize_prop(const char *name,
+                          const svn_string_t **value,
+                          apr_pool_t *result_pool);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

Modified: subversion/branches/tree-read-api/subversion/svnrdump/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnrdump/util.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnrdump/util.c (original)
+++ subversion/branches/tree-read-api/subversion/svnrdump/util.c Sun Jan  6 02:33:34 2013
@@ -31,6 +31,25 @@
 
 
 svn_error_t *
+svn_rdump__normalize_prop(const char *name,
+                          const svn_string_t **value,
+                          apr_pool_t *result_pool)
+{
+  if (svn_prop_needs_translation(name))
+    {
+      const char *cstring;
+
+      SVN_ERR(svn_subst_translate_cstring2((*value)->data, &cstring,
+                                           "\n", TRUE,
+                                           NULL, FALSE,
+                                           result_pool));
+
+      *value = svn_string_create(cstring, result_pool);
+    }
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_rdump__normalize_props(apr_hash_t **normal_props,
                            apr_hash_t *props,
                            apr_pool_t *result_pool)
@@ -45,16 +64,8 @@ svn_rdump__normalize_props(apr_hash_t **
       const char *key = svn__apr_hash_index_key(hi);
       const svn_string_t *value = svn__apr_hash_index_val(hi);
 
-      if (svn_prop_needs_translation(key))
-        {
-          const char *cstring;
-
-          SVN_ERR(svn_subst_translate_cstring2(value->data, &cstring,
-                                               "\n", TRUE,
-                                               NULL, FALSE,
-                                               result_pool));
-          value = svn_string_create(cstring, result_pool);
-        }
+      SVN_ERR(svn_rdump__normalize_prop(key, &value,
+                                        result_pool));
 
       apr_hash_set(*normal_props, key, APR_HASH_KEY_STRING, value);
     }

Modified: subversion/branches/tree-read-api/subversion/svnserve/serve.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnserve/serve.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnserve/serve.c (original)
+++ subversion/branches/tree-read-api/subversion/svnserve/serve.c Sun Jan  6 02:33:34 2013
@@ -221,37 +221,26 @@ static svn_error_t *log_command(server_b
   return log_write(b->log_file, line, nbytes, pool);
 }
 
-svn_error_t *load_configs(svn_config_t **cfg,
-                          svn_config_t **pwdb,
-                          svn_authz_t **authzdb,
-                          enum username_case_type *username_case,
-                          const char *filename,
-                          svn_boolean_t must_exist,
-                          const char *base,
-                          server_baton_t *server,
-                          svn_ra_svn_conn_t *conn,
-                          apr_pool_t *pool)
+svn_error_t *load_pwdb_config(server_baton_t *server,
+                              svn_ra_svn_conn_t *conn,
+                              apr_pool_t *pool)
 {
-  const char *pwdb_path, *authzdb_path;
+  const char *pwdb_path;
   svn_error_t *err;
 
-  SVN_ERR(svn_config_read2(cfg, filename, must_exist, FALSE, pool));
-
-  svn_config_get(*cfg, &pwdb_path, SVN_CONFIG_SECTION_GENERAL,
+  svn_config_get(server->cfg, &pwdb_path, SVN_CONFIG_SECTION_GENERAL,
                  SVN_CONFIG_OPTION_PASSWORD_DB, NULL);
 
-  *pwdb = NULL;
+  server->pwdb = NULL;
   if (pwdb_path)
     {
       pwdb_path = svn_dirent_canonicalize(pwdb_path, pool);
-      pwdb_path = svn_dirent_join(base, pwdb_path, pool);
+      pwdb_path = svn_dirent_join(server->base, pwdb_path, pool);
 
-      err = svn_config_read2(pwdb, pwdb_path, TRUE, FALSE, pool);
+      err = svn_config_read2(&server->pwdb, pwdb_path, TRUE, FALSE, pool);
       if (err)
         {
-          if (server)
-            /* Called by listening server; log error no matter what it is. */
-            log_server_error(err, server, conn, pool);
+          log_server_error(err, server, conn, pool);
 
           /* Because it may be possible to read the pwdb file with some
              access methods and not others, ignore errors reading the pwdb
@@ -265,18 +254,11 @@ svn_error_t *load_configs(svn_config_t *
           if (err->apr_err != SVN_ERR_BAD_FILENAME
               && ! APR_STATUS_IS_EACCES(err->apr_err))
             {
-              if (server)
-                {
-                  /* Called by listening server: Now that we've logged
-                   * the error, clear it and return a nice, generic
-                   * error to the user
-                   * (http://subversion.tigris.org/issues/show_bug.cgi?id=2271). */
-                  svn_error_clear(err);
-                  return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL, NULL);
-                }
-              /* Called during startup; return the error, whereupon it
-               * will go to standard error for the admin to see. */
-              return err;
+                /* Now that we've logged the error, clear it and return a
+                 * nice, generic error to the user:
+                 * http://subversion.tigris.org/issues/show_bug.cgi?id=2271 */
+                svn_error_clear(err);
+                return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL, NULL);
             }
           else
             /* Ignore SVN_ERR_BAD_FILENAME and APR_EACCES and proceed. */
@@ -284,51 +266,63 @@ svn_error_t *load_configs(svn_config_t *
         }
     }
 
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *load_authz_config(server_baton_t *server,
+                               svn_ra_svn_conn_t *conn,
+                               const char *repos_root,
+                               apr_pool_t *pool)
+{
+  const char *authzdb_path;
+  svn_error_t *err;
+
   /* Read authz configuration. */
-  svn_config_get(*cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL,
+  svn_config_get(server->cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL,
                  SVN_CONFIG_OPTION_AUTHZ_DB, NULL);
   if (authzdb_path)
     {
       const char *case_force_val;
 
-      authzdb_path = svn_dirent_canonicalize(authzdb_path, pool);
-      authzdb_path = svn_dirent_join(base, authzdb_path, pool);
-      err = svn_repos_authz_read(authzdb, authzdb_path, TRUE, pool);
+      /* Canonicalize and add the base onto the authzdb_path (if needed).
+       * We don't canonicalize repos relative urls since they are
+       * canonicalized when they are resolved in svn_repos_authz_read2(). */
+      if (svn_path_is_url(authzdb_path))
+        {
+          authzdb_path = svn_uri_canonicalize(authzdb_path, pool);
+        }
+      else if (!svn_path_is_repos_relative_url(authzdb_path))
+        {
+          authzdb_path = svn_dirent_canonicalize(authzdb_path, pool);
+          authzdb_path = svn_dirent_join(server->base, authzdb_path, pool);
+        }
+      err = svn_repos_authz_read2(&server->authzdb, authzdb_path, TRUE,
+                                  repos_root, pool);
       if (err)
         {
-          if (server)
-            {
-              /* Called by listening server: Log the error, clear it,
-               * and return a nice, generic error to the user
-               * (http://subversion.tigris.org/issues/show_bug.cgi?id=2271). */
-              log_server_error(err, server, conn, pool);
-              svn_error_clear(err);
-              return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL, NULL);
-            }
-          else
-            /* Called during startup; return the error, whereupon it
-             * will go to standard error for the admin to see. */
-            return err;
+          log_server_error(err, server, conn, pool);
+          svn_error_clear(err);
+          return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL, NULL);
         }
 
       /* Are we going to be case-normalizing usernames when we consult
        * this authz file? */
-      svn_config_get(*cfg, &case_force_val, SVN_CONFIG_SECTION_GENERAL,
+      svn_config_get(server->cfg, &case_force_val, SVN_CONFIG_SECTION_GENERAL,
                      SVN_CONFIG_OPTION_FORCE_USERNAME_CASE, NULL);
       if (case_force_val)
         {
           if (strcmp(case_force_val, "upper") == 0)
-            *username_case = CASE_FORCE_UPPER;
+            server->username_case = CASE_FORCE_UPPER;
           else if (strcmp(case_force_val, "lower") == 0)
-            *username_case = CASE_FORCE_LOWER;
+            server->username_case = CASE_FORCE_LOWER;
           else
-            *username_case = CASE_ASIS;
+            server->username_case = CASE_ASIS;
         }
     }
   else
     {
-      *authzdb = NULL;
-      *username_case = CASE_ASIS;
+      server->authzdb = NULL;
+      server->username_case = CASE_ASIS;
     }
 
   return SVN_NO_ERROR;
@@ -1005,7 +999,7 @@ get_props(apr_hash_t **props,
   /* Get any inherited properties the user is authorized to. */
   if (iprops)
     {
-      SVN_ERR(svn_repos_fs_get_inherited_props(iprops, root, path,
+      SVN_ERR(svn_repos_fs_get_inherited_props(iprops, root, path, NULL,
                                                authz_check_access_cb_func(b),
                                                b, pool, pool));
     }
@@ -3138,14 +3132,26 @@ static svn_error_t *find_repos(const cha
     b->repos_name = b->authz_repos_name;
   b->repos_name = svn_path_uri_encode(b->repos_name, pool);
 
-  /* If the svnserve configuration files have not been loaded then
-     load them from the repository. */
+  /* If the svnserve configuration has not been loaded then load it from the
+   * repository. */
   if (NULL == b->cfg)
-    SVN_ERR(load_configs(&b->cfg, &b->pwdb, &b->authzdb, &b->username_case,
-                         svn_repos_svnserve_conf(b->repos, pool), FALSE,
-                         svn_repos_conf_dir(b->repos, pool),
-                         b, conn,
-                         pool));
+    {
+      b->base = svn_repos_conf_dir(b->repos, pool);
+
+      SVN_ERR(svn_config_read2(&b->cfg, svn_repos_svnserve_conf(b->repos, pool),
+                               FALSE, /* must_exist */
+                               FALSE, /* section_names_case_sensitive */
+                               pool));
+      SVN_ERR(load_pwdb_config(b, conn, pool));
+      SVN_ERR(load_authz_config(b, conn, repos_root, pool));
+    }
+  /* svnserve.conf has been loaded via the --config-file option so need
+   * to load pwdb and authz. */
+  else
+    {
+      SVN_ERR(load_pwdb_config(b, conn, pool));
+      SVN_ERR(load_authz_config(b, conn, repos_root, pool));
+    }
 
 #ifdef SVN_HAVE_SASL
   /* Should we use Cyrus SASL? */
@@ -3349,9 +3355,10 @@ svn_error_t *serve(svn_ra_svn_conn_t *co
   b.user = NULL;
   b.username_case = params->username_case;
   b.authz_user = NULL;
+  b.base = params->base;
   b.cfg = params->cfg;
-  b.pwdb = params->pwdb;
-  b.authzdb = params->authzdb;
+  b.pwdb = NULL;
+  b.authzdb = NULL;
   b.realm = NULL;
   b.log_file = params->log_file;
   b.pool = pool;

Modified: subversion/branches/tree-read-api/subversion/svnserve/server.h
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnserve/server.h?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnserve/server.h (original)
+++ subversion/branches/tree-read-api/subversion/svnserve/server.h Sun Jan  6 02:33:34 2013
@@ -42,6 +42,7 @@ typedef struct server_baton_t {
   svn_repos_t *repos;
   const char *repos_name;  /* URI-encoded name of repository (not for authz) */
   svn_fs_t *fs;            /* For convenience; same as svn_repos_fs(repos) */
+  const char *base;        /* Base directory for config files */
   svn_config_t *cfg;       /* Parsed repository svnserve.conf */
   svn_config_t *pwdb;      /* Parsed password database */
   svn_authz_t *authzdb;    /* Parsed authz rules */
@@ -87,24 +88,15 @@ typedef struct serve_params_t {
      which forces all connections to be read-only. */
   svn_boolean_t read_only;
 
+  /* The base directory for any relative configuration files. */
+  const char *base;
+
   /* A parsed repository svnserve configuration file, ala
      svnserve.conf.  If this is NULL, then no configuration file was
      specified on the command line.  If this is non-NULL, then
      per-repository svnserve.conf are not read. */
   svn_config_t *cfg;
 
-  /* A parsed repository password database.  If this is NULL, then
-     either no svnserve configuration file was specified on the
-     command line, or it was specified and it did not refer to a
-     password database. */
-  svn_config_t *pwdb;
-
-  /* A parsed repository authorization database.  If this is NULL,
-     then either no svnserve configuration file was specified on the
-     command line, or it was specified and it did not refer to a
-     authorization database. */
-  svn_authz_t *authzdb;
-
   /* A filehandle open for writing logs to; possibly NULL. */
   apr_file_t *log_file;
 
@@ -145,31 +137,24 @@ typedef struct serve_params_t {
 svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
                    apr_pool_t *pool);
 
-/* Load a svnserve configuration file located at FILENAME into CFG,
-   and if such as found, then:
+/* Load the password database for the listening server based on the
+   entries in the SERVER struct. 
 
-    - set *PWDB to any referenced password database,
-    - set *AUTHZDB to any referenced authorization database, and
-    - set *USERNAME_CASE to the enumerated value of the
-      'force-username-case' configuration value (or its default).
-
-   If MUST_EXIST is true and FILENAME does not exist, then return an
-   error.  BASE may be specified as the base path to any referenced
-   password and authorization files found in FILENAME.
-
-   If SERVER is not NULL, log the real errors with SERVER and CONN but
-   return generic errors to the client.  CONN must not be NULL if SERVER
-   is not NULL. */
-svn_error_t *load_configs(svn_config_t **cfg,
-                          svn_config_t **pwdb,
-                          svn_authz_t **authzdb,
-                          enum username_case_type *username_case,
-                          const char *filename,
-                          svn_boolean_t must_exist,
-                          const char *base,
-                          server_baton_t *server,
-                          svn_ra_svn_conn_t *conn,
-                          apr_pool_t *pool);
+   SERVER and CONN must not be NULL. The real errors will be logged with
+   SERVER and CONN but return generic errors to the client. */
+svn_error_t *load_pwdb_config(server_baton_t *server,
+                              svn_ra_svn_conn_t *conn,
+                              apr_pool_t *pool);
+
+/* Load the authz database for the listening server based on the
+   entries in the SERVER struct.
+
+   SERVER and CONN must not be NULL. The real errors will be logged with
+   SERVER and CONN but return generic errors to the client. */
+svn_error_t *load_authz_config(server_baton_t *server,
+                               svn_ra_svn_conn_t *conn,
+                               const char *repos_root,
+                               apr_pool_t *pool);
 
 /* Initialize the Cyrus SASL library. POOL is used for allocations. */
 svn_error_t *cyrus_init(apr_pool_t *pool);

Modified: subversion/branches/tree-read-api/subversion/svnserve/svnserve.8
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnserve/svnserve.8?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnserve/svnserve.8 (original)
+++ subversion/branches/tree-read-api/subversion/svnserve/svnserve.8 Sun Jan  6 02:33:34 2013
@@ -110,12 +110,12 @@ still backgrounds itself at startup time
 .TP 5
 \fB\-\-config\-file\fP=\fIfilename\fP
 When specified, \fBsvnserve\fP reads \fIfilename\fP once at program
-startup and caches the \fBsvnserve\fP configuration and any passwords
-and authorization configuration referenced from \fIfilename\fP.
-\fBsvnserve\fP will not read any per-repository
-\fBconf/svnserve.conf\fP files when this option is used.  See the
-\fBsvnserve.conf\fP(5) man page for details of the file format for
-this option.
+startup and caches the \fBsvnserve\fP configuration.  The password
+and authorization configurations referenced from \fIfilename\fP will
+be loaded on each connection.  \fBsvnserve\fP will not read any
+per-repository \fBconf/svnserve.conf\fP files when this option is
+used.  See the \fBsvnserve.conf\fP(5) man page for details of the
+file format for this option.
 .PP
 .TP 5
 \fB\-\-pid\-file\fP=\fIfilename\fP

Modified: subversion/branches/tree-read-api/subversion/svnserve/svnserve.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnserve/svnserve.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnserve/svnserve.c (original)
+++ subversion/branches/tree-read-api/subversion/svnserve/svnserve.c Sun Jan  6 02:33:34 2013
@@ -1,5 +1,5 @@
 /*
- * main.c :  Main control function for svnserve
+ * svnserve.c :  Main control function for svnserve
  *
  * ====================================================================
  *    Licensed to the Apache Software Foundation (ASF) under one
@@ -503,9 +503,8 @@ int main(int argc, const char *argv[])
   params.tunnel = FALSE;
   params.tunnel_user = NULL;
   params.read_only = FALSE;
+  params.base = NULL;
   params.cfg = NULL;
-  params.pwdb = NULL;
-  params.authzdb = NULL;
   params.compression_level = SVN_DELTA_COMPRESSION_LEVEL_DEFAULT;
   params.log_file = NULL;
   params.vhost = FALSE;
@@ -747,11 +746,14 @@ int main(int argc, const char *argv[])
   /* If a configuration file is specified, load it and any referenced
    * password and authorization files. */
   if (config_filename)
-    SVN_INT_ERR(load_configs(&params.cfg, &params.pwdb, &params.authzdb,
-                             &params.username_case, config_filename, TRUE,
-                             svn_dirent_dirname(config_filename, pool),
-                             NULL, NULL, /* server baton, conn */
-                             pool));
+    {
+      params.base = svn_dirent_dirname(config_filename, pool);
+
+      SVN_INT_ERR(svn_config_read2(&params.cfg, config_filename,
+                                   TRUE, /* must_exist */
+                                   FALSE, /* section_names_case_sensitive */
+                                   pool));
+    }
 
   if (log_filename)
     SVN_INT_ERR(svn_io_file_open(&params.log_file, log_filename,

Modified: subversion/branches/tree-read-api/subversion/svnserve/svnserve.conf.5
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnserve/svnserve.conf.5?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnserve/svnserve.conf.5 (original)
+++ subversion/branches/tree-read-api/subversion/svnserve/svnserve.conf.5 Sun Jan  6 02:33:34 2013
@@ -61,11 +61,13 @@ uses only one section "users"; each vari
 username, and each value is a password.
 .PP
 .TP 5
-\fBauthz-db\fP = \fIfilename\fP
+\fBauthz-db\fP = \fIpath\fP
 The authz-db option controls the location of the authorization
-rules for path-based access control.  \fIfilename\fP may be 
-relative to the repository conf directory.  There is no default value.
-If you don't specify an authz-db, no path-based access control is done.
+rules for path-based access control.  \fIpath\fP may be 
+relative to the repository conf directory.  \fIpath\fP may be a repository
+relative URL (^/) or absolute file:// URL to a text file in a Subversion
+repository.  There is no default value.  If you don't specify an authz-db,
+no path-based access control is done.
 .PP
 .TP 5
 \fBrealm\fP = \fIrealm\-name\fP

Modified: subversion/branches/tree-read-api/subversion/svnsync/svnsync.c
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/svnsync/svnsync.c?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/svnsync/svnsync.c (original)
+++ subversion/branches/tree-read-api/subversion/svnsync/svnsync.c Sun Jan  6 02:33:34 2013
@@ -53,6 +53,7 @@ static svn_opt_subcommand_t initialize_c
 
 enum svnsync__opt {
   svnsync_opt_non_interactive = SVN_OPT_FIRST_LONGOPT_ID,
+  svnsync_opt_force_interactive,
   svnsync_opt_no_auth_cache,
   svnsync_opt_auth_username,
   svnsync_opt_auth_password,
@@ -71,6 +72,7 @@ enum svnsync__opt {
 };
 
 #define SVNSYNC_OPTS_DEFAULT svnsync_opt_non_interactive, \
+                             svnsync_opt_force_interactive, \
                              svnsync_opt_no_auth_cache, \
                              svnsync_opt_auth_username, \
                              svnsync_opt_auth_password, \
@@ -173,7 +175,13 @@ static const apr_getopt_option_t svnsync
     {"allow-non-empty", svnsync_opt_allow_non_empty, 0,
                        N_("allow a non-empty destination repository") },
     {"non-interactive", svnsync_opt_non_interactive, 0,
-                       N_("do no interactive prompting") },
+                       N_("do no interactive prompting (default is to prompt\n"
+                          "                             "
+                          "only if standard input is a terminal device)")},
+    {"force-interactive", svnsync_opt_force_interactive, 0,
+                      N_("do interactive prompting even if standard input\n"
+                         "                             "
+                         "is not a terminal device")},
     {"no-auth-cache",  svnsync_opt_no_auth_cache, 0,
                        N_("do not cache authentication tokens") },
     {"username",       svnsync_opt_auth_username, 1,
@@ -322,7 +330,7 @@ lock_retry_func(void *baton,
 /* Acquire a lock (of sorts) on the repository associated with the
  * given RA SESSION. This lock is just a revprop change attempt in a
  * time-delay loop. This function is duplicated by svnrdump in
- * load_editor.c.
+ * svnrdump/load_editor.c
  */
 static svn_error_t *
 get_lock(const svn_string_t **lock_string_p,
@@ -1888,6 +1896,7 @@ main(int argc, const char *argv[])
   const char *password = NULL, *source_password = NULL, *sync_password = NULL;
   apr_array_header_t *config_options = NULL;
   const char *source_prop_encoding = NULL;
+  svn_boolean_t force_interactive;
 
   if (svn_cmdline_init("svnsync", stderr) != EXIT_SUCCESS)
     {
@@ -1950,6 +1959,10 @@ main(int argc, const char *argv[])
             opt_baton.non_interactive = TRUE;
             break;
 
+          case svnsync_opt_force_interactive:
+            force_interactive = TRUE;
+            break;
+
           case svnsync_opt_trust_server_cert:
             opt_baton.trust_server_cert = TRUE;
             break;
@@ -2079,6 +2092,20 @@ main(int argc, const char *argv[])
   if (opt_baton.help)
     subcommand = svn_opt_get_canonical_subcommand2(svnsync_cmd_table, "help");
 
+  /* The --non-interactive and --force-interactive options are mutually
+   * exclusive. */
+  if (opt_baton.non_interactive && force_interactive)
+    {
+      err = svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                             _("--non-interactive and --force-interactive "
+                               "are mutually exclusive"));
+      return svn_cmdline_handle_exit_error(err, pool, "svnsync: ");
+    }
+  else
+    opt_baton.non_interactive = !svn_cmdline__be_interactive(
+                                  opt_baton.non_interactive,
+                                  force_interactive);
+
   /* Disallow the mixing --username/password with their --source- and
      --sync- variants.  Treat "--username FOO" as "--source-username
      FOO --sync-username FOO"; ditto for "--password FOO". */

Modified: subversion/branches/tree-read-api/subversion/tests/README
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/tests/README?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/tests/README (original)
+++ subversion/branches/tree-read-api/subversion/tests/README Sun Jan  6 02:33:34 2013
@@ -20,7 +20,7 @@ sub-tests it can run.  It has a standard
 
 1.  If run with a numeric argument N, the program runs sub-test N.
 
-2.  If run with the argument `list', it will list the names of all sub-tests.
+2.  If run with the argument `--list', it will list the names of all sub-tests.
 
 3.  If run with no arguments, the program runs *all* sub-tests.
 

Modified: subversion/branches/tree-read-api/subversion/tests/cmdline/README
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/tests/cmdline/README?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/tests/cmdline/README (original)
+++ subversion/branches/tree-read-api/subversion/tests/cmdline/README Sun Jan  6 02:33:34 2013
@@ -141,16 +141,17 @@ Note [1]: It would be quite too much to 
           ----------------------------
 
 
-[If you want to test with serf instead of neon:
-
-  $ ./basic_tests.py --url=http://localhost --http-library=serf
-
-  or
-
-  $ make check BASE_URL=http://localhost HTTP_LIBRARY=serf
-]
-
+As a shorthand to all of the above, ./davautocheck.sh will generate
+an Apache configuration listening on a random port on localhost and
+run some tests.  Without arguments, or when invoking 'make davautocheck'
+on the top-level Makefile, it will run all tests.  With arguments,
+it will run just one suite or just one test:
+
+     $ ./davautocheck.sh
+     $ ./davautocheck.sh basic
+     $ ./davautocheck.sh basic 15
 
+It also respects some environment variables.
 
 Running over ra_svn
 -------------------
@@ -188,6 +189,9 @@ $ saslpasswd2 -c -u svntest jconstant
 
 As usual, both users should use the password 'rayjandom'.
 
+There are 'make svnserveautocheck' and ./svnserveautocheck.sh commands,
+analogous to davautocheck.sh documented above.
+
 
 Running tests in a RAM disk
 --------------------------

Modified: subversion/branches/tree-read-api/subversion/tests/cmdline/authz_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/tests/cmdline/authz_tests.py?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/tests/cmdline/authz_tests.py (original)
+++ subversion/branches/tree-read-api/subversion/tests/cmdline/authz_tests.py Sun Jan  6 02:33:34 2013
@@ -137,7 +137,7 @@ def broken_authz_file(sbox):
   exit_code, out, err = svntest.main.run_svn(1,
                                              "delete",
                                              sbox.repo_url + "/A",
-                                             "-m", "a log message");
+                                             "-m", "a log message")
   if out:
     raise svntest.verify.SVNUnexpectedStdout(out)
   if not err:

Modified: subversion/branches/tree-read-api/subversion/tests/cmdline/basic_tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/tree-read-api/subversion/tests/cmdline/basic_tests.py?rev=1429457&r1=1429456&r2=1429457&view=diff
==============================================================================
--- subversion/branches/tree-read-api/subversion/tests/cmdline/basic_tests.py (original)
+++ subversion/branches/tree-read-api/subversion/tests/cmdline/basic_tests.py Sun Jan  6 02:33:34 2013
@@ -1936,7 +1936,7 @@ def delete_keep_local(sbox):
   # Update working copy to check disk state still greek tree
   expected_disk = svntest.main.greek_state.copy()
   expected_output = svntest.wc.State(wc_dir, {})
-  expected_status.tweak(wc_rev = 2);
+  expected_status.tweak(wc_rev = 2)
 
   svntest.actions.run_and_verify_update(wc_dir,
                                         expected_output,
@@ -2249,12 +2249,14 @@ def automatic_conflict_resolution(sbox):
                                      # stdout, stderr
                                      None,
                                      ".*invalid 'accept' ARG",
-                                     'resolve', '--accept=edit')
+                                     'resolve', '--accept=edit',
+                                     '--force-interactive')
   svntest.actions.run_and_verify_svn(None,
                                      # stdout, stderr
                                      None,
                                      ".*invalid 'accept' ARG",
-                                     'resolve', '--accept=launch')
+                                     'resolve', '--accept=launch',
+                                     '--force-interactive')
   # Run 'svn resolved --accept=NOPE.  Using omega for the test.
   svntest.actions.run_and_verify_svn("Resolve command", None,
                                      ".*NOPE' is not a valid --accept value",
@@ -2493,8 +2495,17 @@ def basic_relative_url_with_peg_revision
                                 '^//A/@3', iota_url)
 
 
+def basic_auth_test_xfail_predicate():
+  """Predicate for XFail for basic_auth_test:
+  The test will fail if plaintext password storage is disabled,
+  and the RA method requires authentication."""
+  return (not svntest.main.is_os_windows()
+          and svntest.main.is_ra_type_dav()
+          and svntest.main.is_plaintext_password_storage_disabled())
+
 # Issue 2242, auth cache picking up password from wrong username entry
 @Issue(2242)
+@XFail(basic_auth_test_xfail_predicate)
 def basic_auth_test(sbox):
   "basic auth test"