You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2014/01/10 19:10:33 UTC

svn commit: r1557207 [2/2] - /subversion/trunk/subversion/libsvn_ra_serf/update.c

Modified: subversion/trunk/subversion/libsvn_ra_serf/update.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/update.c?rev=1557207&r1=1557206&r2=1557207&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/update.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/update.c Fri Jan 10 18:10:32 2014
@@ -49,14 +49,6 @@
 #include "ra_serf.h"
 #include "../libsvn_ra/ra_loader.h"
 
-/*
-#define USE_TRANSITION_PARSER
-*/
-
-#define SPILLBUF_BLOCKSIZE 4096
-#define SPILLBUF_MAXBUFFSIZE 131072
-
-#define PARSE_CHUNK_SIZE 8000 /* Copied from utils.c ### Needs tuning */
 
 
 /*
@@ -72,34 +64,159 @@
  * the tag is 'closed', the pool will be reused.
  */
 typedef enum report_state_e {
-    NONE = 0,
-    INITIAL = XML_STATE_INITIAL /* = 0 */,
-    UPDATE_REPORT,
-    TARGET_REVISION,
-    OPEN_DIR,
-    ADD_DIR,
-    ABSENT_DIR,
-    OPEN_FILE,
-    ADD_FILE,
-    ABSENT_FILE,
-    PROP,
-    IGNORE_PROP_NAME,
-    NEED_PROP_NAME,
-    TXDELTA
-
-#ifdef NOT_USED_YET
-    ,
+  INITIAL = XML_STATE_INITIAL /* = 0 */,
+  UPDATE_REPORT,
+  TARGET_REVISION,
 
-    CHECKED_IN,
-    CHECKED_IN_HREF,
+  OPEN_DIR,
+  ADD_DIR,
 
-    SET_PROP,
+  OPEN_FILE,
+  ADD_FILE,
 
-    MD5_CHECKSUM
-#endif
+  DELETE_ENTRY,
+  ABSENT_DIR,
+  ABSENT_FILE,
+
+  SET_PROP,
+  REMOVE_PROP,
+
+  PROP,
+
+  FETCH_FILE,
+  TXDELTA,
+
+  CHECKED_IN,
+  CHECKED_IN_HREF,
+
+  MD5_CHECKSUM
 } report_state_e;
 
 
+#define D_ "DAV:"
+#define S_ SVN_XML_NAMESPACE
+#define V_ SVN_DAV_PROP_NS_DAV
+static const svn_ra_serf__xml_transition_t update_ttable[] = {
+  { INITIAL, S_, "update-report", UPDATE_REPORT,
+    FALSE, { "?inline-props", "?send-all", NULL }, TRUE },
+
+  { UPDATE_REPORT, S_, "target-revision", TARGET_REVISION,
+    FALSE, { "rev", NULL }, TRUE },
+
+  { UPDATE_REPORT, S_, "open-directory", OPEN_DIR,
+    FALSE, { "rev", NULL }, TRUE },
+
+  { OPEN_DIR, S_, "open-directory", OPEN_DIR,
+    FALSE, { "rev", "name", NULL }, TRUE },
+
+  { ADD_DIR, S_, "open-directory", OPEN_DIR,
+    FALSE, { "rev", "name", NULL }, TRUE },
+
+  { OPEN_DIR, S_, "add-directory", ADD_DIR,
+    FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", /*"?bc-url",*/
+              NULL }, TRUE },
+
+  { ADD_DIR, S_, "add-directory", ADD_DIR,
+    FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", /*"?bc-url",*/
+              NULL }, TRUE },
+
+  { OPEN_DIR, S_, "open-file", OPEN_FILE,
+    FALSE, { "rev", "name", NULL }, TRUE },
+
+  { ADD_DIR, S_, "open-file", OPEN_FILE,
+    FALSE, { "rev", "name", NULL }, TRUE },
+
+  { OPEN_DIR, S_, "add-file", ADD_FILE,
+    FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev",
+             "?sha1-checksum", /*"?bc-url",*/ NULL }, TRUE },
+
+  { ADD_DIR, S_, "add-file", ADD_FILE,
+    FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev",
+             "?sha1-checksum", /*"?bc-url",*/ NULL }, TRUE },
+
+  { OPEN_DIR, S_, "delete-entry", DELETE_ENTRY,
+    FALSE, { "?rev", "name", NULL }, TRUE },
+
+  { ADD_DIR, S_, "delete-entry", DELETE_ENTRY,
+    FALSE, { "?rev", "name", NULL }, TRUE },
+
+  { OPEN_DIR, S_, "absent-directory", ABSENT_DIR,
+    FALSE, { "name", NULL }, TRUE },
+
+  { ADD_DIR, S_, "absent-directory", ABSENT_DIR,
+    FALSE, { "name", NULL }, TRUE },
+
+  { OPEN_DIR, S_, "absent-file", ABSENT_FILE,
+    FALSE, { "name", NULL }, TRUE },
+
+  { ADD_DIR, S_, "absent-file", ABSENT_FILE,
+    FALSE, { "name", NULL }, TRUE },
+
+
+  { OPEN_DIR, D_, "checked-in", CHECKED_IN,
+    FALSE, { NULL }, FALSE },
+
+  { ADD_DIR, D_, "checked-in", CHECKED_IN,
+    FALSE, { NULL }, FALSE },
+
+  { OPEN_FILE, D_, "checked-in", CHECKED_IN,
+    FALSE, { NULL }, FALSE },
+
+  { ADD_FILE, D_, "checked-in", CHECKED_IN,
+    FALSE, { NULL }, FALSE },
+
+
+  { OPEN_DIR, S_, "set-prop", SET_PROP,
+    TRUE, { "name", "?encoding", NULL }, TRUE },
+
+  { ADD_DIR, S_, "set-prop", SET_PROP,
+    TRUE, { "name", "?encoding", NULL }, TRUE },
+
+  { OPEN_FILE, S_, "set-prop", SET_PROP,
+    TRUE, { "name", "?encoding", NULL }, TRUE },
+
+  { ADD_FILE, S_, "set-prop", SET_PROP,
+    TRUE, { "name", "?encoding", NULL }, TRUE },
+
+
+  { OPEN_DIR, S_, "remove-prop", REMOVE_PROP,
+    TRUE, { "name", NULL }, TRUE },
+
+  { ADD_DIR, S_, "remove-prop", REMOVE_PROP,
+    TRUE, { "name", NULL }, TRUE },
+
+  { OPEN_FILE, S_, "remove-prop", REMOVE_PROP,
+    TRUE, { "name", NULL }, TRUE },
+
+  { ADD_FILE, S_, "remove-prop", REMOVE_PROP,
+    TRUE, { "name", NULL }, TRUE },
+
+  { OPEN_FILE, S_, "prop", PROP,
+    FALSE, { NULL }, FALSE },
+  { ADD_FILE, S_, "prop", PROP,
+    FALSE, { NULL }, FALSE },
+
+  { OPEN_FILE, S_, "txdelta", TXDELTA,
+    FALSE, { "?base-checksum" }, TRUE },
+
+  { ADD_FILE, S_, "txdelta", TXDELTA,
+    FALSE, { "?base-checksum" }, TRUE },
+
+  { OPEN_FILE, S_, "fetch-file", FETCH_FILE,
+    FALSE, { "?base-checksum", "?sha1-checksum", NULL }, TRUE},
+
+  { ADD_FILE, S_, "fetch-file", FETCH_FILE,
+    FALSE, { "?base-checksum", "?sha1-checksum", NULL }, TRUE },
+
+  { CHECKED_IN, D_, "href", CHECKED_IN_HREF,
+    TRUE, { NULL }, TRUE },
+
+  { PROP, V_, "md5-checksum", MD5_CHECKSUM,
+    TRUE, { NULL }, TRUE },
+
+  { 0 }
+};
+
 /* While we process the REPORT response, we will queue up GET and PROPFIND
    requests. For a very large checkout, it is very easy to queue requests
    faster than they are resolved. Thus, we need to pause the XML processing
@@ -117,6 +234,10 @@ typedef enum report_state_e {
 #define REQUEST_COUNT_TO_PAUSE 50
 #define REQUEST_COUNT_TO_RESUME 40
 
+#define SPILLBUF_BLOCKSIZE 4096
+#define SPILLBUF_MAXBUFFSIZE 131072
+
+#define PARSE_CHUNK_SIZE 8000 /* Copied from utils.c ### Needs tuning */
 
 /* Forward-declare our report context. */
 typedef struct report_context_t report_context_t;
@@ -124,156 +245,102 @@ typedef struct report_context_t report_c
 /*
  * This structure represents the information for a directory.
  */
-typedef struct report_dir_t
+typedef struct dir_baton_t
 {
-  /* Our parent directory.
-   *
-   * This value is NULL when we are the root.
-   */
-  struct report_dir_t *parent_dir;
+  struct dir_baton_t *parent_dir;       /* NULL when root */
 
-  apr_pool_t *pool;
+  apr_pool_t *pool;                     /* Subpool for this directory */
 
   /* Pointer back to our original report context. */
-  report_context_t *report_context;
-
-  /* Our name sans any parents. */
-  const char *base_name;
+  report_context_t *ctx;
 
-  /* the expanded directory name (including all parent names) */
-  const char *name;
+  const char *relpath;                  /* session relative path */
+  const char *base_name;                /* Name of item "" for root */
 
   /* the canonical url for this directory after updating. (received) */
   const char *url;
 
-  /* The original repos_relpath of this url (from the working copy)
-     or NULL if the repos_relpath can be calculated from the edit root. */
+  /* The original repos_relpath of this url (via the reporter)
+  directly, or via an ancestor. */
   const char *repos_relpath;
 
-  /* Our base revision - SVN_INVALID_REVNUM if we're adding this dir. */
-  svn_revnum_t base_rev;
+  svn_revnum_t base_rev;                /* base revision or NULL for Add */
+
+  const char *copyfrom_path;            /* NULL for open */
+  svn_revnum_t copyfrom_rev;            /* SVN_INVALID_REVNUM for open */
 
   /* controlling dir baton - this is only created in ensure_dir_opened() */
+  svn_boolean_t dir_opened;
   void *dir_baton;
-  apr_pool_t *dir_baton_pool;
 
   /* How many references to this directory do we still have open? */
   apr_size_t ref_count;
 
-  /* Namespace list allocated out of this ->pool. */
-  svn_ra_serf__ns_t *ns_list;
-
-  /* hashtable for all of the properties (shared within a dir) */
-  apr_hash_t *props;
-
-  /* hashtable for all to-be-removed properties (shared within a dir) */
-  apr_hash_t *removed_props;
-
-  /* The propfind request for our current directory */
+  svn_boolean_t fetch_props;                 /* Use PROPFIND request? */
   svn_ra_serf__handler_t *propfind_handler;
+  apr_hash_t *propfind_props;
+  apr_hash_t *remove_props;
 
-  /* Has the server told us to fetch the dir props? */
-  svn_boolean_t fetch_props;
-
-  /* Have we closed the directory tag (meaning no more additions)? */
-  svn_boolean_t tag_closed;
-
-  /* The children of this directory  */
-  struct report_dir_t *children;
-
-  /* The next sibling of this directory */
-  struct report_dir_t *sibling;
-} report_dir_t;
+} dir_baton_t;
 
 /*
- * This structure represents the information for a file.
- *
- * A directory may have a report_info_t associated with it as well.
- *
- * This structure is created as we parse the REPORT response and
- * once the element is completed, we create a report_fetch_t structure
- * to give to serf to retrieve this file.
- */
-typedef struct report_info_t
+* This structure represents the information for a file.
+*
+* This structure is created as we parse the REPORT response and
+* once the element is completed, we may create a fetch_ctx_t structure
+* to give to serf to retrieve this file.
+*/
+typedef struct file_baton_t
 {
-  apr_pool_t *pool;
+  dir_baton_t *parent_dir;              /* The parent */
+  apr_pool_t *pool;                     /* Subpool for this file*/
 
-  /* The enclosing directory.
-   *
-   * If this structure refers to a directory, the dir it points to will be
-   * itself.
-   */
-  report_dir_t *dir;
-
-  /* Our name sans any directory info. */
+  const char *relpath;                  /* session relative path */
   const char *base_name;
 
-  /* the expanded file name (including all parent directory names) */
-  const char *name;
-
-  /* the canonical url for this file. */
+  /* the canonical url for this directory after updating. (received) */
   const char *url;
 
+  /* The original repos_relpath of this url as reported. */
+  const char *repos_relpath;
+
   /* lock token, if we had one to start off with. */
   const char *lock_token;
 
-  /* Our base revision - SVN_INVALID_REVNUM if we're adding this file. */
-  svn_revnum_t base_rev;
-
-  /* our delta base, if present (NULL if we're adding the file) */
-  const char *delta_base;
+  svn_revnum_t base_rev;                /* SVN_INVALID_REVNUM for Add */
 
-  /* Path of original item if add with history */
-  const char *copyfrom_path;
+  const char *copyfrom_path;            /* NULL for open */
+  svn_revnum_t copyfrom_rev;            /* SVN_INVALID_REVNUM for open */
 
-  /* Revision of original item if add with history */
-  svn_revnum_t copyfrom_rev;
+  /* controlling dir baton - this is only created in ensure_file_opened() */
+  svn_boolean_t file_opened;
+  void *file_baton;
 
-  /* The propfind request for our current file (if present) */
+  svn_boolean_t fetch_props;            /* Use PROPFIND request? */
   svn_ra_serf__handler_t *propfind_handler;
-
-  /* Has the server told us to fetch the file props? */
-  svn_boolean_t fetch_props;
+  apr_hash_t *propfind_props;
+  apr_hash_t *remove_props;
 
   /* Has the server told us to go fetch - only valid if we had it already */
   svn_boolean_t fetch_file;
 
-  /* The properties for this file */
-  apr_hash_t *props;
-
-  /* pool passed to update->add_file, etc. */
-  apr_pool_t *editor_pool;
-
   /* controlling file_baton and textdelta handler */
-  void *file_baton;
-  const char *base_checksum;
-  const char *final_sha1_checksum;
-  svn_txdelta_window_handler_t textdelta;
-  void *textdelta_baton;
-  svn_stream_t *svndiff_decoder;
-  svn_stream_t *base64_decoder;
-
-  /* Checksum for close_file */
-  const char *final_checksum;
-
-  /* Stream containing file contents already cached in the working
-     copy (which may be used to avoid a GET request for the same). */
-  svn_stream_t *cached_contents;
+  svn_txdelta_window_handler_t txdelta;
+  void *txdelta_baton;
 
-  /* temporary property for this file which is currently being parsed
-   * It will eventually be stored in our parent directory's property hash.
-   */
-  const char *prop_ns;
-  const char *prop_name;
-  svn_stringbuf_t *prop_value;
-  const char *prop_encoding;
-} report_info_t;
+  svn_checksum_t *base_md5_checksum;
+  svn_checksum_t *final_md5_checksum;
+  svn_checksum_t *final_sha1_checksum;
+
+  svn_stream_t *txdelta_stream;         /* Stream that feeds windows when
+                                           written to within txdelta*/
+} file_baton_t;
 
 /*
  * This structure represents a single request to GET (fetch) a file with
  * its associated Serf session/connection.
  */
-typedef struct report_fetch_t {
+typedef struct fetch_ctx_t {
 
   /* The handler representing this particular fetch.  */
   svn_ra_serf__handler_t *handler;
@@ -285,7 +352,7 @@ typedef struct report_fetch_t {
   svn_ra_serf__connection_t *conn;
 
   /* Stores the information for the file we want to fetch. */
-  report_info_t *info;
+  file_baton_t *file;
 
   /* Have we read our response headers yet? */
   svn_boolean_t read_headers;
@@ -299,22 +366,13 @@ typedef struct report_fetch_t {
   /* This is the amount of data that we have read so far. */
   apr_off_t read_size;
 
-  /* If we're receiving an svndiff, this will be non-NULL. */
-  svn_stream_t *delta_stream;
-
   /* If we're writing this file to a stream, this will be non-NULL. */
-  svn_stream_t *target_stream;
-
-  /* Are we done fetching this file? */
-  svn_boolean_t done;
-
-  /* Discard the rest of the content? */
-  svn_boolean_t discard;
+  svn_stream_t *result_stream;
 
-  svn_ra_serf__list_t **done_list;
-  svn_ra_serf__list_t done_item;
+  /* The base-rev header  */
+  const char *delta_base;
 
-} report_fetch_t;
+} fetch_ctx_t;
 
 /*
  * The master structure for a REPORT request and response.
@@ -335,6 +393,10 @@ struct report_context_t {
   /* What is the target revision that we want for this REPORT? */
   svn_revnum_t target_rev;
 
+  /* Where are we (used while parsing) */
+  dir_baton_t *cur_dir;
+  file_baton_t *cur_file;
+
   /* Have we been asked to ignore ancestry or textdeltas? */
   svn_boolean_t ignore_ancestry;
   svn_boolean_t text_deltas;
@@ -355,12 +417,8 @@ struct report_context_t {
   /* Path -> const char *repos_relpath mapping */
   apr_hash_t *switched_paths;
 
-  /* Boolean indicating whether "" is switched.
-     (This indicates that the we are updating a single file) */
-  svn_boolean_t root_is_switched;
-
   /* Our master update editor and baton. */
-  const svn_delta_editor_t *update_editor;
+  const svn_delta_editor_t *editor;
   void *update_baton;
 
   /* The file holding request body for the REPORT.
@@ -370,136 +428,154 @@ struct report_context_t {
    */
   apr_file_t *body_file;
 
-  /* root directory object */
-  report_dir_t *root_dir;
-
   /* number of pending GET requests */
   unsigned int num_active_fetches;
 
-  /* completed fetches (contains report_fetch_t) */
-  svn_ra_serf__list_t *done_fetches;
-
   /* number of pending PROPFIND requests */
   unsigned int num_active_propfinds;
 
-  /* completed PROPFIND requests (contains svn_ra_serf__handler_t) */
-  svn_ra_serf__list_t *done_propfinds;
-  svn_ra_serf__list_t *done_dir_propfinds;
-
-  /* list of outstanding prop changes (contains report_dir_t) */
-  svn_ra_serf__list_t *active_dir_propfinds;
-
-  /* list of files that only have prop changes (contains report_info_t) */
-  svn_ra_serf__list_t *file_propchanges_only;
-
-  /* The path to the REPORT request */
-  const char *path;
-
   /* Are we done parsing the REPORT response? */
   svn_boolean_t done;
 
   /* Did we receive all data from the network? */
   svn_boolean_t report_received;
 
-  /* Did we get a complete (non-truncated) report? */
-  svn_boolean_t report_completed;
-
-  /* The XML parser context for the REPORT response.  */
-  svn_ra_serf__xml_parser_t *parser_ctx;
-
   /* Did we close the root directory? */
   svn_boolean_t closed_root;
 };
 
+static svn_error_t *
+create_dir_baton(dir_baton_t **new_dir,
+                 report_context_t *ctx,
+                 const char *name,
+                 apr_pool_t *scratch_pool)
+{
+  dir_baton_t *parent = ctx->cur_dir;
+  apr_pool_t *dir_pool;
+  dir_baton_t *dir;
 
-#ifdef USE_TRANSITION_PARSER
+  if (parent)
+    dir_pool = svn_pool_create(parent->pool);
+  else
+    dir_pool = svn_pool_create(ctx->pool);
 
-#define D_ "DAV:"
-#define S_ SVN_XML_NAMESPACE
-#define V_ SVN_DAV_PROP_NS_DAV
-static const svn_ra_serf__xml_transition_t update_ttable[] = {
-  { INITIAL, S_, "update-report", UPDATE_REPORT,
-    FALSE, { NULL }, FALSE },
+  dir = apr_pcalloc(dir_pool, sizeof(*dir));
+  dir->pool = dir_pool;
+  dir->ctx = ctx;
 
-  { UPDATE_REPORT, S_, "target-revision", TARGET_REVISION,
-    FALSE, { "rev", NULL }, TRUE },
+  if (parent)
+    {
+      dir->parent_dir = parent;
+      parent->ref_count++;
+    }
 
-  { UPDATE_REPORT, S_, "open-directory", OPEN_DIR,
-    FALSE, { "rev", NULL }, TRUE },
+  dir->relpath = parent ? svn_relpath_join(parent->relpath, name, dir_pool)
+                        : apr_pstrdup(dir_pool, name);
+  dir->base_name = svn_relpath_basename(dir->relpath, NULL);
 
-  { OPEN_DIR, S_, "open-directory", OPEN_DIR,
-    FALSE, { "rev", "name", NULL }, TRUE },
+  dir->repos_relpath = svn_hash_gets(ctx->switched_paths, dir->relpath);
+  if (!dir->repos_relpath)
+    {
+      if (parent)
+        dir->repos_relpath = svn_relpath_join(parent->repos_relpath, name,
+                                              dir_pool);
+      else
+        dir->repos_relpath = svn_uri_skip_ancestor(ctx->sess->repos_root_str,
+                                                   ctx->sess->session_url_str,
+                                                   dir_pool);
+    }
 
-  { OPEN_DIR, S_, "add-directory", ADD_DIR,
-    FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+  dir->base_rev = SVN_INVALID_REVNUM;
+  dir->copyfrom_rev = SVN_INVALID_REVNUM;
 
-  { ADD_DIR, S_, "add-directory", ADD_DIR,
-    FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+  dir->ref_count = 1;
 
-  { OPEN_DIR, S_, "open-file", OPEN_FILE,
-    FALSE, { "rev", "name", NULL }, TRUE },
+  ctx->cur_dir = dir;
 
-  { OPEN_DIR, S_, "add-file", ADD_FILE,
-    FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+  *new_dir = dir;
+  return SVN_NO_ERROR;
+}
 
-  { ADD_DIR, S_, "add-file", ADD_FILE,
-    FALSE, { "name", "?rev", "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
+static svn_error_t *
+create_file_baton(file_baton_t **new_file,
+                  report_context_t *ctx,
+                  const char *name,
+                  apr_pool_t *scratch_pool)
+{
+  dir_baton_t *parent = ctx->cur_dir;
+  apr_pool_t *file_pool;
+  file_baton_t *file;
 
-  { OPEN_DIR, S_, "delete-entry", OPEN_FILE,
-    FALSE, { "?rev", "name", NULL }, TRUE },
+  file_pool = svn_pool_create(parent->pool);
 
-  { OPEN_DIR, S_, "absent-directory", ABSENT_DIR,
-    FALSE, { "name", NULL }, TRUE },
+  file = apr_pcalloc(file_pool, sizeof(*file));
+  file->pool = file_pool;
 
-  { ADD_DIR, S_, "absent-directory", ABSENT_DIR,
-    FALSE, { "name", NULL }, TRUE },
+  file->parent_dir = parent;
+  parent->ref_count++;
 
-  { OPEN_DIR, S_, "absent-file", ABSENT_FILE,
-    FALSE, { "name", NULL }, TRUE },
+  file->relpath = svn_relpath_join(parent->relpath, name, file_pool);
+  file->base_name = svn_relpath_basename(file->relpath, NULL);
 
-  { ADD_DIR, S_, "absent-file", ABSENT_FILE,
-    FALSE, { "name", NULL }, TRUE },
+  file->repos_relpath = svn_hash_gets(ctx->switched_paths, file->relpath);
+  if (!file->repos_relpath)
+    file->repos_relpath = svn_relpath_join(parent->repos_relpath, name,
+                                           file_pool);
 
+  /* Sane defaults */
+  file->base_rev = SVN_INVALID_REVNUM;
+  file->copyfrom_rev = SVN_INVALID_REVNUM;
 
-  { OPEN_DIR, D_, "checked-in", CHECKED_IN,
-    FALSE, { NULL }, FALSE },
+  file->lock_token = svn_hash_gets(parent->ctx->lock_path_tokens,
+                                   file->relpath);
 
-  { ADD_DIR, D_, "checked-in", CHECKED_IN,
-    FALSE, { NULL }, FALSE },
+  *new_file = file;
 
-  { ADD_FILE, D_, "checked-in", CHECKED_IN,
-    FALSE, { NULL }, FALSE },
+  ctx->cur_file = file;
 
+  return SVN_NO_ERROR;
+}
 
-  { OPEN_DIR, S_, "set-prop", SET_PROP,
-    TRUE, { "name", "?encoding", NULL }, TRUE },
+/** Minimum nr. of outstanding requests needed before a new connection is
+ *  opened. */
+#define REQS_PER_CONN 8
 
-  { ADD_DIR, S_, "set-prop", SET_PROP,
-    TRUE, { "name", "?encoding", NULL }, TRUE },
+/** This function creates a new connection for this serf session, but only
+ * if the number of NUM_ACTIVE_REQS > REQS_PER_CONN or if there currently is
+ * only one main connection open.
+ */
+static svn_error_t *
+open_connection_if_needed(svn_ra_serf__session_t *sess, int num_active_reqs)
+{
+  /* For each REQS_PER_CONN outstanding requests open a new connection, with
+   * a minimum of 1 extra connection. */
+  if (sess->num_conns == 1 ||
+      ((num_active_reqs / REQS_PER_CONN) > sess->num_conns))
+    {
+      int cur = sess->num_conns;
+      apr_status_t status;
 
-  { OPEN_FILE, S_, "set-prop", SET_PROP,
-    TRUE, { "name", "?encoding", NULL }, TRUE },
+      sess->conns[cur] = apr_pcalloc(sess->pool, sizeof(*sess->conns[cur]));
+      sess->conns[cur]->bkt_alloc = serf_bucket_allocator_create(sess->pool,
+                                                                 NULL, NULL);
+      sess->conns[cur]->last_status_code = -1;
+      sess->conns[cur]->session = sess;
+      status = serf_connection_create2(&sess->conns[cur]->conn,
+                                       sess->context,
+                                       sess->session_url,
+                                       svn_ra_serf__conn_setup,
+                                       sess->conns[cur],
+                                       svn_ra_serf__conn_closed,
+                                       sess->conns[cur],
+                                       sess->pool);
+      if (status)
+        return svn_ra_serf__wrap_err(status, NULL);
 
-  { ADD_FILE, S_, "set-prop", SET_PROP,
-    TRUE, { "name", "?encoding", NULL }, TRUE },
-
-
-  { OPEN_FILE, S_, "prop", PROP,
-    FALSE, { NULL }, FALSE },
-  { ADD_FILE, S_, "prop", PROP,
-    FALSE, { NULL }, FALSE },
-
-  { CHECKED_IN, D_, "href", CHECKED_IN_HREF,
-    TRUE, { NULL }, TRUE },
-
-  { PROP, V_, "md5-checksum", MD5_CHECKSUM,
-    TRUE, { NULL }, TRUE },
-
-  { 0 }
-};
-
-#endif /* USE_TRANSITION_PARSER */
+      sess->num_conns++;
+    }
 
+  return SVN_NO_ERROR;
+}
 
 /* Returns best connection for fetching files/properties. */
 static svn_ra_serf__connection_t *
@@ -545,94 +621,7 @@ get_best_connection(report_context_t *ct
 }
 
 
-/** Report state management helper **/
-
-static report_info_t *
-push_state(svn_ra_serf__xml_parser_t *parser,
-           report_context_t *ctx,
-           report_state_e state)
-{
-  report_info_t *info;
-  apr_pool_t *info_parent_pool;
-
-  svn_ra_serf__xml_push_state(parser, state);
-
-  info = parser->state->private;
-
-  /* Our private pool needs to be disjoint from the state pool. */
-  if (!info)
-    {
-      info_parent_pool = ctx->pool;
-    }
-  else
-    {
-      info_parent_pool = info->pool;
-    }
-
-  if (state == OPEN_DIR || state == ADD_DIR)
-    {
-      report_info_t *new_info;
-
-      new_info = apr_pcalloc(info_parent_pool, sizeof(*new_info));
-      new_info->pool = svn_pool_create(info_parent_pool);
-      new_info->lock_token = NULL;
-      new_info->prop_value = svn_stringbuf_create_empty(new_info->pool);
-
-      new_info->dir = apr_pcalloc(new_info->pool, sizeof(*new_info->dir));
-      new_info->dir->pool = new_info->pool;
-
-      /* Create the root property tree. */
-      new_info->dir->props = apr_hash_make(new_info->pool);
-      new_info->props = new_info->dir->props;
-      new_info->dir->removed_props = apr_hash_make(new_info->pool);
-
-      new_info->dir->report_context = ctx;
-
-      if (info)
-        {
-          info->dir->ref_count++;
-
-          new_info->dir->parent_dir = info->dir;
-
-          /* Point our ns_list at our parents to try to reuse it. */
-          new_info->dir->ns_list = info->dir->ns_list;
-
-          /* Add ourselves to our parent's list */
-          new_info->dir->sibling = info->dir->children;
-          info->dir->children = new_info->dir;
-        }
-      else
-        {
-          /* Allow us to be found later. */
-          ctx->root_dir = new_info->dir;
-        }
-
-      parser->state->private = new_info;
-    }
-  else if (state == OPEN_FILE || state == ADD_FILE)
-    {
-      report_info_t *new_info;
-
-      new_info = apr_pcalloc(info_parent_pool, sizeof(*new_info));
-      new_info->pool = svn_pool_create(info_parent_pool);
-      new_info->file_baton = NULL;
-      new_info->lock_token = NULL;
-      new_info->fetch_file = FALSE;
-      new_info->prop_value = svn_stringbuf_create_empty(new_info->pool);
-
-      /* Point at our parent's directory state. */
-      new_info->dir = info->dir;
-      info->dir->ref_count++;
-
-      new_info->props = apr_hash_make(new_info->pool);
-
-      parser->state->private = new_info;
-    }
 
-  return parser->state->private;
-}
-
-
 /** Wrappers around our various property walkers **/
 
 static svn_error_t *
@@ -642,13 +631,13 @@ set_file_props(void *baton,
                const svn_string_t *val,
                apr_pool_t *scratch_pool)
 {
-  report_info_t *info = baton;
-  const svn_delta_editor_t *editor = info->dir->report_context->update_editor;
+  file_baton_t *file = baton;
+  const svn_delta_editor_t *editor = file->parent_dir->ctx->editor;
   const char *prop_name;
 
   prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
   if (prop_name != NULL)
-    return svn_error_trace(editor->change_file_prop(info->file_baton,
+    return svn_error_trace(editor->change_file_prop(file->file_baton,
                                                     prop_name,
                                                     val,
                                                     scratch_pool));
@@ -663,8 +652,8 @@ set_dir_props(void *baton,
               const svn_string_t *val,
               apr_pool_t *scratch_pool)
 {
-  report_dir_t *dir = baton;
-  const svn_delta_editor_t *editor = dir->report_context->update_editor;
+  dir_baton_t *dir = baton;
+  const svn_delta_editor_t *editor = dir->ctx->editor;
   const char *prop_name;
 
   prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
@@ -675,181 +664,140 @@ set_dir_props(void *baton,
                                                    scratch_pool));
   return SVN_NO_ERROR;
 }
-
-
-static svn_error_t *
-remove_file_props(void *baton,
-                  const char *ns,
-                  const char *name,
-                  const svn_string_t *val,
-                  apr_pool_t *scratch_pool)
-{
-  report_info_t *info = baton;
-  const svn_delta_editor_t *editor = info->dir->report_context->update_editor;
-  const char *prop_name;
-
-  prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
-  if (prop_name != NULL)
-    return svn_error_trace(editor->change_file_prop(info->file_baton,
-                                                    prop_name,
-                                                    NULL,
-                                                    scratch_pool));
-  return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *
-remove_dir_props(void *baton,
-                 const char *ns,
-                 const char *name,
-                 const svn_string_t *val,
-                 apr_pool_t *scratch_pool)
-{
-  report_dir_t *dir = baton;
-  const svn_delta_editor_t *editor = dir->report_context->update_editor;
-  const char *prop_name;
-
-  prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
-  if (prop_name != NULL)
-    return svn_error_trace(editor->change_dir_prop(dir->dir_baton,
-                                                   prop_name,
-                                                   NULL,
-                                                   scratch_pool));
-  return SVN_NO_ERROR;
-}
-
 
 /** Helpers to open and close directories */
 
 static svn_error_t*
-ensure_dir_opened(report_dir_t *dir)
+ensure_dir_opened(dir_baton_t *dir,
+                  apr_pool_t *scratch_pool)
 {
-  report_context_t *ctx = dir->report_context;
+  report_context_t *ctx = dir->ctx;
 
-  /* if we're already open, return now */
-  if (dir->dir_baton)
-    {
-      return SVN_NO_ERROR;
-    }
+  if (dir->dir_opened)
+    return SVN_NO_ERROR;
 
   if (dir->base_name[0] == '\0')
     {
-      dir->dir_baton_pool = svn_pool_create(dir->pool);
-
       if (ctx->destination
           && ctx->sess->wc_callbacks->invalidate_wc_props)
         {
           SVN_ERR(ctx->sess->wc_callbacks->invalidate_wc_props(
                       ctx->sess->wc_callback_baton,
                       ctx->update_target,
-                      SVN_RA_SERF__WC_CHECKED_IN_URL, dir->pool));
+                      SVN_RA_SERF__WC_CHECKED_IN_URL, scratch_pool));
         }
 
-      SVN_ERR(ctx->update_editor->open_root(ctx->update_baton, dir->base_rev,
-                                            dir->dir_baton_pool,
-                                            &dir->dir_baton));
+      SVN_ERR(ctx->editor->open_root(ctx->update_baton, dir->base_rev,
+                                     dir->pool,
+                                     &dir->dir_baton));
     }
   else
     {
-      SVN_ERR(ensure_dir_opened(dir->parent_dir));
-
-      dir->dir_baton_pool = svn_pool_create(dir->parent_dir->dir_baton_pool);
+      SVN_ERR(ensure_dir_opened(dir->parent_dir, scratch_pool));
 
       if (SVN_IS_VALID_REVNUM(dir->base_rev))
         {
-          SVN_ERR(ctx->update_editor->open_directory(dir->name,
-                                                     dir->parent_dir->dir_baton,
-                                                     dir->base_rev,
-                                                     dir->dir_baton_pool,
-                                                     &dir->dir_baton));
+          SVN_ERR(ctx->editor->open_directory(dir->relpath,
+                                              dir->parent_dir->dir_baton,
+                                              dir->base_rev,
+                                              dir->pool,
+                                              &dir->dir_baton));
         }
       else
         {
-          SVN_ERR(ctx->update_editor->add_directory(dir->name,
-                                                    dir->parent_dir->dir_baton,
-                                                    NULL, SVN_INVALID_REVNUM,
-                                                    dir->dir_baton_pool,
-                                                    &dir->dir_baton));
+          SVN_ERR(ctx->editor->add_directory(dir->relpath,
+                                             dir->parent_dir->dir_baton,
+                                             dir->copyfrom_path,
+                                             dir->copyfrom_rev,
+                                             dir->pool,
+                                             &dir->dir_baton));
         }
     }
 
+  dir->dir_opened = TRUE;
+
   return SVN_NO_ERROR;
 }
 
 static svn_error_t *
-close_dir(report_dir_t *dir)
+maybe_close_dir(dir_baton_t *dir)
 {
-  report_dir_t *prev;
-  report_dir_t *sibling;
-
-  /* ### is there a better pool... this is tossed at end-of-func  */
-  apr_pool_t *scratch_pool = dir->dir_baton_pool;
+  apr_pool_t *scratch_pool = dir->pool;
+  dir_baton_t *parent = dir->parent_dir;
+  report_context_t *ctx = dir->ctx;
 
-  SVN_ERR_ASSERT(! dir->ref_count);
-
-  SVN_ERR(svn_ra_serf__walk_all_props(dir->props, dir->base_name,
-                                      dir->base_rev,
-                                      set_dir_props, dir,
-                                      scratch_pool));
+  if (--dir->ref_count)
+    {
+      return SVN_NO_ERROR;
+    }
 
-  SVN_ERR(svn_ra_serf__walk_all_props(dir->removed_props, dir->base_name,
-                                      dir->base_rev, remove_dir_props, dir,
-                                      scratch_pool));
+  SVN_ERR(ensure_dir_opened(dir, dir->pool));
 
-  if (dir->fetch_props)
+  if (dir->propfind_props)
     {
-      SVN_ERR(svn_ra_serf__walk_all_props(dir->props, dir->url,
-                                          dir->report_context->target_rev,
+      apr_hash_index_t *hi;
+      SVN_ERR(svn_ra_serf__walk_all_props(dir->propfind_props,
+                                          dir->url,
+                                          ctx->target_rev,
                                           set_dir_props, dir,
                                           scratch_pool));
+
+      if (dir->remove_props)
+        for (hi = apr_hash_first(scratch_pool, dir->remove_props);
+             hi;
+             hi = apr_hash_next(hi))
+          {
+            SVN_ERR(ctx->editor->change_file_prop(dir->dir_baton,
+                                                  svn__apr_hash_index_key(hi),
+                                                  NULL /* value */,
+                                                  scratch_pool));
+          }
     }
 
-  SVN_ERR(dir->report_context->update_editor->close_directory(
-            dir->dir_baton, scratch_pool));
+  SVN_ERR(dir->ctx->editor->close_directory(dir->dir_baton, scratch_pool));
 
-  /* remove us from our parent's children list */
-  if (dir->parent_dir)
-    {
-      prev = NULL;
-      sibling = dir->parent_dir->children;
+  svn_pool_destroy(dir->pool /* scratch_pool */);
 
-      while (sibling != dir)
-        {
-          prev = sibling;
-          sibling = sibling->sibling;
-          if (!sibling)
-            SVN_ERR_MALFUNCTION();
-        }
+  if (parent)
+    return svn_error_trace(maybe_close_dir(parent));
+  else
+    return SVN_NO_ERROR;
+}
 
-      if (!prev)
-        {
-          dir->parent_dir->children = dir->sibling;
-        }
-      else
-        {
-          prev->sibling = dir->sibling;
-        }
-    }
+static svn_error_t *
+ensure_file_opened(file_baton_t *file,
+                   apr_pool_t *scratch_pool)
+{
+  const svn_delta_editor_t *editor = file->parent_dir->ctx->editor;
 
-  svn_pool_destroy(dir->dir_baton_pool);
-  svn_pool_destroy(dir->pool);
+  if (file->file_opened)
+    return SVN_NO_ERROR;
 
-  return SVN_NO_ERROR;
-}
+  /* Ensure our parent is open. */
+  SVN_ERR(ensure_dir_opened(file->parent_dir, scratch_pool));
 
-static svn_error_t *close_all_dirs(report_dir_t *dir)
-{
-  while (dir->children)
+  /* Open (or add) the file. */
+  if (SVN_IS_VALID_REVNUM(file->base_rev))
     {
-      SVN_ERR(close_all_dirs(dir->children));
-      dir->ref_count--;
+      SVN_ERR(editor->open_file(file->relpath,
+                                file->parent_dir->dir_baton,
+                                file->base_rev,
+                                file->pool,
+                                &file->file_baton));
+    }
+  else
+    {
+      SVN_ERR(editor->add_file(file->relpath,
+                               file->parent_dir->dir_baton,
+                               file->copyfrom_path,
+                               file->copyfrom_rev,
+                               file->pool,
+                               &file->file_baton));
     }
 
-  SVN_ERR_ASSERT(! dir->ref_count);
-
-  SVN_ERR(ensure_dir_opened(dir));
+  file->file_opened = TRUE;
 
-  return close_dir(dir);
+  return SVN_NO_ERROR;
 }
 
 
@@ -863,33 +811,39 @@ static svn_error_t *close_all_dirs(repor
  * an active lock.  If not, then we'll assume there isn't a lock
  * anymore.
  */
-static void
-check_lock(report_info_t *info)
+static svn_error_t *
+check_lock(file_baton_t *file,
+           apr_pool_t *scratch_pool)
 {
   const char *lock_val;
 
-  lock_val = svn_ra_serf__get_ver_prop(info->props, info->url,
-                                       info->dir->report_context->target_rev,
+  if (!file->propfind_handler || !file->lock_token)
+    return SVN_NO_ERROR;
+
+  lock_val = svn_ra_serf__get_ver_prop(file->propfind_props, file->url,
+                                       file->parent_dir->ctx->target_rev,
                                        "DAV:", "lockdiscovery");
 
   if (lock_val)
     {
       char *new_lock;
-      new_lock = apr_pstrdup(info->editor_pool, lock_val);
+      new_lock = apr_pstrdup(scratch_pool, lock_val);
       apr_collapse_spaces(new_lock, new_lock);
       lock_val = new_lock;
     }
 
   if (!lock_val || lock_val[0] == '\0')
     {
-      svn_string_t *str;
+      SVN_ERR(ensure_file_opened(file, scratch_pool));
 
-      str = svn_string_ncreate("", 1, info->editor_pool);
-
-      svn_ra_serf__set_ver_prop(info->dir->removed_props, info->base_name,
-                                info->base_rev, "DAV:", "lock-token",
-                                str, info->dir->pool);
+      SVN_ERR(file->parent_dir->ctx->editor->change_file_prop(
+                                                file->file_baton,
+                                                SVN_PROP_ENTRY_LOCK_TOKEN,
+                                                NULL,
+                                                scratch_pool));
     }
+
+  return SVN_NO_ERROR;
 }
 
 static svn_error_t *
@@ -897,14 +851,13 @@ headers_fetch(serf_bucket_t *headers,
               void *baton,
               apr_pool_t *pool)
 {
-  report_fetch_t *fetch_ctx = baton;
+  fetch_ctx_t *fetch_ctx = baton;
 
   /* note that we have old VC URL */
-  if (SVN_IS_VALID_REVNUM(fetch_ctx->info->base_rev) &&
-      fetch_ctx->info->delta_base)
+  if (fetch_ctx->delta_base)
     {
       serf_bucket_headers_setn(headers, SVN_DAV_DELTA_BASE_HEADER,
-                               fetch_ctx->info->delta_base);
+                               fetch_ctx->delta_base);
       serf_bucket_headers_setn(headers, "Accept-Encoding",
                                "svndiff1;q=0.9,svndiff;q=0.8");
     }
@@ -922,7 +875,7 @@ cancel_fetch(serf_request_t *request,
              int status_code,
              void *baton)
 {
-  report_fetch_t *fetch_ctx = baton;
+  fetch_ctx_t *fetch_ctx = baton;
 
   /* Uh-oh.  Our connection died on us.
    *
@@ -952,30 +905,6 @@ cancel_fetch(serf_request_t *request,
   SVN_ERR_MALFUNCTION();
 }
 
-static svn_error_t *
-error_fetch(serf_request_t *request,
-            report_fetch_t *fetch_ctx,
-            svn_error_t *err)
-{
-  fetch_ctx->done = TRUE;
-
-  fetch_ctx->done_item.data = fetch_ctx;
-  fetch_ctx->done_item.next = *fetch_ctx->done_list;
-  *fetch_ctx->done_list = &fetch_ctx->done_item;
-
-  /* Discard the rest of this request
-     (This makes sure it doesn't error when the request is aborted later) */
-  serf_request_set_handler(request,
-                           svn_ra_serf__response_discard_handler, NULL);
-
-  /* Some errors would be handled by serf; make sure they really make
-     the update fail by wrapping it in a different error. */
-  if (!SERF_BUCKET_READ_ERROR(err->apr_err))
-    return svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL);
-
-  return err;
-}
-
 /* Wield the editor referenced by INFO to open (or add) the file
    file also associated with INFO, setting properties on the file and
    calling the editor's apply_textdelta() function on it if necessary
@@ -984,95 +913,82 @@ error_fetch(serf_request_t *request,
    Callers will probably want to also see the function that serves
    the opposite purpose of this one, close_updated_file().  */
 static svn_error_t *
-open_updated_file(report_info_t *info,
-                  svn_boolean_t force_apply_textdelta,
+open_file_txdelta(file_baton_t *file,
                   apr_pool_t *scratch_pool)
 {
-  report_context_t *ctx = info->dir->report_context;
-  const svn_delta_editor_t *update_editor = ctx->update_editor;
-
-  /* Ensure our parent is open. */
-  SVN_ERR(ensure_dir_opened(info->dir));
-  info->editor_pool = svn_pool_create(info->dir->dir_baton_pool);
-
-  /* Expand our full name now if we haven't done so yet. */
-  if (!info->name)
-    {
-      info->name = svn_relpath_join(info->dir->name, info->base_name,
-                                    info->editor_pool);
-    }
+  const svn_delta_editor_t *editor = file->parent_dir->ctx->editor;
 
-  /* Open (or add) the file. */
-  if (SVN_IS_VALID_REVNUM(info->base_rev))
-    {
-      SVN_ERR(update_editor->open_file(info->name,
-                                       info->dir->dir_baton,
-                                       info->base_rev,
-                                       info->editor_pool,
-                                       &info->file_baton));
-    }
-  else
-    {
-      SVN_ERR(update_editor->add_file(info->name,
-                                      info->dir->dir_baton,
-                                      info->copyfrom_path,
-                                      info->copyfrom_rev,
-                                      info->editor_pool,
-                                      &info->file_baton));
-    }
+  SVN_ERR_ASSERT(file->txdelta == NULL);
 
-  /* Check for lock information. */
-  if (info->lock_token)
-    check_lock(info);
+  SVN_ERR(ensure_file_opened(file, scratch_pool));
 
   /* Get (maybe) a textdelta window handler for transmitting file
      content changes. */
-  if (info->fetch_file || force_apply_textdelta)
-    {
-      SVN_ERR(update_editor->apply_textdelta(info->file_baton,
-                                             info->base_checksum,
-                                             info->editor_pool,
-                                             &info->textdelta,
-                                             &info->textdelta_baton));
-    }
+  SVN_ERR(editor->apply_textdelta(file->file_baton,
+                                  svn_checksum_to_cstring(
+                                                  file->base_md5_checksum,
+                                                  scratch_pool),
+                                  file->pool,
+                                  &file->txdelta,
+                                  &file->txdelta_baton));
 
   return SVN_NO_ERROR;
 }
 
-/* Close the file associated with INFO->file_baton, and cleanup other
-   bits of that structure managed by open_updated_file(). */
+/* Close the file, handling loose ends and cleanup */
 static svn_error_t *
-close_updated_file(report_info_t *info,
-                   apr_pool_t *scratch_pool)
+close_file(file_baton_t *file,
+           apr_pool_t *scratch_pool)
 {
-  report_context_t *ctx = info->dir->report_context;
+  dir_baton_t *parent_dir = file->parent_dir;
+  report_context_t *ctx = parent_dir->ctx;
+
+  SVN_ERR(ensure_file_opened(file, scratch_pool));
 
   /* Set all of the properties we received */
-  SVN_ERR(svn_ra_serf__walk_all_props(info->props,
-                                      info->base_name,
-                                      info->base_rev,
-                                      set_file_props, info,
-                                      scratch_pool));
-  SVN_ERR(svn_ra_serf__walk_all_props(info->dir->removed_props,
-                                      info->base_name,
-                                      info->base_rev,
-                                      remove_file_props, info,
-                                      scratch_pool));
-  if (info->fetch_props)
+  if (file->propfind_props)
     {
-      SVN_ERR(svn_ra_serf__walk_all_props(info->props,
-                                          info->url,
+      apr_hash_index_t *hi;
+      SVN_ERR(svn_ra_serf__walk_all_props(file->propfind_props,
+                                          file->url,
                                           ctx->target_rev,
-                                          set_file_props, info,
+                                          set_file_props, file,
+                                          scratch_pool));
+
+      if (file->remove_props)
+        for (hi = apr_hash_first(scratch_pool, file->remove_props);
+             hi;
+             hi = apr_hash_next(hi))
+          {
+            SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
+                                                  svn__apr_hash_index_key(hi),
+                                                  NULL /* value */,
+                                                  scratch_pool));
+          }
+    }
+
+  /* Check for lock information. */
+  SVN_ERR(check_lock(file, scratch_pool));
+
+  if (file->url)
+  {
+    SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
+                                          SVN_RA_SERF__WC_CHECKED_IN_URL,
+                                          svn_string_create(file->url,
+                                                            scratch_pool),
                                           scratch_pool));
     }
 
   /* Close the file via the editor. */
-  SVN_ERR(info->dir->report_context->update_editor->close_file(
-            info->file_baton, info->final_checksum, scratch_pool));
+  SVN_ERR(ctx->editor->close_file(file->file_baton,
+                                  svn_checksum_to_cstring(
+                                        file->final_md5_checksum,
+                                        scratch_pool),
+                                  scratch_pool));
 
-  /* We're done with our editor pool. */
-  svn_pool_destroy(info->editor_pool);
+  svn_pool_destroy(file->pool);
+
+  SVN_ERR(maybe_close_dir(parent_dir)); /* Remove reference */
 
   return SVN_NO_ERROR;
 }
@@ -1087,8 +1003,8 @@ handle_fetch(serf_request_t *request,
   const char *data;
   apr_size_t len;
   apr_status_t status;
-  report_fetch_t *fetch_ctx = handler_baton;
-  svn_error_t *err;
+  fetch_ctx_t *fetch_ctx = handler_baton;
+  file_baton_t *file = fetch_ctx->file;
 
   /* ### new field. make sure we didn't miss some initialization.  */
   SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
@@ -1097,47 +1013,47 @@ handle_fetch(serf_request_t *request,
     {
       serf_bucket_t *hdrs;
       const char *val;
-      report_info_t *info;
+      file_baton_t *file;
+
+      /* If the error code wasn't 200, something went wrong. Don't use the
+       * returned data as its probably an error message. Just bail out instead.
+       */
+      if (fetch_ctx->handler->sline.code != 200)
+        {
+          fetch_ctx->handler->discard_body = TRUE;
+          return SVN_NO_ERROR; /* Will return an error in the DONE handler */
+        }
 
       hdrs = serf_bucket_response_get_headers(response);
       val = serf_bucket_headers_get(hdrs, "Content-Type");
-      info = fetch_ctx->info;
+      file = fetch_ctx->file;
 
       if (val && svn_cstring_casecmp(val, SVN_SVNDIFF_MIME_TYPE) == 0)
         {
-          fetch_ctx->delta_stream =
-              svn_txdelta_parse_svndiff(info->textdelta,
-                                        info->textdelta_baton,
-                                        TRUE, info->editor_pool);
+          fetch_ctx->result_stream =
+              svn_txdelta_parse_svndiff(file->txdelta,
+                                        file->txdelta_baton,
+                                        TRUE, file->pool);
 
           /* Validate the delta base claimed by the server matches
              what we asked for! */
           val = serf_bucket_headers_get(hdrs, SVN_DAV_DELTA_BASE_HEADER);
-          if (val && (strcmp(val, info->delta_base) != 0))
+          if (val && (strcmp(val, fetch_ctx->delta_base) != 0))
             {
-              err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
-                                      _("GET request returned unexpected "
-                                        "delta base: %s"), val);
-              return error_fetch(request, fetch_ctx, err);
+              return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
+                                       _("GET request returned unexpected "
+                                         "delta base: %s"), val);
             }
         }
       else
         {
-          fetch_ctx->delta_stream = NULL;
+          fetch_ctx->result_stream = NULL;
         }
 
       fetch_ctx->read_headers = TRUE;
     }
 
-  /* If the error code wasn't 200, something went wrong. Don't use the returned
-     data as its probably an error message. Just bail out instead. */
-  if (fetch_ctx->handler->sline.code != 200)
-    {
-      err = svn_error_trace(svn_ra_serf__unexpected_status(fetch_ctx->handler));
-      return error_fetch(request, fetch_ctx, err);
-    }
-
-  while (1)
+  while (TRUE)
     {
       svn_txdelta_window_t delta_window = { 0 };
       svn_txdelta_op_t delta_op;
@@ -1164,10 +1080,9 @@ handle_fetch(serf_request_t *request,
                 }
 
               /* Skip on to the next iteration of this loop. */
-              if (APR_STATUS_IS_EAGAIN(status))
-                {
-                  return svn_ra_serf__wrap_err(status, NULL);
-                }
+              if (status /* includes EAGAIN */)
+                return svn_ra_serf__wrap_err(status, NULL);
+
               continue;
             }
 
@@ -1177,17 +1092,12 @@ handle_fetch(serf_request_t *request,
           /* Update data and len to just provide the new data. */
           skip = len - (fetch_ctx->read_size - fetch_ctx->aborted_read_size);
           data += skip;
-          len -= skip;
+          len -= (apr_size_t)skip;
         }
 
-      if (fetch_ctx->delta_stream)
-        {
-          err = svn_stream_write(fetch_ctx->delta_stream, data, &len);
-          if (err)
-            {
-              return error_fetch(request, fetch_ctx, err);
-            }
-        }
+      if (fetch_ctx->result_stream)
+        SVN_ERR(svn_stream_write(fetch_ctx->result_stream, data, &len));
+
       /* otherwise, manually construct the text delta window. */
       else if (len)
         {
@@ -1204,372 +1114,214 @@ handle_fetch(serf_request_t *request,
           delta_window.new_data = &window_data;
 
           /* write to the file located in the info. */
-          err = fetch_ctx->info->textdelta(&delta_window,
-                                           fetch_ctx->info->textdelta_baton);
-          if (err)
-            {
-              return error_fetch(request, fetch_ctx, err);
-            }
+          SVN_ERR(file->txdelta(&delta_window, file->txdelta_baton));
         }
 
       if (APR_STATUS_IS_EOF(status))
         {
-          report_info_t *info = fetch_ctx->info;
-
-          if (fetch_ctx->delta_stream)
-            err = svn_error_trace(svn_stream_close(fetch_ctx->delta_stream));
+          if (fetch_ctx->result_stream)
+            SVN_ERR(svn_stream_close(fetch_ctx->result_stream));
           else
-            err = svn_error_trace(info->textdelta(NULL,
-                                                  info->textdelta_baton));
-          if (err)
-            {
-              return error_fetch(request, fetch_ctx, err);
-            }
+            SVN_ERR(file->txdelta(NULL, file->txdelta_baton));
+        }
 
-          err = close_updated_file(info, info->pool);
-          if (err)
-            {
-              return svn_error_trace(error_fetch(request, fetch_ctx, err));
-            }
+      /* Report EOF, EEAGAIN and other special errors to serf */
+      if (status)
+        return svn_ra_serf__wrap_err(status, NULL);
+    }
+}
+
+/* --------------------------------------------------------- */
 
-          fetch_ctx->done = TRUE;
 
-          fetch_ctx->done_item.data = fetch_ctx;
-          fetch_ctx->done_item.next = *fetch_ctx->done_list;
-          *fetch_ctx->done_list = &fetch_ctx->done_item;
+/* Implements svn_ra_serf__response_done_delegate_t */
+static svn_error_t *
+file_props_done(serf_request_t *request,
+                void *baton,
+                apr_pool_t *scratch_pool)
+{
+  file_baton_t *file = baton;
+  svn_ra_serf__handler_t *handler = file->propfind_handler;
 
-          /* We're done with our pool. */
-          svn_pool_destroy(info->pool);
+  if (handler->server_error)
+      return svn_error_trace(svn_ra_serf__server_error_create(handler,
+                                                              scratch_pool));
 
-          if (status)
-            return svn_ra_serf__wrap_err(status, NULL);
-        }
-      if (APR_STATUS_IS_EAGAIN(status))
-        {
-          return svn_ra_serf__wrap_err(status, NULL);
-        }
-    }
-  /* not reached */
+  if (handler->sline.code != 207)
+    return svn_error_trace(svn_ra_serf__unexpected_status(handler));
+
+  file->parent_dir->ctx->num_active_propfinds--;
+
+  file->fetch_props = FALSE;
+
+  if (file->fetch_file)
+    return SVN_NO_ERROR; /* Still processing file request */
+
+  /* Closing the file will automatically deliver the propfind props.
+   *
+   * Note that closing the directory may dispose the pool containing the
+   * handler, which is only a valid operation in this callback, as only
+   * after this callback our serf plumbing assumes the request is done. */
+
+  return svn_error_trace(close_file(file, scratch_pool));
 }
 
-/* Implements svn_ra_serf__response_handler_t */
 static svn_error_t *
-handle_stream(serf_request_t *request,
-              serf_bucket_t *response,
-              void *handler_baton,
-              apr_pool_t *pool)
+file_fetch_done(serf_request_t *request,
+                void *baton,
+                apr_pool_t *scratch_pool)
 {
-  report_fetch_t *fetch_ctx = handler_baton;
-  svn_error_t *err;
-  apr_status_t status;
+  fetch_ctx_t *fetch_ctx = baton;
+  file_baton_t *file = fetch_ctx->file;
+  svn_ra_serf__handler_t *handler = fetch_ctx->handler;
 
-  /* ### new field. make sure we didn't miss some initialization.  */
-  SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
+  if (handler->server_error)
+      return svn_error_trace(svn_ra_serf__server_error_create(handler,
+                                                              scratch_pool));
 
-  err = svn_ra_serf__error_on_status(fetch_ctx->handler->sline,
-                                     fetch_ctx->info->name,
-                                     fetch_ctx->handler->location);
-  if (err)
-    {
-      fetch_ctx->handler->done = TRUE;
+  if (handler->sline.code != 200)
+    return svn_error_trace(svn_ra_serf__unexpected_status(handler));
 
-      err = svn_error_compose_create(
-                  err,
-                  svn_ra_serf__handle_discard_body(request, response, NULL, pool));
+  file->parent_dir->ctx->num_active_fetches--;
 
-      return svn_error_trace(err);
-    }
+  file->fetch_file = FALSE;
 
-  while (1)
+  if (file->fetch_props)
+    return SVN_NO_ERROR; /* Still processing PROPFIND request */
+
+  /* Closing the file will automatically deliver the propfind props.
+   *
+   * Note that closing the directory may dispose the pool containing the
+   * handler, fetch_ctx, etc. which is only a valid operation in this
+   * callback, as only after this callback our serf plumbing assumes the
+   * request is done. */
+  return svn_error_trace(close_file(file, scratch_pool));
+}
+
+/* Initiates additional requests needed for a file when not in "send-all" mode.
+ */
+static svn_error_t *
+fetch_for_file(file_baton_t *file,
+               apr_pool_t *scratch_pool)
+{
+  report_context_t *ctx = file->parent_dir->ctx;
+  svn_ra_serf__connection_t *conn;
+  svn_ra_serf__handler_t *handler;
+
+  /* Open extra connections if we have enough requests to send. */
+  if (ctx->sess->num_conns < ctx->sess->max_connections)
+    SVN_ERR(open_connection_if_needed(ctx->sess, ctx->num_active_fetches +
+                                                 ctx->num_active_propfinds));
+
+  /* What connection should we go on? */
+  conn = get_best_connection(ctx);
+
+  /* Note that we (still) use conn for both requests.. Should we send
+     them out on different connections? */
+
+  if (file->fetch_file)
     {
-      const char *data;
-      apr_size_t len;
+      SVN_ERR(open_file_txdelta(file, scratch_pool));
 
-      status = serf_bucket_read(response, 8000, &data, &len);
-      if (SERF_BUCKET_READ_ERROR(status))
+      if (!ctx->text_deltas
+          || file->txdelta == svn_delta_noop_window_handler)
         {
-          return svn_ra_serf__wrap_err(status, NULL);
+          SVN_ERR(file->txdelta(NULL, file->txdelta_baton));
+          file->fetch_file = FALSE;
         }
 
-      fetch_ctx->read_size += len;
-
-      if (fetch_ctx->aborted_read)
+      if (file->fetch_file
+          && file->final_sha1_checksum
+          && ctx->sess->wc_callbacks->get_wc_contents)
         {
-          /* We haven't caught up to where we were before. */
-          if (fetch_ctx->read_size < fetch_ctx->aborted_read_size)
-            {
-              /* Eek.  What did the file shrink or something? */
-              if (APR_STATUS_IS_EOF(status))
-                {
-                  SVN_ERR_MALFUNCTION();
-                }
+          svn_error_t *err;
+          svn_stream_t *cached_contents = NULL;
 
-              /* Skip on to the next iteration of this loop. */
-              if (APR_STATUS_IS_EAGAIN(status))
-                {
-                  return svn_ra_serf__wrap_err(status, NULL);
-                }
-              continue;
-            }
-
-          /* Woo-hoo.  We're back. */
-          fetch_ctx->aborted_read = FALSE;
-
-          /* Increment data and len by the difference. */
-          data += fetch_ctx->read_size - fetch_ctx->aborted_read_size;
-          len += fetch_ctx->read_size - fetch_ctx->aborted_read_size;
-        }
-
-      if (len)
-        {
-          apr_size_t written_len;
-
-          written_len = len;
-
-          SVN_ERR(svn_stream_write(fetch_ctx->target_stream, data,
-                                   &written_len));
-        }
-
-      if (APR_STATUS_IS_EOF(status))
-        {
-          fetch_ctx->done = TRUE;
-        }
-
-      if (status)
-        {
-          return svn_ra_serf__wrap_err(status, NULL);
-        }
-    }
-  /* not reached */
-}
-
-/* Close the directory represented by DIR -- and any suitable parents
-   thereof -- if we are able to do so.  This is the case whenever:
-
-     - there are no remaining open items within the directory, and
-     - the directory's XML close tag has been processed (so we know
-       there are no more children to worry about in the future), and
-     - either:
-         - we aren't fetching properties for this directory, or
-         - we've already finished fetching those properties.
-*/
-static svn_error_t *
-maybe_close_dir_chain(report_dir_t *dir)
-{
-  report_dir_t *cur_dir = dir;
-
-  SVN_ERR(ensure_dir_opened(cur_dir));
-
-  while (cur_dir
-         && !cur_dir->ref_count
-         && cur_dir->tag_closed
-         && (!cur_dir->fetch_props || cur_dir->propfind_handler->done))
-    {
-      report_dir_t *parent = cur_dir->parent_dir;
-      report_context_t *report_context = cur_dir->report_context;
-      svn_boolean_t propfind_in_done_list = FALSE;
-      svn_ra_serf__list_t *done_list;
+          err = ctx->sess->wc_callbacks->get_wc_contents(
+                                                ctx->sess->wc_callback_baton,
+                                                &cached_contents,
+                                                file->final_sha1_checksum,
+                                                scratch_pool);
 
-      /* Make sure there are no references to this dir in the
-         active_dir_propfinds list.  If there are, don't close the
-         directory -- which would delete the pool from which the
-         relevant active_dir_propfinds list item is allocated -- and
-         of course don't crawl upward to check the parents for
-         a closure opportunity, either.  */
-      done_list = report_context->active_dir_propfinds;
-      while (done_list)
-        {
-          if (done_list->data == cur_dir)
+          if (err || !cached_contents)
+            svn_error_clear(err); /* ### Can we return some/most errors? */
+          else
             {
-              propfind_in_done_list = TRUE;
-              break;
+              /* ### For debugging purposes we could validate the md5 here,
+                     but our implementations in libsvn_client already do that
+                     for us... */
+              SVN_ERR(svn_txdelta_send_stream(cached_contents,
+                                              file->txdelta,
+                                              file->txdelta_baton,
+                                              NULL, scratch_pool));
+              SVN_ERR(svn_stream_close(cached_contents));
+              file->fetch_file = FALSE;
             }
-          done_list = done_list->next;
         }
-      if (propfind_in_done_list)
-        break;
-
-      SVN_ERR(close_dir(cur_dir));
-      if (parent)
-        {
-          parent->ref_count--;
-        }
-      else
-        {
-          report_context->closed_root = TRUE;
-        }
-      cur_dir = parent;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-/* Open the file associated with INFO for editing, pass along any
-   propchanges we've recorded for it, and then close the file. */
-static svn_error_t *
-handle_propchange_only(report_info_t *info,
-                       apr_pool_t *scratch_pool)
-{
-  SVN_ERR(open_updated_file(info, FALSE, scratch_pool));
-  SVN_ERR(close_updated_file(info, scratch_pool));
-
-  /* We're done with our pool. */
-  svn_pool_destroy(info->pool);
-
-  info->dir->ref_count--;
-
-  /* See if the parent directory of this file (and perhaps even
-     parents of that) can be closed now.  */
-  SVN_ERR(maybe_close_dir_chain(info->dir));
-
-  return SVN_NO_ERROR;
-}
-
-/* "Fetch" a file whose contents were made available via the
-   get_wc_contents() callback (as opposed to requiring a GET to the
-   server), and feed the information through the associated update
-   editor.  In editor-speak, this will add/open the file, transmit any
-   property changes, handle the contents, and then close the file.  */
-static svn_error_t *
-handle_local_content(report_info_t *info,
-                     apr_pool_t *scratch_pool)
-{
-  SVN_ERR(svn_txdelta_send_stream(info->cached_contents, info->textdelta,
-                                  info->textdelta_baton, NULL, scratch_pool));
-  SVN_ERR(svn_stream_close(info->cached_contents));
-  info->cached_contents = NULL;
-  SVN_ERR(close_updated_file(info, scratch_pool));
-
-  /* We're done with our pool. */
-  svn_pool_destroy(info->pool);
-
-  info->dir->ref_count--;
-
-  /* See if the parent directory of this fetched item (and
-     perhaps even parents of that) can be closed now. */
-  SVN_ERR(maybe_close_dir_chain(info->dir));
-
-  return SVN_NO_ERROR;
-}
-
-/* --------------------------------------------------------- */
-
-static svn_error_t *
-fetch_file(report_context_t *ctx, report_info_t *info)
-{
-  svn_ra_serf__connection_t *conn;
-  svn_ra_serf__handler_t *handler;
 
-  /* What connection should we go on? */
-  conn = get_best_connection(ctx);
-
-  /* If needed, create the PROPFIND to retrieve the file's properties. */
-  info->propfind_handler = NULL;
-  if (info->fetch_props)
-    {
-      SVN_ERR(svn_ra_serf__deliver_props(&info->propfind_handler, info->props,
-                                         ctx->sess, conn, info->url,
-                                         ctx->target_rev, "0", all_props,
-                                         &ctx->done_propfinds,
-                                         info->dir->pool));
-      SVN_ERR_ASSERT(info->propfind_handler);
-
-      /* Create a serf request for the PROPFIND.  */
-      svn_ra_serf__request_create(info->propfind_handler);
-
-      ctx->num_active_propfinds++;
-    }
-
-  /* If we've been asked to fetch the file or it's an add, do so.
-   * Otherwise, handle the case where only the properties changed.
-   */
-  if (info->fetch_file && ctx->text_deltas)
-    {
-      svn_stream_t *contents = NULL;
-
-      /* Open the file for editing. */
-      SVN_ERR(open_updated_file(info, FALSE, info->pool));
-
-      if (info->textdelta == svn_delta_noop_window_handler)
+      if (file->fetch_file)
         {
-          /* There is nobody looking for an actual stream.
+          fetch_ctx_t *fetch_ctx;
 
-             Just report an empty stream instead of fetching
-             to be ingored data */
-          info->cached_contents = svn_stream_empty(info->pool);
-        }
-      else if (ctx->sess->wc_callbacks->get_wc_contents
-               && info->final_sha1_checksum)
-        {
-          svn_error_t *err = NULL;
-          svn_checksum_t *checksum = NULL;
-
-          /* Parse the optional SHA1 checksum (1.7+) */
-          err = svn_checksum_parse_hex(&checksum, svn_checksum_sha1,
-                                       info->final_sha1_checksum,
-                                       info->pool);
+          /* Let's fetch the file with a GET request... */
+          SVN_ERR_ASSERT(file->url && file->repos_relpath);
 
-          /* Okay so far?  Let's try to get a stream on some readily
-             available matching content. */
-          if (!err && checksum)
-            {
-              err = ctx->sess->wc_callbacks->get_wc_contents(
-                        ctx->sess->wc_callback_baton, &contents,
-                        checksum, info->pool);
-
-              if (! err)
-                info->cached_contents = contents;
-            }
+          /* Otherwise, we use a GET request for the file's contents. */
 
-          if (err)
-            {
-              /* Meh.  Maybe we'll care one day why we're in an
-                 errorful state, but this codepath is optional.  */
-              svn_error_clear(err);
-            }
-        }
+          fetch_ctx = apr_pcalloc(file->pool, sizeof(*fetch_ctx));
+          fetch_ctx->file = file;
+          fetch_ctx->sess = ctx->sess;
+          fetch_ctx->conn = conn;
 
-      /* If the working copy can provide cached contents for this
-         file, we don't have to fetch them from the server. */
-      if (info->cached_contents)
-        {
-          /* If we'll be doing a PROPFIND for this file... */
-          if (info->propfind_handler)
+          /* Can we somehow get away with just obtaining a DIFF? */
+          if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->sess))
             {
-              /* ... then we'll just leave ourselves a little "todo"
-                 about that fact (and we'll deal with the file content
-                 stuff later, after we've handled that PROPFIND
-                 response. */
-              svn_ra_serf__list_t *list_item;
+              /* If this file is switched vs the editor root we should provide
+                 its real url instead of the one calculated from the session root.
+              */
+              if (SVN_IS_VALID_REVNUM(file->base_rev))
+                {
+                  fetch_ctx->delta_base = apr_psprintf(file->pool, "%s/%ld/%s",
+                                                       ctx->sess->rev_root_stub,
+                                                       file->base_rev,
+                                                       svn_path_uri_encode(
+                                                          file->repos_relpath,
+                                                          file->pool));
+                }
+              else if (file->copyfrom_path)
+                {
+                  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(file->copyfrom_rev));
 
-              list_item = apr_pcalloc(info->dir->pool, sizeof(*list_item));
-              list_item->data = info;
-              list_item->next = ctx->file_propchanges_only;
-              ctx->file_propchanges_only = list_item;
+                  fetch_ctx->delta_base = apr_psprintf(file->pool, "%s/%ld/%s",
+                                                       ctx->sess->rev_root_stub,
+                                                       file->copyfrom_rev,
+                                                       svn_path_uri_encode(
+                                                          file->copyfrom_path+1,
+                                                          file->pool));
+                }
             }
-          else
+          else if (ctx->sess->wc_callbacks->get_wc_prop)
             {
-              /* Otherwise, if we've no PROPFIND to do, we might as
-                 well take care of those locally accessible file
-                 contents now. */
-              SVN_ERR(handle_local_content(info, info->pool));
+              /* If we have a WC, we might be able to dive all the way into the WC
+              * to get the previous URL so we can do a differential GET with the
+              * base URL.
+              */
+              const svn_string_t *value = NULL;
+              SVN_ERR(ctx->sess->wc_callbacks->get_wc_prop(
+                                                ctx->sess->wc_callback_baton,
+                                                file->relpath,
+                                                SVN_RA_SERF__WC_CHECKED_IN_URL,
+                                                &value, scratch_pool));
+
+              fetch_ctx->delta_base = value
+                                        ? apr_pstrdup(file->pool, value->data)
+                                        : NULL;
             }
-        }
-      else
-        {
-          /* Otherwise, we use a GET request for the file's contents. */
-          report_fetch_t *fetch_ctx;
-
-          fetch_ctx = apr_pcalloc(info->dir->pool, sizeof(*fetch_ctx));
-          fetch_ctx->info = info;
-          fetch_ctx->done_list = &ctx->done_fetches;
-          fetch_ctx->sess = ctx->sess;
-          fetch_ctx->conn = conn;
 
-          handler = svn_ra_serf__create_handler(info->dir->pool);
+          handler = svn_ra_serf__create_handler(file->pool);
 
           handler->method = "GET";
-          handler->path = fetch_ctx->info->url;
+          handler->path = file->url;
 
           handler->conn = conn;
           handler->session = ctx->sess;
@@ -1585,6 +1337,9 @@ fetch_file(report_context_t *ctx, report
           handler->response_error = cancel_fetch;
           handler->response_error_baton = fetch_ctx;
 
+          handler->done_delegate = file_fetch_done;
+          handler->done_delegate_baton = fetch_ctx;
+
           fetch_ctx->handler = handler;
 
           svn_ra_serf__request_create(handler);
@@ -1592,991 +1347,573 @@ fetch_file(report_context_t *ctx, report
           ctx->num_active_fetches++;
         }
     }
-  else if (info->propfind_handler)
-    {
-      svn_ra_serf__list_t *list_item;
-
-      list_item = apr_pcalloc(info->dir->pool, sizeof(*list_item));
-      list_item->data = info;
-      list_item->next = ctx->file_propchanges_only;
-      ctx->file_propchanges_only = list_item;
-    }
-  else
-    {
-      /* No propfind or GET request.  Just handle the prop changes now. */
-      SVN_ERR(handle_propchange_only(info, info->pool));
-    }
-
-  return SVN_NO_ERROR;
-}
-
-
-/** XML callbacks for our update-report response parsing */
-
-static svn_error_t *
-start_report(svn_ra_serf__xml_parser_t *parser,
-             svn_ra_serf__dav_props_t name,
-             const char **attrs,
-             apr_pool_t *scratch_pool)
-{
-  report_context_t *ctx = parser->user_data;
-  report_state_e state;
-
-  state = parser->state->current_state;
-
-  if (state == NONE && strcmp(name.name, "update-report") == 0)
-    {
-      const char *val;
-
-      val = svn_xml_get_attr_value("inline-props", attrs);
-      if (val && (strcmp(val, "true") == 0))
-        ctx->add_props_included = TRUE;
-
-      val = svn_xml_get_attr_value("send-all", attrs);
-      if (val && (strcmp(val, "true") == 0))
-        {
-          ctx->send_all_mode = TRUE;
 
-          /* All properties are included in send-all mode. */
-          ctx->add_props_included = TRUE;
-        }
-    }
-  else if (state == NONE && strcmp(name.name, "target-revision") == 0)
+  /* If needed, create the PROPFIND to retrieve the file's properties. */
+  if (file->fetch_props)
     {
-      const char *rev;
+      file->propfind_props = apr_hash_make(file->pool);
+      SVN_ERR(svn_ra_serf__deliver_props(&file->propfind_handler,
+                                         file->propfind_props,
+                                         ctx->sess, conn, file->url,
+                                         ctx->target_rev, "0", all_props,
+                                         NULL, file->pool));
+      SVN_ERR_ASSERT(file->propfind_handler);
 
-      rev = svn_xml_get_attr_value("rev", attrs);
+      file->propfind_handler->done_delegate = file_props_done;
+      file->propfind_handler->done_delegate_baton = file;
 
-      if (!rev)
-        {
-          return svn_error_create(
-            SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-            _("Missing revision attr in target-revision element"));
-        }
+      /* Create a serf request for the PROPFIND.  */
+      svn_ra_serf__request_create(file->propfind_handler);
 
-      SVN_ERR(ctx->update_editor->set_target_revision(ctx->update_baton,
-                                                      SVN_STR_TO_REV(rev),
-                                                      ctx->sess->pool));
+      ctx->num_active_propfinds++;
     }
-  else if (state == NONE && strcmp(name.name, "open-directory") == 0)
-    {
-      const char *rev;
-      report_info_t *info;
-
-      rev = svn_xml_get_attr_value("rev", attrs);
-
-      if (!rev)
-        {
-          return svn_error_create(
-            SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-            _("Missing revision attr in open-directory element"));
-        }
 
-      info = push_state(parser, ctx, OPEN_DIR);
-
-      info->base_rev = SVN_STR_TO_REV(rev);
-      info->dir->base_rev = info->base_rev;
-      info->fetch_props = TRUE;
-
-      info->dir->base_name = "";
-      info->dir->name = "";
+  if (file->fetch_props || file->fetch_file)
+      return SVN_NO_ERROR;
 
-      info->base_name = info->dir->base_name;
-      info->name = info->dir->name;
 
-      info->dir->repos_relpath = svn_hash_gets(ctx->switched_paths, "");
+  /* Somehow we are done; probably via the local cache.
+     Close the file and release memory, etc. */
 
-      if (!info->dir->repos_relpath)
-        SVN_ERR(svn_ra_serf__get_relative_path(&info->dir->repos_relpath,
-                                               ctx->sess->session_url.path,
-                                               ctx->sess, ctx->conn,
-                                               info->dir->pool));
-    }
-  else if (state == NONE)
-    {
-      /* do nothing as we haven't seen our valid start tag yet. */
-    }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "open-directory") == 0)
-    {
-      const char *rev, *dirname;
-      report_dir_t *dir;
-      report_info_t *info;
-
-      rev = svn_xml_get_attr_value("rev", attrs);
-
-      if (!rev)
-        {
-          return svn_error_create(
-            SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-            _("Missing revision attr in open-directory element"));
-        }
+  return svn_error_trace(close_file(file, scratch_pool));
+}
 
-      dirname = svn_xml_get_attr_value("name", attrs);
+/* Implements svn_ra_serf__response_done_delegate_t */
+static svn_error_t *
+dir_props_done(serf_request_t *request,
+               void *baton,
+               apr_pool_t *scratch_pool)
+{
+  dir_baton_t *dir = baton;
+  svn_ra_serf__handler_t *handler = dir->propfind_handler;
 
-      if (!dirname)
-        {
-          return svn_error_create(
-            SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-            _("Missing name attr in open-directory element"));
-        }
+  if (handler->server_error)
+    return svn_ra_serf__server_error_create(handler, scratch_pool);
 
-      info = push_state(parser, ctx, OPEN_DIR);
+  if (handler->sline.code != 207)
+    return svn_error_trace(svn_ra_serf__unexpected_status(handler));
 
-      dir = info->dir;
+  dir->ctx->num_active_propfinds--;
 
-      info->base_rev = SVN_STR_TO_REV(rev);
-      dir->base_rev = info->base_rev;
+  /* Closing the directory will automatically deliver the propfind props.
+   *
+   * Note that closing the directory may dispose the pool containing the
+   * handler, which is only a valid operation in this callback, as after
+   * this callback serf assumes the request is done. */
 
-      info->fetch_props = FALSE;
+  return svn_error_trace(maybe_close_dir(dir));
+}
 
-      dir->base_name = apr_pstrdup(dir->pool, dirname);
-      info->base_name = dir->base_name;
+/* Initiates additional requests needed for a directory when not in "send-all"
+ * mode */
+static svn_error_t *
+fetch_for_dir(dir_baton_t *dir,
+              apr_pool_t *scratch)
+{
+  report_context_t *ctx = dir->ctx;
+  svn_ra_serf__connection_t *conn;
 
-      /* Expand our name. */
-      dir->name = svn_relpath_join(dir->parent_dir->name, dir->base_name,
-                                   dir->pool);
-      info->name = dir->name;
+  /* Open extra connections if we have enough requests to send. */
+  if (ctx->sess->num_conns < ctx->sess->max_connections)
+    SVN_ERR(open_connection_if_needed(ctx->sess, ctx->num_active_fetches +
+                                                 ctx->num_active_propfinds));
 
-      dir->repos_relpath = svn_hash_gets(ctx->switched_paths, dir->name);
+  /* What connection should we go on? */
+  conn = get_best_connection(ctx);
 
-      if (!dir->repos_relpath)
-        dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath,
-                                               dir->base_name, dir->pool);
-    }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "add-directory") == 0)
+  /* If needed, create the PROPFIND to retrieve the file's properties. */
+  if (dir->fetch_props)
     {
-      const char *dir_name, *cf, *cr;
-      report_dir_t *dir;
-      report_info_t *info;
-
-      dir_name = svn_xml_get_attr_value("name", attrs);
-      if (!dir_name)
-        {
-          return svn_error_create(
-            SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-            _("Missing name attr in add-directory element"));
-        }
-      cf = svn_xml_get_attr_value("copyfrom-path", attrs);
-      cr = svn_xml_get_attr_value("copyfrom-rev", attrs);
-
-      info = push_state(parser, ctx, ADD_DIR);
-
-      dir = info->dir;
-
-      dir->base_name = apr_pstrdup(dir->pool, dir_name);
-      info->base_name = dir->base_name;
-
-      /* Expand our name. */
-      dir->name = svn_relpath_join(dir->parent_dir->name, dir->base_name,
-                                   dir->pool);
-      info->name = dir->name;
-
-      info->copyfrom_path = cf ? apr_pstrdup(info->pool, cf) : NULL;
-      info->copyfrom_rev = cr ? SVN_STR_TO_REV(cr) : SVN_INVALID_REVNUM;
+      dir->propfind_props = apr_hash_make(dir->pool);
+      SVN_ERR(svn_ra_serf__deliver_props(&dir->propfind_handler,
+                                         dir->propfind_props,
+                                         ctx->sess, conn, dir->url,
+                                         ctx->target_rev, "0", all_props,
+                                         NULL,
+                                         dir->pool));
+      SVN_ERR_ASSERT(dir->propfind_handler);
 
-      /* Mark that we don't have a base. */
-      info->base_rev = SVN_INVALID_REVNUM;
-      dir->base_rev = info->base_rev;
+      dir->propfind_handler->done_delegate = dir_props_done;
+      dir->propfind_handler->done_delegate_baton = dir;
 
-      /* If the server isn't included properties for added items,
-         we'll need to fetch them ourselves. */
-      if (! ctx->add_props_included)
-        dir->fetch_props = TRUE;
+      /* Create a serf request for the PROPFIND.  */
+      svn_ra_serf__request_create(dir->propfind_handler);
 
-      dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath,
-                                            dir->base_name, dir->pool);
+      ctx->num_active_propfinds++;
     }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "open-file") == 0)
-    {
-      const char *file_name, *rev;
-      report_info_t *info;
-
-      file_name = svn_xml_get_attr_value("name", attrs);
-
-      if (!file_name)
-        {
-          return svn_error_create(
-            SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-            _("Missing name attr in open-file element"));
-        }
-
-      rev = svn_xml_get_attr_value("rev", attrs);
+  else
+    SVN_ERR_MALFUNCTION();
 
-      if (!rev)
-        {
-          return svn_error_create(
-            SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-            _("Missing revision attr in open-file element"));
-        }
+  return SVN_NO_ERROR;
+}
 
-      info = push_state(parser, ctx, OPEN_FILE);
+
+/** XML callbacks for our update-report response parsing */
 
-      info->base_rev = SVN_STR_TO_REV(rev);
-      info->fetch_props = FALSE;
+/* Conforms to svn_ra_serf__xml_opened_t  */
+static svn_error_t *
+update_opened(svn_ra_serf__xml_estate_t *xes,
+              void *baton,
+              int entered_state,
+              const svn_ra_serf__dav_props_t *tag,
+              apr_pool_t *scratch_pool)
+{
+  report_context_t *ctx = baton;
+  apr_hash_t *attrs;
 
-      info->base_name = apr_pstrdup(info->pool, file_name);
-      info->name = NULL;
-    }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "add-file") == 0)
+  switch (entered_state)
     {
-      const char *file_name, *cf, *cr;
-      report_info_t *info;
-
-      file_name = svn_xml_get_attr_value("name", attrs);
-      cf = svn_xml_get_attr_value("copyfrom-path", attrs);
-      cr = svn_xml_get_attr_value("copyfrom-rev", attrs);
-
-      if (!file_name)
+      case UPDATE_REPORT:
         {
-          return svn_error_create(
-            SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-            _("Missing name attr in add-file element"));
-        }
-
-      info = push_state(parser, ctx, ADD_FILE);
+          const char *val;
 
-      info->base_rev = SVN_INVALID_REVNUM;
+          attrs = svn_ra_serf__xml_gather_since(xes, UPDATE_REPORT);
+          val = svn_hash_gets(attrs, "inline-props");
 
-      /* If the server isn't in "send-all" mode, we should expect to
-         fetch contents for added files. */
-      if (! ctx->send_all_mode)
-        info->fetch_file = TRUE;
+          if (val && (strcmp(val, "true") == 0))
+            ctx->add_props_included = TRUE;
 
-      /* If the server isn't included properties for added items,
-         we'll need to fetch them ourselves. */
-      if (! ctx->add_props_included)
-        info->fetch_props = TRUE;
+          val = svn_hash_gets(attrs, "send-all");
 
-      info->base_name = apr_pstrdup(info->pool, file_name);
-      info->name = NULL;
-
-      info->copyfrom_path = cf ? apr_pstrdup(info->pool, cf) : NULL;
-      info->copyfrom_rev = cr ? SVN_STR_TO_REV(cr) : SVN_INVALID_REVNUM;
-
-      info->final_sha1_checksum =
-        svn_xml_get_attr_value("sha1-checksum", attrs);
-      if (info->final_sha1_checksum)
-        info->final_sha1_checksum = apr_pstrdup(info->pool,
-                                                info->final_sha1_checksum);
-    }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "delete-entry") == 0)
-    {
-      const char *file_name;
-      const char *rev_str;
-      report_info_t *info;
-      apr_pool_t *tmppool;
-      const char *full_path;
-      svn_revnum_t delete_rev = SVN_INVALID_REVNUM;
-
-      file_name = svn_xml_get_attr_value("name", attrs);
+          if (val && (strcmp(val, "true") == 0))
+            {
+              ctx->send_all_mode = TRUE;
 
-      if (!file_name)
-        {
-          return svn_error_create(
-            SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-            _("Missing name attr in delete-entry element"));
+              /* All properties are included in send-all mode. */
+              ctx->add_props_included = TRUE;
+            }
         }
+        break;
 
-      rev_str = svn_xml_get_attr_value("rev", attrs);
-      if (rev_str) /* Not available on older repositories! */
-        delete_rev = SVN_STR_TO_REV(rev_str);
-
-      info = parser->state->private;
-
-      SVN_ERR(ensure_dir_opened(info->dir));
-
-      tmppool = svn_pool_create(info->dir->dir_baton_pool);
-
-      full_path = svn_relpath_join(info->dir->name, file_name, tmppool);
-
-      SVN_ERR(ctx->update_editor->delete_entry(full_path,
-                                               delete_rev,
-                                               info->dir->dir_baton,
-                                               tmppool));
-
-      svn_pool_destroy(tmppool);
-    }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "absent-directory") == 0)
-    {
-      const char *file_name;
-      report_info_t *info;
-
-      file_name = svn_xml_get_attr_value("name", attrs);
-
-      if (!file_name)
+      case OPEN_DIR:
+      case ADD_DIR:
         {
-          return svn_error_create(
-            SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-            _("Missing name attr in absent-directory element"));
-        }
+          dir_baton_t *dir;
+          attrs = svn_ra_serf__xml_gather_since(xes, entered_state);
 
-      info = parser->state->private;
+          const char *name = svn_hash_gets(attrs, "name");
+          if (!name)
+            name = "";
 
-      SVN_ERR(ensure_dir_opened(info->dir));
+          SVN_ERR(create_dir_baton(&dir, ctx, name, scratch_pool));
 
-      SVN_ERR(ctx->update_editor->absent_directory(
-                                        svn_relpath_join(info->name, file_name,
-                                                         info->dir->pool),
-                                        info->dir->dir_baton,
-                                        info->dir->pool));
-    }
-  else if ((state == OPEN_DIR || state == ADD_DIR) &&
-           strcmp(name.name, "absent-file") == 0)
-    {
-      const char *file_name;
-      report_info_t *info;
-
-      file_name = svn_xml_get_attr_value("name", attrs);
+          if (entered_state == OPEN_DIR)
+            {
+              apr_int64_t base_rev;
 
-      if (!file_name)
-        {
-          return svn_error_create(
-            SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-            _("Missing name attr in absent-file element"));
-        }
+              SVN_ERR(svn_cstring_atoi64(&base_rev,
+                                         svn_hash_gets(attrs, "rev")));
+              dir->base_rev = (svn_revnum_t)base_rev;
+            }
+          else
+            {
+              dir->copyfrom_path = svn_hash_gets(attrs, "copyfrom-path");
 
-      info = parser->state->private;
+              if (dir->copyfrom_path)
+                {
+                  apr_int64_t copyfrom_rev;
+                  const char *copyfrom_rev_str;
+                  dir->copyfrom_path = svn_fspath__canonicalize(
+                                                        dir->copyfrom_path,
+                                                        dir->pool);
+
+                  copyfrom_rev_str = svn_hash_gets(attrs, "copyfrom-rev");
+
+                  if (!copyfrom_rev_str)
+                    return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND,
+                                             NULL,
+                                            _("Missing '%s' attribute"),
+                                            "copyfrom-rev");
 
-      SVN_ERR(ensure_dir_opened(info->dir));
+                  SVN_ERR(svn_cstring_atoi64(&copyfrom_rev, copyfrom_rev_str));
 
-      SVN_ERR(ctx->update_editor->absent_file(
-                                        svn_relpath_join(info->name, file_name,
-                                                         info->dir->pool),
-                                        info->dir->dir_baton,
-                                        info->dir->pool));
-    }
-  else if (state == OPEN_DIR || state == ADD_DIR)
-    {
-      report_info_t *info;
+                  dir->copyfrom_rev = (svn_revnum_t)copyfrom_rev;
+                }
 
-      if (strcmp(name.name, "checked-in") == 0)
-        {
-          info = push_state(parser, ctx, IGNORE_PROP_NAME);
-          info->prop_ns = name.xmlns;
-          info->prop_name = apr_pstrdup(parser->state->pool, name.name);
-          info->prop_encoding = NULL;
-          svn_stringbuf_setempty(info->prop_value);
+              if (! ctx->add_props_included)
+                dir->fetch_props = TRUE;
+            }
         }
-      else if (strcmp(name.name, "set-prop") == 0 ||
-               strcmp(name.name, "remove-prop") == 0)
+        break;
+      case OPEN_FILE:
+      case ADD_FILE:
         {
-          const char *full_prop_name;
-          const char *colon;
+          file_baton_t *file;
 
-          info = push_state(parser, ctx, PROP);
+          attrs = svn_ra_serf__xml_gather_since(xes, entered_state);
 
-          full_prop_name = svn_xml_get_attr_value("name", attrs);
-          if (!full_prop_name)
-            {
-              return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
-                                       _("Missing name attr in %s element"),
-                                       name.name);
-            }
+          SVN_ERR(create_file_baton(&file, ctx, svn_hash_gets(attrs, "name"),
+                                    scratch_pool));
 
-          colon = strchr(full_prop_name, ':');

[... 1446 lines stripped ...]