You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2012/08/16 12:18:03 UTC

svn commit: r1373783 [21/50] - in /subversion/branches/compressed-pristines: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/client-side/svn-push/ contrib/client-side/svnmerge/ cont...

Modified: subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/update.c?rev=1373783&r1=1373782&r2=1373783&view=diff
==============================================================================
--- subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/update.c (original)
+++ subversion/branches/compressed-pristines/subversion/libsvn_ra_serf/update.c Thu Aug 16 10:17:48 2012
@@ -126,17 +126,10 @@ typedef struct report_dir_t
   /* Our base revision - SVN_INVALID_REVNUM if we're adding this dir. */
   svn_revnum_t base_rev;
 
-  /* The target revision we're retrieving. */
-  svn_revnum_t target_rev;
-
   /* controlling dir baton - this is only created in open_dir() */
   void *dir_baton;
   apr_pool_t *dir_baton_pool;
 
-  /* Our master update editor and baton. */
-  const svn_delta_editor_t *update_editor;
-  void *update_baton;
-
   /* How many references to this directory do we still have open? */
   apr_size_t ref_count;
 
@@ -150,7 +143,7 @@ typedef struct report_dir_t
   apr_hash_t *removed_props;
 
   /* The propfind request for our current directory */
-  svn_ra_serf__propfind_context_t *propfind;
+  svn_ra_serf__handler_t *propfind_handler;
 
   /* Has the server told us to fetch the dir props? */
   svn_boolean_t fetch_props;
@@ -200,9 +193,6 @@ typedef struct report_info_t
   /* Our base revision - SVN_INVALID_REVNUM if we're adding this file. */
   svn_revnum_t base_rev;
 
-  /* The target revision we're retrieving. */
-  svn_revnum_t target_rev;
-
   /* our delta base, if present (NULL if we're adding the file) */
   const char *delta_base;
 
@@ -213,7 +203,7 @@ typedef struct report_info_t
   svn_revnum_t copyfrom_rev;
 
   /* The propfind request for our current file (if present) */
-  svn_ra_serf__propfind_context_t *propfind;
+  svn_ra_serf__handler_t *propfind_handler;
 
   /* Has the server told us to fetch the file props? */
   svn_boolean_t fetch_props;
@@ -230,13 +220,17 @@ typedef struct report_info_t
   /* controlling file_baton and textdelta handler */
   void *file_baton;
   const char *base_checksum;
-  const char *final_sha1_checksum; /* ### currently unused */
+  const char *final_sha1_checksum;
   svn_txdelta_window_handler_t textdelta;
   void *textdelta_baton;
 
   /* 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;
+
   /* temporary property for this file which is currently being parsed
    * It will eventually be stored in our parent directory's property hash.
    */
@@ -252,6 +246,9 @@ typedef struct report_info_t
  */
 typedef struct report_fetch_t {
 
+  /* The handler representing this particular fetch.  */
+  svn_ra_serf__handler_t *handler;
+
   /* The session we should use to fetch the file. */
   svn_ra_serf__session_t *sess;
 
@@ -349,7 +346,7 @@ struct report_context_t {
   /* number of pending PROPFIND requests */
   unsigned int active_propfinds;
 
-  /* completed PROPFIND requests (contains propfind_context_t) */
+  /* completed PROPFIND requests (contains svn_ra_serf__handler_t) */
   svn_ra_serf__list_t *done_propfinds;
 
   /* list of files that only have prop changes (contains report_info_t) */
@@ -407,9 +404,6 @@ push_state(svn_ra_serf__xml_parser_t *pa
       new_info->props = new_info->dir->props;
       new_info->dir->removed_props = apr_hash_make(new_info->pool);
 
-      /* Point to the update_editor */
-      new_info->dir->update_editor = ctx->update_editor;
-      new_info->dir->update_baton = ctx->update_baton;
       new_info->dir->report_context = ctx;
 
       if (info)
@@ -467,13 +461,9 @@ set_file_props(void *baton,
                apr_pool_t *scratch_pool)
 {
   report_info_t *info = baton;
-  const svn_delta_editor_t *editor = info->dir->update_editor;
+  const svn_delta_editor_t *editor = info->dir->report_context->update_editor;
   const char *prop_name;
 
-  if (strcmp(name, "md5-checksum") == 0
-      && strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0)
-    info->final_checksum = apr_pstrdup(info->pool, val->data);
-
   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,
@@ -492,7 +482,7 @@ set_dir_props(void *baton,
               apr_pool_t *scratch_pool)
 {
   report_dir_t *dir = baton;
-  const svn_delta_editor_t *editor = dir->update_editor;
+  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);
@@ -513,7 +503,7 @@ remove_file_props(void *baton,
                   apr_pool_t *scratch_pool)
 {
   report_info_t *info = baton;
-  const svn_delta_editor_t *editor = info->dir->update_editor;
+  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);
@@ -534,7 +524,7 @@ remove_dir_props(void *baton,
                  apr_pool_t *scratch_pool)
 {
   report_dir_t *dir = baton;
-  const svn_delta_editor_t *editor = dir->update_editor;
+  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);
@@ -552,6 +542,8 @@ remove_dir_props(void *baton,
 static svn_error_t*
 open_dir(report_dir_t *dir)
 {
+  report_context_t *ctx = dir->report_context;
+
   /* if we're already open, return now */
   if (dir->dir_baton)
     {
@@ -562,16 +554,16 @@ open_dir(report_dir_t *dir)
     {
       dir->dir_baton_pool = svn_pool_create(dir->pool);
 
-      if (dir->report_context->destination &&
-          dir->report_context->sess->wc_callbacks->invalidate_wc_props)
+      if (ctx->destination
+          && ctx->sess->wc_callbacks->invalidate_wc_props)
         {
-          SVN_ERR(dir->report_context->sess->wc_callbacks->invalidate_wc_props(
-                      dir->report_context->sess->wc_callback_baton,
-                      dir->report_context->update_target,
+          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_ERR(dir->update_editor->open_root(dir->update_baton, dir->base_rev,
+      SVN_ERR(ctx->update_editor->open_root(ctx->update_baton, dir->base_rev,
                                             dir->dir_baton_pool,
                                             &dir->dir_baton));
     }
@@ -583,7 +575,7 @@ open_dir(report_dir_t *dir)
 
       if (SVN_IS_VALID_REVNUM(dir->base_rev))
         {
-          SVN_ERR(dir->update_editor->open_directory(dir->name,
+          SVN_ERR(ctx->update_editor->open_directory(dir->name,
                                                      dir->parent_dir->dir_baton,
                                                      dir->base_rev,
                                                      dir->dir_baton_pool,
@@ -591,7 +583,7 @@ open_dir(report_dir_t *dir)
         }
       else
         {
-          SVN_ERR(dir->update_editor->add_directory(dir->name,
+          SVN_ERR(ctx->update_editor->add_directory(dir->name,
                                                     dir->parent_dir->dir_baton,
                                                     NULL, SVN_INVALID_REVNUM,
                                                     dir->dir_baton_pool,
@@ -625,12 +617,13 @@ close_dir(report_dir_t *dir)
   if (dir->fetch_props)
     {
       SVN_ERR(svn_ra_serf__walk_all_props(dir->props, dir->url,
-                                          dir->target_rev,
+                                          dir->report_context->target_rev,
                                           set_dir_props, dir,
                                           scratch_pool));
     }
 
-  SVN_ERR(dir->update_editor->close_directory(dir->dir_baton, scratch_pool));
+  SVN_ERR(dir->report_context->update_editor->close_directory(
+            dir->dir_baton, scratch_pool));
 
   /* remove us from our parent's children list */
   if (dir->parent_dir)
@@ -694,7 +687,7 @@ check_lock(report_info_t *info)
   const char *lock_val;
 
   lock_val = svn_ra_serf__get_ver_prop(info->props, info->url,
-                                       info->target_rev,
+                                       info->dir->report_context->target_rev,
                                        "DAV:", "lockdiscovery");
 
   if (lock_val)
@@ -733,7 +726,7 @@ headers_fetch(serf_bucket_t *headers,
       serf_bucket_headers_setn(headers, "Accept-Encoding",
                                "svndiff1;q=0.9,svndiff;q=0.8");
     }
-  else if (fetch_ctx->conn->using_compression)
+  else if (fetch_ctx->sess->using_compression)
     {
       serf_bucket_headers_setn(headers, "Accept-Encoding", "gzip");
     }
@@ -801,6 +794,105 @@ error_fetch(serf_request_t *request,
   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
+   (or if FORCE_APPLY_TEXTDELTA is set).
+
+   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,
+                  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(open_dir(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);
+    }
+
+  /* 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));
+    }
+
+  /* Check for lock information. */
+  if (info->lock_token)
+    check_lock(info);
+
+  /* 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)
+    {
+      SVN_ERR(svn_ra_serf__walk_all_props(info->props,
+                                          info->url,
+                                          ctx->target_rev,
+                                          set_file_props, info,
+                                          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));
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Close the file associated with INFO->file_baton, and cleanup other
+   bits of that structure managed by open_updated_file(). */
+static svn_error_t *
+close_updated_file(report_info_t *info,
+                   apr_pool_t *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));
+
+  /* We're done with our editor pool. */
+  svn_pool_destroy(info->editor_pool);
+
+  return SVN_NO_ERROR;
+}
+
 /* Implements svn_ra_serf__response_handler_t */
 static svn_error_t *
 handle_fetch(serf_request_t *request,
@@ -813,7 +905,9 @@ handle_fetch(serf_request_t *request,
   apr_status_t status;
   report_fetch_t *fetch_ctx = handler_baton;
   svn_error_t *err;
-  serf_status_line sl;
+
+  /* ### new field. make sure we didn't miss some initialization.  */
+  SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
 
   if (fetch_ctx->read_headers == FALSE)
     {
@@ -825,50 +919,8 @@ handle_fetch(serf_request_t *request,
       val = serf_bucket_headers_get(hdrs, "Content-Type");
       info = fetch_ctx->info;
 
-      err = open_dir(info->dir);
-      if (err)
-        {
-          return error_fetch(request, fetch_ctx, err);
-        }
-
-      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);
-        }
-
-      if (SVN_IS_VALID_REVNUM(info->base_rev))
-        {
-          err = info->dir->update_editor->open_file(info->name,
-                                                    info->dir->dir_baton,
-                                                    info->base_rev,
-                                                    info->editor_pool,
-                                                    &info->file_baton);
-        }
-      else
-        {
-          err = info->dir->update_editor->add_file(info->name,
-                                                   info->dir->dir_baton,
-                                                   info->copyfrom_path,
-                                                   info->copyfrom_rev,
-                                                   info->editor_pool,
-                                                   &info->file_baton);
-        }
-
-      if (err)
-        {
-          return error_fetch(request, fetch_ctx, err);
-        }
-
-      err = info->dir->update_editor->apply_textdelta(info->file_baton,
-                                                      info->base_checksum,
-                                                      info->editor_pool,
-                                                      &info->textdelta,
-                                                      &info->textdelta_baton);
-
+      /* Open the file for editing. */
+      err = open_updated_file(info, FALSE, info->pool);
       if (err)
         {
           return error_fetch(request, fetch_ctx, err);
@@ -891,16 +943,12 @@ handle_fetch(serf_request_t *request,
 
   /* 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. */
-  status = serf_bucket_response_status(response, &sl);
-  if (SERF_BUCKET_READ_ERROR(status))
-    {
-      return svn_error_wrap_apr(status, NULL);
-    }
-  if (sl.code != 200)
+  if (fetch_ctx->handler->sline.code != 200)
     {
       err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
                               _("GET request failed: %d %s"),
-                              sl.code, sl.reason);
+                              fetch_ctx->handler->sline.code,
+                              fetch_ctx->handler->sline.reason);
       return error_fetch(request, fetch_ctx, err);
     }
 
@@ -983,51 +1031,17 @@ handle_fetch(serf_request_t *request,
         {
           report_info_t *info = fetch_ctx->info;
 
-          /* ### this doesn't feel quite right. but it gets tossed at the
-             ### end of this block, so it will work for now.  */
-          apr_pool_t *scratch_pool = info->editor_pool;
-
           if (fetch_ctx->delta_stream)
             err = svn_error_trace(svn_stream_close(fetch_ctx->delta_stream));
           else
             err = svn_error_trace(info->textdelta(NULL,
                                                   info->textdelta_baton));
-
           if (err)
             {
               return error_fetch(request, fetch_ctx, err);
             }
 
-          if (info->lock_token)
-            check_lock(info);
-
-          /* set all of the properties we received */
-          err = svn_ra_serf__walk_all_props(info->props,
-                                            info->base_name,
-                                            info->base_rev,
-                                            set_file_props, info,
-                                            scratch_pool);
-
-          if (!err)
-            err = svn_ra_serf__walk_all_props(info->dir->removed_props,
-                                              info->base_name,
-                                              info->base_rev,
-                                              remove_file_props, info,
-                                              scratch_pool);
-          if (!err && info->fetch_props)
-            {
-              err = svn_ra_serf__walk_all_props(info->props,
-                                                info->url,
-                                                info->target_rev,
-                                                set_file_props, info,
-                                                scratch_pool);
-            }
-
-          if (!err)
-            err = info->dir->update_editor->close_file(info->file_baton,
-                                                       info->final_checksum,
-                                                       scratch_pool);
-
+          err = close_updated_file(info, info->pool);
           if (err)
             {
               return svn_error_trace(error_fetch(request, fetch_ctx, err));
@@ -1039,8 +1053,7 @@ handle_fetch(serf_request_t *request,
           fetch_ctx->done_item.next = *fetch_ctx->done_list;
           *fetch_ctx->done_list = &fetch_ctx->done_item;
 
-          /* We're done with our pools. */
-          svn_pool_destroy(info->editor_pool);
+          /* We're done with our pool. */
           svn_pool_destroy(info->pool);
 
           if (status)
@@ -1062,26 +1075,18 @@ handle_stream(serf_request_t *request,
               apr_pool_t *pool)
 {
   report_fetch_t *fetch_ctx = handler_baton;
-  serf_status_line sl;
-  const char *location;
   svn_error_t *err;
   apr_status_t status;
 
-  status = serf_bucket_response_status(response, &sl);
-  if (SERF_BUCKET_READ_ERROR(status))
-    {
-      return svn_error_wrap_apr(status, NULL);
-    }
+  /* ### new field. make sure we didn't miss some initialization.  */
+  SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
 
-  /* Woo-hoo.  Nothing here to see.  */
-  location = svn_ra_serf__response_get_location(response, pool);
-
-  err = svn_ra_serf__error_on_status(sl.code,
+  err = svn_ra_serf__error_on_status(fetch_ctx->handler->sline.code,
                                      fetch_ctx->info->name,
-                                     location);
+                                     fetch_ctx->handler->location);
   if (err)
     {
-      fetch_ctx->done = TRUE;
+      fetch_ctx->handler->done = TRUE;
 
       err = svn_error_compose_create(
                   err,
@@ -1153,75 +1158,40 @@ handle_stream(serf_request_t *request,
   /* not reached */
 }
 
+/* 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)
 {
-  /* Ensure our parent is open. */
-  SVN_ERR(open_dir(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);
-    }
-
-  if (SVN_IS_VALID_REVNUM(info->base_rev))
-    {
-      SVN_ERR(info->dir->update_editor->open_file(info->name,
-                                                  info->dir->dir_baton,
-                                                  info->base_rev,
-                                                  info->editor_pool,
-                                                  &info->file_baton));
-    }
-  else
-    {
-      SVN_ERR(info->dir->update_editor->add_file(info->name,
-                                                 info->dir->dir_baton,
-                                                 info->copyfrom_path,
-                                                 info->copyfrom_rev,
-                                                 info->editor_pool,
-                                                 &info->file_baton));
-    }
-
-  if (info->fetch_file)
-    {
-      SVN_ERR(info->dir->update_editor->apply_textdelta(info->file_baton,
-                                                    info->base_checksum,
-                                                    info->editor_pool,
-                                                    &info->textdelta,
-                                                    &info->textdelta_baton));
-    }
+  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);
 
-  if (info->lock_token)
-    check_lock(info);
+  info->dir->ref_count--;
 
-  /* 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)
-    {
-      SVN_ERR(svn_ra_serf__walk_all_props(info->props, info->url,
-                                          info->target_rev,
-                                          set_file_props, info,
-                                          scratch_pool));
-    }
+  return SVN_NO_ERROR;
+}
 
-  SVN_ERR(info->dir->update_editor->close_file(info->file_baton,
-                                               info->final_checksum,
-                                               scratch_pool));
+/* "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(open_updated_file(info, TRUE, 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 pools. */
-  svn_pool_destroy(info->editor_pool);
+  /* We're done with our pool. */
   svn_pool_destroy(info->pool);
 
   info->dir->ref_count--;
@@ -1229,6 +1199,8 @@ handle_propchange_only(report_info_t *in
   return SVN_NO_ERROR;
 }
 
+/* --------------------------------------------------------- */
+
 static svn_error_t *
 fetch_file(report_context_t *ctx, report_info_t *info)
 {
@@ -1239,9 +1211,8 @@ fetch_file(report_context_t *ctx, report
   conn = ctx->sess->conns[ctx->sess->cur_conn];
 
   /* go fetch info->name from DAV:checked-in */
-  info->url =
-      svn_ra_serf__get_ver_prop(info->props, info->base_name,
-                                info->base_rev, "DAV:", "checked-in");
+  info->url = svn_ra_serf__get_ver_prop(info->props, info->base_name,
+                                        info->base_rev, "DAV:", "checked-in");
 
   if (!info->url)
     {
@@ -1251,15 +1222,18 @@ fetch_file(report_context_t *ctx, report
     }
 
   /* If needed, create the PROPFIND to retrieve the file's properties. */
-  info->propfind = NULL;
+  info->propfind_handler = NULL;
   if (info->fetch_props)
     {
-      SVN_ERR(svn_ra_serf__deliver_props(&info->propfind, info->props,
+      SVN_ERR(svn_ra_serf__deliver_props(&info->propfind_handler, info->props,
                                          ctx->sess, conn, info->url,
-                                         info->target_rev, "0", all_props,
+                                         ctx->target_rev, "0", all_props,
                                          &ctx->done_propfinds,
                                          info->dir->pool));
-      SVN_ERR_ASSERT(info->propfind);
+      SVN_ERR_ASSERT(info->propfind_handler);
+
+      /* Create a serf request for the PROPFIND.  */
+      svn_ra_serf__request_create(info->propfind_handler);
 
       ctx->active_propfinds++;
     }
@@ -1269,36 +1243,112 @@ fetch_file(report_context_t *ctx, report
    */
   if (info->fetch_file && ctx->text_deltas)
     {
-      report_fetch_t *fetch_ctx;
+      svn_stream_t *contents = NULL;
 
-      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;
+      if (ctx->sess->wc_callbacks->get_wc_contents
+          && (info->final_sha1_checksum || info->final_checksum))
+        {
+          svn_error_t *err;
+          svn_checksum_t *checksum;
+         
+          /* Parse our checksum, preferring SHA1 to MD5. */
+          if (info->final_sha1_checksum)
+            {
+              err = svn_checksum_parse_hex(&checksum, svn_checksum_sha1,
+                                           info->final_sha1_checksum,
+                                           info->pool);
+            }
+          else if (info->final_checksum)
+            {
+              err = svn_checksum_parse_hex(&checksum, svn_checksum_md5,
+                                           info->final_checksum,
+                                           info->pool);
+            }
 
-      handler = apr_pcalloc(info->dir->pool, sizeof(*handler));
+          /* Okay so far?  Let's try to get a stream on some readily
+             available matching content. */
+          if (!err)
+            {
+              err = ctx->sess->wc_callbacks->get_wc_contents(
+                        ctx->sess->wc_callback_baton, &contents,
+                        checksum, info->pool);
+            }
 
-      handler->method = "GET";
-      handler->path = fetch_ctx->info->url;
+          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);
+            }
+          else
+            {
+              info->cached_contents = contents;
+            }
+        }          
 
-      handler->conn = conn;
-      handler->session = ctx->sess;
+      /* 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)
+            { 
+              /* ... 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;
+
+              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
+            {
+              /* 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));
+            }
+        }
+      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 = apr_pcalloc(info->dir->pool, sizeof(*handler));
+
+          handler->handler_pool = info->dir->pool;
+          handler->method = "GET";
+          handler->path = fetch_ctx->info->url;
 
-      handler->header_delegate = headers_fetch;
-      handler->header_delegate_baton = fetch_ctx;
+          handler->conn = conn;
+          handler->session = ctx->sess;
 
-      handler->response_handler = handle_fetch;
-      handler->response_baton = fetch_ctx;
+          handler->header_delegate = headers_fetch;
+          handler->header_delegate_baton = fetch_ctx;
 
-      handler->response_error = cancel_fetch;
-      handler->response_error_baton = fetch_ctx;
+          handler->response_handler = handle_fetch;
+          handler->response_baton = fetch_ctx;
 
-      svn_ra_serf__request_create(handler);
+          handler->response_error = cancel_fetch;
+          handler->response_error_baton = fetch_ctx;
 
-      ctx->active_fetches++;
+          fetch_ctx->handler = handler;
+
+          svn_ra_serf__request_create(handler);
+
+          ctx->active_fetches++;
+        }
     }
-  else if (info->propfind)
+  else if (info->propfind_handler)
     {
       svn_ra_serf__list_t *list_item;
 
@@ -1373,7 +1423,6 @@ start_report(svn_ra_serf__xml_parser_t *
 
       info->base_rev = SVN_STR_TO_REV(rev);
       info->dir->base_rev = info->base_rev;
-      info->dir->target_rev = ctx->target_rev;
       info->fetch_props = TRUE;
 
       info->dir->base_name = "";
@@ -1426,7 +1475,6 @@ start_report(svn_ra_serf__xml_parser_t *
 
       info->base_rev = SVN_STR_TO_REV(rev);
       dir->base_rev = info->base_rev;
-      dir->target_rev = ctx->target_rev;
 
       info->fetch_props = FALSE;
 
@@ -1480,7 +1528,6 @@ start_report(svn_ra_serf__xml_parser_t *
       /* Mark that we don't have a base. */
       info->base_rev = SVN_INVALID_REVNUM;
       dir->base_rev = info->base_rev;
-      dir->target_rev = ctx->target_rev;
       dir->fetch_props = TRUE;
 
       dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath,
@@ -1513,7 +1560,6 @@ start_report(svn_ra_serf__xml_parser_t *
       info = push_state(parser, ctx, OPEN_FILE);
 
       info->base_rev = SVN_STR_TO_REV(rev);
-      info->target_rev = ctx->target_rev;
       info->fetch_props = FALSE;
 
       info->base_name = apr_pstrdup(info->pool, file_name);
@@ -1539,7 +1585,6 @@ start_report(svn_ra_serf__xml_parser_t *
       info = push_state(parser, ctx, ADD_FILE);
 
       info->base_rev = SVN_INVALID_REVNUM;
-      info->target_rev = ctx->target_rev;
       info->fetch_props = TRUE;
       info->fetch_file = TRUE;
 
@@ -1559,9 +1604,11 @@ start_report(svn_ra_serf__xml_parser_t *
            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);
 
@@ -1572,6 +1619,10 @@ start_report(svn_ra_serf__xml_parser_t *
             _("Missing name attr in delete-entry element"));
         }
 
+      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(open_dir(info->dir));
@@ -1580,10 +1631,10 @@ start_report(svn_ra_serf__xml_parser_t *
 
       full_path = svn_relpath_join(info->dir->name, file_name, tmppool);
 
-      SVN_ERR(info->dir->update_editor->delete_entry(full_path,
-                                                     SVN_INVALID_REVNUM,
-                                                     info->dir->dir_baton,
-                                                     tmppool));
+      SVN_ERR(ctx->update_editor->delete_entry(full_path,
+                                               delete_rev,
+                                               info->dir->dir_baton,
+                                               tmppool));
 
       svn_pool_destroy(tmppool);
     }
@@ -1764,6 +1815,14 @@ start_report(svn_ra_serf__xml_parser_t *
           info->prop_encoding = svn_xml_get_attr_value("encoding", attrs);
           svn_stringbuf_setempty(info->prop_value);
         }
+      else if (strcmp(name.name, "txdelta") == 0)
+        {
+          /* Pre 1.2, mod_dav_svn was using <txdelta> tags (in
+             addition to <fetch-file>s and such) when *not* in
+             "send-all" mode.  As a client, we're smart enough to know
+             that's wrong, so we'll just ignore these tags. */
+          ;
+        }
       else
         {
           return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
@@ -1842,15 +1901,18 @@ end_report(svn_ra_serf__xml_parser_t *pa
           /* Unconditionally set fetch_props now. */
           info->dir->fetch_props = TRUE;
 
-          SVN_ERR(svn_ra_serf__deliver_props(&info->dir->propfind,
+          SVN_ERR(svn_ra_serf__deliver_props(&info->dir->propfind_handler,
                                              info->dir->props, ctx->sess,
                                              ctx->sess->conns[ctx->sess->cur_conn],
                                              info->dir->url,
-                                             info->dir->target_rev, "0",
+                                             ctx->target_rev, "0",
                                              all_props,
                                              &ctx->done_propfinds,
                                              info->dir->pool));
-          SVN_ERR_ASSERT(info->dir->propfind);
+          SVN_ERR_ASSERT(info->dir->propfind_handler);
+
+          /* Create a serf request for the PROPFIND.  */
+          svn_ra_serf__request_create(info->dir->propfind_handler);
 
           ctx->active_propfinds++;
 
@@ -1860,7 +1922,7 @@ end_report(svn_ra_serf__xml_parser_t *pa
         }
       else
         {
-          info->dir->propfind = NULL;
+          info->dir->propfind_handler = NULL;
         }
 
       svn_ra_serf__xml_pop_state(parser);
@@ -2036,6 +2098,13 @@ end_report(svn_ra_serf__xml_parser_t *pa
 
       svn_ra_serf__set_ver_prop(props, info->base_name, info->base_rev,
                                 ns->namespace, ns->url, set_val_str, pool);
+
+      /* Advance handling:  if we spotted the md5-checksum property on
+         the wire, remember it's value. */
+      if (strcmp(ns->url, "md5-checksum") == 0
+          && strcmp(ns->namespace, SVN_DAV_PROP_NS_DAV) == 0)
+        info->final_checksum = apr_pstrdup(info->pool, set_val_str->data);
+
       svn_ra_serf__xml_pop_state(parser);
     }
   else if (state == IGNORE_PROP_NAME || state == NEED_PROP_NAME)
@@ -2197,8 +2266,6 @@ link_path(void *report_baton,
   return APR_SUCCESS;
 }
 
-/** Max. number of connctions we'll open to the server. */
-#define MAX_NR_OF_CONNS 4
 /** Minimum nr. of outstanding requests needed before a new connection is
  *  opened. */
 #define REQS_PER_CONN 8
@@ -2219,16 +2286,10 @@ open_connection_if_needed(svn_ra_serf__s
       apr_status_t status;
 
       sess->conns[cur] = apr_pcalloc(sess->pool, sizeof(*sess->conns[cur]));
-      sess->conns[cur]->http10 = TRUE;  /* until we confirm HTTP/1.1  */
-      sess->conns[cur]->http10 = FALSE; /* ### don't change behavior yet  */
       sess->conns[cur]->bkt_alloc = serf_bucket_allocator_create(sess->pool,
                                                                  NULL, NULL);
-      sess->conns[cur]->hostname  = sess->conns[0]->hostname;
-      sess->conns[cur]->using_ssl = sess->conns[0]->using_ssl;
-      sess->conns[cur]->using_compression = sess->conns[0]->using_compression;
       sess->conns[cur]->last_status_code = -1;
       sess->conns[cur]->session = sess;
-      sess->conns[cur]->useragent = sess->conns[0]->useragent;
       status = serf_connection_create2(&sess->conns[cur]->conn,
                                        sess->context,
                                        sess->session_url,
@@ -2271,7 +2332,7 @@ headers_report(serf_bucket_t *headers,
 {
   report_context_t *report = baton;
 
-  if (report->conn->using_compression)
+  if (report->sess->using_compression)
     {
       serf_bucket_headers_setn(headers, "Accept-Encoding", "gzip");
     }
@@ -2289,7 +2350,6 @@ finish_report(void *report_baton,
   svn_ra_serf__xml_parser_t *parser_ctx;
   const char *report_target;
   svn_boolean_t closed_root;
-  int status_code;
   svn_stringbuf_t *buf = NULL;
   apr_pool_t *iterpool = svn_pool_create(pool);
   svn_error_t *err;
@@ -2319,6 +2379,7 @@ finish_report(void *report_baton,
 
   handler = apr_pcalloc(pool, sizeof(*handler));
 
+  handler->handler_pool = pool;
   handler->method = "REPORT";
   handler->path = report->path;
   handler->body_delegate = create_update_report_body;
@@ -2338,9 +2399,6 @@ finish_report(void *report_baton,
   parser_ctx->end = end_report;
   parser_ctx->cdata = cdata_report;
   parser_ctx->done = &report->done;
-  /* While we provide a location here to store the status code, we don't
-     do anything with it. The error in parser_ctx->error is sufficient. */
-  parser_ctx->status_code = &status_code;
 
   handler->response_handler = svn_ra_serf__handle_xml_parser;
   handler->response_baton = parser_ctx;
@@ -2382,6 +2440,11 @@ finish_report(void *report_baton,
       err = sess->pending_error;
       sess->pending_error = SVN_NO_ERROR;
 
+      if (!err && handler->done && handler->server_error)
+        {
+          err = handler->server_error->error;
+        }
+
       if (APR_STATUS_IS_TIMEUP(status))
         {
           svn_error_clear(err);
@@ -2429,7 +2492,7 @@ finish_report(void *report_baton,
                 {
                   report_info_t *item = cur->data;
 
-                  if (item->propfind == done_list->data)
+                  if (item->propfind_handler == done_list->data)
                     {
                       break;
                     }
@@ -2443,7 +2506,20 @@ finish_report(void *report_baton,
                */
               if (cur)
                 {
-                  SVN_ERR(handle_propchange_only(cur->data, iterpool_inner));
+                  report_info_t *info = cur->data;
+
+                  /* If we've got cached file content for this file,
+                     take care of the locally collected properties and
+                     file content at once.  Otherwise, just deal with
+                     the collected properties. */
+                  if (info->cached_contents)
+                    {
+                      SVN_ERR(handle_local_content(info, iterpool_inner));
+                    }
+                  else
+                    {
+                      SVN_ERR(handle_propchange_only(info, iterpool_inner));
+                    }
 
                   if (!prev)
                     {
@@ -2484,9 +2560,9 @@ finish_report(void *report_baton,
            *   we've already completed the propfind
            * then, we know it's time for us to close this directory.
            */
-          while (cur_dir && !cur_dir->ref_count && cur_dir->tag_closed &&
-                 (!cur_dir->fetch_props ||
-                  svn_ra_serf__propfind_is_done(cur_dir->propfind)))
+          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;
 
@@ -2769,6 +2845,74 @@ svn_ra_serf__do_switch(svn_ra_session_t 
                               switch_editor, switch_baton, pool);
 }
 
+/* Helper svn_ra_serf__get_file(). Attempts to fetch file contents
+ * using SESSION->wc_callbacks->get_wc_contents() if sha1 property is
+ * present in PROPS.
+ * 
+ * Sets *FOUND_P to TRUE if file contents was successfuly fetched.
+ * 
+ * Performs all temporary allocations in POOL.
+ */
+static svn_error_t *
+try_get_wc_contents(svn_boolean_t *found_p,
+                    svn_ra_serf__session_t *session,
+                    apr_hash_t *props,
+                    svn_stream_t *dst_stream,
+                    apr_pool_t *pool)
+{
+  apr_hash_t *svn_props;
+  const char *sha1_checksum_prop;
+  svn_checksum_t *checksum;
+  svn_stream_t *wc_stream;
+  svn_error_t *err;
+
+  /* No contents found by default. */
+  *found_p = FALSE;
+
+  if (!session->wc_callbacks->get_wc_contents)
+    {
+      /* No callback, nothing to do. */
+      return SVN_NO_ERROR;
+    }
+
+
+  svn_props = apr_hash_get(props, SVN_DAV_PROP_NS_DAV, APR_HASH_KEY_STRING);
+  if (!svn_props)
+    {
+      /* No properties -- therefore no checksum property -- in response. */
+      return SVN_NO_ERROR;
+    }
+
+  sha1_checksum_prop = svn_prop_get_value(svn_props, "sha1-checksum");
+  if (sha1_checksum_prop == NULL)
+    {
+      /* No checksum property in response. */
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(svn_checksum_parse_hex(&checksum, svn_checksum_sha1,
+                                 sha1_checksum_prop, pool));
+
+  err = session->wc_callbacks->get_wc_contents(
+          session->wc_callback_baton, &wc_stream, checksum, pool);
+
+  if (err)
+    {
+      svn_error_clear(err);
+
+      /* Ignore errors for now. */
+      return SVN_NO_ERROR;
+    }
+  
+  if (wc_stream)
+    {
+      SVN_ERR(svn_stream_copy3(wc_stream, dst_stream, NULL, NULL, pool));
+      *found_p = TRUE;
+    }
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_ra_serf__get_file(svn_ra_session_t *ra_session,
                       const char *path,
@@ -2782,7 +2926,8 @@ svn_ra_serf__get_file(svn_ra_session_t *
   svn_ra_serf__connection_t *conn;
   const char *fetch_url;
   apr_hash_t *fetch_props;
-  svn_node_kind_t res_kind;
+  svn_kind_t res_kind;
+  const svn_ra_serf__dav_props_t *which_props;
 
   /* What connection should we go on? */
   conn = session->conns[session->cur_conn];
@@ -2798,24 +2943,36 @@ svn_ra_serf__get_file(svn_ra_session_t *
    */
   if (SVN_IS_VALID_REVNUM(revision) || fetched_rev)
     {
-      const char *baseline_url, *rel_path;
-
-      SVN_ERR(svn_ra_serf__get_baseline_info(&baseline_url, &rel_path,
-                                             session, conn, fetch_url,
-                                             revision, fetched_rev, pool));
-      fetch_url = svn_path_url_add_component2(baseline_url, rel_path, pool);
+      SVN_ERR(svn_ra_serf__get_stable_url(&fetch_url, fetched_rev,
+                                          session, conn,
+                                          fetch_url, revision,
+                                          pool, pool));
       revision = SVN_INVALID_REVNUM;
     }
+  /* REVISION is always SVN_INVALID_REVNUM  */
+  SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision));
 
-  SVN_ERR(svn_ra_serf__retrieve_props(&fetch_props, session, conn, fetch_url,
-                                      revision, "0",
-                                      props ? all_props : check_path_props,
-                                      pool, pool));
-
-  /* Verify that resource type is not colelction. */
-  SVN_ERR(svn_ra_serf__get_resource_type(&res_kind, fetch_props, fetch_url,
-                                         revision));
-  if (res_kind != svn_node_file)
+  if (props)
+    {
+      which_props = all_props;
+    }
+  else if (stream && session->wc_callbacks->get_wc_contents)
+    {
+      which_props = type_and_checksum_props;
+    }
+  else
+    {
+      which_props = check_path_props;
+    }
+     
+  SVN_ERR(svn_ra_serf__fetch_node_props(&fetch_props, conn, fetch_url,
+                                        SVN_INVALID_REVNUM,
+                                        which_props,
+                                        pool, pool));
+
+  /* Verify that resource type is not collection. */
+  SVN_ERR(svn_ra_serf__get_resource_type(&res_kind, fetch_props));
+  if (res_kind != svn_kind_file)
     {
       return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL,
                               _("Can't get text contents of a directory"));
@@ -2824,41 +2981,54 @@ svn_ra_serf__get_file(svn_ra_session_t *
   /* TODO Filter out all of our props into a usable format. */
   if (props)
     {
-      SVN_ERR(svn_ra_serf__flatten_props(props, fetch_props, fetch_url,
-                                         revision, pool, pool));
+      /* ### flatten_props() does not copy PROPVALUE, but fetch_node_props()
+         ### put them into POOL, so we're okay.  */
+      SVN_ERR(svn_ra_serf__flatten_props(props, fetch_props,
+                                         pool, pool));
     }
 
   if (stream)
     {
-      report_fetch_t *stream_ctx;
-      svn_ra_serf__handler_t *handler;
+      svn_boolean_t found;
+      SVN_ERR(try_get_wc_contents(&found, session, fetch_props, stream, pool));
+
+      /* No contents found in the WC, let's fetch from server. */
+      if (!found)
+        {
+          report_fetch_t *stream_ctx;
+          svn_ra_serf__handler_t *handler;
+
+          /* Create the fetch context. */
+          stream_ctx = apr_pcalloc(pool, sizeof(*stream_ctx));
+          stream_ctx->target_stream = stream;
+          stream_ctx->sess = session;
+          stream_ctx->conn = conn;
+          stream_ctx->info = apr_pcalloc(pool, sizeof(*stream_ctx->info));
+          stream_ctx->info->name = fetch_url;
+
+          handler = apr_pcalloc(pool, sizeof(*handler));
+
+          handler->handler_pool = pool;
+          handler->method = "GET";
+          handler->path = fetch_url;
+          handler->conn = conn;
+          handler->session = session;
 
-      /* Create the fetch context. */
-      stream_ctx = apr_pcalloc(pool, sizeof(*stream_ctx));
-      stream_ctx->target_stream = stream;
-      stream_ctx->sess = session;
-      stream_ctx->conn = conn;
-      stream_ctx->info = apr_pcalloc(pool, sizeof(*stream_ctx->info));
-      stream_ctx->info->name = fetch_url;
-
-      handler = apr_pcalloc(pool, sizeof(*handler));
-      handler->method = "GET";
-      handler->path = fetch_url;
-      handler->conn = conn;
-      handler->session = session;
-
-      handler->header_delegate = headers_fetch;
-      handler->header_delegate_baton = stream_ctx;
-
-      handler->response_handler = handle_stream;
-      handler->response_baton = stream_ctx;
+          handler->header_delegate = headers_fetch;
+          handler->header_delegate_baton = stream_ctx;
 
-      handler->response_error = cancel_fetch;
-      handler->response_error_baton = stream_ctx;
+          handler->response_handler = handle_stream;
+          handler->response_baton = stream_ctx;
 
-      svn_ra_serf__request_create(handler);
+          handler->response_error = cancel_fetch;
+          handler->response_error_baton = stream_ctx;
 
-      SVN_ERR(svn_ra_serf__context_run_wait(&stream_ctx->done, session, pool));
+          stream_ctx->handler = handler;
+
+          svn_ra_serf__request_create(handler);
+
+          SVN_ERR(svn_ra_serf__context_run_wait(&stream_ctx->done, session, pool));
+        }
     }
 
   return SVN_NO_ERROR;