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

svn commit: r1400556 [13/29] - in /subversion/branches/ev2-export: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/hudson/ contrib/client-side/emacs/ contrib/client-side/svn-push/ contrib/client-side/svnmerge/ contrib/hook-...

Modified: subversion/branches/ev2-export/subversion/libsvn_ra_serf/replay.c
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_ra_serf/replay.c?rev=1400556&r1=1400555&r2=1400556&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/libsvn_ra_serf/replay.c (original)
+++ subversion/branches/ev2-export/subversion/libsvn_ra_serf/replay.c Sun Oct 21 02:00:31 2012
@@ -732,6 +732,7 @@ svn_ra_serf__replay_range(svn_ra_session
   svn_revnum_t rev = start_revision;
   const char *report_target;
   int active_reports = 0;
+  apr_short_interval_time_t waittime_left = session->timeout;
 
   SVN_ERR(svn_ra_serf__report_resource(&report_target, session, NULL, pool));
 
@@ -855,18 +856,36 @@ svn_ra_serf__replay_range(svn_ra_session
          ### ahead and apply it here, too, in case serf eventually uses
          ### that parameter.
       */
-      status = serf_context_run(session->context, session->timeout,
+      status = serf_context_run(session->context,
+                                SVN_RA_SERF__CONTEXT_RUN_DURATION,
                                 pool);
 
       err = session->pending_error;
       session->pending_error = NULL;
 
+      /* If the context duration timeout is up, we'll subtract that
+         duration from the total time alloted for such things.  If
+         there's no time left, we fail with a message indicating that
+         the connection timed out.  */
       if (APR_STATUS_IS_TIMEUP(status))
         {
           svn_error_clear(err);
-          return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT,
-                                  NULL,
-                                  _("Connection timed out"));
+          err = SVN_NO_ERROR;
+          status = 0;
+
+          if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION)
+            {
+              waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION;
+            }
+          else
+            {
+              return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
+                                      _("Connection timed out"));
+            }
+        }
+      else
+        {
+          waittime_left = session->timeout;
         }
 
       /* Substract the number of completely handled responses from our
@@ -885,9 +904,8 @@ svn_ra_serf__replay_range(svn_ra_session
       SVN_ERR(err);
       if (status)
         {
-          return svn_error_wrap_apr(status,
-                                    _("Error retrieving replay REPORT (%d)"),
-                                    status);
+          return svn_ra_serf__wrap_err(status,
+                                       _("Error retrieving replay REPORT"));
         }
       done_reports = NULL;
     }

Modified: subversion/branches/ev2-export/subversion/libsvn_ra_serf/sb_bucket.c
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_ra_serf/sb_bucket.c?rev=1400556&r1=1400555&r2=1400556&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/libsvn_ra_serf/sb_bucket.c (original)
+++ subversion/branches/ev2-export/subversion/libsvn_ra_serf/sb_bucket.c Sun Oct 21 02:00:31 2012
@@ -62,7 +62,7 @@ svn_ra_serf__copy_into_spillbuf(svn_spil
       status = serf_bucket_read(bkt, SERF_READ_ALL_AVAIL, &data, &len);
 
       if (status != APR_SUCCESS && status != APR_EOF)
-        return svn_error_wrap_apr(status, _("Failed to read the request"));
+        return svn_ra_serf__wrap_err(status, _("Failed to read the request"));
 
       SVN_ERR(svn_spillbuf__write(*spillbuf, data, len, scratch_pool));
 

Modified: subversion/branches/ev2-export/subversion/libsvn_ra_serf/serf.c
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_ra_serf/serf.c?rev=1400556&r1=1400555&r2=1400556&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/libsvn_ra_serf/serf.c (original)
+++ subversion/branches/ev2-export/subversion/libsvn_ra_serf/serf.c Sun Oct 21 02:00:31 2012
@@ -307,9 +307,9 @@ load_config(svn_ra_serf__session_t *sess
                                      session->pool);
       if (status)
         {
-          return svn_error_wrap_apr(status,
-                                    _("Could not resolve proxy server '%s'"),
-                                    proxy_host);
+          return svn_ra_serf__wrap_err(
+                   status, _("Could not resolve proxy server '%s'"),
+                   proxy_host);
         }
       session->using_proxy = TRUE;
       serf_config_proxy(session->context, proxy_addr);
@@ -344,7 +344,7 @@ svn_ra_serf__progress(void *progress_bat
 static svn_error_t *
 svn_ra_serf__open(svn_ra_session_t *session,
                   const char **corrected_url,
-                  const char *repos_URL,
+                  const char *session_URL,
                   const svn_ra_callbacks2_t *callbacks,
                   void *callback_baton,
                   apr_hash_t *config,
@@ -374,31 +374,34 @@ svn_ra_serf__open(svn_ra_session_t *sess
                                        serf_sess->pool));
 
 
-  status = apr_uri_parse(serf_sess->pool, repos_URL, &url);
+  status = apr_uri_parse(serf_sess->pool, session_URL, &url);
   if (status)
     {
       return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
-                               _("Illegal repository URL '%s'"),
-                               repos_URL);
+                               _("Illegal URL '%s'"),
+                               session_URL);
     }
-  /* Contrary to what the comment for apr_uri_t.path says in apr-util 1.2.12 and
-     older, for root paths url.path will be "", where serf requires "/". */
+  /* Depending the version of apr-util in use, for root paths url.path
+     will be NULL or "", where serf requires "/". */
   if (url.path == NULL || url.path[0] == '\0')
-    url.path = apr_pstrdup(serf_sess->pool, "/");
+    {
+      url.path = apr_pstrdup(serf_sess->pool, "/");
+    }
   if (!url.port)
     {
       url.port = apr_uri_port_of_scheme(url.scheme);
     }
   serf_sess->session_url = url;
-  serf_sess->session_url_str = apr_pstrdup(serf_sess->pool, repos_URL);
+  serf_sess->session_url_str = apr_pstrdup(serf_sess->pool, session_URL);
   serf_sess->using_ssl = (svn_cstring_casecmp(url.scheme, "https") == 0);
 
   serf_sess->supports_deadprop_count = svn_tristate_unknown;
 
   serf_sess->capabilities = apr_hash_make(serf_sess->pool);
 
-  serf_sess->http10 = TRUE;  /* until we confirm HTTP/1.1  */
-  serf_sess->http10 = FALSE; /* ### don't change behavior yet  */
+  /* We have to assume that the server only supports HTTP/1.0. Once it's clear
+     HTTP/1.1 is supported, we can upgrade. */
+  serf_sess->http10 = TRUE;
 
   SVN_ERR(load_config(serf_sess, config, serf_sess->pool));
 
@@ -428,7 +431,7 @@ svn_ra_serf__open(svn_ra_session_t *sess
                             svn_ra_serf__conn_closed, serf_sess->conns[0],
                             serf_sess->pool);
   if (status)
-    return svn_error_wrap_apr(status, NULL);
+    return svn_ra_serf__wrap_err(status, NULL);
 
   /* Set the progress callback. */
   serf_context_set_progress_cb(serf_sess->context, svn_ra_serf__progress,
@@ -471,14 +474,25 @@ svn_ra_serf__reparent(svn_ra_session_t *
             "URL '%s'"), url, session->repos_root_str);
     }
 
-  status = apr_uri_parse(session->pool, url, &new_url);
+  status = apr_uri_parse(pool, url, &new_url);
   if (status)
     {
       return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                                _("Illegal repository URL '%s'"), url);
     }
 
-  session->session_url.path = new_url.path;
+  /* Depending the version of apr-util in use, for root paths url.path
+     will be NULL or "", where serf requires "/". */
+  /* ### Maybe we should use a string buffer for these strings so we
+     ### don't allocate memory in the session on every reparent? */
+  if (new_url.path == NULL || new_url.path[0] == '\0')
+    {
+      session->session_url.path = apr_pstrdup(session->pool, "/");
+    }
+  else
+    {
+      session->session_url.path = apr_pstrdup(session->pool, new_url.path);
+    }
   session->session_url_str = apr_pstrdup(session->pool, url);
 
   return SVN_NO_ERROR;
@@ -1136,7 +1150,8 @@ static const svn_ra__vtable_t serf_vtabl
   svn_ra_serf__has_capability,
   svn_ra_serf__replay_range,
   svn_ra_serf__get_deleted_rev,
-  svn_ra_serf__register_editor_shim_callbacks
+  svn_ra_serf__register_editor_shim_callbacks,
+  svn_ra_serf__get_inherited_props
 };
 
 svn_error_t *

Modified: subversion/branches/ev2-export/subversion/libsvn_ra_serf/update.c
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_ra_serf/update.c?rev=1400556&r1=1400555&r2=1400556&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/libsvn_ra_serf/update.c (original)
+++ subversion/branches/ev2-export/subversion/libsvn_ra_serf/update.c Sun Oct 21 02:00:31 2012
@@ -313,6 +313,10 @@ struct report_context_t {
   /* Do we want the server to send copyfrom args or not? */
   svn_boolean_t send_copyfrom_args;
 
+  /* Is the server including properties inline for newly added
+     files/dirs? */
+  svn_boolean_t add_props_included;
+
   /* Path -> lock token mapping. */
   apr_hash_t *lock_path_tokens;
 
@@ -358,6 +362,9 @@ struct report_context_t {
   /* Are we done parsing the REPORT response? */
   svn_boolean_t done;
 
+  /* 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;
 };
@@ -926,12 +933,23 @@ handle_fetch(serf_request_t *request,
           return error_fetch(request, fetch_ctx, err);
         }
 
-      if (val && svn_cstring_casecmp(val, "application/vnd.svn-svndiff") == 0)
+      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);
+
+          /* 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))
+            {
+              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);
+            }
         }
       else
         {
@@ -961,7 +979,7 @@ handle_fetch(serf_request_t *request,
       status = serf_bucket_read(response, 8000, &data, &len);
       if (SERF_BUCKET_READ_ERROR(status))
         {
-          return svn_error_wrap_apr(status, NULL);
+          return svn_ra_serf__wrap_err(status, NULL);
         }
 
       fetch_ctx->read_size += len;
@@ -981,7 +999,7 @@ handle_fetch(serf_request_t *request,
               /* Skip on to the next iteration of this loop. */
               if (APR_STATUS_IS_EAGAIN(status))
                 {
-                  return svn_error_wrap_apr(status, NULL);
+                  return svn_ra_serf__wrap_err(status, NULL);
                 }
               continue;
             }
@@ -1057,11 +1075,11 @@ handle_fetch(serf_request_t *request,
           svn_pool_destroy(info->pool);
 
           if (status)
-            return svn_error_wrap_apr(status, NULL);
+            return svn_ra_serf__wrap_err(status, NULL);
         }
       if (APR_STATUS_IS_EAGAIN(status))
         {
-          return svn_error_wrap_apr(status, NULL);
+          return svn_ra_serf__wrap_err(status, NULL);
         }
     }
   /* not reached */
@@ -1103,7 +1121,7 @@ handle_stream(serf_request_t *request,
       status = serf_bucket_read(response, 8000, &data, &len);
       if (SERF_BUCKET_READ_ERROR(status))
         {
-          return svn_error_wrap_apr(status, NULL);
+          return svn_ra_serf__wrap_err(status, NULL);
         }
 
       fetch_ctx->read_size += len;
@@ -1122,7 +1140,7 @@ handle_stream(serf_request_t *request,
               /* Skip on to the next iteration of this loop. */
               if (APR_STATUS_IS_EAGAIN(status))
                 {
-                  return svn_error_wrap_apr(status, NULL);
+                  return svn_ra_serf__wrap_err(status, NULL);
                 }
               continue;
             }
@@ -1152,7 +1170,7 @@ handle_stream(serf_request_t *request,
 
       if (status)
         {
-          return svn_error_wrap_apr(status, NULL);
+          return svn_ra_serf__wrap_err(status, NULL);
         }
     }
   /* not reached */
@@ -1216,9 +1234,9 @@ fetch_file(report_context_t *ctx, report
 
   if (!info->url)
     {
-      return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
-                        _("The OPTIONS response did not include the "
-                          "requested checked-in value"));
+      return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+                              _("The REPORT or PROPFIND response did not "
+                                "include the requested checked-in value"));
     }
 
   /* If needed, create the PROPFIND to retrieve the file's properties. */
@@ -1388,7 +1406,13 @@ start_report(svn_ra_serf__xml_parser_t *
 
   state = parser->state->current_state;
 
-  if (state == NONE && strcmp(name.name, "target-revision") == 0)
+  if (state == NONE && strcmp(name.name, "update-report") == 0)
+    {
+      const char *val = svn_xml_get_attr_value("inline-props", attrs);
+      if (val && (strcmp(val, "true") == 0))
+        ctx->add_props_included = TRUE;
+    }
+  else if (state == NONE && strcmp(name.name, "target-revision") == 0)
     {
       const char *rev;
 
@@ -1528,7 +1552,11 @@ 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->fetch_props = TRUE;
+
+      /* 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;
 
       dir->repos_relpath = svn_relpath_join(dir->parent_dir->repos_relpath,
                                             dir->base_name, dir->pool);
@@ -1585,9 +1613,13 @@ start_report(svn_ra_serf__xml_parser_t *
       info = push_state(parser, ctx, ADD_FILE);
 
       info->base_rev = SVN_INVALID_REVNUM;
-      info->fetch_props = TRUE;
       info->fetch_file = 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;
+
       info->base_name = apr_pstrdup(info->pool, file_name);
       info->name = NULL;
 
@@ -1862,8 +1894,15 @@ end_report(svn_ra_serf__xml_parser_t *pa
 
   if (state == NONE)
     {
-      /* nothing to close yet. */
-      return SVN_NO_ERROR;
+      if (strcmp(name.name, "update-report") == 0)
+        {
+          ctx->report_completed = TRUE;
+        }
+      else
+        {
+          /* nothing to close yet. */
+          return SVN_NO_ERROR;
+        }
     }
 
   if (((state == OPEN_DIR && (strcmp(name.name, "open-directory") == 0)) ||
@@ -1886,9 +1925,9 @@ end_report(svn_ra_serf__xml_parser_t *pa
       if (!checked_in_url &&
           (!SVN_IS_VALID_REVNUM(info->dir->base_rev) || info->dir->fetch_props))
         {
-          return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
-                                  _("The OPTIONS response did not include the "
-                                    "requested checked-in value"));
+          return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+                                  _("The REPORT or PROPFIND response did not "
+                                    "include the requested checked-in value"));
         }
 
       info->dir->url = checked_in_url;
@@ -1896,7 +1935,7 @@ end_report(svn_ra_serf__xml_parser_t *pa
       /* At this point, we should have the checked-in href.
        * If needed, create the PROPFIND to retrieve the dir's properties.
        */
-      if (!SVN_IS_VALID_REVNUM(info->dir->base_rev) || info->dir->fetch_props)
+      if (info->dir->fetch_props)
         {
           /* Unconditionally set fetch_props now. */
           info->dir->fetch_props = TRUE;
@@ -2299,7 +2338,7 @@ open_connection_if_needed(svn_ra_serf__s
                                        sess->conns[cur],
                                        sess->pool);
       if (status)
-        return svn_error_wrap_apr(status, NULL);
+        return svn_ra_serf__wrap_err(status, NULL);
 
       sess->num_conns++;
     }
@@ -2353,6 +2392,7 @@ finish_report(void *report_baton,
   svn_stringbuf_t *buf = NULL;
   apr_pool_t *iterpool = svn_pool_create(pool);
   svn_error_t *err;
+  apr_short_interval_time_t waittime_left = sess->timeout;
 
   svn_xml_make_close_tag(&buf, iterpool, "S:update-report");
   SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len,
@@ -2435,7 +2475,9 @@ finish_report(void *report_baton,
          and what items are allocated within.  */
       iterpool_inner = svn_pool_create(iterpool);
 
-      status = serf_context_run(sess->context, sess->timeout, iterpool_inner);
+      status = serf_context_run(sess->context,
+                                SVN_RA_SERF__CONTEXT_RUN_DURATION,
+                                iterpool_inner);
 
       err = sess->pending_error;
       sess->pending_error = SVN_NO_ERROR;
@@ -2445,19 +2487,35 @@ finish_report(void *report_baton,
           err = handler->server_error->error;
         }
 
+      /* If the context duration timeout is up, we'll subtract that
+         duration from the total time alloted for such things.  If
+         there's no time left, we fail with a message indicating that
+         the connection timed out.  */
       if (APR_STATUS_IS_TIMEUP(status))
         {
           svn_error_clear(err);
-          return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT,
-                                  NULL,
-                                  _("Connection timed out"));
+          err = SVN_NO_ERROR;
+          status = 0;
+
+          if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION)
+            {
+              waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION;
+            }
+          else
+            {
+              return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
+                                      _("Connection timed out"));
+            }
+        }
+      else
+        {
+          waittime_left = sess->timeout;
         }
 
       SVN_ERR(err);
       if (status)
         {
-          return svn_error_wrap_apr(status, _("Error retrieving REPORT (%d)"),
-                                    status);
+          return svn_ra_serf__wrap_err(status, _("Error retrieving REPORT"));
         }
 
       /* Open extra connections if we have enough requests to send. */
@@ -2607,7 +2665,12 @@ finish_report(void *report_baton,
       SVN_ERR(close_all_dirs(report->root_dir));
     }
 
-  err = report->update_editor->close_edit(report->update_baton, iterpool);
+  /* If we got a complete report, close the edit.  Otherwise, abort it. */
+  if (report->report_completed)
+    err = report->update_editor->close_edit(report->update_baton, iterpool);
+  else
+    err = svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+                           _("Missing update-report close tag"));
 
   svn_pool_destroy(iterpool);
   return svn_error_trace(err);
@@ -2750,6 +2813,10 @@ make_update_reporter(svn_ra_session_t *r
       make_simple_xml_tag(&buf, "S:recursive", "no", scratch_pool);
     }
 
+  /* Subversion 1.8+ servers can be told to send properties for newly
+     added items inline even when doing a skelta response. */
+  make_simple_xml_tag(&buf, "S:include-props", "yes", scratch_pool);
+
   make_simple_xml_tag(&buf, "S:depth", svn_depth_to_word(depth), scratch_pool);
 
   SVN_ERR(svn_io_file_write_full(report->body_file, buf->data, buf->len,

Modified: subversion/branches/ev2-export/subversion/libsvn_ra_serf/util.c
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_ra_serf/util.c?rev=1400556&r1=1400555&r2=1400556&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/libsvn_ra_serf/util.c (original)
+++ subversion/branches/ev2-export/subversion/libsvn_ra_serf/util.c Sun Oct 21 02:00:31 2012
@@ -691,6 +691,11 @@ setup_serf_req(serf_request_t *request,
       serf_bucket_headers_setn(*hdrs_bkt, "Content-Type", content_type);
     }
 
+#if SERF_VERSION_AT_LEAST(1, 1, 0)
+  if (session->http10)
+      serf_bucket_headers_setn(*hdrs_bkt, "Connection", "keep-alive");
+#endif
+
   /* These headers need to be sent with every request; see issue #3255
      ("mod_dav_svn does not pass client capabilities to start-commit
      hooks") for why. */
@@ -707,7 +712,8 @@ svn_ra_serf__context_run_wait(svn_boolea
                               apr_pool_t *scratch_pool)
 {
   apr_pool_t *iterpool;
-
+  apr_short_interval_time_t waittime_left = sess->timeout;
+  
   assert(sess->pending_error == SVN_NO_ERROR);
 
   iterpool = svn_pool_create(scratch_pool);
@@ -722,17 +728,36 @@ svn_ra_serf__context_run_wait(svn_boolea
       if (sess->cancel_func)
         SVN_ERR((*sess->cancel_func)(sess->cancel_baton));
 
-      status = serf_context_run(sess->context, sess->timeout, iterpool);
+      status = serf_context_run(sess->context,
+                                SVN_RA_SERF__CONTEXT_RUN_DURATION,
+                                iterpool);
 
       err = sess->pending_error;
       sess->pending_error = SVN_NO_ERROR;
 
+      /* If the context duration timeout is up, we'll subtract that
+         duration from the total time alloted for such things.  If
+         there's no time left, we fail with a message indicating that
+         the connection timed out.  */
       if (APR_STATUS_IS_TIMEUP(status))
         {
           svn_error_clear(err);
-          return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT,
-                                  NULL,
-                                  _("Connection timed out"));
+          err = SVN_NO_ERROR;
+          status = 0;
+
+          if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION)
+            {
+              waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION;
+            }
+          else
+            {
+              return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
+                                      _("Connection timed out"));
+            }
+        }
+      else
+        {
+          waittime_left = sess->timeout;
         }
 
       SVN_ERR(err);
@@ -745,7 +770,7 @@ svn_ra_serf__context_run_wait(svn_boolea
                         _("Error running context"));
             }
 
-          return svn_error_wrap_apr(status, _("Error running context"));
+          return svn_ra_serf__wrap_err(status, _("Error running context"));
         }
 
       /* Debugging purposes only! */
@@ -928,7 +953,7 @@ svn_ra_serf__handle_discard_body(serf_re
 
   status = drain_bucket(response);
   if (status)
-    return svn_error_wrap_apr(status, NULL);
+    return svn_ra_serf__wrap_err(status, NULL);
 
   return SVN_NO_ERROR;
 }
@@ -1467,7 +1492,7 @@ handle_server_error(serf_request_t *requ
      surface. */
   err = drain_bucket(response);
   if (err && !SERF_BUCKET_READ_ERROR(err))
-    return svn_error_wrap_apr(err, NULL);
+    return svn_ra_serf__wrap_err(err, NULL);
 
   return SVN_NO_ERROR;
 }
@@ -1489,7 +1514,7 @@ svn_ra_serf__handle_xml_parser(serf_requ
   status = serf_bucket_response_status(response, &sl);
   if (SERF_BUCKET_READ_ERROR(status))
     {
-      return svn_error_wrap_apr(status, NULL);
+      return svn_ra_serf__wrap_err(status, NULL);
     }
 
   /* Woo-hoo.  Nothing here to see.  */
@@ -1541,7 +1566,7 @@ svn_ra_serf__handle_xml_parser(serf_requ
 
       if (SERF_BUCKET_READ_ERROR(status))
         {
-          return svn_error_wrap_apr(status, NULL);
+          return svn_ra_serf__wrap_err(status, NULL);
         }
 
       ctx->read_size += len;
@@ -1562,7 +1587,7 @@ svn_ra_serf__handle_xml_parser(serf_requ
               /* Skip on to the next iteration of this loop. */
               if (APR_STATUS_IS_EAGAIN(status))
                 {
-                  return svn_error_wrap_apr(status, NULL);
+                  return svn_ra_serf__wrap_err(status, NULL);
                 }
               continue;
             }
@@ -1606,7 +1631,7 @@ svn_ra_serf__handle_xml_parser(serf_requ
 
       if (APR_STATUS_IS_EAGAIN(status))
         {
-          return svn_error_wrap_apr(status, NULL);
+          return svn_ra_serf__wrap_err(status, NULL);
         }
 
       if (APR_STATUS_IS_EOF(status))
@@ -1627,7 +1652,7 @@ svn_ra_serf__handle_xml_parser(serf_requ
               add_done_item(ctx);
             }
 
-          return svn_error_wrap_apr(status, NULL);
+          return svn_ra_serf__wrap_err(status, NULL);
         }
 
       /* feed me! */
@@ -1817,7 +1842,7 @@ handle_response(serf_request_t *request,
           && handler->sline.code != 304)
         {
           err = svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
-                                  svn_error_wrap_apr(status, NULL),
+                                  svn_ra_serf__wrap_err(status, NULL),
                                   _("Premature EOF seen from server"
                                     " (http status=%d)"),
                                   handler->sline.code);
@@ -1966,7 +1991,8 @@ handle_response(serf_request_t *request,
 
   if (err
       && (!SERF_BUCKET_READ_ERROR(err->apr_err)
-          || APR_STATUS_IS_ECONNRESET(err->apr_err)))
+          || APR_STATUS_IS_ECONNRESET(err->apr_err)
+          || APR_STATUS_IS_ECONNABORTED(err->apr_err)))
     {
       /* These errors are special cased in serf
          ### We hope no handler returns these by accident. */
@@ -2212,7 +2238,8 @@ svn_ra_serf__discover_vcc(const char **v
 
       /* Now recreate the root_url. */
       session->repos_root = session->session_url;
-      session->repos_root.path = apr_pstrdup(session->pool, url_buf->data);
+      session->repos_root.path =
+        (char *)svn_fspath__canonicalize(url_buf->data, session->pool);
       session->repos_root_str =
         svn_urlpath__canonicalize(apr_uri_unparse(session->pool,
                                                   &session->repos_root, 0),
@@ -2397,9 +2424,8 @@ expat_response_handler(serf_request_t *r
       XML_SetCharacterDataHandler(ectx->parser, expat_cdata);
     }
 
-  /* ### should we bail on anything < 200 or >= 300 ??
-     ### actually: < 200 should really be handled by the core.  */
-  if (ectx->handler->sline.code == 404)
+  /* ### TODO: sline.code < 200 should really be handled by the core */
+  if ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300))
     {
       /* By deferring to expect_empty_body(), it will make a choice on
          how to handle the body. Whatever the decision, the core handler
@@ -2418,7 +2444,7 @@ expat_response_handler(serf_request_t *r
 
       status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
       if (SERF_BUCKET_READ_ERROR(status))
-        return svn_error_wrap_apr(status, NULL);
+        return svn_ra_serf__wrap_err(status, NULL);
 
 #if 0
       /* ### move restart/skip into the core handler  */
@@ -2472,7 +2498,7 @@ expat_response_handler(serf_request_t *r
 
       if (status && !SERF_BUCKET_READ_ERROR(status))
         {
-          return svn_error_wrap_apr(status, NULL);
+          return svn_ra_serf__wrap_err(status, NULL);
         }
     }
 

Modified: subversion/branches/ev2-export/subversion/libsvn_ra_serf/xml.c
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_ra_serf/xml.c?rev=1400556&r1=1400555&r2=1400556&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/libsvn_ra_serf/xml.c (original)
+++ subversion/branches/ev2-export/subversion/libsvn_ra_serf/xml.c Sun Oct 21 02:00:31 2012
@@ -205,9 +205,23 @@ svn_ra_serf__expand_ns(svn_ra_serf__dav_
             }
         }
     }
+  else
+    {
+      const svn_ra_serf__ns_t *ns;
+
+      for (ns = ns_list; ns; ns = ns->next)
+        {
+          if (! ns->namespace[0])
+            {
+              returned_prop_name->namespace = ns->url;
+              returned_prop_name->name = name;
+              return;
+            }
+        }
+    }    
 
-  /* If there is no prefix, or if the prefix is not found, then the
-     name is NOT within a namespace.  */
+  /* If the prefix is not found, then the name is NOT within a
+     namespace.  */
   returned_prop_name->namespace = "";
   returned_prop_name->name = name;
 }

Modified: subversion/branches/ev2-export/subversion/libsvn_ra_svn/client.c
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_ra_svn/client.c?rev=1400556&r1=1400555&r2=1400556&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/libsvn_ra_svn/client.c (original)
+++ subversion/branches/ev2-export/subversion/libsvn_ra_svn/client.c Sun Oct 21 02:00:31 2012
@@ -258,9 +258,10 @@ static svn_error_t *ra_svn_set_path(void
 {
   ra_svn_reporter_baton_t *b = baton;
 
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "set-path", "crb(?c)w",
-                               path, rev, start_empty, lock_token,
-                               svn_depth_to_word(depth)));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_set_path,
+                                         path, rev, start_empty, lock_token,
+                                         svn_depth_to_word(depth)));
   return SVN_NO_ERROR;
 }
 
@@ -269,7 +270,8 @@ static svn_error_t *ra_svn_delete_path(v
 {
   ra_svn_reporter_baton_t *b = baton;
 
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "delete-path", "c", path));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_delete_path, path));
   return SVN_NO_ERROR;
 }
 
@@ -283,9 +285,11 @@ static svn_error_t *ra_svn_link_path(voi
 {
   ra_svn_reporter_baton_t *b = baton;
 
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "link-path", "ccrb(?c)w",
-                               path, url, rev, start_empty, lock_token,
-                               svn_depth_to_word(depth)));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_link_path,
+                                         path, url, rev, start_empty,
+                                         lock_token,
+                                         svn_depth_to_word(depth)));
   return SVN_NO_ERROR;
 }
 
@@ -294,7 +298,8 @@ static svn_error_t *ra_svn_finish_report
 {
   ra_svn_reporter_baton_t *b = baton;
 
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, b->pool, "finish-report", ""));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, b->pool,
+                                         svn_ra_svn_cmd_finish_report));
   SVN_ERR(handle_auth_request(b->sess_baton, b->pool));
   SVN_ERR(svn_ra_svn_drive_editor2(b->conn, b->pool, b->editor, b->edit_baton,
                                    NULL, FALSE));
@@ -307,7 +312,8 @@ static svn_error_t *ra_svn_abort_report(
 {
   ra_svn_reporter_baton_t *b = baton;
 
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, b->pool, "abort-report", ""));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, b->pool,
+                                         svn_ra_svn_cmd_abort_report));
   return SVN_NO_ERROR;
 }
 
@@ -454,8 +460,9 @@ static void handle_child_process_error(a
       || apr_file_open_stdout(&out_file, pool))
     return;
 
-  conn = svn_ra_svn_create_conn2(NULL, in_file, out_file,
-                                 SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
+  conn = svn_ra_svn_create_conn3(NULL, in_file, out_file,
+                                 SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0,
+                                 0, pool);
   err = svn_error_wrap_apr(status, _("Error in child process: %s"), desc);
   svn_error_clear(svn_ra_svn_write_cmd_failure(conn, pool, err));
   svn_error_clear(err);
@@ -522,8 +529,9 @@ static svn_error_t *make_tunnel(const ch
   apr_file_inherit_unset(proc->out);
 
   /* Guard against dotfile output to stdout on the server. */
-  *conn = svn_ra_svn_create_conn2(NULL, proc->out, proc->in,
-                                  SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
+  *conn = svn_ra_svn_create_conn3(NULL, proc->out, proc->in,
+                                  SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
+                                  0, 0, pool);
   err = svn_ra_svn_skip_leading_garbage(*conn, pool);
   if (err)
     return svn_error_quick_wrap(
@@ -586,17 +594,34 @@ static svn_error_t *open_session(svn_ra_
   sess->callbacks = callbacks;
   sess->callbacks_baton = callbacks_baton;
   sess->bytes_read = sess->bytes_written = 0;
-
+  
   if (tunnel_argv)
     SVN_ERR(make_tunnel(tunnel_argv, &conn, pool));
   else
     {
       SVN_ERR(make_connection(uri->hostname, uri->port, &sock, pool));
-      conn = svn_ra_svn_create_conn2(sock, NULL, NULL,
+      conn = svn_ra_svn_create_conn3(sock, NULL, NULL,
                                      SVN_DELTA_COMPRESSION_LEVEL_DEFAULT,
-                                     pool);
+                                     0, 0, pool);
     }
 
+  /* Build the useragent string, querying the client for any
+     customizations it wishes to note.  For historical reasons, we
+     still deliver the hard-coded client version info
+     (SVN_RA_SVN__DEFAULT_USERAGENT) and the customized client string
+     separately in the protocol/capabilities handshake below.  But the
+     commit logic wants the combined form for use with the
+     SVN_PROP_TXN_USER_AGENT ephemeral property because that's
+     consistent with our DAV approach.  */
+  if (sess->callbacks->get_client_string != NULL)
+    SVN_ERR(sess->callbacks->get_client_string(sess->callbacks_baton,
+                                               &client_string, pool));
+  if (client_string)
+    sess->useragent = apr_pstrcat(pool, SVN_RA_SVN__DEFAULT_USERAGENT "/",
+                                  client_string, (char *)NULL);
+  else
+    sess->useragent = SVN_RA_SVN__DEFAULT_USERAGENT;
+
   /* Make sure we set conn->session before reading from it,
    * because the reader and writer functions expect a non-NULL value. */
   sess->conn = conn;
@@ -623,10 +648,6 @@ static svn_error_t *open_session(svn_ra_
     return svn_error_create(SVN_ERR_RA_SVN_BAD_VERSION, NULL,
                             _("Server does not support edit pipelining"));
 
-  if (sess->callbacks->get_client_string != NULL)
-    SVN_ERR(sess->callbacks->get_client_string(sess->callbacks_baton,
-                                               &client_string, pool));
-
   /* In protocol version 2, we send back our protocol version, our
    * capability list, and the URL, and subsequently there is an auth
    * request. */
@@ -639,7 +660,9 @@ static svn_error_t *open_session(svn_ra_
                                  SVN_RA_SVN_CAP_DEPTH,
                                  SVN_RA_SVN_CAP_MERGEINFO,
                                  SVN_RA_SVN_CAP_LOG_REVPROPS,
-                                 url, "SVN/" SVN_VER_NUMBER, client_string));
+                                 url,
+                                 SVN_RA_SVN__DEFAULT_USERAGENT,
+                                 client_string));
   SVN_ERR(handle_auth_request(sess, pool));
 
   /* This is where the security layer would go into effect if we
@@ -753,7 +776,8 @@ static svn_error_t *ra_svn_reparent(svn_
   svn_ra_svn__session_baton_t *new_sess;
   apr_uri_t uri;
 
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "reparent", "c", url));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool,
+                                         svn_ra_svn_cmd_reparent, url));
   err = handle_auth_request(sess, pool);
   if (! err)
     {
@@ -802,7 +826,8 @@ static svn_error_t *ra_svn_get_latest_re
   svn_ra_svn__session_baton_t *sess_baton = session->priv;
   svn_ra_svn_conn_t *conn = sess_baton->conn;
 
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "get-latest-rev", ""));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool,
+                                         svn_ra_svn_cmd_get_latest_rev));
   SVN_ERR(handle_auth_request(sess_baton, pool));
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "r", rev));
   return SVN_NO_ERROR;
@@ -815,7 +840,8 @@ static svn_error_t *ra_svn_get_dated_rev
   svn_ra_svn__session_baton_t *sess_baton = session->priv;
   svn_ra_svn_conn_t *conn = sess_baton->conn;
 
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "get-dated-rev", "c",
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool,
+                                         svn_ra_svn_cmd_get_dated_rev,
                                svn_time_to_cstring(tm, pool)));
   SVN_ERR(handle_auth_request(sess_baton, pool));
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "r", rev));
@@ -859,11 +885,14 @@ static svn_error_t *ra_svn_change_rev_pr
     }
 
   if (has_atomic_revprops)
-    SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "change-rev-prop2", "rc(?s)(b?s)",
-                                 rev, name, value, dont_care, old_value));
+    SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool,
+                                           svn_ra_svn_cmd_change_rev_prop2,
+                                           rev, name, value, dont_care,
+                                           old_value));
   else
-    SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "change-rev-prop", "rc?s",
-                                 rev, name, value));
+    SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool,
+                                           svn_ra_svn_cmd_change_rev_prop,
+                                           rev, name, value));
 
   SVN_ERR(handle_auth_request(sess_baton, pool));
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, ""));
@@ -900,7 +929,8 @@ static svn_error_t *ra_svn_rev_proplist(
   svn_ra_svn_conn_t *conn = sess_baton->conn;
   apr_array_header_t *proplist;
 
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "rev-proplist", "r", rev));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool,
+                                         svn_ra_svn_cmd_rev_proplist, rev));
   SVN_ERR(handle_auth_request(sess_baton, pool));
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "l", &proplist));
   SVN_ERR(svn_ra_svn_parse_proplist(proplist, pool, props));
@@ -914,7 +944,8 @@ static svn_error_t *ra_svn_rev_prop(svn_
   svn_ra_svn__session_baton_t *sess_baton = session->priv;
   svn_ra_svn_conn_t *conn = sess_baton->conn;
 
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "rev-prop", "rc", rev, name));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool,
+                                         svn_ra_svn_cmd_rev_prop, rev, name));
   SVN_ERR(handle_auth_request(sess_baton, pool));
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "(?s)", value));
   return SVN_NO_ERROR;
@@ -966,6 +997,21 @@ static svn_error_t *ra_svn_commit(svn_ra
                             _("Server doesn't support setting arbitrary "
                               "revision properties during commit"));
 
+  /* If the server supports ephemeral txnprops, add the one that
+     reports the client's version level string. */
+  if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_COMMIT_REVPROPS) &&
+      svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS))
+    {
+      apr_hash_set(revprop_table,
+                   SVN_PROP_TXN_CLIENT_COMPAT_VERSION,
+                   APR_HASH_KEY_STRING,
+                   svn_string_create(SVN_VER_NUMBER, pool));
+      apr_hash_set(revprop_table,
+                   SVN_PROP_TXN_USER_AGENT,
+                   APR_HASH_KEY_STRING,
+                   svn_string_create(sess_baton->useragent, pool));
+    }
+
   /* Tell the server we're starting the commit.
      Send log message here for backwards compatibility with servers
      before 1.5. */
@@ -998,6 +1044,7 @@ static svn_error_t *ra_svn_commit(svn_ra
   ccb = apr_palloc(pool, sizeof(*ccb));
   ccb->sess_baton = sess_baton;
   ccb->pool = pool;
+  ccb->new_rev = NULL;
   ccb->callback = callback;
   ccb->callback_baton = callback_baton;
 
@@ -1009,6 +1056,80 @@ static svn_error_t *ra_svn_commit(svn_ra
   return SVN_NO_ERROR;
 }
 
+/* Parse IPROPLIST, an array of svn_ra_svn_item_t structures, as a list of
+   const char * repos relative paths and properties for those paths, storing
+   the result as an array of svn_prop_inherited_item_t *items. */
+static svn_error_t *
+parse_iproplist(apr_array_header_t **inherited_props,
+                const apr_array_header_t *iproplist,
+                svn_ra_session_t *session,
+                apr_pool_t *result_pool,
+                apr_pool_t *scratch_pool)
+
+{
+  int i;
+  const char *repos_root_url;
+  apr_pool_t *iterpool;
+
+  if (iproplist == NULL)
+    {
+      /* If the server doesn't have the SVN_RA_CAPABILITY_INHERITED_PROPS
+         capability we shouldn't be asking for inherited props, but if we
+         did and the server sent back nothing then we'll want to handle
+         that. */
+      *inherited_props = NULL;
+      return SVN_NO_ERROR;
+    }
+
+  SVN_ERR(ra_svn_get_repos_root(session, &repos_root_url, scratch_pool));
+
+  *inherited_props = apr_array_make(
+    result_pool, iproplist->nelts, sizeof(svn_prop_inherited_item_t *));
+
+  iterpool = svn_pool_create(scratch_pool);
+
+  for (i = 0; i < iproplist->nelts; i++)
+    {
+      apr_array_header_t *iprop_list;
+      char *parent_rel_path;
+      apr_hash_t *iprops;
+      apr_hash_index_t *hi;
+      svn_prop_inherited_item_t *new_iprop =
+        apr_palloc(result_pool, sizeof(*new_iprop));
+      svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(iproplist, i,
+                                              svn_ra_svn_item_t);
+      if (elt->kind != SVN_RA_SVN_LIST)
+        return svn_error_create(
+          SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
+          _("Inherited proplist element not a list"));
+
+      svn_pool_clear(iterpool);
+
+      SVN_ERR(svn_ra_svn_parse_tuple(elt->u.list, iterpool, "cl",
+                                     &parent_rel_path, &iprop_list));
+      SVN_ERR(svn_ra_svn_parse_proplist(iprop_list, iterpool, &iprops));
+      new_iprop->path_or_url = svn_path_url_add_component2(repos_root_url,
+                                                           parent_rel_path,
+                                                           result_pool);
+      new_iprop->prop_hash = apr_hash_make(result_pool);
+      for (hi = apr_hash_first(iterpool, iprops);
+           hi;
+           hi = apr_hash_next(hi))
+        {
+          const char *name = svn__apr_hash_index_key(hi);
+          svn_string_t *value = svn__apr_hash_index_val(hi);
+          apr_hash_set(new_iprop->prop_hash,
+                       apr_pstrdup(result_pool, name),
+                       APR_HASH_KEY_STRING,
+                       svn_string_dup(value, result_pool));
+        }
+      APR_ARRAY_PUSH(*inherited_props, svn_prop_inherited_item_t *) =
+        new_iprop;
+    }
+  svn_pool_destroy(iterpool);
+  return SVN_NO_ERROR;
+}
+
 static svn_error_t *ra_svn_get_file(svn_ra_session_t *session, const char *path,
                                     svn_revnum_t rev, svn_stream_t *stream,
                                     svn_revnum_t *fetched_rev,
@@ -1023,8 +1144,9 @@ static svn_error_t *ra_svn_get_file(svn_
   svn_checksum_ctx_t *checksum_ctx;
   apr_pool_t *iterpool;
 
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "get-file", "c(?r)bb", path,
-                               rev, (props != NULL), (stream != NULL)));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool,
+                                         svn_ra_svn_cmd_get_file, path, rev,
+                                         (props != NULL), (stream != NULL)));
   SVN_ERR(handle_auth_request(sess_baton, pool));
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "(?c)rl",
                                        &expected_digest,
@@ -1254,9 +1376,10 @@ static svn_error_t *ra_svn_update(svn_ra
   svn_boolean_t recurse = DEPTH_TO_RECURSE(depth);
 
   /* Tell the server we want to start an update. */
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "update", "(?r)cbwb", rev, target,
-                               recurse, svn_depth_to_word(depth),
-                               send_copyfrom_args));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool, svn_ra_svn_cmd_update,
+                                         rev, target, recurse,
+                                         svn_depth_to_word(depth),
+                                         send_copyfrom_args));
   SVN_ERR(handle_auth_request(sess_baton, pool));
 
   /* Fetch a reporter for the caller to drive.  The reporter will drive
@@ -1279,9 +1402,9 @@ static svn_error_t *ra_svn_switch(svn_ra
   svn_boolean_t recurse = DEPTH_TO_RECURSE(depth);
 
   /* Tell the server we want to start a switch. */
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "switch", "(?r)cbcw", rev,
-                               target, recurse, switch_url,
-                               svn_depth_to_word(depth)));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool, svn_ra_svn_cmd_switch,
+                                         rev, target, recurse, switch_url,
+                                         svn_depth_to_word(depth)));
   SVN_ERR(handle_auth_request(sess_baton, pool));
 
   /* Fetch a reporter for the caller to drive.  The reporter will drive
@@ -1304,9 +1427,9 @@ static svn_error_t *ra_svn_status(svn_ra
   svn_boolean_t recurse = DEPTH_TO_RECURSE(depth);
 
   /* Tell the server we want to start a status operation. */
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "status", "cb(?r)w",
-                               target, recurse, rev,
-                               svn_depth_to_word(depth)));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool, svn_ra_svn_cmd_status,
+                                         target, recurse, rev,
+                                         svn_depth_to_word(depth)));
   SVN_ERR(handle_auth_request(sess_baton, pool));
 
   /* Fetch a reporter for the caller to drive.  The reporter will drive
@@ -1332,10 +1455,10 @@ static svn_error_t *ra_svn_diff(svn_ra_s
   svn_boolean_t recurse = DEPTH_TO_RECURSE(depth);
 
   /* Tell the server we want to start a diff. */
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "diff", "(?r)cbbcbw", rev,
-                               target, recurse, ignore_ancestry,
-                               versus_url, text_deltas,
-                               svn_depth_to_word(depth)));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool, svn_ra_svn_cmd_diff,
+                                         rev, target, recurse, ignore_ancestry,
+                                         versus_url, text_deltas,
+                                         svn_depth_to_word(depth)));
   SVN_ERR(handle_auth_request(sess_baton, pool));
 
   /* Fetch a reporter for the caller to drive.  The reporter will drive
@@ -1567,7 +1690,9 @@ static svn_error_t *ra_svn_check_path(sv
   svn_ra_svn_conn_t *conn = sess_baton->conn;
   const char *kind_word;
 
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "check-path", "c(?r)", path, rev));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool,
+                                         svn_ra_svn_cmd_check_path,
+                                         path, rev));
   SVN_ERR(handle_auth_request(sess_baton, pool));
   SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "w", &kind_word));
   *kind = svn_node_kind_from_word(kind_word);
@@ -1596,7 +1721,8 @@ static svn_error_t *ra_svn_stat(svn_ra_s
   apr_array_header_t *list = NULL;
   svn_dirent_t *the_dirent;
 
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "stat", "c(?r)", path, rev));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool, svn_ra_svn_cmd_stat,
+                                         path, rev));
 
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
                                  N_("'stat' not implemented")));
@@ -1774,9 +1900,10 @@ static svn_error_t *ra_svn_get_file_revs
   rev_pool = svn_pool_create(pool);
   chunk_pool = svn_pool_create(pool);
 
-  SVN_ERR(svn_ra_svn_write_cmd(sess_baton->conn, pool, "get-file-revs",
-                               "c(?r)(?r)b", path, start, end,
-                               include_merged_revisions));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(sess_baton->conn, pool,
+                                         svn_ra_svn_cmd_get_file_revs,
+                                         path, start, end,
+                                         include_merged_revisions));
 
   /* Servers before 1.1 don't support this command.  Check for this here. */
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
@@ -1905,9 +2032,10 @@ static svn_error_t *ra_svn_lock_compat(s
       path = key;
       revnum = val;
 
-      SVN_ERR(svn_ra_svn_write_cmd(conn, iterpool, "lock", "c(?c)b(?r)",
-                                   path, comment,
-                                   steal_lock, *revnum));
+      SVN_ERR(svn_ra_svn_write_templated_cmd(conn, iterpool,
+                                             svn_ra_svn_cmd_lock,
+                                             path, comment,
+                                             steal_lock, *revnum));
 
       /* Servers before 1.2 doesn't support locking.  Check this here. */
       SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
@@ -1970,8 +2098,9 @@ static svn_error_t *ra_svn_unlock_compat
       else
         token = NULL;
 
-      SVN_ERR(svn_ra_svn_write_cmd(conn, iterpool, "unlock", "c(?c)b",
-                                   path, token, break_lock));
+      SVN_ERR(svn_ra_svn_write_templated_cmd(conn, iterpool,
+                                             svn_ra_svn_cmd_unlock,
+                                             path, token, break_lock));
 
       /* Servers before 1.2 don't support locking.  Check this here. */
       SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, iterpool),
@@ -2258,7 +2387,8 @@ static svn_error_t *ra_svn_get_lock(svn_
   svn_ra_svn_conn_t* conn = sess->conn;
   apr_array_header_t *list;
 
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "get-lock", "c", path));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool,
+                                         svn_ra_svn_cmd_get_lock, path));
 
   /* Servers before 1.2 doesn't support locking.  Check this here. */
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
@@ -2310,8 +2440,9 @@ static svn_error_t *ra_svn_get_locks(svn
   SVN_ERR(path_relative_to_root(session, &abs_path, full_url, pool));
   abs_path = svn_fspath__canonicalize(abs_path, pool);
 
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "get-locks", "c(w)", path,
-                               svn_depth_to_word(depth)));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool,
+                                         svn_ra_svn_cmd_get_locks, path,
+                                         svn_depth_to_word(depth)));
 
   /* Servers before 1.2 doesn't support locking.  Check this here. */
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
@@ -2368,8 +2499,9 @@ static svn_error_t *ra_svn_replay(svn_ra
 {
   svn_ra_svn__session_baton_t *sess = session->priv;
 
-  SVN_ERR(svn_ra_svn_write_cmd(sess->conn, pool, "replay", "rrb", revision,
-                               low_water_mark, send_deltas));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(sess->conn, pool,
+                                         svn_ra_svn_cmd_replay, revision,
+                                         low_water_mark, send_deltas));
 
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
                                  N_("Server doesn't support the replay "
@@ -2398,9 +2530,10 @@ ra_svn_replay_range(svn_ra_session_t *se
   svn_revnum_t rev;
   svn_boolean_t drive_aborted = FALSE;
 
-  SVN_ERR(svn_ra_svn_write_cmd(sess->conn, pool, "replay-range", "rrrb",
-                               start_revision, end_revision,
-                               low_water_mark, send_deltas));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(sess->conn, pool,
+                                         svn_ra_svn_cmd_replay_range,
+                                         start_revision, end_revision,
+                                         low_water_mark, send_deltas));
 
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess, pool),
                                  N_("Server doesn't support the "
@@ -2474,6 +2607,12 @@ static svn_error_t *ra_svn_has_capabilit
   else if (strcmp(capability, SVN_RA_CAPABILITY_ATOMIC_REVPROPS) == 0)
     *has = svn_ra_svn_has_capability(sess->conn,
                                      SVN_RA_SVN_CAP_ATOMIC_REVPROPS);
+  else if (strcmp(capability, SVN_RA_CAPABILITY_INHERITED_PROPS) == 0)
+    *has = svn_ra_svn_has_capability(sess->conn,
+                                     SVN_RA_SVN_CAP_INHERITED_PROPS);
+  else if (strcmp(capability, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS) == 0)
+    *has = svn_ra_svn_has_capability(sess->conn,
+                                     SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS);
   else  /* Don't know any other capabilities, so error. */
     {
       return svn_error_createf
@@ -2497,8 +2636,9 @@ ra_svn_get_deleted_rev(svn_ra_session_t 
   svn_ra_svn_conn_t *conn = sess_baton->conn;
 
   /* Transmit the parameters. */
-  SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "get-deleted-rev", "crr",
-                               path, peg_revision, end_revision));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, pool,
+                                         svn_ra_svn_cmd_get_deleted_rev,
+                                         path, peg_revision, end_revision));
 
   /* Servers before 1.6 don't support this command.  Check for this here. */
   SVN_ERR(handle_unsupported_cmd(handle_auth_request(sess_baton, pool),
@@ -2519,6 +2659,28 @@ ra_svn_register_editor_shim_callbacks(sv
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+ra_svn_get_inherited_props(svn_ra_session_t *session,
+                           apr_array_header_t **iprops,
+                           const char *path,
+                           svn_revnum_t revision,
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool)
+{
+  svn_ra_svn__session_baton_t *sess_baton = session->priv;
+  svn_ra_svn_conn_t *conn = sess_baton->conn;
+  apr_array_header_t *iproplist;
+
+  SVN_ERR(svn_ra_svn_write_templated_cmd(conn, scratch_pool,
+                                         svn_ra_svn_cmd_get_iprops,
+                                         path, revision));
+  SVN_ERR(handle_auth_request(sess_baton, scratch_pool));
+  SVN_ERR(svn_ra_svn_read_cmd_response(conn, scratch_pool, "l", &iproplist));
+  SVN_ERR(parse_iproplist(iprops, iproplist, session, result_pool,
+                          scratch_pool));
+
+  return SVN_NO_ERROR;
+}
 
 static const svn_ra__vtable_t ra_svn_vtable = {
   svn_ra_svn_version,
@@ -2556,7 +2718,8 @@ static const svn_ra__vtable_t ra_svn_vta
   ra_svn_has_capability,
   ra_svn_replay_range,
   ra_svn_get_deleted_rev,
-  ra_svn_register_editor_shim_callbacks
+  ra_svn_register_editor_shim_callbacks,
+  ra_svn_get_inherited_props
 };
 
 svn_error_t *

Modified: subversion/branches/ev2-export/subversion/libsvn_ra_svn/editorp.c
URL: http://svn.apache.org/viewvc/subversion/branches/ev2-export/subversion/libsvn_ra_svn/editorp.c?rev=1400556&r1=1400555&r2=1400556&view=diff
==============================================================================
--- subversion/branches/ev2-export/subversion/libsvn_ra_svn/editorp.c (original)
+++ subversion/branches/ev2-export/subversion/libsvn_ra_svn/editorp.c Sun Oct 21 02:00:31 2012
@@ -121,13 +121,23 @@ static ra_svn_baton_t *ra_svn_make_baton
 
 /* Check for an early error status report from the consumer.  If we
  * get one, abort the edit and return the error. */
-static svn_error_t *check_for_error(ra_svn_edit_baton_t *eb, apr_pool_t *pool)
+static svn_error_t *
+check_for_error_internal(ra_svn_edit_baton_t *eb, apr_pool_t *pool)
 {
   SVN_ERR_ASSERT(!eb->got_status);
+
+  /* reset TX counter */
+  eb->conn->written_since_error_check = 0;
+
+  /* if we weren't asked to always check, wait for at least the next TX */
+  eb->conn->may_check_for_error = eb->conn->error_check_interval == 0;
+
+  /* any incoming data? */
   if (svn_ra_svn__input_waiting(eb->conn, pool))
     {
       eb->got_status = TRUE;
-      SVN_ERR(svn_ra_svn_write_cmd(eb->conn, pool, "abort-edit", ""));
+      SVN_ERR(svn_ra_svn_write_templated_cmd(eb->conn, pool,
+                                             svn_ra_svn_cmd_abort_edit));
       SVN_ERR(svn_ra_svn_read_cmd_response(eb->conn, pool, ""));
       /* We shouldn't get here if the consumer is doing its job. */
       return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
@@ -136,13 +146,22 @@ static svn_error_t *check_for_error(ra_s
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+check_for_error(ra_svn_edit_baton_t *eb, apr_pool_t *pool)
+{
+  return eb->conn->may_check_for_error
+    ? check_for_error_internal(eb, pool)
+    : SVN_NO_ERROR;
+}
+
 static svn_error_t *ra_svn_target_rev(void *edit_baton, svn_revnum_t rev,
                                       apr_pool_t *pool)
 {
   ra_svn_edit_baton_t *eb = edit_baton;
 
   SVN_ERR(check_for_error(eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(eb->conn, pool, "target-rev", "r", rev));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(eb->conn, pool,
+                                         svn_ra_svn_cmd_target_rev, rev));
   return SVN_NO_ERROR;
 }
 
@@ -153,8 +172,9 @@ static svn_error_t *ra_svn_open_root(voi
   const char *token = make_token('d', eb, pool);
 
   SVN_ERR(check_for_error(eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(eb->conn, pool, "open-root", "(?r)c", rev,
-                               token));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(eb->conn, pool,
+                                         svn_ra_svn_cmd_open_root, rev,
+                                         token));
   *root_baton = ra_svn_make_baton(eb->conn, pool, eb, token);
   return SVN_NO_ERROR;
 }
@@ -165,8 +185,9 @@ static svn_error_t *ra_svn_delete_entry(
   ra_svn_baton_t *b = parent_baton;
 
   SVN_ERR(check_for_error(b->eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "delete-entry", "c(?r)c",
-                               path, rev, b->token));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_delete_entry,
+                                         path, rev, b->token));
   return SVN_NO_ERROR;
 }
 
@@ -181,8 +202,10 @@ static svn_error_t *ra_svn_add_dir(const
   SVN_ERR_ASSERT((copy_path && SVN_IS_VALID_REVNUM(copy_rev))
                  || (!copy_path && !SVN_IS_VALID_REVNUM(copy_rev)));
   SVN_ERR(check_for_error(b->eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "add-dir", "ccc(?cr)", path,
-                               b->token, token, copy_path, copy_rev));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_add_dir, path,
+                                         b->token, token, copy_path,
+                                         copy_rev));
   *child_baton = ra_svn_make_baton(b->conn, pool, b->eb, token);
   return SVN_NO_ERROR;
 }
@@ -195,8 +218,9 @@ static svn_error_t *ra_svn_open_dir(cons
   const char *token = make_token('d', b->eb, pool);
 
   SVN_ERR(check_for_error(b->eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "open-dir", "ccc(?r)",
-                               path, b->token, token, rev));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_open_dir,
+                                         path, b->token, token, rev));
   *child_baton = ra_svn_make_baton(b->conn, pool, b->eb, token);
   return SVN_NO_ERROR;
 }
@@ -208,8 +232,9 @@ static svn_error_t *ra_svn_change_dir_pr
   ra_svn_baton_t *b = dir_baton;
 
   SVN_ERR(check_for_error(b->eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "change-dir-prop", "cc(?s)",
-                               b->token, name, value));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_change_dir_prop,
+                                         b->token, name, value));
   return SVN_NO_ERROR;
 }
 
@@ -218,7 +243,9 @@ static svn_error_t *ra_svn_close_dir(voi
   ra_svn_baton_t *b = dir_baton;
 
   SVN_ERR(check_for_error(b->eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "close-dir", "c", b->token));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_close_dir,
+                                         b->token));
   return SVN_NO_ERROR;
 }
 
@@ -233,8 +260,9 @@ static svn_error_t *ra_svn_absent_dir(co
     return SVN_NO_ERROR;
 
   SVN_ERR(check_for_error(b->eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "absent-dir", "cc", path,
-                               b->token));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_absent_dir, path,
+                                         b->token));
   return SVN_NO_ERROR;
 }
 
@@ -251,8 +279,10 @@ static svn_error_t *ra_svn_add_file(cons
   SVN_ERR_ASSERT((copy_path && SVN_IS_VALID_REVNUM(copy_rev))
                  || (!copy_path && !SVN_IS_VALID_REVNUM(copy_rev)));
   SVN_ERR(check_for_error(b->eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "add-file", "ccc(?cr)", path,
-                               b->token, token, copy_path, copy_rev));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_add_file, path,
+                                         b->token, token, copy_path,
+                                         copy_rev));
   *file_baton = ra_svn_make_baton(b->conn, pool, b->eb, token);
   return SVN_NO_ERROR;
 }
@@ -267,8 +297,9 @@ static svn_error_t *ra_svn_open_file(con
   const char *token = make_token('c', b->eb, pool);
 
   SVN_ERR(check_for_error(b->eb, b->pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "open-file", "ccc(?r)",
-                               path, b->token, token, rev));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_open_file,
+                                         path, b->token, token, rev));
   *file_baton = ra_svn_make_baton(b->conn, pool, b->eb, token);
   return SVN_NO_ERROR;
 }
@@ -282,8 +313,9 @@ static svn_error_t *ra_svn_svndiff_handl
   SVN_ERR(check_for_error(b->eb, b->pool));
   str.data = data;
   str.len = *len;
-  return svn_ra_svn_write_cmd(b->conn, b->pool, "textdelta-chunk", "cs",
-                              b->token, &str);
+  return svn_ra_svn_write_templated_cmd(b->conn, b->pool,
+                                        svn_ra_svn_cmd_textdelta_chunk,
+                                        b->token, &str);
 }
 
 static svn_error_t *ra_svn_svndiff_close_handler(void *baton)
@@ -291,8 +323,9 @@ static svn_error_t *ra_svn_svndiff_close
   ra_svn_baton_t *b = baton;
 
   SVN_ERR(check_for_error(b->eb, b->pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, b->pool, "textdelta-end", "c",
-                               b->token));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, b->pool,
+                                         svn_ra_svn_cmd_textdelta_end,
+                                         b->token));
   return SVN_NO_ERROR;
 }
 
@@ -307,8 +340,9 @@ static svn_error_t *ra_svn_apply_textdel
 
   /* Tell the other side we're starting a text delta. */
   SVN_ERR(check_for_error(b->eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "apply-textdelta", "c(?c)",
-                               b->token, base_checksum));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_apply_textdelta,
+                                         b->token, base_checksum));
 
   /* Transform the window stream to an svndiff stream.  Reuse the
    * file baton for the stream handler, since it has all the
@@ -337,8 +371,9 @@ static svn_error_t *ra_svn_change_file_p
   ra_svn_baton_t *b = file_baton;
 
   SVN_ERR(check_for_error(b->eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "change-file-prop", "cc(?s)",
-                               b->token, name, value));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_change_file_prop,
+                                         b->token, name, value));
   return SVN_NO_ERROR;
 }
 
@@ -349,8 +384,9 @@ static svn_error_t *ra_svn_close_file(vo
   ra_svn_baton_t *b = file_baton;
 
   SVN_ERR(check_for_error(b->eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "close-file", "c(?c)",
-                               b->token, text_checksum));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_close_file,
+                                         b->token, text_checksum));
   return SVN_NO_ERROR;
 }
 
@@ -365,8 +401,9 @@ static svn_error_t *ra_svn_absent_file(c
     return SVN_NO_ERROR;
 
   SVN_ERR(check_for_error(b->eb, pool));
-  SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "absent-file", "cc", path,
-                               b->token));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(b->conn, pool,
+                                         svn_ra_svn_cmd_absent_file, path,
+                                         b->token));
   return SVN_NO_ERROR;
 }
 
@@ -377,11 +414,13 @@ static svn_error_t *ra_svn_close_edit(vo
 
   SVN_ERR_ASSERT(!eb->got_status);
   eb->got_status = TRUE;
-  SVN_ERR(svn_ra_svn_write_cmd(eb->conn, pool, "close-edit", ""));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(eb->conn, pool,
+                                         svn_ra_svn_cmd_close_edit));
   err = svn_ra_svn_read_cmd_response(eb->conn, pool, "");
   if (err)
     {
-      svn_error_clear(svn_ra_svn_write_cmd(eb->conn, pool, "abort-edit", ""));
+      svn_error_clear(svn_ra_svn_write_templated_cmd(eb->conn, pool,
+                                                     svn_ra_svn_cmd_abort_edit));
       return err;
     }
   if (eb->callback)
@@ -395,7 +434,8 @@ static svn_error_t *ra_svn_abort_edit(vo
 
   if (eb->got_status)
     return SVN_NO_ERROR;
-  SVN_ERR(svn_ra_svn_write_cmd(eb->conn, pool, "abort-edit", ""));
+  SVN_ERR(svn_ra_svn_write_templated_cmd(eb->conn, pool,
+                                         svn_ra_svn_cmd_abort_edit));
   SVN_ERR(svn_ra_svn_read_cmd_response(eb->conn, pool, ""));
   return SVN_NO_ERROR;
 }
@@ -911,30 +951,47 @@ svn_error_t *svn_ra_svn_drive_editor2(sv
   while (!state.done)
     {
       svn_pool_clear(subpool);
-      SVN_ERR(svn_ra_svn_read_tuple(conn, subpool, "wl", &cmd, &params));
-      for (i = 0; ra_svn_edit_cmds[i].cmd; i++)
-        {
-          if (strcmp(cmd, ra_svn_edit_cmds[i].cmd) == 0)
-            break;
-        }
-      if (ra_svn_edit_cmds[i].cmd)
-        err = (*ra_svn_edit_cmds[i].handler)(conn, subpool, params, &state);
-      else if (strcmp(cmd, "failure") == 0)
+      if (editor)
         {
-          /* While not really an editor command this can occur when
-             reporter->finish_report() fails before the first editor command */
-          if (aborted)
-            *aborted = TRUE;
-          err = svn_ra_svn__handle_failure_status(params, pool);
-          return svn_error_compose_create(
-                            err,
-                            editor->abort_edit(edit_baton, subpool));
+          SVN_ERR(svn_ra_svn_read_tuple(conn, subpool, "wl", &cmd, &params));
+          for (i = 0; ra_svn_edit_cmds[i].cmd; i++)
+              if (strcmp(cmd, ra_svn_edit_cmds[i].cmd) == 0)
+                break;
+
+          if (ra_svn_edit_cmds[i].cmd)
+            err = (*ra_svn_edit_cmds[i].handler)(conn, subpool, params, &state);
+          else if (strcmp(cmd, "failure") == 0)
+            {
+              /* While not really an editor command this can occur when
+                reporter->finish_report() fails before the first editor
+                command */
+              if (aborted)
+                *aborted = TRUE;
+              err = svn_ra_svn__handle_failure_status(params, pool);
+              return svn_error_compose_create(
+                                err,
+                                editor->abort_edit(edit_baton, subpool));
+            }
+          else
+            {
+              err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
+                                      _("Unknown command '%s'"), cmd);
+              err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
+            }
         }
       else
         {
-          err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
-                                  _("Unknown command '%s'"), cmd);
-          err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
+          const char* command = NULL;
+          SVN_ERR(svn_ra_svn__read_command_only(conn, subpool, &command));
+          if (strcmp(command, "close-edit") == 0)
+            {
+              state.done = TRUE;
+              if (aborted)
+                *aborted = FALSE;
+              err = svn_ra_svn_write_cmd_response(conn, pool, "");
+            }
+          else
+            err = NULL;
         }
 
       if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
@@ -944,7 +1001,8 @@ svn_error_t *svn_ra_svn_drive_editor2(sv
           if (!state.done)
             {
               /* Abort the edit and use non-blocking I/O to write the error. */
-              svn_error_clear(editor->abort_edit(edit_baton, subpool));
+              if (editor)
+                svn_error_clear(editor->abort_edit(edit_baton, subpool));
               svn_ra_svn__set_block_handler(conn, blocked_write, &state);
             }
           write_err = svn_ra_svn_write_cmd_failure(